From acc4e9479ce9a8c9a14ed874c50fdbe52408c1c6 Mon Sep 17 00:00:00 2001 From: Lily Hoskin Date: Fri, 19 Jul 2019 15:37:46 +0100 Subject: [PATCH 1/3] Add onChange props to React forms --- packages/react/src/JsonForms.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/react/src/JsonForms.tsx b/packages/react/src/JsonForms.tsx index 1106365784..6a3c98d04a 100644 --- a/packages/react/src/JsonForms.tsx +++ b/packages/react/src/JsonForms.tsx @@ -24,7 +24,7 @@ */ import isEqual from 'lodash/isEqual'; import maxBy from 'lodash/maxBy'; -import React from 'react'; +import React, { useEffect } from 'react'; import AJV from 'ajv'; import RefParser from 'json-schema-ref-parser'; import { UnknownRenderer } from './UnknownRenderer'; @@ -40,6 +40,7 @@ import { UISchemaElement, } from '@jsonforms/core'; import { ctxToJsonFormsDispatchProps, JsonFormsStateProvider, useJsonForms } from './JsonFormsContext'; +import { ErrorObject } from 'ajv'; interface JsonFormsRendererState { id: string; @@ -48,6 +49,10 @@ interface JsonFormsRendererState { resolvedSchema: JsonSchema; } +interface JsonFormsReactProps { + onChange?(state: {data: any, errors?: ErrorObject[]}): void; +} + const hasRefs = (schema: JsonSchema): boolean => { if (schema !== undefined) { return Object.keys(findRefs(schema)).length > 0; @@ -156,9 +161,13 @@ export class JsonFormsDispatchRenderer extends ResolvedJsonFormsDispatchRenderer } } -export const JsonFormsDispatch = (props: OwnPropsOfJsonFormsRenderer) => { +export const JsonFormsDispatch = (props: OwnPropsOfJsonFormsRenderer & JsonFormsReactProps) => { const ctx = useJsonForms(); const { refResolver } = ctxToJsonFormsDispatchProps(ctx, props); + useEffect(() => { + props.onChange && props.onChange({data: ctx.core.data, errors: ctx.core.errors}) + }, [ctx.core.data, ctx.core.errors]) + return ( { - const { ajv, data, schema, uischema, renderers, refParserOptions } = props; +export const JsonForms = (props: JsonFormsInitStateProps & JsonFormsReactProps) => { + const { ajv, data, schema, uischema, renderers, refParserOptions, onChange } = props; return ( { renderers }} > - + ); }; From 0d85940c27ba14c9ccbcb21101f16eaf09638183 Mon Sep 17 00:00:00 2001 From: Lily Hoskin Date: Mon, 22 Jul 2019 14:21:55 +0100 Subject: [PATCH 2/3] Add test for onChange handler --- .../react/test/renderers/JsonForms.test.tsx | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/packages/react/test/renderers/JsonForms.test.tsx b/packages/react/test/renderers/JsonForms.test.tsx index 1a79a62797..d098a8d1f4 100644 --- a/packages/react/test/renderers/JsonForms.test.tsx +++ b/packages/react/test/renderers/JsonForms.test.tsx @@ -41,6 +41,7 @@ import { } from '@jsonforms/core'; import Enzyme from 'enzyme'; import { mount, shallow } from 'enzyme'; +import { act } from 'react-dom/test-utils'; import RefParser from 'json-schema-ref-parser'; import { StatelessRenderer } from '../../src/Renderer'; @@ -49,6 +50,7 @@ import { JsonFormsDispatchRenderer, JsonFormsDispatch, JsonForms } from '../../s import { JsonFormsReduxContext, useJsonForms, + withJsonFormsControlProps, JsonFormsStateProvider } from '../../src/JsonFormsContext'; @@ -505,3 +507,97 @@ test('JsonForms should create a JsonFormsStateProvider with initState props', () expect(jsonFormsStateProviderInitStateProp.renderers).toBe(renderers); }); + +test('JsonForms should call onChange handler with new data', () => { + const onChangeHandler = jest.fn(); + const TestInputRenderer = withJsonFormsControlProps(props => ( + props.handleChange('foo', ev.target.value)} /> + )); + + const renderers = [ + { + tester: () => 10, + renderer: TestInputRenderer + } + ]; + const wrapper = mount( + + ); + + const event = { + target: { + value: 'Test Value' + } + } as React.ChangeEvent; + act(() => { + wrapper + .find('input') + .props() + .onChange(event); + }) + + const calls = onChangeHandler.mock.calls + const lastCallParameter = calls[calls.length - 1][0] + expect(lastCallParameter.data).toEqual({foo: 'Test Value'}) + expect(lastCallParameter.errors).toEqual([]) +}); + +test('JsonForms should call onChange handler with errors', () => { + const onChangeHandler = jest.fn(); + const TestInputRenderer = withJsonFormsControlProps(props => ( + props.handleChange('foo', ev.target.value)} /> + )); + + const schema = { + type: 'object', + properties: { + foo: { + type: 'string', + minLength: 5 + } + }, + required: ["foo"], + } + + const renderers = [ + { + tester: () => 10, + renderer: TestInputRenderer + } + ]; + const wrapper = mount( + + ); + + const event = { + target: { + value: "xyz" + } + } as React.ChangeEvent; + + act(() => { + wrapper + .find('input') + .props() + .onChange(event); + }) + + + const calls = onChangeHandler.mock.calls + const lastCallParameter = calls[calls.length - 1][0] + expect(lastCallParameter.data).toEqual({foo: "xyz"}) + expect(lastCallParameter.errors.length).toEqual(1) + expect(lastCallParameter.errors[0].keyword).toEqual("minLength") +}) From 8f91039a090b4007f630c749c81ee3dd4abd8ef7 Mon Sep 17 00:00:00 2001 From: Lily Hoskin Date: Tue, 23 Jul 2019 10:19:45 +0100 Subject: [PATCH 3/3] =?UTF-8?q?Use=20Pick=20type,=20use=20simulate=20chang?= =?UTF-8?q?e,=20linting=20=F0=9F=9A=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/react/src/JsonForms.tsx | 9 ++- .../react/test/renderers/JsonForms.test.tsx | 75 ++++++++----------- 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/packages/react/src/JsonForms.tsx b/packages/react/src/JsonForms.tsx index 6a3c98d04a..3123261dc2 100644 --- a/packages/react/src/JsonForms.tsx +++ b/packages/react/src/JsonForms.tsx @@ -38,9 +38,9 @@ import { OwnPropsOfJsonFormsRenderer, removeId, UISchemaElement, + JsonFormsCore } from '@jsonforms/core'; import { ctxToJsonFormsDispatchProps, JsonFormsStateProvider, useJsonForms } from './JsonFormsContext'; -import { ErrorObject } from 'ajv'; interface JsonFormsRendererState { id: string; @@ -50,7 +50,7 @@ interface JsonFormsRendererState { } interface JsonFormsReactProps { - onChange?(state: {data: any, errors?: ErrorObject[]}): void; + onChange?(state: Pick): void; } const hasRefs = (schema: JsonSchema): boolean => { @@ -164,9 +164,10 @@ export class JsonFormsDispatchRenderer extends ResolvedJsonFormsDispatchRenderer export const JsonFormsDispatch = (props: OwnPropsOfJsonFormsRenderer & JsonFormsReactProps) => { const ctx = useJsonForms(); const { refResolver } = ctxToJsonFormsDispatchProps(ctx, props); + const {data, errors} = ctx.core useEffect(() => { - props.onChange && props.onChange({data: ctx.core.data, errors: ctx.core.errors}) - }, [ctx.core.data, ctx.core.errors]) + props.onChange && props.onChange({ data, errors }); + }, [data, errors]); return ( { - , + ); expect(ids.indexOf('#/properties/foo') > -1).toBeTruthy(); @@ -530,22 +533,16 @@ test('JsonForms should call onChange handler with new data', () => { /> ); - const event = { + wrapper.find('input').simulate('change', { target: { value: 'Test Value' } - } as React.ChangeEvent; - act(() => { - wrapper - .find('input') - .props() - .onChange(event); - }) - - const calls = onChangeHandler.mock.calls - const lastCallParameter = calls[calls.length - 1][0] - expect(lastCallParameter.data).toEqual({foo: 'Test Value'}) - expect(lastCallParameter.errors).toEqual([]) + }); + + const calls = onChangeHandler.mock.calls; + const lastCallParameter = calls[calls.length - 1][0]; + expect(lastCallParameter.data).toEqual({ foo: 'Test Value' }); + expect(lastCallParameter.errors).toEqual([]); }); test('JsonForms should call onChange handler with errors', () => { @@ -555,15 +552,15 @@ test('JsonForms should call onChange handler with errors', () => { )); const schema = { - type: 'object', - properties: { - foo: { - type: 'string', - minLength: 5 - } - }, - required: ["foo"], - } + type: 'object', + properties: { + foo: { + type: 'string', + minLength: 5 + } + }, + required: ['foo'] + }; const renderers = [ { @@ -581,23 +578,15 @@ test('JsonForms should call onChange handler with errors', () => { /> ); - const event = { + wrapper.find('input').simulate('change', { target: { - value: "xyz" + value: 'xyz' } - } as React.ChangeEvent; - - act(() => { - wrapper - .find('input') - .props() - .onChange(event); - }) - - - const calls = onChangeHandler.mock.calls - const lastCallParameter = calls[calls.length - 1][0] - expect(lastCallParameter.data).toEqual({foo: "xyz"}) - expect(lastCallParameter.errors.length).toEqual(1) - expect(lastCallParameter.errors[0].keyword).toEqual("minLength") -}) + }); + + const calls = onChangeHandler.mock.calls; + const lastCallParameter = calls[calls.length - 1][0]; + expect(lastCallParameter.data).toEqual({ foo: 'xyz' }); + expect(lastCallParameter.errors.length).toEqual(1); + expect(lastCallParameter.errors[0].keyword).toEqual('minLength'); +});