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 | 300 | Built in debounce for onChange |
extraClasses | string | undefined | Custom classes name for autocomplete wrapper element | |
onChange | ChangeEventHandler<HTMLInputElement, Element> | undefined | () => null | 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, Element> | undefined | () => null | Fired when the input value changes (with debounce) |
onDelete | ((item: LegacyAutocompleteOptionType) => void) | undefined | () => null | Fired when the option gets deleted from selected list (only on multiselect mode). |
onReset | (() => void) | undefined | () => null | Fired when "reset" icon is clicked (only on multiselect mode). |
onSelect | ((item: LegacyAutocompleteOptionType) => void) | undefined | () => null | 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 | () => null | Called when the user focuses on an option using the keyboard arrow keys |
onKeyDown | KeyboardEventHandler<HTMLDivElement> | undefined | () => null | Fired when a key is pressed inside of the LegacyAutocomplete `container` |
onFocus | FocusEventHandler<HTMLDivElement> | undefined | () => null | Fired when the `container` receives focus event |
onBlur | FocusEventHandler<HTMLDivElement> | undefined | () => null | 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} title="A pin" />}
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>