[EuiToolTip] Show tooltip only on keyboard focus#9624
[EuiToolTip] Show tooltip only on keyboard focus#9624weronikaolejniczak merged 15 commits intoelastic:mainfrom
Conversation
e60a2df to
94b6fd9
Compare
94b6fd9 to
23b6524
Compare
There was a problem hiding this comment.
Pull request overview
Updates EuiToolTip focus behavior so tooltips only persist/show on keyboard focus (:focus-visible), preventing unwanted tooltip re-open/persistence for mouse click focus and programmatic focus return (e.g., overlay close).
Changes:
- Gate tooltip “focus show” logic behind
:focus-visiblechecks (including initial mount/autoFocus handling). - Add RTL helper
simulateFocusVisible()and update Jest tests to reflect the new focus-visible behavior. - Adjust Cypress spec to use real keyboard tabbing for focus-visible coverage and add a changelog entry.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/eui/src/components/tool_tip/tool_tip.tsx | Only show/persist tooltip on :focus-visible focus; keep hover behavior |
| packages/eui/src/components/tool_tip/tool_tip_anchor.tsx | Thread focus event through anchor → tooltip handler |
| packages/eui/src/test/rtl/component_helpers.ts | Add simulateFocusVisible() RTL helper for jsdom modality gap |
| packages/eui/src/components/tool_tip/tool_tip.test.tsx | Update unit tests for keyboard-focus-visible vs click-focus behavior |
| packages/eui/src/components/tool_tip/tool_tip.spec.tsx | Update Cypress tests to use realPress('Tab') for keyboard focus-visible |
| packages/eui/src/components/tool_tip/tool_tip.stories.tsx | Add manual QA stories for focus-visible/focus-return scenarios |
| packages/eui/src/components/table/table_header_cell.test.tsx | Switch tooltip assertions from focus to hover interactions |
| packages/eui/src/components/color_picker/saturation.test.tsx | Use simulateFocusVisible() before focus assertions |
| packages/eui/src/components/color_picker/hue.test.tsx | Use simulateFocusVisible() before focus assertions |
| packages/eui/src/components/color_picker/color_picker_swatch.test.tsx | Use simulateFocusVisible() before focus assertions |
| packages/eui/src/components/button/button_group/button_group.test.tsx | Use simulateFocusVisible() before focus assertions |
| packages/eui/changelogs/upcoming/9624.md | Document tooltip behavior fixes |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
mgadewoll
left a comment
There was a problem hiding this comment.
I checked the listed components, everything works as expected. 👍
I left some general thoughts, and I think the Copilot notes are generally worth to address.
ℹ️ I did notice that EuiMarkdownEditor doesn't actually show a tooltip on keyboard focus, but that's also currently the behavior on production. (We might want to improve that at some point, but it's not related to this PR)
Return cleanup function from simulateFocusVisible.
|
@mgadewoll I addressed all your comments. Thank you for the review, Lene 🙏🏻 Let's see what Copilot has to say and how EUI CI and Kibana CI responds. Thanks for pushing me to do it early, I already found some issues 😄 |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 17 out of 17 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
buildkite test this |
| const { getByRole } = render(<MyComponent />); | ||
| const cleanup = focusEuiToolTipTrigger(getByRole('button')); | ||
| expect(getByRole('tooltip')).toBeInTheDocument(); | ||
| cleanup(); |
There was a problem hiding this comment.
Based on #9624 (comment) - We should probably highlight using try...finally here to ensure the cleanup is always called (independent of potential assertion failures).
There was a problem hiding this comment.
We agreed to update this after merging.
mgadewoll
left a comment
There was a problem hiding this comment.
🟢 Code changes are LGTM. The behavior was tested manually and works as expected and affected components don't have any regression.
Thanks for adding the additional ESLint rule 🙏
💚 Build SucceededHistory
|
💚 Build Succeeded
History
|
## Dependency updates - `@elastic/eui` - v114.3.0 ⏩ v115.0.0 - `@elastic/eslint-plugin-eui` - v2.11.1 ⏩ v2.12.0 ## Package updates ### Summary > [!NOTE] > Please be mindful that as the Design System team we are not aware of your specific area pre-requisites and testing setups, and are realistically not capable of testing the entirety of Kibana. That being said, we want to do our best at migrating breaking changes. All tooltip usages were updated accordingly but there might be mistakes and bugs. That's why, **I would advise every reviewer to smoke-test their domain tooltips**. We'd also greatly appreciate your help with adjusting anything that may be wrong 🙏🏻 https://github.com/user-attachments/assets/2da29c89-aba1-40f9-ae3e-13790a34aedf - tooltips **no longer appear on programmatic focus return** (e.g. when closing flyouts, modals) - unless you're using the keyboard to navigate, - tooltips **do not persist on mouse click** which was annoying for the sighted users and tricky to handle in automated testing environments, - there's **no timeout-based delay** that's inconsistent across Kibana and makes the UI seem laggy, - we made the **animation simpler** and tooltip content **font-size smaller**, resulting in a better visual hierarchy, -VoiceOver no longer has problems with **announcing tooltip content** on trigger focus. These changes have been developed with [SC 1.4.13 Content on Hover or Focus](https://www.w3.org/TR/WCAG21/#content-on-hover-or-focus) in mind while providing the best possible UX. ### `@elastic/eui` [v115.0.0](https://github.com/elastic/eui/blob/main/packages/eui/changelogs/CHANGELOG_2026.md) - Updated `EuiToolTip` default font size from 14px to 12px ([#9627](elastic/eui#9627)) - Updated `EuiToolTip` show animation to opacity-only with a 150ms grace period delay, preventing visual flickering when quickly hovering over multiple tooltip triggers ([#9626](elastic/eui#9626)) - Updated `EuiToolTip` to respect input modality. Tooltip no longer persists on mouse-click focus or shows on programmatic focus return. ([#9624](elastic/eui#9624)) **Bug fixes** - Fixed `uiPlugins[].button` type to allow UI plugins to not have a toolbar button in `EuiMarkdownEditor` ([#9634](elastic/eui#9634)) - Fixed `EuiToolTip` self-hiding when the mouse moves over child elements within the trigger ([#9626](elastic/eui#9626)) **Breaking changes** - Removed `delay` prop and `ToolTipDelay` type from `EuiToolTip` and `EuiIconTip` ([#9626](elastic/eui#9626)) - Removed `waitForEuiToolTipVisible` and `waitForEuiToolTipHidden` RTL test helpers; tooltip show/hide is now synchronous so direct assertions can be used instead ([#9626](elastic/eui#9626)) **Accessibility** - Fixed invalid nested interactive elements in `EuiMarkdownEditor` by removing `role` from the drop zone wrapper. ([#9625](elastic/eui#9625)) ### `@elastic/eslint-plugin-eui` [v2.12.0](https://github.com/elastic/eui/blob/main/packages/eslint-plugin/changelogs/CHANGELOG_2026.md) - Added a new `prefer-tooltip-trigger-focus-test-utility` rule that flags `fireEvent.focus()` inside `it`/`test` blocks that also query for a tooltip. The rule auto-fixes to `focusEuiToolTipTrigger` from EUI's RTL test utilities. ([#9624](elastic/eui#9624)) --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Summary
EuiToolTiponly on keyboard focus (:focus-visible), not on mouse-click focus or programmatic focus return.element.matches(':focus-visible')inonFocushandler before settinghasFocusand showing the tooltip.simulateFocusVisibletest helper to workaround jsdom not tracking input modality.Testing videos
Kapture.2026-04-29.at.13.59.03.mp4
Kapture.2026-04-29.at.14.01.18.mp4
Kapture.2026-04-29.at.14.01.57.mp4
Kapture.2026-04-29.at.14.03.44.mp4
Kapture.2026-04-29.at.14.04.27.mp4
Kapture.2026-04-29.at.14.05.08.mp4
Kapture.2026-04-29.at.14.06.27.mp4
Kapture.2026-04-29.at.14.05.53.mp4
Kapture.2026-04-29.at.14.50.10.mp4
Accessibility
The behavior has been approved by a11y consultants. It satisfies the Success Criterion 1.4.13 Content on Hover or Focus:
Some more details here.
API Changes
N/A
Screenshots
N/A
Impact Assessment
Note: Most PRs should be tested in Kibana to help gauge their Impact before merging.
fireEvent.focus()in tests will need to addsimulateFocusVisible. Found 1 such test in Kibana (x-pack/solutions/security/packages/kbn-securitysolution-exception-list-components/src/value_with_space_warning/value_with_space_warning.test.tsx:44).Impact level: 🟢 Low
Release Readiness
Documentation: {link to docs page(s)}Figma: {link to Figma or issue}Migration guide: {steps or link, for breaking/visual changes or deprecations}Adoption plan (new features): {link to issue/doc or outline who will integrate this and where}QA instructions for reviewer
I added temp stories to make testing easier:
FocusClickVsKeyboard story
ArrowKeyNavigation story
FocusReturnFlyout
FocusReturnModal
FocusReturnPopover
Smoke-test affected components
EuiToolTipRefimperatively (showToolTip/hideToolTip); no story available, can skipEuiToolTipRefChecklist before marking Ready for Review
light/dark modes, high contrast, mobile, Chrome/Safari/Edge/Firefox, keyboard-only, screen readerCodeSandbox andKibanaQA: Tested docs changesCypress, and VRTBreaking changes: Addedbreaking changelabel (if applicable)Reviewer checklist