feat(treelist): full APG tree keyboard pattern + aria-level/posinset/setsize#3409
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR Analysis Report📚 Storybook PreviewView Storybook for this PR 🧪 Sandbox PreviewView Sandbox for this PR No new or modified components detected. Bundle Size Summary
Accessibility AuditStatus: No accessibility violations detected. Generated by PR Enrichment workflow | Storybook | Sandbox | View full report |
|
Hmm are we able to use useListFocus? If not, can we break out a useTreeFocus? |
d58e46f to
f571efb
Compare
f571efb to
7328f79
Compare
|
Good question. So going with your second suggestion: extracting a dedicated |
ad97afb to
10e8607
Compare
Problem
TreeListrenders arole="tree"withrole="treeitem"rows but does not implement the WAI-ARIA APG Tree View keyboard model. Keyboard users could not navigate the tree with arrow keys, there was no roving tab stop, and treeitems were missing the structural ARIA (aria-level,aria-posinset,aria-setsize) that assistive tech needs to convey hierarchy and position. The interim fix in #3344 made parent rows keyboard-expandable via a real toggle button; this PR completes the full pattern (complex-1).Fix
A standalone keyboard handler on the
role="tree"container plus roving tabindex managed withinTreeList— no dependency on any shared composite/list hook, since tree navigation (visible-order traversal + expand/collapse) is its own model:tabIndex=0) at a time; the rest are-1. The default tab stop is the selected item, else the first enabled row. Arrow keys move the tab stop and focus together.<li>) is the roving-tabindex focus owner. Inner actions (link/button/chevron) are removed from the tab order (tabIndex=-1); Enter/Space on the row forwards activation to the inner action, preserving existing click/href/onClick behavior. The:focus-visibleoutline is bound to each row's own treeitem via a scoped StyleX marker so focusing a parent doesn't leak the ring onto descendants.ArrowDown/ArrowUp— next/previous visible row, skipping disabled.ArrowRight— collapsed parent → expand; expanded parent → first child; leaf → no-op.ArrowLeft— expanded parent → collapse; else → move to parent row.Home/End— first/last visible row.Enter/Space— activate the row's action, or toggle expansion for a parent without its own action.aria-level,aria-posinset, andaria-setsize, threaded through the existingrenderItemsrecursion.Order/parent/child resolution uses
querySelectorAll('[role="treeitem"]')over the tree ref (visible DOM order) plusdata-tree-*attributes for level/id/disabled.Tests
Added coverage in
TreeList.test.tsx:aria-level/aria-posinset/aria-setsizeat multiple depths; roving tabindex (exactly one tabbable row; defaults to selected); ArrowDown/Up (incl. skipping disabled); ArrowRight expand-then-enter-child and leaf no-op; ArrowLeft collapse-then-parent; Home/End; Enter/Space activation and parent-toggle fallback; typeahead. All existing tests continue to pass.pnpm -F @astryxdesign/core typecheckclean,eslintclean,vitest45/45 pass.Part of the accessibility & keyboard-management program (#3343). Completes
complex-1(the full APG tree pattern on top of the interim #3344 toggle fix).