Dropdown
An accessible, custom alternative to the native Select component.
React
The Dropdown component provides a more flexible alternative to the native Select component to implement things like populating options from a search API request.
Multiple Selection
We currently only support selecting a single item, if you need multi-select support, please request it here.
Installation
The Dropdown component is available as a separate package, @hoverinc/dropdown that depends on @hoverinc/design-system-react-web.
yarn add @hoverinc/dropdownTheme
The theme for the Dropdown component must also be added to your DesignSystemProvider
import { DesignSystemProvider } from '@hoverinc/design-system-react-web';
import { Dropdown } from '@hoverinc/design-system-react-web/extra/theme';
interface AppProps {
children: React.ReactNode;
}
const App = ({ children }: AppProps) => (
<DesignSystemProvider extraComponentThemes={{ Dropdown }}>
{children}
</DesignSystemProvider>
);
export { App };Simple Select
Dropdown can behave just like a simple native Select component, albeit a
bit prettier. That said, a native Select might be
the right choice here. See below for use cases where Dropdown shines.
The inPortal prop tells Dropdown to open its menu in a
Portal in cases where overflow: hidden or
adjacent components may interfere with it.
<Dropdown
inPortal
itemToString={item => (item ? item.title : '')}
itemToValue={({ id }) => id}
items={[
{ title: 'One', id: 1 },
{ title: 'Two', id: 2 },
{ title: 'Three', id: 3 },
{ title: 'Four', id: 4 },
]}
placeholder="Select..."
/>Filtering
Use the onInputValue change prop to implement filtering on items.
() => {
const mockItems = mocks.dropdown;
const [items, setItems] = useState(mockItems);
return (
<Dropdown
inPortal
itemToString={item => (item ? item.title : '')}
itemToValue={({ id }) => id}
items={items}
onInputValueChange={({ inputValue }) => {
setItems(
inputValue
? mockItems.filter(({ title }) =>
title.toLowerCase().includes(inputValue.toLowerCase()),
)
: mockItems,
);
}}
placeholder="Filtering..."
/>
);
};Asynchronous
Set the isLoading prop to indicate the state of an asynchronous request when
populating items from an API.
() => {
const mockItems = mocks.dropdown;
const [isLoading, setIsLoading] = useState(false);
const [items, setItems] = useState(mockItems);
return (
<Dropdown
isLoading={isLoading}
inPortal
items={items}
itemToString={item => (item ? item.title : '')}
itemToValue={({ id }) => id}
onInputValueChange={({ inputValue }) => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false);
setItems(
inputValue
? mockItems.filter(({ title }) =>
title.toLowerCase().includes(inputValue.toLowerCase()),
)
: mockItems,
);
}, 500);
}}
placeholder="Asynchronous"
/>
);
};Icon
Like other form inputs, Dropdown supports displaying an icon in the input.
<Dropdown
inPortal
iconBefore={iconFilter}
itemToString={item => (item ? item.title : '')}
itemToValue={({ id }) => id}
items={[
{ title: 'One', id: 1 },
{ title: 'Two', id: 2 },
{ title: 'Three', id: 3 },
{ title: 'Four', id: 4 },
]}
placeholder="Select..."
/>Item Icons
Include a system icon as the icon property in items to render an icons in the
list.
<Dropdown
inPortal
iconBefore={iconFilter}
itemToString={item => (item ? item.title : '')}
itemToValue={({ id }) => id}
items={[
{ title: 'One', id: 1, icon: iconHeartFilled },
{ title: 'Two', id: 2, icon: iconUser },
{ title: 'Three', id: 3, icon: iconFile },
{ title: 'Four', id: 4, icon: iconExternalLink },
]}
placeholder="Select..."
/>Combobox
actAsComboboxkeeps the text input visible after selection and preserves the typed value on blur orEscape.
Autocomplete Mode (actAsCombobox)
Use actAsCombobox when the dropdown should behave like an autocomplete input.
() => {
const mockItems = mocks.dropdown;
const [items, setItems] = useState(mockItems);
return (
<Dropdown
actAsCombobox
inPortal
itemToString={item => (item ? item.title : '')}
itemToValue={({ id }) => id}
items={items}
onInputValueChange={({ inputValue }) => {
setItems(
inputValue
? mockItems.filter(({ title }) =>
title.toLowerCase().includes(inputValue.toLowerCase()),
)
: mockItems,
);
}}
placeholder="Search..."
/>
);
};