legacy-autocomplete
Props
| Prop name | Type | Default | Description |
|---|---|---|---|
id | string | undefined | Component container id. Overrides automatically generated portion of children ids if needed. | |
children | LegacyAutocompleteChildrenComponents | Required | Expects the following components for composition: LegacyAutocompleteInput LegacyAutocompleteOptions LegacyAutocompleteSelectedOptions |
debounce | number | undefined | Built in debounce for onChange | |
extraClasses | string | undefined | Custom classes name for autocomplete wrapper element | |
onChange | ChangeEventHandler<HTMLInputElement> | undefined | Fired when the input value changes (without debounce) to be used only in edge cases, rely on debounce to avoid excessive requests to your API | |
onDebounce | ChangeEventHandler<HTMLInputElement> | undefined | Fired when the input value changes (with debounce) | |
onDelete | ((item: LegacyAutocompleteOptionType) => void) | undefined | Fired when the option gets deleted from selected list (only on multiselect mode). | |
onReset | (() => void) | undefined | Fired when "reset" icon is clicked (only on multiselect mode). | |
onSelect | ((item: LegacyAutocompleteOptionType) => void) | undefined | Called when a given option is selected wether by click or by pressing "Enter" key. | |
onOptionFocus | ((item: LegacyAutocompleteOptionType, event: FocusEvent<Element, Element> | KeyboardEvent<Element>) => void) | undefined | Called when the user focuses on an option using the keyboard arrow keys | |
onKeyDown | KeyboardEventHandler<HTMLDivElement> | undefined | Fired when a key is pressed inside of the LegacyAutocomplete `container` | |
onFocus | FocusEventHandler<HTMLDivElement> | undefined | Fired when the `container` receives focus event | |
onBlur | FocusEventHandler<HTMLDivElement> | undefined | Fired when blur event happens inside of the `container` | |
options | LegacyAutocompleteOptionType[] | LegacyAutocompleteGroupType[] | Required | Results of autocomplete to be show in dropdown, all extra item props will be returned on select (see onSelect) this allows you to reference them by id (or any other field you need) |
optionsSelected | LegacyAutocompleteOptionType[] | undefined | Selected options to be displayed in `AutocompleteSelectedOptions` when present. | |
optionsListVisible | boolean | undefined | Makes options list visibility controllable by ignoring focus, blur, escape and arrow key events for showing/hiding the list. | |
value | string | undefined | Controls component value, should be an array on multiselect mode. |
The autocomplete component is intended to be used as search input for simple entities. You should use autocomplete if the size of your select fields get above a given threshold.
Component relies on composition and requires following components to be passed as children:
- LegacyAutocompleteInput - the input field itself, note that
onDebounceandvalueprops need to be passed to the parentLegacyAutocompletecomponent in order for things to work; - LegacyAutocompleteOptions - a dropdown with the list of options, allows for customization of its appearance like number of options to display, passing custom render functions for option elements etc.
- LegacyAutocompleteSelectedOptions - renders area with the list of selected options when multiselect variation is used.
A simple example to illustrate the concept:
import { LegacyAutocomplete, LegacyAutocompleteOptions, LegacyAutocompleteInput } from '@flixbus/honeycomb-react'; const [data, setData] = React.useState([]); const [value, setValue] = React.useState(''); const [highlight, setHighlight] = React.useState(''); const [loading, setLoading] = React.useState(false); // -------------------------- // Mock data and the filter function const AutocompleteMockData = [ { title: 'Bologna', subtitle: 'Don\'t forget to eat tortellini', }, { title: 'Belo Horizonte', subtitle: 'Ready for a really long ride?', }, { title: 'Belgium', subtitle: '🍫 and 🍻', }, { title: 'Bermuda', subtitle: 'Bring your 🎣', }, { title: 'Berlin', subtitle: 'The place to be', }, ]; const filterAutocompleteMockData = (searchQuery, data) => ( new Promise((resolve) => { setTimeout(() => { const res = data.filter(item => ( item.title.toLowerCase().includes(searchQuery.toLowerCase()) )); resolve(res); }, 200); }) ); // -------------------------- <LegacyAutocomplete onChange={(e) => { setLoading(true) }} onDebounce={(e) => { setLoading(true); setHighlight(e.target.value); filterAutocompleteMockData(e.target.value, AutocompleteMockData).then( options => { setData(options) setLoading(false) } ) }} onSelect={(item) => { setData([]); setHighlight(); setValue(item.title); }} options={data} value={value} > {/* configures input field appearance */} <LegacyAutocompleteInput id="autocomplete-1" placeholder="Try to type Berlin slowly" label="Place:" loading={loading} type="search" autoComplete="off" info={data.length === 0 ? 'No options found.' : `${data.length} option${data.length > 1 ? 's' : ''}.`} /> {/* displays a dropdown with options */} <LegacyAutocompleteOptions label="Places" optionsToDisplay={2} optionHasSubtitle highlightQuery={highlight} /> </LegacyAutocomplete>
Please pay attention to the following things:
optionsandvalueprops are passed to the parentLegacyAutocompletecomponent;onDebounceandonSelectcallbacks are used to filter through the options and control option selection;loadingprop of theInputcomponent gets set to true when new data is fetched;- we recommend specifying
type="search"to theInputas it's more appropriate for this case; - check the documentation for LegacyAutocompleteOptions component to find more about customization possibilities for the options list;
- you can make the
LegacyAutocompleteInputvisually hidden by using thesrOnlyhelper class name, this way the message is still read out by screen readers; - to enable the screen reader to react to changes on the input
info, addaria-live="polite"to the element;
Here is the same example illustrating the LegacyAutocompleteInput element customization:
import { LegacyAutocomplete, LegacyAutocompleteOptions, LegacyAutocompleteInput, a11yHelpers } from '@flixbus/honeycomb-react'; import { Icon, IconPinSolid } from '@flixbus/honeycomb-icons-react'; const [data, setData] = React.useState([]); const [value, setValue] = React.useState(''); const [highlight, setHighlight] = React.useState(); const [loading, setLoading] = React.useState(false); const inputEl = React.useRef(null); // -------------------------- // Mock data and the filter function const AutocompleteMockData = [ { title: 'Bologna', subtitle: 'Don\'t forget to eat tortellini', }, { title: 'Belo Horizonte', subtitle: 'Ready for a really long ride?', }, { title: 'Belgium', subtitle: '🍫 and 🍻', }, { title: 'Bermuda', subtitle: 'Bring your 🎣', }, { title: 'Berlin', subtitle: 'The place to be', }, ]; const filterAutocompleteMockData = (searchQuery, data) => ( new Promise((resolve) => { setTimeout(() => { const res = data.filter(item => ( item.title.toLowerCase().includes(searchQuery.toLowerCase()) )); resolve(res); }, 200); }) ); // -------------------------- <LegacyAutocomplete options={data} value={value} onChange={(e) => { setLoading(true); }} onDebounce={(e) => { setLoading(true); setHighlight(e.target.value); filterAutocompleteMockData(e.target.value, AutocompleteMockData).then( options => { setData(options); setLoading(false); } ) }} onSelect={(item) => { setValue(item.title); setData([]); setHighlight(); }} > {/* configures input field appearance */} <LegacyAutocompleteInput id="autocomplete-2" placeholder="Try to type Berlin slowly" iconLeft={<Icon InlineIcon={IconPinSolid} />} label="Place:" loading={loading} type="search" autoComplete="off" info={( <span aria-live="polite" className={a11yHelpers().srOnly}> {data.length === 0 ? 'No options found.' : `${data.length} option${data.length > 1 ? 's' : ''}.`} </span> )} /> {/* displays a dropdown with options */} <LegacyAutocompleteOptions label="Places" highlightQuery={highlight} /> </LegacyAutocomplete>
Multiselect
Passing LegacyAutocompleteSelectedOptions component as child allows for implementing selecting multiple values from the list.
Please note, you'll need to take care of handling selected values and options on your own,
our component exposes a set of callback props (onSelect, onDelete, onReset), as well as optionsSelected prop displaying the selected items, giving you a great amount of flexibility in implementing this in a way it works for your app.
Bellow is an example implementation with handful comments that you can take as a basis for more complex solutions:
import { LegacyAutocomplete, LegacyAutocompleteOptions, LegacyAutocompleteSelectedOptions, LegacyAutocompleteInput } from '@flixbus/honeycomb-react'; // a data set we'll be working with const [data, setData] = React.useState([]); // storing value as an array of title properties from the data set const [value, setValue] = React.useState(['Bologna']); // the input content with the parts that should be highlighted in the options const [highlight, setHighlight] = React.useState(); // stores selection options, get's populated from data set const [selectedOptions, setSelectedOptions] = React.useState([]) // -------------------------- // Mock data and the filter function const AutocompleteMockData = [ { title: 'Bologna', subtitle: 'Don\'t forget to eat tortellini', }, { title: 'Belo Horizonte', subtitle: 'Ready for a really long ride?', }, { title: 'Belgium', subtitle: '🍫 and 🍻', }, { title: 'Bermuda', subtitle: 'Bring your 🎣', }, { title: 'Berlin', subtitle: 'The place to be', }, ]; const filterAutocompleteMockData = (searchQuery, data) => ( new Promise((resolve) => { setTimeout(() => { const res = data.filter(item => ( item.title.toLowerCase().includes(searchQuery.toLowerCase()) )); resolve(res); }, 200); }) ); // -------------------------- // narrows down suggested options list based on user input const filterOptionsList = (e) => { setHighlight(e.target.value) filterAutocompleteMockData(e.target.value, AutocompleteMockData).then( (options) => { setData(options); }, ); }; // returns options that have their values stored const setSelectedOptionsFromValue = (value) => { setSelectedOptions( AutocompleteMockData.filter(option => value.includes(option.title)) ); }; // updating selected options every time a value gets updated React.useEffect(() => { setSelectedOptionsFromValue(value); }, [value] ); // pushes selected option value to the state const handleItemSelect = (selectedItem) => { if (!value.includes(selectedItem.title)) { setValue(value.concat([selectedItem.title])); } setHighlight() setData([]); // hiding the options list }; // removes deleted option value from the state const handleItemDelete = (selectedItem) => { setValue(value.filter(valueItem => valueItem !== selectedItem.title)); }; // clears value const handleReset = () => { setValue([]); } <LegacyAutocomplete placeholder="Try to type Berlin slowly" label="Place:" options={data} // filters the data based on value in the state filling out selected options list optionsSelected={selectedOptions} onDebounce={filterOptionsList} onDelete={handleItemDelete} onReset={handleReset} onSelect={handleItemSelect} > <LegacyAutocompleteInput id="autocomplete-3" placeholder="Try to type Berlin slowly" label="Place:" type="search" onFocus={()=>{setData(AutocompleteMockData)}} // allows option list to be shown on focus /> {/* displays a dropdown with options */} <LegacyAutocompleteOptions label="Places" highlightQuery={highlight} /> {/* displays selected options with "clear selection" controls */} <LegacyAutocompleteSelectedOptions deleteAllItemsLabel="Clear selection" deleteItemLabel="Remove item from selection" /> </LegacyAutocomplete>