diff --git a/code/addons/docs/src/blocks/blocks/Controls.tsx b/code/addons/docs/src/blocks/blocks/Controls.tsx index 5f7e9eee69fa..0b96cb4d53f6 100644 --- a/code/addons/docs/src/blocks/blocks/Controls.tsx +++ b/code/addons/docs/src/blocks/blocks/Controls.tsx @@ -75,6 +75,7 @@ export const Controls: FC = (props) => { globals={globals} updateArgs={updateArgs} resetArgs={resetArgs} + idPrefix={story.id} /> ); } @@ -101,6 +102,7 @@ export const Controls: FC = (props) => { globals={globals} updateArgs={updateArgs} resetArgs={resetArgs} + idPrefix={story.id} /> ); }; diff --git a/code/addons/docs/src/blocks/components/ArgsTable/ArgControl.tsx b/code/addons/docs/src/blocks/components/ArgsTable/ArgControl.tsx index dd6f7a997544..f9ac8cbe992a 100644 --- a/code/addons/docs/src/blocks/components/ArgsTable/ArgControl.tsx +++ b/code/addons/docs/src/blocks/components/ArgsTable/ArgControl.tsx @@ -21,6 +21,11 @@ export interface ArgControlProps { arg: any; updateArgs: (args: Args) => void; isHovered: boolean; + /** + * Optional prefix to ensure unique control IDs when multiple Controls blocks + * are rendered on the same page. Typically the story ID. + */ + idPrefix?: string; } const Controls: Record> = { @@ -43,7 +48,7 @@ const Controls: Record> = { const NoControl = () => <>-; -export const ArgControl: FC = ({ row, arg, updateArgs, isHovered }) => { +export const ArgControl: FC = ({ row, arg, updateArgs, isHovered, idPrefix }) => { const { key, control } = row; const [isFocused, setFocused] = useState(false); @@ -84,7 +89,7 @@ export const ArgControl: FC = ({ row, arg, updateArgs, isHovere } // 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 }; + const props = { name: key, argType: row, value: boxedValue.value, onChange, onBlur, onFocus, idPrefix }; const Control = Controls[control.type] || NoControl; return ; }; diff --git a/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.tsx b/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.tsx index c6ccb0099ae2..2fdf2a66e1b7 100644 --- a/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.tsx +++ b/code/addons/docs/src/blocks/components/ArgsTable/ArgRow.tsx @@ -21,6 +21,11 @@ interface ArgRowProps { compact?: boolean; expandable?: boolean; initialExpandedArgs?: boolean; + /** + * Optional prefix to ensure unique control IDs when multiple Controls blocks + * are rendered on the same page. Typically the story ID. + */ + idPrefix?: string; } const Name = styled.span({ fontWeight: 'bold' }); diff --git a/code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx b/code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx index 874edbc2ccc2..ddb870e52e1d 100644 --- a/code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx +++ b/code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx @@ -204,6 +204,11 @@ export interface ArgsTableOptionProps { initialExpandedArgs?: boolean; isLoading?: boolean; sort?: SortType; + /** + * Optional prefix to ensure unique control IDs when multiple Controls blocks + * are rendered on the same page. Typically the story ID. + */ + idPrefix?: string; } interface ArgsTableDataProps { rows: ArgTypes; @@ -327,6 +332,7 @@ export const ArgsTable: FC = (props) => { initialExpandedArgs, sort = 'none', isLoading, + idPrefix, } = props; if ('error' in props) { @@ -380,7 +386,7 @@ export const ArgsTable: FC = (props) => { } const expandable = Object.keys(groups.sections).length > 0; - const common = { updateArgs, compact, inAddonPanel, initialExpandedArgs }; + const common = { updateArgs, compact, inAddonPanel, initialExpandedArgs, idPrefix }; return ( diff --git a/code/addons/docs/src/blocks/controls/Boolean.tsx b/code/addons/docs/src/blocks/controls/Boolean.tsx index 584ed2a6d0e0..cb46a228fa72 100644 --- a/code/addons/docs/src/blocks/controls/Boolean.tsx +++ b/code/addons/docs/src/blocks/controls/Boolean.tsx @@ -121,6 +121,7 @@ export const BooleanControl: FC = ({ onBlur, onFocus, argType, + idPrefix, }) => { const onSetFalse = useCallback(() => onChange(false), [onChange]); const readonly = !!argType?.table?.readonly; @@ -130,7 +131,7 @@ export const BooleanControl: FC = ({ ariaLabel={false} variant="outline" size="medium" - id={getControlSetterButtonId(name)} + id={getControlSetterButtonId(name, idPrefix)} onClick={onSetFalse} disabled={readonly} > @@ -138,7 +139,7 @@ export const BooleanControl: FC = ({ ); } - const controlId = getControlId(name); + const controlId = getControlId(name, idPrefix); const parsedValue = typeof value === 'string' ? parse(value) : value; diff --git a/code/addons/docs/src/blocks/controls/Color.tsx b/code/addons/docs/src/blocks/controls/Color.tsx index 3c3610c21621..851b2ff7baa0 100644 --- a/code/addons/docs/src/blocks/controls/Color.tsx +++ b/code/addons/docs/src/blocks/controls/Color.tsx @@ -367,6 +367,7 @@ export const ColorControl: FC = ({ presetColors, startOpen = false, argType, + idPrefix, }) => { const debouncedOnChange = useCallback(debounce(onChange, 200), [onChange]); const { value, realValue, updateValue, color, colorSpace, cycleColorSpace } = useColorInput( @@ -377,7 +378,7 @@ export const ColorControl: FC = ({ const Picker = ColorPicker[colorSpace]; const readOnly = !!argType?.table?.readonly; - const controlId = getControlId(name); + const controlId = getControlId(name, idPrefix); return ( diff --git a/code/addons/docs/src/blocks/controls/Date.tsx b/code/addons/docs/src/blocks/controls/Date.tsx index 3568cf940f5c..11ee05f5641e 100644 --- a/code/addons/docs/src/blocks/controls/Date.tsx +++ b/code/addons/docs/src/blocks/controls/Date.tsx @@ -66,7 +66,15 @@ const FlexSpaced = styled.fieldset({ }); export type DateProps = ControlProps & DateConfig; -export const DateControl: FC = ({ name, value, onChange, onFocus, onBlur, argType }) => { +export const DateControl: FC = ({ + name, + value, + onChange, + onFocus, + onBlur, + argType, + idPrefix, +}) => { const [valid, setValid] = useState(true); const dateRef = useRef(); const timeRef = useRef(); @@ -114,7 +122,7 @@ export const DateControl: FC = ({ name, value, onChange, onFocus, onB setValid(!!time); }; - const controlId = getControlId(name); + const controlId = getControlId(name, idPrefix); return ( diff --git a/code/addons/docs/src/blocks/controls/Files.tsx b/code/addons/docs/src/blocks/controls/Files.tsx index 1569790641da..515df2292024 100644 --- a/code/addons/docs/src/blocks/controls/Files.tsx +++ b/code/addons/docs/src/blocks/controls/Files.tsx @@ -43,6 +43,7 @@ export const FilesControl: FC = ({ accept = 'image/*', value, argType, + idPrefix, }) => { const inputElement = useRef(null); const readonly = argType?.control?.readOnly; @@ -63,7 +64,7 @@ export const FilesControl: FC = ({ } }, [value, name]); - const controlId = getControlId(name); + const controlId = getControlId(name, idPrefix); return ( <> diff --git a/code/addons/docs/src/blocks/controls/Number.tsx b/code/addons/docs/src/blocks/controls/Number.tsx index 947a9f7afca7..157b075d1a34 100644 --- a/code/addons/docs/src/blocks/controls/Number.tsx +++ b/code/addons/docs/src/blocks/controls/Number.tsx @@ -35,6 +35,7 @@ export const NumberControl: FC = ({ onBlur, onFocus, argType, + idPrefix, }) => { const [inputValue, setInputValue] = useState(typeof value === 'number' ? value : ''); const [forceVisible, setForceVisible] = useState(false); @@ -102,7 +103,7 @@ export const NumberControl: FC = ({ ariaLabel={false} variant="outline" size="medium" - id={getControlSetterButtonId(name)} + id={getControlSetterButtonId(name, idPrefix)} onClick={onForceVisible} disabled={readonly} > @@ -115,7 +116,7 @@ export const NumberControl: FC = ({ JsonTreeProps['getStyle'] = (the }, }); -export const ObjectControl: FC = ({ name, value, onChange, argType }) => { +export const ObjectControl: FC = ({ name, value, onChange, argType, idPrefix }) => { const theme = useTheme(); const data = useMemo(() => value && cloneDeep(value), [value]); const hasData = data !== null && data !== undefined; @@ -217,7 +217,7 @@ export const ObjectControl: FC = ({ name, value, onChange, argType