fix: revise shouldCloseOnInteractOutside for FreeSoloPopover (#2536)

* fix(select): set undefined to shouldCloseOnInteractOutside in select

* fix(select): set false to shouldCloseOnInteractOutside in autocomplete

* fix(popover): take shouldCloseOnInteractOutside from props

* chore(changeset):  revise shouldCloseOnInteractOutside for FreeSoloPopover

* feat(select): add test cases to select

* feat(select): assert that the select is open after click

* feat(autocomplete): add test cases to autocomplete

* feat(select): assert that the select is open after click
This commit is contained in:
աӄա 2024-03-22 00:04:59 +08:00 committed by GitHub
parent 26a3a7d2c6
commit 2b9f89023a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 192 additions and 5 deletions

View File

@ -0,0 +1,7 @@
---
"@nextui-org/autocomplete": patch
"@nextui-org/popover": patch
"@nextui-org/select": patch
---
revise shouldCloseOnInteractOutside for FreeSoloPopover

View File

@ -1,7 +1,9 @@
import * as React from "react";
import {render} from "@testing-library/react";
import {act, render} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {Autocomplete, AutocompleteItem, AutocompleteSection} from "../src";
import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter} from "../../modal/src";
type Item = {
label: string;
@ -133,4 +135,88 @@ describe("Autocomplete", () => {
expect(() => wrapper.unmount()).not.toThrow();
});
it("should close dropdown when clicking outside autocomplete", async () => {
const wrapper = render(
<Autocomplete
aria-label="Favorite Animal"
data-testid="close-when-clicking-outside-test"
label="Favorite Animal"
>
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
</Autocomplete>,
);
const autocomplete = wrapper.getByTestId("close-when-clicking-outside-test");
// open the select dropdown
await act(async () => {
await userEvent.click(autocomplete);
});
// assert that the autocomplete dropdown is open
expect(autocomplete).toHaveAttribute("aria-expanded", "true");
// click outside the autocomplete component
await act(async () => {
await userEvent.click(document.body);
});
// assert that the autocomplete is closed
expect(autocomplete).toHaveAttribute("aria-expanded", "false");
});
it("should close dropdown when clicking outside autocomplete with modal open", async () => {
const wrapper = render(
<Modal isOpen>
<ModalContent>
<ModalHeader>Modal header</ModalHeader>
<ModalBody>
<Autocomplete
aria-label="Favorite Animal"
data-testid="close-when-clicking-outside-test"
label="Favorite Animal"
>
<AutocompleteItem key="penguin" value="penguin">
Penguin
</AutocompleteItem>
<AutocompleteItem key="zebra" value="zebra">
Zebra
</AutocompleteItem>
<AutocompleteItem key="shark" value="shark">
Shark
</AutocompleteItem>
</Autocomplete>
</ModalBody>
<ModalFooter>Modal footer</ModalFooter>
</ModalContent>
</Modal>,
);
const autocomplete = wrapper.getByTestId("close-when-clicking-outside-test");
// open the autocomplete dropdown
await act(async () => {
await userEvent.click(autocomplete);
});
// assert that the autocomplete dropdown is open
expect(autocomplete).toHaveAttribute("aria-expanded", "true");
// click outside the autocomplete component
await act(async () => {
await userEvent.click(document.body);
});
// assert that the autocomplete dropdown is closed
expect(autocomplete).toHaveAttribute("aria-expanded", "false");
});
});

View File

@ -32,7 +32,12 @@ function Autocomplete<T extends object>(props: Props<T>, ref: ForwardedRef<HTMLI
} = useAutocomplete<T>({...props, ref});
const popoverContent = isOpen ? (
<FreeSoloPopover {...getPopoverProps()} state={state}>
<FreeSoloPopover
{...getPopoverProps()}
// avoid popover closing issue in autocomplete with open modal
shouldCloseOnInteractOutside={() => false}
state={state}
>
<ScrollShadow {...getListBoxWrapperProps()}>
<Listbox {...getListBoxProps()} />
</ScrollShadow>

View File

@ -74,8 +74,6 @@ const FreeSoloPopover = forwardRef<"div", FreeSoloPopoverProps>((props, ref) =>
getContentProps,
} = usePopover({
...props,
// avoid closing the popover when navigating with the keyboard
shouldCloseOnInteractOutside: () => false,
ref,
});

View File

@ -3,6 +3,7 @@ import {act, render} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {Select, SelectItem, SelectSection, type SelectProps} from "../src";
import {Modal, ModalContent, ModalHeader, ModalBody, ModalFooter} from "../../modal/src";
type Item = {
label: string;
@ -276,4 +277,88 @@ describe("Select", () => {
expect(wrapper.getByText("next Penguin")).toBeInTheDocument();
expect(wrapper.queryByText("Select an favorite animal")).toBe(null);
});
it("should close dropdown when clicking outside select", async () => {
const wrapper = render(
<Select
aria-label="Favorite Animal"
data-testid="close-when-clicking-outside-test"
label="Favorite Animal"
>
<SelectItem key="penguin" value="penguin">
Penguin
</SelectItem>
<SelectItem key="zebra" value="zebra">
Zebra
</SelectItem>
<SelectItem key="shark" value="shark">
Shark
</SelectItem>
</Select>,
);
const select = wrapper.getByTestId("close-when-clicking-outside-test");
// open the select dropdown
await act(async () => {
await userEvent.click(select);
});
// assert that the select is open
expect(select).toHaveAttribute("aria-expanded", "true");
// click outside the select component
await act(async () => {
await userEvent.click(document.body);
});
// assert that the select is closed
expect(select).toHaveAttribute("aria-expanded", "false");
});
it("should close dropdown when clicking outside select with modal open", async () => {
const wrapper = render(
<Modal isOpen>
<ModalContent>
<ModalHeader>Modal header</ModalHeader>
<ModalBody>
<Select
aria-label="Favorite Animal"
data-testid="close-when-clicking-outside-test"
label="Favorite Animal"
>
<SelectItem key="penguin" value="penguin">
Penguin
</SelectItem>
<SelectItem key="zebra" value="zebra">
Zebra
</SelectItem>
<SelectItem key="shark" value="shark">
Shark
</SelectItem>
</Select>
</ModalBody>
<ModalFooter>Modal footer</ModalFooter>
</ModalContent>
</Modal>,
);
const select = wrapper.getByTestId("close-when-clicking-outside-test");
// open the select dropdown
await act(async () => {
await userEvent.click(select);
});
// assert that the select is open
expect(select).toHaveAttribute("aria-expanded", "true");
// click outside the select component
await act(async () => {
await userEvent.click(document.body);
});
// assert that the select is closed
expect(select).toHaveAttribute("aria-expanded", "false");
});
});

View File

@ -103,7 +103,13 @@ function Select<T extends object>(props: Props<T>, ref: ForwardedRef<HTMLSelectE
const popoverContent = useMemo(
() =>
state.isOpen ? (
<FreeSoloPopover {...getPopoverProps()} state={state} triggerRef={triggerRef}>
<FreeSoloPopover
{...getPopoverProps()}
// avoid closing the popover when navigating with the keyboard
shouldCloseOnInteractOutside={undefined}
state={state}
triggerRef={triggerRef}
>
<ScrollShadow {...getListboxWrapperProps()}>
<Listbox {...getListboxProps()} />
</ScrollShadow>