From 1125324233e261a9b026c5a11e324156b8979e8b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 29 Dec 2025 19:40:51 +0000
Subject: [PATCH 1/2] Initial plan
From 7d09acbb7085f5529b059854a25f6769347feac4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 29 Dec 2025 19:55:21 +0000
Subject: [PATCH 2/2] Add cleanup for dragging styles on unmount mid-drag
- Cache DOM element references in DragHandle for cleanup
- Update cleanup effect to remove dragging styles if unmounting mid-drag
- Add test to verify dragging styles are cleaned up on unmount
- Refs from context get cleared during unmount, so cache elements when drag starts
Co-authored-by: mattcosta7 <8616962+mattcosta7@users.noreply.github.com>
---
.../react/src/PageLayout/PageLayout.test.tsx | 29 +++++++++++++++++++
packages/react/src/PageLayout/PageLayout.tsx | 19 ++++++++++++
2 files changed, 48 insertions(+)
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,
+ })
+ }
}
}, [])