Skip to content

Commit c7f6bc6

Browse files
authored
Fix nested Popover components not opening (#2293)
* fix nested `Popover`s not working * update changelog
1 parent 10efaa9 commit c7f6bc6

File tree

6 files changed

+135
-1
lines changed

6 files changed

+135
-1
lines changed

packages/@headlessui-react/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- Ensure the main tree and parent `Dialog` components are marked as `inert` ([#2290](https://github.com/tailwindlabs/headlessui/pull/2290))
13+
- Fix nested `Popover` components not opening ([#2293](https://github.com/tailwindlabs/headlessui/pull/2293))
1314

1415
## [1.7.11] - 2023-02-15
1516

packages/@headlessui-react/src/components/popover/popover.test.tsx

+61
Original file line numberDiff line numberDiff line change
@@ -2481,3 +2481,64 @@ describe('Mouse interactions', () => {
24812481
})
24822482
)
24832483
})
2484+
2485+
describe('Nested popovers', () => {
2486+
it(
2487+
'should be possible to nest Popover components and control them individually',
2488+
suppressConsoleLogs(async () => {
2489+
render(
2490+
<Popover data-testid="popover-a">
2491+
<Popover.Button>Toggle A</Popover.Button>
2492+
<Popover.Panel>
2493+
<span>Contents A</span>
2494+
<Popover data-testid="popover-b">
2495+
<Popover.Button>Toggle B</Popover.Button>
2496+
<Popover.Panel>
2497+
<span>Contents B</span>
2498+
</Popover.Panel>
2499+
</Popover>
2500+
</Popover.Panel>
2501+
</Popover>
2502+
)
2503+
2504+
// Verify that Popover B is not there yet
2505+
expect(document.querySelector('[data-testid="popover-b"]')).toBeNull()
2506+
2507+
// Open Popover A
2508+
await click(getByText('Toggle A'))
2509+
2510+
// Ensure Popover A is visible
2511+
assertPopoverPanel(
2512+
{ state: PopoverState.Visible },
2513+
document.querySelector(
2514+
'[data-testid="popover-a"] [id^="headlessui-popover-panel-"]'
2515+
) as HTMLElement
2516+
)
2517+
2518+
// Ensure Popover B is visible
2519+
assertPopoverPanel(
2520+
{ state: PopoverState.InvisibleUnmounted },
2521+
document.querySelector(
2522+
'[data-testid="popover-b"] [id^="headlessui-popover-panel-"]'
2523+
) as HTMLElement
2524+
)
2525+
2526+
// Open Popover B
2527+
await click(getByText('Toggle B'))
2528+
2529+
// Ensure both popovers are open
2530+
assertPopoverPanel(
2531+
{ state: PopoverState.Visible },
2532+
document.querySelector(
2533+
'[data-testid="popover-a"] [id^="headlessui-popover-panel-"]'
2534+
) as HTMLElement
2535+
)
2536+
assertPopoverPanel(
2537+
{ state: PopoverState.Visible },
2538+
document.querySelector(
2539+
'[data-testid="popover-b"] [id^="headlessui-popover-panel-"]'
2540+
) as HTMLElement
2541+
)
2542+
})
2543+
)
2544+
})

packages/@headlessui-react/src/components/popover/popover.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,12 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
393393
let closeOthers = groupContext?.closeOthers
394394

395395
let panelContext = usePopoverPanelContext()
396-
let isWithinPanel = panelContext !== null
396+
397+
// A button inside a panel will just have "close" functionality, no "open" functionality. However,
398+
// if a `Popover.Button` is rendered inside a `Popover` which in turn is rendered inside a
399+
// `Popover.Panel` (aka nested popovers), then we need to make sure that the button is able to
400+
// open the nested popover.
401+
let isWithinPanel = panelContext === null ? false : panelContext === state.panelId
397402

398403
useEffect(() => {
399404
if (isWithinPanel) return

packages/@headlessui-vue/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- Ensure the main tree and parent `Dialog` components are marked as `inert` ([#2290](https://github.com/tailwindlabs/headlessui/pull/2290))
13+
- Fix nested `Popover` components not opening ([#2293](https://github.com/tailwindlabs/headlessui/pull/2293))
1314

1415
## [1.7.10] - 2023-02-15
1516

packages/@headlessui-vue/src/components/popover/popover.test.ts

+61
Original file line numberDiff line numberDiff line change
@@ -2574,3 +2574,64 @@ describe('Mouse interactions', () => {
25742574
})
25752575
)
25762576
})
2577+
2578+
describe('Nested popovers', () => {
2579+
it(
2580+
'should be possible to nest Popover components and control them individually',
2581+
suppressConsoleLogs(async () => {
2582+
renderTemplate(html`
2583+
<Popover data-testid="popover-a">
2584+
<PopoverButton>Toggle A</PopoverButton>
2585+
<PopoverPanel>
2586+
<span>Contents A</span>
2587+
<Popover data-testid="popover-b">
2588+
<PopoverButton>Toggle B</PopoverButton>
2589+
<PopoverPanel>
2590+
<span>Contents B</span>
2591+
</PopoverPanel>
2592+
</Popover>
2593+
</PopoverPanel>
2594+
</Popover>
2595+
`)
2596+
2597+
// Verify that Popover B is not there yet
2598+
expect(document.querySelector('[data-testid="popover-b"]')).toBeNull()
2599+
2600+
// Open Popover A
2601+
await click(getByText('Toggle A'))
2602+
2603+
// Ensure Popover A is visible
2604+
assertPopoverPanel(
2605+
{ state: PopoverState.Visible },
2606+
document.querySelector(
2607+
'[data-testid="popover-a"] [id^="headlessui-popover-panel-"]'
2608+
) as HTMLElement
2609+
)
2610+
2611+
// Ensure Popover B is visible
2612+
assertPopoverPanel(
2613+
{ state: PopoverState.InvisibleUnmounted },
2614+
document.querySelector(
2615+
'[data-testid="popover-b"] [id^="headlessui-popover-panel-"]'
2616+
) as HTMLElement
2617+
)
2618+
2619+
// Open Popover B
2620+
await click(getByText('Toggle B'))
2621+
2622+
// Ensure both popovers are open
2623+
assertPopoverPanel(
2624+
{ state: PopoverState.Visible },
2625+
document.querySelector(
2626+
'[data-testid="popover-a"] [id^="headlessui-popover-panel-"]'
2627+
) as HTMLElement
2628+
)
2629+
assertPopoverPanel(
2630+
{ state: PopoverState.Visible },
2631+
document.querySelector(
2632+
'[data-testid="popover-b"] [id^="headlessui-popover-panel-"]'
2633+
) as HTMLElement
2634+
)
2635+
})
2636+
)
2637+
})

packages/@headlessui-vue/src/components/popover/popover.ts

+5
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,11 @@ export let PopoverButton = defineComponent({
288288
let closeOthers = groupContext?.closeOthers
289289

290290
let panelContext = usePopoverPanelContext()
291+
292+
// A button inside a panel will just have "close" functionality, no "open" functionality.
293+
// However, if a `Popover.Button` is rendered inside a `Popover` which in turn is rendered
294+
// inside a `Popover.Panel` (aka nested popovers), then we need to make sure that the button is
295+
// able to open the nested popover.
291296
let isWithinPanel = computed(() =>
292297
panelContext === null ? false : panelContext.value === api.panelId.value
293298
)

0 commit comments

Comments
 (0)