Skip to content

Commit

Permalink
[Form lib] UseField onError listener (#89895) (#90023)
Browse files Browse the repository at this point in the history
* added callback for listening to field onerror events

* added onError component integration test

* address tslint issues

Co-authored-by: Kibana Machine <[email protected]>

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
jloleysens and kibanamachine authored Feb 3, 2021
1 parent ca3fe07 commit 7d36576
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -287,4 +287,55 @@ describe('<UseField />', () => {
expect(formHook?.getFormData()).toEqual({ name: 'myName' });
});
});

describe('change handlers', () => {
const onError = jest.fn();

beforeEach(() => {
jest.resetAllMocks();
});

const getTestComp = (fieldConfig: FieldConfig) => {
const TestComp = () => {
const { form } = useForm<any>();

return (
<Form form={form}>
<UseField path="name" config={fieldConfig} data-test-subj="myField" onError={onError} />
</Form>
);
};
return TestComp;
};

const setup = (fieldConfig: FieldConfig) => {
return registerTestBed(getTestComp(fieldConfig), {
memoryRouter: { wrapComponent: false },
})() as TestBed;
};

it('calls onError when validation state changes', async () => {
const {
form: { setInputValue },
} = setup({
validations: [
{
validator: ({ value }) => (value === '1' ? undefined : { message: 'oops!' }),
},
],
});

expect(onError).toBeCalledTimes(0);
await act(async () => {
setInputValue('myField', '0');
});
expect(onError).toBeCalledTimes(1);
expect(onError).toBeCalledWith(['oops!']);
await act(async () => {
setInputValue('myField', '1');
});
expect(onError).toBeCalledTimes(2);
expect(onError).toBeCalledWith(null);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface Props<T, FormType = FormData, I = T> {
componentProps?: Record<string, any>;
readDefaultValueOnForm?: boolean;
onChange?: (value: I) => void;
onError?: (errors: string[] | null) => void;
children?: (field: FieldHook<T, I>) => JSX.Element | null;
[key: string]: any;
}
Expand All @@ -33,6 +34,7 @@ function UseFieldComp<T = unknown, FormType = FormData, I = T>(props: Props<T, F
componentProps,
readDefaultValueOnForm = true,
onChange,
onError,
children,
...rest
} = props;
Expand Down Expand Up @@ -62,7 +64,7 @@ function UseFieldComp<T = unknown, FormType = FormData, I = T>(props: Props<T, F
}
}

const field = useField<T, FormType, I>(form, path, fieldConfig, onChange);
const field = useField<T, FormType, I>(form, path, fieldConfig, onChange, onError);

// Children prevails over anything else provided.
if (children) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export const useField = <T, FormType = FormData, I = T>(
form: FormHook<FormType>,
path: string,
config: FieldConfig<T, FormType, I> & InternalFieldConfig<T> = {},
valueChangeListener?: (value: I) => void
valueChangeListener?: (value: I) => void,
errorChangeListener?: (errors: string[] | null) => void
) => {
const {
type = FIELD_TYPES.TEXT,
Expand Down Expand Up @@ -596,6 +597,15 @@ export const useField = <T, FormType = FormData, I = T>(
};
}, [onValueChange]);

useEffect(() => {
if (!isMounted.current) {
return;
}
if (errorChangeListener) {
errorChangeListener(errors.length ? errors.map((error) => error.message) : null);
}
}, [errors, errorChangeListener]);

useEffect(() => {
isMounted.current = true;

Expand Down

0 comments on commit 7d36576

Please sign in to comment.