From 950f5a68f3cdef043ca4d0330dbcfa431a2bed10 Mon Sep 17 00:00:00 2001 From: Dhenain Ambroise Date: Sat, 27 Feb 2021 13:37:56 +0100 Subject: [PATCH 1/6] Add readonly option to ControlProps and use it in all controls (ignoring Object, etc.) + add warning when using both disable/readonly on the same input (disable wins) --- lib/components/src/blocks/ArgsTable/ArgControl.tsx | 6 +++++- lib/components/src/controls/Boolean.tsx | 11 +++++++++-- lib/components/src/controls/Date.tsx | 11 +++++++++-- lib/components/src/controls/Files.tsx | 2 ++ lib/components/src/controls/Number.tsx | 3 ++- lib/components/src/controls/Range.tsx | 3 ++- lib/components/src/controls/Text.tsx | 11 +++++++++-- lib/components/src/controls/options/Checkbox.tsx | 2 ++ lib/components/src/controls/options/Radio.tsx | 10 +++++++++- lib/components/src/controls/types.ts | 1 + 10 files changed, 50 insertions(+), 10 deletions(-) diff --git a/lib/components/src/blocks/ArgsTable/ArgControl.tsx b/lib/components/src/blocks/ArgsTable/ArgControl.tsx index 9140f96eaaf6..5b08c8c8d47d 100644 --- a/lib/components/src/blocks/ArgsTable/ArgControl.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgControl.tsx @@ -22,7 +22,7 @@ const NoControl = () => <>-; export const ArgControl: FC = ({ row, arg, updateArgs }) => { const { key, control } = row; - + console.log('row', row); const [isFocused, setFocused] = useState(false); // box because arg can be a fn (e.g. actions) and useState calls fn's const [boxedValue, setBoxedValue] = useState({ value: arg }); @@ -43,6 +43,10 @@ export const ArgControl: FC = ({ row, arg, updateArgs }) => { const onBlur = useCallback(() => setFocused(false), []); const onFocus = useCallback(() => setFocused(true), []); + if (control?.disable && control?.readonly) { + console.warn(`Both "disable" and "readonly" options were defined, applying "disable".`); + } + if (!control || control.disable) return ; // row.name is a display name and not a suitable DOM input id or name - i might contain whitespace etc. diff --git a/lib/components/src/controls/Boolean.tsx b/lib/components/src/controls/Boolean.tsx index 39be3869f453..4b41a46ee7de 100644 --- a/lib/components/src/controls/Boolean.tsx +++ b/lib/components/src/controls/Boolean.tsx @@ -80,14 +80,21 @@ const format = (value: BooleanValue): string | null => (value ? String(value) : const parse = (value: string | null) => value === 'true'; export type BooleanProps = ControlProps & BooleanConfig; -export const BooleanControl: FC = ({ name, value, onChange, onBlur, onFocus }) => ( +export const BooleanControl: FC = ({ + name, + value, + onChange, + onBlur, + onFocus, + readonly, +}) => ( diff --git a/lib/components/src/controls/options/Radio.tsx b/lib/components/src/controls/options/Radio.tsx index 0078c6c43d49..fe02d3220ada 100644 --- a/lib/components/src/controls/options/Radio.tsx +++ b/lib/components/src/controls/options/Radio.tsx @@ -47,7 +47,14 @@ const Label = styled.label({ type RadioConfig = NormalizedOptionsConfig & { isInline: boolean }; type RadioProps = ControlProps & RadioConfig; -export const RadioControl: FC = ({ name, options, value, onChange, isInline }) => { +export const RadioControl: FC = ({ + name, + options, + value, + onChange, + isInline, + readonly, +}) => { const selection = selectedKey(value, options); return ( @@ -62,6 +69,7 @@ export const RadioControl: FC = ({ name, options, value, onChange, i value={key} onChange={(e) => onChange(options[e.currentTarget.value])} checked={key === selection} + {...{ readonly }} /> {key} diff --git a/lib/components/src/controls/types.ts b/lib/components/src/controls/types.ts index 20bf32df2a87..d4b30095648a 100644 --- a/lib/components/src/controls/types.ts +++ b/lib/components/src/controls/types.ts @@ -9,6 +9,7 @@ export interface ControlProps { onChange: (value: T) => T | void; onFocus?: (evt: any) => void; onBlur?: (evt: any) => void; + readonly?: boolean; } export type ArrayValue = string[] | readonly string[]; From 593002c14c789afd8653aaad97eb77b1bff86ce8 Mon Sep 17 00:00:00 2001 From: Dhenain Ambroise Date: Sat, 27 Feb 2021 13:55:02 +0100 Subject: [PATCH 2/6] Add readonly input to controls stories --- .../official-storybook/stories/addon-controls.stories.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/official-storybook/stories/addon-controls.stories.tsx b/examples/official-storybook/stories/addon-controls.stories.tsx index 0d32c3e8c45c..d798bc0e16ae 100644 --- a/examples/official-storybook/stories/addon-controls.stories.tsx +++ b/examples/official-storybook/stories/addon-controls.stories.tsx @@ -6,6 +6,13 @@ export default { component: Button, argTypes: { children: { control: 'text', name: 'Children' }, + readonlyInput: { + control: 'text', + name: 'readonlyInput', + controls: { + readonly: true, + }, + }, type: { control: 'text', name: 'Type' }, json: { control: 'object', name: 'JSON' }, imageUrls: { control: { type: 'file', accept: '.png' }, name: 'Image Urls' }, From cb7c7f37c14347a385252687cc5baa5e70f6df88 Mon Sep 17 00:00:00 2001 From: Dhenain Ambroise Date: Sat, 27 Feb 2021 14:06:53 +0100 Subject: [PATCH 3/6] Typo (misc) --- .../common/component-story-disable-controls.js.mdx | 4 ++-- .../common/component-story-disable-controls.mdx.mdx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/snippets/common/component-story-disable-controls.js.mdx b/docs/snippets/common/component-story-disable-controls.js.mdx index 818ea786e7bf..2b93fd6e9e54 100644 --- a/docs/snippets/common/component-story-disable-controls.js.mdx +++ b/docs/snippets/common/component-story-disable-controls.js.mdx @@ -10,9 +10,9 @@ export default { // foo is the property we want to remove from the UI foo:{ table:{ - disable:true + disable: true } } } }; -``` \ No newline at end of file +``` diff --git a/docs/snippets/common/component-story-disable-controls.mdx.mdx b/docs/snippets/common/component-story-disable-controls.mdx.mdx index f1864d3068e2..e503f6a14055 100644 --- a/docs/snippets/common/component-story-disable-controls.mdx.mdx +++ b/docs/snippets/common/component-story-disable-controls.mdx.mdx @@ -5,12 +5,12 @@ import { Meta,Story, Canvas } from '@storybook/addon-docs/blocks'; import { YourComponent } from './your-component' - @@ -22,4 +22,4 @@ export const Template = (args) => {Template.bind({})} -``` \ No newline at end of file +``` From ea2d0debff55e2fc319e9558d29839d0ba23da34 Mon Sep 17 00:00:00 2001 From: Dhenain Ambroise Date: Sat, 27 Feb 2021 14:08:33 +0100 Subject: [PATCH 4/6] Add readonly input to controls stories --- .../stories/addon-controls.stories.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/official-storybook/stories/addon-controls.stories.tsx b/examples/official-storybook/stories/addon-controls.stories.tsx index d798bc0e16ae..7ef459dc5f61 100644 --- a/examples/official-storybook/stories/addon-controls.stories.tsx +++ b/examples/official-storybook/stories/addon-controls.stories.tsx @@ -7,9 +7,9 @@ export default { argTypes: { children: { control: 'text', name: 'Children' }, readonlyInput: { - control: 'text', name: 'readonlyInput', - controls: { + control: { + type: 'text', readonly: true, }, }, @@ -142,3 +142,8 @@ FilteredWithExcludeRegex.parameters = { exclude: /hello*/, }, }; +export const Readonly = Template.bind({}); +Readonly.args = { + readonlyInput: 'This is read-only', +}; +Readonly.parameters = {}; From 861f863d7fb02c195dc2a703c64f15136c7f359f Mon Sep 17 00:00:00 2001 From: Dhenain Ambroise Date: Sat, 27 Feb 2021 14:19:21 +0100 Subject: [PATCH 5/6] Rename option to readOnly (avoids react warning) + add disabled input example --- .../stories/addon-controls.stories.tsx | 11 +++++++++-- lib/components/src/blocks/ArgsTable/ArgControl.tsx | 8 +++++--- lib/components/src/controls/Boolean.tsx | 4 ++-- lib/components/src/controls/Date.tsx | 4 ++-- lib/components/src/controls/Files.tsx | 4 ++-- lib/components/src/controls/Number.tsx | 4 ++-- lib/components/src/controls/Range.tsx | 4 ++-- lib/components/src/controls/Text.tsx | 4 ++-- lib/components/src/controls/options/Checkbox.tsx | 4 ++-- lib/components/src/controls/options/Radio.tsx | 4 ++-- lib/components/src/controls/types.ts | 2 +- 11 files changed, 31 insertions(+), 22 deletions(-) diff --git a/examples/official-storybook/stories/addon-controls.stories.tsx b/examples/official-storybook/stories/addon-controls.stories.tsx index 7ef459dc5f61..315da5ad30be 100644 --- a/examples/official-storybook/stories/addon-controls.stories.tsx +++ b/examples/official-storybook/stories/addon-controls.stories.tsx @@ -7,10 +7,17 @@ export default { argTypes: { children: { control: 'text', name: 'Children' }, readonlyInput: { - name: 'readonlyInput', + name: 'Readonly input', control: { type: 'text', - readonly: true, + readOnly: true, + }, + }, + disabledInput: { + name: 'Disabled input', + control: { + type: 'text', + disable: true, }, }, type: { control: 'text', name: 'Type' }, diff --git a/lib/components/src/blocks/ArgsTable/ArgControl.tsx b/lib/components/src/blocks/ArgsTable/ArgControl.tsx index 5b08c8c8d47d..694d2ffc49db 100644 --- a/lib/components/src/blocks/ArgsTable/ArgControl.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgControl.tsx @@ -22,7 +22,7 @@ const NoControl = () => <>-; export const ArgControl: FC = ({ row, arg, updateArgs }) => { const { key, control } = row; - console.log('row', row); + const [isFocused, setFocused] = useState(false); // box because arg can be a fn (e.g. actions) and useState calls fn's const [boxedValue, setBoxedValue] = useState({ value: arg }); @@ -43,8 +43,10 @@ export const ArgControl: FC = ({ row, arg, updateArgs }) => { const onBlur = useCallback(() => setFocused(false), []); const onFocus = useCallback(() => setFocused(true), []); - if (control?.disable && control?.readonly) { - console.warn(`Both "disable" and "readonly" options were defined, applying "disable".`); + if (control?.disable && control?.readOnly) { + console.warn( + `Both "disable" and "readOnly" options were defined. The "disable" option takes precedence.` + ); } if (!control || control.disable) return ; diff --git a/lib/components/src/controls/Boolean.tsx b/lib/components/src/controls/Boolean.tsx index 4b41a46ee7de..0eb9347d0055 100644 --- a/lib/components/src/controls/Boolean.tsx +++ b/lib/components/src/controls/Boolean.tsx @@ -86,7 +86,7 @@ export const BooleanControl: FC = ({ onChange, onBlur, onFocus, - readonly, + readOnly, }) => ( ); diff --git a/lib/components/src/controls/Range.tsx b/lib/components/src/controls/Range.tsx index 1475048d1595..3040e530861b 100644 --- a/lib/components/src/controls/Range.tsx +++ b/lib/components/src/controls/Range.tsx @@ -160,7 +160,7 @@ export const RangeControl: FC = ({ step = 1, onBlur, onFocus, - readonly, + readOnly, }) => { const handleChange = (event: ChangeEvent) => { onChange(parse(event.target.value)); @@ -171,7 +171,7 @@ export const RangeControl: FC = ({ {`${value} / ${max}`} diff --git a/lib/components/src/controls/Text.tsx b/lib/components/src/controls/Text.tsx index c424ec6f7e04..b246c45cf55a 100644 --- a/lib/components/src/controls/Text.tsx +++ b/lib/components/src/controls/Text.tsx @@ -18,7 +18,7 @@ export const TextControl: FC = ({ onChange, onFocus, onBlur, - readonly, + readOnly, }) => { const handleChange = (event: ChangeEvent) => { onChange(event.target.value); @@ -30,7 +30,7 @@ export const TextControl: FC = ({ onChange={handleChange} size="flex" placeholder="Adjust string dynamically" - {...{ name, value: format(value), onFocus, onBlur, readonly }} + {...{ name, value: format(value), onFocus, onBlur, readOnly }} /> ); diff --git a/lib/components/src/controls/options/Checkbox.tsx b/lib/components/src/controls/options/Checkbox.tsx index aaad00620fb7..7cf4956e9b44 100644 --- a/lib/components/src/controls/options/Checkbox.tsx +++ b/lib/components/src/controls/options/Checkbox.tsx @@ -47,7 +47,7 @@ export const CheckboxControl: FC = ({ value, onChange, isInline, - readonly, + readOnly, }) => { const initial = selectedKeys(value, options); const [selected, setSelected] = useState(initial); @@ -77,7 +77,7 @@ export const CheckboxControl: FC = ({ value={key} onChange={handleChange} checked={selected?.includes(key)} - {...{ readonly }} + {...{ readOnly }} /> {key} diff --git a/lib/components/src/controls/options/Radio.tsx b/lib/components/src/controls/options/Radio.tsx index fe02d3220ada..8e863652a0bd 100644 --- a/lib/components/src/controls/options/Radio.tsx +++ b/lib/components/src/controls/options/Radio.tsx @@ -53,7 +53,7 @@ export const RadioControl: FC = ({ value, onChange, isInline, - readonly, + readOnly, }) => { const selection = selectedKey(value, options); return ( @@ -69,7 +69,7 @@ export const RadioControl: FC = ({ value={key} onChange={(e) => onChange(options[e.currentTarget.value])} checked={key === selection} - {...{ readonly }} + {...{ readOnly }} /> {key} diff --git a/lib/components/src/controls/types.ts b/lib/components/src/controls/types.ts index d4b30095648a..9f7195736f14 100644 --- a/lib/components/src/controls/types.ts +++ b/lib/components/src/controls/types.ts @@ -9,7 +9,7 @@ export interface ControlProps { onChange: (value: T) => T | void; onFocus?: (evt: any) => void; onBlur?: (evt: any) => void; - readonly?: boolean; + readOnly?: boolean; } export type ArrayValue = string[] | readonly string[]; From 1cd26fdfe4d160bc095f9f831dcb839c9c390453 Mon Sep 17 00:00:00 2001 From: Dhenain Ambroise Date: Sat, 27 Feb 2021 14:35:27 +0100 Subject: [PATCH 6/6] Add documentation --- docs/essentials/controls.md | 13 +++++++++- .../component-story-readonly-controls.js.mdx | 18 +++++++++++++ .../component-story-readonly-controls.mdx.mdx | 25 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 docs/snippets/common/component-story-readonly-controls.js.mdx create mode 100644 docs/snippets/common/component-story-readonly-controls.mdx.mdx diff --git a/docs/essentials/controls.md b/docs/essentials/controls.md index af9acc9e40e5..6ff5f8c831ce 100644 --- a/docs/essentials/controls.md +++ b/docs/essentials/controls.md @@ -195,7 +195,7 @@ Here is the full list of available controls you can use: | | color | color picker input that assumes strings are color values | - | | | date | date picker input | - | -If you need to customize a control to use a enum data type in your story, for instance the `inline-radio` you can do it like so: +If you need to customize a control to use an enum data type in your story, for instance the `inline-radio` you can do it like so: @@ -235,6 +235,8 @@ If you don't provide a specific one, it defaults to the number control type. Controls supports the following configuration [parameters](../writing-stories/parameters.md), either globally or on a per-story basis: +> NOTE: `parameters` uses `controls`, while `argTypes` uses `control`. + ## Show full documentation for each property Since Controls is built on the same engine as Storybook Docs, it can also show property documentation alongside your controls using the expanded parameter (defaults to false). This means you embed a complete [ArgsTable](../writing-docs/doc-blocks.md#argstable) doc block in the controls pane. The description and default value rendering can be [customized](#fully-custom-args) in the same way as the doc block. @@ -296,6 +298,15 @@ As with other Storybook properties, such as [decorators](../writing-stories/deco +### Read-only controls for specific properties + +Alternatively, if you wish to still display the control but disallow edits, you can use the `readOnly` option. + + + ## Hide NoControls warning If you don't plan to handle the control args inside your Story, you can remove the warning with: diff --git a/docs/snippets/common/component-story-readonly-controls.js.mdx b/docs/snippets/common/component-story-readonly-controls.js.mdx new file mode 100644 index 000000000000..c1c852b958ef --- /dev/null +++ b/docs/snippets/common/component-story-readonly-controls.js.mdx @@ -0,0 +1,18 @@ +```js +// YourComponent.stories.js | YourComponent.stories.ts + +import { YourComponent } from './your-component' + +export default { + component: YourComponent, + title:'My Story', + argTypes:{ + // foo is the property we want to make readonly on the UI + foo:{ + control:{ + readOnly: true + } + } + } +}; +``` diff --git a/docs/snippets/common/component-story-readonly-controls.mdx.mdx b/docs/snippets/common/component-story-readonly-controls.mdx.mdx new file mode 100644 index 000000000000..09053497bb03 --- /dev/null +++ b/docs/snippets/common/component-story-readonly-controls.mdx.mdx @@ -0,0 +1,25 @@ +```md + + +import { Meta,Story, Canvas } from '@storybook/addon-docs/blocks'; + +import { YourComponent } from './your-component' + + + +export const Template = (args) => + + + + {Template.bind({})} + + +```