From f9699a6e3b1fd071edfc0e935adfec721750c6de Mon Sep 17 00:00:00 2001 From: Daniel Junior Date: Tue, 16 Jan 2024 17:47:44 -0300 Subject: [PATCH] feat(form): email input type --- .changeset/stale-poems-yawn.md | 5 ++ .../src/components/react/Signup.tsx | 5 ++ packages/form/src/module.ts | 57 +++++++++++++++---- 3 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 .changeset/stale-poems-yawn.md diff --git a/.changeset/stale-poems-yawn.md b/.changeset/stale-poems-yawn.md new file mode 100644 index 0000000..963704f --- /dev/null +++ b/.changeset/stale-poems-yawn.md @@ -0,0 +1,5 @@ +--- +"simple-stack-form": patch +--- + +Set input type as email for inputs validating email values diff --git a/examples/playground/src/components/react/Signup.tsx b/examples/playground/src/components/react/Signup.tsx index 96f117d..65e3d44 100644 --- a/examples/playground/src/components/react/Signup.tsx +++ b/examples/playground/src/components/react/Signup.tsx @@ -13,6 +13,7 @@ export const signup = createForm({ await sleep(400); return s !== "admin"; }), + email: z.string().email().optional().or(z.literal("")), optIn: z.boolean().optional(), }); @@ -30,6 +31,10 @@ export default function Signup({ + + + + diff --git a/packages/form/src/module.ts b/packages/form/src/module.ts index 8547f5e..109443b 100644 --- a/packages/form/src/module.ts +++ b/packages/form/src/module.ts @@ -8,8 +8,11 @@ import { ZodObject, ZodOptional, type ZodRawShape, + ZodString, type ZodType, z, + ZodUnion, + ZodLiteral, } from "zod"; export type FormValidator = ZodRawShape; @@ -20,7 +23,7 @@ export type FieldErrors< export type InputProp = { "aria-required": boolean; name: string; - type: "text" | "number" | "checkbox"; + type: "text" | "number" | "checkbox" | "email"; }; export const formNameInputProps = { @@ -213,6 +216,48 @@ function getInputProp( return inputProp; } +function getInputType(fieldValidator: T): InputProp["type"] { + if (fieldValidator instanceof ZodBoolean) { + return "checkbox"; + } + + if (fieldValidator instanceof ZodNumber) { + return "number"; + } + + if ( + fieldValidator instanceof ZodString && + fieldValidator._def.checks.some((check) => check.kind === "email") + ) { + return "email"; + } + + if (fieldValidator instanceof ZodOptional) { + return getInputType(fieldValidator._def.innerType); + } + + if (fieldValidator instanceof ZodUnion) { + const types: InputProp["type"][] = + fieldValidator._def.options.map(getInputType); + if (!types[0]) { + return "text"; + } + if (types.every((type) => type === types[0])) { + return types[0]; + } + if ( + types.length === 2 && + fieldValidator._def.options[1] instanceof ZodLiteral && + !fieldValidator._def.options[1].value + ) { + // Handles specific case where email is optional. E.g.: `z.string().email().optional().or(z.literal(""))` + return types[0]; + } + } + + return "text"; +} + function getInputInfo(fieldValidator: T): { type: InputProp["type"]; isArray: boolean; @@ -236,15 +281,7 @@ function getInputInfo(fieldValidator: T): { // TODO: respect preprocess() wrappers - let type: InputProp["type"]; - - if (resolvedType instanceof ZodBoolean) { - type = "checkbox"; - } else if (resolvedType instanceof ZodNumber) { - type = "number"; - } else { - type = "text"; - } + const type = getInputType(resolvedType); return { type, isArray, isOptional }; }