Skip to content

feat: Add Tree expand/collapse animations with intrinsic sizing - #10606

Open
RobHannay wants to merge 1 commit into
adobe:mainfrom
RobHannay:rook/tree-animations-refresh
Open

RobHannay wants to merge 1 commit into
adobe:mainfrom
RobHannay:rook/tree-animations-refresh

Conversation

@RobHannay

@RobHannay RobHannay commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Supersedes #10378, which was closed as stale pending passing tests. This replacement is a single commit based on current main (4693fcc84), rather than reopening the original PR.

Intent

Allow non-virtualized Tree subtrees to animate when expanded or collapsed without delaying the actual expansion state. Consumers should be able to use data-entering / data-exiting and content-sized row heights, while keyboard navigation, selection, drag targets and aria-expanded reflect collapse immediately.

Prior art: Disclosure (#8867), TabPanels (#9077), and the existing shared animation lifecycle helpers. There is no separate feature issue; the problem and proposed solution are described here for discussion.

Implementation

  • Adds isEntering / isExiting render props and the corresponding data attributes to TreeItem.
  • Keeps the behavioral collection unchanged. A separate rendering collection retains the exact outgoing item keys, preserving traversal through their ancestors without rendering completed siblings or newly inserted hidden children. Each row releases its own key through the shared useAnimation helper.
  • Uses immutable expansion snapshots rather than communicating entering keys through a ref mutated during render. Initial defaultExpandedKeys rows do not animate.
  • Keeps outgoing rows inert, preserving consumer-supplied inertness. Drop-indicator rendering also receives its source node, so an exiting descendant cannot produce a duplicate indicator for a visible ancestor.
  • Publishes --tree-item-height when a nonzero height transition is declared. Initial entry is staged before applying animation selectors; resting size changes are observed using a border-box ResizeObserver. Measurement uses the computed CSS height, supports both box-sizing modes, and does not remove animation attributes or disable author transitions. Completion cleanup is cancellation-safe.
  • Updates the docs, CSS and Tailwind starters, and the animated Tree story.

Changes From the Original PR

The original branch retained whole expanded branches rather than exact exiting rows. That could restore a completed sibling, prematurely remove an animating grandchild, or retain loaders forever. These are fixed, with independent-completion, nested, section, and loader regression coverage.

The height implementation is smaller and no longer temporarily strips selectors or suppresses transitions to measure a row. Tests now cover content and padding resizing, content-box sizing, cancellation, StrictMode effect replay, and mixed keyframe/height entry.

The browser tests also stop asserting that the unrelated root row, which contains a native button, must have the same height as plain-text child rows. Intermediate animation states are inspected explicitly, and animations are finished through the Web Animations API rather than timed sleeps.

Scope and Limitations

  • Virtualized trees keep immediate expand/collapse behavior. Offscreen rows cannot own an animation-completion callback; supporting retained virtualized rows needs separate layout/persistence integration.
  • Load-more sentinels disappear immediately on collapse rather than continuing to observe a hidden branch.
  • CSS must allow a row to shrink: padding, borders and minimum heights remain the consumer's responsibility.
  • This adds expansion/collapse animation, not animation of item deletion from the data source.

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue. No separate issue; predecessor and proposed design linked above.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices. Keyboard, inertness and drag-indicator regressions are tested.
  • I understand every change in this PR and can explain why it's there.
  • If AI-assisted, I followed our AI contribution guidance and pointed my assistant at CLAUDE.md.

AI assistance: implemented and reviewed with Rook using GPT-6 Astra, including four focused read-only review passes and follow-up checks. The human author remains responsible for review and upstream discussion.

📝 Test Instructions:

Full verification on the identical source tree before transplanting the commit onto this clean branch:

Command Result
yarn test --maxWorkers=2 --silent 377 suites passed; 8,291 tests passed, 16 skipped; 262 snapshots passed
yarn test:ssr --silent 60 suites and 74 tests passed
yarn vitest run --config=vitest.browser.config.ts --no-file-parallelism 444 tests passed, 69 skipped across Chromium, Firefox and WebKit
yarn lint Formatting, types, oxlint, package lint and workspace constraints passed

The unrestricted full browser command exhausted browser-session startup timeouts in this single-core sandbox. Running all files with --no-file-parallelism passed; no CI configuration or test exclusions were changed. The focused 16-test Tree animation file was rerun after transplanting the commit and passed all 48 browser executions with default parallelism.

The drag-indicator regression was also verified to fail when its fix was temporarily removed (two "Insert after Root" targets instead of one), then pass with the fix restored.

Manual review: yarn start -> React Aria Components / Tree / Animated expand/collapse. Expand, collapse, reverse mid-animation, and check that the collapsed parent reports its actual state throughout. Also review keyboard/screen-reader dragging, reduced motion, wrapping content, and initial expanded rows.

No new manual screen-reader, touch-device, high-contrast, zoom, or visual-regression matrix was performed in this refresh. Those remain review gaps; the automated tests are not a substitute for them.

🧢 Your Project:

Metaview


View in Rook
Rook session · Slack thread · Requested by Rob · Model gpt-6-astra

Retain outgoing rows separately from the semantic collection, publish measured row heights, and cover interruption, independent exits, loaders, StrictMode and drag indicators.

Co-authored-by: Rook <rook@rook.is>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant