Skip to content

fix(flyout): content roles, tooltip focus trap, Escape stacking, Autocomplete a11y - #684

Open
blvdmitry wants to merge 4 commits into
fix/numeric-inputsfrom
fix/flyout-a11y
Open

blvdmitry wants to merge 4 commits into
fix/numeric-inputsfrom
fix/flyout-a11y

Conversation

@blvdmitry

Copy link
Copy Markdown
Contributor

Summary

PR 4 of the Phase −1 bug-fix stack.

  • Content role (Flyout/FlyoutContent.tsx): role="tooltip" is now used only for hover content that doesn't trap focus. Otherwise the role comes from trapFocusMode. DropdownMenu submenus are now role="menu". Hover Popovers (content-menu) keep whatever role is passed in their attributes, and no longer get tooltip.
  • Tooltip focus trap (Flyout/FlyoutControlled.tsx): the default trapFocusMode is now false for triggerType="hover" and stays "dialog" otherwise.
    • Popover hover (content-menu) and DropdownMenu (action-menu) already pass their mode explicitly, so they're unchanged.
    • Plain tooltips no longer start a dialog trap. That trap would also aria-hide the rest of the page through TrapScreenReader once the tooltip had focusable content.
  • Escape stacking (Flyout/FlyoutControlled.tsx, hooks/useIsDismissible.ts):
    • The callback returned by useIsDismissible takes a new optional { strict } argument. Strict mode skips the "independent, not nested" shortcut.
    • The Escape handler now closes a flyout only when it's the latest one in the queue, so only the topmost open flyout closes. The click guard used for outside clicks is unchanged.
    • The change is additive to the public useIsDismissible signature.
  • Popover.Dismissible (Popover/Popover.tsx): now passes reason: "close-button".
  • Dead code (Flyout/Flyout.constants.ts): removed defaultStyles, resetStyles and the unused mouseLeave. None of them were exported publicly.
  • Autocomplete describedby (Flyout/FlyoutTrigger.tsx): aria-describedby is now set only for hover triggers. Focus triggers like Autocomplete are already linked with aria-controls.
  • Autocomplete scrolling (Autocomplete/Autocomplete.tsx): the highlighted option is scrolled into view on ArrowUp/ArrowDown with scrollIntoView({ block: "nearest" }).
  • Menu double highlight (MenuItem/MenuItem.module.css): CSS-only fix.
    • The :hover background is ignored in keyboard mode (data-rs-keyboard on <html>), but only for role="menuitem" and role="option" items, so standalone MenuItems such as navigation keep their hover.

Tests (all fail on the base branch and pass here):

  • Flyout.stories.tsx: test: content roles, test: escape closes the latest flyout
  • Autocomplete.stories.tsx: test: keyboard highlight scroll, which also checks aria-describedby and aria-controls
  • Popover.stories.tsx: the existing dismissible story now expects { reason: "close-button" }

Related Issue

Phase −1 of the headless-extraction plan.

Screenshots / Recordings

The only visual change: menu items no longer show a hover background while keyboard navigation mode is active.

Notes for Reviewers

  • This PR is stacked on fix(numeric-inputs): NumberField, ProgressBar, Slider and PinField value handling #683 (fix/numeric-inputs).
  • To decide (MenuItem hover): keyboard mode only resets on mousedown. After keyboard navigation, hovering menu items shows no highlight until the user clicks. That's what "suppress :hover in keyboard mode" means, but please confirm the trade-off.
  • To decide (Escape): with two independent click Popovers open at the same time (only possible with disableCloseOnOutsideClick), Escape used to close both. It now closes only the latest one.
  • To decide (Flyout default): Flyout used directly with triggerType="hover" and no trapFocusMode no longer traps focus. Interactive hover content needs an explicit mode, as Popover already passes.
  • Untested: the MenuItem CSS change has no test, because play functions can't trigger a real CSS :hover.
  • Results: pnpm lint passes. pnpm test:browser has 618 passing and 1 failing, Image > onLoad, which also fails on main in this sandbox because it has no network access.

Generated by Claude Code

…tocomplete a11y

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VD8gaUA7K5gseR3E5X8cBA

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants