Skip to content

Commit

Permalink
feat(Multiselect): Add freeSolo for custom text in multiselect (#158)
Browse files Browse the repository at this point in the history
  • Loading branch information
cogwirrel authored Apr 8, 2021
1 parent 50f38ac commit 0bfac31
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 3 deletions.
15 changes: 15 additions & 0 deletions src/components/Multiselect/Multiselect.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,19 @@ import { awsServices } from '../Autosuggest/data/data';
]}
/>
</FormField>
```

Free Solo (Allowing custom text)
```jsx
import FormField from 'aws-northstar/components/FormField';
import { awsServices } from '../Autosuggest/data/data';

<FormField label="Form field label" controlId="formFieldId3">
<Multiselect
options={awsServices}
controlId="formFieldId3"
ariaDescribedby="This is a description"
freeSolo
/>
</FormField>
```
14 changes: 14 additions & 0 deletions src/components/Multiselect/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,17 @@ export const AsyncLoading = () => {
</FormField>
);
};

export const FreeSolo = () => (
<FormField label="Form field label" controlId="formFieldId1">
<Multiselect
options={awsServices}
controlId="formFieldId1"
ariaDescribedby="This is a description"
onChange={action('onChange')}
onFocus={action('onFocus')}
onBlur={action('onBlur')}
freeSolo
/>
</FormField>
);
14 changes: 14 additions & 0 deletions src/components/Multiselect/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,20 @@ describe('MultiSelect', () => {
expect(container.querySelector('.MuiChip-label')).toHaveTextContent('Aoption 2');
});

it('should render the entered text as a token in freeSolo mode', () => {
const handleChange = jest.fn();
const { getByPlaceholderText, container } = render(
<Multiselect options={options} placeholder="input-1" onChange={handleChange} freeSolo />
);
const input = getByPlaceholderText('input-1');

fireEvent.change(input, { target: { value: 'Free Solo Text' } });
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith([{ value: 'Free Solo Text', label: 'Free Solo Text' }]);
expect(container.querySelector('.MuiChip-label')).toHaveTextContent('Free Solo Text');
});

it('should render multiple selected options as tokens', () => {
const handleChange = jest.fn();
const { getByText, getByTitle, container } = render(
Expand Down
15 changes: 12 additions & 3 deletions src/components/Multiselect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export interface MultiselectProps extends SelectBaseProps, AriaBaseProps {
* The default value is false, but we highly recommend to set this to true.
*/
checkboxes?: boolean;
/**
* If `true`, the Multiselect is free solo, meaning that the user input is not bound to provided options.
*/
freeSolo?: boolean;
/** Callback fired when the value changes. */
onChange?: (value: SelectOption[]) => void;
/** Callback fired when the input value changes. */
Expand Down Expand Up @@ -91,6 +95,7 @@ const Multiselect: FunctionComponent<MultiselectProps> = ({
placeholder,
invalid,
checkboxes = false,
freeSolo = false,
ariaRequired = false,
ariaDescribedby,
ariaLabelledby,
Expand Down Expand Up @@ -138,9 +143,12 @@ const Multiselect: FunctionComponent<MultiselectProps> = ({
}, [options]);

const handleOnChange = useCallback(
(event: React.ChangeEvent<{}>, values: SelectOption[] | null): void => {
onChange(values || []);
setInputValue(values || []);
(event: React.ChangeEvent<{}>, values: (string | SelectOption)[] | null): void => {
const valuesAsOptions: SelectOption[] = (values || []).map((value) =>
typeof value === 'string' ? { value, label: value } : value
);
onChange(valuesAsOptions);
setInputValue(valuesAsOptions);
},
[onChange, setInputValue]
);
Expand Down Expand Up @@ -242,6 +250,7 @@ const Multiselect: FunctionComponent<MultiselectProps> = ({
autoHighlight
popupIcon={null}
id={controlId}
freeSolo={freeSolo}
noOptionsText={empty}
value={inputValue}
getOptionSelected={(opt: any, v: any) => opt.value === v.value}
Expand Down

0 comments on commit 0bfac31

Please sign in to comment.