Problem Statement
Select elements built using native HTML <select> wrappers are notoriously difficult to style consistently across browsers. Consequently, engineering teams often build custom dropdowns. However, these custom implementations frequently fail on accessibility, leaving keyboard-only users and screen readers locked out of interaction.
Your task is to build a custom Autocomplete (Combobox) component in React. Users must be able to:
- Type text to filter a list of suggestions.
- Navigate the suggestions list using their keyboard (Arrow Up and Arrow Down) with a highlight indicating the active index.
- Select suggestions using the
Enterkey or mouse clicks. - Dismiss the list by pressing
Escapeor clicking outside the component. - Have screen readers announce highlighted selections and state changes.
Requirements
Functional
- Filtering Logic: Accept a list of string options and filter them based on the current input value case-insensitively.
- Dropdown Visibility: The dropdown list of filtered options should only open when the input is focused or has content. It should close when a value is selected, when
Escapeis pressed, or when clicking outside. - Keyboard Management:
- Arrow Down: Move selection highlight to the next item in the filtered options list. If on the last item, wrap focus to the first item.
- Arrow Up: Move selection highlight to the previous item. If on the first item, wrap focus to the last item.
- Enter: Select the currently highlighted option, update the input text, and close the dropdown.
- Escape: Close the dropdown and blur the input.
- Accessibility (ARIA Roles):
- The outer input must have
role="combobox",aria-autocomplete="list", andaria-expanded(true/false) matching the dropdown open state. - The dropdown container must be a
<ul>withrole="listbox". - Each suggestion must be a
<li>withrole="option"andaria-selected(true/false) indicating if it is highlighted. - The input must use
aria-activedescendantset to the uniqueidof the highlighted suggestion. This allows screen readers to read the active item without moving the document's physical focus off the input field.
- The outer input must have
Non-Functional
- Implement a custom React hook
useClickOutsideto handle clicks outside the input/dropdown to dismiss the dropdown container. - Prevent page scrolling when pressing Arrow Up or Arrow Down keys while focusing on the combobox.
- Provide clean, type-safe custom interfaces in TypeScript.
Concepts Tested
- Compound states (dropdown toggles, text filters, list highlights).
- Keyboard event handling (
onKeyDowncapture, scroll prevention). - Ref forwarding and tracking DOM clicks outside bounded coordinates.
- ARIA active-descendant focus management strategy.
