@graphcommerce/react-hook-form 3.0.5 → 3.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Change Log
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#1379](https://github.com/graphcommerce-org/graphcommerce/pull/1379) [`104abd14e`](https://github.com/graphcommerce-org/graphcommerce/commit/104abd14e1585ef0d8de77937d25156b8fa1e201) Thanks [@paales](https://github.com/paales)! - useFormPersist no accepts a persist array to persist values even if they aren’t dirty anymore
8
+
9
+ * [#1379](https://github.com/graphcommerce-org/graphcommerce/pull/1379) [`2a125b1f9`](https://github.com/graphcommerce-org/graphcommerce/commit/2a125b1f98bb9272d96c3577f21d6c984caad892) Thanks [@paales](https://github.com/paales)! - ComposedSubmit uses the result of form.trigger() method to check if fomrs are valid before atempting to submit any form in the composition
10
+
11
+ ## 3.0.7
12
+
13
+ ### Patch Changes
14
+
15
+ - [#1378](https://github.com/graphcommerce-org/graphcommerce/pull/1378) [`22ff9df16`](https://github.com/graphcommerce-org/graphcommerce/commit/22ff9df1677742ae8e07d9b7e5b12fbb487580dc) Thanks [@paales](https://github.com/paales)! - upgrade to latest versions of packages
16
+
17
+ ## 3.0.6
18
+
19
+ ### Patch Changes
20
+
21
+ - [#1369](https://github.com/graphcommerce-org/graphcommerce/pull/1369) [`ae6449502`](https://github.com/graphcommerce-org/graphcommerce/commit/ae64495024a455bbe5188588604368c1542840c9) Thanks [@paales](https://github.com/paales)! - Upgraded dependencies
22
+
3
23
  ## 3.0.5
4
24
 
5
25
  ### Patch Changes
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/react-hook-form",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "3.0.5",
5
+ "version": "3.1.0",
6
6
  "sideEffects": false,
7
7
  "engines": {
8
8
  "node": "14.x"
@@ -15,17 +15,19 @@
15
15
  }
16
16
  },
17
17
  "devDependencies": {
18
- "@graphcommerce/eslint-config-pwa": "^4.0.6",
19
- "@graphcommerce/prettier-config-pwa": "^4.0.4",
18
+ "@graphcommerce/eslint-config-pwa": "^4.1.4",
19
+ "@graphcommerce/prettier-config-pwa": "^4.0.5",
20
20
  "@graphcommerce/typescript-config-pwa": "^4.0.2",
21
- "@playwright/test": "^1.19.2"
21
+ "@playwright/test": "^1.20.1",
22
+ "type-fest": "2.12.1"
22
23
  },
23
24
  "dependencies": {
24
25
  "@apollo/client": "^3.5.10",
25
26
  "graphql": "^16.3.0",
27
+ "react-hook-form": "7.29.0"
28
+ },
29
+ "peerDependencies": {
26
30
  "react": "^17.0.2",
27
- "react-dom": "^17.0.2",
28
- "react-hook-form": "7.28.0",
29
- "type-fest": "^2.12.0"
31
+ "react-dom": "^17.0.2"
30
32
  }
31
33
  }
@@ -46,35 +46,59 @@ export function ComposedSubmit(props: ComposedSubmitProps) {
46
46
  * If we have forms that are have errors, we don't need to actually submit anything yet. We can
47
47
  * trigger the submission of the invalid forms and highlight the errors in those forms.
48
48
  */
49
- let formsToSubmit = formEntries.filter(
50
- ([, f]) => Object.keys(f.form?.formState.errors ?? {}).length > 0,
51
- )
49
+ dispatch({ type: 'SUBMIT' })
52
50
 
53
- // We have no errors and no invalid forms, this means we can submit everything.
54
- if (!formsToSubmit.length) formsToSubmit = formEntries
51
+ const invalidKeys: string[] = []
52
+ for (const [, { form, key }] of formEntries) {
53
+ // eslint-disable-next-line no-await-in-loop
54
+ const result = await form?.trigger()
55
+ if (result === false) invalidKeys.push(key)
56
+ }
55
57
 
56
- dispatch({ type: 'SUBMIT' })
58
+ let formsToProcess = formEntries
59
+ if (invalidKeys.length === 0) {
60
+ if (process.env.NODE_ENV !== 'production') {
61
+ console.log(
62
+ '[ComposedForm] All forms are valid, submitting...',
63
+ formsToProcess.map(([, { key }]) => key),
64
+ )
65
+ }
66
+ } else {
67
+ formsToProcess = formEntries.filter(([, { key }]) => invalidKeys.includes(key))
68
+ if (process.env.NODE_ENV !== 'production') {
69
+ console.log(
70
+ '[ComposedForm] Found invalid forms, triggering error messages by submitting...',
71
+ Object.fromEntries(
72
+ formsToProcess.map(([, { key, form }]) => [
73
+ key,
74
+ `Invalid fields ${(form?.formState.errors
75
+ ? Object.keys(form.formState.errors)
76
+ : []
77
+ ).join(', ')}`,
78
+ ]),
79
+ ),
80
+ )
81
+ }
82
+ }
57
83
 
58
84
  try {
59
85
  /**
60
86
  * We're executing these steps all in sequence, since certain forms can depend on other forms
61
87
  * in the backend.
62
- *
63
- * Todo: There might be a performance optimization by submitting multiple forms in parallel.
64
88
  */
65
- let canSubmit = true
66
- for (const [, { submit, form }] of formsToSubmit) {
67
- // eslint-disable-next-line no-await-in-loop
68
- if (canSubmit) await submit?.()
69
- // eslint-disable-next-line no-await-in-loop
70
- if (!canSubmit) await form?.trigger()
71
- if (form && isFormGqlOperation(form) && form.error) {
72
- // console.log(
73
- // key,
74
- // form?.formState.isValid,
75
- // form && (isFormGqlOperation(form) ? form.error : undefined),
76
- // )
77
- canSubmit = false
89
+ for (const [, { submit, key }] of formsToProcess) {
90
+ try {
91
+ console.log(`[ComposedForm] Submitting ${key}`)
92
+ // eslint-disable-next-line no-await-in-loop
93
+ await submit?.()
94
+ } catch (e) {
95
+ if (process.env.NODE_ENV !== 'production') {
96
+ console.error(
97
+ `[ComposedForm] The form ${key} has thrown an Error during submission, halting submissions`,
98
+ e,
99
+ )
100
+ }
101
+ throw e
78
102
  }
79
103
  }
80
104
  dispatch({ type: 'SUBMITTING' })
@@ -1,3 +1,4 @@
1
+ import { persistCache } from '@graphcommerce/graphql'
1
2
  import { useEffect } from 'react'
2
3
  import {
3
4
  FieldValues,
@@ -5,22 +6,29 @@ import {
5
6
  Path,
6
7
  FieldPathValue,
7
8
  UnpackNestedValue,
9
+ FieldPath,
8
10
  } from 'react-hook-form'
9
11
 
10
- export type UseFormPersistOptions<V> = {
11
- /** Instance of current form */
12
- form: UseFormReturn<V>
12
+ export type UseFormPersistOptions<
13
+ TFieldValues extends FieldValues = FieldValues,
14
+ TContext = any,
15
+ > = {
16
+ /** Instance of current form, used to watch value */
17
+ form: UseFormReturn<TFieldValues, TContext>
13
18
 
14
19
  /** Name of the key how it will be stored in the storage. */
15
20
  name: string
16
21
 
17
22
  /**
18
- * SessionStorage: Will not be avaiable when the user returns later (recommended). localStorage:
19
- * Will be available when the user returns later.
23
+ * - `sessionStorage`: Will not be avaiable when the user returns later (recommended).
24
+ * - `localStorage`: Will be available when the user returns later.
20
25
  */
21
26
  storage?: 'sessionStorage' | 'localStorage'
22
27
 
23
- exclude?: string[]
28
+ /** Exclude sensitive data from the storage like passwords. */
29
+ exclude?: FieldPath<TFieldValues>[]
30
+
31
+ persist?: FieldPath<TFieldValues>[]
24
32
  }
25
33
 
26
34
  /**
@@ -28,16 +36,24 @@ export type UseFormPersistOptions<V> = {
28
36
  * dirty fields when the form is initialized
29
37
  */
30
38
  export function useFormPersist<V>(options: UseFormPersistOptions<V>) {
31
- const { form, name, storage = 'sessionStorage', exclude = [] } = options
39
+ const { form, name, storage = 'sessionStorage', exclude = [], persist = [] } = options
32
40
  const { setValue, watch, formState } = form
33
41
 
34
42
  const dirtyFieldKeys = Object.keys(formState.dirtyFields) as Path<V>[]
35
- const valuesJson = JSON.stringify(
36
- Object.fromEntries(
37
- dirtyFieldKeys.filter((f) => !exclude.includes(f)).map((field) => [field, watch(field)]),
38
- ),
43
+
44
+ // Get all dirty field values and exclude sensitive data
45
+ const newValues = Object.fromEntries(
46
+ dirtyFieldKeys.filter((f) => !exclude.includes(f)).map((field) => [field, watch(field)]),
39
47
  )
40
48
 
49
+ // Amend the values with the values that should always be persisted
50
+ persist.forEach((persistKey) => {
51
+ const value = watch(persistKey)
52
+ if (value) newValues[persistKey] = value
53
+ })
54
+
55
+ const valuesJson = JSON.stringify(newValues)
56
+
41
57
  // Restore changes
42
58
  useEffect(() => {
43
59
  try {
@@ -51,7 +67,12 @@ export function useFormPersist<V>(options: UseFormPersistOptions<V>) {
51
67
  Path<V>,
52
68
  UnpackNestedValue<FieldPathValue<V, Path<V>>>,
53
69
  ][]
54
- entries.forEach((entry) => setValue(...entry, { shouldDirty: true, shouldValidate: true }))
70
+ entries.forEach(([entryName, value]) =>
71
+ setValue(entryName, value, {
72
+ shouldDirty: true,
73
+ shouldValidate: true,
74
+ }),
75
+ )
55
76
  }
56
77
  } catch {
57
78
  //