Refactor formularios para eliminar el uso de register en favor de name en los campos de formulario, mejorando la legibilidad y simplificando la gestión de formularios.
This commit is contained in:
parent
3eb0396cbd
commit
6a79ea2299
@ -66,7 +66,7 @@ const calculateQuoteTotals = (prices: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const QuotePricesResume = ({ currency }: { currency: CurrencyData }) => {
|
export const QuotePricesResume = ({ currency }: { currency: CurrencyData }) => {
|
||||||
const { register, formState, control } = useFormContext();
|
const { formState, control } = useFormContext();
|
||||||
const { formatNumber } = useLocalization();
|
const { formatNumber } = useLocalization();
|
||||||
|
|
||||||
const pricesWatch = useWatch({ control, name: ["subtotal_price", "discount", "tax"] });
|
const pricesWatch = useWatch({ control, name: ["subtotal_price", "discount", "tax"] });
|
||||||
@ -105,9 +105,7 @@ export const QuotePricesResume = ({ currency }: { currency: CurrencyData }) => {
|
|||||||
scale={2}
|
scale={2}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.discount.placeholder")}
|
placeholder={t("quotes.form_fields.discount.placeholder")}
|
||||||
{...register("discount", {
|
name='discount'
|
||||||
required: false,
|
|
||||||
})}
|
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
console.log("discount", value);
|
console.log("discount", value);
|
||||||
}}
|
}}
|
||||||
@ -134,9 +132,7 @@ export const QuotePricesResume = ({ currency }: { currency: CurrencyData }) => {
|
|||||||
scale={2}
|
scale={2}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.tax.placeholder")}
|
placeholder={t("quotes.form_fields.tax.placeholder")}
|
||||||
{...register("tax", {
|
name='tax'
|
||||||
required: false,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid gap-1 font-semibold text-muted-foreground'>
|
<div className='grid gap-1 font-semibold text-muted-foreground'>
|
||||||
|
|||||||
@ -31,7 +31,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
defaultValues: Readonly<{ [x: string]: any }> | undefined;
|
defaultValues: Readonly<{ [x: string]: any }> | undefined;
|
||||||
}) => {
|
}) => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { control, register } = useFormContext();
|
const { control } = useFormContext();
|
||||||
|
|
||||||
const [pickerMode] = useState<"dialog" | "panel">("dialog");
|
const [pickerMode] = useState<"dialog" | "panel">("dialog");
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
<FormTextAreaField
|
<FormTextAreaField
|
||||||
readOnly={original?.id_article}
|
readOnly={original?.id_article}
|
||||||
autoSize
|
autoSize
|
||||||
{...register(`items.${index}.id_article`)}
|
name={`items.${index}.id_article`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -80,7 +80,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
<FormTextAreaField
|
<FormTextAreaField
|
||||||
readOnly={original?.id_article}
|
readOnly={original?.id_article}
|
||||||
autoSize
|
autoSize
|
||||||
{...register(`items.${index}.description`)}
|
name={`items.${index}.description`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -98,7 +98,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
<FormQuantityField
|
<FormQuantityField
|
||||||
scale={0}
|
scale={0}
|
||||||
className='text-right'
|
className='text-right'
|
||||||
{...register(`items.${index}.quantity`)}
|
name={`items.${index}.quantity`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -117,7 +117,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
language={language}
|
language={language}
|
||||||
scale={2}
|
scale={2}
|
||||||
className='text-right'
|
className='text-right'
|
||||||
{...register(`items.${index}.unit_price`)}
|
name={`items.${index}.unit_price`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -138,7 +138,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
scale={2}
|
scale={2}
|
||||||
readOnly
|
readOnly
|
||||||
className='text-right'
|
className='text-right'
|
||||||
{...register(`items.${index}.subtotal_price`)}
|
name={`items.${index}.subtotal_price`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -155,7 +155,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
<FormPercentageField
|
<FormPercentageField
|
||||||
scale={2}
|
scale={2}
|
||||||
className='text-right'
|
className='text-right'
|
||||||
{...register(`items.${index}.discount`)}
|
name={`items.${index}.discount`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -176,7 +176,7 @@ export const QuoteDetailsCardEditor = ({
|
|||||||
scale={2}
|
scale={2}
|
||||||
readOnly
|
readOnly
|
||||||
className='font-semibold text-right'
|
className='font-semibold text-right'
|
||||||
{...register(`items.${index}.total_price`)}
|
name={`items.${index}.total_price`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { t } from "i18next";
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
export const QuoteGeneralCardEditor = () => {
|
export const QuoteGeneralCardEditor = () => {
|
||||||
const { register, formState } = useFormContext();
|
const { formState } = useFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='grid gap-6 md:grid-cols-6'>
|
<div className='grid gap-6 md:grid-cols-6'>
|
||||||
@ -21,7 +21,7 @@ export const QuoteGeneralCardEditor = () => {
|
|||||||
description={t("quotes.form_fields.customer_reference.desc")}
|
description={t("quotes.form_fields.customer_reference.desc")}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.customer_reference.placeholder")}
|
placeholder={t("quotes.form_fields.customer_reference.placeholder")}
|
||||||
{...register("customer_reference")}
|
name='customer_reference'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormDatePickerField
|
<FormDatePickerField
|
||||||
@ -30,18 +30,14 @@ export const QuoteGeneralCardEditor = () => {
|
|||||||
description={t("quotes.form_fields.date.desc")}
|
description={t("quotes.form_fields.date.desc")}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.date.placeholder")}
|
placeholder={t("quotes.form_fields.date.placeholder")}
|
||||||
{...register("date", {
|
name='date'
|
||||||
required: true,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
<FormTextField
|
<FormTextField
|
||||||
label={t("quotes.form_fields.validity.label")}
|
label={t("quotes.form_fields.validity.label")}
|
||||||
description={t("quotes.form_fields.validity.desc")}
|
description={t("quotes.form_fields.validity.desc")}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.validity.placeholder")}
|
placeholder={t("quotes.form_fields.validity.placeholder")}
|
||||||
{...register("validity", {
|
name='validity'
|
||||||
required: false,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
<FormTextAreaField
|
<FormTextAreaField
|
||||||
className='col-span-2'
|
className='col-span-2'
|
||||||
@ -49,9 +45,7 @@ export const QuoteGeneralCardEditor = () => {
|
|||||||
description={t("quotes.form_fields.payment_method.desc")}
|
description={t("quotes.form_fields.payment_method.desc")}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.payment_method.placeholder")}
|
placeholder={t("quotes.form_fields.payment_method.placeholder")}
|
||||||
{...register("payment_method", {
|
name='payment_method'
|
||||||
required: false,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-cols-6 gap-6'>
|
<div className='grid grid-cols-6 gap-6'>
|
||||||
@ -62,9 +56,7 @@ export const QuoteGeneralCardEditor = () => {
|
|||||||
description={t("quotes.form_fields.customer_information.desc")}
|
description={t("quotes.form_fields.customer_information.desc")}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.customer_information.placeholder")}
|
placeholder={t("quotes.form_fields.customer_information.placeholder")}
|
||||||
{...register("customer_information", {
|
name='customer_information'
|
||||||
required: true,
|
|
||||||
})}
|
|
||||||
errors={formState.errors}
|
errors={formState.errors}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -74,9 +66,7 @@ export const QuoteGeneralCardEditor = () => {
|
|||||||
description={t("quotes.form_fields.notes.desc")}
|
description={t("quotes.form_fields.notes.desc")}
|
||||||
disabled={formState.disabled}
|
disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.notes.placeholder")}
|
placeholder={t("quotes.form_fields.notes.placeholder")}
|
||||||
{...register("notes", {
|
name='notes'
|
||||||
required: false,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
@ -105,9 +95,7 @@ export const QuoteGeneralCardEditor = () => {
|
|||||||
description={t("quotes.form_fields.lang_code.desc")}
|
description={t("quotes.form_fields.lang_code.desc")}
|
||||||
//disabled={formState.disabled}
|
//disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.lang_code.placeholder")}
|
placeholder={t("quotes.form_fields.lang_code.placeholder")}
|
||||||
{...register("lang_code", {
|
name='lang_code'
|
||||||
required: true,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
<FormTextField
|
<FormTextField
|
||||||
required
|
required
|
||||||
@ -115,9 +103,7 @@ export const QuoteGeneralCardEditor = () => {
|
|||||||
description={t("quotes.form_fields.currency_code.desc")}
|
description={t("quotes.form_fields.currency_code.desc")}
|
||||||
//disabled={formState.disabled}
|
//disabled={formState.disabled}
|
||||||
placeholder={t("quotes.form_fields.currency_code.placeholder")}
|
placeholder={t("quotes.form_fields.currency_code.placeholder")}
|
||||||
{...register("currency_code", {
|
name='currency_code'
|
||||||
required: true,
|
|
||||||
})}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|||||||
@ -43,8 +43,7 @@ export type FormCurrencyFieldProps<
|
|||||||
scale: number;
|
scale: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrencyFieldProps>(
|
export const FormCurrencyField = (props: FormCurrencyFieldProps) => {
|
||||||
(props /*ref*/) => {
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
@ -156,7 +155,6 @@ export const FormCurrencyField = React.forwardRef<HTMLInputElement, FormCurrency
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|
||||||
FormCurrencyField.displayName = "FormCurrencyField";
|
FormCurrencyField.displayName = "FormCurrencyField";
|
||||||
|
|||||||
@ -42,10 +42,7 @@ export type FormPercentageFieldProps<
|
|||||||
scale: number;
|
scale: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormPercentageField = React.forwardRef<
|
export const FormPercentageField = (props: FormPercentageFieldProps) => {
|
||||||
HTMLDivElement,
|
|
||||||
React.HTMLAttributes<HTMLDivElement> & FormPercentageFieldProps
|
|
||||||
>((props /*ref*/) => {
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
@ -144,4 +141,4 @@ export const FormPercentageField = React.forwardRef<
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
};
|
||||||
|
|||||||
@ -41,8 +41,7 @@ export type FormQuantityFieldProps<
|
|||||||
scale: number;
|
scale: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FormQuantityField = React.forwardRef<HTMLInputElement, FormQuantityFieldProps>(
|
export const FormQuantityField = (props: FormQuantityFieldProps) => {
|
||||||
(props /*ref*/) => {
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
@ -140,5 +139,4 @@ export const FormQuantityField = React.forwardRef<HTMLInputElement, FormQuantity
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|||||||
@ -46,11 +46,7 @@ export type FormTextAreaFieldProps<
|
|||||||
errors?: FieldErrors<TFieldValues>;
|
errors?: FieldErrors<TFieldValues>;
|
||||||
} & VariantProps<typeof formTextAreaFieldVariants>;
|
} & VariantProps<typeof formTextAreaFieldVariants>;
|
||||||
|
|
||||||
export const FormTextAreaField = React.forwardRef<
|
export const FormTextAreaField = ({
|
||||||
HTMLDivElement,
|
|
||||||
React.TextareaHTMLAttributes<HTMLTextAreaElement> & FormTextAreaFieldProps
|
|
||||||
>(
|
|
||||||
({
|
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
hint,
|
hint,
|
||||||
@ -64,11 +60,9 @@ export const FormTextAreaField = React.forwardRef<
|
|||||||
variant,
|
variant,
|
||||||
|
|
||||||
...props
|
...props
|
||||||
}) =>
|
}: React.TextareaHTMLAttributes<HTMLTextAreaElement> & FormTextAreaFieldProps) => {
|
||||||
/*ref*/
|
const { control } = useFormContext();
|
||||||
{
|
return (
|
||||||
const { control } = useFormContext();
|
|
||||||
return (
|
|
||||||
<FormField
|
<FormField
|
||||||
control={control}
|
control={control}
|
||||||
name={name}
|
name={name}
|
||||||
@ -108,6 +102,5 @@ export const FormTextAreaField = React.forwardRef<
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|||||||
@ -33,8 +33,7 @@ export type FormTextFieldProps<
|
|||||||
UseControllerProps<TFieldValues, TName> &
|
UseControllerProps<TFieldValues, TName> &
|
||||||
VariantProps<typeof formTextFieldVariants>;
|
VariantProps<typeof formTextFieldVariants>;
|
||||||
|
|
||||||
export const FormTextField = React.forwardRef<HTMLInputElement, FormTextFieldProps>(
|
export const FormTextField = (props: FormTextFieldProps) => {
|
||||||
(props /*ref*/) => {
|
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
@ -87,5 +86,4 @@ export const FormTextField = React.forwardRef<HTMLInputElement, FormTextFieldPro
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { act, useEffect, useRef } from "react";
|
import { act, useEffect, useRef } from "react";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import { FormProvider, UseFormReturn, useForm } from "react-hook-form";
|
import { FormProvider, UseFormReturn, useForm } from "react-hook-form";
|
||||||
|
import { FormTextField } from "@/components/Forms/FormTextField";
|
||||||
import { FormField } from "./form";
|
import { FormField } from "./form";
|
||||||
|
|
||||||
type TestFormValues = {
|
type TestFormValues = {
|
||||||
items: Array<{ quantity: string }>;
|
items: Array<{ quantity: string }>;
|
||||||
|
title: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -41,6 +43,7 @@ const FormHarness = ({ name, onMount, onFormReady }: FormHarnessProps) => {
|
|||||||
const form = useForm<TestFormValues>({
|
const form = useForm<TestFormValues>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
items: [{ quantity: "1" }, { quantity: "2" }],
|
items: [{ quantity: "1" }, { quantity: "2" }],
|
||||||
|
title: "before",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,6 +58,25 @@ const FormHarness = ({ name, onMount, onFormReady }: FormHarnessProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ControlledTextFieldHarness = ({ onFormReady }: Pick<FormHarnessProps, "onFormReady">) => {
|
||||||
|
const form = useForm<TestFormValues>({
|
||||||
|
defaultValues: {
|
||||||
|
items: [],
|
||||||
|
title: "before",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onFormReady(form);
|
||||||
|
}, [form, onFormReady]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormProvider {...form}>
|
||||||
|
<FormTextField name='title' />
|
||||||
|
</FormProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
describe("FormField", () => {
|
describe("FormField", () => {
|
||||||
let container: HTMLDivElement;
|
let container: HTMLDivElement;
|
||||||
let root: ReturnType<typeof createRoot>;
|
let root: ReturnType<typeof createRoot>;
|
||||||
@ -98,4 +120,23 @@ describe("FormField", () => {
|
|||||||
expect((container.querySelector("input[aria-label='quantity']") as HTMLInputElement).value).toBe("5");
|
expect((container.querySelector("input[aria-label='quantity']") as HTMLInputElement).value).toBe("5");
|
||||||
expect(form?.getValues("items.1.quantity")).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<TestFormValues> | undefined;
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
root.render(<ControlledTextFieldHarness onFormReady={(value) => (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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user