Skip to content

Commit

Permalink
Fix nested Popover components not opening (#2293)
Browse files Browse the repository at this point in the history
* fix nested `Popover`s not working

* update changelog
  • Loading branch information
RobinMalfait authored Feb 17, 2023
1 parent 10efaa9 commit c7f6bc6
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/@headlessui-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
61 changes: 61 additions & 0 deletions packages/@headlessui-react/src/components/popover/popover.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<Popover data-testid="popover-a">
<Popover.Button>Toggle A</Popover.Button>
<Popover.Panel>
<span>Contents A</span>
<Popover data-testid="popover-b">
<Popover.Button>Toggle B</Popover.Button>
<Popover.Panel>
<span>Contents B</span>
</Popover.Panel>
</Popover>
</Popover.Panel>
</Popover>
)

// 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
)
})
)
})
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,12 @@ let Button = forwardRefWithAs(function Button<TTag extends ElementType = typeof
let closeOthers = groupContext?.closeOthers

let panelContext = usePopoverPanelContext()
let isWithinPanel = panelContext !== null

// 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 = panelContext === null ? false : panelContext === state.panelId

useEffect(() => {
if (isWithinPanel) return
Expand Down
1 change: 1 addition & 0 deletions packages/@headlessui-vue/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
61 changes: 61 additions & 0 deletions packages/@headlessui-vue/src/components/popover/popover.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
<Popover data-testid="popover-a">
<PopoverButton>Toggle A</PopoverButton>
<PopoverPanel>
<span>Contents A</span>
<Popover data-testid="popover-b">
<PopoverButton>Toggle B</PopoverButton>
<PopoverPanel>
<span>Contents B</span>
</PopoverPanel>
</Popover>
</PopoverPanel>
</Popover>
`)

// 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
)
})
)
})
5 changes: 5 additions & 0 deletions packages/@headlessui-vue/src/components/popover/popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down

0 comments on commit c7f6bc6

Please sign in to comment.