Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to get tests for nested fields using reach #1581

Closed
lars-bekkema-cko opened this issue Feb 9, 2022 · 2 comments
Closed

Unable to get tests for nested fields using reach #1581

lars-bekkema-cko opened this issue Feb 9, 2022 · 2 comments

Comments

@lars-bekkema-cko
Copy link

In our project, we have some complex forms that are quite layered fields with some of them that rely on .when and .test to update the validation. We have been trying to leverage using .reach to determine which fields are required so that we can display this to the user as per inspiration from the following tickets:

We have noticed that for any top-level field this method works really great, but for any nested field (such as an array or object) the schema doesn't resolve as expected. I have noticed that when I would modify getIn to always resolve the schema it does work, but it would break the existing Yup.reach implementation.

I've created a CodeSandbox with an example here where you can toggle between the custom Yup.reach that "fully resolves" and the default Yup.reach to see that the asterisk shows up for the former and not for the latter: https://codesandbox.io/s/currying-wave-3yfnf?file=/src/App.tsx.

Do you have any recommendations to resolve this correctly?

@OperKH
Copy link

OperKH commented May 17, 2022

In more complex forms with recursive nesting of .when - customReach doesn't work.

Here is real anonymized schema from production:

Yup.object().shape({
  field1: Yup.string().trim().required(),
  field2: Yup.string().when('field1', {
    is: (field1: string) => {
      return field1 === 'Report 1';
    },
    // Report 1
    then: Yup.string().when(['field3', 'field4'], {
      is: (field3?: string[], field4?: string[]) =>
        (field3 && field3.length) || (field4 && field4.length),
      // field3 or field4 selected
      then: Yup.string().trim().optional().maxLines(300),
      // field3 and field4 empty
      otherwise: Yup.string()
        .trim()
        .required('lorem')
        .maxLines(300),
    }),
    // Not Report 1
    otherwise: Yup.string().when('field1', {
      is: (field1: string) => {
          return field1 === 'Report 2';
      },
      // Report 2
      then: Yup.string().when('field4', {
        is: (field4?: string[]) => field4 && field4.length,
        // field4 selected
        then: Yup.string().trim().optional().maxLines(300),
        // field4 empty
        otherwise: Yup.string()
          .label('Policy numbers')
          .trim()
          .required('lorem')
          .maxLines(300),
      }),
      // Not Report 1 or Report 2
      otherwise: Yup.string().when('field1', {
        is: (field1: string) => {
          return field1 === 'Report 3' || field1 === 'Report 4';
        },
        // Report 3 or Report 4
        then: Yup.string().trim().optional(), // hidden
        // Not Report 1, Report 2, Report 3 or Report 4 = Report 5
        otherwise: Yup.string().trim().required().maxLines(300),
      }),
    }),
  }),
  field3: Yup.array().when('field1', {
    is: (field1: string) => {
      return field1 === 'Report 1';
    },
    // Report 1
    then: Yup.array().when(['field2', 'field4'], {
      is: (field2?: string, field4?: string[]) => field2 || (field4 && field4.length),
      // field2 or field4 selected
      then: Yup.array().min(0),
      // field2 and field4 empty
      otherwise: Yup.array()
        .min(1, 'lorem'),
    }),
    // Not Report 1
    otherwise: Yup.array().optional(), // hidden
  }),
  field4: Yup.array().when('field1', {
    is: (field1: string) => {
      return field1 === 'Report 1';
    },
    // Report 1
    then: Yup.array().when(['field2', 'field3'], {
      is: (field2?: string, field3?: string[]) => field2 || (field3 && field3.length),
      // field2 or field3 selected
      then: Yup.array().min(0),
      // field2 and field3 empty
      otherwise: Yup.array()
        .min(1, 'lorem'),
    }),
    // Not Report 1
    otherwise: Yup.array().when('field1', {
      is: (field1: string) => {
          return field1 === 'Report 1';
      },
      // Report 2
      then: Yup.array().when('field2', {
        is: (field2?: string) => !!field2,
        // field2 selected
        then: Yup.array().min(0),
        // field2 empty
        otherwise: Yup.array()
          .min(1, 'lorem'),
      }),
      // Not  Report 1 or Report 2
      otherwise: Yup.array().min(0),
    }),
  }),
});

@jquense
Copy link
Owner

jquense commented Aug 23, 2022

If using reach expect the schema you get back to work just like a top level schema, e.g. it will be unresolved. I am not sure what you are doing with the schema after reaching but it would work the same as any schema you do something with, you can describe, cast, validate, and should pass in the context/parent.

I realize this feels duplicative, but it's par for the course when using other schema whose context you don't know. To make the whole thing more ergonomic, use getIn instead of reach.

import { getIn } from 'yup'

const { schema, parent } = getIn(obj, path)

schema.describe({ parent })

@jquense jquense closed this as completed Aug 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants