Skip to content

Commit

Permalink
fix: properly aggregrate nested errors for yup
Browse files Browse the repository at this point in the history
  • Loading branch information
logaretm committed Mar 11, 2023
1 parent c2e02b7 commit 7f90bbc
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 5 deletions.
1 change: 0 additions & 1 deletion packages/vee-validate/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ export interface FormContext<
| 'fieldsByPath'
| 'schema'
| 'validateSchema'
| 'errorBag'
| 'setFieldErrorBag'
| 'stageInitialValue'
| 'setFieldInitialValue'
Expand Down
17 changes: 16 additions & 1 deletion packages/vee-validate/src/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
FormValidationResult,
RawFormSchema,
YupSchema,
TypedSchemaError,
} from './types';
import { isCallable, FieldValidationMetaInfo } from '../../shared';

Expand Down Expand Up @@ -158,7 +159,21 @@ function yupToTypedSchema(yupSchema: YupSchema): TypedSchema {
throw err;
}

return { errors: err.inner || [] };
const errors: Record<string, TypedSchemaError> = err.inner.reduce((acc: any, curr: any) => {
if (!curr.path) {
return acc;
}

if (!acc[curr.path]) {
acc[curr.path] = { errors: [], path: curr.path };
}

acc[curr.path].errors.push(...curr.errors);

return acc;
}, {} as Record<string, TypedSchemaError>);

return { errors: Object.values(errors) };
}
},
};
Expand Down
21 changes: 18 additions & 3 deletions packages/yup/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { InferType, Schema, ValidateOptions, ValidationError } from 'yup';
import { TypedSchema } from 'vee-validate';
import { TypedSchema, TypedSchemaError } from 'vee-validate';
import { PartialDeep } from 'type-fest';

export function toTypedSchema<TSchema extends Schema, TOutput = InferType<TSchema>, TInput = PartialDeep<TOutput>>(
Expand All @@ -10,7 +10,8 @@ export function toTypedSchema<TSchema extends Schema, TOutput = InferType<TSchem
__type: 'VVTypedSchema',
async validate(values) {
try {
const output = await yupSchema.validate(values, opts);
// we spread the options because yup mutates the opts object passed
const output = await yupSchema.validate(values, { ...opts });

return {
value: output,
Expand All @@ -28,8 +29,22 @@ export function toTypedSchema<TSchema extends Schema, TOutput = InferType<TSchem
return { errors: [{ path: error.path, errors: error.errors }] };
}

const errors: Record<string, TypedSchemaError> = error.inner.reduce((acc, curr) => {
if (!curr.path) {
return acc;
}

if (!acc[curr.path]) {
acc[curr.path] = { errors: [], path: curr.path };
}

acc[curr.path].errors.push(...curr.errors);

return acc;
}, {} as Record<string, TypedSchemaError>);

// list of aggregated errors
return { errors: error.inner || [] };
return { errors: Object.values(errors) };
}
},
parse(values) {
Expand Down
57 changes: 57 additions & 0 deletions packages/yup/tests/yup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,63 @@ test('validates typed schema form with yup', async () => {
expect(passwordError.textContent).toBe('');
});

test('shows multiple errors using error bag', async () => {
const wrapper = mountWithHoc({
setup() {
const schema = toTypedSchema(
yup.object({
email: yup.string().email(EMAIL_MSG).min(7, MIN_MSG),
password: yup.string().min(8, MIN_MSG),
})
);

const { useFieldModel, errorBag } = useForm({
validationSchema: schema,
validateOnMount: true,
});

const [email, password] = useFieldModel(['email', 'password']);

return {
schema,
email,
password,
errorBag,
};
},
template: `
<div>
<input id="email" name="email" v-model="email" />
<span id="emailErr">{{ errorBag.email?.join(',') }}</span>
<input id="password" name="password" type="password" v-model="password" />
<span id="passwordErr">{{ errorBag.password?.join(',') }}</span>
</div>
`,
});

const email = wrapper.$el.querySelector('#email');
const password = wrapper.$el.querySelector('#password');
const emailError = wrapper.$el.querySelector('#emailErr');
const passwordError = wrapper.$el.querySelector('#passwordErr');

await flushPromises();

setValue(email, 'hello@');
setValue(password, '1234');
await flushPromises();

expect(emailError.textContent).toBe([EMAIL_MSG, MIN_MSG].join(','));
expect(passwordError.textContent).toBe([MIN_MSG].join(','));

setValue(email, 'hello@email.com');
setValue(password, '12346789');
await flushPromises();

expect(emailError.textContent).toBe('');
expect(passwordError.textContent).toBe('');
});

test('uses yup for form values transformations and parsing', async () => {
const submitSpy = jest.fn();
mountWithHoc({
Expand Down

0 comments on commit 7f90bbc

Please sign in to comment.