leva icon indicating copy to clipboard operation
leva copied to clipboard

fix: panel height not updating when conditionally rendering fields

Open Copilot opened this issue 1 month ago β€’ 8 comments

The Leva panel height doesn't update when fields are conditionally rendered using the render option. The panel remains at its original size, causing overflow or clipped content.

// Panel height doesn't update when show2 toggles the folder visibility
const values = useControls({
  show2: { value: false, label: 'Show folder' },
  folder: folder(
    {
      number: 1,
      string: { value: 'text', render: (get) => get('folder.number') >= 1 },
    },
    { render: (get) => get('show2') }
  ),
})

Changes

  • Added ResizeObserver to useToggle hook to detect content size changes while the panel is expanded
  • Animate height transitions by setting explicit heights before/after content changes, then removing them post-transition
  • Prevent race conditions by canceling pending animations and removing orphaned event listeners before starting new transitions
  • Capture refs at effect creation to ensure proper cleanup when component unmounts
  • Added Storybook story demonstrating the fix with multiple conditional sections and nested rendering

Implementation

The ResizeObserver compares rendered heights (not style properties) and triggers smooth animations only when differences exceed 1px. Uses requestAnimationFrame to ensure height changes are properly batched for transitions.

Storybook Story

Added a comprehensive story "Conditional Rendering - Panel Height Fix" that demonstrates:

  • Multiple conditional sections with checkboxes to toggle visibility
  • Nested conditional rendering within folders
  • Smooth panel height animations when content changes
  • Real-world use case with various input types

Location: Misc / Input options β†’ Conditional Rendering - Panel Height Fix

Screenshots

Before: Panel doesn't resize when folder appears Before

After: Panel smoothly animates to fit new content After

Story - Initial state with basic fields: Story Initial

Story - Expanded with advanced settings: Story Advanced

Story - Fully expanded with all sections: Story Fully Expanded

[!WARNING]

Original prompt

This section details on the original issue you should resolve

<filter_complete></filter_complete>

<issue_title>Panel height doesn't change when adding new fields via conditional rendering</issue_title> <issue_description>See pmndrs/leva#540 </issue_description>

Comments on the Issue (you are @copilot in this section)

  • Fixes pmndrs/leva#578
Original prompt

This section details on the original issue you should resolve

<issue_title>Panel height doesn't change when adding new fields via conditional rendering</issue_title> <issue_description>See pmndrs/leva#540 </issue_description>

Comments on the Issue (you are @copilot in this section)

  • Fixes pmndrs/leva#578

πŸ’¬ We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Summary by CodeRabbit

  • New Features

    • Panel height now smoothly animates and automatically adjusts to content size changes when toggled.
  • Documentation

    • Added example story demonstrating conditional rendering with dynamic panel height handling.

Copilot avatar Nov 02 '25 22:11 Copilot

⚠️ No Changeset found

Latest commit: 108592b7e4db3194ec3a184103f3117e56553df8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

changeset-bot[bot] avatar Nov 02 '25 22:11 changeset-bot[bot]

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
leva Ready Ready Preview Comment Nov 8, 2025 3:25pm

vercel[bot] avatar Nov 02 '25 22:11 vercel[bot]

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 108592b7e4db3194ec3a184103f3117e56553df8:

Sandbox Source
leva-minimal Configuration
leva-busy Configuration
leva-scroll Configuration
leva-advanced-panels Configuration
leva-ui Configuration
leva-theme Configuration
leva-transient Configuration
leva-plugin-plot Configuration
leva-plugin-bezier Configuration
leva-plugin-spring Configuration
leva-plugin-dates Configuration
leva-custom-plugin Configuration

codesandbox-ci[bot] avatar Nov 02 '25 22:11 codesandbox-ci[bot]

@copilot write a story for this change

gsimone avatar Nov 03 '25 12:11 gsimone

@copilot write a story for this change

Added a comprehensive Storybook story "Conditional Rendering - Panel Height Fix" in commit bda7da4.

The story demonstrates:

  • Multiple conditional sections with checkboxes to toggle visibility
  • Nested conditional rendering within folders
  • Smooth panel height animations when content changes
  • Real-world use case with various input types (text, numbers, sliders, selects, booleans)

You can find it at: Misc / Input options β†’ Conditional Rendering - Panel Height Fix

Screenshots showing the panel expanding as sections are toggled:

Initial state: Initial

With Advanced Settings: Advanced

Fully expanded: Expanded

[!WARNING]

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • storybook.js.org
    • Triggering command: node /home/REDACTED/work/leva/leva/node_modules/.bin/../storybook/dist/bin/dispatcher.js dev -p 6006 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot avatar Nov 03 '25 12:11 Copilot

@coderabbitai review

gsimone avatar Nov 08 '25 17:11 gsimone

βœ… Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot] avatar Nov 08 '25 17:11 coderabbitai[bot]

[!NOTE]

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

The useToggle hook is enhanced with a ResizeObserver to detect content size changes and smoothly animate panel height adjustments when controls are conditionally rendered. A Storybook story is added to demonstrate this panel height auto-adjustment behavior.

Changes

Cohort / File(s) Change Summary
Panel Height Animation Enhancement
packages/leva/src/hooks/useToggle.ts
Adds ResizeObserver effect to detect content size changes; computes and animates height transitions with requestAnimationFrame; includes cleanup for observers, animation frames, and transition handlers; skips animations when height difference is below 1px threshold
Conditional Rendering Story
packages/leva/stories/input-options.stories.tsx
Adds new Storybook story component ConditionalRenderingPanelHeight with nested conditional groups (showBasicFields, showAdvanced, showDebug) to demonstrate panel height auto-adjustment behavior

Sequence Diagram

sequenceDiagram
    participant User
    participant Panel
    participant ResizeObserver as ResizeObserver
    participant AnimationFrame as requestAnimationFrame
    participant DOM

    User->>Panel: Toggle conditional field visibility
    Panel->>ResizeObserver: Detect content size change
    ResizeObserver->>Panel: Content dimensions changed
    Panel->>AnimationFrame: Queue height animation
    AnimationFrame->>DOM: Set wrapper to current height
    AnimationFrame->>DOM: Animate to target height
    DOM->>DOM: Transition height (CSS)
    DOM->>Panel: transitionend event
    Panel->>DOM: Remove explicit height property

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20-25 minutes

  • useToggle hook modifications: Logic density is moderate with ResizeObserver setup, animation frame sequencing, and transition event handling. Requires careful review for proper cleanup, memory leak prevention, and edge cases around rapid toggling or nested conditional renders.
  • Story component: Straightforward demonstration with minimal logic.
  • Key areas for attention:
    • ResizeObserver cleanup on effect unmount or dependency changes
    • Proper handling of concurrent animations (cancellation of pending frames)
    • Height calculation accuracy and negligible difference threshold (1px)
    • Transition handler cleanup to avoid memory leaks

Poem

A panel that dances with graceful ease, As fields appear and disappear with ease, The ResizeObserver watches content grow, Then animates the height with a gentle flow, No jarring jumpsβ€”just smooth transitions so. 🐰✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses issue #578 (panel height with conditional rendering) but does not implement the coding requirements from issue #39 (store sanitization logic). Implement the store sanitization responsibility outlined in issue #39 to centralize value validation/sanitization as specified in the linked requirements.
βœ… Passed checks (4 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title clearly and specifically describes the main fix: panel height not updating during conditional field rendering, which aligns with the primary change in the PR.
Out of Scope Changes check βœ… Passed The ResizeObserver implementation and Storybook story are directly aligned with fixing issue #578; no out-of-scope changes detected.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • [ ] πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment
  • [ ] Commit unit tests in branch copilot/fix-panel-height-issue

πŸ“œ Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between f70a62f38d4e97f3acaed3495fc27fcab926b852 and 108592b7e4db3194ec3a184103f3117e56553df8.

πŸ“’ Files selected for processing (2)
  • packages/leva/src/hooks/useToggle.ts (1 hunks)
  • packages/leva/stories/input-options.stories.tsx (1 hunks)
🧰 Additional context used
πŸͺ› GitHub Actions: CI
packages/leva/src/hooks/useToggle.ts

[error] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.

packages/leva/stories/input-options.stories.tsx

[error] 1-1: Prettier formatting check failed. Run 'prettier --write' to fix code style issues in this file.

πŸ”‡ Additional comments (2)
packages/leva/src/hooks/useToggle.ts (1)

129-189: Verify race condition between toggle and resize animations.

As previously noted by Copilot, when toggling from closed to open, both the toggle animation effect (lines 96-127) and this ResizeObserver effect will run simultaneously. During expansion, the ResizeObserver may fire and manipulate height while the toggle animation is in progress, causing conflicting height changes.

The concern remains valid: both effects can set explicit heights on the same wrapper element concurrently, and without coordination they may override each other's transitions.

Consider adding an animation state flag as previously suggested:

const isAnimating = useRef(false)

// In toggle effect (line 96), set isAnimating.current = true
// Clear it in the transitionend handler (line 106)
// In ResizeObserver callback (line 137), check:
if (isAnimating.current) return

Or alternatively, add a delay before observing that matches the transition duration:

const timeoutId = window.setTimeout(() => {
  resizeObserver.observe(contentRef.current!)
}, 300) // Match transition duration

return () => {
  clearTimeout(timeoutId)
  resizeObserver.disconnect()
  // ... rest of cleanup
}

Based on learnings

packages/leva/stories/input-options.stories.tsx (1)

65-108: LGTM! Excellent demonstration of the panel height fix.

The story effectively demonstrates conditional rendering with multiple nested sections and provides clear documentation for users. The combination of basic fields, folders with nested conditions, and the JSON preview creates a comprehensive test case for the ResizeObserver-based height adjustment.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Nov 08 '25 17:11 coderabbitai[bot]