fix: add hack for root zod validation with hook form

This commit is contained in:
Mythie
2023-09-28 15:32:03 +10:00
parent a219588287
commit a622fd402f
3 changed files with 29 additions and 16 deletions

View File

@ -126,12 +126,12 @@ export const AddSignersFormPartial = ({
<AnimatePresence> <AnimatePresence>
{signers.map((signer, index) => ( {signers.map((signer, index) => (
<motion.div <motion.div
key={signer.formId} key={signer.id}
data-native-id={signer.nativeId} data-native-id={signer.nativeId}
className="flex flex-wrap items-end gap-x-4" className="flex flex-wrap items-end gap-x-4"
> >
<div className="flex-1"> <div className="flex-1">
<Label htmlFor={`signer-${signer.formId}-email`}> <Label htmlFor={`signer-${signer.id}-email`}>
Email Email
<span className="text-destructive ml-1 inline-block font-medium">*</span> <span className="text-destructive ml-1 inline-block font-medium">*</span>
</Label> </Label>
@ -141,7 +141,7 @@ export const AddSignersFormPartial = ({
name={`signers.${index}.email`} name={`signers.${index}.email`}
render={({ field }) => ( render={({ field }) => (
<Input <Input
id={`signer-${signer.formId}-email`} id={`signer-${signer.id}-email`}
type="email" type="email"
className="bg-background mt-2" className="bg-background mt-2"
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)} disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
@ -153,14 +153,14 @@ export const AddSignersFormPartial = ({
</div> </div>
<div className="flex-1"> <div className="flex-1">
<Label htmlFor={`signer-${signer.formId}-name`}>Name</Label> <Label htmlFor={`signer-${signer.id}-name`}>Name</Label>
<Controller <Controller
control={control} control={control}
name={`signers.${index}.name`} name={`signers.${index}.name`}
render={({ field }) => ( render={({ field }) => (
<Input <Input
id={`signer-${signer.formId}-name`} id={`signer-${signer.id}-name`}
type="text" type="text"
className="bg-background mt-2" className="bg-background mt-2"
disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)} disabled={isSubmitting || hasBeenSentToRecipientId(signer.nativeId)}
@ -195,7 +195,11 @@ export const AddSignersFormPartial = ({
</AnimatePresence> </AnimatePresence>
</div> </div>
<FormErrorMessage className="mt-2" error={errors.signers} /> <FormErrorMessage
className="mt-2"
// Dirty hack to handle errors when .root is populated for an array type
error={'signers__root' in errors && errors['signers__root']}
/>
<div className="mt-4"> <div className="mt-4">
<Button type="button" disabled={isSubmitting} onClick={() => onAddSigner()}> <Button type="button" disabled={isSubmitting} onClick={() => onAddSigner()}>

View File

@ -1,19 +1,24 @@
import { z } from 'zod'; import { z } from 'zod';
export const ZAddSignersFormSchema = z.object({ export const ZAddSignersFormSchema = z
signers: z .object({
.array( signers: z.array(
z.object({ z.object({
formId: z.string().min(1), formId: z.string().min(1),
nativeId: z.number().optional(), nativeId: z.number().optional(),
email: z.string().min(1).email(), email: z.string().min(1).email(),
name: z.string(), name: z.string(),
}), }),
) ),
.refine((signers) => { })
const emails = signers.map((signer) => signer.email); .refine(
(schema) => {
const emails = schema.signers.map((signer) => signer.email.toLowerCase());
return new Set(emails).size === emails.length; return new Set(emails).size === emails.length;
}, 'Signers must have unique emails'), },
}); // Dirty hack to handle errors when .root is populated for an array type
{ message: 'Signers must have unique emails', path: ['signers__root'] },
);
export type TAddSignersFormSchema = z.infer<typeof ZAddSignersFormSchema>; export type TAddSignersFormSchema = z.infer<typeof ZAddSignersFormSchema>;

View File

@ -4,13 +4,17 @@ import { cn } from '@documenso/ui/lib/utils';
export type FormErrorMessageProps = { export type FormErrorMessageProps = {
className?: string; className?: string;
error: { message?: string } | undefined; error: { message?: string } | undefined | unknown;
};
const isErrorWithMessage = (error: unknown): error is { message?: string } => {
return typeof error === 'object' && error !== null && 'message' in error;
}; };
export const FormErrorMessage = ({ error, className }: FormErrorMessageProps) => { export const FormErrorMessage = ({ error, className }: FormErrorMessageProps) => {
return ( return (
<AnimatePresence> <AnimatePresence>
{error && ( {isErrorWithMessage(error) && (
<motion.p <motion.p
initial={{ initial={{
opacity: 0, opacity: 0,