Skip to content

Commit

Permalink
[Form lib] Fixes (#128489)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebelga authored Apr 12, 2022
1 parent b0591a4 commit 092c71a
Show file tree
Hide file tree
Showing 11 changed files with 445 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React, { useEffect } from 'react';
import { act } from 'react-dom/test-utils';

import { registerTestBed } from '../shared_imports';
import { useForm } from '../hooks/use_form';
import { useFormData } from '../hooks/use_form_data';
import { Form } from './form';
import { UseField } from './use_field';
import { UseArray } from './use_array';

describe('<UseArray />', () => {
beforeAll(() => {
jest.useFakeTimers();
});

afterAll(() => {
jest.useRealTimers();
});

test('it should render by default 1 array item', () => {
const TestComp = () => {
const { form } = useForm();
return (
<Form form={form}>
<UseArray path="myArray">
{({ items }) => {
return (
<>
{items.map(({ id }) => {
return (
<p key={id} data-test-subj="arrayItem">
Array item
</p>
);
})}
</>
);
}}
</UseArray>
</Form>
);
};

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

const { find } = setup();

expect(find('arrayItem').length).toBe(1);
});

test('it should allow to listen to array item field value change', async () => {
const onFormData = jest.fn();

const TestComp = ({ onData }: { onData: (data: any) => void }) => {
const { form } = useForm();
const [formData] = useFormData({ form, watch: 'users[0].name' });

useEffect(() => {
onData(formData);
}, [onData, formData]);

return (
<Form form={form}>
<UseArray path="users">
{({ items }) => {
return (
<>
{items.map(({ id, path }) => {
return (
<UseField
key={id}
path={`${path}.name`}
data-test-subj={`nameField__${id}`}
/>
);
})}
</>
);
}}
</UseArray>
</Form>
);
};

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

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

await act(async () => {
setInputValue('nameField__0', 'John');
});

const formData = onFormData.mock.calls[onFormData.mock.calls.length - 1][0];

expect(formData.users[0].name).toEqual('John');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Side Public License, v 1.
*/

import uuid from 'uuid';
import { useEffect, useRef, useCallback, useMemo } from 'react';

import { FormHook, FieldConfig } from '../types';
Expand Down Expand Up @@ -53,7 +54,7 @@ export interface FormArrayField {
*/
export const UseArray = ({
path,
initialNumberOfItems,
initialNumberOfItems = 1,
validations,
readDefaultValueOnForm = true,
children,
Expand Down Expand Up @@ -92,6 +93,9 @@ export const UseArray = ({
// Create an internal hook field which behaves like any other form field except that it is not
// outputed in the form data (when calling form.submit() or form.getFormData())
// This allow us to run custom validations (passed to the props) on the Array items

const internalFieldPath = useMemo(() => `${path}__${uuid.v4()}`, [path]);

const fieldConfigBase: FieldConfig<ArrayItem[]> & InternalFieldConfig<ArrayItem[]> = {
defaultValue: fieldDefaultValue,
initialValue: fieldDefaultValue,
Expand All @@ -103,7 +107,7 @@ export const UseArray = ({
? { validations, ...fieldConfigBase }
: fieldConfigBase;

const field = useField(form, path, fieldConfig);
const field = useField(form, internalFieldPath, fieldConfig);
const { setValue, value, isChangingValue, errors } = field;

// Derived state from the field
Expand Down
Loading

0 comments on commit 092c71a

Please sign in to comment.