diff --git a/packages/react/src/PageLayout/PageLayout.test.tsx b/packages/react/src/PageLayout/PageLayout.test.tsx index 5f022c5aea6..9d640f0fb8c 100644 --- a/packages/react/src/PageLayout/PageLayout.test.tsx +++ b/packages/react/src/PageLayout/PageLayout.test.tsx @@ -259,6 +259,35 @@ describe('PageLayout', async () => { fireEvent.lostPointerCapture(divider, {pointerId: 1}) expect(pane!.style.willChange).toBe('') }) + + it('should cleanup dragging styles on unmount mid-drag', async () => { + const {container, unmount} = render( + + + + + + + + , + ) + + const pane = container.querySelector('[class*="Pane"][data-resizable]') + const content = container.querySelector('[class*="PageLayoutContent"]') + const divider = await screen.findByRole('slider') + + // Start drag + fireEvent.pointerDown(divider, {clientX: 300, clientY: 200, pointerId: 1}) + expect(pane).toHaveAttribute('data-dragging', 'true') + expect(content).toHaveAttribute('data-dragging', 'true') + + // Unmount mid-drag + unmount() + + // Attributes should be cleaned up + expect(pane).not.toHaveAttribute('data-dragging') + expect(content).not.toHaveAttribute('data-dragging') + }) }) describe('PageLayout.Content', () => { diff --git a/packages/react/src/PageLayout/PageLayout.tsx b/packages/react/src/PageLayout/PageLayout.tsx index 943c9561d50..2d17911817f 100644 --- a/packages/react/src/PageLayout/PageLayout.tsx +++ b/packages/react/src/PageLayout/PageLayout.tsx @@ -244,10 +244,19 @@ const DragHandle = memo(function DragHandle({ // Dragging state as a ref - cheaper than reading from DOM style const isDraggingRef = React.useRef(false) + + // Cache DOM elements for cleanup - refs from context get cleared during unmount + const cachedHandleRef = React.useRef(null) + const cachedPaneRef = React.useRef(null) + const cachedContentRef = React.useRef(null) // Set inline styles for drag optimizations - zero overhead at rest const startDragging = React.useCallback(() => { if (isDraggingRef.current) return + // Cache current element references for cleanup + cachedHandleRef.current = handleRef.current + cachedPaneRef.current = paneRef.current + cachedContentRef.current = contentRef.current setDraggingStyles({ handle: handleRef.current, pane: paneRef.current, @@ -378,10 +387,20 @@ const DragHandle = memo(function DragHandle({ // Cleanup rAF on unmount to prevent stale callbacks React.useEffect(() => { return () => { + // Cancel pending rAF if (rafIdRef.current !== null) { cancelAnimationFrame(rafIdRef.current) rafIdRef.current = null } + // Clean up dragging state if unmounting mid-drag + // Use cached element references since refs from context get cleared during unmount + if (isDraggingRef.current) { + removeDraggingStyles({ + handle: cachedHandleRef.current, + pane: cachedPaneRef.current, + content: cachedContentRef.current, + }) + } } }, [])