From b72d0d5c9098e69cc670c9cc3282f2d4d0be01a9 Mon Sep 17 00:00:00 2001 From: Romain Le Cellier Date: Wed, 26 Jul 2023 16:52:22 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(react)=20react-hook-form=20Input=20ex?= =?UTF-8?q?ample?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our form elements needs to be usable with react-hook-form --- .../src/components/Forms/Input/index.mdx | 10 +++- .../src/components/Forms/Input/index.spec.tsx | 4 +- .../components/Forms/Input/index.stories.tsx | 51 ++++++++++++++++++- .../src/components/Forms/Input/index.tsx | 27 +++++----- .../react/src/tests/reactHookFormUtils.tsx | 26 ++++++++++ 5 files changed, 99 insertions(+), 19 deletions(-) create mode 100644 packages/react/src/tests/reactHookFormUtils.tsx diff --git a/packages/react/src/components/Forms/Input/index.mdx b/packages/react/src/components/Forms/Input/index.mdx index 63da25a..4a925dd 100644 --- a/packages/react/src/components/Forms/Input/index.mdx +++ b/packages/react/src/components/Forms/Input/index.mdx @@ -100,12 +100,20 @@ using the component in a controlled way. ## Ref -You can use the `ref` props to get a reference to the input element. The ref to the native input is nested inside the `input` attribute. +You can use the `ref` props to get a reference to the input element. +## Usage with react-hook-form + +You can use this input with [react-hook-form](https://react-hook-form.com/docs) + + + + + ## Props You can use all the props of the native html `` element props plus the following. diff --git a/packages/react/src/components/Forms/Input/index.spec.tsx b/packages/react/src/components/Forms/Input/index.spec.tsx index f558d4e..b26264c 100644 --- a/packages/react/src/components/Forms/Input/index.spec.tsx +++ b/packages/react/src/components/Forms/Input/index.spec.tsx @@ -1,4 +1,4 @@ -import { render, screen } from "@testing-library/react"; +import { render, screen, waitFor } from "@testing-library/react"; import React, { useRef } from "react"; import userEvent from "@testing-library/user-event"; import { expect } from "vitest"; @@ -160,7 +160,7 @@ describe("", () => { }); expect(input).not.toHaveFocus(); await user.click(screen.getByRole("button", { name: "Focus" })); - expect(input).toHaveFocus(); + waitFor(() => expect(input).toHaveFocus()); }); it("works controlled", async () => { const Wrapper = () => { diff --git a/packages/react/src/components/Forms/Input/index.stories.tsx b/packages/react/src/components/Forms/Input/index.stories.tsx index 7a00f45..42924e3 100644 --- a/packages/react/src/components/Forms/Input/index.stories.tsx +++ b/packages/react/src/components/Forms/Input/index.stories.tsx @@ -1,7 +1,15 @@ +import { yupResolver } from "@hookform/resolvers/yup"; import { Meta } from "@storybook/react"; +import { useForm } from "react-hook-form"; +import * as Yup from "yup"; import React, { useRef } from "react"; import { Input, InputRefType } from ":/components/Forms/Input/index"; import { Button } from ":/components/Button"; +import { + getFieldState, + getFieldErrorMessage, + onSubmit, +} from ":/tests/reactHookFormUtils"; export default { title: "Components/Forms/Input", @@ -162,11 +170,52 @@ export const WithRef = () => { return (
- +
); }; +export const ReactHookForm = () => { + interface InputExampleFormValues { + email: string; + } + + const inputExampleSchema = Yup.object().shape({ + email: Yup.string().email().required(), + }); + const { register, handleSubmit, formState } = useForm( + { + defaultValues: { + email: "", + }, + mode: "onChange", + reValidateMode: "onChange", + resolver: yupResolver(inputExampleSchema), + }, + ); + + return ( +
+ + +
+ ); +}; + export const FormExample = () => { return (
diff --git a/packages/react/src/components/Forms/Input/index.tsx b/packages/react/src/components/Forms/Input/index.tsx index 9864801..a1a6afe 100644 --- a/packages/react/src/components/Forms/Input/index.tsx +++ b/packages/react/src/components/Forms/Input/index.tsx @@ -3,7 +3,6 @@ import React, { InputHTMLAttributes, ReactNode, useEffect, - useImperativeHandle, useRef, useState, } from "react"; @@ -12,10 +11,6 @@ import { randomString } from ":/utils"; import { Field, FieldProps } from ":/components/Forms/Field"; import { LabelledBox } from ":/components/Forms/LabelledBox"; -export interface InputRefType { - input: HTMLInputElement | null; -} - type Props = InputHTMLAttributes & FieldProps & { label?: string; @@ -25,7 +20,7 @@ type Props = InputHTMLAttributes & charCounterMax?: number; }; -export const Input = forwardRef( +export const Input = forwardRef( ( { className, @@ -48,8 +43,7 @@ export const Input = forwardRef( if (className) { classes.push(className); } - - const inputRef = useRef(null); + const inputRef = useRef(null); const [inputFocus, setInputFocus] = useState(false); const [value, setValue] = useState(defaultValue || props.value || ""); const [labelAsPlaceholder, setLabelAsPlaceholder] = useState(!value); @@ -78,12 +72,6 @@ export const Input = forwardRef( setValue(props.value || ""); }, [props.value]); - useImperativeHandle(ref, () => ({ - get input() { - return inputRef.current; - }, - })); - return ( ( setValue(e.target.value); props.onChange?.(e); }} - ref={inputRef} + ref={(inputTextRef) => { + if (ref) { + if (typeof ref === "function") { + ref(inputTextRef); + } else { + ref.current = inputTextRef; + } + } + inputRef.current = inputTextRef; + }} /> {!!rightIcon && ( diff --git a/packages/react/src/tests/reactHookFormUtils.tsx b/packages/react/src/tests/reactHookFormUtils.tsx new file mode 100644 index 0000000..9ac4bf3 --- /dev/null +++ b/packages/react/src/tests/reactHookFormUtils.tsx @@ -0,0 +1,26 @@ +import { FieldValues, FormState } from "react-hook-form"; + +export function getFieldState( + field: keyof FormValues, + formState: FormState, +) { + if (field in formState.errors) { + return "error"; + } + return "default"; +} + +export function getFieldErrorMessage( + field: keyof FormValues, + formState: FormState, +): string { + const errorMessage = formState.errors[field]?.message; + if (!errorMessage) { + return ""; + } + return errorMessage as string; +} + +export function onSubmit(data: FormValues) { + alert(`Submited form values: ${JSON.stringify(data)}`); +}