Skip to content

Commit

Permalink
[Form lib] Correctly add field to form on component mount (#75796)
Browse files Browse the repository at this point in the history
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
sebelga and elasticmachine committed Aug 27, 2020
1 parent 1c0a793 commit 8a980f5
Show file tree
Hide file tree
Showing 22 changed files with 479 additions and 370 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import React, { useState } from 'react';
import { act } from 'react-dom/test-utils';

import { registerTestBed, TestBed } from '../shared_imports';
Expand All @@ -36,13 +36,14 @@ describe('<FormDataProvider />', () => {
return (
<Form form={form}>
<UseField path="name" defaultValue="Initial value" data-test-subj="nameField" />
<UseField path="lastName" defaultValue="Initial value" data-test-subj="lastNameField" />
<FormDataProvider>
{(formData) => {
onFormData(formData);
return null;
}}
</FormDataProvider>
{/* Putting one field below to make sure the order in the DOM does not affect behaviour */}
<UseField path="lastName" defaultValue="Initial value" data-test-subj="lastNameField" />
</Form>
);
};
Expand Down Expand Up @@ -95,6 +96,63 @@ describe('<FormDataProvider />', () => {
});
});

test('should subscribe to the latest updated form data when mounting late', async () => {
const onFormData = jest.fn();

const TestComp = () => {
const { form } = useForm();
const [isOn, setIsOn] = useState(false);

return (
<Form form={form}>
<UseField path="name" defaultValue="Initial value" data-test-subj="nameField" />
<button onClick={() => setIsOn(true)} data-test-subj="btn">
Toggle On
</button>
{isOn && (
<FormDataProvider>
{(formData) => {
onFormData(formData);
return null;
}}
</FormDataProvider>
)}
</Form>
);
};

const setup = registerTestBed(TestComp, {
memoryRouter: { wrapComponent: false },
});

const {
form: { setInputValue },
find,
} = setup() as TestBed;

expect(onFormData.mock.calls.length).toBe(0); // Not present in the DOM yet

// Make some changes to the form fields
await act(async () => {
setInputValue('nameField', 'updated value');
});

// Update state to trigger the mounting of the FormDataProvider
await act(async () => {
find('btn').simulate('click').update();
});

expect(onFormData.mock.calls.length).toBe(1);

const [formDataUpdated] = onFormData.mock.calls[onFormData.mock.calls.length - 1] as Parameters<
OnUpdateHandler
>;

expect(formDataUpdated).toEqual({
name: 'updated value',
});
});

test('props.pathsToWatch (string): should not re-render the children when the field that changed is not the one provided', async () => {
const onFormData = jest.fn();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const FormDataProvider = React.memo(({ children, pathsToWatch }: Props) =
const form = useFormContext();
const { subscribe } = form;
const previousRawData = useRef<FormData>(form.__getFormData$().value);
const isMounted = useRef(false);
const [formData, setFormData] = useState<FormData>(previousRawData.current);

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

useEffect(() => {
isMounted.current = true;
return () => {
isMounted.current = false;
};
}, []);

if (!isMounted.current && Object.keys(formData).length === 0) {
// No field has mounted yet, don't render anything
return null;
}

return children(formData);
});
Loading

0 comments on commit 8a980f5

Please sign in to comment.