From eb594e289afbc02d831c5b875da6dc4713ec8231 Mon Sep 17 00:00:00 2001 From: Maharshi Alpesh Date: Fri, 13 Dec 2024 01:08:04 +0530 Subject: [PATCH 1/2] fix: input should filter away disallowed characters from value prop --- .changeset/silent-jars-grow.md | 5 ++ .../input-otp/__tests__/input-otp.test.tsx | 36 ++++++++++++- .../components/input-otp/src/use-input-otp.ts | 9 ++++ .../input-otp/stories/input-otp.stories.tsx | 53 ++++++++++++++++++- 4 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 .changeset/silent-jars-grow.md diff --git a/.changeset/silent-jars-grow.md b/.changeset/silent-jars-grow.md new file mode 100644 index 0000000000..d39e74f92d --- /dev/null +++ b/.changeset/silent-jars-grow.md @@ -0,0 +1,5 @@ +--- +"@nextui-org/input-otp": patch +--- + +Change ensures that the input value does not accept any disallowed characters when the value prop contains them(#4329) diff --git a/packages/components/input-otp/__tests__/input-otp.test.tsx b/packages/components/input-otp/__tests__/input-otp.test.tsx index 36f967f183..c1ad664d05 100644 --- a/packages/components/input-otp/__tests__/input-otp.test.tsx +++ b/packages/components/input-otp/__tests__/input-otp.test.tsx @@ -2,7 +2,7 @@ import "@testing-library/jest-dom"; import * as React from "react"; import {render, renderHook, screen} from "@testing-library/react"; -import {useForm} from "react-hook-form"; +import {Controller, useForm} from "react-hook-form"; import userEvent, {UserEvent} from "@testing-library/user-event"; import {InputOtp} from "../src"; @@ -250,4 +250,38 @@ describe("InputOtp with react-hook-form", () => { expect(onSubmit).toHaveBeenCalledTimes(1); }); + + it("should work correctly wiht react-hook-form controller", async () => { + const {result} = renderHook(() => + useForm({ + defaultValues: { + withController: "", + }, + }), + ); + + const {control} = result.current; + + render( +
+ } + /> + , + ); + + const inputOtp = screen.getByTestId("input-otp"); + const input = inputOtp.querySelector("input"); + + if (!input) { + throw new Error("Input not found"); + } + + await user.click(input); + await user.type(input, "1nj23aa4"); + + expect(input).toHaveAttribute("value", "1234"); + }); }); diff --git a/packages/components/input-otp/src/use-input-otp.ts b/packages/components/input-otp/src/use-input-otp.ts index 2604ba6a63..0206c4565d 100644 --- a/packages/components/input-otp/src/use-input-otp.ts +++ b/packages/components/input-otp/src/use-input-otp.ts @@ -119,6 +119,7 @@ export function useInputOtp(originalProps: UseInputOtpProps) { pasteTransformer, containerClassName, noScriptCSSFallback, + onChange, ...otherProps } = props; @@ -209,6 +210,14 @@ export function useInputOtp(originalProps: UseInputOtpProps) { }), filterDOMProps(props), ), + onChange: (e: React.ChangeEvent) => { + const val = e.target?.value; + const regex = new RegExp(allowedKeys); + + if (regex.test(val)) { + onChange?.(e); + } + }, }; }, [baseDomRef, slots, baseStyles, isDisabled, isInvalid, isRequired, isReadOnly, value, length], diff --git a/packages/components/input-otp/stories/input-otp.stories.tsx b/packages/components/input-otp/stories/input-otp.stories.tsx index f79a749bd7..3726f36d51 100644 --- a/packages/components/input-otp/stories/input-otp.stories.tsx +++ b/packages/components/input-otp/stories/input-otp.stories.tsx @@ -1,7 +1,7 @@ import React from "react"; import {Meta} from "@storybook/react"; import {button, inputOtp} from "@nextui-org/theme"; -import {useForm} from "react-hook-form"; +import {Controller, useForm} from "react-hook-form"; import {ValidationResult} from "@react-types/shared"; import {Button} from "@nextui-org/button"; import {Form} from "@nextui-org/form"; @@ -177,6 +177,49 @@ const WithReactHookFormTemplate = (args) => { ); }; +const WithReactHookFormControllerTemplate = (args) => { + const { + handleSubmit, + control, + formState: {errors}, + } = useForm({ + defaultValues: { + otp: "", + }, + }); + + const onSubmit = (data) => { + alert(JSON.stringify(data)); + }; + + return ( +
+ ( + + )} + rules={{ + required: "OTP is required", + minLength: { + value: 4, + message: "Please enter a valid OTP", + }, + }} + /> + + + ); +}; + const ServerValidationTemplate = (args) => { const [serverErrors, setServerErrors] = React.useState({}); @@ -337,6 +380,14 @@ export const WithReactHookForm = { }, }; +export const WithReactHookFormController = { + render: WithReactHookFormControllerTemplate, + args: { + ...defaultProps, + length: 4, + }, +}; + export const CustomWithClassNames = { render: Template, args: { From 0b467c8415d53f6f074a2723067935e513aa8475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D5=A1=D3=84=D5=A1?= Date: Fri, 13 Dec 2024 20:38:32 +0800 Subject: [PATCH 2/2] chore(changeset): add space --- .changeset/silent-jars-grow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/silent-jars-grow.md b/.changeset/silent-jars-grow.md index d39e74f92d..fd977ba035 100644 --- a/.changeset/silent-jars-grow.md +++ b/.changeset/silent-jars-grow.md @@ -2,4 +2,4 @@ "@nextui-org/input-otp": patch --- -Change ensures that the input value does not accept any disallowed characters when the value prop contains them(#4329) +Change ensures that the input value does not accept any disallowed characters when the value prop contains them (#4329)