Skip to content

Commit ec15160

Browse files
[Form lib] Correctly add field to form on component mount (#75796)
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
1 parent 8e2cb56 commit ec15160

File tree

22 files changed

+479
-370
lines changed

22 files changed

+479
-370
lines changed

src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.test.tsx

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
import React from 'react';
19+
import React, { useState } from 'react';
2020
import { act } from 'react-dom/test-utils';
2121

2222
import { registerTestBed, TestBed } from '../shared_imports';
@@ -36,13 +36,14 @@ describe('<FormDataProvider />', () => {
3636
return (
3737
<Form form={form}>
3838
<UseField path="name" defaultValue="Initial value" data-test-subj="nameField" />
39-
<UseField path="lastName" defaultValue="Initial value" data-test-subj="lastNameField" />
4039
<FormDataProvider>
4140
{(formData) => {
4241
onFormData(formData);
4342
return null;
4443
}}
4544
</FormDataProvider>
45+
{/* Putting one field below to make sure the order in the DOM does not affect behaviour */}
46+
<UseField path="lastName" defaultValue="Initial value" data-test-subj="lastNameField" />
4647
</Form>
4748
);
4849
};
@@ -95,6 +96,63 @@ describe('<FormDataProvider />', () => {
9596
});
9697
});
9798

99+
test('should subscribe to the latest updated form data when mounting late', async () => {
100+
const onFormData = jest.fn();
101+
102+
const TestComp = () => {
103+
const { form } = useForm();
104+
const [isOn, setIsOn] = useState(false);
105+
106+
return (
107+
<Form form={form}>
108+
<UseField path="name" defaultValue="Initial value" data-test-subj="nameField" />
109+
<button onClick={() => setIsOn(true)} data-test-subj="btn">
110+
Toggle On
111+
</button>
112+
{isOn && (
113+
<FormDataProvider>
114+
{(formData) => {
115+
onFormData(formData);
116+
return null;
117+
}}
118+
</FormDataProvider>
119+
)}
120+
</Form>
121+
);
122+
};
123+
124+
const setup = registerTestBed(TestComp, {
125+
memoryRouter: { wrapComponent: false },
126+
});
127+
128+
const {
129+
form: { setInputValue },
130+
find,
131+
} = setup() as TestBed;
132+
133+
expect(onFormData.mock.calls.length).toBe(0); // Not present in the DOM yet
134+
135+
// Make some changes to the form fields
136+
await act(async () => {
137+
setInputValue('nameField', 'updated value');
138+
});
139+
140+
// Update state to trigger the mounting of the FormDataProvider
141+
await act(async () => {
142+
find('btn').simulate('click').update();
143+
});
144+
145+
expect(onFormData.mock.calls.length).toBe(1);
146+
147+
const [formDataUpdated] = onFormData.mock.calls[onFormData.mock.calls.length - 1] as Parameters<
148+
OnUpdateHandler
149+
>;
150+
151+
expect(formDataUpdated).toEqual({
152+
name: 'updated value',
153+
});
154+
});
155+
98156
test('props.pathsToWatch (string): should not re-render the children when the field that changed is not the one provided', async () => {
99157
const onFormData = jest.fn();
100158

src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) =
3131
const form = useFormContext();
3232
const { subscribe } = form;
3333
const previousRawData = useRef<FormData>(form.__getFormData$().value);
34+
const isMounted = useRef(false);
3435
const [formData, setFormData] = useState<FormData>(previousRawData.current);
3536

3637
const onFormData = useCallback(
@@ -59,5 +60,17 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) =
5960
return subscription.unsubscribe;
6061
}, [subscribe, onFormData]);
6162

63+
useEffect(() => {
64+
isMounted.current = true;
65+
return () => {
66+
isMounted.current = false;
67+
};
68+
}, []);
69+
70+
if (!isMounted.current && Object.keys(formData).length === 0) {
71+
// No field has mounted yet, don't render anything
72+
return null;
73+
}
74+
6275
return children(formData);
6376
});

0 commit comments

Comments
 (0)