@hookform/resolvers 2.0.0-rc.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -18
  3. package/dist/index.d.ts +1 -0
  4. package/dist/resolvers.js +2 -0
  5. package/dist/resolvers.js.map +1 -0
  6. package/dist/resolvers.mjs +2 -0
  7. package/dist/resolvers.modern.js +2 -0
  8. package/dist/resolvers.modern.js.map +1 -0
  9. package/dist/resolvers.module.js +2 -0
  10. package/dist/resolvers.module.js.map +1 -0
  11. package/dist/resolvers.umd.js +2 -0
  12. package/dist/resolvers.umd.js.map +1 -0
  13. package/dist/toNestError.d.ts +2 -0
  14. package/joi/dist/index.d.ts +2 -0
  15. package/joi/dist/joi.d.ts +2 -0
  16. package/joi/dist/joi.js +2 -0
  17. package/joi/dist/joi.js.map +1 -0
  18. package/joi/dist/joi.mjs +2 -0
  19. package/joi/dist/joi.modern.js +2 -0
  20. package/joi/dist/joi.modern.js.map +1 -0
  21. package/joi/dist/joi.module.js +2 -0
  22. package/joi/dist/joi.module.js.map +1 -0
  23. package/joi/dist/joi.umd.js +2 -0
  24. package/joi/dist/joi.umd.js.map +1 -0
  25. package/joi/dist/types.d.ts +5 -0
  26. package/joi/package.json +17 -0
  27. package/joi/src/__tests__/Form.tsx +61 -0
  28. package/joi/src/__tests__/__fixtures__/data.ts +76 -0
  29. package/joi/src/__tests__/__snapshots__/joi.ts.snap +283 -0
  30. package/joi/src/__tests__/joi.ts +89 -0
  31. package/joi/src/index.ts +2 -0
  32. package/joi/src/joi.ts +63 -0
  33. package/joi/src/types.ts +17 -0
  34. package/package.json +111 -55
  35. package/superstruct/dist/index.d.ts +2 -0
  36. package/superstruct/dist/superstruct.d.ts +2 -0
  37. package/superstruct/dist/superstruct.js +2 -0
  38. package/superstruct/dist/superstruct.js.map +1 -0
  39. package/superstruct/dist/superstruct.mjs +2 -0
  40. package/superstruct/dist/superstruct.modern.js +2 -0
  41. package/superstruct/dist/superstruct.modern.js.map +1 -0
  42. package/superstruct/dist/superstruct.module.js +2 -0
  43. package/superstruct/dist/superstruct.module.js.map +1 -0
  44. package/superstruct/dist/superstruct.umd.js +2 -0
  45. package/superstruct/dist/superstruct.umd.js.map +1 -0
  46. package/superstruct/dist/types.d.ts +5 -0
  47. package/superstruct/package.json +18 -0
  48. package/superstruct/src/__tests__/Form.tsx +62 -0
  49. package/superstruct/src/__tests__/__fixtures__/data.ts +73 -0
  50. package/superstruct/src/__tests__/__snapshots__/superstruct.ts.snap +64 -0
  51. package/superstruct/src/__tests__/superstruct.ts +20 -0
  52. package/superstruct/src/index.ts +2 -0
  53. package/superstruct/src/superstruct.ts +30 -0
  54. package/superstruct/src/types.ts +18 -0
  55. package/vest/dist/index.d.ts +1 -0
  56. package/vest/dist/vest.d.ts +2 -0
  57. package/vest/dist/vest.js +2 -0
  58. package/vest/dist/vest.js.map +1 -0
  59. package/vest/dist/vest.mjs +2 -0
  60. package/vest/dist/vest.modern.js +2 -0
  61. package/vest/dist/vest.modern.js.map +1 -0
  62. package/vest/dist/vest.module.js +2 -0
  63. package/vest/dist/vest.module.js.map +1 -0
  64. package/vest/dist/vest.umd.js +2 -0
  65. package/vest/dist/vest.umd.js.map +1 -0
  66. package/vest/package.json +18 -0
  67. package/vest/src/__tests__/Form.tsx +64 -0
  68. package/vest/src/__tests__/__fixtures__/data.ts +67 -0
  69. package/vest/src/__tests__/__snapshots__/vest.ts.snap +141 -0
  70. package/vest/src/__tests__/vest.ts +65 -0
  71. package/vest/src/index.ts +1 -0
  72. package/vest/src/types.ts +21 -0
  73. package/vest/src/vest.ts +45 -0
  74. package/yup/dist/index.d.ts +2 -0
  75. package/yup/dist/types.d.ts +8 -0
  76. package/yup/dist/yup.d.ts +2 -0
  77. package/yup/dist/yup.js +2 -0
  78. package/yup/dist/yup.js.map +1 -0
  79. package/yup/dist/yup.mjs +2 -0
  80. package/yup/dist/yup.modern.js +2 -0
  81. package/yup/dist/yup.modern.js.map +1 -0
  82. package/yup/dist/yup.module.js +2 -0
  83. package/yup/dist/yup.module.js.map +1 -0
  84. package/yup/dist/yup.umd.js +2 -0
  85. package/yup/dist/yup.umd.js.map +1 -0
  86. package/yup/package.json +17 -0
  87. package/yup/src/__tests__/Form.tsx +54 -0
  88. package/yup/src/__tests__/__fixtures__/data.ts +65 -0
  89. package/yup/src/__tests__/__snapshots__/yup.ts.snap +221 -0
  90. package/yup/src/__tests__/yup.ts +150 -0
  91. package/yup/src/index.ts +2 -0
  92. package/yup/src/types.ts +22 -0
  93. package/yup/src/yup.ts +65 -0
  94. package/zod/dist/index.d.ts +2 -0
  95. package/zod/dist/types.d.ts +6 -0
  96. package/zod/dist/zod.d.ts +2 -0
  97. package/zod/dist/zod.js +2 -0
  98. package/zod/dist/zod.js.map +1 -0
  99. package/zod/dist/zod.mjs +2 -0
  100. package/zod/dist/zod.modern.js +2 -0
  101. package/zod/dist/zod.modern.js.map +1 -0
  102. package/zod/dist/zod.module.js +2 -0
  103. package/zod/dist/zod.module.js.map +1 -0
  104. package/zod/dist/zod.umd.js +2 -0
  105. package/zod/dist/zod.umd.js.map +1 -0
  106. package/zod/package.json +17 -0
  107. package/zod/src/__tests__/Form.tsx +54 -0
  108. package/zod/src/__tests__/__fixtures__/data.ts +68 -0
  109. package/zod/src/__tests__/__snapshots__/zod.ts.snap +355 -0
  110. package/zod/src/__tests__/zod.ts +71 -0
  111. package/zod/src/index.ts +2 -0
  112. package/zod/src/types.ts +18 -0
  113. package/zod/src/zod.ts +65 -0
  114. package/dist/ie11/joi.d.ts +0 -3
  115. package/dist/ie11/joi.js +0 -104
  116. package/dist/ie11/joi.js.map +0 -1
  117. package/dist/ie11/superstruct.d.ts +0 -3
  118. package/dist/ie11/superstruct.js +0 -95
  119. package/dist/ie11/superstruct.js.map +0 -1
  120. package/dist/ie11/utils/convertArrayToPathName.d.ts +0 -2
  121. package/dist/ie11/utils/convertArrayToPathName.js +0 -12
  122. package/dist/ie11/utils/convertArrayToPathName.js.map +0 -1
  123. package/dist/ie11/yup.d.ts +0 -3
  124. package/dist/ie11/yup.js +0 -132
  125. package/dist/ie11/yup.js.map +0 -1
  126. package/dist/joi.d.ts +0 -3
  127. package/dist/joi.js +0 -41
  128. package/dist/joi.js.map +0 -1
  129. package/dist/superstruct.d.ts +0 -3
  130. package/dist/superstruct.js +0 -39
  131. package/dist/superstruct.js.map +0 -1
  132. package/dist/umd/index.js +0 -2
  133. package/dist/umd/index.js.map +0 -1
  134. package/dist/utils/convertArrayToPathName.d.ts +0 -2
  135. package/dist/utils/convertArrayToPathName.js +0 -8
  136. package/dist/utils/convertArrayToPathName.js.map +0 -1
  137. package/dist/yup.d.ts +0 -3
  138. package/dist/yup.js +0 -49
  139. package/dist/yup.js.map +0 -1
  140. package/joi.d.ts +0 -1
  141. package/joi.js +0 -1
  142. package/superstruct.js +0 -1
  143. package/yup.d.ts +0 -1
  144. package/yup.js +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019-present Beier(Bill) Luo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <div align="center">
2
2
  <p align="center">
3
3
  <a href="https://react-hook-form.com" title="React Hook Form - Simple React forms validation">
4
- <img src="https://raw.githubusercontent.com/bluebill1049/react-hook-form/master/website/logo.png" alt="React Hook Form Logo - React hook custom hook for form validation" width="300px" />
4
+ <img src="https://raw.githubusercontent.com/bluebill1049/react-hook-form/master/docs/logo.png" alt="React Hook Form Logo - React hook custom hook for form validation" width="300px" />
5
5
  </a>
6
6
  </p>
7
7
  </div>
@@ -26,12 +26,13 @@ We are moving away from native support for Yup validation. We are now supporting
26
26
 
27
27
  ## API
28
28
 
29
- `resolver(schema: object, config?: object)`
29
+ `resolver(schema: object, schemaOptions?: object, resolverOptions: { mode: 'async' | 'sync' })`
30
30
 
31
- | | type | Required | Description |
32
- | ------ | -------- | -------- | -------------------------------------- |
33
- | schema | `object` | ✓ | validation schema |
34
- | config | `object` | | validation schema configuration object |
31
+ | | type | Required | Description |
32
+ | --------------- | -------- | -------- | --------------------------------------------- |
33
+ | schema | `object` | ✓ | validation schema |
34
+ | schemaOptions | `object` | | validation library schema options |
35
+ | resolverOptions | `object` | | resolver options, `async` is the default mode |
35
36
 
36
37
  ## Quickstart
37
38
 
@@ -44,7 +45,7 @@ Dead simple Object schema validation.
44
45
  ```typescript jsx
45
46
  import React from 'react';
46
47
  import { useForm } from 'react-hook-form';
47
- import { yupResolver } from '@hookform/resolvers';
48
+ import { yupResolver } from '@hookform/resolvers/yup';
48
49
  import * as yup from 'yup';
49
50
 
50
51
  const schema = yup.object().shape({
@@ -61,11 +62,50 @@ const App = () => {
61
62
  <form onSubmit={handleSubmit((d) => console.log(d))}>
62
63
  <input name="name" ref={register} />
63
64
  <input name="age" type="number" ref={register} />
65
+ <input type="submit" />
66
+ </form>
67
+ );
68
+ };
69
+
70
+ export default App;
71
+ ```
72
+
73
+ ### [Zod](https://github.com/vriad/zod)
74
+
75
+ TypeScript-first schema validation with static type inference
76
+
77
+ [![npm](https://img.shields.io/bundlephobia/minzip/zod?style=for-the-badge)](https://bundlephobia.com/result?p=zod)
78
+
79
+ > ⚠️ Example below uses the `valueAsNumber`, which requires `react-hook-form` v6.12.0 (released Nov 28, 2020) or later.
80
+
81
+ ```tsx
82
+ import React from 'react';
83
+ import { useForm } from 'react-hook-form';
84
+ import { zodResolver } from '@hookform/resolvers/zod';
85
+ import * as z from 'zod';
86
+
87
+ const schema = z.object({
88
+ name: z.string().nonempty({ message: 'Required' }),
89
+ age: z.number().min(10),
90
+ });
91
+
92
+ const App = () => {
93
+ const { register, handleSubmit, errors } = useForm({
94
+ resolver: zodResolver(schema),
95
+ });
64
96
 
97
+ return (
98
+ <form onSubmit={handleSubmit((d) => console.log(d))}>
99
+ <input name="name" ref={register} />
100
+ {errors.name?.message && <p>{errors.name?.message}</p>}
101
+ <input name="age" type="number" ref={register({ valueAsNumber: true })} />
102
+ {errors.age?.message && <p>{errors.age?.message}</p>}
65
103
  <input type="submit" />
66
104
  </form>
67
105
  );
68
106
  };
107
+
108
+ export default App;
69
109
  ```
70
110
 
71
111
  ### [Superstruct](https://github.com/ianstormtaylor/superstruct)
@@ -77,12 +117,12 @@ A simple and composable way to validate data in JavaScript (or TypeScript).
77
117
  ```typescript jsx
78
118
  import React from 'react';
79
119
  import { useForm } from 'react-hook-form';
80
- import { superstructResolver } from '@hookform/resolvers';
81
- import { struct } from 'superstruct';
120
+ import { superstructResolver } from '@hookform/resolvers/superstruct';
121
+ import { object, string, number } from 'superstruct';
82
122
 
83
- const schema = struct({
84
- name: 'string',
85
- age: 'number',
123
+ const schema = object({
124
+ name: string(),
125
+ age: number(),
86
126
  });
87
127
 
88
128
  const App = () => {
@@ -93,25 +133,26 @@ const App = () => {
93
133
  return (
94
134
  <form onSubmit={handleSubmit((d) => console.log(d))}>
95
135
  <input name="name" ref={register} />
96
- <input name="age" type="number" ref={register} />
97
-
136
+ <input name="age" type="number" ref={register({ valueAsNumber: true })} />
98
137
  <input type="submit" />
99
138
  </form>
100
139
  );
101
140
  };
141
+
142
+ export default App;
102
143
  ```
103
144
 
104
- ### [Joi](https://github.com/hapijs/joi)
145
+ ### [Joi](https://github.com/sideway/joi)
105
146
 
106
147
  The most powerful data validation library for JS.
107
148
 
108
- [![npm](https://img.shields.io/bundlephobia/minzip/@hapi/joi?style=for-the-badge)](https://bundlephobia.com/result?p=@hapi/joi)
149
+ [![npm](https://img.shields.io/bundlephobia/minzip/joi?style=for-the-badge)](https://bundlephobia.com/result?p=joi)
109
150
 
110
151
  ```typescript jsx
111
152
  import React from 'react';
112
153
  import { useForm } from 'react-hook-form';
113
- import { joiResolver } from '@hookform/resolvers';
114
- import Joi from '@hapi/joi';
154
+ import { joiResolver } from '@hookform/resolvers/joi';
155
+ import Joi from 'joi';
115
156
 
116
157
  const schema = Joi.object({
117
158
  username: Joi.string().required(),
@@ -126,11 +167,67 @@ const App = () => {
126
167
  <form onSubmit={handleSubmit((d) => console.log(d))}>
127
168
  <input name="name" ref={register} />
128
169
  <input name="age" type="number" ref={register} />
170
+ <input type="submit" />
171
+ </form>
172
+ );
173
+ };
174
+
175
+ export default App;
176
+ ```
177
+
178
+ ### [Vest](https://github.com/ealush/vest)
179
+
180
+ Vest 🦺 Declarative Validation Testing.
181
+
182
+ [![npm](https://img.shields.io/bundlephobia/minzip/vest?style=for-the-badge)](https://bundlephobia.com/result?p=vest)
183
+
184
+ ```typescript jsx
185
+ import * as React from 'react';
186
+ import { useForm } from 'react-hook-form';
187
+ import { vestResolver } from '@hookform/resolvers/vest';
188
+ import vest, { test, enforce } from 'vest';
189
+
190
+ const validationSuite = vest.create((data = {}) => {
191
+ test('username', 'Username is required', () => {
192
+ enforce(data.username).isNotEmpty();
193
+ });
129
194
 
195
+ test('username', 'Must be longer than 3 chars', () => {
196
+ enforce(data.username).longerThan(3);
197
+ });
198
+
199
+ test('password', 'Password is required', () => {
200
+ enforce(data.password).isNotEmpty();
201
+ });
202
+
203
+ test('password', 'Password must be at least 5 chars', () => {
204
+ enforce(data.password).longerThanOrEquals(5);
205
+ });
206
+
207
+ test('password', 'Password must contain a digit', () => {
208
+ enforce(data.password).matches(/[0-9]/);
209
+ });
210
+
211
+ test('password', 'Password must contain a symbol', () => {
212
+ enforce(data.password).matches(/[^A-Za-z0-9]/);
213
+ });
214
+ });
215
+
216
+ const App = () => {
217
+ const { register, handleSubmit, errors } = useForm({
218
+ resolver: vestResolver(validationSuite),
219
+ });
220
+
221
+ return (
222
+ <form onSubmit={handleSubmit((data) => console.log(data))}>
223
+ <input type="text" name="username" ref={register} />
224
+ <input type="text" name="password" ref={register} />
130
225
  <input type="submit" />
131
226
  </form>
132
227
  );
133
228
  };
229
+
230
+ export default App;
134
231
  ```
135
232
 
136
233
  ## Backers
@@ -0,0 +1 @@
1
+ export * from './toNestError';
@@ -0,0 +1,2 @@
1
+ var r=require("react-hook-form");exports.toNestError=function(e,t){var o={};for(var a in e){var f=r.get(t,a);r.set(o,a,Object.assign(e[a],{ref:f&&f.ref}))}return o};
2
+ //# sourceMappingURL=resolvers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvers.js","sources":["../src/toNestError.ts"],"sourcesContent":["import {\n set,\n get,\n FieldError,\n FieldErrors,\n Field,\n InternalFieldName,\n} from 'react-hook-form';\n\nexport const toNestError = <TFieldValues>(\n errors: Record<string, FieldError>,\n fields: Record<InternalFieldName, Field['_f']>,\n): FieldErrors<TFieldValues> => {\n const fieldErrors: FieldErrors<TFieldValues> = {};\n for (const path in errors) {\n const field = get(fields, path) as Field['_f'] | undefined;\n set(\n fieldErrors,\n path,\n Object.assign(errors[path], { ref: field && field.ref }),\n );\n }\n\n return fieldErrors;\n};\n"],"names":["errors","fields","fieldErrors","path","field","get","set","Object","assign","ref"],"mappings":"qDAS2B,SACzBA,EACAC,GAEA,IAAMC,EAAyC,GAC/C,IAAK,IAAMC,KAAQH,EAAQ,CACzB,IAAMI,EAAQC,MAAIJ,EAAQE,GAC1BG,MACEJ,EACAC,EACAI,OAAOC,OAAOR,EAAOG,GAAO,CAAEM,IAAKL,GAASA,EAAMK,OAItD,OAAOP"}
@@ -0,0 +1,2 @@
1
+ import{get as r,set as o}from"react-hook-form";var a=function(a,e){var f={};for(var t in a){var n=r(e,t);o(f,t,Object.assign(a[t],{ref:n&&n.ref}))}return f};export{a as toNestError};
2
+ //# sourceMappingURL=resolvers.module.js.map
@@ -0,0 +1,2 @@
1
+ import{get as o,set as r}from"react-hook-form";const t=(t,n)=>{const c={};for(const e in t){const s=o(n,e);r(c,e,Object.assign(t[e],{ref:s&&s.ref}))}return c};export{t as toNestError};
2
+ //# sourceMappingURL=resolvers.modern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvers.modern.js","sources":["../src/toNestError.ts"],"sourcesContent":["import {\n set,\n get,\n FieldError,\n FieldErrors,\n Field,\n InternalFieldName,\n} from 'react-hook-form';\n\nexport const toNestError = <TFieldValues>(\n errors: Record<string, FieldError>,\n fields: Record<InternalFieldName, Field['_f']>,\n): FieldErrors<TFieldValues> => {\n const fieldErrors: FieldErrors<TFieldValues> = {};\n for (const path in errors) {\n const field = get(fields, path) as Field['_f'] | undefined;\n set(\n fieldErrors,\n path,\n Object.assign(errors[path], { ref: field && field.ref }),\n );\n }\n\n return fieldErrors;\n};\n"],"names":["toNestError","errors","fields","fieldErrors","path","field","get","set","Object","assign","ref"],"mappings":"+CASaA,MAAAA,EAAc,CACzBC,EACAC,KAEA,MAAMC,EAAyC,GAC/C,IAAK,MAAMC,KAAQH,EAAQ,CACzB,MAAMI,EAAQC,EAAIJ,EAAQE,GAC1BG,EACEJ,EACAC,EACAI,OAAOC,OAAOR,EAAOG,GAAO,CAAEM,IAAKL,GAASA,EAAMK,OAItD,OAAOP"}
@@ -0,0 +1,2 @@
1
+ import{get as r,set as o}from"react-hook-form";var a=function(a,e){var f={};for(var t in a){var n=r(e,t);o(f,t,Object.assign(a[t],{ref:n&&n.ref}))}return f};export{a as toNestError};
2
+ //# sourceMappingURL=resolvers.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvers.module.js","sources":["../src/toNestError.ts"],"sourcesContent":["import {\n set,\n get,\n FieldError,\n FieldErrors,\n Field,\n InternalFieldName,\n} from 'react-hook-form';\n\nexport const toNestError = <TFieldValues>(\n errors: Record<string, FieldError>,\n fields: Record<InternalFieldName, Field['_f']>,\n): FieldErrors<TFieldValues> => {\n const fieldErrors: FieldErrors<TFieldValues> = {};\n for (const path in errors) {\n const field = get(fields, path) as Field['_f'] | undefined;\n set(\n fieldErrors,\n path,\n Object.assign(errors[path], { ref: field && field.ref }),\n );\n }\n\n return fieldErrors;\n};\n"],"names":["toNestError","errors","fields","fieldErrors","path","field","get","set","Object","assign","ref"],"mappings":"+CASaA,IAAAA,EAAc,SACzBC,EACAC,GAEA,IAAMC,EAAyC,GAC/C,IAAK,IAAMC,KAAQH,EAAQ,CACzB,IAAMI,EAAQC,EAAIJ,EAAQE,GAC1BG,EACEJ,EACAC,EACAI,OAAOC,OAAOR,EAAOG,GAAO,CAAEM,IAAKL,GAASA,EAAMK,OAItD,OAAOP"}
@@ -0,0 +1,2 @@
1
+ !function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("react-hook-form")):"function"==typeof define&&define.amd?define(["exports","react-hook-form"],o):o((e||self).hookformResolvers={},e.reactHookForm)}(this,function(e,o){e.toNestError=function(e,r){var t={};for(var f in e){var n=o.get(r,f);o.set(t,f,Object.assign(e[f],{ref:n&&n.ref}))}return t}});
2
+ //# sourceMappingURL=resolvers.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolvers.umd.js","sources":["../src/toNestError.ts"],"sourcesContent":["import {\n set,\n get,\n FieldError,\n FieldErrors,\n Field,\n InternalFieldName,\n} from 'react-hook-form';\n\nexport const toNestError = <TFieldValues>(\n errors: Record<string, FieldError>,\n fields: Record<InternalFieldName, Field['_f']>,\n): FieldErrors<TFieldValues> => {\n const fieldErrors: FieldErrors<TFieldValues> = {};\n for (const path in errors) {\n const field = get(fields, path) as Field['_f'] | undefined;\n set(\n fieldErrors,\n path,\n Object.assign(errors[path], { ref: field && field.ref }),\n );\n }\n\n return fieldErrors;\n};\n"],"names":["errors","fields","fieldErrors","path","field","get","set","Object","assign","ref"],"mappings":"wTAS2B,SACzBA,EACAC,GAEA,IAAMC,EAAyC,GAC/C,IAAK,IAAMC,KAAQH,EAAQ,CACzB,IAAMI,EAAQC,MAAIJ,EAAQE,GAC1BG,MACEJ,EACAC,EACAI,OAAOC,OAAOR,EAAOG,GAAO,CAAEM,IAAKL,GAASA,EAAMK,OAItD,OAAOP"}
@@ -0,0 +1,2 @@
1
+ import { FieldError, FieldErrors, Field, InternalFieldName } from 'react-hook-form';
2
+ export declare const toNestError: <TFieldValues>(errors: Record<string, FieldError>, fields: Record<InternalFieldName, Field['_f']>) => import("react-hook-form").DeepMap<TFieldValues, FieldError>;
@@ -0,0 +1,2 @@
1
+ export * from './joi';
2
+ export * from './types';
@@ -0,0 +1,2 @@
1
+ import { Resolver } from './types';
2
+ export declare const joiResolver: Resolver;
@@ -0,0 +1,2 @@
1
+ var e=require("react-hook-form"),r=require("@hookform/resolvers");exports.joiResolver=function(t,n,o){return void 0===n&&(n={abortEarly:!1}),void 0===o&&(o={}),function(i,a,s){try{var u=function(){return{values:v.error?{}:v.value,errors:v.error?r.toNestError((t=v.error,n="all"===s.criteriaMode,t.details.length?t.details.reduce(function(r,t){var o=t.path.join(".");return r[o]||(r[o]={message:t.message,type:t.type}),n&&(r[o]=e.appendErrors(o,n,r,t.type,t.message)),r},{}):{}),s.fields):{}};var t,n},c=Object.assign({},n,{context:a}),v={},l=function(){if("sync"===o.mode)v=t.validate(i,c);else{var e=function(e,r){try{var t=e()}catch(e){return r(e)}return t&&t.then?t.then(void 0,r):t}(function(){return Promise.resolve(t.validateAsync(i,c)).then(function(e){v.value=e})},function(e){v.error=e});if(e&&e.then)return e.then(function(){})}}();return Promise.resolve(l&&l.then?l.then(u):u())}catch(e){return Promise.reject(e)}}};
2
+ //# sourceMappingURL=joi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"joi.js","sources":["../src/joi.ts"],"sourcesContent":["import { appendErrors, FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\nimport type { ValidationError } from 'joi';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (\n error: ValidationError,\n validateAllFieldCriteria: boolean,\n) =>\n error.details.length\n ? error.details.reduce<Record<string, FieldError>>((previous, error) => {\n const _path = error.path.join('.');\n\n if (!previous[_path]) {\n previous[_path] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n previous[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n previous,\n error.type,\n error.message,\n ) as FieldError;\n }\n\n return previous;\n }, {})\n : {};\n\nexport const joiResolver: Resolver = (\n schema,\n schemaOptions = {\n abortEarly: false,\n },\n resolverOptions = {},\n) => async (values, context, options) => {\n const _schemaOptions = Object.assign({}, schemaOptions, {\n context,\n });\n\n let result: Record<string, any> = {};\n if (resolverOptions.mode === 'sync') {\n result = schema.validate(values, _schemaOptions);\n } else {\n try {\n result.value = await schema.validateAsync(values, _schemaOptions);\n } catch (e) {\n result.error = e;\n }\n }\n\n return {\n values: result.error ? {} : result.value,\n errors: result.error\n ? toNestError(\n parseErrorSchema(result.error, options.criteriaMode === 'all'),\n options.fields,\n )\n : {},\n };\n};\n"],"names":["schema","schemaOptions","resolverOptions","abortEarly","values","context","options","result","error","value","errors","toNestError","validateAllFieldCriteria","criteriaMode","details","length","reduce","previous","_path","path","join","message","type","appendErrors","fields","_schemaOptions","Object","assign","mode","validate","validateAsync","e"],"mappings":"sFA+BqC,SACnCA,EACAC,EAGAC,mBAHAD,IAAAA,EAAgB,CACdE,YAAY,aAEdD,IAAAA,EAAkB,aACRE,EAAQC,EAASC,wBAgB3B,MAAO,CACLF,OAAQG,EAAOC,MAAQ,GAAKD,EAAOE,MACnCC,OAAQH,EAAOC,MACXG,eAlDNH,EAmDyBD,EAAOC,MAlDhCI,EAkDgE,QAAzBN,EAAQO,aAhD/CL,EAAMM,QAAQC,OACVP,EAAMM,QAAQE,OAAmC,SAACC,EAAUT,GAC1D,IAAMU,EAAQV,EAAMW,KAAKC,KAAK,KAgB9B,OAdKH,EAASC,KACZD,EAASC,GAAS,CAAEG,QAASb,EAAMa,QAASC,KAAMd,EAAMc,OAGtDV,IACFK,EAASC,GAASK,eAChBL,EACAN,EACAK,EACAT,EAAMc,KACNd,EAAMa,UAIHJ,GACN,IACH,IA6BIX,EAAQkB,QAEV,IAvDiB,IACvBhB,EACAI,GA+BMa,EAAiBC,OAAOC,OAAO,GAAI1B,EAAe,CACtDI,QAAAA,IAGEE,EAA8B,mBACL,SAAzBL,EAAgB0B,KAClBrB,EAASP,EAAO6B,SAASzB,EAAQqB,sIAGVzB,EAAO8B,cAAc1B,EAAQqB,qBAAlDlB,EAAOE,oBACAsB,GACPxB,EAAOC,MAAQuB,iGAlBgB"}
@@ -0,0 +1,2 @@
1
+ import{appendErrors as r}from"react-hook-form";import{toNestError as e}from"@hookform/resolvers";var t=function(t,n,o){return void 0===n&&(n={abortEarly:!1}),void 0===o&&(o={}),function(i,a,s){try{var u=function(){return{values:f.error?{}:f.value,errors:f.error?e((t=f.error,n="all"===s.criteriaMode,t.details.length?t.details.reduce(function(e,t){var o=t.path.join(".");return e[o]||(e[o]={message:t.message,type:t.type}),n&&(e[o]=r(o,n,e,t.type,t.message)),e},{}):{}),s.fields):{}};var t,n},c=Object.assign({},n,{context:a}),f={},v=function(){if("sync"===o.mode)f=t.validate(i,c);else{var r=function(r,e){try{var t=r()}catch(r){return e(r)}return t&&t.then?t.then(void 0,e):t}(function(){return Promise.resolve(t.validateAsync(i,c)).then(function(r){f.value=r})},function(r){f.error=r});if(r&&r.then)return r.then(function(){})}}();return Promise.resolve(v&&v.then?v.then(u):u())}catch(r){return Promise.reject(r)}}};export{t as joiResolver};
2
+ //# sourceMappingURL=joi.module.js.map
@@ -0,0 +1,2 @@
1
+ import{appendErrors as e}from"react-hook-form";import{toNestError as r}from"@hookform/resolvers";const t=(t,o={abortEarly:!1},a={})=>async(s,l,c)=>{const i=Object.assign({},o,{context:l});let n={};if("sync"===a.mode)n=t.validate(s,i);else try{n.value=await t.validateAsync(s,i)}catch(e){n.error=e}return{values:n.error?{}:n.value,errors:n.error?r((m=n.error,d="all"===c.criteriaMode,m.details.length?m.details.reduce((r,t)=>{const o=t.path.join(".");return r[o]||(r[o]={message:t.message,type:t.type}),d&&(r[o]=e(o,d,r,t.type,t.message)),r},{}):{}),c.fields):{}};var m,d};export{t as joiResolver};
2
+ //# sourceMappingURL=joi.modern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"joi.modern.js","sources":["../src/joi.ts"],"sourcesContent":["import { appendErrors, FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\nimport type { ValidationError } from 'joi';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (\n error: ValidationError,\n validateAllFieldCriteria: boolean,\n) =>\n error.details.length\n ? error.details.reduce<Record<string, FieldError>>((previous, error) => {\n const _path = error.path.join('.');\n\n if (!previous[_path]) {\n previous[_path] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n previous[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n previous,\n error.type,\n error.message,\n ) as FieldError;\n }\n\n return previous;\n }, {})\n : {};\n\nexport const joiResolver: Resolver = (\n schema,\n schemaOptions = {\n abortEarly: false,\n },\n resolverOptions = {},\n) => async (values, context, options) => {\n const _schemaOptions = Object.assign({}, schemaOptions, {\n context,\n });\n\n let result: Record<string, any> = {};\n if (resolverOptions.mode === 'sync') {\n result = schema.validate(values, _schemaOptions);\n } else {\n try {\n result.value = await schema.validateAsync(values, _schemaOptions);\n } catch (e) {\n result.error = e;\n }\n }\n\n return {\n values: result.error ? {} : result.value,\n errors: result.error\n ? toNestError(\n parseErrorSchema(result.error, options.criteriaMode === 'all'),\n options.fields,\n )\n : {},\n };\n};\n"],"names":["joiResolver","schema","schemaOptions","abortEarly","resolverOptions","async","values","context","options","_schemaOptions","Object","assign","result","mode","validate","value","validateAsync","e","error","errors","toNestError","validateAllFieldCriteria","criteriaMode","details","length","reduce","previous","_path","path","join","message","type","appendErrors","fields"],"mappings":"iGAKA,MA0BaA,EAAwB,CACnCC,EACAC,EAAgB,CACdC,YAAY,GAEdC,EAAkB,KACfC,MAAOC,EAAQC,EAASC,KAC3B,MAAMC,EAAiBC,OAAOC,OAAO,GAAIT,EAAe,CACtDK,QAAAA,IAGF,IAAIK,EAA8B,GAClC,GAA6B,SAAzBR,EAAgBS,KAClBD,EAASX,EAAOa,SAASR,EAAQG,QAEjC,IACEG,EAAOG,YAAcd,EAAOe,cAAcV,EAAQG,GAClD,MAAOQ,GACPL,EAAOM,MAAQD,EAInB,MAAO,CACLX,OAAQM,EAAOM,MAAQ,GAAKN,EAAOG,MACnCI,OAAQP,EAAOM,MACXE,GAlDNF,EAmDyBN,EAAOM,MAlDhCG,EAkDgE,QAAzBb,EAAQc,aAhD/CJ,EAAMK,QAAQC,OACVN,EAAMK,QAAQE,OAAmC,CAACC,EAAUR,KAC1D,MAAMS,EAAQT,EAAMU,KAAKC,KAAK,KAgB9B,OAdKH,EAASC,KACZD,EAASC,GAAS,CAAEG,QAASZ,EAAMY,QAASC,KAAMb,EAAMa,OAGtDV,IACFK,EAASC,GAASK,EAChBL,EACAN,EACAK,EACAR,EAAMa,KACNb,EAAMY,UAIHJ,GACN,IACH,IA6BIlB,EAAQyB,QAEV,IAvDiB,IACvBf,EACAG"}
@@ -0,0 +1,2 @@
1
+ import{appendErrors as r}from"react-hook-form";import{toNestError as e}from"@hookform/resolvers";var t=function(t,n,o){return void 0===n&&(n={abortEarly:!1}),void 0===o&&(o={}),function(i,a,s){try{var u=function(){return{values:f.error?{}:f.value,errors:f.error?e((t=f.error,n="all"===s.criteriaMode,t.details.length?t.details.reduce(function(e,t){var o=t.path.join(".");return e[o]||(e[o]={message:t.message,type:t.type}),n&&(e[o]=r(o,n,e,t.type,t.message)),e},{}):{}),s.fields):{}};var t,n},c=Object.assign({},n,{context:a}),f={},v=function(){if("sync"===o.mode)f=t.validate(i,c);else{var r=function(r,e){try{var t=r()}catch(r){return e(r)}return t&&t.then?t.then(void 0,e):t}(function(){return Promise.resolve(t.validateAsync(i,c)).then(function(r){f.value=r})},function(r){f.error=r});if(r&&r.then)return r.then(function(){})}}();return Promise.resolve(v&&v.then?v.then(u):u())}catch(r){return Promise.reject(r)}}};export{t as joiResolver};
2
+ //# sourceMappingURL=joi.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"joi.module.js","sources":["../src/joi.ts"],"sourcesContent":["import { appendErrors, FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\nimport type { ValidationError } from 'joi';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (\n error: ValidationError,\n validateAllFieldCriteria: boolean,\n) =>\n error.details.length\n ? error.details.reduce<Record<string, FieldError>>((previous, error) => {\n const _path = error.path.join('.');\n\n if (!previous[_path]) {\n previous[_path] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n previous[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n previous,\n error.type,\n error.message,\n ) as FieldError;\n }\n\n return previous;\n }, {})\n : {};\n\nexport const joiResolver: Resolver = (\n schema,\n schemaOptions = {\n abortEarly: false,\n },\n resolverOptions = {},\n) => async (values, context, options) => {\n const _schemaOptions = Object.assign({}, schemaOptions, {\n context,\n });\n\n let result: Record<string, any> = {};\n if (resolverOptions.mode === 'sync') {\n result = schema.validate(values, _schemaOptions);\n } else {\n try {\n result.value = await schema.validateAsync(values, _schemaOptions);\n } catch (e) {\n result.error = e;\n }\n }\n\n return {\n values: result.error ? {} : result.value,\n errors: result.error\n ? toNestError(\n parseErrorSchema(result.error, options.criteriaMode === 'all'),\n options.fields,\n )\n : {},\n };\n};\n"],"names":["joiResolver","schema","schemaOptions","resolverOptions","abortEarly","values","context","options","result","error","value","errors","toNestError","validateAllFieldCriteria","criteriaMode","details","length","reduce","previous","_path","path","join","message","type","appendErrors","fields","_schemaOptions","Object","assign","mode","validate","validateAsync","e"],"mappings":"iGAKA,IA0BaA,EAAwB,SACnCC,EACAC,EAGAC,mBAHAD,IAAAA,EAAgB,CACdE,YAAY,aAEdD,IAAAA,EAAkB,aACRE,EAAQC,EAASC,wBAgB3B,MAAO,CACLF,OAAQG,EAAOC,MAAQ,GAAKD,EAAOE,MACnCC,OAAQH,EAAOC,MACXG,GAlDNH,EAmDyBD,EAAOC,MAlDhCI,EAkDgE,QAAzBN,EAAQO,aAhD/CL,EAAMM,QAAQC,OACVP,EAAMM,QAAQE,OAAmC,SAACC,EAAUT,GAC1D,IAAMU,EAAQV,EAAMW,KAAKC,KAAK,KAgB9B,OAdKH,EAASC,KACZD,EAASC,GAAS,CAAEG,QAASb,EAAMa,QAASC,KAAMd,EAAMc,OAGtDV,IACFK,EAASC,GAASK,EAChBL,EACAN,EACAK,EACAT,EAAMc,KACNd,EAAMa,UAIHJ,GACN,IACH,IA6BIX,EAAQkB,QAEV,IAvDiB,IACvBhB,EACAI,GA+BMa,EAAiBC,OAAOC,OAAO,GAAI1B,EAAe,CACtDI,QAAAA,IAGEE,EAA8B,mBACL,SAAzBL,EAAgB0B,KAClBrB,EAASP,EAAO6B,SAASzB,EAAQqB,sIAGVzB,EAAO8B,cAAc1B,EAAQqB,qBAAlDlB,EAAOE,oBACAsB,GACPxB,EAAOC,MAAQuB,iGAlBgB"}
@@ -0,0 +1,2 @@
1
+ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("react-hook-form"),require("@hookform/resolvers")):"function"==typeof define&&define.amd?define(["exports","react-hook-form","@hookform/resolvers"],r):r((e||self).hookformResolversJoi={},e.reactHookForm,e.hookformResolvers)}(this,function(e,r,o){e.joiResolver=function(e,t,n){return void 0===t&&(t={abortEarly:!1}),void 0===n&&(n={}),function(i,s,a){try{var f=function(){return{values:c.error?{}:c.value,errors:c.error?o.toNestError((e=c.error,t="all"===a.criteriaMode,e.details.length?e.details.reduce(function(e,o){var n=o.path.join(".");return e[n]||(e[n]={message:o.message,type:o.type}),t&&(e[n]=r.appendErrors(n,t,e,o.type,o.message)),e},{}):{}),a.fields):{}};var e,t},u=Object.assign({},t,{context:s}),c={},l=function(){if("sync"===n.mode)c=e.validate(i,u);else{var r=function(e,r){try{var o=e()}catch(e){return r(e)}return o&&o.then?o.then(void 0,r):o}(function(){return Promise.resolve(e.validateAsync(i,u)).then(function(e){c.value=e})},function(e){c.error=e});if(r&&r.then)return r.then(function(){})}}();return Promise.resolve(l&&l.then?l.then(f):f())}catch(e){return Promise.reject(e)}}}});
2
+ //# sourceMappingURL=joi.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"joi.umd.js","sources":["../src/joi.ts"],"sourcesContent":["import { appendErrors, FieldError } from 'react-hook-form';\nimport { toNestError } from '@hookform/resolvers';\nimport type { ValidationError } from 'joi';\nimport { Resolver } from './types';\n\nconst parseErrorSchema = (\n error: ValidationError,\n validateAllFieldCriteria: boolean,\n) =>\n error.details.length\n ? error.details.reduce<Record<string, FieldError>>((previous, error) => {\n const _path = error.path.join('.');\n\n if (!previous[_path]) {\n previous[_path] = { message: error.message, type: error.type };\n }\n\n if (validateAllFieldCriteria) {\n previous[_path] = appendErrors(\n _path,\n validateAllFieldCriteria,\n previous,\n error.type,\n error.message,\n ) as FieldError;\n }\n\n return previous;\n }, {})\n : {};\n\nexport const joiResolver: Resolver = (\n schema,\n schemaOptions = {\n abortEarly: false,\n },\n resolverOptions = {},\n) => async (values, context, options) => {\n const _schemaOptions = Object.assign({}, schemaOptions, {\n context,\n });\n\n let result: Record<string, any> = {};\n if (resolverOptions.mode === 'sync') {\n result = schema.validate(values, _schemaOptions);\n } else {\n try {\n result.value = await schema.validateAsync(values, _schemaOptions);\n } catch (e) {\n result.error = e;\n }\n }\n\n return {\n values: result.error ? {} : result.value,\n errors: result.error\n ? toNestError(\n parseErrorSchema(result.error, options.criteriaMode === 'all'),\n options.fields,\n )\n : {},\n };\n};\n"],"names":["schema","schemaOptions","resolverOptions","abortEarly","values","context","options","result","error","value","errors","toNestError","validateAllFieldCriteria","criteriaMode","details","length","reduce","previous","_path","path","join","message","type","appendErrors","fields","_schemaOptions","Object","assign","mode","validate","validateAsync","e"],"mappings":"sYA+BqC,SACnCA,EACAC,EAGAC,mBAHAD,IAAAA,EAAgB,CACdE,YAAY,aAEdD,IAAAA,EAAkB,aACRE,EAAQC,EAASC,wBAgB3B,MAAO,CACLF,OAAQG,EAAOC,MAAQ,GAAKD,EAAOE,MACnCC,OAAQH,EAAOC,MACXG,eAlDNH,EAmDyBD,EAAOC,MAlDhCI,EAkDgE,QAAzBN,EAAQO,aAhD/CL,EAAMM,QAAQC,OACVP,EAAMM,QAAQE,OAAmC,SAACC,EAAUT,GAC1D,IAAMU,EAAQV,EAAMW,KAAKC,KAAK,KAgB9B,OAdKH,EAASC,KACZD,EAASC,GAAS,CAAEG,QAASb,EAAMa,QAASC,KAAMd,EAAMc,OAGtDV,IACFK,EAASC,GAASK,eAChBL,EACAN,EACAK,EACAT,EAAMc,KACNd,EAAMa,UAIHJ,GACN,IACH,IA6BIX,EAAQkB,QAEV,IAvDiB,IACvBhB,EACAI,GA+BMa,EAAiBC,OAAOC,OAAO,GAAI1B,EAAe,CACtDI,QAAAA,IAGEE,EAA8B,mBACL,SAAzBL,EAAgB0B,KAClBrB,EAASP,EAAO6B,SAASzB,EAAQqB,sIAGVzB,EAAO8B,cAAc1B,EAAQqB,qBAAlDlB,EAAOE,oBACAsB,GACPxB,EAAOC,MAAQuB,iGAlBgB"}
@@ -0,0 +1,5 @@
1
+ import { FieldValues, ResolverOptions, ResolverResult, UnpackNestedValue } from 'react-hook-form';
2
+ import type { AsyncValidationOptions, Schema } from 'joi';
3
+ export declare type Resolver = <T extends Schema>(schema: T, schemaOptions?: AsyncValidationOptions, factoryOptions?: {
4
+ mode?: 'async' | 'sync';
5
+ }) => <TFieldValues extends FieldValues, TContext>(values: UnpackNestedValue<TFieldValues>, context: TContext | undefined, options: ResolverOptions<TFieldValues>) => Promise<ResolverResult<TFieldValues>>;
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "joi",
3
+ "amdName": "hookformResolversJoi",
4
+ "version": "1.0.0",
5
+ "private": true,
6
+ "description": "React Hook Form validation resolver: joi",
7
+ "main": "dist/joi.js",
8
+ "module": "dist/joi.module.js",
9
+ "umd:main": "dist/joi.umd.js",
10
+ "source": "src/index.ts",
11
+ "types": "dist/index.d.ts",
12
+ "license": "MIT",
13
+ "peerDependencies": {
14
+ "react-hook-form": ">=6.6.0",
15
+ "@hookform/resolvers": "^1.0.0"
16
+ }
17
+ }
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { render, screen, act } from '@testing-library/react';
3
+ import user from '@testing-library/user-event';
4
+ import { useForm } from 'react-hook-form';
5
+ import * as Joi from 'joi';
6
+ import { joiResolver } from '..';
7
+
8
+ const schema = Joi.object({
9
+ username: Joi.string().required(),
10
+ password: Joi.string().required(),
11
+ });
12
+
13
+ interface FormData {
14
+ username: string;
15
+ password: string;
16
+ }
17
+
18
+ interface Props {
19
+ onSubmit: (data: FormData) => void;
20
+ }
21
+
22
+ function TestComponent({ onSubmit }: Props) {
23
+ const {
24
+ register,
25
+ formState: { errors },
26
+ handleSubmit,
27
+ } = useForm<FormData>({
28
+ resolver: joiResolver(schema), // Useful to check TypeScript regressions
29
+ });
30
+
31
+ return (
32
+ <form onSubmit={handleSubmit(onSubmit)}>
33
+ <input {...register('username')} />
34
+ {errors.username && <span role="alert">{errors.username.message}</span>}
35
+
36
+ <input {...register('password')} />
37
+ {errors.password && <span role="alert">{errors.password.message}</span>}
38
+
39
+ <button type="submit">submit</button>
40
+ </form>
41
+ );
42
+ }
43
+
44
+ test("form's validation with Joi and TypeScript's integration", async () => {
45
+ const handleSubmit = jest.fn();
46
+ render(<TestComponent onSubmit={handleSubmit} />);
47
+
48
+ expect(screen.queryAllByRole(/alert/i)).toHaveLength(0);
49
+
50
+ await act(async () => {
51
+ user.click(screen.getByText(/submit/i));
52
+ });
53
+
54
+ expect(
55
+ screen.getByText(/"username" is not allowed to be empty/i),
56
+ ).toBeInTheDocument();
57
+ expect(
58
+ screen.getByText(/"password" is not allowed to be empty/i),
59
+ ).toBeInTheDocument();
60
+ expect(handleSubmit).not.toHaveBeenCalled();
61
+ });
@@ -0,0 +1,76 @@
1
+ import * as Joi from 'joi';
2
+
3
+ import { Field, InternalFieldName } from 'react-hook-form';
4
+
5
+ export const schema = Joi.object({
6
+ username: Joi.string().alphanum().min(3).max(30).required(),
7
+ password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required(),
8
+ repeatPassword: Joi.ref('password'),
9
+ accessToken: [Joi.string(), Joi.number()],
10
+ birthYear: Joi.number().integer().min(1900).max(2013),
11
+ email: Joi.string().email({
12
+ minDomainSegments: 2,
13
+ tlds: { allow: ['com', 'net'] },
14
+ }),
15
+ tags: Joi.array().items(Joi.string()).required(),
16
+ enabled: Joi.boolean().required(),
17
+ like: Joi.array()
18
+ .items(
19
+ Joi.object({ id: Joi.number(), name: Joi.string().length(4).regex(/a/) }),
20
+ )
21
+ .optional(),
22
+ });
23
+
24
+ interface Data {
25
+ username: string;
26
+ password: string;
27
+ repeatPassword: string;
28
+ accessToken?: number | string;
29
+ birthYear?: number;
30
+ email?: string;
31
+ tags: string[];
32
+ enabled: boolean;
33
+ like: { id: number; name: string }[];
34
+ }
35
+
36
+ export const validData: Data = {
37
+ username: 'Doe',
38
+ password: 'Password123',
39
+ repeatPassword: 'Password123',
40
+ birthYear: 2000,
41
+ email: 'john@doe.com',
42
+ tags: ['tag1', 'tag2'],
43
+ enabled: true,
44
+ like: [
45
+ {
46
+ id: 1,
47
+ name: 'name',
48
+ },
49
+ ],
50
+ };
51
+
52
+ export const invalidData = {
53
+ password: '___',
54
+ email: '',
55
+ birthYear: 'birthYear',
56
+ like: [{ id: 'z', name: 'r' }],
57
+ };
58
+
59
+ export const fields: Record<InternalFieldName, Field['_f']> = {
60
+ username: {
61
+ ref: { name: 'username' },
62
+ name: 'username',
63
+ },
64
+ password: {
65
+ ref: { name: 'password' },
66
+ name: 'password',
67
+ },
68
+ email: {
69
+ ref: { name: 'email' },
70
+ name: 'email',
71
+ },
72
+ birthday: {
73
+ ref: { name: 'birthday' },
74
+ name: 'birthday',
75
+ },
76
+ };