Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework JsonFormsStateProvider #1583

Merged
merged 2 commits into from May 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 7 additions & 9 deletions packages/core/src/actions/index.ts
Expand Up @@ -23,12 +23,12 @@
THE SOFTWARE.
*/
import AJV, { ErrorObject } from 'ajv';
import RefParser from 'json-schema-ref-parser';
import { RankedTester } from '../testers';
import { JsonSchema, UISchemaElement } from '../';
import { generateDefaultUISchema, generateJsonSchema } from '../generators';

import { RankedTester } from '../testers';
import RefParser from 'json-schema-ref-parser';
import { UISchemaTester } from '../reducers/uischemas';
import { AnyAction, Dispatch } from 'redux';

export const INIT: 'jsonforms/INIT' = 'jsonforms/INIT';
export const SET_AJV: 'jsonforms/SET_AJV' = 'jsonforms/SET_AJV';
Expand Down Expand Up @@ -201,12 +201,10 @@ export interface SetConfigAction {
config: any;
}

export const setConfig = (config: any) => (dispatch: Dispatch<AnyAction>) => {
dispatch({
type: SET_CONFIG,
config
});
};
export const setConfig = (config: any) => ({
type: SET_CONFIG,
config,
});

export type UISchemaActions = AddUISchemaAction | RemoveUISchemaAction;

Expand Down
75 changes: 38 additions & 37 deletions packages/core/src/reducers/index.ts
@@ -1,3 +1,39 @@
import { ControlElement, UISchemaElement } from '../models/uischema';
import {
JsonFormsCore,
coreReducer,
errorAt,
errorsAt,
extractData,
extractRefParserOptions,
extractSchema,
extractUiSchema,
subErrorsAt
} from './core';
import {
JsonFormsDefaultDataRegistryEntry,
defaultDataReducer,
extractDefaultData
} from './default-data';
import { JsonFormsRendererRegistryEntry, rendererReducer } from './renderers';
import { JsonFormsState, JsonFormsSubStates } from '../store';
import { Reducer, combineReducers } from 'redux';
import {
UISchemaTester,
findMatchingUISchema,
uischemaRegistryReducer
} from './uischemas';
import {
fetchLocale,
findLocalizedSchema,
findLocalizedUISchema,
i18nReducer
} from './i18n';

import { Generate } from '../generators';
import { JsonFormsCellRendererRegistryEntry } from './cells';
import { JsonSchema } from '../models/jsonSchema';
import RefParser from 'json-schema-ref-parser';
/*
The MIT License

Expand All @@ -23,50 +59,15 @@
THE SOFTWARE.
*/
import { cellReducer } from './cells';
import get from 'lodash/get';
import {
defaultDataReducer,
extractDefaultData,
JsonFormsDefaultDataRegistryEntry
} from './default-data';
import { combineReducers, Reducer } from 'redux';
import { JsonFormsRendererRegistryEntry, rendererReducer } from './renderers';
import RefParser from 'json-schema-ref-parser';
import { configReducer } from './config';
import {
coreReducer,
errorAt,
errorsAt,
extractData,
extractRefParserOptions,
extractSchema,
extractUiSchema,
JsonFormsCore,
subErrorsAt
} from './core';
import { JsonFormsState, JsonFormsSubStates } from '../store';
import {
findMatchingUISchema,
uischemaRegistryReducer,
UISchemaTester
} from './uischemas';
import {
fetchLocale,
findLocalizedSchema,
findLocalizedUISchema,
i18nReducer
} from './i18n';

import { JsonSchema } from '../models/jsonSchema';
import { ControlElement, UISchemaElement } from '../models/uischema';
import { Generate } from '../generators';
import { JsonFormsCellRendererRegistryEntry } from './cells';
import get from 'lodash/get';

export {
rendererReducer,
cellReducer,
coreReducer,
i18nReducer,
configReducer,
UISchemaTester,
uischemaRegistryReducer,
findMatchingUISchema
Expand Down
2 changes: 1 addition & 1 deletion packages/example/src/reduxUtil.ts
Expand Up @@ -84,7 +84,7 @@ const mapDispatchToProps = (dispatch: Dispatch<AnyAction>) => ({
changeExampleData: (example: ReactExampleDescription) => {
dispatch(changeExample(example));
dispatch(Actions.init(example.data, example.schema, example.uischema));
Actions.setConfig(example.config)(dispatch);
dispatch(Actions.setConfig(example.config));
},
getComponent: (example: ReactExampleDescription) =>
example.customReactExtension
Expand Down
7 changes: 5 additions & 2 deletions packages/react/src/JsonForms.tsx
Expand Up @@ -218,6 +218,7 @@ export interface JsonFormsInitStateProps {
cells?: JsonFormsCellRendererRegistryEntry[];
ajv?: AJV.Ajv;
refParserOptions?: RefParser.Options;
config?: any;
}

export const JsonForms = (
Expand All @@ -231,7 +232,8 @@ export const JsonForms = (
renderers,
cells,
refParserOptions,
onChange
onChange,
config,
} = props;
const schemaToUse = schema !== undefined ? schema : Generate.jsonSchema(data);
const uischemaToUse =
Expand All @@ -246,11 +248,12 @@ export const JsonForms = (
schema: schemaToUse,
uischema: uischemaToUse
},
config,
renderers,
cells
}}
>
<JsonFormsDispatch onChange={onChange} />
</JsonFormsStateProvider>
);
};
};
95 changes: 65 additions & 30 deletions packages/react/src/JsonFormsContext.tsx
Expand Up @@ -23,55 +23,55 @@
THE SOFTWARE.
*/

import React, { ComponentType, Dispatch, ReducerAction, useCallback, useContext, useEffect, useReducer } from 'react';
import isEqual from 'lodash/isEqual';
import omit from 'lodash/omit';
import get from 'lodash/get';
import {
Actions,
ArrayControlProps,
ArrayLayoutProps,
CellProps,
CombinatorProps,
ControlProps,
coreReducer,
DispatchCellProps,
DispatchPropsOfControl,
EnumCellProps,
JsonFormsCore,
JsonFormsState,
JsonFormsSubStates,
LayoutProps,
OwnPropsOfCell,
OwnPropsOfControl,
OwnPropsOfEnum,
OwnPropsOfEnumCell,
OwnPropsOfJsonFormsRenderer,
OwnPropsOfLayout,
OwnPropsOfMasterListItem,
OwnPropsOfRenderer,
StatePropsOfCombinator,
StatePropsOfControlWithDetail,
StatePropsOfMasterItem,
configReducer,
coreReducer,
mapDispatchToArrayControlProps,
mapStateToAllOfProps,
mapStateToAnyOfProps,
mapStateToArrayControlProps,
mapStateToArrayLayoutProps,
mapStateToCellProps,
mapStateToControlProps,
mapStateToEnumControlProps,
mapStateToControlWithDetailProps,
mapStateToDispatchCellProps,
mapStateToEnumControlProps,
mapStateToJsonFormsRendererProps,
mapStateToLayoutProps,
mapStateToMasterListItemProps,
mapStateToOneOfProps,
OwnPropsOfCell,
OwnPropsOfControl,
OwnPropsOfEnum,
OwnPropsOfEnumCell,
OwnPropsOfJsonFormsRenderer,
OwnPropsOfMasterListItem,
rendererReducer,
StatePropsOfCombinator,
StatePropsOfControlWithDetail,
StatePropsOfMasterItem,
update,
OwnPropsOfLayout,
OwnPropsOfRenderer,
DispatchCellProps,
mapStateToDispatchCellProps,
cellReducer
update
} from '@jsonforms/core';
import React, { ComponentType, Dispatch, ReducerAction, useCallback, useContext, useEffect, useReducer, useRef } from 'react';

import { connect } from 'react-redux';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import omit from 'lodash/omit';

const initialCoreState: JsonFormsCore = {
data: {},
Expand All @@ -92,22 +92,57 @@ export const JsonFormsContext = React.createContext<JsonFormsStateContext>({
renderers: []
});

/**
* Hook similar to `useEffect` with the difference that the effect
* is only executed from the second call onwards.
*/
const useEffectAfterFirstRender = (
effect: () => void,
dependencies: Array<any>
) => {
const firstExecution = useRef(true);
useEffect(() => {
if (firstExecution.current) {
firstExecution.current = false;
return;
}
effect();
}, dependencies);
};

export const JsonFormsStateProvider = ({ children, initState }: any) => {
const [core, dispatch] = useReducer(coreReducer, initState.core);
const [renderers] = useReducer(rendererReducer, initState.renderers);
const [cells] = useReducer(cellReducer, initState.cells);
const { data, schema, uischema, ajv, refParserOptions } = initState.core;
useEffect(() => {
dispatch(Actions.init(data, schema, uischema, { ajv, refParserOptions }));
// Initialize core immediatly
const [core, coreDispatch] = useReducer(
coreReducer,
coreReducer(
initState.core,
Actions.init(data, schema, uischema, { ajv, refParserOptions })
)
);
useEffectAfterFirstRender(() => {
coreDispatch(
Actions.init(data, schema, uischema, { ajv, refParserOptions })
);
}, [data, schema, uischema, ajv, refParserOptions]);

const [config, configDispatch] = useReducer(
configReducer,
configReducer(undefined, Actions.setConfig(initState.config))
);
useEffectAfterFirstRender(() => {
configDispatch(Actions.setConfig(initState.config));
}, [initState.config]);

return (
<JsonFormsContext.Provider
value={{
core,
renderers,
cells,
renderers: initState.renderers,
cells: initState.cells,
config: config,
// only core dispatch available
dispatch
dispatch: coreDispatch,
}}
>
{children}
Expand Down
25 changes: 25 additions & 0 deletions packages/react/test/renderers/JsonForms.test.tsx
Expand Up @@ -698,6 +698,31 @@ test('JsonForms should create a JsonFormsStateProvider with initState props', ()
expect(jsonFormsStateProviderInitStateProp.renderers).toBe(renderers);
});

test('JsonForms should honor config passed via initState props', () => {

const customRenderer = () => {
const ctx = useJsonForms();
return <h2>{ctx.config.myConfigProperty}</h2>;
};
const renderers = [
{ tester: () => 30, renderer: customRenderer }
];

const wrapper = mount(
<JsonForms
data={fixture.data}
uischema={fixture.uischema}
schema={fixture.schema}
renderers={renderers}
config={{myConfigProperty: 'true'}}
/>
);

wrapper.update();
expect(wrapper.find('h2').text()).toBe('true');
wrapper.unmount();
});

test('JsonForms should generate an ui schema when uischema is not given', () => {
const schema = {
type: 'object',
Expand Down