feat(ui5-toolbar): implement WAI-ARIA toolbar keyboard navigation#13622
feat(ui5-toolbar): implement WAI-ARIA toolbar keyboard navigation#13622plamenivanov91 wants to merge 15 commits into
Conversation
…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
|
🚀 Deployed on https://pr-13622--ui5-webcomponents-preview.netlify.app |
- 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)
…ling when up/down keys are pressed
👋 Heads-up: dev close is in effectThanks for the contribution! This repository is currently in dev close ahead of release This PR appears to introduce public-API changes (detected by diffing the Custom Elements Manifest against the latest published version on npm):
Could you please hold off on merging into If this change must ship in the current release, please request a review from one or two members of @UI5/ui5-team-webc so the team can sign off explicitly.
Posted automatically by the Dev Close Notice workflow. |
…avState interface
Replace the index-based ToolbarMovementInfo interface with a boolean
ToolbarArrowNavState { atLeftEnd, atRightEnd }. Items now report
whether they can absorb a keypress rather than exposing their internal
item count and cursor position.
- ToolbarItemBase/Item: remove ToolbarMovementInfo, ToolbarMovementEnabler,
moveWithinToolbarItem. Add getArrowNavState() returning
{ atLeftEnd, atRightEnd } (delegates to single child if present,
falls back to multi-child position check). Remove dead no-op branch
in focusForToolbarNavigation().
- Input/TextArea: getToolbarMovementInfo ? getArrowNavState, returning
{ atLeftEnd: caret===0, atRightEnd: caret>=len }.
- Toolbar: _applyRovingTabIndex sets all items to tabIndex=0 (Tab exits
naturally). _setCurrentItem only tracks _lastFocusedItem, no tabIndex
manipulation. Arrow nav guards via getArrowNavState(). Home/End also
guarded so caret-aware items (Input, TextArea) can handle them
internally. _moveToNext/_moveToPrev clamped at boundaries, no wrap.
- Tests: add no-wrap boundary, Tab navigation, Input caret-aware nav,
and Home key non-interception tests. Fix ToolbarSelect tests using
incorrect "click" event name and jQuery .get(0) anti-pattern.
Replace Tab-through-every-item with a proper arrow-key navigation
pattern per the WAI-ARIA toolbar pattern.
Keyboard handling
[Left]/[Right] navigate between toolbar items (RTL-aware). At the
first/last item, the keypress is absorbed and nothing happens (no wrap).
[Home]/[End] jump to first/last item, unless the focused item is
caret-aware (Input, TextArea), in which case the item handles the key.
[Tab]/[Shift+Tab] exit the toolbar — all items have tabIndex=0 so
the browser's natural tab order handles traversal out of the toolbar.
Overflow popover is fully isolated: arrow keys and focusin handling
are suppressed when the popover is open, leaving the popover's natural
Tab order intact.
On popover open, first interactive overflow item receives focus.
Tab navigation
All toolbar items are kept at tabIndex=0. Tab exits the toolbar
naturally without any roving tabIndex management.
_lastFocusedItem tracks the last focused item for re-entry (e.g.
Shift+Tab back into the toolbar) but does not change tabIndex values.
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.
ToolbarItemBase — hook API for complex items
New overridable methods (all no-ops in base):
getArrowNavState(): reports { atLeftEnd, atRightEnd } so the toolbar
knows when a Left/Right/Home/End press is at an internal boundary and
should be handled by the item rather than moving to the next item.
focusForToolbarNavigation(isForward): direction-aware focus entry.
setToolbarForcedTabIndex(tabIndex): distributes tabIndex to the item's
focus target.
ToolbarItem — three navigation paths
_itemNavigation introspection: components that use ItemNavigation
internally (e.g. SegmentedButton) are detected via duck-typing and
their currentIndex/itemCount is used to derive boundary state.
getArrowNavState interface: explicit opt-in for components that want
to expose caret/selection position as boundary info (e.g. Input).
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 getArrowNavState() returning { atLeftEnd: caret===0,
atRightEnd: caret>=len } so Left/Right/Home/End only exits to the
next toolbar item once the caret is at 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.
Restored @csspart root JSDoc entry on ui5-radio-button.
Tests (Toolbar.cy.tsx)
Arrow Left/Right navigation between items.
No-wrap: ArrowLeft on first item and ArrowRight on last item do nothing.
Home/End jump to first/last.
Tab navigates between all items (all have tabIndex=0).
Input caret-aware: Left/Right exits toolbar only at caret boundary.
Home key not intercepted by toolbar when Input has focus.
First overflow item focused on popover open.
Checkbox group: arrow traversal within group + boundary exit.
Overflow button: ArrowLeft navigates to last visible toolbar item.
Updated "overflow button disappears" test to check focused
ToolbarButton directly instead of shadow .ui5-tb-item wrapper.
JIRA: BGSOFUIPIRIN-7018
Fixes: #12945