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

Support multiple errors messages #32

Conversation

Paul-Vandell
Copy link
Contributor

@Paul-Vandell Paul-Vandell commented Jul 19, 2020

  • 100% test coverage for the yup resolver
  • Now errors have a types props with a Record key type and for values an array of error messages, if criteriaMode: "all"

At the same time:

  • Fix the abort early issue. It does not work because it let a yup resolver with an inner error of an empty array. So it always return any errors. Now we check if inner errors is an empty array or not. If no please return only one error at a time. Test has been done for this. it could help resolve issue from -> feat(context): inject context into yup context #30 (comment) where yup validation return anything.
  • Optimise the validate all field criteria props in the yup resolver script. The yup error path is always defined so i remove some useless code logic ( if i don't make a mistake )
  • The new props types is always defined it can be an empty array too.
  • Still compatible with the ErrorMessage component.
  • Add more types to the resolver script -> to be check im not an expert. I used types from rhf dist i think maybe it is not the right way :/

Improvements which can be done into

  1. For an array, If we remove the transformToNestObject we will have at the same time array errors and all field errors inside this array.
    Cons: errors duplication :
foo[0].test: {
      message: ...,
      type: ...,
      types: [ ] | {}
},
foo: [
  { 
    test: {
      message: ...,
      type: ...,
      types: [ ] | {}
    }
  },
  ...
]

Pros: it can be interesting to have it to get at the same time array errors and fields errors in.
We could maybe do : validateAllFieldCriteria ? call transformToNestObject
You can have example of use case here -> #26 (comment)
From issue -> #26

  1. On first try to rhf we don't know where to place the context and where will be the changes !? We should remove the context option from the yup options by his types. It come in trouble when you expect your form validation to be used from cached context or from yup context options. From my issue -> Yup resolver validation seems to be unlinked with the form react-hook-form#2222. We could do this to force user to always use the context manage by rhf:
export const yupResolver = <TFieldValues extends Record<string, any>>(
  schema: Yup.ObjectSchema | Yup.Lazy,
  options: Omit<Yup.ValidateOptions, 'context'> = { // OMIT THE CONTEXT
    abortEarly: false,
  },

it could help issue like -> #30 (comment)

@Paul-Vandell Paul-Vandell force-pushed the feature/support-multiple-errors-message branch from c0977e0 to dba4bb9 Compare July 19, 2020 14:35
@Paul-Vandell Paul-Vandell changed the title support-multiple-errors-messages Support multiple errors messages Jul 19, 2020
@bluebill1049
Copy link
Member

oh man! thanks very much for your amazing contribution, I will take a look at the PR. 🚀

Copy link

@pmaier983 pmaier983 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Great testing coverage!

@bluebill1049
Copy link
Member

bluebill1049 commented Jul 20, 2020

Thanks @pmaier983 I will take a look at the PR tonight as well. solid effort @Vandell63 ❤️

src/yup.test.ts Outdated
Comment on lines 116 to 132
expect(resolve.errors['password'].types).toMatchInlineSnapshot(`
Object {
"matches": Array [
"Special",
"Number",
"Uppercase",
"Lowercase",
],
"min": Array [
"password must be at least 8 characters",
],
"required": Array [
"password is a required field",
],
}
`);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The structure I had in mind was:

expect(resolve.errors['password'].types).toMatchInlineSnapshot(`
  Object {
    "matches": Array [
      "Special",
      "Number",
      "Uppercase",
      "Lowercase",
    ],
    "min": "password must be at least 8 characters",
    "required": "password is a required field",
  }
`);

We should discuss whether all should be array message when validateAllFieldCriteria is true.

Copy link
Member

@bluebill1049 bluebill1049 Jul 20, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @kotarella1110. Above is a breaking change, we should stick with

expect(resolve.errors['password'].types).toMatchInlineSnapshot(`
  Object {
    "matches": Array [
      "Special",
      "Number",
      "Uppercase",
      "Lowercase",
    ],
    "min": "password must be at least 8 characters",
    "required": "password is a required field",
  }
`);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bluebill1049 I agree with you 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, i was thinking if we keep this mix structure, i'm afraid that we have to handle the case where messages can be an array or a string in the code for every property in the schema. This is why i keep every messages in array format when validateAllCriteria is set to true. But you are in better place to take the right decision here :).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks very much for understanding and putting this PR together!

@Paul-Vandell
Copy link
Contributor Author

Paul-Vandell commented Jul 20, 2020

Thanks @pmaier983 I will take a look at the PR tonight as well. solid effort @Vandell63

Thx you . As much as the time you gave me when i was in trouble

Comment on lines 9 to 11
"types": Object {
"required": "age is a required field",
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If validateAllFieldCriteria is false, types field shouldn't be included.

Comment on lines 16 to 18
"types": Object {
"min": "name is a min field",
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

@kotarella1110
Copy link
Member

@Vandell63 I have created a PR (Paul-Vandell#1) to take my feedback comment above.
Please merge if no problem 🙏
After merging this PR, you are free to make changes.

@Paul-Vandell
Copy link
Contributor Author

Paul-Vandell commented Jul 20, 2020

@kotarella1110 sry i was thinking that @bluebill1049 will make some changes too. So if i understood, you want the props types to be undefined if validateAllFieldCriteria is set to false. To resume :

  1. Should i keep the yup resolver script that i made and then change it to match Your expected test result ? Or should i remove all my previous yup resolver changes and keep yours instead ?
  2. Should i keep the mixed array and/or string types if validateAllFieldCriteria is set to true from your review @kotarella1110 ?
  3. [Improvement] Should i remove the script transformToNestObject if validateAllFieldCriteria is set to true to get all fields error even if array have some ?
  4. [Improvement] Should i force the user to inject his context in the rhf options instead of yup options with the Omit type ?
  5. Should i re-add the script code to
    Fix the abort early issue. It does not work because it let a yup resolver with an inner error of an empty array. So it always return any errors. Now we check if inner errors is an empty array or not
    By checking if
    Array.isArray(error.inner) && error.inner.length is true

@kotarella1110
Copy link
Member

  1. Should i keep the yup resolver script that i made and then change it to match Your expected test result ? Or should i remove all my previous yup resolver changes and keep yours instead ?

I will commit the No.5 fix to the PR I created.
Array.isArray(error.inner) && error.inner.length is true
You can then merge.

  1. Should i keep the mixed array and/or string types if validateAllFieldCriteria is set to true from your review @kotarella1110 ?

yes. I think we should keep mixed types.

  1. [Improvement] Should i remove the script transformToNestObject if validateAllFieldCriteria is set to true to get all fields error even if array have some ?

What do @bluebill1049 think about this ?

  1. [Improvement] Should i force the user to inject his context in the rhf options instead of yup options with the Omit type ?

This is under consideration. Please give me some time.

  1. Should i re-add the script code to
    Fix the abort early issue. It does not work because it let a yup resolver with an inner error of an empty array. So it always return any errors. Now we check if inner errors is an empty array or not
    By checking if
    Array.isArray(error.inner) && error.inner.length is true

we should fix this 👍

@bluebill1049
Copy link
Member

3. [Improvement] Should i remove the script transformToNestObject if validateAllFieldCriteria is set to true to get all fields error even if array have some ?

Yes totally 👍

@bluebill1049
Copy link
Member

Thanks @kotarella1110 for the review, sorry i was busy and haven't look close enough, i will make sure to do that tonight.

Copy link
Member

@bluebill1049 bluebill1049 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comments as @kotarella1110 thank for the PR 👍

src/yup.ts Outdated
Array.isArray(error.inner) && error.inner.length
? error.inner.reduce<FieldErrors<TFieldValues>>(
(previous, { path, message, type }) => {
const previousPath = previous[path] as FieldErrors | undefined;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use this variable inline (save few bytes)

src/yup.ts Outdated
type,
types: previousTypes,
}),
types: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

types should be optional.

@kotarella1110
Copy link
Member

@Vandell63 @bluebill1049 please review this PR (Paul-Vandell#1).

@Paul-Vandell
Copy link
Contributor Author

Paul-Vandell commented Jul 21, 2020

@Vandell63 @bluebill1049 please review this PR (Vandell63#1).

Done @kotarella1110

@Paul-Vandell
Copy link
Contributor Author

Paul-Vandell commented Jul 21, 2020

Thank @bluebill1049 @kotarella1110 merge done.
I will work on #32 (comment) and make the test for tonight.
And I wait for the point 4 with @kotarella1110 message : This is under consideration. Please give me some time.

@Paul-Vandell
Copy link
Contributor Author

@bluebill1049 @kotarella1110 Oh i maybe forget this. In yup errors the path seems to be never undefined so should i remove path logic from the code. Or should i keep it and make a test on it ?

@Paul-Vandell
Copy link
Contributor Author

I finally added test and #32 (comment) logic.
Pending for my previous messages.

@bluebill1049
Copy link
Member

  1. [Improvement] Should i force the user to inject his context in the rhf options instead of yup options with the Omit type ?

I think it would be better to centralize the context at useForm, that way our API is consistent through other resolvers as well. what do you think @kotarella1110?

@bluebill1049
Copy link
Member

@bluebill1049 @kotarella1110 Oh i maybe forget this. In yup errors the path seems to be never undefined so should i remove path logic from the code. Or should i keep it and make a test on it ?

actually, from memory they do get undefined in certain circumstance, I can't remember when :(

@kotarella1110
Copy link
Member

  1. [Improvement] Should i force the user to inject his context in the rhf options instead of yup options with the Omit type ?

I think it would be better to centralize the context at useForm, that way our API is consistent through other resolvers as well. what do you think @kotarella1110?

@bluebill1049 Totally agree 👍
@Vandell63 Let's remove the context option from yupResolver option.

@Paul-Vandell
Copy link
Contributor Author

Paul-Vandell commented Jul 21, 2020

So @bluebill1049 @kotarella1110 should you prefer:

  1. add the Omit typescript: Omit<Yup.ValidateOptions, 'context'>
  2. delete options.context
  3. Add a console warn to the user if context is passed ( for non typescript dev ) to yup options on non-production build ?

It can be all of them.
I let you me know what you prefer and then :

  • I will add the missing test statement if yup path is undefined.
  • Refactor context to be only the rhf context based of your choices.
  • Test the logic based of your choices.

@kotarella1110
Copy link
Member

kotarella1110 commented Jul 21, 2020

#32 (comment)

@Vandell63 Does option 2 mean this?

 values:  await schema.validate(values, {
-   context,
    ...options,
+   context,
  }) as any,

I prefer all of them.
Let's wait for @bluebill1049 's opinion.

@Paul-Vandell
Copy link
Contributor Author

Oh sorry @kotarella1110 , no in the code it could be

try {
  if(options.context){
    console.warn("please use rhf context option instead")
    delete options.context
  }
  process()
} ...

Remove context property from yup option if user is not in typescript it could still add context props into. Just to be sure.

@bluebill1049
Copy link
Member

bluebill1049 commented Jul 22, 2020

shall we do 1 and 3?

  • for TS user just a type
  • for JS user just a console.warn for dev only
if (process.env.NODE_ENV ==='development') {
 console.warn('xxx');
}

@kotarella1110
Copy link
Member

@bluebill1049 I agree 👍
@Vandell63 Also, we prefer the context passed from useForm over the options.context of yupResolver, so I think it should be fixed as I described in this #32 (comment).

@Paul-Vandell
Copy link
Contributor Author

@bluebill1049 @kotarella1110 ok for me i will propose you a final commit with test as soon as i can .

@bluebill1049
Copy link
Member

@bluebill1049 @kotarella1110 ok for me i will propose you a final commit with test as soon as i can .

Thank you very much. no rush whenever you are free.

@Paul-Vandell
Copy link
Contributor Author

@bluebill1049 a pleasure for me no worry

@Paul-Vandell
Copy link
Contributor Author

Paul-Vandell commented Jul 22, 2020

@bluebill1049 @kotarella1110 there we go.

  • 100% test coverage
  • Remove context type from the yup options
  • Add warn log if yup context is defined
    Maybe you will have to change the message, im bit crappy in english :/ .
    Tell me if you want a special squash commit with a rebase to finish it.

@Paul-Vandell Paul-Vandell force-pushed the feature/support-multiple-errors-message branch from b08757d to e998e69 Compare July 22, 2020 18:55
Copy link
Member

@kotarella1110 kotarella1110 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🎉

@bluebill1049 bluebill1049 merged commit bafb36d into react-hook-form:master Jul 23, 2020
@bluebill1049
Copy link
Member

Let's go! thanks @kotarella1110 's review and @Vandell63's hard work! do you have a twitter account? let's tweet this new feature.

@Paul-Vandell
Copy link
Contributor Author

Yess it was so cool thx . let's go this new fresh twitter -> https://twitter.com/Paul_Vandell.
Thank you again for your high availability @bluebill1049 @kotarella1110

@sngreg
Copy link

sngreg commented Jul 23, 2020

@Vandell63 @bluebill1049 @kotarella1110 - THANK you so much this code looks great. Sorry I went MIA for a couple of days, I was out of the office on vacation.

This feature is a GREAT add to this repo!

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

Successfully merging this pull request may close these issues.

5 participants