Blueprint

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, @hover/dropdown that depends on @hover/blueprint.


yarn add @hover/dropdown

npm

Theme

The theme for the Dropdown component must also be added to your BlueprintProvider.

App.tsx

import { BlueprintProvider } from '@hover/blueprint';
import { Dropdown } from '@hover/blueprint/extra/theme';
interface AppProps {
children: React.ReactNode;
}
const App = ({ children }: AppProps) => (
<BlueprintProvider extraComponentThemes={{ Dropdown }}>
{children}
</BlueprintProvider>
);
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.

Required Props

The itemToString and itemToValue props are required and determine what will be displayed and the value persisted in the internal form element

live

<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.

live

() => {
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.

live

() => {
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.

live

<Dropdown
inPortal
iconBefore={iList}
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.

live

<Dropdown
inPortal
iconBefore={iList}
itemToString={item => (item ? item.title : '')}
itemToValue={({ id }) => id}
items={[
{ title: 'One', id: 1, icon: iCpu },
{ title: 'Two', id: 2, icon: iCloud },
{ title: 'Three', id: 3, icon: iBox },
{ title: 'Four', id: 4, icon: iBriefcase },
]}
placeholder="Select..."
/>