envoc-form 2.0.1-9 → 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 (141) hide show
  1. package/README.md +7 -7
  2. package/dist/css/envoc-form-styles.css +7 -6
  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 +3 -2
  9. package/es/FileInput/DropzoneFileInput.js +15 -12
  10. package/es/FileInput/FileInput.js +31 -9
  11. package/es/Form/Form.js +2 -1
  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/lib/AddressInput/AddressInput.js +14 -8
  22. package/lib/ConfirmBaseForm/ConfirmBaseForm.js +4 -2
  23. package/lib/ConfirmDeleteForm/ConfirmDeleteForm.js +3 -1
  24. package/lib/DatePickerInput/DatePickerInput.js +25 -5
  25. package/lib/FileInput/DefaultFileList.js +3 -2
  26. package/lib/FileInput/DropzoneFileInput.js +17 -12
  27. package/lib/FileInput/FileInput.js +39 -10
  28. package/lib/Form/Form.js +9 -3
  29. package/lib/Form/FormBasedPreventNavigation.js +5 -1
  30. package/lib/FormGroupWrapper.js +3 -1
  31. package/lib/FormInput/FormInput.js +13 -6
  32. package/lib/FormInputArray/FormInputArray.js +47 -26
  33. package/lib/FormSection.js +5 -1
  34. package/lib/IconInput.js +3 -1
  35. package/lib/ReactSelectField/ReactSelectField.js +13 -5
  36. package/lib/ReactSelectField/index.js +6 -2
  37. package/lib/SubmitFormButton.js +3 -1
  38. package/lib/__Tests__/FormTestBase.js +6 -2
  39. package/lib/index.js +7 -3
  40. package/lib/normalizers.js +10 -5
  41. package/lib/useStandardFormInput.js +5 -2
  42. package/lib/validators/index.js +5 -1
  43. package/package.json +99 -93
  44. package/src/AddressInput/AddesssInput.test.js +23 -23
  45. package/src/AddressInput/AddressInput.js +73 -73
  46. package/src/AddressInput/UsStates.js +53 -53
  47. package/src/AddressInput/__snapshots__/AddesssInput.test.js.snap +207 -207
  48. package/src/AddressInput/index.js +2 -2
  49. package/src/BoolInput/BoolInput.js +7 -7
  50. package/src/BoolInput/BoolInput.test.js +23 -23
  51. package/src/BoolInput/InlineBoolInput.js +7 -7
  52. package/src/BoolInput/__snapshots__/BoolInput.test.js.snap +89 -89
  53. package/src/BoolInput/boolOptions.js +6 -6
  54. package/src/BoolInput/index.js +4 -4
  55. package/src/ConfirmBaseForm/ConfirmBaseForm.js +37 -37
  56. package/src/ConfirmBaseForm/ConfirmBaseForm.test.js +14 -14
  57. package/src/ConfirmBaseForm/__snapshots__/ConfirmBaseForm.test.js.snap +23 -23
  58. package/src/ConfirmBaseForm/index.js +1 -1
  59. package/src/ConfirmDeleteForm/ConfirmDeleteForm.js +39 -39
  60. package/src/ConfirmDeleteForm/ConfirmDeleteForm.test.js +24 -24
  61. package/src/ConfirmDeleteForm/__snapshots__/ConfirmDeleteForm.test.js.snap +25 -25
  62. package/src/ConfirmDeleteForm/index.js +1 -1
  63. package/src/DatePickerInput/DatePickerInput.js +58 -46
  64. package/src/DatePickerInput/DatePickerInput.test.js +74 -74
  65. package/src/DatePickerInput/__snapshots__/DatePickerInput.test.js.snap +134 -134
  66. package/src/DatePickerInput/date-picker-input.scss +42 -42
  67. package/src/DatePickerInput/index.js +3 -3
  68. package/src/ErrorScrollTarget.js +6 -6
  69. package/src/FileInput/DefaultFileList.js +39 -39
  70. package/src/FileInput/DropzoneFileInput.js +56 -55
  71. package/src/FileInput/DropzoneFileInput.test.js +24 -15
  72. package/src/FileInput/FileInput.js +77 -49
  73. package/src/FileInput/FileInput.test.js +24 -15
  74. package/src/FileInput/__snapshots__/DropzoneFileInput.test.js.snap +57 -28
  75. package/src/FileInput/__snapshots__/FileInput.test.js.snap +58 -22
  76. package/src/FileInput/file-input.scss +57 -57
  77. package/src/FileInput/index.js +4 -4
  78. package/src/Form/FocusError.js +48 -48
  79. package/src/Form/Form.js +139 -139
  80. package/src/Form/Form.test.js +23 -23
  81. package/src/Form/FormBasedPreventNavigation.js +25 -25
  82. package/src/Form/ServerErrorContext.js +7 -7
  83. package/src/Form/__snapshots__/Form.test.js.snap +9 -9
  84. package/src/Form/index.js +3 -3
  85. package/src/FormGroup.js +30 -30
  86. package/src/FormGroupWrapper.js +28 -28
  87. package/src/FormInput/FormInput.js +144 -144
  88. package/src/FormInput/FormInput.test.js +66 -66
  89. package/src/FormInput/__snapshots__/FormInput.test.js.snap +323 -316
  90. package/src/FormInput/form-input.scss +9 -9
  91. package/src/FormInput/index.js +2 -2
  92. package/src/FormInputArray/FormInputArray.js +224 -210
  93. package/src/FormInputArray/FormInputArray.test.js +108 -59
  94. package/src/FormInputArray/__snapshots__/FormInputArray.test.js.snap +52 -40
  95. package/src/FormInputArray/form-input-array.scss +13 -8
  96. package/src/FormInputArray/index.js +2 -2
  97. package/src/FormSection.js +13 -13
  98. package/src/IconInput.js +31 -31
  99. package/src/InlineFormInput/InlineFormInput.js +6 -6
  100. package/src/InlineFormInput/InlineFormInput.test.js +23 -23
  101. package/src/InlineFormInput/__snapshots__/InlineFormInput.test.js.snap +26 -26
  102. package/src/InlineFormInput/index.js +3 -3
  103. package/src/InlineFormInput/inline-form-input.scss +3 -3
  104. package/src/MoneyInput/InlineMoneyInput.js +7 -7
  105. package/src/MoneyInput/MoneyInput.js +7 -7
  106. package/src/MoneyInput/MoneyInputs.test.js +43 -43
  107. package/src/MoneyInput/__snapshots__/MoneyInputs.test.js.snap +81 -81
  108. package/src/MoneyInput/index.js +4 -4
  109. package/src/MoneyInput/money-input.scss +3 -3
  110. package/src/MoneyInput/moneyInputProps.js +12 -12
  111. package/src/NestedFormFieldContext.js +6 -6
  112. package/src/ReactSelectField/ReactSelectField.js +122 -120
  113. package/src/ReactSelectField/index.js +6 -6
  114. package/src/ReactSelectField/react-select-field.scss +5 -5
  115. package/src/StandardFormActions.js +27 -27
  116. package/src/SubmitFormButton.js +28 -28
  117. package/src/__Tests__/FormTestBase.js +14 -11
  118. package/src/__Tests__/IconInput.test.js +23 -23
  119. package/src/__Tests__/StandardFormActions.test.js +23 -23
  120. package/src/__Tests__/SubmitFormButton.test.js +23 -23
  121. package/src/__Tests__/__snapshots__/IconInput.test.js.snap +38 -38
  122. package/src/__Tests__/__snapshots__/StandardFormActions.test.js.snap +25 -25
  123. package/src/__Tests__/__snapshots__/SubmitFormButton.test.js.snap +18 -18
  124. package/src/__Tests__/index.js +2 -2
  125. package/src/_variables.scss +11 -11
  126. package/src/index.js +33 -33
  127. package/src/normalizers.js +42 -32
  128. package/src/selectors.js +3 -3
  129. package/src/styles.scss +7 -7
  130. package/src/useStandardFormInput.js +118 -118
  131. package/src/utils/index.js +3 -3
  132. package/src/utils/objectContainsNonSerializableProperty.js +15 -15
  133. package/src/utils/objectContainsNonSerializableProperty.test.js +49 -49
  134. package/src/utils/objectToFormData.js +89 -89
  135. package/src/utils/objectToFormData.test.js +76 -76
  136. package/src/utils/typeChecks.js +18 -18
  137. package/src/validators/index.js +2 -2
  138. package/src/validators/validators.js +93 -93
  139. package/src/validators/validators.test.js +79 -79
  140. package/CHANGELOG.json +0 -95
  141. package/CHANGELOG.md +0 -58
@@ -1,210 +1,224 @@
1
- import React, { useEffect, useRef, useState } from 'react';
2
- import { v4 as uuid } from 'uuid';
3
- import classnames from 'classnames';
4
- import { Col, Card, Button, CardBody, Row, Alert } from 'reactstrap';
5
- import PropTypes from 'prop-types';
6
- import useStandardFormInput from '../useStandardFormInput';
7
- import NestedFormFieldContext from '../NestedFormFieldContext';
8
-
9
- export default function FormInputArray({
10
- id,
11
- name,
12
- label,
13
- newItem,
14
- component: Component,
15
- prepend: Prepend,
16
- disabled,
17
- validate,
18
- ...props
19
- }) {
20
- const [inputProps, meta] = useStandardFormInput({
21
- id,
22
- name,
23
- validate,
24
- disabled,
25
- ...props,
26
- });
27
- const inputPropsRef = useRef();
28
- const [hasChangedLength, setHasChangedLength] = useState(false);
29
- inputPropsRef.current = inputProps;
30
- const values = inputProps.value || [];
31
- const length = values.length;
32
-
33
- useEffect(() => {
34
- return () => {
35
- setHasChangedLength(true);
36
- };
37
- }, [length, setHasChangedLength]);
38
-
39
- useEffect(() => {
40
- if (!hasChangedLength) {
41
- return;
42
- }
43
- inputPropsRef.current.onBlur();
44
- }, [length, hasChangedLength]);
45
-
46
- return (
47
- <Card className="field-array-card">
48
- <CardActionHeader>
49
- <CardActionHeader.Title>{label}</CardActionHeader.Title>
50
- <CardActionHeader.Actions>
51
- {!disabled && (
52
- <Button
53
- color="link"
54
- onClick={addItem}
55
- title="Add an additional item"
56
- className="add-array-item">
57
- <i className="fas fa-plus" /> Add New
58
- </Button>
59
- )}
60
- </CardActionHeader.Actions>
61
- </CardActionHeader>
62
- <CardBody>
63
- {/* See: https://jaredpalmer.com/formik/docs/api/fieldarray#fieldarray-validation-gotchas */}
64
- {meta.error && typeof meta.error === 'string' && (
65
- <Row>
66
- <Col>
67
- <Alert className="field-array-alert" color="danger">
68
- {meta.error}
69
- </Alert>
70
- </Col>
71
- </Row>
72
- )}
73
- {Prepend && values.filter((x) => !x.isDeleted).length > 0 && (
74
- <Row className={classnames('input-array-header')}>
75
- <Prepend />
76
- {!disabled && <div className="remove-array-item"></div>}
77
- </Row>
78
- )}
79
- {values.map((value, index) => {
80
- const itemName = `${inputProps.name}[${index}]`;
81
- return (
82
- <Row
83
- key={
84
- (value && value['form-input-array-key']) ||
85
- (value && value['id']) ||
86
- itemName
87
- }>
88
- <NestedFormFieldContext.Provider value={itemName}>
89
- <Component
90
- value={value}
91
- name={itemName}
92
- disabled={disabled}
93
- {...props}
94
- />
95
- </NestedFormFieldContext.Provider>
96
- {!disabled && (
97
- <div className="remove-array-item">
98
- <Button
99
- color="link"
100
- onClick={() => removeItem(index)}
101
- title="Remove Item">
102
- <i className="fa fa-trash-alt" />
103
- </Button>
104
- </div>
105
- )}
106
- </Row>
107
- );
108
- })}
109
- </CardBody>
110
- </Card>
111
- );
112
-
113
- function addItem() {
114
- if (disabled) {
115
- return;
116
- }
117
- const item = Object.assign({}, newItem, { 'form-input-array-key': uuid() });
118
- const mapped = [...values, item];
119
- inputProps.onChange(mapped);
120
- }
121
-
122
- function removeItem(index) {
123
- if (disabled) {
124
- return;
125
- }
126
- const itemToRemove = values[index];
127
- // assumes anything from the server has an 'id' value sent
128
- if (!itemToRemove.id) {
129
- inputProps.onChange(values.filter((x) => x !== itemToRemove));
130
- return;
131
- }
132
- const mapped = values.map((x) =>
133
- x !== itemToRemove ? x : Object.assign({}, x, { isDeleted: true })
134
- );
135
- inputProps.onChange(mapped);
136
- }
137
- }
138
-
139
- FormInputArray.Header = ({ className, ...props }) => {
140
- return (
141
- <FormInputArray.Column
142
- className={classnames('mb-2', className)}
143
- {...props}
144
- />
145
- );
146
- };
147
-
148
- FormInputArray.Column = ({ className, children, ...props }) => {
149
- return (
150
- <Col className={classnames('d-flex', className)} {...props}>
151
- {children}
152
- </Col>
153
- );
154
- };
155
-
156
- FormInputArray.propTypes = {
157
- name: PropTypes.string.isRequired,
158
- label: PropTypes.string.isRequired,
159
-
160
- /**
161
- * The component to use for each element of the array
162
- **/
163
- component: PropTypes.func.isRequired,
164
-
165
- disabled: PropTypes.bool,
166
-
167
- /**
168
- * When 'Add Item' is used this object is passed as the default (so you can, as an example, give a default)
169
- **/
170
- newItem: PropTypes.object,
171
-
172
- /**
173
- * Affixes a 'header' above all rows so you can have a grid-like display
174
- **/
175
- prepend: PropTypes.func,
176
-
177
- /**
178
- * standard validator, BUT, will be passed the whole array to validate
179
- **/
180
- validate: PropTypes.oneOfType([
181
- PropTypes.arrayOf(PropTypes.func),
182
- PropTypes.func,
183
- ]),
184
- };
185
-
186
- function CardActionHeader({ children, className, ...props }) {
187
- return (
188
- <div
189
- className={`card-header card-action-header d-flex align-items-baseline ${className}`}
190
- {...props}>
191
- {children}
192
- </div>
193
- );
194
- }
195
-
196
- CardActionHeader.Title = function Title({ children, className, ...props }) {
197
- return (
198
- <strong className={`mr-auto ${className}`} {...props}>
199
- {children}
200
- </strong>
201
- );
202
- };
203
-
204
- CardActionHeader.Actions = function Actions({ children, className, ...props }) {
205
- return (
206
- <div className={`card-action-header-actions ${className}`} {...props}>
207
- {children}
208
- </div>
209
- );
210
- };
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { v4 as uuid } from 'uuid';
3
+ import classnames from 'classnames';
4
+ import { Col, Card, Button, CardBody, Row, Alert } from 'reactstrap';
5
+ import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
6
+ import {faPlus, faTrash} from '@fortawesome/free-solid-svg-icons'
7
+ import PropTypes from 'prop-types';
8
+ import useStandardFormInput from '../useStandardFormInput';
9
+ import NestedFormFieldContext from '../NestedFormFieldContext';
10
+ import classNames from 'classnames';
11
+
12
+ export default function FormInputArray({
13
+ id,
14
+ name,
15
+ label,
16
+ newItem,
17
+ component: Component,
18
+ prepend: Prepend,
19
+ disabled,
20
+ validate,
21
+ ...props
22
+ }) {
23
+ const [inputProps, meta] = useStandardFormInput({
24
+ id,
25
+ name,
26
+ validate,
27
+ disabled,
28
+ ...props,
29
+ });
30
+ const inputPropsRef = useRef();
31
+ const [hasChangedLength, setHasChangedLength] = useState(false);
32
+ inputPropsRef.current = inputProps;
33
+ const values = inputProps.value || [];
34
+ const length = values.length;
35
+
36
+ useEffect(() => {
37
+ return () => {
38
+ setHasChangedLength(true);
39
+ };
40
+ }, [length, setHasChangedLength]);
41
+
42
+ useEffect(() => {
43
+ if (!hasChangedLength) {
44
+ return;
45
+ }
46
+ inputPropsRef.current.onBlur();
47
+ }, [length, hasChangedLength]);
48
+
49
+ return (
50
+ <Card className="field-array-card">
51
+ <CardActionHeader>
52
+ <CardActionHeader.Title>{label}</CardActionHeader.Title>
53
+ <CardActionHeader.Actions>
54
+ {!disabled && (
55
+ <Button
56
+ color="link"
57
+ onClick={addItem}
58
+ title="Add an additional item"
59
+ className="add-array-item">
60
+ <FontAwesomeIcon icon={faPlus} /> Add New
61
+ </Button>
62
+ )}
63
+ </CardActionHeader.Actions>
64
+ </CardActionHeader>
65
+ <CardBody>
66
+ {/* See: https://jaredpalmer.com/formik/docs/api/fieldarray#fieldarray-validation-gotchas */}
67
+ {meta.error && typeof meta.error === 'string' && (
68
+ <Row>
69
+ <Col>
70
+ <Alert className="field-array-alert" color="danger">
71
+ {meta.error}
72
+ </Alert>
73
+ </Col>
74
+ </Row>
75
+ )}
76
+ {Prepend && values.filter((x) => !x.isDeleted).length > 0 && (
77
+ <Row className={classnames('input-array-header')}>
78
+ <Prepend />
79
+ {!disabled && <div className="remove-array-item"></div>}
80
+ </Row>
81
+ )}
82
+ {values.map((value, index) => {
83
+ const itemName = `${inputProps.name}[${index}]`;
84
+ return (
85
+ <Row
86
+ key={
87
+ (value && value['form-input-array-key']) ||
88
+ (value && value['id']) ||
89
+ itemName
90
+ }
91
+ className={classnames('field-array-item', {
92
+ removed: value.isDeleted,
93
+ })}
94
+ role="listitem">
95
+ <NestedFormFieldContext.Provider value={itemName}>
96
+ <div className="component">
97
+ <Component
98
+ value={value}
99
+ name={itemName}
100
+ disabled={disabled}
101
+ {...props}
102
+ />
103
+ </div>
104
+ </NestedFormFieldContext.Provider>
105
+ {!disabled && (
106
+ <div className="remove-array-item">
107
+ <Button
108
+ color="link"
109
+ onClick={() => removeItem(index)}
110
+ title="Remove Item">
111
+ <FontAwesomeIcon icon={faTrash} />
112
+ </Button>
113
+ </div>
114
+ )}
115
+ </Row>
116
+ );
117
+ })}
118
+ </CardBody>
119
+ </Card>
120
+ );
121
+
122
+ function addItem() {
123
+ if (disabled) {
124
+ return;
125
+ }
126
+ const item = Object.assign({}, newItem, { 'form-input-array-key': uuid() });
127
+ const mapped = [...values, item];
128
+ inputProps.onChange(mapped);
129
+ }
130
+
131
+ function removeItem(index) {
132
+ if (disabled) {
133
+ return;
134
+ }
135
+ const itemToRemove = values[index];
136
+ // assumes anything from the server has an 'id' value sent
137
+ if (!itemToRemove.id) {
138
+ inputProps.onChange(values.filter((x) => x !== itemToRemove));
139
+ return;
140
+ }
141
+ const mapped = values.map((x) =>
142
+ x !== itemToRemove ? x : Object.assign({}, x, { isDeleted: true })
143
+ );
144
+ inputProps.onChange(mapped);
145
+ }
146
+ }
147
+
148
+ FormInputArray.Header = ({ className, ...props }) => {
149
+ return (
150
+ <FormInputArray.Column
151
+ className={classnames('mb-2', className)}
152
+ {...props}
153
+ />
154
+ );
155
+ };
156
+
157
+ FormInputArray.Column = ({ className, children, ...props }) => {
158
+ return (
159
+ <Col className={classnames('d-flex', className)} {...props}>
160
+ {children}
161
+ </Col>
162
+ );
163
+ };
164
+
165
+ FormInputArray.propTypes = {
166
+ name: PropTypes.string.isRequired,
167
+ label: PropTypes.string.isRequired,
168
+
169
+ /**
170
+ * The component to use for each element of the array
171
+ **/
172
+ component: PropTypes.func.isRequired,
173
+
174
+ disabled: PropTypes.bool,
175
+
176
+ /**
177
+ * When 'Add Item' is used this object is passed as the default (so you can, as an example, give a default)
178
+ **/
179
+ newItem: PropTypes.object,
180
+
181
+ /**
182
+ * Affixes a 'header' above all rows so you can have a grid-like display
183
+ **/
184
+ prepend: PropTypes.func,
185
+
186
+ /**
187
+ * standard validator, BUT, will be passed the whole array to validate
188
+ **/
189
+ validate: PropTypes.oneOfType([
190
+ PropTypes.arrayOf(PropTypes.func),
191
+ PropTypes.func,
192
+ ]),
193
+ };
194
+
195
+ function CardActionHeader({ children, className, ...props }) {
196
+ return (
197
+ <div
198
+ className={classnames(
199
+ 'card-header card-action-header d-flex align-items-baseline',
200
+ className
201
+ )}
202
+ {...props}>
203
+ {children}
204
+ </div>
205
+ );
206
+ }
207
+
208
+ CardActionHeader.Title = function Title({ children, className, ...props }) {
209
+ return (
210
+ <strong className={classnames('mr-auto', className)} {...props}>
211
+ {children}
212
+ </strong>
213
+ );
214
+ };
215
+
216
+ CardActionHeader.Actions = function Actions({ children, className, ...props }) {
217
+ return (
218
+ <div
219
+ className={classnames('card-action-header-actions', className)}
220
+ {...props}>
221
+ {children}
222
+ </div>
223
+ );
224
+ };
@@ -1,59 +1,108 @@
1
- import React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
- import userEvent from '@testing-library/user-event';
4
- import { FormTestBase } from '../__Tests__';
5
- import FormInput from '../FormInput';
6
- import FormInputArray from './FormInputArray';
7
-
8
- describe('FormInputArray', () => {
9
- it('renders without crashing', () => {
10
- render(
11
- <FormTestBase>
12
- <FormInputArray
13
- label="Names"
14
- name="things"
15
- component={FirstLastNameInput}
16
- />
17
- </FormTestBase>
18
- );
19
- });
20
-
21
- it('has matching snapshot', () => {
22
- const renderResult = render(
23
- <FormTestBase>
24
- <FormInputArray
25
- label="Names"
26
- name="things"
27
- component={FirstLastNameInput}
28
- />
29
- </FormTestBase>
30
- );
31
- expect(renderResult.asFragment()).toMatchSnapshot();
32
- });
33
-
34
- it('renders a simple two input array', async () => {
35
- render(
36
- <FormTestBase>
37
- <FormInputArray
38
- label="Names"
39
- name="things"
40
- component={FirstLastNameInput}
41
- />
42
- </FormTestBase>
43
- );
44
- const addButton = screen.getByText('Add New');
45
- userEvent.click(addButton);
46
- userEvent.click(addButton);
47
- const firstNameElements = await screen.findAllByLabelText('First Name');
48
- expect(firstNameElements).toHaveLength(2);
49
- });
50
- });
51
-
52
- function FirstLastNameInput(props) {
53
- return (
54
- <>
55
- <FormInput label="First Name" name="firstName" type="text" />
56
- <FormInput label="Last Name" name="lastName" type="text" />
57
- </>
58
- );
59
- }
1
+ import React from 'react';
2
+ import { render, screen, waitFor, within } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { FormTestBase } from '../__Tests__';
5
+ import FormInput from '../FormInput';
6
+ import FormInputArray from './FormInputArray';
7
+
8
+ describe('FormInputArray', () => {
9
+ it('renders without crashing', () => {
10
+ render(
11
+ <FormTestBase>
12
+ <FormInputArray
13
+ label="Names"
14
+ name="things"
15
+ component={FirstLastNameInput}
16
+ />
17
+ </FormTestBase>
18
+ );
19
+ });
20
+
21
+ it('has matching snapshot', () => {
22
+ const renderResult = render(
23
+ <FormTestBase>
24
+ <FormInputArray
25
+ label="Names"
26
+ name="things"
27
+ component={FirstLastNameInput}
28
+ />
29
+ </FormTestBase>
30
+ );
31
+ expect(renderResult.asFragment()).toMatchSnapshot();
32
+ });
33
+
34
+ it('renders a simple two input array', async () => {
35
+ render(
36
+ <FormTestBase>
37
+ <FormInputArray
38
+ label="Names"
39
+ name="things"
40
+ component={FirstLastNameInput}
41
+ />
42
+ </FormTestBase>
43
+ );
44
+ const addButton = screen.getByText('Add New');
45
+ userEvent.click(addButton);
46
+ userEvent.click(addButton);
47
+ const firstNameElements = await screen.findAllByLabelText('First Name');
48
+ expect(firstNameElements).toHaveLength(2);
49
+ });
50
+
51
+ it('adds "removed" class to deleted items specified in initialValues', async () => {
52
+ const initialValues = {
53
+ things: [
54
+ {
55
+ id: 1,
56
+ firstName: 'John',
57
+ lastName: 'Doe',
58
+ },
59
+ ],
60
+ };
61
+
62
+ render(
63
+ <FormTestBase initialValues={initialValues}>
64
+ <FormInputArray
65
+ label="Names"
66
+ name="things"
67
+ component={FirstLastNameInput}
68
+ />
69
+ </FormTestBase>
70
+ );
71
+
72
+ const item = screen.getByRole(/listitem/i);
73
+ const deleteButton = within(item).getByTitle(/remove item/i);
74
+ expect(item).not.toHaveClass('removed');
75
+ userEvent.click(deleteButton);
76
+ expect(item).toHaveClass('removed');
77
+ });
78
+
79
+ it('removes deleted items not specified in initialValues', async () => {
80
+ render(
81
+ <FormTestBase>
82
+ <FormInputArray
83
+ label="Names"
84
+ name="things"
85
+ component={FirstLastNameInput}
86
+ />
87
+ </FormTestBase>
88
+ );
89
+
90
+ const addButton = screen.getByText(/add new/i);
91
+ userEvent.click(addButton);
92
+ const item = screen.getByRole(/listitem/i);
93
+ const deleteButton = within(item).getByTitle(/remove item/i);
94
+ userEvent.click(deleteButton);
95
+ await waitFor(() => {
96
+ expect(screen.queryByRole(/listitem/i)).not.toBeInTheDocument();
97
+ });
98
+ });
99
+ });
100
+
101
+ function FirstLastNameInput() {
102
+ return (
103
+ <>
104
+ <FormInput label="First Name" name="firstName" type="text" />
105
+ <FormInput label="Last Name" name="lastName" type="text" />
106
+ </>
107
+ );
108
+ }