diff --git a/src/create_form.ts b/src/create_form.ts index ee47477..b8c21b3 100644 --- a/src/create_form.ts +++ b/src/create_form.ts @@ -1,4 +1,4 @@ -import { createMemo, createSignal } from "solid-js"; +import { createMemo, createSignal, onCleanup } from "solid-js"; import { createDirtyFields } from "./logic/create_dirty_fields"; import { createErrors } from "./logic/create_errors"; import { createFields } from "./logic/create_fields"; @@ -33,8 +33,8 @@ export const createForm: CreateForm = ( ): CreateFormReturn => { const { defaultValues, mode = "onChange", shouldFocusError = true, resolver } = arg; - const { fields, getField, setField, focusField } = createFields(); - const { rules, addRule, getRule } = createRules(); + const { fields, getField, setField, focusField, removeField } = createFields(); + const { rules, addRule, getRule, removeRule } = createRules(); const [values, setValues] = createSignal(structuredClone(defaultValues)); const { errors, appendError, clearError, resetErrors, clearErrors, getError } = createErrors(); const { touchedFields, addTouched, resetTouched } = createTouchedFields(); @@ -128,7 +128,7 @@ export const createForm: CreateForm = ( const onFieldChange = (event: Event | unknown, name: Path) => { const fieldValue = getFieldValue(event); - const value = formatValue(fieldValue, rules[name]); + const value = formatValue(fieldValue, getRule(name)); setValues((prev) => { const newState = { ...prev }; @@ -144,6 +144,12 @@ export const createForm: CreateForm = ( const register: Register = (name, options = {}) => { addRule(name, options); + onCleanup(() => { + removeRule(name); + removeField(name); + clearError(name); + }); + return { name, onInput(event) { diff --git a/src/logic/create_fields.ts b/src/logic/create_fields.ts index 0a23a64..84f0567 100644 --- a/src/logic/create_fields.ts +++ b/src/logic/create_fields.ts @@ -16,10 +16,15 @@ export const createFields = () => { getField(name)?.focus(); }; + const removeField = (name: string) => { + delete fields[name]; + }; + return { fields, getField, setField, - focusField + focusField, + removeField }; }; diff --git a/src/logic/create_rules.ts b/src/logic/create_rules.ts index f21f48e..8126274 100644 --- a/src/logic/create_rules.ts +++ b/src/logic/create_rules.ts @@ -22,5 +22,9 @@ export const createRules = () => { return rules[name]; }; - return { rules, addRule, getRule }; + const removeRule = (name: string) => { + delete rules[name]; + }; + + return { rules, addRule, getRule, removeRule }; }; diff --git a/src/logic/format_value.ts b/src/logic/format_value.ts index f4e0226..9f914ff 100644 --- a/src/logic/format_value.ts +++ b/src/logic/format_value.ts @@ -2,8 +2,8 @@ import type { Path } from "react-hook-form"; import type { FormValues } from "../types/form"; import type { Rules } from "../types/validate"; -export const formatValue = (value: T, rules: Rules>) => { - if (rules.valueAsNumber) { +export const formatValue = (value: T, rules?: Rules>) => { + if (rules?.valueAsNumber) { return Number(value); } diff --git a/tests/src/register/register.test.tsx b/tests/src/register/register.test.tsx index ec0291d..ac19cd4 100644 --- a/tests/src/register/register.test.tsx +++ b/tests/src/register/register.test.tsx @@ -31,11 +31,9 @@ describe("register", () => { }); it("should handle conditional render", async () => { - const emailBadResult = "email"; const pattern = /^[^@]+@[^@]+.[^@]+$/; const emailOkResult = "email@example.com"; const emailErrorMessage = "Invalid email"; - const passwordBadResult = "pass"; const minLength = 5; const passwordOkResult = "password123"; const passwordErrorMessage = "Min length is 5"; @@ -49,7 +47,7 @@ describe("register", () => { email: "", password: "" }} - render={({ errors, register }) => ( + render={({ errors, formState, register, trigger }) => ( <> { })} error={errors.email} /> + { })} error={errors.password} /> + - - )} onSubmit={onSubmit} @@ -81,51 +88,34 @@ describe("register", () => { }); const emailInput = page.getByRole("textbox", { name: "email" }); - const step1Button = page.getByRole("button", { name: "Step 1" }); - const step2Button = page.getByRole("button", { name: "Step 2" }); + const nextButton = page.getByRole("button", { name: "Next" }); const submitButton = page.getByRole("button", { name: "Submit" }); expect(emailInput).toHaveValue(""); - await emailInput.fill(emailBadResult); + await submitButton.click(); + expect(onSubmit).not.toHaveBeenCalled(); + expect(emailInput).toHaveFocus(); expect(emailInput).toHaveAttribute("aria-invalid", "true"); expect(emailInput).toHaveAccessibleErrorMessage(emailErrorMessage); - await step2Button.click(); + await emailInput.fill(emailOkResult); + await nextButton.click(); const passwordInput = page.getByRole("textbox", { name: "password" }); + const backButton = page.getByRole("button", { name: "Back" }); expect(passwordInput).toHaveValue(""); - await passwordInput.fill(passwordBadResult); - expect(passwordInput).toHaveAttribute("aria-invalid", "true"); - expect(passwordInput).toHaveAccessibleErrorMessage(passwordErrorMessage); - - await step1Button.click(); - expect(emailInput).not.toHaveFocus(); - expect(emailInput).toHaveValue(emailBadResult); - expect(emailInput).toHaveAttribute("aria-invalid", "true"); - expect(emailInput).toHaveAccessibleErrorMessage(emailErrorMessage); - await submitButton.click(); expect(onSubmit).not.toHaveBeenCalled(); - expect(emailInput).toHaveFocus(); - - await emailInput.fill(emailOkResult); - expect(emailInput).toHaveAttribute("aria-invalid", "false"); - expect(emailInput).not.toHaveAccessibleErrorMessage(); - - await step2Button.click(); - expect(passwordInput).not.toHaveFocus(); - expect(passwordInput).toHaveValue(passwordBadResult); + expect(passwordInput).toHaveFocus(); expect(passwordInput).toHaveAttribute("aria-invalid", "true"); expect(passwordInput).toHaveAccessibleErrorMessage(passwordErrorMessage); - - await submitButton.click(); - expect(onSubmit).not.toHaveBeenCalled(); - expect(passwordInput).toHaveFocus(); - await passwordInput.fill(passwordOkResult); - expect(passwordInput).toHaveAttribute("aria-invalid", "false"); - expect(passwordInput).not.toHaveAccessibleErrorMessage(); + await backButton.click(); + + expect(emailInput).toHaveValue(emailOkResult); + await nextButton.click(); + expect(passwordInput).toHaveValue(passwordOkResult); await submitButton.click(); expect(onSubmit).toHaveBeenCalledWith({