Import
import { Multiselect } from '@contentful/f36-multiselect';
Examples
Basic usage
The Multiselect is a fully controlled form element and expects the selectable items as children via the compound component Multiselect.Option.
Properties are:
For the multiselect itself:
- currentSelection (optional but recommended): an array of the labels of selected options. This will give the user a hint that there are selected items. This is optional
- placeholder (optional): Label of the trigger button without selected elements
- startIcon (optional): Icon on the start of the trigger button
- isLoading: enables showing a loading animation while searching or loading results
Inherited propertys from Popover Component.
- listWidth (optional): auto or full
- listMaxHeight (optional): sets the maximum height of the list drawer, overflow will be scrolled
It also accepts refs for the toggle button, the searchInput and the list.
For the option:
- itemId: identifier for the element, used to link the label with the checkbox
- value: the actual value of the select option
- label: displayed label
- onSelectItem: This function is called when the user selects a option, it needs to be passed to the MultiselectOption
- isChecked (optional): controlls if a element is initially selected
- isDisabled (optional): controlls if a element is changeable
function MultiselectBasicUsageExample() {
const spaces = [
'Travel Blog',
'Finnance Blog',
'Fitness App',
'News Website',
'eCommerce Catalogue',
'Photo Gallery',
];
const [selectedSpaces, setSelectedSpaces] = React.useState([]);
const handleSelectItem = (event) => {
const { checked, value } = event.target;
if (checked) {
setSelectedSpaces((prevState) => [...prevState, value]);
} else {
const newSelectedSpaces = selectedSpaces.filter(
(space) => space !== value,
);
setSelectedSpaces(newSelectedSpaces);
}
};
return (
<Stack flexDirection="column" alignItems="start">
<Multiselect
currentSelection={selectedSpaces}
popoverProps={{ isFullWidth: true }}
>
{spaces.map((space) => {
const val = space.toLowerCase().replace(/\s/g, '-');
return (
<Multiselect.Option
key={`key-${val}}`}
itemId={`space-${val}}`}
value={space}
label={space}
onSelectItem={handleSelectItem}
isChecked={selectedSpaces.includes(space)}
/>
);
})}
</Multiselect>
</Stack>
);
}
Searchable options
One optional feature of the Multiselect component is to allow filtering the list via a search field.
To make it work, the onSearchValueChange callback function needs to be provided.
To enable searching the following properties have to be set.
- onSearchValueChange: Callbackfunction which enables the search field. This function needs to provide the search /filter algorithm
- searchPlaceholder (optional): placeholder for the search field
- noMatchesMessage (optional): message shown when search result is empty
function MultiselectSearchExample() {
const spaces = [
'Travel Blog',
'Finnance Blog',
'Fitness App',
'News Website',
'eCommerce Catalogue',
'Photo Gallery',
];
const [selectedItems, setSelectedItems] = React.useState([]);
const [filteredItems, setFilteredItems] = React.useState(spaces);
const handleSearchValueChange = (event) => {
const value = event.target.value;
const newFilteredItems = spaces.filter((item) =>
item.toLowerCase().includes(value.toLowerCase()),
);
setFilteredItems(newFilteredItems);
};
const handleSelectItem = (event) => {
const { checked, value } = event.target;
if (checked) {
setSelectedItems((prevState) => [...prevState, value]);
} else {
const newSelectedItems = selectedItems.filter((space) => space !== value);
setSelectedItems(newSelectedItems);
}
};
return (
<Stack flexDirection="column" alignItems="start">
<Multiselect
searchProps={{
searchPlaceholder: 'Search spaces',
onSearchValueChange: handleSearchValueChange,
}}
popoverProps={{ isFullWidth: true }}
currentSelection={selectedItems}
>
{filteredItems.map((item, index) => {
return (
<Multiselect.Option
value={item}
label={item}
onSelectItem={handleSelectItem}
key={`${item}-${index}`}
itemId={`${item}-${index}`}
isChecked={selectedItems.includes(item)}
isDisabled={item === 'eCommerce Catalogue'}
/>
);
})}
</Multiselect>
</Stack>
);
}
SelectAll Option
To offer a shortcut for selecting and deselecting all options, you can use the compound component SelectAll. This requires a callback function which needs to contain your implementation for selecting all options.
function MultiselectSelectAllExample() {
const spaces = React.useMemo(
() => [
'Travel Blog',
'Finnance Blog',
'Fitness App',
'News Website',
'eCommerce Catalogue',
'Photo Gallery',
],
[],
);
const [selectedSpaces, setSelectedSpaces] = React.useState([]);
const handleSelectItem = (event) => {
const { checked, value } = event.target;
if (checked) {
setSelectedSpaces((prevState) => [...prevState, value]);
} else {
setSelectedSpaces((prevState) =>
prevState.filter((space) => space !== value),
);
}
};
const toggleAll = (event) => {
const { checked } = event.target;
if (checked) {
setSelectedSpaces(spaces);
} else {
setSelectedSpaces([]);
}
};
const areAllSelected = React.useMemo(() => {
return spaces.every((element) => selectedSpaces.includes(element));
}, [selectedSpaces, spaces]);
return (
<Stack flexDirection="column" alignItems="start">
<Multiselect
currentSelection={selectedSpaces}
popoverProps={{ isFullWidth: true }}
>
<Multiselect.SelectAll
onSelectItem={toggleAll}
isChecked={areAllSelected}
/>
{spaces.map((space) => {
const val = space.toLowerCase().replace(/\s/g, '-');
return (
<Multiselect.Option
key={`key-${val}`}
itemId={`space-${val}`}
value={space}
label={space}
onSelectItem={handleSelectItem}
isChecked={selectedSpaces.includes(space)}
/>
);
})}
</Multiselect>
</Stack>
);
}
Controlling from the Outside
In some cases it is desired to toggle the status of the popover drawer as well as to clear the search field from the parent. E.g. after a change has been applied. For this the Component offers two optional reference properties toggleRef
and resetSearchRef
function MultiselectReferenceExample() {
const spaces = [
'Travel Blog',
'Finnance Blog',
'Fitness App',
'News Website',
'eCommerce Catalogue',
'Photo Gallery',
];
const [selectedItems, setSelectedItems] = React.useState([]);
const [filteredItems, setFilteredItems] = React.useState(spaces);
const togglePopOverRef = React.useRef(null);
const clearSearchFieldRef = React.useRef(null);
const handleSearchValueChange = (event) => {
const value = event.target.value;
const newFilteredItems = spaces.filter((item) =>
item.toLowerCase().includes(value.toLowerCase()),
);
setFilteredItems(newFilteredItems);
};
const handleSelectItem = (event) => {
const { checked, value } = event.target;
if (checked) {
setSelectedItems((prevState) => [...prevState, value]);
} else {
const newSelectedItems = selectedItems.filter((space) => space !== value);
setSelectedItems(newSelectedItems);
}
};
const handleExtToggle = () => {
togglePopOverRef.current.click();
};
const handleExtClearSearch = () => {
clearSearchFieldRef.current.click();
};
return (
<Stack flexDirection="column" alignItems="start">
<Button onClick={handleExtToggle}>Toggle Popover</Button>
<Button onClick={handleExtClearSearch}>Clear Search Field</Button>
<Multiselect
searchProps={{
searchPlaceholder: 'Search spaces',
onSearchValueChange: handleSearchValueChange,
resetSearchRef: clearSearchFieldRef,
}}
popoverProps={{ isFullWidth: true, closeOnBlur: false }}
currentSelection={selectedItems}
toggleRef={togglePopOverRef}
>
{filteredItems.map((item, index) => {
return (
<Multiselect.Option
value={item}
label={item}
onSelectItem={handleSelectItem}
key={`${item}-${index}`}
itemId={`${item}-${index}`}
isChecked={selectedItems.includes(item)}
isDisabled={item === 'eCommerce Catalogue'}
/>
);
})}
</Multiselect>
</Stack>
);
}
Content guidelines
- Multiselect placeholder should be short but descriptive.
- Multiselect options can come from a simple array of strings or more complex objects.
- Do not use the index position of the items in the filtered array for keys or ids, as they are going to change while filtering.
- Use any algorithm you like in order to search and filter. Depending on your implementation you can also generate a new request to your dataset based on the users input.
Accessibility
- When focussing the toggle button, the enter key opens it. The dropdown content automatically will be focussed
- Pressing the space bar toggles the checked state of an option and will trigger the onSelectItem callback function