@paragrav/rhf-utils 0.0.134 → 0.0.135
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.
- package/LICENSE.md +21 -0
- package/README.md +46 -29
- package/dist/esm/client/utils.mjs +21 -12
- package/dist/esm/client/utils.mjs.map +1 -1
- package/dist/types/form/RhfUtilsFormOptions.d.ts +1 -1
- package/package.json +5 -1
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-present paragrav.dev
|
|
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
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Integration and utility library for [react-hook-form](https://www.react-hook-form.com/).
|
|
6
6
|
|
|
7
|
-
If you have multiple forms and would like a more declarative API to manage their behavior
|
|
7
|
+
If you have multiple forms and would like a more declarative API to manage their behavior via global and form-level options, including transformation of backend errors, and debugging field errors.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -18,11 +18,11 @@ If you have multiple forms and would like a more declarative API to manage their
|
|
|
18
18
|
- schema-typed Controller component
|
|
19
19
|
- schema-typed FormSubmitError class
|
|
20
20
|
- throw in submit handler to add errors to RHF context and fail submit
|
|
21
|
-
- flatter `FieldErrors` structure (`FlatFieldErrors`)
|
|
22
|
-
-
|
|
23
|
-
- `FormErrorMessageByPath` for displaying error message
|
|
21
|
+
- simpler/flatter `FieldErrors` structure (`FlatFieldErrors`)
|
|
22
|
+
- context groups errors into `all`, `fields`, `roots`, and `orphans` (`useFlatFieldErrorsContext`)
|
|
23
|
+
- `FormErrorMessageByPath` for displaying error message
|
|
24
24
|
- `zod` support
|
|
25
|
-
- including
|
|
25
|
+
- including input/output types for transformations
|
|
26
26
|
- safer `FieldValues` type (`SafeFieldValues`)
|
|
27
27
|
- 3.3kB min+gzip core functionality (excluding [peer dependencies](#peer-dependencies))
|
|
28
28
|
|
|
@@ -221,12 +221,12 @@ Currently, only `zod` is supported.
|
|
|
221
221
|
If you prefer to define your `Children` component as standalone:
|
|
222
222
|
|
|
223
223
|
```tsx
|
|
224
|
-
const Children: RhfUtilsUseFormChildrenZodFC<typeof schema> = ({
|
|
224
|
+
const Children: RhfUtilsUseFormChildrenZodFC<typeof schema> = ({...}) => { };
|
|
225
225
|
|
|
226
226
|
function Children({...}: RhfUtilsUseFormChildrenZodProps<typeof schema>) { }
|
|
227
227
|
```
|
|
228
228
|
|
|
229
|
-
## Component Hierarchy
|
|
229
|
+
## Form Component Hierarchy
|
|
230
230
|
|
|
231
231
|
```tsx
|
|
232
232
|
<ReactHookForm.FormProvider>
|
|
@@ -242,7 +242,7 @@ function Children({...}: RhfUtilsUseFormChildrenZodProps<typeof schema>) { }
|
|
|
242
242
|
|
|
243
243
|
## `FormSubmitError`
|
|
244
244
|
|
|
245
|
-
This is an `Error
|
|
245
|
+
This is an extended `Error` class that you can use to throw a structured error in your submit handler. It uses `FormSubmitErrors` (with an "s") type's structure, which is a flat, simplified version of RHF's `FieldErrors`.
|
|
246
246
|
|
|
247
247
|
It differs from `FlatFieldError` only in that it is narrower. Namely, it is schema-typed, so it allows field names from your schema and `root.${string}` keys. And only `type` (optional) and `message` props for error.
|
|
248
248
|
|
|
@@ -262,7 +262,7 @@ Example:
|
|
|
262
262
|
/>
|
|
263
263
|
```
|
|
264
264
|
|
|
265
|
-
Any non-`FormSubmitError`
|
|
265
|
+
Any non-`FormSubmitError` error thrown from your submit handler (e.g., fetch/axios error) can be transformed by `RhfUtilsClientConfig`'s `onSubmitErrorUnknown` callback. This takes an `unknown` error and can return a `FormSubmitErrors` object, which is merged into RHF's form state errors.
|
|
266
266
|
|
|
267
267
|
Most common use case will be transforming backend errors to frontend shape. (You can use `getOnSubmitTrpcClientErrorHandler` HOF provided by this library for TRPC backends.)
|
|
268
268
|
|
|
@@ -272,7 +272,7 @@ These options can be set globally and/or per form.
|
|
|
272
272
|
|
|
273
273
|
```ts
|
|
274
274
|
type RhfUtilsFormOptions = {
|
|
275
|
-
/** Stop propagation of submit event.
|
|
275
|
+
/** Stop propagation of submit event. */
|
|
276
276
|
stopSubmitPropagation?: boolean;
|
|
277
277
|
|
|
278
278
|
/** Request submit via listener on form change. */
|
|
@@ -293,7 +293,7 @@ type RhfUtilsFormOptions = {
|
|
|
293
293
|
};
|
|
294
294
|
```
|
|
295
295
|
|
|
296
|
-
If you need access to options deeper in component structure, use `useRhfUtilsContext` to receive `RhfUtilsContext` object, which includes `formId`, `formRef`, and `options` settings.
|
|
296
|
+
If you need access to options deeper in your component structure, use `useRhfUtilsContext` to receive `RhfUtilsContext` object, which includes `formId`, `formRef`, and `options` settings.
|
|
297
297
|
|
|
298
298
|
Use `useRhfUtilsContextRequestSubmit` hook to get `requestSubmit` function for current form ref in context. This is useful when you need to trigger form submission programatically.
|
|
299
299
|
|
|
@@ -314,39 +314,56 @@ declare module '@paragrav/rhf-utils' {
|
|
|
314
314
|
}
|
|
315
315
|
```
|
|
316
316
|
|
|
317
|
-
## Errors
|
|
317
|
+
## Field Errors
|
|
318
318
|
|
|
319
|
-
|
|
319
|
+
### FlatFieldErrors
|
|
320
|
+
|
|
321
|
+
`FlatFieldErrors` type is a flattened, simplified version of RHF's `FieldErrors`. Keys represent flattened, dot-notation field paths.
|
|
320
322
|
|
|
321
323
|
Use `useFlatFieldErrorsContext()` hook, which returns an object with errors grouped by `all`, `fields`, `roots`, `orphans` records, and `hasErrors` and `hasErrors` and `hasOrphans` booleans.
|
|
322
324
|
|
|
323
|
-
|
|
325
|
+
### Output
|
|
326
|
+
|
|
327
|
+
For the purposes of debugging and/or logging, you can configure when form state errors are outputted (i.e., console log and/or thrown) via `RhfUtilsClientConfig` (example at the top).
|
|
324
328
|
|
|
325
329
|
### Orphan Errors
|
|
326
330
|
|
|
327
|
-
The concept of orphan errors is any error that is not being shown to user.
|
|
331
|
+
The concept of "orphan" errors is any field error that is not being shown to user. (For example, a field in a schema that is prohibiting users from submitting a valid form.)
|
|
328
332
|
|
|
329
|
-
|
|
333
|
+
More technically, an "orphan" is any form state error that meets ALL of the following criteria:
|
|
330
334
|
|
|
331
|
-
- not a root error -- e
|
|
332
|
-
-
|
|
333
|
-
- has no
|
|
335
|
+
- not a root error -- i.e., `root` and `root.${string}` paths
|
|
336
|
+
- these are assumed to be listed for users somewhere
|
|
337
|
+
- has no `ref` -- RHF includes `ref` to the associated input for each error (when applicable)
|
|
338
|
+
- these are assumed to be shown next to their input fields
|
|
339
|
+
- has no marker in DOM (i.e., `FormNonFieldErrorMarker`)
|
|
340
|
+
- see section further below for more information
|
|
334
341
|
|
|
335
|
-
|
|
342
|
+
Detected orphans can be accessed via any of the following:
|
|
336
343
|
|
|
337
|
-
|
|
344
|
+
- via `RhfUtilsClientConfig.FieldErrors.output`; e.g.:
|
|
345
|
+
- console log to facilitate debugging on development
|
|
346
|
+
- console error to facilitate reporting on production (via your own error reporting service)
|
|
347
|
+
- throw error for developer during development
|
|
348
|
+
- `useFlatFieldErrorsContext()` hook
|
|
349
|
+
- returns an object with list of errors grouped by `all`, `fields` (with `ref`), `roots`, `orphans` records, and includes computed booleans `hasErrors` and `hasOrphans`
|
|
350
|
+
- boolean value from `useFlatFieldErrorsContextHasOnlyOrphans`
|
|
338
351
|
|
|
339
|
-
|
|
340
|
-
<FormErrorMessageByPath path="street.address" />
|
|
341
|
-
```
|
|
352
|
+
### FormNonFieldErrorMarker
|
|
342
353
|
|
|
343
|
-
|
|
354
|
+
To get accurate orphan detection, you must use `FormNonFieldErrorMarker` in all errors you are displaying to user. (Technically, "root" errors and field errors with a `ref` do not need the marker, because those do not match the first two criteria of an orphan (as listed above). But there is no harm in including it consistently for all error displayed.)
|
|
344
355
|
|
|
345
|
-
|
|
356
|
+
#### When is the marker required?
|
|
346
357
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
358
|
+
For example, a field array with a minimum number of items -- e.g., `items: z.array(...).min(1)`.
|
|
359
|
+
|
|
360
|
+
When there are zero items, the error is not "root" and is not associated with a specific field. Therefore, in order to NOT detect this as an orphan, it must be "marked" as displayed using `FormNonFieldErrorMarker`.
|
|
361
|
+
|
|
362
|
+
You can incorporate `FormNonFieldErrorMarker` into your own component library, or use this library's `FormErrorMessageByPath` to display error message to user, which includes this marker.
|
|
363
|
+
|
|
364
|
+
```tsx
|
|
365
|
+
<FormErrorMessageByPath path="items" />
|
|
366
|
+
```
|
|
350
367
|
|
|
351
368
|
## Form Groups
|
|
352
369
|
|
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
const
|
|
1
|
+
const u = (n, ...r) => (
|
|
2
2
|
// sequentially merge each set of props into defaults
|
|
3
|
-
|
|
4
|
-
(
|
|
5
|
-
var
|
|
3
|
+
r.reduce(
|
|
4
|
+
(t, e) => {
|
|
5
|
+
var s, i, l, m, o, f;
|
|
6
6
|
return {
|
|
7
7
|
rhf: {
|
|
8
|
-
...
|
|
8
|
+
...t == null ? void 0 : t.rhf,
|
|
9
9
|
...e.rhf
|
|
10
10
|
},
|
|
11
11
|
utils: {
|
|
12
|
-
...
|
|
13
|
-
...e.utils
|
|
12
|
+
...t == null ? void 0 : t.utils,
|
|
13
|
+
...e.utils,
|
|
14
|
+
// manual merges
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- false positive
|
|
16
|
+
...(((s = t == null ? void 0 : t.utils) == null ? void 0 : s.resetOnSubmitted) || ((i = e.utils) == null ? void 0 : i.resetOnSubmitted)) && {
|
|
17
|
+
resetOnSubmitted: {
|
|
18
|
+
...(l = t == null ? void 0 : t.utils) == null ? void 0 : l.resetOnSubmitted,
|
|
19
|
+
...(m = e.utils) == null ? void 0 : m.resetOnSubmitted
|
|
20
|
+
}
|
|
21
|
+
}
|
|
14
22
|
},
|
|
15
23
|
form: {
|
|
16
|
-
...
|
|
24
|
+
...t == null ? void 0 : t.form,
|
|
17
25
|
...e.form,
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
// manual merges
|
|
27
|
+
className: [(o = t == null ? void 0 : t.form) == null ? void 0 : o.className, (f = e.form) == null ? void 0 : f.className].filter(Boolean).join(" ") || void 0
|
|
28
|
+
// stringify (replace empty string with undefined)
|
|
20
29
|
}
|
|
21
30
|
};
|
|
22
31
|
},
|
|
23
32
|
// start with defaults
|
|
24
|
-
|
|
33
|
+
n
|
|
25
34
|
)
|
|
26
35
|
);
|
|
27
36
|
export {
|
|
28
|
-
|
|
37
|
+
u as mergeRhfUtilsClientConfigDefaultsWithUseRhfUtilsFormProps
|
|
29
38
|
};
|
|
30
39
|
//# sourceMappingURL=utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","sources":["../../../src/client/utils.ts"],"sourcesContent":["import type { RhfUtilsFormOptions } from '@/form/RhfUtilsFormOptions';\nimport type { SafeFieldValues } from '@/form/SafeFieldValues';\nimport type { UseRhfUtilsFormProps } from '@/form/UseRhfUtilsFormProps';\n\nimport type { RhfUtilsClientConfig } from './config/RhfUtilsClientConfig';\n\n/**\n * Merge {@link RhfUtilsClientConfig.defaults} with array of {@link UseRhfUtilsFormProps}'s overrides.\n */\nexport const mergeRhfUtilsClientConfigDefaultsWithUseRhfUtilsFormProps = <\n TFieldValues extends SafeFieldValues,\n TTransformedValues extends SafeFieldValues,\n>(\n defaults: RhfUtilsClientConfig['defaults'],\n ...props: Pick<\n UseRhfUtilsFormProps<TFieldValues, TTransformedValues>,\n 'rhf' | 'utils' | 'form'\n >[]\n) =>\n // sequentially merge each set of props into defaults\n props.reduce(\n (acc, curr) => ({\n rhf: {\n ...acc?.rhf,\n ...curr.rhf,\n },\n\n utils: {\n ...acc?.utils,\n ...curr.utils,\n } satisfies RhfUtilsFormOptions,\n\n form: {\n ...acc?.form,\n ...curr.form,\n className
|
|
1
|
+
{"version":3,"file":"utils.mjs","sources":["../../../src/client/utils.ts"],"sourcesContent":["import type { RhfUtilsFormOptions } from '@/form/RhfUtilsFormOptions';\nimport type { SafeFieldValues } from '@/form/SafeFieldValues';\nimport type { UseRhfUtilsFormProps } from '@/form/UseRhfUtilsFormProps';\n\nimport type { RhfUtilsClientConfig } from './config/RhfUtilsClientConfig';\n\n/**\n * Merge {@link RhfUtilsClientConfig.defaults} with array of {@link UseRhfUtilsFormProps}'s overrides.\n */\nexport const mergeRhfUtilsClientConfigDefaultsWithUseRhfUtilsFormProps = <\n TFieldValues extends SafeFieldValues,\n TTransformedValues extends SafeFieldValues,\n>(\n defaults: RhfUtilsClientConfig['defaults'],\n ...props: Pick<\n UseRhfUtilsFormProps<TFieldValues, TTransformedValues>,\n 'rhf' | 'utils' | 'form'\n >[]\n) =>\n // sequentially merge each set of props into defaults\n props.reduce(\n (acc, curr) => ({\n rhf: {\n ...acc?.rhf,\n ...curr.rhf,\n },\n\n utils: {\n ...acc?.utils,\n ...curr.utils,\n\n // manual merges\n\n // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- false positive\n ...((acc?.utils?.resetOnSubmitted || curr.utils?.resetOnSubmitted) && {\n resetOnSubmitted: {\n ...acc?.utils?.resetOnSubmitted,\n ...curr.utils?.resetOnSubmitted,\n },\n }),\n } satisfies RhfUtilsFormOptions,\n\n form: {\n ...acc?.form,\n ...curr.form,\n\n // manual merges\n\n className:\n [acc?.form?.className, curr.form?.className]\n .filter(Boolean) // filter out falsy values\n .join(' ') || undefined, // stringify (replace empty string with undefined)\n },\n }),\n\n // start with defaults\n defaults,\n );\n"],"names":["mergeRhfUtilsClientConfigDefaultsWithUseRhfUtilsFormProps","defaults","props","acc","curr","_a","_b","_c","_d","_e","_f"],"mappings":"AASa,MAAAA,IAA4D,CAIvEC,MACGC;AAAA;AAAA,EAMHA,EAAM;AAAA,IACJ,CAACC,GAAKC,MAAU;AAZP,UAAAC,GAAAC,GAAAC,GAAAC,GAAAC,GAAAC;AAYO;AAAA,QACd,KAAK;AAAA,UACH,GAAGP,KAAA,gBAAAA,EAAK;AAAA,UACR,GAAGC,EAAK;AAAA,QACV;AAAA,QAEA,OAAO;AAAA,UACL,GAAGD,KAAA,gBAAAA,EAAK;AAAA,UACR,GAAGC,EAAK;AAAA;AAAA;AAAA,UAKR,MAAKC,IAAAF,KAAA,gBAAAA,EAAK,UAAL,gBAAAE,EAAY,uBAAoBC,IAAAF,EAAK,UAAL,gBAAAE,EAAY,sBAAqB;AAAA,YACpE,kBAAkB;AAAA,cAChB,IAAGC,IAAAJ,KAAA,gBAAAA,EAAK,UAAL,gBAAAI,EAAY;AAAA,cACf,IAAGC,IAAAJ,EAAK,UAAL,gBAAAI,EAAY;AAAA,YACjB;AAAA,UACF;AAAA,QACF;AAAA,QAEA,MAAM;AAAA,UACJ,GAAGL,KAAA,gBAAAA,EAAK;AAAA,UACR,GAAGC,EAAK;AAAA;AAAA,UAIR,WACE,EAACK,IAAAN,KAAA,gBAAAA,EAAK,SAAL,gBAAAM,EAAW,YAAWC,IAAAN,EAAK,SAAL,gBAAAM,EAAW,SAAS,EACxC,OAAO,OAAO,EACd,KAAK,GAAG,KAAK;AAAA;AAAA,QACpB;AAAA,MAAA;AAAA;AAAA;AAAA,IAIFT;AAAA,EACF;AAAA;"}
|
|
@@ -8,7 +8,7 @@ import { Register } from '../register';
|
|
|
8
8
|
* Can be extended using {@link Register.RhfUtilsFormOptions}.
|
|
9
9
|
*/
|
|
10
10
|
export type RhfUtilsFormOptions = {
|
|
11
|
-
/** Stop propagation of submit event.
|
|
11
|
+
/** Stop propagation of submit event. */
|
|
12
12
|
stopSubmitPropagation?: boolean;
|
|
13
13
|
/** Request submit via listener on form change. */
|
|
14
14
|
submitOnChange?: boolean;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@paragrav/rhf-utils",
|
|
3
3
|
"author": "paragrav.dev",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.135",
|
|
6
6
|
"description": "Integration utilities for react-hook-form.",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"sideEffects": false,
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"react-hook-form": "7",
|
|
31
31
|
"zod": "3"
|
|
32
32
|
},
|
|
33
|
+
"resolutions": {
|
|
34
|
+
"@playwright/experimental-ct-react": "1.44.1",
|
|
35
|
+
"@playwright/test": "1.44.1"
|
|
36
|
+
},
|
|
33
37
|
"repository": {
|
|
34
38
|
"type": "git",
|
|
35
39
|
"url": "git+https://github.com/paragrav/rhf-utils.git"
|