diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f58df..03ffda3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Stop the event from propagating in the `Popover` component ([#798](https://github.com/tailwindlabs/headlessui/pull/798)) - Allow to click on elements inside a `Dialog.Overlay` ([#816](https://github.com/tailwindlabs/headlessui/pull/816)) +- Ensure interactability with `Popover.Panel` contents when using the `static` prop ([#857](https://github.com/tailwindlabs/headlessui/pull/857)) ## [Unreleased - Vue] diff --git a/packages/@headlessui-react/src/components/popover/popover.test.tsx b/packages/@headlessui-react/src/components/popover/popover.test.tsx index 3df9c9b..ef1b208 100644 --- a/packages/@headlessui-react/src/components/popover/popover.test.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.test.tsx @@ -1,5 +1,5 @@ import React, { createElement, useEffect, useRef } from 'react' -import { render } from '@testing-library/react' +import { render, screen } from '@testing-library/react' import { Popover } from './popover' import { suppressConsoleLogs } from '../../test-utils/suppress-console-logs' @@ -2125,4 +2125,78 @@ describe('Mouse interactions', () => { assertActiveElement(getPopoverButton()) }) ) + + it( + 'should not close the Popover when clicking on a focusable element inside a static Popover.Panel', + suppressConsoleLogs(async () => { + let clickFn = jest.fn() + + render( + + Open + + + + + ) + + // Open the popover + await click(getPopoverButton()) + + // The button should not close the popover + await click(getByText('btn')) + + // Verify it is still open + assertPopoverButton({ state: PopoverState.Visible }) + + // Verify we actually clicked the button + expect(clickFn).toHaveBeenCalledTimes(1) + }) + ) + + it( + 'should not close the Popover when clicking on a non-focusable element inside a static Popover.Panel', + suppressConsoleLogs(async () => { + render( + + Open + + element + + + ) + + // Open the popover + await click(getPopoverButton()) + + // The element should not close the popover + await click(getByText('element')) + + // Verify it is still open + assertPopoverButton({ state: PopoverState.Visible }) + }) + ) + + it( + 'should close the Popover when clicking outside of a static Popover.Panel', + suppressConsoleLogs(async () => { + render( + + Open + + element + + + ) + + // Open the popover + await click(getPopoverButton()) + + // The element should close the popover + await click(document.body) + + // Verify it is still open + assertPopoverButton({ state: PopoverState.InvisibleHidden }) + }) + ) }) diff --git a/packages/@headlessui-react/src/components/popover/popover.tsx b/packages/@headlessui-react/src/components/popover/popover.tsx index 9ddbf11..f0e7264 100644 --- a/packages/@headlessui-react/src/components/popover/popover.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.tsx @@ -619,10 +619,12 @@ let Panel = forwardRefWithAs(function Panel { + if (props.static) return + if (state.popoverState === PopoverStates.Closed && (props.unmount ?? true)) { dispatch({ type: ActionTypes.SetPanel, panel: null }) } - }, [state.popoverState, props.unmount, dispatch]) + }, [state.popoverState, props.unmount, props.static, dispatch]) // Move focus within panel useEffect(() => { diff --git a/packages/@headlessui-vue/src/components/popover/popover.test.ts b/packages/@headlessui-vue/src/components/popover/popover.test.ts index bc872cc..7ba73f6 100644 --- a/packages/@headlessui-vue/src/components/popover/popover.test.ts +++ b/packages/@headlessui-vue/src/components/popover/popover.test.ts @@ -2323,4 +2323,81 @@ describe('Mouse interactions', () => { assertActiveElement(getPopoverButton()) }) ) + + it( + 'should not close the Popover when clicking on a focusable element inside a static PopoverPanel', + suppressConsoleLogs(async () => { + let clickFn = jest.fn() + + renderTemplate({ + template: html` + + Open + + + + + `, + setup: () => ({ clickFn }), + }) + + // Open the popover + await click(getPopoverButton()) + + // The button should not close the popover + await click(getByText('btn')) + + // Verify it is still open + assertPopoverButton({ state: PopoverState.Visible }) + + // Verify we actually clicked the button + expect(clickFn).toHaveBeenCalledTimes(1) + }) + ) + + it( + 'should not close the Popover when clicking on a non-focusable element inside a static PopoverPanel', + suppressConsoleLogs(async () => { + renderTemplate(html` + + Open + + element + + + `) + + // Open the popover + await click(getPopoverButton()) + + // The element should not close the popover + await click(getByText('element')) + + // Verify it is still open + assertPopoverButton({ state: PopoverState.Visible }) + }) + ) + + it( + 'should close the Popover when clicking outside of a static PopoverPanel', + suppressConsoleLogs(async () => { + renderTemplate(html` + + Open + + element + + + `) + + // Open the popover + await click(getPopoverButton()) + + // The element should close the popover + await click(document.body) + + // Verify it is still open + assertPopoverButton({ state: PopoverState.InvisibleHidden }) + }) + ) })