From 5f5e1ad32afeef1e42c546506db5218f0ea23596 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 20 May 2026 14:54:30 -0500 Subject: [PATCH 1/2] fix(s2): prevent infinite render loop in Tabs inside DropZone --- packages/@react-spectrum/s2/src/Tabs.tsx | 11 ++- .../@react-spectrum/s2/test/Tabs.test.tsx | 72 +++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 packages/@react-spectrum/s2/test/Tabs.test.tsx diff --git a/packages/@react-spectrum/s2/src/Tabs.tsx b/packages/@react-spectrum/s2/src/Tabs.tsx index 2829a3389a1..1c5f92c16af 100644 --- a/packages/@react-spectrum/s2/src/Tabs.tsx +++ b/packages/@react-spectrum/s2/src/Tabs.tsx @@ -139,6 +139,7 @@ const InternalTabsContext = createContext< Partial & { tablistRef?: RefObject; selectedKey?: Key | null; + baseId?: string; } >({}); @@ -200,6 +201,8 @@ export const Tabs = forwardRef(function Tabs(props: TabsProps, ref: DOMRef(null); + // Shared base id so child Tab/TabPanel components can derive stable ids. + let baseId = useId(); return ( { + let user; + beforeAll(() => { + jest.useFakeTimers(); + user = userEvent.setup({delay: null, pointerMap}); + }); + + afterEach(() => { + jest.clearAllMocks(); + act(() => jest.runAllTimers()); + }); + + afterAll(() => { + jest.restoreAllMocks(); + }); + + it('should not infinite-loop when inside DropZone with a controlled Tabs + Skeleton + Text', async () => { + function Repro() { + const [tab, setTab] = useState('a'); + return ( + + + setTab(k as string)}> + + A + + + + x + + + + + ); + } + + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + + const {getByTestId} = render(); + + await act(async () => { + await user.hover(getByTestId('dropzone')); + }); + + const updateDepthError = errorSpy.mock.calls.find(call => + call.some(arg => String(arg).includes('Maximum update depth')) + ); + expect(updateDepthError).toBeUndefined(); + errorSpy.mockRestore(); + }); +}); From 28f447b2a020a989ec87724258850752437629d9 Mon Sep 17 00:00:00 2001 From: Reid Barber Date: Wed, 20 May 2026 15:02:42 -0500 Subject: [PATCH 2/2] fix test --- packages/@react-spectrum/s2/test/Tabs.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@react-spectrum/s2/test/Tabs.test.tsx b/packages/@react-spectrum/s2/test/Tabs.test.tsx index 27d140043ac..7927e135a94 100644 --- a/packages/@react-spectrum/s2/test/Tabs.test.tsx +++ b/packages/@react-spectrum/s2/test/Tabs.test.tsx @@ -24,6 +24,7 @@ describe('Tabs', () => { beforeAll(() => { jest.useFakeTimers(); user = userEvent.setup({delay: null, pointerMap}); + Element.prototype.getAnimations = jest.fn().mockImplementation(() => []); }); afterEach(() => {