Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions examples/official-storybook/stories/addon-controls.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,42 @@ export default {
},
};

const DEFAULT_NESTED_OBJECT = { a: 4, b: { c: 'hello', d: [1, 2, 3] } };

const Template = (args) => <Button {...args} />;

export const Basic = Template.bind({});
Basic.args = {
children: 'basic',
somethingElse: { a: 2 },
somethingElse: DEFAULT_NESTED_OBJECT,
};

export const Action = Template.bind({});
Action.args = {
children: 'hmmm',
type: 'action',
somethingElse: { a: 4 },
somethingElse: DEFAULT_NESTED_OBJECT,
};

export const CustomControls = Template.bind({});
CustomControls.args = {
children: 'hmmm',
type: 'action',
somethingElse: DEFAULT_NESTED_OBJECT,
};

CustomControls.argTypes = {
children: { table: { disable: true } },
type: { control: { disable: true } },
somethingElse: {
control: {
type: 'object',
collapsed: true,
displayObjectSize: false,
displayDataTypes: false,
// See src/controls/types:ObjectConfig for all options
},
},
};

export const NoArgs = () => <Button>no args</Button>;
1 change: 1 addition & 0 deletions lib/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"react": "^16.8.3",
"react-color": "^2.17.0",
"react-dom": "^16.8.3",
"react-json-view": "^1.19.1",
"react-popper-tooltip": "^3.1.0",
"react-syntax-highlighter": "^13.5.0",
"react-textarea-autosize": "^8.1.1",
Expand Down
80 changes: 33 additions & 47 deletions lib/components/src/controls/Object.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,10 @@
import React, { FC, ChangeEvent, useState, useCallback, useEffect } from 'react';
import React, { FC, useCallback } from 'react';
import { styled } from '@storybook/theming';

import deepEqual from 'fast-deep-equal';
import { Form } from '../form';
import ReactJson from 'react-json-view';
import type { InteractionProps } from 'react-json-view';
import { ControlProps, ObjectValue, ObjectConfig } from './types';
import { ArgType } from '../blocks';

const format = (value: any) => (value ? JSON.stringify(value) : '');

const parse = (value: string) => {
const trimmed = value && value.trim();
return trimmed ? JSON.parse(trimmed) : {};
};

const validate = (value: any, argType: ArgType) => {
if (argType && argType.type.name === 'array') {
return Array.isArray(value);
}
return true;
};

const Wrapper = styled.label({
display: 'flex',
Expand All @@ -27,46 +13,46 @@ const Wrapper = styled.label({
export type ObjectProps = ControlProps<ObjectValue> & ObjectConfig;
export const ObjectControl: FC<ObjectProps> = ({
name,
argType,
value,
onChange,
onBlur,
onFocus,
// ReactJSON Props
iconStyle = 'triangle',
indentWidth = 4,
collapsed = false,
collapseStringsAfterLength = false as false,
groupArraysAfterLength = 100,
enableClipboard = true,
displayObjectSize = true,
displayDataTypes = true,
sortKeys = false,
}) => {
const [valid, setValid] = useState(true);
const [text, setText] = useState(format(value));

useEffect(() => {
const newText = format(value);
if (text !== newText) setText(newText);
}, [value]);

const handleChange = useCallback(
(e: ChangeEvent<HTMLTextAreaElement>) => {
try {
const newVal = parse(e.target.value);
const newValid = validate(newVal, argType);
if (newValid && !deepEqual(value, newVal)) {
onChange(newVal);
}
setValid(newValid);
} catch (err) {
setValid(false);
(payload: InteractionProps) => {
// Compare just the modified value, but if accepted, refresh whole tree.
if (!deepEqual(payload.existing_value, payload.new_value)) {
onChange(payload.updated_src);
}
setText(e.target.value);
},
[onChange, setValid]
[onChange]
);

return (
<Wrapper>
<Form.Textarea
valid={valid ? undefined : 'error'}
value={text}
onChange={handleChange}
size="flex"
placeholder="Adjust object dynamically"
{...{ name, onBlur, onFocus }}
<ReactJson
name={name}
src={value}
onEdit={handleChange}
// NOT documenting/exposing the "theme" property as we would like to
// tree-shake unused themes in future for bundle weight optimization.
iconStyle={iconStyle}
indentWidth={indentWidth}
collapsed={collapsed}
collapseStringsAfterLength={collapseStringsAfterLength}
groupArraysAfterLength={groupArraysAfterLength}
enableClipboard={enableClipboard}
displayObjectSize={displayObjectSize}
displayDataTypes={displayDataTypes}
sortKeys={sortKeys}
/>
</Wrapper>
);
Expand Down
62 changes: 61 additions & 1 deletion lib/components/src/controls/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,67 @@ export interface NumberConfig {
export type RangeConfig = NumberConfig;

export type ObjectValue = any;
export interface ObjectConfig {}
export interface ObjectConfig {
/**
* Style of expand/collapse icons. Accepted values are "circle", triangle" or "square".
*
* Default: {}
*/
iconStyle?: 'circle' | 'triangle' | 'square';
/**
* Set the indent-width for nested objects.
*
* Default: 4
*/
indentWidth?: number;
/**
* When set to true, all nodes will be collapsed by default.
* Use an integer value to collapse at a particular depth.
*
* Default: false
*/
collapsed?: boolean | number;
/**
* When an integer value is assigned, strings will be cut off at that length.
* Collapsed strings are followed by an ellipsis.
* String content can be expanded and collapsed by clicking on the string value.
*
* Default: false
*/
collapseStringsAfterLength?: number | false;
/**
* When an integer value is assigned, arrays will be displayed in groups by count of the value.
* Groups are displayed with brakcet notation and can be expanded and collapsed by clickong on the brackets.
*
* Default: 100
*/
groupArraysAfterLength?: number;
/**
* When prop is not false, the user can copy objects and arrays to clipboard by clicking on the clipboard icon.
* Copy callbacks are supported.
*
* Default: true
*/
enableClipboard?: boolean;
/**
* When set to true, objects and arrays are labeled with size.
*
* Default: true
*/
displayObjectSize?: boolean;
/**
* When set to true, data type labels prefix values.
*
* Default: true
*/
displayDataTypes?: boolean;
/**
* Set to true to sort object keys.
*
* Default: false
*/
sortKeys?: boolean;
}

export type OptionsSingleSelection = any;
export type OptionsMultiSelection = any[];
Expand Down
64 changes: 63 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8874,6 +8874,11 @@ base-x@^3.0.7:
dependencies:
safe-buffer "^5.0.1"

base16@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
integrity sha1-4pf2DX7BAUp6lxo568ipjAtoHnA=

[email protected]:
version "0.1.5"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
Expand Down Expand Up @@ -15497,7 +15502,14 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"

fbjs@^0.8.1, fbjs@^0.8.9:
fbemitter@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-2.1.1.tgz#523e14fdaf5248805bb02f62efc33be703f51865"
integrity sha1-Uj4U/a9SSIBbsC9i78M75wP1GGU=
dependencies:
fbjs "^0.8.4"

fbjs@^0.8.0, fbjs@^0.8.1, fbjs@^0.8.4, fbjs@^0.8.9:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
Expand Down Expand Up @@ -15848,6 +15860,14 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"

flux@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/flux/-/flux-3.1.3.tgz#d23bed515a79a22d933ab53ab4ada19d05b2f08a"
integrity sha1-0jvtUVp5oi2TOrU6tK2hnQWy8Io=
dependencies:
fbemitter "^2.0.0"
fbjs "^0.8.0"

[email protected]:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
Expand Down Expand Up @@ -21983,6 +22003,11 @@ lodash.clonedeep@^4.4.1, lodash.clonedeep@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=

lodash.curry@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170"
integrity sha1-JI42By7ekGUB11lmIAqG2riyMXA=

lodash.debounce@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-3.1.1.tgz#812211c378a94cc29d5aa4e3346cf0bfce3a7df5"
Expand Down Expand Up @@ -22023,6 +22048,11 @@ lodash.flattendeep@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=

lodash.flow@^3.3.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
integrity sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=

lodash.foreach@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53"
Expand Down Expand Up @@ -26828,6 +26858,11 @@ puppeteer@^2.0.0:
rimraf "^2.6.1"
ws "^6.1.0"

pure-color@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e"
integrity sha1-H+Bk+wrIUfDeYTIKi/eWg2Qi8z4=

purgecss@^1.4.0:
version "1.4.2"
resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-1.4.2.tgz#67ab50cb4f5c163fcefde56002467c974e577f41"
Expand Down Expand Up @@ -27282,6 +27317,16 @@ react-app-polyfill@^1.0.1, react-app-polyfill@^1.0.6:
regenerator-runtime "^0.13.3"
whatwg-fetch "^3.0.0"

react-base16-styling@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c"
integrity sha1-7yFW1mz0E5aVyKFniGy2nqZgeSw=
dependencies:
base16 "^1.0.0"
lodash.curry "^4.0.1"
lodash.flow "^3.3.0"
pure-color "^1.2.0"

react-color@^2.17.0:
version "2.18.1"
resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.18.1.tgz#2cda8cc8e06a9e2c52ad391a30ddad31972472f4"
Expand Down Expand Up @@ -27497,6 +27542,16 @@ react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-i
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==

react-json-view@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.19.1.tgz#95d8e59e024f08a25e5dc8f076ae304eed97cf5c"
integrity sha512-u5e0XDLIs9Rj43vWkKvwL8G3JzvXSl6etuS5G42a8klMohZuYFQzSN6ri+/GiBptDqlrXPTdExJVU7x9rrlXhg==
dependencies:
flux "^3.1.3"
react-base16-styling "^0.6.0"
react-lifecycles-compat "^3.0.4"
react-textarea-autosize "^6.1.0"

react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
Expand Down Expand Up @@ -27727,6 +27782,13 @@ react-test-renderer@^16.0.0-0, react-test-renderer@^16.8.3:
react-is "^16.8.6"
scheduler "^0.19.1"

react-textarea-autosize@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-6.1.0.tgz#df91387f8a8f22020b77e3833c09829d706a09a5"
integrity sha512-F6bI1dgib6fSvG8so1HuArPUv+iVEfPliuLWusLF+gAKz0FbB4jLrWUrTAeq1afnPT2c9toEZYUdz/y1uKMy4A==
dependencies:
prop-types "^15.6.0"

react-textarea-autosize@^8.1.1:
version "8.1.1"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.1.1.tgz#d31dd1d04235af11161765782c70cb27c2c2832e"
Expand Down