envoc-form 2.0.1-7 → 2.0.1

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 (143) hide show
  1. package/README.md +7 -7
  2. package/dist/css/envoc-form-styles.css +43 -5
  3. package/dist/css/envoc-form-styles.css.map +1 -1
  4. package/es/AddressInput/AddressInput.js +7 -6
  5. package/es/ConfirmBaseForm/ConfirmBaseForm.js +3 -2
  6. package/es/ConfirmDeleteForm/ConfirmDeleteForm.js +2 -1
  7. package/es/DatePickerInput/DatePickerInput.js +22 -5
  8. package/es/FileInput/DefaultFileList.js +12 -8
  9. package/es/FileInput/DropzoneFileInput.js +24 -23
  10. package/es/FileInput/FileInput.js +31 -9
  11. package/es/Form/Form.js +4 -2
  12. package/es/FormGroupWrapper.js +2 -1
  13. package/es/FormInput/FormInput.js +12 -6
  14. package/es/FormInputArray/FormInputArray.js +39 -24
  15. package/es/IconInput.js +2 -1
  16. package/es/ReactSelectField/ReactSelectField.js +6 -3
  17. package/es/SubmitFormButton.js +2 -1
  18. package/es/__Tests__/FormTestBase.js +5 -2
  19. package/es/normalizers.js +10 -5
  20. package/es/useStandardFormInput.js +4 -2
  21. package/es/utils/objectToFormData.js +10 -2
  22. package/lib/AddressInput/AddressInput.js +14 -8
  23. package/lib/ConfirmBaseForm/ConfirmBaseForm.js +4 -2
  24. package/lib/ConfirmDeleteForm/ConfirmDeleteForm.js +3 -1
  25. package/lib/DatePickerInput/DatePickerInput.js +25 -5
  26. package/lib/FileInput/DefaultFileList.js +20 -13
  27. package/lib/FileInput/DropzoneFileInput.js +25 -25
  28. package/lib/FileInput/FileInput.js +39 -10
  29. package/lib/Form/Form.js +11 -4
  30. package/lib/Form/FormBasedPreventNavigation.js +5 -1
  31. package/lib/FormGroupWrapper.js +3 -1
  32. package/lib/FormInput/FormInput.js +13 -6
  33. package/lib/FormInputArray/FormInputArray.js +47 -26
  34. package/lib/FormSection.js +5 -1
  35. package/lib/IconInput.js +3 -1
  36. package/lib/ReactSelectField/ReactSelectField.js +13 -5
  37. package/lib/ReactSelectField/index.js +6 -2
  38. package/lib/SubmitFormButton.js +3 -1
  39. package/lib/__Tests__/FormTestBase.js +6 -2
  40. package/lib/index.js +7 -3
  41. package/lib/normalizers.js +10 -5
  42. package/lib/useStandardFormInput.js +5 -2
  43. package/lib/utils/objectToFormData.js +10 -2
  44. package/lib/validators/index.js +5 -1
  45. package/package.json +99 -93
  46. package/src/AddressInput/AddesssInput.test.js +23 -23
  47. package/src/AddressInput/AddressInput.js +73 -73
  48. package/src/AddressInput/UsStates.js +53 -53
  49. package/src/AddressInput/__snapshots__/AddesssInput.test.js.snap +207 -207
  50. package/src/AddressInput/index.js +2 -2
  51. package/src/BoolInput/BoolInput.js +7 -7
  52. package/src/BoolInput/BoolInput.test.js +23 -23
  53. package/src/BoolInput/InlineBoolInput.js +7 -7
  54. package/src/BoolInput/__snapshots__/BoolInput.test.js.snap +89 -89
  55. package/src/BoolInput/boolOptions.js +6 -6
  56. package/src/BoolInput/index.js +4 -4
  57. package/src/ConfirmBaseForm/ConfirmBaseForm.js +37 -37
  58. package/src/ConfirmBaseForm/ConfirmBaseForm.test.js +14 -14
  59. package/src/ConfirmBaseForm/__snapshots__/ConfirmBaseForm.test.js.snap +23 -23
  60. package/src/ConfirmBaseForm/index.js +1 -1
  61. package/src/ConfirmDeleteForm/ConfirmDeleteForm.js +39 -39
  62. package/src/ConfirmDeleteForm/ConfirmDeleteForm.test.js +24 -24
  63. package/src/ConfirmDeleteForm/__snapshots__/ConfirmDeleteForm.test.js.snap +25 -25
  64. package/src/ConfirmDeleteForm/index.js +1 -1
  65. package/src/DatePickerInput/DatePickerInput.js +58 -46
  66. package/src/DatePickerInput/DatePickerInput.test.js +74 -74
  67. package/src/DatePickerInput/__snapshots__/DatePickerInput.test.js.snap +134 -134
  68. package/src/DatePickerInput/date-picker-input.scss +42 -42
  69. package/src/DatePickerInput/index.js +3 -3
  70. package/src/ErrorScrollTarget.js +6 -6
  71. package/src/FileInput/DefaultFileList.js +39 -39
  72. package/src/FileInput/DropzoneFileInput.js +56 -59
  73. package/src/FileInput/DropzoneFileInput.test.js +24 -0
  74. package/src/FileInput/FileInput.js +77 -49
  75. package/src/FileInput/FileInput.test.js +24 -15
  76. package/src/FileInput/__snapshots__/DropzoneFileInput.test.js.snap +57 -0
  77. package/src/FileInput/__snapshots__/FileInput.test.js.snap +58 -22
  78. package/src/FileInput/file-input.scss +58 -17
  79. package/src/FileInput/index.js +4 -4
  80. package/src/Form/FocusError.js +48 -48
  81. package/src/Form/Form.js +139 -138
  82. package/src/Form/Form.test.js +23 -23
  83. package/src/Form/FormBasedPreventNavigation.js +25 -25
  84. package/src/Form/ServerErrorContext.js +7 -7
  85. package/src/Form/__snapshots__/Form.test.js.snap +9 -9
  86. package/src/Form/index.js +3 -3
  87. package/src/FormGroup.js +30 -30
  88. package/src/FormGroupWrapper.js +28 -28
  89. package/src/FormInput/FormInput.js +144 -144
  90. package/src/FormInput/FormInput.test.js +66 -66
  91. package/src/FormInput/__snapshots__/FormInput.test.js.snap +323 -316
  92. package/src/FormInput/form-input.scss +9 -9
  93. package/src/FormInput/index.js +2 -2
  94. package/src/FormInputArray/FormInputArray.js +224 -210
  95. package/src/FormInputArray/FormInputArray.test.js +108 -59
  96. package/src/FormInputArray/__snapshots__/FormInputArray.test.js.snap +52 -40
  97. package/src/FormInputArray/form-input-array.scss +13 -8
  98. package/src/FormInputArray/index.js +2 -2
  99. package/src/FormSection.js +13 -13
  100. package/src/IconInput.js +31 -31
  101. package/src/InlineFormInput/InlineFormInput.js +6 -6
  102. package/src/InlineFormInput/InlineFormInput.test.js +23 -23
  103. package/src/InlineFormInput/__snapshots__/InlineFormInput.test.js.snap +26 -26
  104. package/src/InlineFormInput/index.js +3 -3
  105. package/src/InlineFormInput/inline-form-input.scss +3 -3
  106. package/src/MoneyInput/InlineMoneyInput.js +7 -7
  107. package/src/MoneyInput/MoneyInput.js +7 -7
  108. package/src/MoneyInput/MoneyInputs.test.js +43 -43
  109. package/src/MoneyInput/__snapshots__/MoneyInputs.test.js.snap +81 -81
  110. package/src/MoneyInput/index.js +4 -4
  111. package/src/MoneyInput/money-input.scss +3 -3
  112. package/src/MoneyInput/moneyInputProps.js +12 -12
  113. package/src/NestedFormFieldContext.js +6 -6
  114. package/src/ReactSelectField/ReactSelectField.js +122 -120
  115. package/src/ReactSelectField/index.js +6 -6
  116. package/src/ReactSelectField/react-select-field.scss +5 -5
  117. package/src/StandardFormActions.js +27 -27
  118. package/src/SubmitFormButton.js +28 -28
  119. package/src/__Tests__/FormTestBase.js +14 -11
  120. package/src/__Tests__/IconInput.test.js +23 -23
  121. package/src/__Tests__/StandardFormActions.test.js +23 -23
  122. package/src/__Tests__/SubmitFormButton.test.js +23 -23
  123. package/src/__Tests__/__snapshots__/IconInput.test.js.snap +38 -38
  124. package/src/__Tests__/__snapshots__/StandardFormActions.test.js.snap +25 -25
  125. package/src/__Tests__/__snapshots__/SubmitFormButton.test.js.snap +18 -18
  126. package/src/__Tests__/index.js +2 -2
  127. package/src/_variables.scss +11 -11
  128. package/src/index.js +33 -33
  129. package/src/normalizers.js +42 -32
  130. package/src/selectors.js +3 -3
  131. package/src/styles.scss +7 -7
  132. package/src/useStandardFormInput.js +118 -118
  133. package/src/utils/index.js +3 -3
  134. package/src/utils/objectContainsNonSerializableProperty.js +15 -15
  135. package/src/utils/objectContainsNonSerializableProperty.test.js +49 -49
  136. package/src/utils/objectToFormData.js +89 -83
  137. package/src/utils/objectToFormData.test.js +76 -47
  138. package/src/utils/typeChecks.js +18 -18
  139. package/src/validators/index.js +2 -2
  140. package/src/validators/validators.js +93 -93
  141. package/src/validators/validators.test.js +79 -79
  142. package/CHANGELOG.json +0 -95
  143. package/CHANGELOG.md +0 -58
@@ -1,25 +1,25 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`StandardFormActions has matching snapshot 1`] = `
4
- <DocumentFragment>
5
- <form
6
- action="#"
7
- >
8
- <button
9
- class="btn btn-primary disabled"
10
- disabled=""
11
- title="You haven't made any changes"
12
- type="submit"
13
- >
14
- Submit
15
- </button>
16
-  
17
- <button
18
- class="btn btn-link"
19
- type="button"
20
- >
21
- Cancel
22
- </button>
23
- </form>
24
- </DocumentFragment>
25
- `;
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`StandardFormActions has matching snapshot 1`] = `
4
+ <DocumentFragment>
5
+ <form
6
+ action="#"
7
+ >
8
+ <button
9
+ class="btn btn-primary disabled"
10
+ disabled=""
11
+ title="You haven't made any changes"
12
+ type="submit"
13
+ >
14
+ Submit
15
+ </button>
16
+  
17
+ <button
18
+ class="btn btn-link"
19
+ type="button"
20
+ >
21
+ Cancel
22
+ </button>
23
+ </form>
24
+ </DocumentFragment>
25
+ `;
@@ -1,18 +1,18 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
2
-
3
- exports[`SubmitFormButton has matching snapshot 1`] = `
4
- <DocumentFragment>
5
- <form
6
- action="#"
7
- >
8
- <button
9
- class="btn btn-primary disabled"
10
- disabled=""
11
- title="You haven't made any changes"
12
- type="submit"
13
- >
14
- Submit
15
- </button>
16
- </form>
17
- </DocumentFragment>
18
- `;
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`SubmitFormButton has matching snapshot 1`] = `
4
+ <DocumentFragment>
5
+ <form
6
+ action="#"
7
+ >
8
+ <button
9
+ class="btn btn-primary disabled"
10
+ disabled=""
11
+ title="You haven't made any changes"
12
+ type="submit"
13
+ >
14
+ Submit
15
+ </button>
16
+ </form>
17
+ </DocumentFragment>
18
+ `;
@@ -1,2 +1,2 @@
1
- import FormTestBase from './FormTestBase';
2
- export { FormTestBase };
1
+ import FormTestBase from './FormTestBase';
2
+ export { FormTestBase };
@@ -1,11 +1,11 @@
1
- //These variables are normally in the template directly,
2
-
3
- $red: #f86c6b;
4
- // Couldn't find these variables directly from coreui/bootstrap
5
- $input-border-color: #c2cfd6;
6
- $input-border-color--focused: #8ad4ee;
7
- $input-box-shadow--focused: 0 0 0 0.2rem rgba(32, 168, 216, 0.25);
8
-
9
- //Taken from react-select because it fit better in the color scheme and is more readable.
10
- $input-disabled-background-color: hsl(0, 0%, 95%);
11
- $input-disabled-border-color: hsl(0, 0%, 90%);
1
+ //These variables are normally in the template directly,
2
+
3
+ $red: #f86c6b;
4
+ // Couldn't find these variables directly from coreui/bootstrap
5
+ $input-border-color: #c2cfd6;
6
+ $input-border-color--focused: #8ad4ee;
7
+ $input-box-shadow--focused: 0 0 0 0.2rem rgba(32, 168, 216, 0.25);
8
+
9
+ //Taken from react-select because it fit better in the color scheme and is more readable.
10
+ $input-disabled-background-color: hsl(0, 0%, 95%);
11
+ $input-disabled-border-color: hsl(0, 0%, 90%);
package/src/index.js CHANGED
@@ -1,33 +1,33 @@
1
- import Form from './Form';
2
- import FormInput from './FormInput';
3
- import InlineFormInput from './InlineFormInput';
4
- import FormInputArray from './FormInputArray';
5
- import IconInput from './IconInput';
6
- import { BoolInput, InlineBoolInput } from './BoolInput';
7
- import StandardFormActions from './StandardFormActions';
8
- import validators from './validators';
9
- import * as normalizers from './normalizers';
10
- import ConfirmBaseForm from './ConfirmBaseForm';
11
- import ConfirmDeleteForm from './ConfirmDeleteForm';
12
- import SubmitFormButton from './SubmitFormButton';
13
- import AddressInput from './AddressInput';
14
- import { MoneyInput, InlineMoneyInput } from './MoneyInput';
15
-
16
- export {
17
- Form,
18
- FormInput,
19
- InlineFormInput,
20
- FormInputArray,
21
- IconInput,
22
- BoolInput,
23
- InlineBoolInput,
24
- validators,
25
- normalizers,
26
- StandardFormActions,
27
- ConfirmBaseForm,
28
- ConfirmDeleteForm,
29
- SubmitFormButton,
30
- AddressInput,
31
- MoneyInput,
32
- InlineMoneyInput,
33
- };
1
+ import Form from './Form';
2
+ import FormInput from './FormInput';
3
+ import InlineFormInput from './InlineFormInput';
4
+ import FormInputArray from './FormInputArray';
5
+ import IconInput from './IconInput';
6
+ import { BoolInput, InlineBoolInput } from './BoolInput';
7
+ import StandardFormActions from './StandardFormActions';
8
+ import validators from './validators';
9
+ import * as normalizers from './normalizers';
10
+ import ConfirmBaseForm from './ConfirmBaseForm';
11
+ import ConfirmDeleteForm from './ConfirmDeleteForm';
12
+ import SubmitFormButton from './SubmitFormButton';
13
+ import AddressInput from './AddressInput';
14
+ import { MoneyInput, InlineMoneyInput } from './MoneyInput';
15
+
16
+ export {
17
+ Form,
18
+ FormInput,
19
+ InlineFormInput,
20
+ FormInputArray,
21
+ IconInput,
22
+ BoolInput,
23
+ InlineBoolInput,
24
+ validators,
25
+ normalizers,
26
+ StandardFormActions,
27
+ ConfirmBaseForm,
28
+ ConfirmDeleteForm,
29
+ SubmitFormButton,
30
+ AddressInput,
31
+ MoneyInput,
32
+ InlineMoneyInput,
33
+ };
@@ -1,32 +1,42 @@
1
- export const phoneNumber = (value) => {
2
- if (!value) {
3
- return value;
4
- }
5
-
6
- if (value.length === 12 && value.startsWith('+1')) {
7
- value = value.substring(2);
8
- }
9
-
10
- const onlyNums = value.replace(/[^\d]/g, '');
11
- if (onlyNums.length <= 3) {
12
- return onlyNums;
13
- }
14
- if (onlyNums.length <= 7) {
15
- return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3)}`;
16
- }
17
- return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3, 6)}-${onlyNums.slice(
18
- 6,
19
- 10
20
- )}`;
21
- };
22
-
23
- export const zipCode = (value) => {
24
- if (!value) {
25
- return value;
26
- }
27
- const onlyNums = value.replace(/[^\d]/g, '');
28
- if (onlyNums.length > 5) {
29
- return onlyNums.substring(0, 5) + '-' + onlyNums.substring(5, 9);
30
- }
31
- return onlyNums;
32
- };
1
+ export const phoneNumber = (value) => {
2
+ if (!value) {
3
+ return value;
4
+ }
5
+
6
+ const onlyNums = value.replace(/[^\d]/g, '');
7
+ if (onlyNums.length <= 3) {
8
+ return onlyNums;
9
+ }
10
+ if (onlyNums.length <= 7) {
11
+ return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3)}`;
12
+ }
13
+ if (onlyNums.length <= 10) {
14
+ return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3, 6)}-${onlyNums.slice(
15
+ 6,
16
+ 10
17
+ )}`;
18
+ }
19
+ if (onlyNums.length <= 13) {
20
+ const countryCodeLength = onlyNums - 10;
21
+ return `+${onlyNums.slice(0, countryCodeLength)} ${onlyNums.slice(
22
+ countryCodeLength,
23
+ 3 + countryCodeLength
24
+ )}-${onlyNums.slice(
25
+ 3 + countryCodeLength,
26
+ 6 + countryCodeLength
27
+ )}-${onlyNums.slice(6 + countryCodeLength, onlyNums.length)}`;
28
+ }
29
+
30
+ return onlyNums;
31
+ };
32
+
33
+ export const zipCode = (value) => {
34
+ if (!value) {
35
+ return value;
36
+ }
37
+ const onlyNums = value.replace(/[^\d]/g, '');
38
+ if (onlyNums.length > 5) {
39
+ return onlyNums.substring(0, 5) + '-' + onlyNums.substring(5, 9);
40
+ }
41
+ return onlyNums;
42
+ };
package/src/selectors.js CHANGED
@@ -1,3 +1,3 @@
1
- export const getForm = (state, ownProps) => state.form[ownProps.form];
2
- export const getRegisteredFields = (state, ownProps) =>
3
- getForm(state, ownProps) ? getForm(state, ownProps).registeredFields : {};
1
+ export const getForm = (state, ownProps) => state.form[ownProps.form];
2
+ export const getRegisteredFields = (state, ownProps) =>
3
+ getForm(state, ownProps) ? getForm(state, ownProps).registeredFields : {};
package/src/styles.scss CHANGED
@@ -1,7 +1,7 @@
1
- @use './DatePickerInput/date-picker-input.scss';
2
- @use './InlineFormInput/inline-form-input.scss';
3
- @use './MoneyInput/money-input.scss';
4
- @use './FormInput/form-input.scss';
5
- @use './FileInput/file-input.scss';
6
- @use './FormInputArray/form-input-array.scss';
7
- @use './ReactSelectField/react-select-field.scss';
1
+ @use './DatePickerInput/date-picker-input.scss';
2
+ @use './InlineFormInput/inline-form-input.scss';
3
+ @use './MoneyInput/money-input.scss';
4
+ @use './FormInput/form-input.scss';
5
+ @use './FileInput/file-input.scss';
6
+ @use './FormInputArray/form-input-array.scss';
7
+ @use './ReactSelectField/react-select-field.scss';
@@ -1,118 +1,118 @@
1
- import { useField, useFormikContext } from 'formik';
2
- import { useContext, useEffect } from 'react';
3
- import NestedFormFieldContext from './NestedFormFieldContext';
4
- import { ServerErrorContext } from './Form';
5
-
6
- // provides a consistent way to deal with all form fields (non array)
7
- export default function useStandardFormInput({
8
- id: providedId,
9
- name: providedName,
10
- disabled,
11
- validate,
12
- normalize,
13
- ...props
14
- }) {
15
- // because the formik errors are evaluated all at the same time we need to keep server errors separate
16
- const { getError: getServerError, setError: setServerError } = useContext(
17
- ServerErrorContext
18
- );
19
-
20
- // ensure that form section values are obeyed, e.g. homeAddress.zipCode
21
- const parentFormFieldContext = useContext(NestedFormFieldContext);
22
- const name = parentFormFieldContext
23
- ? `${parentFormFieldContext}.${providedName}`
24
- : providedName;
25
-
26
- // ensure that nested contexts don't have duplicate id issues when an id is specified
27
- const id =
28
- providedId && parentFormFieldContext
29
- ? `${parentFormFieldContext}.${providedId}`
30
- : providedId;
31
-
32
- // ensure that our custom validation rules are handled
33
- // e.g. we allow arrays of validators
34
- const [{ onChange: formikOnChange, value, ...formikProps }, meta] = useField(
35
- Object.assign({ name, id }, props, {
36
- validate: callAllValidators,
37
- })
38
- );
39
- const { setFieldValue, isSubmitting } = useFormikContext();
40
-
41
- // these are the props we expect consumers of this hook to pass directly to the input (or other control)
42
- const inputProps = Object.assign({}, props, formikProps, {
43
- // ensure that we don't send a undefined / null - which signals to react that we are uncontrolled
44
- // also, pass any direct from server props through normalize without making the form dirty (e.g. phone number)
45
- value:
46
- value === undefined || value === null
47
- ? ''
48
- : (normalize && normalize(value)) || value,
49
- // ensure the id is set if not provided
50
- id: id ? id : name,
51
- // ensure we can handle both events from direct inputs and controlled ones like react select AND normalize
52
- onChange: handleChange,
53
- onBlur: handleBlur,
54
- disabled: disabled,
55
- });
56
-
57
- useEffect(() => {
58
- if (!meta.touched && isSubmitting) {
59
- // because we do not always register all fields up front.
60
- // e.g. formik expects even a 'create' form to have all fields given, at least, blank values
61
- // that seems to be how it touches on submit:
62
- // see: https://codesandbox.io/s/formik-example-4n7n8 vs https://codesandbox.io/s/formik-example-kttk5
63
- // note in particular the change in line 24
64
- // thus, we manually touch all fields here by calling onBlur
65
- // there is a "setFieldTouched" from useFormikContext but it doesn't appear to work on field arrays
66
- handleBlur();
67
- }
68
- });
69
-
70
- const resultMeta = Object.assign({}, meta, {
71
- error: getServerError(name) || (meta.touched ? meta.error : null),
72
- });
73
-
74
- return [inputProps, resultMeta];
75
-
76
- function handleBlur(e) {
77
- // some components are fully controlled and do not return synthetic events on blur
78
- if (!e || !e.target) {
79
- formikProps.onBlur({ target: { name: name } });
80
- } else {
81
- formikProps.onBlur(e);
82
- }
83
- }
84
-
85
- function handleChange(e) {
86
- if (disabled) {
87
- return;
88
- }
89
- // some components are fully controlled and do not return synthetic events (e.g. ReactSelect) on change
90
- // we should handle all our normal cases here to normalize / get the value / etc
91
- const value =
92
- e && e.target
93
- ? e.target.type === 'checkbox'
94
- ? e.target.checked
95
- : e.target.value
96
- : e;
97
- const normalized = normalize ? normalize(value) : value;
98
- setFieldValue(name, normalized);
99
- setServerError(name, null);
100
- }
101
-
102
- function callAllValidators(value) {
103
- if (disabled || !validate) {
104
- return;
105
- }
106
-
107
- if (!Array.isArray(validate)) {
108
- return validate(value);
109
- }
110
-
111
- for (let i = 0; i < validate.length; i++) {
112
- const result = validate[i](value);
113
- if (result) {
114
- return result;
115
- }
116
- }
117
- }
118
- }
1
+ import { useField, useFormikContext } from 'formik';
2
+ import { useContext, useEffect } from 'react';
3
+ import NestedFormFieldContext from './NestedFormFieldContext';
4
+ import { ServerErrorContext } from './Form';
5
+
6
+ // provides a consistent way to deal with all form fields (non array)
7
+ export default function useStandardFormInput({
8
+ id: providedId,
9
+ name: providedName,
10
+ disabled,
11
+ validate,
12
+ normalize,
13
+ ...props
14
+ }) {
15
+ // because the formik errors are evaluated all at the same time we need to keep server errors separate
16
+ const { getError: getServerError, setError: setServerError } = useContext(
17
+ ServerErrorContext
18
+ );
19
+
20
+ // ensure that form section values are obeyed, e.g. homeAddress.zipCode
21
+ const parentFormFieldContext = useContext(NestedFormFieldContext);
22
+ const name = parentFormFieldContext
23
+ ? `${parentFormFieldContext}.${providedName}`
24
+ : providedName;
25
+
26
+ // ensure that nested contexts don't have duplicate id issues when an id is specified
27
+ const id =
28
+ providedId && parentFormFieldContext
29
+ ? `${parentFormFieldContext}.${providedId}`
30
+ : providedId;
31
+
32
+ // ensure that our custom validation rules are handled
33
+ // e.g. we allow arrays of validators
34
+ const [{ onChange: formikOnChange, value, ...formikProps }, meta] = useField(
35
+ Object.assign({ name, id }, props, {
36
+ validate: callAllValidators,
37
+ })
38
+ );
39
+ const { setFieldValue, isSubmitting } = useFormikContext();
40
+
41
+ // these are the props we expect consumers of this hook to pass directly to the input (or other control)
42
+ const inputProps = Object.assign({}, props, formikProps, {
43
+ // ensure that we don't send a undefined / null - which signals to react that we are uncontrolled
44
+ // also, pass any direct from server props through normalize without making the form dirty (e.g. phone number)
45
+ value:
46
+ value === undefined || value === null
47
+ ? ''
48
+ : (normalize && normalize(value)) || value,
49
+ // ensure the id is set if not provided
50
+ id: id ? id : name,
51
+ // ensure we can handle both events from direct inputs and controlled ones like react select AND normalize
52
+ onChange: handleChange,
53
+ onBlur: handleBlur,
54
+ disabled: disabled,
55
+ });
56
+
57
+ useEffect(() => {
58
+ if (!meta.touched && isSubmitting) {
59
+ // because we do not always register all fields up front.
60
+ // e.g. formik expects even a 'create' form to have all fields given, at least, blank values
61
+ // that seems to be how it touches on submit:
62
+ // see: https://codesandbox.io/s/formik-example-4n7n8 vs https://codesandbox.io/s/formik-example-kttk5
63
+ // note in particular the change in line 24
64
+ // thus, we manually touch all fields here by calling onBlur
65
+ // there is a "setFieldTouched" from useFormikContext but it doesn't appear to work on field arrays
66
+ handleBlur();
67
+ }
68
+ });
69
+
70
+ const resultMeta = Object.assign({}, meta, {
71
+ error: getServerError(name) || (meta.touched ? meta.error : null),
72
+ });
73
+
74
+ return [inputProps, resultMeta];
75
+
76
+ function handleBlur(e) {
77
+ // some components are fully controlled and do not return synthetic events on blur
78
+ if (!e || !e.target) {
79
+ formikProps.onBlur({ target: { name: name } });
80
+ } else {
81
+ formikProps.onBlur(e);
82
+ }
83
+ }
84
+
85
+ function handleChange(e) {
86
+ if (disabled) {
87
+ return;
88
+ }
89
+ // some components are fully controlled and do not return synthetic events (e.g. ReactSelect) on change
90
+ // we should handle all our normal cases here to normalize / get the value / etc
91
+ const value =
92
+ e && e.target
93
+ ? e.target.type === 'checkbox'
94
+ ? e.target.checked
95
+ : e.target.value
96
+ : e;
97
+ const normalized = normalize ? normalize(value) : value;
98
+ setFieldValue(name, normalized);
99
+ setServerError(name, null);
100
+ }
101
+
102
+ function callAllValidators(value) {
103
+ if (disabled || !validate) {
104
+ return;
105
+ }
106
+
107
+ if (!Array.isArray(validate)) {
108
+ return validate(value);
109
+ }
110
+
111
+ for (let i = 0; i < validate.length; i++) {
112
+ const result = validate[i](value);
113
+ if (result) {
114
+ return result;
115
+ }
116
+ }
117
+ }
118
+ }
@@ -1,3 +1,3 @@
1
- import objectToFormData from './objectToFormData';
2
- import objectContainsNonSerializableProperty from './objectContainsNonSerializableProperty';
3
- export { objectToFormData, objectContainsNonSerializableProperty };
1
+ import objectToFormData from './objectToFormData';
2
+ import objectContainsNonSerializableProperty from './objectContainsNonSerializableProperty';
3
+ export { objectToFormData, objectContainsNonSerializableProperty };
@@ -1,15 +1,15 @@
1
- import { isObject, isBlob, isFile } from './typeChecks';
2
-
3
- export default function objectContainsNonSerializableProperty(object) {
4
- return Object.entries(object).some((value) => {
5
- if (value) {
6
- if (isBlob(value[1]) || isFile(value[1])) {
7
- return true;
8
- }
9
- if (isObject(value[1])) {
10
- return objectContainsNonSerializableProperty(value[1]);
11
- }
12
- }
13
- return false;
14
- });
15
- }
1
+ import { isObject, isBlob, isFile } from './typeChecks';
2
+
3
+ export default function objectContainsNonSerializableProperty(object) {
4
+ return Object.entries(object).some((value) => {
5
+ if (value) {
6
+ if (isBlob(value[1]) || isFile(value[1])) {
7
+ return true;
8
+ }
9
+ if (isObject(value[1])) {
10
+ return objectContainsNonSerializableProperty(value[1]);
11
+ }
12
+ }
13
+ return false;
14
+ });
15
+ }
@@ -1,49 +1,49 @@
1
- import check from './objectContainsNonSerializableProperty';
2
-
3
- describe('Object contains non serializable property checker', () => {
4
- it('Should return true for simple object', () => {
5
- const object = {
6
- name: 'Bob',
7
- age: 20,
8
- favoriteFoods: ['Hamburger', 'Pizza'],
9
- homeAddress: {
10
- addressLine1: '12345 Test Place',
11
- addressLine2: null,
12
- city: 'SomePlace',
13
- state: 'LA',
14
- zip: 12345,
15
- },
16
- };
17
-
18
- const result = check(object);
19
- expect(result).toBe(false);
20
- });
21
-
22
- it('Should return false for direct file property', () => {
23
- const object = {
24
- name: 'Bob',
25
- age: 20,
26
- profilePhoto: new File(['foo'], 'foo.txt', {
27
- type: 'text/plain',
28
- }),
29
- };
30
-
31
- const result = check(object);
32
- expect(result).toBe(true);
33
- });
34
-
35
- it('Should return false for deeply nested file property', () => {
36
- const object = {
37
- a: {
38
- b: {
39
- c: new File(['foo'], 'foo.txt', {
40
- type: 'text/plain',
41
- }),
42
- },
43
- },
44
- };
45
-
46
- const result = check(object);
47
- expect(result).toBe(true);
48
- });
49
- });
1
+ import check from './objectContainsNonSerializableProperty';
2
+
3
+ describe('Object contains non serializable property checker', () => {
4
+ it('Should return true for simple object', () => {
5
+ const object = {
6
+ name: 'Bob',
7
+ age: 20,
8
+ favoriteFoods: ['Hamburger', 'Pizza'],
9
+ homeAddress: {
10
+ addressLine1: '12345 Test Place',
11
+ addressLine2: null,
12
+ city: 'SomePlace',
13
+ state: 'LA',
14
+ zip: 12345,
15
+ },
16
+ };
17
+
18
+ const result = check(object);
19
+ expect(result).toBe(false);
20
+ });
21
+
22
+ it('Should return false for direct file property', () => {
23
+ const object = {
24
+ name: 'Bob',
25
+ age: 20,
26
+ profilePhoto: new File(['foo'], 'foo.txt', {
27
+ type: 'text/plain',
28
+ }),
29
+ };
30
+
31
+ const result = check(object);
32
+ expect(result).toBe(true);
33
+ });
34
+
35
+ it('Should return false for deeply nested file property', () => {
36
+ const object = {
37
+ a: {
38
+ b: {
39
+ c: new File(['foo'], 'foo.txt', {
40
+ type: 'text/plain',
41
+ }),
42
+ },
43
+ },
44
+ };
45
+
46
+ const result = check(object);
47
+ expect(result).toBe(true);
48
+ });
49
+ });