From 62795556750563083c5159a02e6ea88bb42a311f Mon Sep 17 00:00:00 2001 From: Rob Hannay Date: Thu, 6 Aug 2026 02:24:42 +0100 Subject: [PATCH] fix: DialogTrigger inside Tabs (#10367) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: DialogTrigger inside Tabs Collection triggers render nothing inside another collection's hidden pass so the outer collection doesn't inherit items from the inner one. #10019 did this for MenuTrigger, Select and ComboBox, but DialogTrigger was missed, so a dialog containing collection content still renders detached during an ancestor collection's scan and throws — for example a Menu inside a popover dialog, which builds its items with a null selection manager. Mirrors the MenuTrigger fix. * Move the hidden check below the hooks Returning early between hooks made useRef, useOverlayTrigger and useId conditional. MenuTrigger runs all of its hooks before bailing; do the same here. * Update packages/react-aria-components/test/Tabs.test.js * Apply suggestion from @snowystinger fix lint --------- Co-authored-by: Rob Snow --- packages/react-aria-components/src/Dialog.tsx | 10 +++++- .../react-aria-components/test/Tabs.test.js | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/react-aria-components/src/Dialog.tsx b/packages/react-aria-components/src/Dialog.tsx index 0d78f95ef18..945fab68383 100644 --- a/packages/react-aria-components/src/Dialog.tsx +++ b/packages/react-aria-components/src/Dialog.tsx @@ -42,6 +42,7 @@ import React, { import {RootMenuTriggerStateContext} from './Menu'; import {TextContext} from './Text'; import {useId} from 'react-aria/useId'; +import {useIsHidden} from 'react-aria/private/collections/Hidden'; import {useMenuTriggerState} from 'react-stately/useMenuTriggerState'; import {useOverlayTrigger} from 'react-aria/useOverlayTrigger'; @@ -77,7 +78,7 @@ export const OverlayTriggerStateContext = createContext { expect(menu.getOptions()).toHaveLength(3); await menu.close(); }); + + it('supports DialogTrigger inside Tabs', async () => { + let tree = render( + +
+ + First Tab + Second Tab + + + + + + Item 1 + Item 2 + + + +
+ First Tab content + Second Tab content +
+ ); + + let tester = testUtilUser.createTester('Tabs', {root: tree.getByRole('tablist')}); + expect(tester.getTabs().length).toBe(2); + + let trigger = tree.getByRole('button'); + await user.click(trigger); + + let dialog = tree.getByRole('dialog'); + expect(within(dialog).getAllByRole('menuitem')).toHaveLength(2); + }); });