Let's make a text input with fixed options, where the user gets suggestions while typing, but they can only "submit" the field with a value from the list of options. Here, we have an example of an input where you can select languages you know.
The LanguagesInput
component receives an array of selected languages, the onChange
callback, and an optional error message. It consists of two parts: the FixedOptionsInput
and a list of entered languages. The most sophisticated piece here is the Combobox
component. First, we pass properties common to form fields: label, placeholder, error, ref, onChange, and value, which is null because we don't preselect any value. Then we provide options that could be of any type, so we follow with a string convertor. To clear the input after choosing a language, we pass the clearAfterOptionSelected
flag. We can also provide a render option function to display a custom element in the list of suggestions.
<FixedOptionsInput
label="Languages"
placeholder="Spanish"
value={null}
ref={ref}
onChange={(value) => {
if (value) {
handleAddLanguage(value)
}
}}
error={error}
optionToString={(option) => option}
options={availableLanguageNames}
clearAfterOptionSelected
/>
The parent element for the combo box is a div, for which we set up ref
, and the onBlur callback to close the menu when inner elements are out of focus. Then we have an input wrapper for providing a label and error message together with an absolutely positioned button to toggle men open state. I covered both the InputWrapperWithErrorMessage and TextInputContainer
in another post.
To display suggestions, we have the DropdownMenuPlacer
component. We pass input as children and a list of options to the placer, and it sets up refs to position the menu right under the input. To show the menu with are using the react-popper library. Here we set up a config to show the menu on top in case there is not enough space, then we add a margin between the input and the menu. Finally, we make the menu the same width as the input.
The ComboboxOptions
component takes a list of suggestions and renders them inside a container that sets a specific max height together with overflow-y set to auto to show a scroll. We store refs of every option to scroll into view a selected one. To allow moving between suggestions with keyboards, we are using a combination of the user keypress hook together with the getHandleStep function.
The FixedOptionsInput
has some tricky effects managing input and menu with suggestions. We won't go deep into them, but if you're curious, welcome to explore them on GitHub.