[EuiToolTip] Tooltip does not dismiss on mouse-out for aria-disabled anchors
- Dominant language
- TypeScript
- Stars
- 6.4k
- Forks
- 911
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 65
Description
**Reported by**
Found while reviewing https://github.com/elastic/eui/pull/9992.
**Describe the bug**
When an `EuiToolTip` wraps an element that is semantically disabled via `aria-disabled` (i.e. an EUI button with `isDisabled` + `hasAriaDisabled`) rather than the native `disabled` attribute, the tooltip appears on hover but **never dismisses on mouse-out**. It stays on screen until something else forces it to unmount.
`euiToolTipAnchor` neutralises pointer events only for natively disabled elements:
https://github.com/elastic/eui/blob/main/packages/eui/src/components/tool_tip/tool_tip.styles.ts#L86-L97
```css
*[disabled] {
pointer-events: none;
}
```
That rule exists precisely to prevent this failure — its comment reads *"Disabled elements don't fire mouse events, which means leaving a disabled element wouldn't trigger the onMouseOut and hide the tooltip."*
`hasAriaDisabled` swaps `disabled` for `aria-disabled="true"`, so the selector stops matching and `pointer-events` stays `auto`. The element then becomes a live hit-test target, and `useEuiDisabledElement`'s capture-phase listeners call `stopImmediatePropagation()`/`stopPropagation()` on `mouseout`/`mouseover`. React delegates those at the root to synthesise `onMouseEnter`/`onMouseLeave`, so the anchor's `onMouseLeave` → `hideToolTip()` never runs.
**Impact and severity**
Moderate. A stale tooltip can obscure adjacent UI until the user interacts elsewhere. Keyboard focus/blur is unaffected — this is mouse-only.
These call sites already pair `isDisabled` with `hasAriaDisabled` inside a tooltip and should all be affected:
- `packages/eui/src/components/basic_table/default_item_action.tsx`
- `packages/eui/src/components/basic_table/collapsed_item_actions.tsx`
- `packages/eui/src/components/context_menu/context_menu_item.tsx`
- `packages/eui/src/components/button/button_group/button_group.tsx`
- `packages/eui/src/components/list_item_layout/_list_item_layout.tsx`
- `packages/eui/src/components/date_picker/auto_refresh/auto_refresh.tsx`
Workaround: apply `pointer-events: none` to the disabled element yourself, which is what the tooltip docs already advise for custom elements.
**Environment and versions**
- EUI version: `main` (the affected style rule and hook are both on `main`; reproduced against the branch in #9992, which newly surfaces it on flyout menu actions)
- Browser: Chromium (via Playwright)
**To Reproduce**
```tsx
```
1. Move the pointer onto the button — the tooltip appears.
2. Move the pointer away.
3. The tooltip remains visible indefinitely.
Note: the pointer must move across the element in steps. Instantly warping the cursor (as some automated tests do) will not generate the intermediate `mouseover` and can mask the bug.
**Expected behavior**
The tooltip should hide on mouse-out, matching the behavior of a natively `disabled` anchor.
**Additional context**
Verified fix — extending the existing rule restores mouse-out dismissal while leaving keyboard focus/blur working:
```diff
- *[disabled] {
+ *[disabled],
+ *[aria-disabled='true'] {
pointer-events: none;
}
```
A/B measured in-browser on the same story:
| | hover on | hover off |
| --- | --- | --- |
| `pointer-events: auto` (current) | tooltip shown | **tooltip still shown** |
| `pointer-events: none` (fix) | tooltip shown | dismissed |
This also aligns the component with its own documentation, which already tells consumers to set `pointer-events: none` on `aria-disabled` elements:
https://github.com/elastic/eui/blob/main/packages/website/docs/components/display/tooltip.mdx#L138
Existing coverage misses this because `tool_tip.spec.tsx` fires synthetic events directly at the anchor wrapper, bypassing the child entirely:
https://github.com/elastic/eui/blob/main/packages/eui/src/components/tool_tip/tool_tip.spec.tsx#L59-L79
A regression test should use `cy.realHover()` on the child element and then move the pointer away.
Contributor guide
Assessment
This issue has not been assessed yet.