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
2 changes: 2 additions & 0 deletions code/addons/docs/src/blocks/blocks/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const Controls: FC<ControlsProps> = (props) => {
globals={globals}
updateArgs={updateArgs}
resetArgs={resetArgs}
idPrefix={story.id}
/>
);
}
Expand All @@ -101,6 +102,7 @@ export const Controls: FC<ControlsProps> = (props) => {
globals={globals}
updateArgs={updateArgs}
resetArgs={resetArgs}
idPrefix={story.id}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, FC<any>> = {
Expand All @@ -43,7 +48,7 @@ const Controls: Record<string, FC<any>> = {

const NoControl = () => <>-</>;

export const ArgControl: FC<ArgControlProps> = ({ row, arg, updateArgs, isHovered }) => {
export const ArgControl: FC<ArgControlProps> = ({ row, arg, updateArgs, isHovered, idPrefix }) => {
const { key, control } = row;

const [isFocused, setFocused] = useState(false);
Expand Down Expand Up @@ -84,7 +89,7 @@ export const ArgControl: FC<ArgControlProps> = ({ 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 <Control {...props} {...control} controlType={control.type} />;
};
5 changes: 5 additions & 0 deletions code/addons/docs/src/blocks/components/ArgsTable/ArgRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -327,6 +332,7 @@ export const ArgsTable: FC<ArgsTableProps> = (props) => {
initialExpandedArgs,
sort = 'none',
isLoading,
idPrefix,
} = props;

if ('error' in props) {
Expand Down Expand Up @@ -380,7 +386,7 @@ export const ArgsTable: FC<ArgsTableProps> = (props) => {
}
const expandable = Object.keys(groups.sections).length > 0;

const common = { updateArgs, compact, inAddonPanel, initialExpandedArgs };
const common = { updateArgs, compact, inAddonPanel, initialExpandedArgs, idPrefix };

return (
<ResetWrapper>
Expand Down
5 changes: 3 additions & 2 deletions code/addons/docs/src/blocks/controls/Boolean.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export const BooleanControl: FC<BooleanProps> = ({
onBlur,
onFocus,
argType,
idPrefix,
}) => {
const onSetFalse = useCallback(() => onChange(false), [onChange]);
const readonly = !!argType?.table?.readonly;
Expand All @@ -130,15 +131,15 @@ export const BooleanControl: FC<BooleanProps> = ({
ariaLabel={false}
variant="outline"
size="medium"
id={getControlSetterButtonId(name)}
id={getControlSetterButtonId(name, idPrefix)}
onClick={onSetFalse}
disabled={readonly}
>
Set boolean
</Button>
);
}
const controlId = getControlId(name);
const controlId = getControlId(name, idPrefix);

const parsedValue = typeof value === 'string' ? parse(value) : value;

Expand Down
3 changes: 2 additions & 1 deletion code/addons/docs/src/blocks/controls/Color.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ export const ColorControl: FC<ColorControlProps> = ({
presetColors,
startOpen = false,
argType,
idPrefix,
}) => {
const debouncedOnChange = useCallback(debounce(onChange, 200), [onChange]);
const { value, realValue, updateValue, color, colorSpace, cycleColorSpace } = useColorInput(
Expand All @@ -377,7 +378,7 @@ export const ColorControl: FC<ColorControlProps> = ({
const Picker = ColorPicker[colorSpace];

const readOnly = !!argType?.table?.readonly;
const controlId = getControlId(name);
const controlId = getControlId(name, idPrefix);

return (
<Wrapper>
Expand Down
12 changes: 10 additions & 2 deletions code/addons/docs/src/blocks/controls/Date.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,15 @@ const FlexSpaced = styled.fieldset({
});

export type DateProps = ControlProps<DateValue> & DateConfig;
export const DateControl: FC<DateProps> = ({ name, value, onChange, onFocus, onBlur, argType }) => {
export const DateControl: FC<DateProps> = ({
name,
value,
onChange,
onFocus,
onBlur,
argType,
idPrefix,
}) => {
const [valid, setValid] = useState(true);
const dateRef = useRef<HTMLInputElement>();
const timeRef = useRef<HTMLInputElement>();
Expand Down Expand Up @@ -114,7 +122,7 @@ export const DateControl: FC<DateProps> = ({ name, value, onChange, onFocus, onB
setValid(!!time);
};

const controlId = getControlId(name);
const controlId = getControlId(name, idPrefix);

return (
<FlexSpaced>
Expand Down
3 changes: 2 additions & 1 deletion code/addons/docs/src/blocks/controls/Files.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const FilesControl: FC<FilesControlProps> = ({
accept = 'image/*',
value,
argType,
idPrefix,
}) => {
const inputElement = useRef<HTMLInputElement>(null);
const readonly = argType?.control?.readOnly;
Expand All @@ -63,7 +64,7 @@ export const FilesControl: FC<FilesControlProps> = ({
}
}, [value, name]);

const controlId = getControlId(name);
const controlId = getControlId(name, idPrefix);

return (
<>
Expand Down
5 changes: 3 additions & 2 deletions code/addons/docs/src/blocks/controls/Number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const NumberControl: FC<NumberProps> = ({
onBlur,
onFocus,
argType,
idPrefix,
}) => {
const [inputValue, setInputValue] = useState(typeof value === 'number' ? value : '');
const [forceVisible, setForceVisible] = useState(false);
Expand Down Expand Up @@ -102,7 +103,7 @@ export const NumberControl: FC<NumberProps> = ({
ariaLabel={false}
variant="outline"
size="medium"
id={getControlSetterButtonId(name)}
id={getControlSetterButtonId(name, idPrefix)}
onClick={onForceVisible}
disabled={readonly}
>
Expand All @@ -115,7 +116,7 @@ export const NumberControl: FC<NumberProps> = ({
<Wrapper>
<FormInput
ref={htmlElRef}
id={getControlId(name)}
id={getControlId(name, idPrefix)}
type="number"
onChange={handleChange}
size="flex"
Expand Down
6 changes: 3 additions & 3 deletions code/addons/docs/src/blocks/controls/Object.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ const getCustomStyleFunction: (theme: Theme) => JsonTreeProps['getStyle'] = (the
},
});

export const ObjectControl: FC<ObjectProps> = ({ name, value, onChange, argType }) => {
export const ObjectControl: FC<ObjectProps> = ({ name, value, onChange, argType, idPrefix }) => {
const theme = useTheme();
const data = useMemo(() => value && cloneDeep(value), [value]);
const hasData = data !== null && data !== undefined;
Expand Down Expand Up @@ -217,7 +217,7 @@ export const ObjectControl: FC<ObjectProps> = ({ name, value, onChange, argType
<Button
ariaLabel={false}
disabled={readonly}
id={getControlSetterButtonId(name)}
id={getControlSetterButtonId(name, idPrefix)}
onClick={onForceVisible}
>
Set object
Expand All @@ -228,7 +228,7 @@ export const ObjectControl: FC<ObjectProps> = ({ name, value, onChange, argType
const rawJSONForm = (
<RawInput
ref={htmlElRef}
id={getControlId(name)}
id={getControlId(name, idPrefix)}
minRows={3}
name={name}
key={jsonString}
Expand Down
3 changes: 2 additions & 1 deletion code/addons/docs/src/blocks/controls/Range.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export const RangeControl: FC<RangeProps> = ({
onBlur,
onFocus,
argType,
idPrefix,
}) => {
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange(parse(event.target.value));
Expand All @@ -194,7 +195,7 @@ export const RangeControl: FC<RangeProps> = ({
const numberOFDecimalsPlaces = useMemo(() => getNumberOfDecimalPlaces(step), [step]);

const readonly = !!argType?.table?.readonly;
const controlId = getControlId(name);
const controlId = getControlId(name, idPrefix);

return (
<RangeWrapper readOnly={readonly}>
Expand Down
5 changes: 3 additions & 2 deletions code/addons/docs/src/blocks/controls/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const TextControl: FC<TextProps> = ({
onBlur,
maxLength,
argType,
idPrefix,
}) => {
const handleChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
onChange(event.target.value);
Expand All @@ -49,7 +50,7 @@ export const TextControl: FC<TextProps> = ({
variant="outline"
size="medium"
disabled={readonly}
id={getControlSetterButtonId(name)}
id={getControlSetterButtonId(name, idPrefix)}
onClick={onForceVisible}
>
Set string
Expand All @@ -62,7 +63,7 @@ export const TextControl: FC<TextProps> = ({
return (
<Wrapper>
<Form.Textarea
id={getControlId(name)}
id={getControlId(name, idPrefix)}
maxLength={maxLength}
onChange={handleChange}
disabled={readonly}
Expand Down
18 changes: 18 additions & 0 deletions code/addons/docs/src/blocks/controls/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ describe('getControlId', () => {
])('%s', (name, input, expected) => {
expect(getControlId(input)).toBe(expected);
});

it.each([
// caseName, input, prefix, expected
['with prefix', 'some-id', 'story-1', 'control-story-1-some-id'],
['with prefix and spaces', 'my prop', 'story-2', 'control-story-2-my-prop'],
['with undefined prefix', 'some-id', undefined, 'control-some-id'],
])('%s with prefix', (name, input, prefix, expected) => {
expect(getControlId(input, prefix)).toBe(expected);
});
});

describe('getControlSetterButtonId', () => {
Expand All @@ -22,4 +31,13 @@ describe('getControlSetterButtonId', () => {
])('%s', (name, input, expected) => {
expect(getControlSetterButtonId(input)).toBe(expected);
});

it.each([
// caseName, input, prefix, expected
['with prefix', 'some-id', 'story-1', 'set-story-1-some-id'],
['with prefix and spaces', 'my prop', 'story-2', 'set-story-2-my-prop'],
['with undefined prefix', 'some-id', undefined, 'set-some-id'],
])('%s with prefix', (name, input, prefix, expected) => {
expect(getControlSetterButtonId(input, prefix)).toBe(expected);
});
});
18 changes: 14 additions & 4 deletions code/addons/docs/src/blocks/controls/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
/**
* Adds `control` prefix to make ID attribute more specific. Removes spaces because spaces are not
* allowed in ID attributes
* allowed in ID attributes. Optionally accepts a prefix to ensure uniqueness when multiple
* Controls blocks are rendered on the same page.
*
* @example
*
* ```ts
* getControlId('my prop name') -> 'control-my-prop-name'
* getControlId('my prop name', 'story-1') -> 'control-story-1-my-prop-name'
* ```
*
* @link http://xahlee.info/js/html_allowed_chars_in_attribute.html
*/
export const getControlId = (value: string) => `control-${value.replace(/\s+/g, '-')}`;
export const getControlId = (value: string, idPrefix?: string) => {
const sanitizedValue = value.replace(/\s+/g, '-');
return idPrefix ? `control-${idPrefix}-${sanitizedValue}` : `control-${sanitizedValue}`;
};

/**
* Adds `set` prefix to make ID attribute more specific. Removes spaces because spaces are not
* allowed in ID attributes
* allowed in ID attributes. Optionally accepts a prefix to ensure uniqueness when multiple
* Controls blocks are rendered on the same page.
*
* @example
*
* ```ts
* getControlSetterButtonId('my prop name') -> 'set-my-prop-name'
* getControlSetterButtonId('my prop name', 'story-1') -> 'set-story-1-my-prop-name'
* ```
*
* @link http://xahlee.info/js/html_allowed_chars_in_attribute.html
*/
export const getControlSetterButtonId = (value: string) => `set-${value.replace(/\s+/g, '-')}`;
export const getControlSetterButtonId = (value: string, idPrefix?: string) => {
const sanitizedValue = value.replace(/\s+/g, '-');
return idPrefix ? `set-${idPrefix}-${sanitizedValue}` : `set-${sanitizedValue}`;
};
3 changes: 2 additions & 1 deletion code/addons/docs/src/blocks/controls/options/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export const CheckboxControl: FC<CheckboxProps> = ({
onChange,
isInline,
argType,
idPrefix,
}) => {
if (!options) {
logger.warn(`Checkbox with no options: ${name}`);
Expand Down Expand Up @@ -88,7 +89,7 @@ export const CheckboxControl: FC<CheckboxProps> = ({
setSelected(selectedKeys(value || [], options));
}, [value]);

const controlId = getControlId(name);
const controlId = getControlId(name, idPrefix);

return (
<Wrapper $isInline={isInline}>
Expand Down
3 changes: 2 additions & 1 deletion code/addons/docs/src/blocks/controls/options/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ export const RadioControl: FC<RadioProps> = ({
onChange,
isInline,
argType,
idPrefix,
}) => {
if (!options) {
logger.warn(`Radio with no options: ${name}`);
return <>-</>;
}
const selection = selectedKey(value, options);
const controlId = getControlId(name);
const controlId = getControlId(name, idPrefix);

const readonly = !!argType?.table?.readonly;

Expand Down
Loading
Loading