diff --git a/packages/react/src/PageLayout/PageLayout.test.tsx b/packages/react/src/PageLayout/PageLayout.test.tsx index 5d841b799de..34a25206971 100644 --- a/packages/react/src/PageLayout/PageLayout.test.tsx +++ b/packages/react/src/PageLayout/PageLayout.test.tsx @@ -234,7 +234,7 @@ describe('PageLayout', async () => { expect(content!.style.getPropertyValue('contain')).toBe('') }) - it('should add will-change during drag for optimized updates', async () => { + it('should apply containment optimizations during drag', async () => { const {container} = render( @@ -247,17 +247,50 @@ describe('PageLayout', async () => { ) const pane = container.querySelector('[class*="Pane"][data-resizable]') + const content = container.querySelector('[class*="PageLayoutContent"]') const divider = await screen.findByRole('slider') - // Before drag - no will-change - expect(pane!.style.willChange).toBe('') - - // Start drag - will-change is added + // Mock offsetHeight for testing + Object.defineProperty(pane, 'offsetHeight', { + configurable: true, + value: 320, + }) + Object.defineProperty(content, 'offsetHeight', { + configurable: true, + value: 640, + }) + + // Before drag - no containment + expect(pane!.style.contain).toBe('') + expect(pane!.style.pointerEvents).toBe('') + expect(pane!.style.contentVisibility).toBe('') + expect(pane!.style.containIntrinsicSize).toBe('') + expect(content!.style.contain).toBe('') + expect(content!.style.pointerEvents).toBe('') + expect(content!.style.contentVisibility).toBe('') + expect(content!.style.containIntrinsicSize).toBe('') + + // Start drag - containment and content-visibility are added to both pane and content fireEvent.pointerDown(divider, {clientX: 300, clientY: 200, pointerId: 1}) - expect(pane!.style.willChange).toBe('width') - // End drag - will-change is removed + expect(pane!.style.contain).toBe('layout style paint') + expect(pane!.style.pointerEvents).toBe('none') + expect(pane!.style.contentVisibility).toBe('auto') + expect(pane!.style.containIntrinsicSize).toBe('auto 320px') + expect(content!.style.contain).toBe('layout style paint') + expect(content!.style.pointerEvents).toBe('none') + expect(content!.style.contentVisibility).toBe('auto') + expect(content!.style.containIntrinsicSize).toBe('auto 640px') + + // End drag - containment is removed fireEvent.lostPointerCapture(divider, {pointerId: 1}) - expect(pane!.style.willChange).toBe('') + expect(pane!.style.contain).toBe('') + expect(pane!.style.pointerEvents).toBe('') + expect(pane!.style.contentVisibility).toBe('') + expect(pane!.style.containIntrinsicSize).toBe('') + expect(content!.style.contain).toBe('') + expect(content!.style.pointerEvents).toBe('') + expect(content!.style.contentVisibility).toBe('') + expect(content!.style.containIntrinsicSize).toBe('') }) }) diff --git a/packages/react/src/PageLayout/paneUtils.ts b/packages/react/src/PageLayout/paneUtils.ts index 4ac2ebca89b..caca5bf983d 100644 --- a/packages/react/src/PageLayout/paneUtils.ts +++ b/packages/react/src/PageLayout/paneUtils.ts @@ -1,15 +1,15 @@ /** * Apply CSS containment optimizations to isolate an element during resize/drag. * - contain: limits layout/paint recalc to this subtree - * - content-visibility: skip rendering off-screen content (valuable for large DOMs) - * - contain-intrinsic-size: prevents layout thrashing from size estimation when using content-visibility + * - content-visibility: skip rendering off-screen content + * - contain-intrinsic-size: uses actual element height to prevent layout shift * - pointer-events: skip hit-testing large child trees */ export function setContainmentOptimizations(element: HTMLElement | null) { if (!element) return element.style.contain = 'layout style paint' element.style.contentVisibility = 'auto' - element.style.containIntrinsicSize = 'auto 500px' + element.style.containIntrinsicSize = `auto ${element.offsetHeight}px` element.style.pointerEvents = 'none' } @@ -34,11 +34,10 @@ type DraggingStylesParams = { export function setDraggingStyles({handle, pane, content}: DraggingStylesParams) { handle?.style.setProperty('background-color', 'var(--bgColor-accent-emphasis)') handle?.style.setProperty('--draggable-handle--drag-opacity', '1') - // Disable transition for instant visual feedback during drag handle?.style.setProperty('--draggable-handle--transition', 'none') - pane?.style.setProperty('will-change', 'width') - setContainmentOptimizations(content) + // No will-change: width - doesn't help layout properties setContainmentOptimizations(pane) + setContainmentOptimizations(content) } /** Remove drag styles and restore normal state */ @@ -46,7 +45,6 @@ export function removeDraggingStyles({handle, pane, content}: DraggingStylesPara handle?.style.removeProperty('background-color') handle?.style.removeProperty('--draggable-handle--drag-opacity') handle?.style.removeProperty('--draggable-handle--transition') - pane?.style.removeProperty('will-change') - removeContainmentOptimizations(content) removeContainmentOptimizations(pane) + removeContainmentOptimizations(content) }