diff --git a/.changeset/tough-kiwis-knock.md b/.changeset/tough-kiwis-knock.md new file mode 100644 index 00000000000..5ec6b47788c --- /dev/null +++ b/.changeset/tough-kiwis-knock.md @@ -0,0 +1,7 @@ +--- +"@chakra-ui/hooks": patch +"@chakra-ui/popover": patch +--- + +Fixed an issue where the prop `isLazy` did not work as expected. +This was achieved by updating the hook `useAnimationState`. diff --git a/packages/hooks/src/use-animation-state.ts b/packages/hooks/src/use-animation-state.ts index f8d86fd32b9..69e2e0c906e 100644 --- a/packages/hooks/src/use-animation-state.ts +++ b/packages/hooks/src/use-animation-state.ts @@ -28,7 +28,7 @@ export function useAnimationState(props: UseAnimationStateProps) { () => ref.current, ) - const hidden = isOpen ? false : !mounted && once + const hidden = isOpen ? false : !mounted return { present: !hidden, diff --git a/packages/popover/tests/popover.test.tsx b/packages/popover/tests/popover.test.tsx index c40a4e882ae..abe85e21dd1 100644 --- a/packages/popover/tests/popover.test.tsx +++ b/packages/popover/tests/popover.test.tsx @@ -84,12 +84,59 @@ test("can close the popover by pressing escape", async () => { // utils.getByRole("dialog", { hidden: true }) }) -test("load content lazily", async () => { - const utils = render() +type LazyPopoverContentProps = { + mockFn: () => Promise +} + +const LazyPopoverContent = (props: LazyPopoverContentProps) => { + const { mockFn } = props + React.useEffect(() => { + mockFn() + }, []) + return

Lazy content

+} + +const LazyPopoverComponent = ( + props: UsePopoverProps & LazyPopoverContentProps, +) => { + const { + getTriggerProps, + getPopoverProps, + getPopoverPositionerProps, + onClose, + } = usePopover(props) + + return ( +
+ +
+
+ +
+ ), + })} + /> +
+ +
+ ) +} + +test("loads content lazily and unmounts the component from the DOM", async () => { + const mock = jest.fn() + const utils = render() // by default, content should not be visible let content = screen.queryByTestId("content") expect(content).not.toBeInTheDocument() + expect(mock).toHaveBeenCalledTimes(0) // open the popover fireEvent.click(utils.getByText(/open/i)) @@ -97,14 +144,61 @@ test("load content lazily", async () => { const dialog = await utils.findByRole("dialog") content = screen.queryByTestId("content") - // content should now be visible + // content should now be in the DOM expect(content).toBeInTheDocument() + expect(content).toBeVisible() + expect(mock).toHaveBeenCalledTimes(1) + + // close the popover with escape + fireEvent.keyDown(dialog, { key: "Escape" }) + expect(content).not.toBeInTheDocument() + expect(content).not.toBeVisible() + + // ensure that when popover reopens, it also + // gets remounted + fireEvent.click(utils.getByText(/open/i)) + await utils.findByRole("dialog") + + content = screen.queryByTestId("content") + expect(content).toBeInTheDocument() + expect(content).toBeVisible() + expect(mock).toHaveBeenCalledTimes(2) +}) + +test("loads content lazily and persists the component in the DOM", async () => { + const mock = jest.fn() + const utils = render( + , + ) + + // by default, content should not be visible + let content = screen.queryByTestId("content") + expect(content).not.toBeInTheDocument() + expect(mock).toHaveBeenCalledTimes(0) + + // open the popover + fireEvent.click(utils.getByText(/open/i)) + + const dialog = await utils.findByRole("dialog") + content = screen.queryByTestId("content") + + // content should now be in the DOM + expect(content).toBeInTheDocument() + expect(mock).toHaveBeenCalledTimes(1) // close the popover with escape fireEvent.keyDown(dialog, { key: "Escape" }) + expect(content).toBeInTheDocument() + expect(content).not.toBeVisible() + + // ensure that when popover reopens, it is + // not remounting + fireEvent.click(utils.getByText(/open/i)) + await utils.findByRole("dialog") - // content should still be visible + content = screen.queryByTestId("content") expect(content).toBeInTheDocument() + expect(mock).toHaveBeenCalledTimes(1) }) // For testing focus interaction, use another component with a focusable element inside.