From 8f200066c2d5dfae37298d9cf094efa98cd802ec Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Tue, 20 Jun 2023 20:02:38 -0700 Subject: [PATCH] Make RAC overlay open props override DialogTrigger state --- packages/react-aria-components/src/Modal.tsx | 4 +-- .../react-aria-components/src/Popover.tsx | 5 ++- .../react-aria-components/test/Dialog.test.js | 35 +++++++++++++++++++ .../test/Popover.test.js | 19 ++++++++++ 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/packages/react-aria-components/src/Modal.tsx b/packages/react-aria-components/src/Modal.tsx index 811cd0b5a2a..acaad65fe8a 100644 --- a/packages/react-aria-components/src/Modal.tsx +++ b/packages/react-aria-components/src/Modal.tsx @@ -93,8 +93,8 @@ export {_Modal as Modal}; function ModalOverlayWithForwardRef(props: ModalOverlayProps, ref: ForwardedRef) { let ctx = useContext(ModalContext); - // eslint-disable-next-line react-hooks/rules-of-hooks - let state = ctx?.state ?? useOverlayTriggerState(props); + let localState = useOverlayTriggerState(props); + let state = props.isOpen != null || props.defaultOpen != null || !ctx?.state ? localState : ctx.state; let objectRef = useObjectRef(ref); let modalRef = useRef(null); diff --git a/packages/react-aria-components/src/Popover.tsx b/packages/react-aria-components/src/Popover.tsx index 439500933b7..2e53f1e56ef 100644 --- a/packages/react-aria-components/src/Popover.tsx +++ b/packages/react-aria-components/src/Popover.tsx @@ -56,9 +56,8 @@ export const PopoverContext = createContext) { [props, ref] = useContextProps(props, ref, PopoverContext); let ctx = props as PopoverContextValue; - // React components cannot be reparented so if there's context there should always be context. - // eslint-disable-next-line react-hooks/rules-of-hooks - let state = ctx?.state ?? useOverlayTriggerState(props); + let localState = useOverlayTriggerState(props); + let state = props.isOpen != null || props.defaultOpen != null || !ctx?.state ? localState : ctx.state; let isExiting = useExitAnimation(ref, state.isOpen); if (state && !state.isOpen && !isExiting) { diff --git a/packages/react-aria-components/test/Dialog.test.js b/packages/react-aria-components/test/Dialog.test.js index 0a78b5665e6..a406511d181 100644 --- a/packages/react-aria-components/test/Dialog.test.js +++ b/packages/react-aria-components/test/Dialog.test.js @@ -186,4 +186,39 @@ describe('Dialog', () => { expect(dialog).not.toBeInTheDocument(); }); + + it('should support Modal being used standalone', async () => { + let onOpenChange = jest.fn(); + let {getByRole} = render( + + A modal + + ); + + let dialog = getByRole('dialog'); + expect(dialog).toHaveTextContent('A modal'); + + userEvent.click(document.body); + expect(onOpenChange).toHaveBeenCalledTimes(1); + expect(onOpenChange).toHaveBeenCalledWith(false); + }); + + it('isOpen and defaultOpen should override state from context', async () => { + let onOpenChange = jest.fn(); + let {getByRole} = render(<> + +