From 0cae1c87a2ba6d8f0eb072f1314e10197cabfc97 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Fri, 12 May 2023 14:08:02 +0200 Subject: [PATCH] add tests to verify that actions in `Portal` components won't close the `Popover` --- .../src/components/popover/popover.test.tsx | 74 +++++++++++++ .../src/components/popover/popover.test.ts | 101 +++++++++++++++--- 2 files changed, 163 insertions(+), 12 deletions(-) diff --git a/packages/@headlessui-react/src/components/popover/popover.test.tsx b/packages/@headlessui-react/src/components/popover/popover.test.tsx index c43414993a..af1f0f6b75 100644 --- a/packages/@headlessui-react/src/components/popover/popover.test.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.test.tsx @@ -2623,6 +2623,80 @@ describe('Mouse interactions', () => { assertActiveElement(getPopoverButton()) }) ) + + it( + 'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using Portals', + suppressConsoleLogs(async () => { + let clickFn = jest.fn() + render( + + Toggle + + + + + + + ) + + // Open the popover + await click(getPopoverButton()) + + // Verify it is open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Click the button outside the Popover (DOM) but inside (Portal / React tree) + await click(getByText('foo')) + + // Verify it is still open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Verify the button was clicked + expect(clickFn).toHaveBeenCalled() + }) + ) + + it( + 'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using nested Portals', + suppressConsoleLogs(async () => { + let clickFn = jest.fn() + render( + + Toggle + + Level 0 + + Level 1 + + Level 2 + + Level 3 + + Level 4 + + + + + + + ) + + // Open the popover + await click(getPopoverButton()) + + // Verify it is open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Click the button outside the Popover (DOM) but inside (Portal / React tree) + await click(getByText('foo')) + + // Verify it is still open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Verify the button was clicked + expect(clickFn).toHaveBeenCalled() + }) + ) }) describe('Nested popovers', () => { diff --git a/packages/@headlessui-vue/src/components/popover/popover.test.ts b/packages/@headlessui-vue/src/components/popover/popover.test.ts index 825f2b33b4..449bb0d3b5 100644 --- a/packages/@headlessui-vue/src/components/popover/popover.test.ts +++ b/packages/@headlessui-vue/src/components/popover/popover.test.ts @@ -28,18 +28,14 @@ beforeAll(() => { afterAll(() => jest.restoreAllMocks()) -function getDefaultComponents() { - return { - Popover, - PopoverGroup, - PopoverButton, - PopoverPanel, - PopoverOverlay, - Portal, - } -} - -const renderTemplate = createRenderTemplate(getDefaultComponents()) +const renderTemplate = createRenderTemplate({ + Popover, + PopoverGroup, + PopoverButton, + PopoverPanel, + PopoverOverlay, + Portal, +}) describe('Safe guards', () => { it.each([ @@ -2573,6 +2569,87 @@ describe('Mouse interactions', () => { assertActiveElement(getPopoverButton()) }) ) + + it( + 'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using Portals', + suppressConsoleLogs(async () => { + let clickFn = jest.fn() + renderTemplate({ + template: html` + + Toggle + + + + + + + `, + setup: () => ({ clickFn }), + }) + + // Open the popover + await click(getPopoverButton()) + + // Verify it is open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Click the button outside the Popover (DOM) but inside (Portal / React tree) + await click(getByText('foo')) + + // Verify it is still open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Verify the button was clicked + expect(clickFn).toHaveBeenCalled() + }) + ) + + it( + 'should not close the Popover if the focus is moved outside of the Popover but still in the same React tree using nested Portals', + suppressConsoleLogs(async () => { + let clickFn = jest.fn() + renderTemplate({ + template: html` + + Toggle + + Level 0 + + Level 1 + + Level 2 + + Level 3 + + Level 4 + + + + + + + + `, + setup: () => ({ clickFn }), + }) + + // Open the popover + await click(getPopoverButton()) + + // Verify it is open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Click the button outside the Popover (DOM) but inside (Portal / React tree) + await click(getByText('foo')) + + // Verify it is still open + assertPopoverPanel({ state: PopoverState.Visible }) + + // Verify the button was clicked + expect(clickFn).toHaveBeenCalled() + }) + ) }) describe('Nested popovers', () => {