diff --git a/lib/components/src/blocks/ArgsTable/ArgControl.tsx b/lib/components/src/blocks/ArgsTable/ArgControl.tsx index 9140f96eaaf6..eed086327b70 100644 --- a/lib/components/src/blocks/ArgsTable/ArgControl.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgControl.tsx @@ -18,22 +18,39 @@ export interface ArgControlProps { updateArgs: (args: Args) => void; } +const Controls: Record = { + array: ObjectControl, + object: ObjectControl, + boolean: BooleanControl, + color: ColorControl, + date: DateControl, + number: NumberControl, + check: OptionsControl, + 'inline-check': OptionsControl, + radio: OptionsControl, + 'inline-radio': OptionsControl, + select: OptionsControl, + 'multi-select': OptionsControl, + range: RangeControl, + text: TextControl, + file: FilesControl, +}; + const NoControl = () => <>-; export const ArgControl: FC = ({ row, arg, updateArgs }) => { const { key, control } = 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 }); + const [value, setValue] = useState(() => arg); useEffect(() => { - if (!isFocused) setBoxedValue({ value: arg }); + if (!isFocused) setValue(arg); }, [isFocused, arg]); const onChange = useCallback( (argVal: any) => { - setBoxedValue({ value: argVal }); + setValue(argVal); updateArgs({ [key]: argVal }); return argVal; }, @@ -47,33 +64,7 @@ export const ArgControl: FC = ({ row, arg, updateArgs }) => { // row.name is a display name and not a suitable DOM input id or name - i might contain whitespace etc. // row.key is a hash key and therefore a much safer choice - const props = { name: key, argType: row, value: boxedValue.value, onChange, onBlur, onFocus }; - switch (control.type) { - case 'array': - case 'object': - return ; - case 'boolean': - return ; - case 'color': - return ; - case 'date': - return ; - case 'number': - return ; - case 'check': - case 'inline-check': - case 'radio': - case 'inline-radio': - case 'select': - case 'multi-select': - return ; - case 'range': - return ; - case 'text': - return ; - case 'file': - return ; - default: - return ; - } + const props = { name: key, argType: row, value, onChange, onBlur, onFocus }; + const Control = Controls[control.type] || NoControl; + return ; }; diff --git a/lib/components/src/controls/options/Options.tsx b/lib/components/src/controls/options/Options.tsx index 9b88795c8814..26a86b4b09da 100644 --- a/lib/components/src/controls/options/Options.tsx +++ b/lib/components/src/controls/options/Options.tsx @@ -28,10 +28,25 @@ const normalizeOptions = (options: Options, labels?: Record) => { return options; }; +const Controls: Record = { + check: CheckboxControl, + 'inline-check': CheckboxControl, + radio: RadioControl, + 'inline-radio': RadioControl, + select: SelectControl, + 'multi-select': SelectControl, +}; + export type OptionsProps = ControlProps & OptionsConfig; export const OptionsControl: FC = (props) => { const { type = 'select', options, labels, argType } = props; - const normalized = { ...props, options: normalizeOptions(options || argType.options, labels) }; + const normalized = { + ...props, + options: normalizeOptions(options || argType.options, labels), + isInline: type.includes('inline'), + isMulti: type.includes('multi'), + }; + if (options) { once.warn(dedent` 'control.options' is deprecated and will be removed in Storybook 7.0. Define 'options' directly on the argType instead, and use 'control.labels' for custom labels. @@ -39,17 +54,10 @@ export const OptionsControl: FC = (props) => { More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#deprecated-controloptions `); } - switch (type) { - case 'check': - case 'inline-check': - return ; - case 'radio': - case 'inline-radio': - return ; - case 'select': - case 'multi-select': - return ; - default: - throw new Error(`Unknown options type: ${type}`); + + const Control = Controls[type]; + if (Control) { + return ; } + throw new Error(`Unknown options type: ${type}`); };