ARIA 1.1 combo box

A combo box using the ARIA 1.1 pattern.

The ARIA 1.0 pattern created a confusing accessibility tree as the listbox became a child of the textbox.

The 1.1 pattern attempted to resolve this by creating a composite widget made from a combobox with a child textbox. This actually created a more problems then it solved and eventually this was resolved with the ARIA 1.2 pattern.

This example creates the ARIA 1.1 pattern. While it is technically valid, it is not recommended and is only here as an example.

import { useState } from 'react';
import { ComboBox, useTokenSearch } from '@citizensadvice/react-combo-boxes';

const options = ['Apple', 'Orange', 'Lemon', 'Raspberry', 'Strawberry'];

function renderWrapper(
  { key, ...props },
  { expanded },
  { id, 'aria-labelledby': ariaLabelledBy },
) {
  return (
    <div
      key={key}
      {...props}
      role="combobox"
      aria-owns={`${id}_listbox`}
      aria-expanded={expanded ? 'true' : 'false'}
      aria-labelledby={ariaLabelledBy}
    />
  );
}

function renderInput(props, state, { id }) {
  return (
    <input
      {...props}
      role={null}
      aria-expanded={null}
      aria-owns={null}
      aria-controls={`${id}_listbox`}
    />
  );
}

export function Example() {
  const [value, setValue] = useState(null);
  const [search, setSearch] = useState(null);
  const filteredOptions = useTokenSearch(search, { options });
  const [managedFocus, setManagedFocus] = useState(true);

  return (
    <>
      <label
        id="select-label"
        htmlFor="select"
      >
        Select
      </label>
      <ComboBox
        id="select"
        aria-labelledby="select-label"
        value={value}
        onValue={setValue}
        onSearch={setSearch}
        options={filteredOptions}
        renderWrapper={renderWrapper}
        renderInput={renderInput}
        managedFocus={managedFocus}
      />

      <label>
        <input
          type="checkbox"
          onChange={({ target: { checked } }) => setManagedFocus(checked)}
          checked={managedFocus}
        />{' '}
        Toggle managed focus
      </label>

      <label htmlFor="output">Current value</label>
      <output
        htmlFor="select"
        id="output"
      >
        {JSON.stringify(value, undefined, ' ')}
      </output>
    </>
  );
}