From 2f53b70ef9c086a268330fa263390a2edd0164dd Mon Sep 17 00:00:00 2001 From: Evan Jacobs <570070+probablyup@users.noreply.github.com> Date: Sat, 27 May 2023 09:23:11 -0400 Subject: [PATCH] feat(validationSchema): support yup transforms in validate & submit (#3796) --- .changeset/fluffy-rivers-rest.md | 5 ++ packages/formik/src/Formik.tsx | 21 ++++++-- packages/formik/test/Formik.test.tsx | 68 +++++++++++++++++++++++- packages/formik/test/withFormik.test.tsx | 9 ++-- 4 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 .changeset/fluffy-rivers-rest.md diff --git a/.changeset/fluffy-rivers-rest.md b/.changeset/fluffy-rivers-rest.md new file mode 100644 index 000000000..f5cae3aad --- /dev/null +++ b/.changeset/fluffy-rivers-rest.md @@ -0,0 +1,5 @@ +--- +'formik': minor +--- + +Add support for Yup ["transforms"](https://github.com/jquense/yup#parsing-transforms). diff --git a/packages/formik/src/Formik.tsx b/packages/formik/src/Formik.tsx index 2e7a8f4aa..1b1b92679 100755 --- a/packages/formik/src/Formik.tsx +++ b/packages/formik/src/Formik.tsx @@ -218,10 +218,7 @@ export function useFormik({ */ const runValidationSchema = React.useCallback( (values: Values, field?: string): Promise> => { - const validationSchema = props.validationSchema; - const schema = isFunction(validationSchema) - ? validationSchema(field) - : validationSchema; + const schema = getValidationSchema(props.validationSchema); const promise = field && schema.validateAt ? schema.validateAt(field, values) @@ -845,7 +842,11 @@ export function useFormik({ }; const executeSubmit = useEventCallback(() => { - return onSubmit(state.values, imperativeMethods); + const schema = getValidationSchema(props.validationSchema); + const actualizedValues = + schema && schema.cast ? schema.cast(state.values) : state.values; + + return onSubmit(actualizedValues, imperativeMethods); }); const handleReset = useEventCallback(e => { @@ -1023,6 +1024,16 @@ export function Formik< ); } +function getValidationSchema( + validationSchema?: FormikConfig['validationSchema'] +) { + if (!validationSchema) { + return; + } + + return isFunction(validationSchema) ? validationSchema() : validationSchema; +} + function warnAboutMissingIdentifier({ htmlContent, documentationAnchorLink, diff --git a/packages/formik/test/Formik.test.tsx b/packages/formik/test/Formik.test.tsx index 7d13ba81e..ed1e0e237 100644 --- a/packages/formik/test/Formik.test.tsx +++ b/packages/formik/test/Formik.test.tsx @@ -1435,8 +1435,8 @@ describe('', () => { await act(async () => { try { await getProps().validateForm(); - } catch ({ message }) { - caughtError = message; + } catch (err) { + caughtError = (err as Yup.ValidationError).message; } }); @@ -1450,4 +1450,68 @@ describe('', () => { expect(innerRef.current).toEqual(getProps()); }); + + it('transforms in yup schema are applied on validation', async () => { + const validationSchema = Yup.object({ + users: Yup.array().of( + Yup.object({ + firstName: Yup.string() + .transform(currentValue => + currentValue.split('').reverse().join('') + ) + // @ts-expect-error incorrect typing for second arg + .oneOf(['foo'], x => x.value), + }) + ), + }); + + const { getProps } = renderFormik({ + initialValues: { users: [{ firstName: 'foo' }] }, + validationSchema, + }); + + await act(async () => { + await getProps().validateForm(); + + expect(getProps().errors).toEqual({ + users: [ + { + // the transform reverses "foo" to "oof", which then fails the `oneOf` assertion + firstName: 'oof', + }, + ], + }); + }); + }); + + it('transforms in yup schema are applied on submit', async () => { + const validationSchema = Yup.object({ + users: Yup.array().of( + Yup.object({ + firstName: Yup.string().transform(currentValue => + currentValue.split('').reverse().join('') + ), + }) + ), + }); + + const spy = jest.fn(); + + const { getProps } = renderFormik({ + initialValues: { users: [{ firstName: 'foo' }] }, + onSubmit: spy, + validationSchema, + }); + + await act(async () => { + await getProps().submitForm(); + + expect(spy).toHaveBeenCalledWith( + { + users: [{ firstName: 'oof' }], + }, + expect.anything() + ); + }); + }); }); diff --git a/packages/formik/test/withFormik.test.tsx b/packages/formik/test/withFormik.test.tsx index e4e139698..5f136b5c2 100644 --- a/packages/formik/test/withFormik.test.tsx +++ b/packages/formik/test/withFormik.test.tsx @@ -134,15 +134,18 @@ describe('withFormik()', () => { }); it('calls validationSchema', async () => { - const validate = jest.fn(() => Promise.resolve()); + const validationSchema = Yup.object(); + + jest.spyOn(validationSchema, 'validate'); + const { getProps } = renderWithFormik({ - validationSchema: { validate }, + validationSchema, }); act(() => { getProps().submitForm(); }); - await waitFor(() => expect(validate).toHaveBeenCalled()); + await waitFor(() => expect(validationSchema.validate).toHaveBeenCalled()); }); it('calls validationSchema function with props', async () => {