From 6a79ea229969f94457131a7aa8a68d54a4fd1a2d Mon Sep 17 00:00:00 2001 From: David Arranz Date: Wed, 16 Sep 2026 19:43:10 +0200 Subject: [PATCH] =?UTF-8?q?Refactor=20formularios=20para=20eliminar=20el?= =?UTF-8?q?=20uso=20de=20register=20en=20favor=20de=20name=20en=20los=20ca?= =?UTF-8?q?mpos=20de=20formulario,=20mejorando=20la=20legibilidad=20y=20si?= =?UTF-8?q?mplificando=20la=20gesti=C3=B3n=20de=20formularios.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../quotes/components/QuotePricesResume.tsx | 10 ++--- .../editors/QuoteDetailsCardEditor.tsx | 16 ++++---- .../editors/QuoteGeneralCardEditor.tsx | 32 ++++----------- .../components/Forms/FormCurrencyField.tsx | 6 +-- .../components/Forms/FormPercentageField.tsx | 7 +--- .../components/Forms/FormQuantityField.tsx | 6 +-- .../components/Forms/FormTextAreaField.tsx | 19 +++------ client/src/components/Forms/FormTextField.tsx | 6 +-- client/src/ui/form.test.tsx | 41 +++++++++++++++++++ 9 files changed, 75 insertions(+), 68 deletions(-) diff --git a/client/src/app/quotes/components/QuotePricesResume.tsx b/client/src/app/quotes/components/QuotePricesResume.tsx index 659d0a8..ca5b891 100644 --- a/client/src/app/quotes/components/QuotePricesResume.tsx +++ b/client/src/app/quotes/components/QuotePricesResume.tsx @@ -66,7 +66,7 @@ const calculateQuoteTotals = (prices: any) => { }; export const QuotePricesResume = ({ currency }: { currency: CurrencyData }) => { - const { register, formState, control } = useFormContext(); + const { formState, control } = useFormContext(); const { formatNumber } = useLocalization(); const pricesWatch = useWatch({ control, name: ["subtotal_price", "discount", "tax"] }); @@ -105,9 +105,7 @@ export const QuotePricesResume = ({ currency }: { currency: CurrencyData }) => { scale={2} disabled={formState.disabled} placeholder={t("quotes.form_fields.discount.placeholder")} - {...register("discount", { - required: false, - })} + name='discount' onChange={(value) => { console.log("discount", value); }} @@ -134,9 +132,7 @@ export const QuotePricesResume = ({ currency }: { currency: CurrencyData }) => { scale={2} disabled={formState.disabled} placeholder={t("quotes.form_fields.tax.placeholder")} - {...register("tax", { - required: false, - })} + name='tax' />
diff --git a/client/src/app/quotes/components/editors/QuoteDetailsCardEditor.tsx b/client/src/app/quotes/components/editors/QuoteDetailsCardEditor.tsx index 457ca7a..9d763d4 100644 --- a/client/src/app/quotes/components/editors/QuoteDetailsCardEditor.tsx +++ b/client/src/app/quotes/components/editors/QuoteDetailsCardEditor.tsx @@ -31,7 +31,7 @@ export const QuoteDetailsCardEditor = ({ defaultValues: Readonly<{ [x: string]: any }> | undefined; }) => { const { toast } = useToast(); - const { control, register } = useFormContext(); + const { control } = useFormContext(); const [pickerMode] = useState<"dialog" | "panel">("dialog"); @@ -65,7 +65,7 @@ export const QuoteDetailsCardEditor = ({ ); }, @@ -80,7 +80,7 @@ export const QuoteDetailsCardEditor = ({ ); }, @@ -98,7 +98,7 @@ export const QuoteDetailsCardEditor = ({ ); }, @@ -117,7 +117,7 @@ export const QuoteDetailsCardEditor = ({ language={language} scale={2} className='text-right' - {...register(`items.${index}.unit_price`)} + name={`items.${index}.unit_price`} /> ); }, @@ -138,7 +138,7 @@ export const QuoteDetailsCardEditor = ({ scale={2} readOnly className='text-right' - {...register(`items.${index}.subtotal_price`)} + name={`items.${index}.subtotal_price`} /> ); }, @@ -155,7 +155,7 @@ export const QuoteDetailsCardEditor = ({ ); }, @@ -176,7 +176,7 @@ export const QuoteDetailsCardEditor = ({ scale={2} readOnly className='font-semibold text-right' - {...register(`items.${index}.total_price`)} + name={`items.${index}.total_price`} /> ); }, diff --git a/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx b/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx index 27634b3..7cd864d 100644 --- a/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx +++ b/client/src/app/quotes/components/editors/QuoteGeneralCardEditor.tsx @@ -4,7 +4,7 @@ import { t } from "i18next"; import { useFormContext } from "react-hook-form"; export const QuoteGeneralCardEditor = () => { - const { register, formState } = useFormContext(); + const { formState } = useFormContext(); return (
@@ -21,7 +21,7 @@ export const QuoteGeneralCardEditor = () => { description={t("quotes.form_fields.customer_reference.desc")} disabled={formState.disabled} placeholder={t("quotes.form_fields.customer_reference.placeholder")} - {...register("customer_reference")} + name='customer_reference' /> { description={t("quotes.form_fields.date.desc")} disabled={formState.disabled} placeholder={t("quotes.form_fields.date.placeholder")} - {...register("date", { - required: true, - })} + name='date' /> { description={t("quotes.form_fields.payment_method.desc")} disabled={formState.disabled} placeholder={t("quotes.form_fields.payment_method.placeholder")} - {...register("payment_method", { - required: false, - })} + name='payment_method' />
@@ -62,9 +56,7 @@ export const QuoteGeneralCardEditor = () => { description={t("quotes.form_fields.customer_information.desc")} disabled={formState.disabled} placeholder={t("quotes.form_fields.customer_information.placeholder")} - {...register("customer_information", { - required: true, - })} + name='customer_information' errors={formState.errors} /> @@ -74,9 +66,7 @@ export const QuoteGeneralCardEditor = () => { description={t("quotes.form_fields.notes.desc")} disabled={formState.disabled} placeholder={t("quotes.form_fields.notes.placeholder")} - {...register("notes", { - required: false, - })} + name='notes' />
@@ -105,9 +95,7 @@ export const QuoteGeneralCardEditor = () => { description={t("quotes.form_fields.lang_code.desc")} //disabled={formState.disabled} placeholder={t("quotes.form_fields.lang_code.placeholder")} - {...register("lang_code", { - required: true, - })} + name='lang_code' /> { description={t("quotes.form_fields.currency_code.desc")} //disabled={formState.disabled} placeholder={t("quotes.form_fields.currency_code.placeholder")} - {...register("currency_code", { - required: true, - })} + name='currency_code' />
diff --git a/client/src/components/Forms/FormCurrencyField.tsx b/client/src/components/Forms/FormCurrencyField.tsx index 06531f0..f3f4d4e 100644 --- a/client/src/components/Forms/FormCurrencyField.tsx +++ b/client/src/components/Forms/FormCurrencyField.tsx @@ -43,8 +43,7 @@ export type FormCurrencyFieldProps< scale: number; }; -export const FormCurrencyField = React.forwardRef( - (props /*ref*/) => { +export const FormCurrencyField = (props: FormCurrencyFieldProps) => { const { name, label, @@ -156,7 +155,6 @@ export const FormCurrencyField = React.forwardRef ); - } -); +}; FormCurrencyField.displayName = "FormCurrencyField"; diff --git a/client/src/components/Forms/FormPercentageField.tsx b/client/src/components/Forms/FormPercentageField.tsx index 92f12b2..b132150 100644 --- a/client/src/components/Forms/FormPercentageField.tsx +++ b/client/src/components/Forms/FormPercentageField.tsx @@ -42,10 +42,7 @@ export type FormPercentageFieldProps< scale: number; }; -export const FormPercentageField = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & FormPercentageFieldProps ->((props /*ref*/) => { +export const FormPercentageField = (props: FormPercentageFieldProps) => { const { name, label, @@ -144,4 +141,4 @@ export const FormPercentageField = React.forwardRef< }} /> ); -}); +}; diff --git a/client/src/components/Forms/FormQuantityField.tsx b/client/src/components/Forms/FormQuantityField.tsx index d7d70d9..8a6c16c 100644 --- a/client/src/components/Forms/FormQuantityField.tsx +++ b/client/src/components/Forms/FormQuantityField.tsx @@ -41,8 +41,7 @@ export type FormQuantityFieldProps< scale: number; }; -export const FormQuantityField = React.forwardRef( - (props /*ref*/) => { +export const FormQuantityField = (props: FormQuantityFieldProps) => { const { name, label, @@ -140,5 +139,4 @@ export const FormQuantityField = React.forwardRef ); - } -); +}; diff --git a/client/src/components/Forms/FormTextAreaField.tsx b/client/src/components/Forms/FormTextAreaField.tsx index 26bc79f..6838650 100644 --- a/client/src/components/Forms/FormTextAreaField.tsx +++ b/client/src/components/Forms/FormTextAreaField.tsx @@ -46,11 +46,7 @@ export type FormTextAreaFieldProps< errors?: FieldErrors; } & VariantProps; -export const FormTextAreaField = React.forwardRef< - HTMLDivElement, - React.TextareaHTMLAttributes & FormTextAreaFieldProps ->( - ({ +export const FormTextAreaField = ({ name, label, hint, @@ -64,11 +60,9 @@ export const FormTextAreaField = React.forwardRef< variant, ...props - }) => - /*ref*/ - { - const { control } = useFormContext(); - return ( + }: React.TextareaHTMLAttributes & FormTextAreaFieldProps) => { + const { control } = useFormContext(); + return ( )} /> - ); - } -); + ); +}; diff --git a/client/src/components/Forms/FormTextField.tsx b/client/src/components/Forms/FormTextField.tsx index a157480..b6ab8e8 100644 --- a/client/src/components/Forms/FormTextField.tsx +++ b/client/src/components/Forms/FormTextField.tsx @@ -33,8 +33,7 @@ export type FormTextFieldProps< UseControllerProps & VariantProps; -export const FormTextField = React.forwardRef( - (props /*ref*/) => { +export const FormTextField = (props: FormTextFieldProps) => { const { name, label, @@ -87,5 +86,4 @@ export const FormTextField = React.forwardRef ); - } -); +}; diff --git a/client/src/ui/form.test.tsx b/client/src/ui/form.test.tsx index 8f3cc59..d43ca6c 100644 --- a/client/src/ui/form.test.tsx +++ b/client/src/ui/form.test.tsx @@ -1,10 +1,12 @@ import { act, useEffect, useRef } from "react"; import { createRoot } from "react-dom/client"; import { FormProvider, UseFormReturn, useForm } from "react-hook-form"; +import { FormTextField } from "@/components/Forms/FormTextField"; import { FormField } from "./form"; type TestFormValues = { items: Array<{ quantity: string }>; + title: string; }; declare global { @@ -41,6 +43,7 @@ const FormHarness = ({ name, onMount, onFormReady }: FormHarnessProps) => { const form = useForm({ defaultValues: { items: [{ quantity: "1" }, { quantity: "2" }], + title: "before", }, }); @@ -55,6 +58,25 @@ const FormHarness = ({ name, onMount, onFormReady }: FormHarnessProps) => { ); }; +const ControlledTextFieldHarness = ({ onFormReady }: Pick) => { + const form = useForm({ + defaultValues: { + items: [], + title: "before", + }, + }); + + useEffect(() => { + onFormReady(form); + }, [form, onFormReady]); + + return ( + + + + ); +}; + describe("FormField", () => { let container: HTMLDivElement; let root: ReturnType; @@ -98,4 +120,23 @@ describe("FormField", () => { expect((container.querySelector("input[aria-label='quantity']") as HTMLInputElement).value).toBe("5"); expect(form?.getValues("items.1.quantity")).toBe("5"); }); + + it("updates React Hook Form through a Controller without an external register spread", () => { + let form: UseFormReturn | undefined; + + act(() => { + root.render( (form = value)} />); + }); + + const input = container.querySelector("input[name='title']") as HTMLInputElement; + const valueSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set; + + act(() => { + valueSetter?.call(input, "after"); + input.dispatchEvent(new Event("input", { bubbles: true })); + }); + + expect(input.value).toBe("after"); + expect(form?.getValues("title")).toBe("after"); + }); });