Honeycomb

autocomplete

Since: ver.12.0.0

Props

Prop nameTypeDefaultDescription
extraClassesstring | undefined

Injected classes.

innerRefRef<HTMLDivElement> | undefined

React ref forwarded to the outer div.

expandedboolean | undefined

Controls the listbox visibility and disables internal active state management.

optionsListBoxOptionsType<OptionType> | undefined

List of options to choose from.

valueOptionType[] | undefined

List of currently selected options. Makes the component controlled.

defaultValueOptionType[] | undefined[]

List of options that are initially selected.

inputValuestring | undefined

Controls the input value.

defaultInputValuestring | undefined

Initial input value.

multipleboolean | undefinedfalse

Enables selection of multiple options.

debouncenumber | undefined300

Debounce time in milliseconds.

listBoxPropsAutocompleteListBoxProps<OptionType> | undefined

Props forwarded to the options list, ListBox component.

comboboxPropsAutocompleteComboboxProps<OptionType> | undefined

Props forwarded to the text field, Combobox component.

onChange((selectedOptions: OptionType[], event?: SyntheticEvent<Element, Event> | undefined) => void) | undefined

Called back with an array of the currently selected options whenever this value changes.

onToggle((expanded: boolean, event?: SyntheticEvent<Element, Event> | undefined) => void) | undefined

Called back with the current expanded state whenever it changes.

onDebouncetypeof defaultAutocompleteFilter<OptionType> | ((inputValue: string, options: ListBoxOptionsType<OptionType>) => void | ListBoxOptionsType<OptionType>) | undefined

Fired when the input value changes (with debounce). Replaces internal filtering logic. Updates options list if either options array or a Promise is returned.

The Autocomplete is responsible for orchestrating the interactions between Combobox and ListBox to create an input that can be filled with an option from a list of available options.

The user input on the Combobox filters the options available on the ListBox and the controller handles all necessary events and interactions.

The options prop accepts a list of all available options, and the structure is the one supported by the ListBox component:

{
  title: string
  subtitle?: string
  value: string
}[]

By default, the component will filter the options by the contents of their title and subtitle.

This example shows a simple Autocomplete with title:

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

<Autocomplete
  options={pokemons}
  comboboxProps={{
    label: 'Pokémon',
    placeholder: 'Find your Pokémon...',
  }}
/>

Here is an example with title and subtitle. Notice how you can also find pokémons by typing their types on the combobox field. That is because we created the subtitle property from their types.

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const pokemonsWithTypeSubtitle = pokemons.map((pokemon) => ({
  ...pokemon,
  // Adds `subtitle` property from each pokemon array of types
  subtitle: pokemon.types.join(', ')
}));

<Autocomplete
  options={pokemonsWithTypeSubtitle}
  comboboxProps={{
    label: 'Pokémon with types',
    placeholder: 'Search pokémon title and subtitle...',
  }}
/>

Grouped options

To show options in a group you must change the format of the options prop. Group them in the following format instead:

{
  group: string
  options: {
    title: string
    subtitle?: string
    value: string
  }[]
}[]

Optionally, you can customize the listbox group names by using the renderGroupName function in the listBoxProps prop. This function will be called with the group as an argument and must return an element.

⚠️ Please do not mix up grouped and ungrouped options as the component does not support mixed option formats.

import { Autocomplete } from '@flixbus/honeycomb-react';
import { Icon, IconTimeSolid } from '@flixbus/honeycomb-icons-react';

const groupedOptions = [
  {
    group: 'Recent',
    options: [
      { title: 'Pikachu', value: '25' },
    ],
  },
  {
    group: 'Grass',
    options: [
      { title: 'Bulbasaur', value: '1' },
      { title: 'Ivysaur', value: '2' },
      { title: 'Venusaur', value: '3' },
    ],
  },
  {
    group: 'Fire',
    options: [
      { title: 'Charmander', value: '4' },
      { title: 'Charmeleon', value: '5' },
      { title: 'Charizard', value: '6' },
    ],
  },
  {
    group: 'Water',
    options: [
      { title: 'Squirtle', value: '7' },
      { title: 'Wartortle', value: '8' },
      { title: 'Blastoise', value: '9' },
    ],
  },
];

const renderGroupName = ({ group }) => {
  if (group === 'Recent') {
    return <><Icon InlineIcon={IconTimeSolid} aria-hidden="true" /> Recent</>;
  }

  return group;
}

<Autocomplete
  options={groupedOptions}
  comboboxProps={{ label: 'Grouped Pokémon' }}
  listBoxProps={{ renderGroupName }}
/>

Custom filtering and external state synchronization

If you're not satisfied with the default filtering, you can provide your own filtering function via the onDebounce prop. This function receives the input value and the options array as arguments and can return either a Promise that resolves to an array with the filtered options or an array itself. This output will be displayed on the list box.

In this example we will implement search by Pokémon type using onDebounce callback to mutate options stored in external state.

By default, the filtering happens on both title and subtitle, but here we only want to filter Pokémon by their types. To reflect that we set the highlightOptions prop to subtitle, this will highlight only types (subtitles) without touching the names in the list box options list.

highlightOptions listBox prop allows you to customize where the component will apply the matches highlight, it can equal to title, subtitle, none or both.

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const pokemonsWithTypeSubtitle = pokemons.map((pokemon) => ({
  ...pokemon,
  // Adds `subtitle` property from each pokemon array of types
  subtitle: pokemon.types.join(', ')
}));

const [options, setOptions] = React.useState(pokemonsWithTypeSubtitle);

const filterPokemonsByType = (inputValue) => {
  const filterRegex = new RegExp(inputValue, 'i');
  const filteredPokemons = pokemonsWithTypeSubtitle.filter((pokemon) => pokemon.types.some((type) => type.match(filterRegex)));
  setOptions(filteredPokemons);
};

<Autocomplete
  options={options}
  onDebounce={filterPokemonsByType}
  onChange={(selectedOptions) => {setOptions(selectedOptions)}}
  comboboxProps={{
    label: 'Find Pokémon by types',
    placeholder: 'Grass, Fire, Water...',
  }}
  listBoxProps={{
    highlightOptions: 'subtitle',
  }}
/>

Fetch as you type

Instead of filtering through a static list of options you can also use the onDebounce prop to fetch new options as the user types. The function receives the input value as an argument and must return a Promise that is resolved with the desired results. If you need to store the options state on your side you can do this here.

In the example bellow we mimic an API that fetches a list of pokémons as the user types.

import React from 'react';
import { Autocomplete, Spinner } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const [loading, setLoading] = React.useState(false);
const [options, setOptions] = React.useState([]);

const fetchFromPokedex = (inputValue) => {
  setLoading(true);

  return new Promise((resolve) => {
    setTimeout(() => {
      const response = pokemons.filter((option) => option.title.match(new RegExp(inputValue, 'i')));

      setLoading(false);
      setOptions(response);
      // resolve the promise with the API response
      resolve(response);
    }, 1000);
  });
};

<Autocomplete
  onDebounce={fetchFromPokedex}
  comboboxProps={{
    label: 'Pokédex',
    placeholder: 'Find your Pokémon...',
    loading,
    'aria-live': 'polite',
    srOnlyInfo: `${options.length} option${options.length === 1 ? '' : 's'}.`
  }}
/>

Multiple selection

To allow multiple selection, set the multiple prop.

When multiple selection is enabled, you may also provide the Confirm and Reset buttons to enhance the user experience. This is done by using the actionButtonProps and resetButtonProps props.

The "Confirm" is a list box action and to enable it you must provide at least a label or children in the listBoxProps prop.

You can also pass any other props that are supported by the Button component and they will be forwarded as ...rest props. Including onClick handlers.

In addition to that, the optional onClick handler will be called with not only the event but also the current selection as second argument.

The reset button is a combobox controller and will show when there are selected options.

To enable it you must provide at least a label for it in the comboboxProps prop. Other props will be forwarded to the underlying close button component as ...rest props.

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

<Autocomplete
  multiple
  options={pokemons}
  comboboxProps={{
    label: 'Multiple Pokémons',
    resetProps: {
      label: 'Clear',
      onClick: (event) => console.log('Reset button clicked!'),
    }
  }}
  listBoxProps={{
    actionButtonProps: {
      children: 'Confirm',
      onClick: (event, selectedOptions) => {
        alert(`You have selected ${selectedOptions.length} pokemons.`)
      }
    },
  }}
/>

Customizing the text field

With the comboboxProps prop you can customize the underlying Combobox component.

The combobox supports most Input props, except for the controllers (which is used for rendering the Autocomplete reset and toggle buttons). In addition to that, the following props are available for Combobox customization:

`resetProps`
{ label: string } & HTMLButtonAttributes
Props forwarded to the Reset button. Label is required for assistive technologies. If absent, the Reset button will not be rendered.
`toggleProps`
{ label: string } & HTMLButtonAttributes
Props forwarded to the Toggle button. Label is required for assistive technologies. If absent, the Reset button will not be rendered.
`limitSelectedOptions`
number
Limits the amount of selected options displayed as tags.
`tagProps`
(option: ListBoxOptionType) => TagProps
Function that must return an object with rest props for each of the selected options tag.
`srOnlyInfo`
string
Info text used to announce changes for screen readers, e.g.: announcing changes in the number of available options.

Toggle and reset buttons

The toggle and reset buttons are optional and can be enabled by providing at least a label for them in the comboboxProps.

You can also pass other Button props to the underlying Button component as ...rest props, and they will be forwarded accordingly.

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const [info, setInfo] = React.useState('');

<Autocomplete
  options={pokemons}
  defaultValue={[pokemons[0]]}
  defaultInputValue={pokemons[0].title}
  comboboxProps={{
    label: 'Combobox with toggle and reset controllers',
    resetProps: { label: 'Reset', onClick: () => setInfo('Reset button clicked') },
    toggleProps: { label: 'Toggle', onClick: () => setInfo('Toggle button clicked') },
    info,
  }}
/>

Customizing the selected options tags

The tagProps prop is a function that must return an object with rest props for each of the tags. The returned object will be forwarded to the underlying Tag component and accepts any valid Tag prop.

In addition to the Tag component props, you can also provide an additional prop renderTag function that must return a ReactNode to be rendered as the tag children.

For example, let's add the Pokémon numbers as their tags instead of the titles:

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

<Autocomplete
  multiple
  options={pokemons}
  defaultValue={[
    { title: 'Pikachu', value: 25 },
    pokemons[0],
  ]}
  comboboxProps={{
    label: 'Combobox with custom tags',
    tagProps: (selectedOption) => ({
      renderTag: () => <>#{String(selectedOption.value).padStart(3, '0')}</>,
      closeProps: {
        label: `Remove ${selectedOption.title}`,
      },
    }),
  }}
/>

You can limit the number of selected options that are displayed when the component is not focused by setting the limitSelectedOptions prop with the desired number of tags.

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

<Autocomplete
  multiple
  options={pokemons}
  defaultValue={pokemons}
  comboboxProps={{
    label: 'Limited tags',
    limitSelectedOptions: 3,
    resetProps: {
      label: 'Clear',
      onClick: (event) => console.log('Selection reset!')
    },
    tagProps: (option) => ({
      closeProps: {
        label: `Remove ${option.title}`,
      },
    }),
  }}
  listBoxProps={{
    actionButtonProps: {
      children: 'Confirm',
    },
  }}
/>

Most of the input component props are supported by the combobox as well, so if you want to add an icon, inline labels and do validation, you can do it with the comboboxProps.

import { Autocomplete } from '@flixbus/honeycomb-react';
import { Icon, IconGhost } from '@flixbus/honeycomb-icons-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const [pokemon1, setPokemon1] = React.useState({});
const [pokemon2, setPokemon2] = React.useState({});

<>
  <Autocomplete
    options={pokemons}
    comboboxProps={{
      label: '1. Combobox with icon',
      iconLeft: <Icon InlineIcon={IconGhost} aria-hidden="true" />,
    }}
  />
  <Autocomplete
    options={pokemons}
    comboboxProps={{
      label: '2. Value on inline label',
      inlineLabelLeft: (pokemon1 && pokemon1.value) ? `#00${pokemon1.value}`: undefined,
    }}
    onChange={([selection]) => setPokemon1(selection)}
  />
  <Autocomplete
    options={pokemons}
    comboboxProps={{
      label: '3. Combobox validation',
      valid: pokemon2 && pokemon2.title === 'Bulbasaur',
      infoError: 'You must pick Bulbasaur',
    }}
    onChange={([selection]) => setPokemon2(selection)}
  />
  <Autocomplete
    options={pokemons}
    comboboxProps={{
      disabled: true,
      label: '4. Disabled combobox',
      inlineLabelLeft: (pokemon1 && pokemon1.value) ? `#00${pokemon1.value}`: undefined,
    }}
    onChange={([selection]) => setPokemon1(selection)}
  />
</>

Customizing the list box

With the listBoxProps prop you can customize the underlying ListBox component.

The supported props are:

`appearance`
"boxed" (default) or "flat"
`hideDividers`
Hides dividers between options.
`widget`
React.ReactElement
Widget rendered after the options list.
`optionsToDisplay`
Number of options to display before the list box starts scrolling. Default is 3. Set it to 0 to remove the height constraint.
`actionButtonProps`
Props forwarded to the action button, e.g.: "Confirm button". If no props are provided, the button will not be rendered.
`optionProps`
(option: ListBoxOptionType) => ListBoxOptionProps
Function returning an object with the rest props for the LI element of each option.

Appearance and dividers

The default appearance for the options list is boxed and with visible dividers.

You can make it look like a flat list by setting the appearance prop to flat. The flat listbox has square borders, no outer box shadow and a more subtle inner shadow.

You can also hide the dividers between the options by setting the hideDividers prop to true.

import { Autocomplete } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

<>
  <Autocomplete
    comboboxProps={{ label: 'Flat listbox' }}
    options={pokemons}
    listBoxProps={{
      appearance: 'flat'
    }}
  />
  <Autocomplete
    comboboxProps={{ label: 'hideDividers' }}
    options={pokemons}
    listBoxProps={{
      hideDividers: true
    }}
  />
</>

Widget

The widget prop can be used to render a widget after the list of options and before the confirm button (if it is present).

For example:

import { Autocomplete, ListBox, ImageBox, spaceHelpers } from '@flixbus/honeycomb-react';

const pokemons = [
  { title: 'Growlithe', value: '58' },
  { title: 'Arcanine', value: '59' },
];

const { 2: space2 } = spaceHelpers();

<Autocomplete
  multiple
  comboboxProps={{ label: 'Listbox with a widget' }}
  options={pokemons}
  listBoxProps={{
    actionButtonProps: { children: 'Confirm' },
    optionsToDisplay: 0,
    widget: (
      <ImageBox
        title="Growlithe"
        text="Friendly and loyal Pokémon that will fearlessly defend its Trainer and territory from harm."
        outlined
        small
        img={{
          src: 'https://styleguide.hive.flix.tech/img/img-placeholder-grey.png',
          alt: 'Growlithe'
        }}
        extraClasses={`${space2.top} ${space2.right} ${space2.bottom} ${space2.left}`}
      />
    ),
  }}
/>

You can use the widget area to display a message if there are no available options for a given query. The onUpdate list box callback happens whenever the length of the options list changes. Another use for these changes can be to announce the number of options for screen readers using the combobox srOnlyInfo prop.

import React from 'react';
import { Autocomplete, ListBox, Box, spaceHelpers } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const [options, setOptions] = React.useState(pokemons);

const { 2: space2 } = spaceHelpers();

// To keep options filtering between rerenders caused by listBoxProps/onUpdate callback state update,
// we need to perform filtering externally
const filterOptionsByTitle = (inputValue) => {
  const filterRegex = new RegExp(inputValue, 'i');

  const filteredOptions = pokemons.filter((pokemon) => {
    return pokemon.title.match(filterRegex)
  });

  setOptions(filteredOptions);

  return new Promise((resolve) => {
    resolve(filteredOptions);
  });
};

<Autocomplete
  comboboxProps={{
    label: 'Options feedback',
    srOnlyInfo: `${options.length} option${options.length === 1 ? '' : 's'}.`
  }}
  onDebounce={filterOptionsByTitle}
  options={options}
  listBoxProps={{
    actionButtonProps: { children: 'Confirm' },
    widget: !options || options.length === 0 ? (
      <Box small appearance="dimmed" extraClasses={`${space2.top} ${space2.right} ${space2.bottom} ${space2.left}`}>
        No options found.
      </Box>
    ) : null,
  }}
/>

Customizing individual options

The optionProps prop can be used to customize the rendering of the individual options and also render option specific widgets, such as a clear or a toggle button.

The function will be called for each option and should return an object that contains the rest props forwarded to the option component. The supported component props are:

{
  /**
   * Function that can be used to customize the rendering of each option.
   */
  renderOption?: ({ option, highlightQuery, shouldHighlightTitle, shouldHighlightSubtitle }) => React.ReactNode
  /**
   * Function that allows you to render a widget next to the option,
   * such as a clear button or an icon.
   */
  renderWidget?: (...args: unknown[]) => React.ReactNode
  /** Modifier to show the option indented. */
  indent?: boolean
}

Notice that when you use the renderOption, the component's automatic highlighting will not work, so you must handle it on your side. You can use the highlightText helper to do it, or any other package you like that will add <mark> tags around the matches.

The other properties in the optionProps object will be forwarded as rest props to the options LI element.

import React from 'react';
import { Autocomplete, Text, Heading, Tag, CloseButton, highlightText } from '@flixbus/honeycomb-react';

const [inputValue, setInputValue] = React.useState('');
const recent = [{
  group: 'Recent',
  options: [
    { value: '#001', line: 'bulb', title: 'Bulbasaur', types: ['Grass', 'Poison'], recent: true },
    { value: '#004', line: 'char', title: 'Charmander', types: ['Fire'], recent: true },
    { value: '#007', line: 'squi', title: 'Squirtle', types: ['Water'], recent: true },
  ]
}];

const pokemons = [
  { value: 'bulb', line: 'bulb', title: 'Bulbasaur (all evolution line)' },
  { value: '#001', line: 'bulb', title: 'Bulbasaur', types: ['Grass', 'Poison'] },
  { value: '#002', line: 'bulb', title: 'Ivysaur', types: ['Grass', 'Poison'] },
  { value: '#003', line: 'bulb', title: 'Venusaur', types: ['Grass', 'Poison'] },
  { value: 'char', line: 'char', title: 'Charmander (all evolution line)' },
  { value: '#004', line: 'char', title: 'Charmander', types: ['Fire'] },
  { value: '#005', line: 'char', title: 'Charmeleon', types: ['Fire'] },
  { value: '#006', line: 'char', title: 'Charizard', types: ['Fire', 'Flying'] },
  { value: 'squi', line: 'squi', title: 'Squirtle (all evolution line)' },
  { value: '#007', line: 'squi', title: 'Squirtle', types: ['Water'] },
  { value: '#008', line: 'squi', title: 'Wartortle', types: ['Water'] },
  { value: '#009', line: 'squi', title: 'Blastoise', types: ['Water'] },
];

const renderOption = (pokemon) => (
  <>
    <Heading Elem="span" size={4} flushSpace>
      {(pokemon.value !== pokemon.line) && highlightText(pokemon.value, inputValue)}
      {' '}
      {highlightText(pokemon.title, inputValue)}
    </Heading>
    {pokemon.types && (
      <Text small>
        {pokemon.types.join(', ')}
      </Text>
    )}
  </>
);

const renderWidget = (pokemon) => {
  if (!pokemon.recent) {
    return null;
  }

  return (
    <CloseButton
      aria-label={`Remove ${pokemon.title} from recent searches`}
      onClick={(e) => {
        e.stopPropagation();
        alert(`${pokemon.title} removed from recent searches`);
      }}
    />
  );
};

const filterPokemonsByTitleAndValue = (inputValue) => {
  const filterRegex = new RegExp(inputValue, 'i');

  if (!inputValue) {
    return new Promise((resolve) => resolve(recent));
  }

  return new Promise((resolve) => {
    resolve(pokemons.filter((pokemon) => {
      return pokemon.title.match(filterRegex) || pokemon.value.match(filterRegex)
    }));
  });
};

<Autocomplete
  options={recent}
  multiple
  onDebounce={filterPokemonsByTitleAndValue}
  comboboxProps={{
    label: 'Customized pokémon options',
    onChange: ({ target }) => {
      setInputValue(target.value)
    },
  }}
  listBoxProps={{
    optionsToDisplay: 0,
    optionProps: (option) => ({
      renderOption: () => renderOption(option),
      renderWidget: () => renderWidget(option),
      indent: !option.recent && option.line !== option.value,
      'data-testid': `pokemon-${option.value}`,
    })
  }}
/>

Events

The autocomplete currently has 3 main event callbacks:

  • onDebounce: Fires with debounce when the combobox value (inputValue) changes.
  • onToggle: Fires when the expanded state changes.
  • onChange: Fires when the selected options (value) changes.

The underlying components also have their own events you can use.

The combobox accepts all the same events as an input component, the Autocomplete does not intervene in the handling of onChange events, so please notice that value changes that happened programmatically will not trigger the onChange event.

The list-box has 2 events related to state changes:

  • onChange: Called back when the list of selected options (value) changes.
  • onUpdate: Called back when the list os options to be displayed changes.

Additionally, the list-box individual options accept an onChange callback that will be called whenever each option selection state changes.

import React from 'react';
import { Autocomplete, Text } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const [value, setValue] = React.useState([]);
const [inputValue, setInputValue] = React.useState('');
const [expanded, setExpanded] = React.useState(false);
const [options, setOptions] = React.useState(pokemons);
const [optionsLength, setOptionsLength] = React.useState(pokemons.length);
const [lastSelectedOption, setLastSelectedOption] = React.useState();

const filterOptionsByTitle = (inputValue) => {
  const filterRegex = new RegExp(inputValue, 'i');

  const filteredOptions = pokemons.filter((pokemon) => {
    return pokemon.title.match(filterRegex)
  });

  setOptions(filteredOptions);

  return new Promise((resolve) => {
    resolve(filteredOptions);
  });
};

<>
  <Text><code>value</code>: <output>{value}</output></Text>
  <Text><code>inputValue</code>: <output>{inputValue}</output></Text>
  <Text><code>expanded</code>: <output>{`${expanded}`}</output></Text>
  <Text><code>optionsLength</code>: <output>{optionsLength}</output></Text>
  <Text><code>lastSelectedOption</code>: <output>{lastSelectedOption}</output></Text>
  <Autocomplete
    multiple
    options={options}
    onToggle={(expanded) => setExpanded(expanded)}
    onChange={(selectedOptions) => setValue(selectedOptions.map(({ title }) => title).join(', '))}
    onDebounce={filterOptionsByTitle}
    comboboxProps={{
      label: 'Eventful autocomplete',
      onChange: ({ target }) => setInputValue(target.value),
    }}
    listBoxProps={{
      onUpdate: (options) => setOptionsLength(options.length),
      optionProps: () => ({
        onChange: (selected, option) => {
          if (selected) {
            setLastSelectedOption(option.title);
          }
        }
      })
    }}
  />
</>

Controlled states

You can take the control of the autocomplete states by passing the value, inputValue and expanded props. These will override the internal state of the component.

When doing so, please make sure to also provide accessible controls for keyboard navigation too.

import React from 'react';
import { Autocomplete } from '@flixbus/honeycomb-react';

const op1 = { title: 'Option 1', value: 1 };
const op2 = { title: 'Option 2', value: 2 };

// External controlled state
const [value, setValue] = React.useState(op1);

{/* Min-height added to make the example more readable. Please do not copy the style. */}
<div style={{ minHeight: '10em' }}>
  <Autocomplete
    options={[op1, op2]}
    /* controlled value */
    value={[value]}
    /* controlled input value */
    inputValue={value.title}
    comboboxProps={{
      label: 'Controlled autocomplete',
    }}
    listBoxProps={{
      optionProps: (option) => ({
        /* option onClick controls the state externally from the component */
        onClick: () => setValue(option)
      })
    }}
    expanded
    /* onChange is called whenever the value changes with the current selection and the event that triggered the change */
    onChange={(selectedOptions, event) => console.log(selectedOptions, event)}
    /* this fake onDebounce was added to make the example easier to understand when clicking different options. */
    onDebounce={() => new Promise((resolve) => resolve([op1, op2]))}
  />
</div>

Moreover, you can disable the components internal event handlers by setting the flag preventHCREventHandling to true on the event object. Only do this if you plan on rewriting the entire functionality on your side (for testing purposes for example) and at your own risk.

In the example bellow, we disable the onClick handler from the listbox options. Notice how the selection of options with clicks no longer works.

import React from 'react';
import { Autocomplete, Text } from '@flixbus/honeycomb-react';

const [option, setOption] = React.useState({});

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

<>
  <Text>Option clicked: <output>{option.title}</output></Text>
  <Autocomplete
    options={pokemons}
    comboboxProps={{
      label: 'Preventable events',
    }}
    listBoxProps={{
      optionProps: () => ({
        onClick: (e, option) => {
          e.preventHCREventHandling = true;

          setOption(option);
        }
      })
    }}
  />
</>

Integrating with external libraries

React Hook Form

Both useController and the Controller component can be used to write up with the Autocomplete component. On the example bellow we showcase both examples but we recommend the Controller component over the hook, because we think it is the easiest one to implement and maintain since it does not require you to rename all of the different controller variables.

import { Controller, useController, useForm } from 'react-hook-form';
import { Autocomplete, Fieldset, Legend, Button } from '@flixbus/honeycomb-react';

const { control, setError, handleSubmit } = useForm({});

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

const onSubmit = (data, event) => console.log('submit', data, event);
const onError = (errors, event) => console.log('error', errors, event);

const { field: field_pk2, fieldState: fieldState_pk2 } = useController({
  name: 'pokemon-2',
  control,
  rules: { required: true },
});

<form onSubmit={handleSubmit(onSubmit, onError)}>
  <Fieldset>
    <Legend>Choose your team</Legend>
    <Controller
      control={control}
      name="pokemon-1"
      rules={{ required: true }}
      render={({ field, fieldState }) => (
        <Autocomplete
          options={pokemons}
          onChange={(selectedOptions) => field.onChange(selectedOptions[0])}
          comboboxProps={{
            label: '1st Pokémon',
            onBlur: field.onBlur,
            inputFieldRef: field.ref,
            valid: !fieldState.invalid,
            infoError: fieldState.error ? fieldState.error.type : ''
          }}
        />
      )}
    />
    <Autocomplete
      options={pokemons}
      onChange={(selectedOptions) => field_pk2.onChange(selectedOptions[0])}
      comboboxProps={{
        label: '2nd Pokémon',
        onBlur: field_pk2.onBlur,
        onChange: field_pk2.onChange,
        inputFieldRef: field_pk2.ref,
        valid: !fieldState_pk2.invalid,
        infoError: fieldState_pk2.error ? fieldState_pk2.error.type : ''
      }}
    />
  </Fieldset>
  <Button type="submit">Submit</Button>
</form>

Formik

The recommended approach for Formik is to skip the library's own handleChange prop to set the field value manually instead. This is due to the fact that the combobox is a text field, but we are dealing with a custom data structure, and multiple values may potentially be selected with the Autocomplete.

In this simple example we showcase how to set the input value on Formik state using the Autocomplete onChange callback.

import { Formik } from 'formik';
import { Autocomplete, FormRow, Button } from '@flixbus/honeycomb-react';

const pokemons = [
  {
    title: 'Bulbasaur',
    value: 1,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Ivysaur',
    value: 2,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Venusaur',
    value: 3,
    types: ['Grass', 'Poison'],
    weaknesses: ['Fire', 'Ice', 'Flying', 'Psychic'],
  },
  {
    title: 'Charmander',
    value: 4,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charmeleon',
    value: 5,
    types: ['Fire'],
    weaknesses: ['Water', 'Ground', 'Rock'],
  },
  {
    title: 'Charizard',
    value: 6,
    types: ['Fire', 'Flying'],
    weaknesses: ['Water', 'Electric', 'Rock'],
  },
  {
    title: 'Squirtle',
    value: 7,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Wartortle',
    value: 8,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
  {
    title: 'Blastoise',
    value: 9,
    types: ['Water'],
    weaknesses: ['Grass', 'Electric'],
  },
];

<Formik
  initialValues={{ search: '' }}
  onSubmit={(values, actions) => {
    actions.setSubmitting(false);
    console.log('submit', values);
  }}
  validate={(values) => {
    console.log('validate', values);
  }}
>
  {(props) => (
    <form onSubmit={props.handleSubmit}>
      <FormRow>
        <Autocomplete
          options={pokemons}
          comboboxProps={{
            name: 'search',
            label: 'Search pokemons',
            valid: props.touched.search && !Boolean(props.errors.search),
            infoError: props.touched.search && props.errors.search,
            onBlur: props.handleBlur,
          }}
          onChange={([selection]) => {
            // on the Autocomplete onChange, call setFieldValue accordingly to updated the form state
            props.setFieldValue('search', selection);
          }}
        />
      </FormRow>
      <Button type="submit">Submit</Button>
    </form>
  )}
</Formik>

Using the Autocomplete to make a custom select

The combobox pattern that exists under the hood of the Autocomplete works as a custom-built select, which you can use to add custom formatting to the options and the input.

In essence, it is a regular text input with added functionality and accessibility features to make it look and behave like a combobox (aka: select).

For example: showing icons inside the options, or customizing the value displayed after selecting an option.

import { Autocomplete, Heading } from '@flixbus/honeycomb-react';
import { Icon, IconArrowDown, IconArrowUp, IconQuestion, IconBeach, IconCity, IconCoffee, IconDrinks, IconForrest, IconMountains, IconPlayingcard } from '@flixbus/honeycomb-icons-react';

const LEISURE_TYPES = [{
  title: 'Beach',
  icon: IconBeach,
  value: 'beach',
}, {
  title: 'Cassino',
  icon: IconPlayingcard,
  value: 'cassino',
}, {
  title: 'City sights',
  icon: IconCity,
  value: 'city-sights',
}, {
  title: 'Coffee shops',
  icon: IconCoffee,
  value: 'coffee-shops',
}, {
  title: 'Nightlife',
  icon: IconDrinks,
  value: 'nightlife',
}, {
  title: 'Forrest',
  icon: IconForrest,
  value: 'forrest',
}, {
  title: 'Mountains',
  icon: IconMountains,
  value: 'mountains',
}];

const [open, setOpen] = React.useState(false);
const [selectedItem, setSelectedItem] = React.useState();

<Autocomplete
  options={LEISURE_TYPES}
  onToggle={(open) => setOpen(open)}
  onChange={(item) => setSelectedItem(item[0]) }
  comboboxProps={{
    label: 'Leisure types:',
    placeholder: 'Search for leisure types...',
    autoComplete: 'off',
    iconLeft: <Icon InlineIcon={selectedItem ? selectedItem.icon : IconQuestion} />,
    iconRight: <Icon InlineIcon={open ? IconArrowUp : IconArrowDown} />,
  }}
  listBoxProps={{
    optionsToDisplay: 6,
    optionProps: ({ title, icon }) => ({
      renderOption: () => (
        <Heading Elem="span" size={4} flushSpace sectionHeader>
          <Icon InlineIcon={icon} /> {title}
        </Heading>
      ),
    })
  }}
/>