MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-126

2.5.15 Tree row virtualization — window the treegrid (a11y-honest)

Done
Description

Estimate: 22m · Depends on: 2.5.14

The DOM-perf layer the 2.5.11 design calls for, split out of 2.5.14: virtualize the treegrid so only the rows in (or near) the viewport mount — a deeply-expanded big tree stays fast. Lazy-load (2.5.14) already bounds the DATA fetched; this bounds the DOM. Add a windowing approach consistent with the stack (e.g. @tanstack/react-virtual) over the flat ordered "visible rows" model 2.5.14 produced; an off-view row is removed and a spacer preserves scroll height.

(PR #123 merged 2026-06-06, merge commit 23eac88 — hand-rolled fixed-row (40px) windowing in components/ui/TreeTable.tsx over the 2.5.14 flat visible-rows model. The rowgroup keeps its FULL height and each mounted row is absolutely positioned at index * ROW_PX (the spacer), so only viewport(+overscan) rows mount while the page scrollbar stays honest. Windows against the nearest scrollable ANCESTOR (the shell <main> — no internal scrollbar, no layout change) or a getScrollElement prop; degrades to render-all when no viewport is measurable (SSR / the small-tree component tests), so markup is identical with/without a live scroll container. A11y-honest: each mounted row keeps its TRUE aria-level/posinset/setsize/expanded from the flat model; roving tabindex intact (arrowing to an off-window row scrolls it in → mounts → focuses it). Pure rowgroup → row structure (no spacer divs) so the strict axe treegrid sweep is unaffected. Chose hand-rolled over @tanstack/react-virtual — that lib mounts 0 rows under happy-dom, so it can't back the required deterministic component test; the card allows "or equivalent". 4 new TreeTable virtualization tests (window slice · scroll shifts window · arrow-past mounts+focuses · honest setsize); 142 component tests green; tsc/eslint(--max-warnings=0)/prettier clean; next build compiles + static-generates.)

A11y is the hard part (must hold across the window): each mounted row keeps its TRUE aria-level / aria-posinset / aria-setsize (using 2.5.14's per-node count) so a screen reader announces the real position though only a window exists; the shipped roving-tabindex keyboard model still works — ↑/↓ move the active row and auto-scroll mounts the landed row, →/← expand/collapse, Enter activates. No distinct visual (a virtualized row is identical to a real one).

Acceptance criteria

  • Only viewport(+overscan) rows are in the DOM; scrolling a large expanded tree keeps the DOM row count bounded + stays smooth; a spacer preserves scroll height + the scrollbar.
  • Each mounted row keeps honest aria-level/posinset/setsize/expanded; the STRICT axe sweep + the treegrid contract still pass with virtualization on.
  • Keyboard nav unchanged: arrow-moving to an off-view row scrolls it into view + mounts it + focuses it (roving tabindex intact); Enter activates the row link.
  • Component test: a tall tree mounts only a window; arrowing past the window mounts + focuses the landed row. tsc/eslint/prettier clean; next build compiles.

Context refs

  • The 2.5.14 IssueTreeTable / TreeTable (the flat visible-rows model + roving tabindex to window)
  • A virtualization lib (@tanstack/react-virtual or equivalent) — add to deps; the 2.5.11 design's virtualization section
  • tests/e2e/shell-a11y.spec.ts tree-table strict sweep — must stay green with windowing
Status
Done
Type
Sub-task