Skip to content

feat(ui): add accessible VS Code-parity Tree - #1065

Draft
EhabY wants to merge 3 commits into
mainfrom
feat/ui-tree-suite
Draft

feat(ui): add accessible VS Code-parity Tree#1065
EhabY wants to merge 3 commits into
mainfrom
feat/ui-tree-suite

Conversation

@EhabY

@EhabY EhabY commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a controlled, accessible Tree component to @repo/ui with current VS Code workbench behavior and visuals.

  • Flat, data-driven row projection with aria-activedescendant
  • Independent focus and selection, including hidden selection behavior
  • Single and multi-selection, ranges, modifier selection, and scoped select-all
  • Arrow, Home, End, PageUp, PageDown, Escape, and buffered type navigation
  • Single-click and double-click expansion modes
  • Focusable but non-selectable disabled rows
  • Sticky scroll with pinned-row pointer, keyboard, accessibility, and native-style shadow behavior
  • Explorer styling, indent guides, actions, high-contrast support, and Storybook coverage

Closes #1037

Architecture

flowchart LR
  accTitle: Tree architecture
  accDescr: Data and input flow through pure Tree modules into the React and DOM adapter.

  Props[Nodes and controlled props] --> Model[treeModel.ts]
  Events[Pointer and keyboard events] --> Policy[treePolicy.ts]
  Policy --> Commands[Tree commands]
  Model --> Transition[treeTransition.ts]
  Commands --> Transition
  Transition --> Adapter[useTreeAdapter.ts]
  Adapter --> Rows[Tree.tsx and TreeRow.tsx]
  Adapter --> Sticky[StickyScroll.tsx]
Loading

treeModel.ts, treePolicy.ts, and treeTransition.ts are pure. useTreeAdapter.ts is the thin React and DOM boundary. Rendering stays split between normal rows and sticky behavior rather than placing all state and behavior in one component.

The flat visible model supports future windowing, but this Tree is not currently virtualized.

Diff composition

Measured against origin/main...HEAD with git diff --numstat:

Area Added Deleted
Production UI source 1,832 0
Automated tests and shared test helpers 1,734 0
Stories and visual fixtures 348 1
Documentation 115 8
Build and workspace configuration 31 2
Total 4,060 11

Production source to automated test code is approximately 1.06:1. Counting stories and visual fixtures as test support gives 1,832 production lines to 2,082 test and visual verification lines.

Performance

Measured in development-mode jsdom against the first data-driven architecture at d794977:

  • Keyboard navigation wall time improved by approximately 44% to 57%
  • Selection changes improved by approximately 21% to 31%
  • Expansion improved at small and medium sizes and was approximately 6% slower at 2,000 rows

These are relative development-mode measurements, not browser frame-time guarantees.

Validation

  • pnpm test:webview --reporter=dot: 45 files, 381 tests passed
  • Focused Tree suite: 10 files, 63 tests passed
  • pnpm typecheck
  • pnpm format:check
  • pnpm storybook:ci
  • git diff --check
Implementation plan and decisions
  1. Keep the model, input policy, and interaction transitions pure.
  2. Keep React state, controlled callbacks, refs, event normalization, and DOM effects in one thin adapter.
  3. Render a flat visible row projection while retaining complete hierarchy data for hidden focus and selection behavior.
  4. Preserve VS Code selection, expansion, keyboard, accessibility, sticky-scroll, and visual behavior.
  5. Replace the external mutable store and context layers unless measurements justify them.
  6. Document the architecture with a compact Mermaid diagram.
  7. Split the large component test into focused core, row, selection, keyboard, and sticky suites with shared helpers.
  8. Validate behavior, type safety, formatting, linting, builds, Storybook, test counts, and measured interaction performance.

This pull request description was updated by Coder Agents on behalf of @EhabY.

@EhabY EhabY self-assigned this Aug 5, 2026
@EhabY
EhabY force-pushed the feat/ui-tree-suite branch from 510e466 to 309f05d Compare August 5, 2026 14:11
Base automatically changed from feat/ui-package-gaps to main August 7, 2026 10:52
@EhabY
EhabY force-pushed the feat/ui-tree-suite branch 9 times, most recently from 65c0990 to efc2a15 Compare August 12, 2026 13:23
Tree and TreeItem components with ARIA tree semantics, roving tabindex,
hierarchy navigation, buffered type-ahead, multi-select, sticky scroll,
and VS Code-parity styling across the shared, Modern, and stable
styles. Collapsed branches unmount and row re-renders are scoped to the
rows that changed. Review hardening is folded in: range-anchor seeding,
selection-echo tab stop handling, modifier clicks that never toggle
expansion, sticky backgrounds only while pinned, React 19 ref cleanups,
Shift+Home/End ranges, Escape to clear, and Ctrl+Shift+A passthrough.

Closes #1037
@EhabY
EhabY force-pushed the feat/ui-tree-suite branch 6 times, most recently from 1297bb2 to 2daa1c2 Compare August 12, 2026 16:44
Tree now takes nodes plus expandedIds instead of composed TreeItem
children, and renders every visible node as a flat sibling row with
declared aria-level, posinset, and setsize, the projection VS Code,
react-arborist, and headless-tree converged on. treeModel.ts owns the
projection; order, visibility, and hierarchy derive from data, so the
store's DOM queries, the item registry, the hierarchy context, and the
subtree-commit reconcile all disappear, and refs forward natively. The
folder splits by concern: store/ for interaction state, sticky/ for
pinning, with per-part unit tests.

Sticky scroll becomes a widget with VS Code semantics: a pinned-count
cap (stickyScrollMaxItemCount), a 40% viewport cap, per-level
indentation with twisties, the deepest row sliding out as its subtree
ends, scroll and resize tracking, click to reveal and focus the real
row, and the twistie collapsing the pinned branch in place. The pure
math lives in stickyState.ts.

PageUp/PageDown move focus by the scroller's viewport in rows, with
Shift extending the selection like the other navigation keys.

Benchmarks, 5,020 expanded rows in jsdom with the React Compiler,
median of three: mount 965ms to 695ms, 20 arrow keydowns 249ms to 89ms.
Virtualization, if ever needed, is a windowed slice over the same
projection.
@EhabY
EhabY force-pushed the feat/ui-tree-suite branch from 2daa1c2 to d794977 Compare August 12, 2026 16:56
@EhabY
EhabY force-pushed the feat/ui-tree-suite branch from 3a55395 to 3fe1ebe Compare August 13, 2026 13:03
@EhabY EhabY changed the title feat(ui): add accessible tree suite feat(ui): add accessible VS Code-parity Tree Aug 13, 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.

ui: Tree suite with native-fidelity look and ARIA keyboard nav

1 participant