Skip to content

feat(ui5-toolbar): implement WAI-ARIA toolbar keyboard navigation#13622

Draft
plamenivanov91 wants to merge 10 commits into
mainfrom
tb-arrow-nav-1-june
Draft

feat(ui5-toolbar): implement WAI-ARIA toolbar keyboard navigation#13622
plamenivanov91 wants to merge 10 commits into
mainfrom
tb-arrow-nav-1-june

Conversation

@plamenivanov91
Copy link
Copy Markdown
Contributor

@plamenivanov91 plamenivanov91 commented Jun 2, 2026

Replace Tab-through-every-item with a proper roving tabIndex +
arrow-key pattern per the WAI-ARIA toolbar pattern.

Keyboard handling

  • [Left]/[Right] navigate between toolbar items (RTL-aware)
  • [Home]/[End] jump to first/last item
  • [Tab]/[Shift+Tab] exit the toolbar to the next/previous tabbable
    element in the document (shadow-DOM-aware traversal)
  • [Up]/[Down] preventDefault to prevent accidental page scroll
  • Overflow popover is fully isolated: arrow keys, focusin, and Tab
    handling are suppressed when the popover is open, leaving the
    popover's natural Tab order intact

Roving tabIndex

  • Only the current toolbar item has tabIndex=0; all others get -1
  • _lastFocusedItem is restored on re-render so focus memory survives
    overflow layout changes
  • When the overflow button disappears (items move back to toolbar),
    focus moves to the last navigable item via focusForToolbarNavigation
  • Overflowed items are reset to tabIndex=0 so Tab works inside the
    overflow popover
  • Disabled standard items get tabIndex=-1 + aria-disabled="true"
  • On popover open, first interactive overflow item receives focus

ToolbarItemBase — hook API for complex items

New overridable methods (all no-ops in base):

  • getToolbarMovementInfo(): reports {currentIndex, itemCount} so the
    toolbar knows when a Left/Right press is at an internal boundary
  • moveWithinToolbarItem(isForward): advances focus within the item
  • focusForToolbarNavigation(isForward): direction-aware focus entry
  • setToolbarForcedTabIndex(tabIndex): distributes roving tabIndex
  • handlesOwnKeyboardNavigation: signals the item manages its own
    internal arrow traversal

ToolbarItem — three navigation paths

  1. _itemNavigation introspection: components that use ItemNavigation
    internally (e.g. SegmentedButton) are detected via duck-typing and
    their currentIndex/itemCount is read directly
  2. getToolbarMovementInfo interface: explicit opt-in for components
    that want to expose caret/selection position as boundary info
  3. Multi-child fallback: ToolbarItems with more than one slotted child
    (radio button groups, checkbox groups) are treated as a navigable
    group using _getCurrentNavigationState — no API needed on children

Input / TextArea — caret-aware boundary

Both implement getToolbarMovementInfo() using selectionStart and
value.length so Left/Right only exits to the next toolbar item once
the caret reaches the start or end of the text.

Templates

ToolbarButtonTemplate and ToolbarSelectTemplate: removed tabIndex JSX prop — setting it on the UI5 host broke F6Navigation's focus traversal.

Docs

  • Updated keyboard handling JSDoc on ui5-toolbar
  • Added WCAG 2.1 notes to accessibleName / accessibleNameRef

Tests (Toolbar.cy.tsx)

  • Arrow Left/Right navigation between items
  • Home/End jump to first/last
  • Up/Down does not scroll the page
  • First overflow item focused on popover open
  • Checkbox group: arrow traversal within group + boundary exit
  • Overflow button: ArrowRight wraps to first item
  • Fix: "overflow button disappears" test updated to check focused
    ToolbarButton directly instead of shadow .ui5-tb-item wrapper

Minor

  • RadioButton: remove stale @csspart root JSDoc entry

Fixes: #12945
JIRA: BGSOFUIPIRIN-7018

…2 (prevent page scroll on unhandled up/down)
Refactor toolbar keyboard handling around a single toolbar-owned flow.

- centralize arrow and tab navigation in Toolbar
- add movement-info and roving-tabindex hooks to ToolbarItemBase
- adapt grouped ToolbarItem content through shared internal target logic
- restore caret-aware movement for Input and TextArea
- apply forced tabindex to toolbar button/select templates
- remove redundant select-owned keyboard handling
- add Toolbar regressions for checkbox groups and overflow-button exit
@ui5-webcomponents-bot
Copy link
Copy Markdown
Collaborator

ui5-webcomponents-bot commented Jun 2, 2026

@ui5-webcomponents-bot ui5-webcomponents-bot temporarily deployed to preview June 2, 2026 08:48 Inactive
@ui5-webcomponents-bot ui5-webcomponents-bot temporarily deployed to preview June 2, 2026 09:45 Inactive
- Skip focusin/keydown handling when focus is inside the open overflow
  popover, preventing arrow-nav logic from firing inside the popover
- Skip forcedTabIndex on overflowed ToolbarButton/ToolbarSelect so
  overflow items keep their natural tab order
- Fix Tab-exit containment check to use shadow-DOM-aware walk
  (_isNodeInsideElement) instead of contains/shadowRoot.contains
- Remove own-fallback movement info path from ToolbarItem; items without
  _itemNavigation or getToolbarMovementInfo are now treated as single
  tab stops
- Drop dead WeakMap tab-index restoration machinery (no longer needed
  now that overflow items manage their own tab order)
@ui5-webcomponents-bot ui5-webcomponents-bot temporarily deployed to preview June 3, 2026 10:24 Inactive
@plamenivanov91 plamenivanov91 changed the title fix(ui5-toolbar): simplify toolbar item keyboard navigation feat(ui5-toolbar): implement WAI-ARIA toolbar keyboard navigation Jun 3, 2026
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.

[ui5-toolbar][A11y]: toolbar keyboard interaction does not align with WCAG requirement

2 participants