envoc-form 2.0.1-8 → 3.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.
- package/README.md +7 -7
- package/dist/css/envoc-form-styles.css +7 -6
- package/dist/css/envoc-form-styles.css.map +1 -1
- package/es/AddressInput/AddressInput.js +7 -6
- package/es/ConfirmBaseForm/ConfirmBaseForm.js +3 -2
- package/es/ConfirmDeleteForm/ConfirmDeleteForm.js +6 -5
- package/es/DatePickerInput/DatePickerInput.js +8 -3
- package/es/FileInput/DefaultFileList.js +3 -2
- package/es/FileInput/DropzoneFileInput.js +15 -12
- package/es/FileInput/FileInput.js +31 -9
- package/es/Form/Form.js +4 -2
- package/es/Form/FormBasedPreventNavigation.js +4 -7
- package/es/FormGroupWrapper.js +2 -1
- package/es/FormInput/FormInput.js +14 -8
- package/es/FormInputArray/FormInputArray.js +39 -24
- package/es/IconInput.js +2 -1
- package/es/ReactSelectField/ReactSelectField.js +6 -3
- package/es/SubmitFormButton.js +2 -1
- package/es/__Tests__/FormTestBase.js +5 -2
- package/es/normalizers.js +10 -5
- package/es/useStandardFormInput.js +4 -2
- package/es/utils/objectToFormData.js +10 -2
- package/lib/AddressInput/AddressInput.js +14 -8
- package/lib/ConfirmBaseForm/ConfirmBaseForm.js +4 -2
- package/lib/ConfirmDeleteForm/ConfirmDeleteForm.js +6 -4
- package/lib/DatePickerInput/DatePickerInput.js +9 -3
- package/lib/FileInput/DefaultFileList.js +3 -2
- package/lib/FileInput/DropzoneFileInput.js +17 -12
- package/lib/FileInput/FileInput.js +39 -10
- package/lib/Form/Form.js +11 -4
- package/lib/Form/FormBasedPreventNavigation.js +4 -10
- package/lib/FormGroupWrapper.js +3 -1
- package/lib/FormInput/FormInput.js +15 -8
- package/lib/FormInputArray/FormInputArray.js +47 -26
- package/lib/FormSection.js +5 -1
- package/lib/IconInput.js +3 -1
- package/lib/ReactSelectField/ReactSelectField.js +13 -5
- package/lib/ReactSelectField/index.js +6 -2
- package/lib/SubmitFormButton.js +3 -1
- package/lib/__Tests__/FormTestBase.js +6 -2
- package/lib/index.js +7 -3
- package/lib/normalizers.js +10 -5
- package/lib/useStandardFormInput.js +5 -2
- package/lib/utils/objectToFormData.js +10 -2
- package/lib/validators/index.js +5 -1
- package/package.json +99 -92
- package/src/AddressInput/AddesssInput.test.js +23 -23
- package/src/AddressInput/AddressInput.js +73 -73
- package/src/AddressInput/UsStates.js +53 -53
- package/src/AddressInput/__snapshots__/AddesssInput.test.js.snap +207 -207
- package/src/AddressInput/index.js +2 -2
- package/src/BoolInput/BoolInput.js +7 -7
- package/src/BoolInput/BoolInput.test.js +23 -23
- package/src/BoolInput/InlineBoolInput.js +7 -7
- package/src/BoolInput/__snapshots__/BoolInput.test.js.snap +89 -89
- package/src/BoolInput/boolOptions.js +6 -6
- package/src/BoolInput/index.js +4 -4
- package/src/ConfirmBaseForm/ConfirmBaseForm.js +37 -37
- package/src/ConfirmBaseForm/ConfirmBaseForm.test.js +14 -14
- package/src/ConfirmBaseForm/__snapshots__/ConfirmBaseForm.test.js.snap +23 -23
- package/src/ConfirmBaseForm/index.js +1 -1
- package/src/ConfirmDeleteForm/ConfirmDeleteForm.js +39 -39
- package/src/ConfirmDeleteForm/ConfirmDeleteForm.test.js +24 -24
- package/src/ConfirmDeleteForm/__snapshots__/ConfirmDeleteForm.test.js.snap +25 -25
- package/src/ConfirmDeleteForm/index.js +1 -1
- package/src/DatePickerInput/DatePickerInput.js +49 -46
- package/src/DatePickerInput/DatePickerInput.test.js +74 -74
- package/src/DatePickerInput/__snapshots__/DatePickerInput.test.js.snap +134 -134
- package/src/DatePickerInput/date-picker-input.scss +42 -42
- package/src/DatePickerInput/index.js +3 -3
- package/src/ErrorScrollTarget.js +6 -6
- package/src/FileInput/DefaultFileList.js +39 -39
- package/src/FileInput/DropzoneFileInput.js +56 -55
- package/src/FileInput/DropzoneFileInput.test.js +24 -15
- package/src/FileInput/FileInput.js +77 -49
- package/src/FileInput/FileInput.test.js +24 -15
- package/src/FileInput/__snapshots__/DropzoneFileInput.test.js.snap +57 -28
- package/src/FileInput/__snapshots__/FileInput.test.js.snap +58 -22
- package/src/FileInput/file-input.scss +57 -57
- package/src/FileInput/index.js +4 -4
- package/src/Form/FocusError.js +48 -48
- package/src/Form/Form.js +139 -138
- package/src/Form/Form.test.js +23 -23
- package/src/Form/FormBasedPreventNavigation.js +25 -25
- package/src/Form/ServerErrorContext.js +7 -7
- package/src/Form/__snapshots__/Form.test.js.snap +9 -9
- package/src/Form/index.js +3 -3
- package/src/FormGroup.js +30 -30
- package/src/FormGroupWrapper.js +28 -28
- package/src/FormInput/FormInput.js +145 -144
- package/src/FormInput/FormInput.test.js +66 -66
- package/src/FormInput/__snapshots__/FormInput.test.js.snap +323 -316
- package/src/FormInput/form-input.scss +9 -9
- package/src/FormInput/index.js +2 -2
- package/src/FormInputArray/FormInputArray.js +224 -210
- package/src/FormInputArray/FormInputArray.test.js +108 -59
- package/src/FormInputArray/__snapshots__/FormInputArray.test.js.snap +52 -40
- package/src/FormInputArray/form-input-array.scss +13 -8
- package/src/FormInputArray/index.js +2 -2
- package/src/FormSection.js +13 -13
- package/src/IconInput.js +31 -31
- package/src/InlineFormInput/InlineFormInput.js +6 -6
- package/src/InlineFormInput/InlineFormInput.test.js +23 -23
- package/src/InlineFormInput/__snapshots__/InlineFormInput.test.js.snap +26 -26
- package/src/InlineFormInput/index.js +3 -3
- package/src/InlineFormInput/inline-form-input.scss +3 -3
- package/src/MoneyInput/InlineMoneyInput.js +7 -7
- package/src/MoneyInput/MoneyInput.js +7 -7
- package/src/MoneyInput/MoneyInputs.test.js +43 -43
- package/src/MoneyInput/__snapshots__/MoneyInputs.test.js.snap +81 -81
- package/src/MoneyInput/index.js +4 -4
- package/src/MoneyInput/money-input.scss +3 -3
- package/src/MoneyInput/moneyInputProps.js +12 -12
- package/src/NestedFormFieldContext.js +6 -6
- package/src/ReactSelectField/ReactSelectField.js +122 -120
- package/src/ReactSelectField/index.js +6 -6
- package/src/ReactSelectField/react-select-field.scss +5 -5
- package/src/StandardFormActions.js +27 -27
- package/src/SubmitFormButton.js +28 -28
- package/src/__Tests__/FormTestBase.js +14 -11
- package/src/__Tests__/IconInput.test.js +23 -23
- package/src/__Tests__/StandardFormActions.test.js +23 -23
- package/src/__Tests__/SubmitFormButton.test.js +23 -23
- package/src/__Tests__/__snapshots__/IconInput.test.js.snap +38 -38
- package/src/__Tests__/__snapshots__/StandardFormActions.test.js.snap +25 -25
- package/src/__Tests__/__snapshots__/SubmitFormButton.test.js.snap +18 -18
- package/src/__Tests__/index.js +2 -2
- package/src/_variables.scss +11 -11
- package/src/index.js +33 -33
- package/src/normalizers.js +42 -32
- package/src/selectors.js +3 -3
- package/src/styles.scss +7 -7
- package/src/useStandardFormInput.js +118 -118
- package/src/utils/index.js +3 -3
- package/src/utils/objectContainsNonSerializableProperty.js +15 -15
- package/src/utils/objectContainsNonSerializableProperty.test.js +49 -49
- package/src/utils/objectToFormData.js +89 -83
- package/src/utils/objectToFormData.test.js +76 -47
- package/src/utils/typeChecks.js +18 -18
- package/src/validators/index.js +2 -2
- package/src/validators/validators.js +93 -93
- package/src/validators/validators.test.js +79 -79
- package/CHANGELOG.json +0 -95
- 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
|
+
`;
|
package/src/__Tests__/index.js
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
import FormTestBase from './FormTestBase';
|
2
|
-
export { FormTestBase };
|
1
|
+
import FormTestBase from './FormTestBase';
|
2
|
+
export { FormTestBase };
|
package/src/_variables.scss
CHANGED
@@ -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
|
+
};
|
package/src/normalizers.js
CHANGED
@@ -1,32 +1,42 @@
|
|
1
|
-
export const phoneNumber = (value) => {
|
2
|
-
if (!value) {
|
3
|
-
return value;
|
4
|
-
}
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
}
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
+
}
|
package/src/utils/index.js
CHANGED
@@ -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
|
+
});
|