elastic / elastic/eui

[EuiSuperSelect] Dropdown stays open when Tab leaves the control (all options disabled)

Open
#9,964 1 comment 0 reactions 0 assignees View on GitHub
accessibility
Dominant language
TypeScript
Stars
6.4k
Forks
911
Avg merge
2d 11h
Merged PRs (30d)
65

Description

**Reported by**

Kibana accessibility audit — [elastic/kibana#276916](https://github.com/elastic/kibana/issues/276916)

**Describe the bug**

`EuiSuperSelect` leaves its dropdown open when keyboard focus leaves the collapsed control. This is most reproducible when **every option is disabled**: opening via keyboard never lands focus inside the listbox, so `Tab` moves to the next element while the overlay stays behind (WCAG SC 1.4.13).

This is not unique to one Kibana screen. Any SuperSelect whose options are all `disabled` (or any open state where focus never enters the list) has the same failure.

Two implementation issues in current `main` (`packages/eui/src/components/form/super_select/super_select.tsx`):

1. **`openPopover` cannot focus a disabled item.** It walks past disabled options, then falls back to a disabled index and calls `.focus()` on that button. Disabled buttons cannot take focus, so focus stays on the toggle. The listbox has `tabIndex={0}` but is never focused.

2. **`closePopover` always restores focus to the toggle.** That is correct for Escape / click-outside / selection, but wrong when the user has already tabbed away. Consumers cannot work around this: `isOpen={isOpen || state.isPopoverOpen}` cannot force-close, so the only consumer-side dismiss is remounting the component.

Tab-to-close only exists on **items** (`onItemKeyDown` selects + closes). `EuiInputPopover`’s tab handler only runs for Tab **inside the panel**. The control has no blur/Tab close, and SuperSelect sets `disableFocusTrap`, so focus can leave while the overlay stays.

**Impact and severity**

Keyboard users (and anyone tabbing through a form) can leave a stale overlay covering the next field. Kibana hit this on Synthetics → Settings → Private locations → **Agent policy** when every policy is already used (all options disabled). We considered a remount-on-blur workaround in Kibana ([PR #277692](https://github.com/elastic/kibana/pull/277692)) and are **not** landing it — this belongs in EUI so every SuperSelect is fixed.

No good consumer workaround: calling `closePopover` yanks focus back to the control.

**Environment and versions**

- EUI version: `118.0.0` (Kibana) — same `openPopover` / `closePopover` logic is still on `elastic/eui` `main`
- Kibana version: 9.5.0
- Browser: Chrome
- OS: macOS

**Minimum reproducible sandbox**

Repro on https://eui.elastic.co/docs/components/forms/selection/super-select (or any SuperSelect playground) by making **all** options `disabled`:

```tsx
import React, { useState } from 'react';
import { EuiSuperSelect } from '@elastic/eui';

export default () => {
const options = [
{ value: 'one', inputDisplay: 'Option one', disabled: true },
{ value: 'two', inputDisplay: 'Option two', disabled: true },
];
const [value, setValue] = useState();

return (
<>

Next field

);
};
```

**To Reproduce**

1. Render `EuiSuperSelect` with every option `disabled: true`.
2. Keyboard-focus the control.
3. Press `ArrowDown` (or `Space`) to open the list.
4. Press `Tab`.
5. Focus moves to the next element; the dropdown stays open.

**Expected behavior**

The dropdown closes when focus leaves the component, without moving focus back to the toggle. Native `` dismisses on Tab-away.

Suggested EUI fix (small):

- Split `closePopover` so restore-focus is optional (`closePopover({ restoreFocus })`), and skip restore when focus has already left.
- Handle `Tab` on the **control** while the popover is open: close without restoring focus.
- If no enabled option exists: do not open, or focus the listbox so panel tab-to-close can run (still needs `restoreFocus: false`, or `handleTabNavigation` → `closePopover` will steal focus back).

**Screenshots (Optional)**

Recording from the Kibana a11y report: https://github.com/user-attachments/assets/8814602b-29ce-41f8-b886-a85d6e785b8f

**Additional context (Optional)**

- Kibana issue: https://github.com/elastic/kibana/issues/276916
- Rejected Kibana workaround PR: https://github.com/elastic/kibana/pull/277692
- Related a11y: [WCAG 2.2 SC 1.4.13 Content on Hover or Focus](https://www.w3.org/WAI/WCAG22/Understanding/content-on-hover-or-focus.html)
- Prior SuperSelect focus work: https://github.com/elastic/eui/pull/7650 (did not cover the all-disabled / focus-never-enters-list case)

Contributor guide

Open the contributing guide

Research direction

Start in packages/eui/src/components/form/super_select/super_select.tsx and reproduce the all-disabled case from the issue or the SuperSelect documentation playground. Trace openPopover, closePopover, the control key handling, and the panel Tab handling; done means Tab-away closes the dropdown while preserving focus on the next element, including when no option can receive focus.

Written by the indexing model from the issue text.

Assessment

Tech stack
react, typescript
Domain
accessibility, frontend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.