diff --git a/packages/@headlessui-react/CHANGELOG.md b/packages/@headlessui-react/CHANGELOG.md index 6ce4c25..85e823d 100644 --- a/packages/@headlessui-react/CHANGELOG.md +++ b/packages/@headlessui-react/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Ensure the main tree and parent `Dialog` components are marked as `inert` ([#2290](https://github.com/tailwindlabs/headlessui/pull/2290)) +- Fix nested `Popover` components not opening ([#2293](https://github.com/tailwindlabs/headlessui/pull/2293)) ## [1.7.11] - 2023-02-15 diff --git a/packages/@headlessui-react/src/components/popover/popover.test.tsx b/packages/@headlessui-react/src/components/popover/popover.test.tsx index fb6d760..4f6c755 100644 --- a/packages/@headlessui-react/src/components/popover/popover.test.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.test.tsx @@ -2481,3 +2481,64 @@ describe('Mouse interactions', () => { }) ) }) + +describe('Nested popovers', () => { + it( + 'should be possible to nest Popover components and control them individually', + suppressConsoleLogs(async () => { + render( + + Toggle A + + Contents A + + Toggle B + + Contents B + + + + + ) + + // Verify that Popover B is not there yet + expect(document.querySelector('[data-testid="popover-b"]')).toBeNull() + + // Open Popover A + await click(getByText('Toggle A')) + + // Ensure Popover A is visible + assertPopoverPanel( + { state: PopoverState.Visible }, + document.querySelector( + '[data-testid="popover-a"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + + // Ensure Popover B is visible + assertPopoverPanel( + { state: PopoverState.InvisibleUnmounted }, + document.querySelector( + '[data-testid="popover-b"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + + // Open Popover B + await click(getByText('Toggle B')) + + // Ensure both popovers are open + assertPopoverPanel( + { state: PopoverState.Visible }, + document.querySelector( + '[data-testid="popover-a"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + assertPopoverPanel( + { state: PopoverState.Visible }, + document.querySelector( + '[data-testid="popover-b"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + }) + ) +}) diff --git a/packages/@headlessui-react/src/components/popover/popover.tsx b/packages/@headlessui-react/src/components/popover/popover.tsx index e14ae8a..96d9ad6 100644 --- a/packages/@headlessui-react/src/components/popover/popover.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.tsx @@ -393,7 +393,12 @@ let Button = forwardRefWithAs(function Button { if (isWithinPanel) return diff --git a/packages/@headlessui-vue/CHANGELOG.md b/packages/@headlessui-vue/CHANGELOG.md index 5deee34..f3fc70e 100644 --- a/packages/@headlessui-vue/CHANGELOG.md +++ b/packages/@headlessui-vue/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Ensure the main tree and parent `Dialog` components are marked as `inert` ([#2290](https://github.com/tailwindlabs/headlessui/pull/2290)) +- Fix nested `Popover` components not opening ([#2293](https://github.com/tailwindlabs/headlessui/pull/2293)) ## [1.7.10] - 2023-02-15 diff --git a/packages/@headlessui-vue/src/components/popover/popover.test.ts b/packages/@headlessui-vue/src/components/popover/popover.test.ts index f048460..825f2b3 100644 --- a/packages/@headlessui-vue/src/components/popover/popover.test.ts +++ b/packages/@headlessui-vue/src/components/popover/popover.test.ts @@ -2574,3 +2574,64 @@ describe('Mouse interactions', () => { }) ) }) + +describe('Nested popovers', () => { + it( + 'should be possible to nest Popover components and control them individually', + suppressConsoleLogs(async () => { + renderTemplate(html` + + Toggle A + + Contents A + + Toggle B + + Contents B + + + + + `) + + // Verify that Popover B is not there yet + expect(document.querySelector('[data-testid="popover-b"]')).toBeNull() + + // Open Popover A + await click(getByText('Toggle A')) + + // Ensure Popover A is visible + assertPopoverPanel( + { state: PopoverState.Visible }, + document.querySelector( + '[data-testid="popover-a"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + + // Ensure Popover B is visible + assertPopoverPanel( + { state: PopoverState.InvisibleUnmounted }, + document.querySelector( + '[data-testid="popover-b"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + + // Open Popover B + await click(getByText('Toggle B')) + + // Ensure both popovers are open + assertPopoverPanel( + { state: PopoverState.Visible }, + document.querySelector( + '[data-testid="popover-a"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + assertPopoverPanel( + { state: PopoverState.Visible }, + document.querySelector( + '[data-testid="popover-b"] [id^="headlessui-popover-panel-"]' + ) as HTMLElement + ) + }) + ) +}) diff --git a/packages/@headlessui-vue/src/components/popover/popover.ts b/packages/@headlessui-vue/src/components/popover/popover.ts index ff7a470..eb7d948 100644 --- a/packages/@headlessui-vue/src/components/popover/popover.ts +++ b/packages/@headlessui-vue/src/components/popover/popover.ts @@ -288,6 +288,11 @@ export let PopoverButton = defineComponent({ let closeOthers = groupContext?.closeOthers let panelContext = usePopoverPanelContext() + + // A button inside a panel will just have "close" functionality, no "open" functionality. + // However, if a `Popover.Button` is rendered inside a `Popover` which in turn is rendered + // inside a `Popover.Panel` (aka nested popovers), then we need to make sure that the button is + // able to open the nested popover. let isWithinPanel = computed(() => panelContext === null ? false : panelContext.value === api.panelId.value )