diff --git a/src/components/SplitPane.test.tsx b/src/components/SplitPane.test.tsx index 1120dbd5..704988cb 100644 --- a/src/components/SplitPane.test.tsx +++ b/src/components/SplitPane.test.tsx @@ -235,4 +235,47 @@ describe('SplitPane initial size calculation', () => { // Second pane: remaining 624px expect(panes[1]).toHaveStyle({ width: `${CONTAINER_WIDTH - 400}px` }); }); + + it('updates pane sizes when controlled size prop changes', async () => { + const ControlledComponent = ({ sizes }: { sizes: number[] }) => ( + + Pane 1 + Pane 2 + + ); + + const { container, rerender } = render( + + ); + + await act(async () => { + await vi.runAllTimersAsync(); + }); + + const panes = container.querySelectorAll('[data-pane="true"]'); + expect(panes).toHaveLength(2); + expect(panes[0]).toHaveStyle({ width: '200px' }); + expect(panes[1]).toHaveStyle({ width: '400px' }); + + // Change sizes (simulates parent state update after drag) + rerender(); + + await act(async () => { + await vi.runAllTimersAsync(); + }); + + expect(panes[0]).toHaveStyle({ width: '300px' }); + expect(panes[1]).toHaveStyle({ width: '300px' }); + + // Reset to initial values (simulates clicking "Reset" button) + rerender(); + + await act(async () => { + await vi.runAllTimersAsync(); + }); + + // This is the bug: sizes should update to [200, 400] + expect(panes[0]).toHaveStyle({ width: '200px' }); + expect(panes[1]).toHaveStyle({ width: '400px' }); + }); }); diff --git a/src/components/SplitPane.tsx b/src/components/SplitPane.tsx index b9aeb734..c4bab238 100644 --- a/src/components/SplitPane.tsx +++ b/src/components/SplitPane.tsx @@ -152,6 +152,30 @@ export function SplitPane(props: SplitPaneProps) { calculateInitialSizes(containerSize) ); + // Sync paneSizes with controlled size props when they change + // This handles the case where parent state is reset (e.g., clicking a "Reset" button) + useEffect(() => { + if (containerSize === 0) return; + + // Check if any pane has a controlled size prop + const hasControlledSizes = paneConfigs.some( + (config) => config.size !== undefined + ); + if (!hasControlledSizes) return; + + // Calculate what sizes should be based on current props + const expectedSizes = calculateInitialSizes(containerSize); + + // Only update if sizes actually differ (avoid unnecessary re-renders) + setPaneSizes((currentSizes) => { + const sizesMatch = + currentSizes.length === expectedSizes.length && + currentSizes.every((size, i) => size === expectedSizes[i]); + + return sizesMatch ? currentSizes : expectedSizes; + }); + }, [containerSize, paneConfigs, calculateInitialSizes]); + // Handle container size changes - update sizes proportionally // Using a ref comparison to avoid effect dependency issues const handleContainerSizeChange = useCallback(