Files
2026-05-27 23:29:53 +01:00

4.2 KiB

Tasks: enterprise-react-tree-view

Change ID: enterprise-react-tree-view Package: @suaveplan/react-tree-view Target path: react/ui/react-tree-view Prerequisite packages on Verdaccio: @suaveplan/error, @suaveplan/react-translate, @suaveplan/react-virtual


0. Pre-flight (non-negotiable)

  • 0.1 Confirm clean working tree (git status reports nothing) and no in-flight worktrees with uncommitted work (git worktree list then git status in each)
  • 0.2 If any worktrees exist under .claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
  • 0.3 Confirm the package set in MANIFEST.md matches find packages -maxdepth 3 -name package.json count. Drift here means a prior change archived without regenerating the manifest.

1. Scaffold

  • Create directory tree per proposal layout.
  • Write package.json at 0.1.0 with "sideEffects": false, exports map.
  • Dependencies: @suaveplan/error workspace:^, @suaveplan/react-translate workspace:^, @suaveplan/react-virtual workspace:^, zod catalog:forms, tslib catalog:build.
  • Write tsconfig.json, vitest.config.ts, scripts.

2. Types and errors

  • Define TreeViewError extending SuaveplanError; code: TREE_VIEW_CONTEXT_MISSING.
  • Define TreeNode<T>: { id: string; label: string; children?: TreeNode<T>[]; data?: T; disabled?: boolean }.
  • Define TreeViewProps<T>, UseTreeStateOptions, UseTreeStateReturn.

3. Utils (src/utils/tree-utils/)

  • Implement flattenTree<T>(root) — DFS traversal, returns flat array.
  • Implement filterTree<T>(root, predicate) — returns subtree containing matching nodes; null if no match.
  • Implement collectAllIds<T>(root) — returns all id values.
  • Write tree-utils.test.ts (≤500 lines): flat tree, nested tree, filter no-match, filter partial-match, collectAllIds deep. { timeout: 5000 }.
  • Write tree-utils.md.

4. Hook — useTreeState

  • Manages expandedIds: Set<string> in useState.
  • Exposes toggle(id), expand(id), collapse(id), expandAll(tree), collapseAll(), isExpanded(id).
  • Controlled mode: accepts expandedIds + onExpandedChange props; uncontrolled: manages internally.
  • Activates @suaveplan/react-virtual useVirtualList when flattenTree(root).length > 100.
  • Write use-tree-state.test.ts: toggle, expand, collapse, expandAll, collapseAll, controlled mode, uncontrolled mode. { timeout: 5000 }.
  • Write use-tree-state.md.

5. Components

TreeNodeComponent

  • Renders arrow indicator (collapsed/expanded), indent per depth, optional showLines, renderNode slot for custom node markup.
  • Keyboard: ArrowRight expands, ArrowLeft collapses (or moves to parent when already collapsed), ArrowDown/ArrowUp moves focus to next/prev visible node.
  • aria-expanded, aria-selected, role="treeitem", tabIndex management.
  • Write test, stories (default, with-custom-render, playground), md.

TreeView

  • Top-level controlled/uncontrolled tree: optional search input (showSearch prop) using useTranslation for placeholder; filters via filterTree on input change.
  • Renders TreeNodeComponent for each visible node; uses virtual list when node count > 100.
  • role="tree", aria-label from i18n.
  • Write test, stories (default, large-tree 500 nodes, with-search, keyboard-nav, playground), md.

6. E2E

  • e2e/tree-view.spec.ts — Playwright:
    • Render tree, click expand arrow, verify children visible.
    • Keyboard: Tab into tree, ArrowRight to expand, ArrowDown to move focus, ArrowLeft to collapse.
    • Type in search box, verify filtered nodes only.

7. Verify and publish

  • bunx turbo lint typecheck test --filter=@suaveplan/react-tree-view — green.
  • bunx turbo test:coverage --filter=@suaveplan/react-tree-view — 100%.
  • bun scripts/publish.ts --package @suaveplan/react-tree-view.
  • Archive change folder.