import "@testing-library/jest-dom"; import type {UserEvent} from "@testing-library/user-event"; import * as React from "react"; import {render, fireEvent, act} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import {Button} from "@heroui/button"; import {spy, shouldIgnoreReactWarning} from "@heroui/test-utils"; import {Popover, PopoverContent, PopoverTrigger} from "../src"; import {Select, SelectItem} from "../../select/src"; describe("Popover", () => { let user: UserEvent; beforeEach(() => { user = userEvent.setup(); }); afterEach(() => { jest.clearAllMocks(); }); it("should render correctly", () => { const wrapper = render(

This is the content of the popover.

, ); expect(() => wrapper.unmount()).not.toThrow(); }); it("should not throw error when clicking trigger button", async () => { const wrapper = render(

This is the content of the popover.

, ); const trigger = wrapper.getByTestId("trigger-test"); // open popover await user.click(trigger); if (!shouldIgnoreReactWarning(spy)) { expect(spy).toHaveBeenCalledTimes(0); } }); it("ref should be forwarded", () => { const ref = React.createRef(); render(

This is the content of the popover.

, ); expect(ref.current).not.toBeNull(); }); it("should hide the popover when pressing the escape key", () => { const onClose = jest.fn(); const wrapper = render( (!isOpen ? onClose() : undefined)}>

This is the content of the popover.

, ); const content = wrapper.getByTestId("content-test"); fireEvent.keyDown(content, {key: "Escape"}); expect(onClose).toHaveBeenCalledTimes(1); }); it("should still hide the popover when pressing the escape key ", () => { const onClose = jest.fn(); const wrapper = render(

This is the content of the popover.

, ); const content = wrapper.getByTestId("content-test"); fireEvent.keyDown(content, {key: "Escape"}); expect(onClose).toHaveBeenCalledTimes(1); }); it("should hide the popover on blur (shouldCloseOnBlur=true)", () => { const onClose = jest.fn(); const wrapper = render(

This is the content of the popover.

, ); const content = wrapper.getByTestId("content-test"); expect(content).toHaveFocus(); fireEvent.keyDown(content, {key: "Escape"}); expect(onClose).toHaveBeenCalledTimes(1); }); it("should work with HeroUI button", async () => { const onClose = jest.fn(); const wrapper = render(

This is the content of the popover.

, ); const trigger = wrapper.getByTestId("trigger-test"); // open popover await user.click(trigger); expect(onClose).toHaveBeenCalledTimes(0); // close popover await user.click(trigger); expect(onClose).toHaveBeenCalledTimes(1); }); it("should close listbox by clicking another popover", async () => { const wrapper = render( <>

This is the content of the popover.

This is the content of the popover.

, ); const popover = wrapper.getByTestId("popover"); const popover2 = wrapper.getByTestId("popover2"); expect(popover).not.toBeNull(); expect(popover2).not.toBeNull(); // open the popover by clicking popover in the first popover await user.click(popover); // assert that the first popover is open expect(popover).toHaveAttribute("aria-expanded", "true"); // assert that the second popover is close expect(popover2).toHaveAttribute("aria-expanded", "false"); // close the popover by clicking the second popover await user.click(popover2); // assert that the first popover is closed expect(popover).toHaveAttribute("aria-expanded", "false"); // assert that the second popover is open expect(popover2).toHaveAttribute("aria-expanded", "true"); }); it("should focus on dialog when opened", async () => { const wrapper = render(

This is the content of the popover.

, ); const trigger = wrapper.getByTestId("trigger-test"); // open popover await user.click(trigger); const {getByRole} = wrapper; let dialog = getByRole("dialog"); // assert that the focus is on the dialog expect(dialog).toHaveFocus(); }); it("should restore focus on trigger when closed", async () => { const wrapper = render(

This is the content of the popover.

, ); const trigger = wrapper.getByTestId("popover-trigger"); await act(async () => { // open popover await user.pointer({target: trigger, keys: "[MouseLeft>]"}); // close popover await user.pointer({target: trigger, keys: "[/MouseLeft]"}); // assert that the focus is restored back to trigger expect(trigger).toHaveFocus(); }); }); it("should not close popover if nested select is closed", async () => { const wrapper = render( , ); const popover = wrapper.getByTestId("popover"); // open popover await user.click(popover); // assert that the popover is open expect(popover).toHaveAttribute("aria-expanded", "true"); const select = wrapper.getByTestId("select"); // open select await user.click(select); // assert that the select is open expect(select).toHaveAttribute("aria-expanded", "true"); await user.click(document.body); // assert that the select is closed expect(select).toHaveAttribute("aria-expanded", "false"); // assert that the popover is still open expect(popover).toHaveAttribute("aria-expanded", "true"); }); it("should close popover on scroll", async () => { const wrapper = render( , ); const popover = wrapper.getByTestId("popover"); // open popover await user.click(popover); // assert that the popover is open expect(popover).toHaveAttribute("aria-expanded", "true"); // scroll it fireEvent.scroll(document.body); // assert that the popover is closed expect(popover).toHaveAttribute("aria-expanded", "false"); }); }); it("should close popover on scroll when shouldCloseOnScroll is false", async () => { const wrapper = render( , ); const popover = wrapper.getByTestId("popover"); // open popover await act(async () => { await userEvent.click(popover); }); // assert that the popover is open expect(popover).toHaveAttribute("aria-expanded", "true"); // scroll it fireEvent.scroll(document.body); // assert that the popover is still open expect(popover).toHaveAttribute("aria-expanded", "true"); }); it("should display popover content only after content is ready", async () => { jest.useFakeTimers(); const TestComponent = () => { const [content, setContent] = React.useState(""); React.useEffect(() => { const timer = setTimeout(() => { setContent("test content"); }, 1000); return () => clearTimeout(timer); }, []); return ( {content} ); }; const wrapper = render(); expect(wrapper.queryByTestId("content-test")).not.toBeInTheDocument(); act(() => { jest.advanceTimersByTime(1000); }); expect(wrapper.getByTestId("content-test")).toBeInTheDocument(); jest.useRealTimers(); });