diff --git a/src/components/ui/ScrollArea/fragments/ScrollAreaRoot.tsx b/src/components/ui/ScrollArea/fragments/ScrollAreaRoot.tsx index 39668dbad..70abe1539 100644 --- a/src/components/ui/ScrollArea/fragments/ScrollAreaRoot.tsx +++ b/src/components/ui/ScrollArea/fragments/ScrollAreaRoot.tsx @@ -56,8 +56,23 @@ const ScrollAreaRoot = forwardRef(({ resizeObserver.observe(viewport); Array.from(viewport.children).forEach(child => resizeObserver.observe(child)); - const mutationObserver = new MutationObserver(() => { + const mutationObserver = new MutationObserver((mutations) => { + const contentSwapped = mutations.some( + (mutation) => + mutation.type === "childList" && + (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) + ); + + if (contentSwapped) { + viewport.scrollTop = 0; + viewport.scrollLeft = 0; + } + handleResize(); + + if (contentSwapped) { + handleScroll(); + } }); mutationObserver.observe(viewport, { diff --git a/src/components/ui/ScrollArea/tests/ScrollArea.test.tsx b/src/components/ui/ScrollArea/tests/ScrollArea.test.tsx index cc50437a6..0fd3441a6 100644 --- a/src/components/ui/ScrollArea/tests/ScrollArea.test.tsx +++ b/src/components/ui/ScrollArea/tests/ScrollArea.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { act, fireEvent, render, screen } from '@testing-library/react'; +import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import ScrollArea from '../ScrollArea'; @@ -176,4 +176,37 @@ describe('ScrollArea', () => { expect(screen.getByTestId('scrollbar')).toHaveAttribute('data-state', 'visible'); }); + + test('resets scroll position when viewport children change', async() => { + const scrollArea = (content: React.ReactNode) => ( + + + {content} + + + + + + ); + + const { rerender } = render(scrollArea(
tab a
)); + + const viewport = screen.getByTestId('viewport'); + Object.defineProperty(viewport, 'scrollHeight', { configurable: true, value: 400 }); + Object.defineProperty(viewport, 'clientHeight', { configurable: true, value: 100 }); + + act(() => { + viewport.scrollTop = 300; + fireEvent.scroll(viewport); + }); + + expect(viewport.scrollTop).toBe(300); + + rerender(scrollArea(
tab b
)); + + await waitFor(() => { + expect(viewport.scrollTop).toBe(0); + expect(viewport.scrollLeft).toBe(0); + }); + }); });