From 0401f2548f525fc4194186b54f563cb91c6ccd0c Mon Sep 17 00:00:00 2001 From: Peterl561 <76144929+Peterl561@users.noreply.github.com> Date: Fri, 3 Jan 2025 03:17:42 +0800 Subject: [PATCH] fix(autocomplete): validate prop not working after hovering (#4452) * fix(autocomplete): validate prop not working after hovering * test(autocomplete): validate prop function should work after hover * chore(changeset): fixed autocomplete validate not working after hover * chore(autocomplete): minor comment change --- .changeset/ninety-lobsters-deliver.md | 5 ++ .../__tests__/autocomplete.test.tsx | 54 +++++++++++++++++++ .../autocomplete/src/use-autocomplete.ts | 9 +++- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 .changeset/ninety-lobsters-deliver.md diff --git a/.changeset/ninety-lobsters-deliver.md b/.changeset/ninety-lobsters-deliver.md new file mode 100644 index 0000000000..62a92b9ea2 --- /dev/null +++ b/.changeset/ninety-lobsters-deliver.md @@ -0,0 +1,5 @@ +--- +"@nextui-org/autocomplete": patch +--- + +fixed autocomplete validate prop not working after hovering when validation behavior is native diff --git a/packages/components/autocomplete/__tests__/autocomplete.test.tsx b/packages/components/autocomplete/__tests__/autocomplete.test.tsx index bc06f15d5c..09160db838 100644 --- a/packages/components/autocomplete/__tests__/autocomplete.test.tsx +++ b/packages/components/autocomplete/__tests__/autocomplete.test.tsx @@ -782,6 +782,60 @@ describe("Autocomplete", () => { expect(input).not.toHaveAttribute("aria-describedby"); expect(input.validity.valid).toBe(true); }); + + // this test is to cover a case where hovering over the combobox causes the validation from use-input to overwrite the validation from use-autocomplete if not handled properly + // this causes the first form submit after initial render to always succeed even if the validate function returns an error + it("should work with validate after hovering", async () => { + const onSubmit = jest.fn((e) => { + e.preventDefault(); + }); + + const {getByTestId, findByRole} = render( +
+ { + if (!value?.selectedKey) { + return "Please select an animal"; + } + }} + validationBehavior="native" + /> + + , + ); + + const combobox = getByTestId("combobox") as HTMLInputElement; + const submit = getByTestId("submit"); + + expect(combobox).not.toHaveAttribute("aria-describedby"); + expect(combobox.validity.valid).toBe(false); + + await user.hover(combobox); + await user.click(submit); + + expect(onSubmit).toHaveBeenCalledTimes(0); + expect(combobox).toHaveAttribute("aria-describedby"); + expect( + document.getElementById(combobox.getAttribute("aria-describedby")!), + ).toHaveTextContent("Please select an animal"); + + await user.click(combobox); + await user.keyboard("pe"); + + const listbox = await findByRole("listbox"); + const items = within(listbox).getAllByRole("option"); + + await user.click(items[0]); + expect(combobox).toHaveAttribute("aria-describedby"); + + await user.click(submit); + expect(onSubmit).toHaveBeenCalledTimes(1); + expect(combobox).not.toHaveAttribute("aria-describedby"); + }); }); describe("validationBehavior=aria", () => { diff --git a/packages/components/autocomplete/src/use-autocomplete.ts b/packages/components/autocomplete/src/use-autocomplete.ts index 16ae255af6..4e258d8ba9 100644 --- a/packages/components/autocomplete/src/use-autocomplete.ts +++ b/packages/components/autocomplete/src/use-autocomplete.ts @@ -429,12 +429,19 @@ export function useAutocomplete(originalProps: UseAutocomplete }), } as ButtonProps); + // prevent use-input's useFormValidation hook from overwriting use-autocomplete's useFormValidation hook when there are uncommitted validation errors + // see https://github.com/nextui-org/nextui/pull/4452 + const hasUncommittedValidation = + validationBehavior === "native" && + state.displayValidation.isInvalid === false && + state.realtimeValidation.isInvalid === true; + const getInputProps = () => ({ ...otherProps, ...inputProps, ...slotsProps.inputProps, - isInvalid, + isInvalid: hasUncommittedValidation ? undefined : isInvalid, validationBehavior, errorMessage: typeof errorMessage === "function"