Demos#
Default autocomplete#
<Autocomplete data={topMovies} label="Label" />
Large data sets#
Virtualization is opt-in. Import the driver from @dnb/eufemia/fragments/drawer-list/Virtualization and pass it as listDriver. The default Autocomplete bundle does not import the virtualization dependency. Filtering and “Show all” continue to operate on the complete data set while only the visible options are rendered.
Install the optional peer dependency before using the driver:
yarn add @tanstack/react-virtual
import { Autocomplete } from '@dnb/eufemia/components'import { createDrawerListVirtualization } from '@dnb/eufemia/fragments/drawer-list/Virtualization'const listDriver = createDrawerListVirtualization()<Autocomplete data={items} listDriver={listDriver} />
<Autocomplete label="Search 10,000 items" data={virtualizedItems} listDriver={virtualizedList} showSubmitButton />
Autocomplete with numbers#
This example uses search={{ numbers: true }} to enable number-optimized matching.
<Autocomplete inputValue="201" showClearButton label="Label" data={numbersData} search={{ numbers: true, }} />
Search options#
Use the search prop to configure search behavior. This example uses match: 'starts-with' to only show results that begin with the typed text, and highlight: false to disable text highlighting.
<Autocomplete label="Search movies (starts-with)" data={topMovies} search={{ match: 'starts-with', highlight: false, }} />
Autocomplete with a custom title#
keepValuemeans the input value gets not removed after an input blur happens.showClearButtonmeans a clear button will show up when the input field contains a value.
<Autocomplete data={topMovies} keepValue={true} showClearButton={true} label="Label" placeholder="Custom placeholder ..." onChange={({ data }) => { console.log('onChange', data) }} />
Async usage, dynamically update data during typing#
This example simulates server delay with a timeout and - if it gets debounced, we cancel the timeout. Read more about the debounce method.
Also, you may consider using search={{ filter: false }} if you have a backend doing the search operation.
const onTypeHandler = ({ value, showIndicator, hideIndicator, updateData, showNoOptionsItem, debounce, /* ... */ }) => { console.log('typed value:', value) showIndicator() debounce( ({ value }) => { console.log('debounced value:', value) const normalizedValue = value.trim().toLowerCase() const filteredData = topMovies.filter(({ content }) => { if (typeof content === 'string') { return content.toLowerCase().includes(normalizedValue) } if (Array.isArray(content)) { return content .filter((part) => typeof part === 'string') .join(' ') .toLowerCase() .includes(normalizedValue) } return false }) const newData = normalizedValue.length > 0 ? filteredData : topMovies // simulate server delay const timeout = setTimeout(() => { // update the drawerList updateData(newData) hideIndicator() if (newData.length === 0) { showNoOptionsItem() } }, 600) // cancel invocation method return () => clearTimeout(timeout) }, { value, }, 250 ) } render( <Autocomplete mode="async" onType={onTypeHandler} noScrollAnimation={true} placeholder="Search ..." /> )
Update data dynamically on the first focus#
const onFocusHandler = ({ updateData, dataList, showIndicatorItem }) => { if (!dataList.length) { showIndicatorItem() setTimeout(() => { updateData(topMovies) }, 1e3) } } render( <Autocomplete mode="async" noScrollAnimation={true} preventSelection={true} onType={({ value /* updateData, ... */ }) => { console.log('onType', value) }} onFocus={onFocusHandler} /> )
With a Button to toggle the open / close state#
NB: Just to show the possibility; the data is given as a function.
<Autocomplete label="Label" value={10} showSubmitButton={true} onChange={({ data }) => { console.log('onChange', data) }} > {() => topMovies} </Autocomplete>
With a predefined input/search value#
<Autocomplete label="Label" inputValue="the pa ther" noAnimation onChange={({ data }) => { console.log('onChange', data) }} > {() => topMovies} </Autocomplete>
Inline results#
Use the inline property to render the results list persistently open in normal document flow, instead of as an overlay. The toggle button is hidden, while filtering, highlighting, selection and keyboard navigation stay the same.
<Autocomplete inline stretch label="Label" data={topMovies} />
Different sizes#
Four sizes are available: small, default, medium and large.
<Flex.Vertical> <Autocomplete label="Label" size="default" data={() => topMovies} /> <Autocomplete label="Label" size="medium" data={() => topMovies} /> <Autocomplete label="Label" size="large" data={() => topMovies} /> </Flex.Vertical>
Data suffix value#
Data is provided as such:
const { locale } = React.useContext(Context)const data = [{suffixValue: (<NumberFormat.Currency srLabel="Total:" locale={locale}>{12345678}</NumberFormat.Currency>),selectedValue: `Brukskonto (${ban})`,content: ['Brukskonto', ban],},]
const CustomWidth = styled(Autocomplete)` .dnb-drawer-list__root, .dnb-autocomplete__shell { width: 50vw; min-width: 15rem; max-width: 30rem; } ` render( <CustomWidth value={1} data={numbers} size="medium" icon={null} showSubmitButton label="From account" /> )
Custom width#
const CustomWidthOne = styled(Autocomplete)` .dnb-autocomplete__shell { width: 10rem; } ` const CustomWidthTwo = styled(Autocomplete)` &.dnb-autocomplete--is-popup .dnb-drawer-list__root { width: 12rem; } ` const CustomWidthThree = styled(Autocomplete)` /** Change the "__shell" width */ .dnb-autocomplete__shell { width: 12rem; } /** Change the "__list" width */ .dnb-drawer-list__root { width: 20rem; } ` render( <Flex.Vertical> <CustomWidthOne label="Label" labelSrOnly size="default" iconPosition="left" data={topMovies} /> <CustomWidthTwo label="Label" labelSrOnly size="medium" data={topMovies} /> <CustomWidthThree label="Label" labelSrOnly size="large" align="right" iconPosition="right" icon="bell" data={topMovies} /> </Flex.Vertical> )
Autocomplete with status message#
<Autocomplete data={topMovies} label="Label" status="You need to select a movie" statusState="information" showSubmitButton />
Autocomplete with List item content#
Reuse the List row layout for rich option content. The option is already an <li> and wraps its content in <span> elements, so use element="span" on List.Item.Basic and its cells to keep the markup valid. Give the Autocomplete a width that fits both the title and the end value. Provide selectedValue with the plain text so the input shows a sensible value once an option is selected, and searchContent so typing still filters the options. See rendering a row outside a List.Container for the details.
// A List row keeps its end cell at content width, so the row needs // horizontal room. Give the Autocomplete a width of its own, or the // title column will be squeezed by the end cell. const AccountAutocomplete = styled(Autocomplete)` .dnb-autocomplete__shell, .dnb-drawer-list__root { width: 22rem; } ` const data = [ { selectedKey: 'accounts', // selectedValue is the plain text shown in the input once selected; // searchContent keeps typing/filtering working with rich content selectedValue: 'Accounts', searchContent: 'Accounts Bills Savings', content: ( <List.Item.Basic element="span"> <List.Cell.Title element="span">Accounts</List.Cell.Title> <List.Cell.End element="span">Bills, Savings</List.Cell.End> </List.Item.Basic> ), }, { selectedKey: 'loans', selectedValue: 'Loans', searchContent: 'Loans Mortgage Car', content: ( <List.Item.Basic element="span"> <List.Cell.Title element="span">Loans</List.Cell.Title> <List.Cell.End element="span">Mortgage, Car</List.Cell.End> </List.Item.Basic> ), }, { selectedKey: 'cards', selectedValue: 'Cards', searchContent: 'Cards Visa Mastercard', content: ( <List.Item.Basic element="span"> <List.Cell.Title element="span">Cards</List.Cell.Title> <List.Cell.End element="span">Visa, Mastercard</List.Cell.End> </List.Item.Basic> ), }, ] render(<AccountAutocomplete data={data} label="Label" />)
Groups#
If an item has a groupIndex property, it will use the groups in the groups property. Only the first group can be without title, all other groups must have a title.
<Autocomplete groups={[undefined, 'Pets', 'Cars']} data={[ { groupIndex: 0, content: 'Default 1', }, { groupIndex: 0, content: 'Default 2', }, { groupIndex: 1, content: 'Cat', }, { groupIndex: 1, content: 'Dog', }, { groupIndex: 2, content: 'Jeep', }, { groupIndex: 2, content: 'Van', }, ]} />
No divider#
We can remove the divider between items with the noDivider prop. Beware that this can make information dense lists difficult to parse.
<Autocomplete noDivider data={['Cat', 'Dog', 'Canary', 'Hamster', 'Piglet']} />