Skip to content

Commit

Permalink
feat(conform-react,conform-zod,conform-yup)!: multiple errors by defa…
Browse files Browse the repository at this point in the history
…ult (#228)
  • Loading branch information
edmundhung authored Jul 18, 2023
1 parent a1e0bd5 commit 217af69
Show file tree
Hide file tree
Showing 9 changed files with 55 additions and 179 deletions.
17 changes: 1 addition & 16 deletions packages/conform-react/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1052,15 +1052,6 @@ export function validateConstraint(options: {
context: { formData: FormData; attributeValue: string },
) => boolean
>;
acceptMultipleErrors?: ({
name,
intent,
payload,
}: {
name: string;
intent: string;
payload: Record<string, any>;
}) => boolean;
formatMessages?: ({
name,
validity,
Expand Down Expand Up @@ -1138,15 +1129,9 @@ export function validateConstraint(options: {
constraint,
defaultErrors: getDefaultErrors(element.validity, constraint),
});
const shouldAcceptMultipleErrors =
options?.acceptMultipleErrors?.({
name,
payload,
intent,
}) ?? false;

if (errors.length > 0) {
error[name] = shouldAcceptMultipleErrors ? errors : errors[0];
error[name] = errors;
}
}
}
Expand Down
31 changes: 1 addition & 30 deletions packages/conform-yup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,47 +91,20 @@ export function parse<Schema extends yup.AnyObjectSchema>(
payload: FormData | URLSearchParams,
config: {
schema: Schema | ((intent: string) => Schema);
acceptMultipleErrors?: ({
name,
intent,
payload,
}: {
name: string;
intent: string;
payload: Record<string, any>;
}) => boolean;
async?: false;
},
): Submission<yup.InferType<Schema>>;
export function parse<Schema extends yup.AnyObjectSchema>(
payload: FormData | URLSearchParams,
config: {
schema: Schema | ((intent: string) => Schema);
acceptMultipleErrors?: ({
name,
intent,
payload,
}: {
name: string;
intent: string;
payload: Record<string, any>;
}) => boolean;
async: true;
},
): Promise<Submission<yup.InferType<Schema>>>;
export function parse<Schema extends yup.AnyObjectSchema>(
payload: FormData | URLSearchParams,
config: {
schema: Schema | ((intent: string) => Schema);
acceptMultipleErrors?: ({
name,
intent,
payload,
}: {
name: string;
intent: string;
payload: Record<string, any>;
}) => boolean;
async?: boolean;
},
):
Expand All @@ -153,9 +126,7 @@ export function parse<Schema extends yup.AnyObjectSchema>(

if (typeof result[name] === 'undefined') {
result[name] = e.message;
} else if (
config.acceptMultipleErrors?.({ name, intent, payload })
) {
} else {
result[name] = ([] as string[]).concat(
result[name],
e.message,
Expand Down
31 changes: 1 addition & 30 deletions packages/conform-zod/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,15 +174,6 @@ export function parse<Schema extends z.ZodTypeAny>(
payload: FormData | URLSearchParams,
config: {
schema: Schema | ((intent: string) => Schema);
acceptMultipleErrors?: ({
name,
intent,
payload,
}: {
name: string;
intent: string;
payload: Record<string, any>;
}) => boolean;
async?: false;
errorMap?: z.ZodErrorMap;
stripEmptyValue?: boolean;
Expand All @@ -192,15 +183,6 @@ export function parse<Schema extends z.ZodTypeAny>(
payload: FormData | URLSearchParams,
config: {
schema: Schema | ((intent: string) => Schema);
acceptMultipleErrors?: ({
name,
intent,
payload,
}: {
name: string;
intent: string;
payload: Record<string, any>;
}) => boolean;
async: true;
errorMap?: z.ZodErrorMap;
stripEmptyValue?: boolean;
Expand All @@ -210,15 +192,6 @@ export function parse<Schema extends z.ZodTypeAny>(
payload: FormData | URLSearchParams,
config: {
schema: Schema | ((intent: string) => Schema);
acceptMultipleErrors?: ({
name,
intent,
payload,
}: {
name: string;
intent: string;
payload: Record<string, any>;
}) => boolean;
async?: boolean;
errorMap?: z.ZodErrorMap;
stripEmptyValue?: boolean;
Expand Down Expand Up @@ -249,9 +222,7 @@ export function parse<Schema extends z.ZodTypeAny>(

if (typeof result[name] === 'undefined') {
result[name] = e.message;
} else if (
config.acceptMultipleErrors?.({ name, intent, payload })
) {
} else {
result[name] = ([] as string[]).concat(result[name], e.message);
}

Expand Down
19 changes: 14 additions & 5 deletions playground/app/routes/async-validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,23 @@ function createSchema(
return z.object({
email: z
.string({ required_error: 'Email is required' })
.email('Email is invalid')
.superRefine((email, ctx) =>
refine(ctx, {
.superRefine((email, ctx) => {
const result = z.string().email().safeParse(email);

if (!result.success) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: 'Email is invalid',
});
return;
}

return refine(ctx, {
validate: () => constraints.isEmailUnique?.(email),
when: intent === 'validate/email' || intent === 'submit',
message: 'Email is already used',
}),
),
});
}),
title: z
.string({ required_error: 'Title is required' })
.max(20, 'Title is too long'),
Expand Down
6 changes: 0 additions & 6 deletions playground/app/routes/multiple-errors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ function parseForm(formData: FormData, validator: string | null) {
(username) => !username || username.match(/[0-9]/) !== null,
),
}),
acceptMultipleErrors({ name }) {
return name === 'username';
},
});
}
case 'zod': {
Expand All @@ -61,9 +58,6 @@ function parseForm(formData: FormData, validator: string | null) {
)
.refine((username) => username.match(/[0-9]/), 'At least 1 number'),
}),
acceptMultipleErrors({ name }) {
return name === 'username';
},
});
}
default: {
Expand Down
8 changes: 1 addition & 7 deletions playground/app/routes/validate-constraint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ export async function loader({ request }: LoaderArgs) {
const url = new URL(request.url);

return {
multiplePasswordError:
url.searchParams.get('multiplePasswordError') === 'yes',
enableCustomMessage: url.searchParams.get('enableCustomMessage') === 'yes',
};
}
Expand All @@ -28,8 +26,7 @@ export default function Example() {
const [lastSubmission, setLastSubmission] = useState<
Submission | undefined
>();
const { multiplePasswordError, enableCustomMessage } =
useLoaderData<typeof loader>();
const { enableCustomMessage } = useLoaderData<typeof loader>();
const [form, { email, password, confirmPassword }] = useForm<Schema>({
fallbackNative: true,
shouldRevalidate: 'onInput',
Expand All @@ -41,9 +38,6 @@ export default function Example() {
return value === formData.get(attributeValue);
},
},
acceptMultipleErrors({ name }) {
return multiplePasswordError && name === 'password';
},
formatMessages({ name, defaultErrors }) {
if (!enableCustomMessage) {
return defaultErrors;
Expand Down
28 changes: 2 additions & 26 deletions tests/conform-yup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ test.describe('conform-yup', () => {
list: [{ key: '' }],
};
const error = {
text: 'min',
tag: 'required',
text: ['min', 'regex'],
tag: ['required', 'invalid'],
number: 'max',
timestamp: 'min',
'options[1]': 'invalid',
Expand Down Expand Up @@ -116,29 +116,5 @@ test.describe('conform-yup', () => {
payload,
error,
});
expect(
parse(formData, { schema, acceptMultipleErrors: () => true }),
).toEqual({
intent: 'submit',
payload,
error: {
...error,
text: ['min', 'regex'],
tag: ['required', 'invalid'],
},
});
expect(
parse(formData, {
schema,
acceptMultipleErrors: ({ name }) => name === 'tag',
}),
).toEqual({
intent: 'submit',
payload,
error: {
...error,
tag: ['required', 'invalid'],
},
});
});
});
54 changes: 2 additions & 52 deletions tests/conform-zod.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ test.describe('conform-zod', () => {
list: [{ key: undefined }],
};
const error = {
text: 'min',
text: ['min', 'regex', 'refine'],
number: 'step',
timestamp: 'min',
options: 'min',
Expand All @@ -230,31 +230,6 @@ test.describe('conform-zod', () => {
payload,
error,
});
expect(
parse(formData, {
schema,
stripEmptyValue: true,
acceptMultipleErrors: () => false,
}),
).toEqual({
intent: 'submit',
payload,
error,
});
expect(
parse(formData, {
schema,
stripEmptyValue: true,
acceptMultipleErrors: () => true,
}),
).toEqual({
intent: 'submit',
payload,
error: {
...error,
text: ['min', 'regex', 'refine'],
},
});
});

test('parse without empty value stripped', () => {
Expand Down Expand Up @@ -282,7 +257,7 @@ test.describe('conform-zod', () => {
list: [{ key: '' }],
};
const error = {
text: 'min',
text: ['min', 'regex', 'refine'],
number: 'step',
timestamp: 'min',
options: 'min',
Expand All @@ -301,31 +276,6 @@ test.describe('conform-zod', () => {
payload,
error,
});
expect(
parse(formData, {
schema,
stripEmptyValue: false,
acceptMultipleErrors: () => false,
}),
).toEqual({
intent: 'submit',
payload,
error,
});
expect(
parse(formData, {
schema,
stripEmptyValue: false,
acceptMultipleErrors: () => true,
}),
).toEqual({
intent: 'submit',
payload,
error: {
...error,
text: ['min', 'regex', 'refine'],
},
});
});

test('parse with errorMap', () => {
Expand Down
Loading

0 comments on commit 217af69

Please sign in to comment.