Skip to content

Commit 35f6c70

Browse files
ryan-bennettsdirix
authored andcommitted
Add React Material oneOf enum cell
Also includes example and test cases.
1 parent e586672 commit 35f6c70

File tree

10 files changed

+268
-2
lines changed

10 files changed

+268
-2
lines changed

packages/core/src/util/cell.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { JsonFormsState } from '../store';
3838
import { JsonFormsCellRendererRegistryEntry } from '../reducers/cells';
3939
import { JsonSchema } from '../models/jsonSchema';
4040
import { isInherentlyEnabled, isVisible } from './runtime';
41-
import { AnyAction, Dispatch, formatErrorMessage, Resolve } from '.';
41+
import { AnyAction, Dispatch, formatErrorMessage, oneOfToEnumOptionMapper, Resolve } from '.';
4242

4343
export { JsonFormsCellRendererRegistryEntry };
4444

@@ -189,6 +189,27 @@ export const defaultMapStateToEnumCellProps = (
189189
};
190190
};
191191

192+
/**
193+
* mapStateToOneOfEnumCellProps for one of enum cell. Options is used for populating dropdown list from oneOf
194+
* @param state
195+
* @param ownProps
196+
* @returns {StatePropsOfEnumCell}
197+
*/
198+
export const mapStateToOneOfEnumCellProps = (
199+
state: JsonFormsState,
200+
ownProps: OwnPropsOfEnumCell
201+
): StatePropsOfEnumCell => {
202+
const props: StatePropsOfCell = mapStateToCellProps(state, ownProps);
203+
const options: EnumOption[] =
204+
ownProps.options ||
205+
(props.schema.oneOf as JsonSchema[])?.map(oneOfToEnumOptionMapper);
206+
return {
207+
...props,
208+
options
209+
};
210+
};
211+
212+
192213
/**
193214
* Synonym for mapDispatchToControlProps.
194215
*

packages/core/test/util/cell.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
defaultMapDispatchToControlProps,
3131
defaultMapStateToEnumCellProps,
3232
DispatchPropsOfCell,
33-
mapStateToCellProps
33+
mapStateToCellProps, mapStateToOneOfEnumCellProps, oneOfToEnumOptionMapper
3434
} from '../../src/util';
3535
import { UPDATE_DATA, UpdateAction } from '../../src/actions';
3636
import configureStore from 'redux-mock-store';
@@ -291,6 +291,34 @@ test('mapStateToEnumCellProps - set default options for dropdown list', t => {
291291
t.is(props.data, undefined);
292292
});
293293

294+
295+
test('mapStateToOneOfEnumCellProps - set one of options for dropdown list', t => {
296+
const uischema: ControlElement = {
297+
type: 'Control',
298+
scope: '#/properties/country'
299+
};
300+
const ownProps = {
301+
schema: {
302+
oneOf: [
303+
{
304+
const: 'AU',
305+
title: 'Australia'
306+
},
307+
{
308+
const: 'NZ',
309+
title: 'New Zealand'
310+
}
311+
]
312+
},
313+
uischema,
314+
path: 'country'
315+
};
316+
317+
const props = mapStateToOneOfEnumCellProps(createState(uischema), ownProps);
318+
t.deepEqual(props.options, [{title: 'Australia' , const: 'AU', }, { title: 'New Zealand', const: 'NZ' }].map(oneOfToEnumOptionMapper));
319+
t.is(props.data, undefined);
320+
});
321+
294322
test('defaultMapDispatchToControlProps, initialized with custom handleChange', t => {
295323
let didChange = false;
296324
const uiSchema = {

packages/examples/src/enumInArray.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { registerExamples } from './register';
2+
3+
export const schema = {
4+
type: 'array',
5+
items: {
6+
type: 'object',
7+
properties: {
8+
plainEnum: {
9+
type: 'string',
10+
enum: ['foo', 'bar']
11+
},
12+
oneOfEnum: {
13+
type: 'string',
14+
oneOf: [
15+
{ const: 'foo', title: 'Foo' },
16+
{ const: 'bar', title: 'Bar' },
17+
{ const: 'foobar', title: 'FooBar' }
18+
]
19+
},
20+
}
21+
}
22+
};
23+
24+
export const uischema = {
25+
type: 'VerticalLayout',
26+
elements: [
27+
{
28+
type: 'Control',
29+
scope: '#'
30+
}
31+
]
32+
};
33+
34+
export const data: any[] = [];
35+
36+
registerExamples([
37+
{
38+
name: 'enumInArray',
39+
label: 'Array containing enums',
40+
data,
41+
schema,
42+
uischema
43+
}
44+
]);

packages/examples/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ import * as enumExample from './enum';
7272
import * as radioGroupExample from './radioGroup';
7373
import * as booleanToggle from './booleanToggle';
7474
import * as multiEnum from './multi-enum';
75+
import * as enumInArray from './enumInArray';
7576
import * as readonly from './readonly';
7677
export * from './register';
7778
export * from './example';
@@ -130,5 +131,6 @@ export {
130131
radioGroupExample,
131132
booleanToggle,
132133
multiEnum,
134+
enumInArray,
133135
readonly
134136
};

packages/material/src/cells/CustomizableCells.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ export { MaterialEnumCell } from './MaterialEnumCell';
2828
export { MaterialIntegerCell } from './MaterialIntegerCell';
2929
export { MaterialNumberCell } from './MaterialNumberCell';
3030
export { MaterialNumberFormatCell } from './MaterialNumberFormatCell';
31+
export { MaterialOneOfEnumCell } from './MaterialOneOfEnumCell';
3132
export { MaterialTextCell } from './MaterialTextCell';
3233
export { MaterialTimeCell } from './MaterialTimeCell';
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
The MIT License
3+
4+
Copyright (c) 2017-2019 EclipseSource Munich
5+
https://github.com/eclipsesource/jsonforms
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
*/
25+
import React from 'react';
26+
import {
27+
EnumCellProps,
28+
isOneOfEnumControl,
29+
RankedTester,
30+
rankWith,
31+
WithClassname
32+
} from '@jsonforms/core';
33+
import { withJsonFormsOneOfEnumCellProps } from '@jsonforms/react';
34+
import { MuiSelect } from '../mui-controls/MuiSelect';
35+
36+
export const MaterialOneOfEnumCell = (props: EnumCellProps & WithClassname) => (
37+
<MuiSelect {...props} />
38+
);
39+
40+
/**
41+
* Default tester for oneOf enum controls.
42+
* @type {RankedTester}
43+
*/
44+
export const materialOneOfEnumCellTester: RankedTester = rankWith(2, isOneOfEnumControl);
45+
46+
export default withJsonFormsOneOfEnumCellProps(MaterialOneOfEnumCell);

packages/material/src/cells/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ import MaterialNumberCell, {
3939
import MaterialNumberFormatCell, {
4040
materialNumberFormatCellTester
4141
} from './MaterialNumberFormatCell';
42+
import MaterialOneOfEnumCell, {
43+
materialOneOfEnumCellTester
44+
} from './MaterialOneOfEnumCell';
4245
import MaterialTextCell, { materialTextCellTester } from './MaterialTextCell';
4346
import MaterialTimeCell, { materialTimeCellTester } from './MaterialTimeCell';
4447

@@ -57,6 +60,8 @@ export {
5760
materialNumberCellTester,
5861
MaterialNumberFormatCell,
5962
materialNumberFormatCellTester,
63+
MaterialOneOfEnumCell,
64+
materialOneOfEnumCellTester,
6065
MaterialTextCell,
6166
materialTextCellTester,
6267
MaterialTimeCell,

packages/material/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ import {
103103
materialNumberCellTester,
104104
MaterialNumberFormatCell,
105105
materialNumberFormatCellTester,
106+
MaterialOneOfEnumCell,
107+
materialOneOfEnumCellTester,
106108
MaterialTextCell,
107109
materialTextCellTester,
108110
MaterialTimeCell,
@@ -188,6 +190,7 @@ export const materialCells: JsonFormsCellRendererRegistryEntry[] = [
188190
{ tester: materialIntegerCellTester, cell: MaterialIntegerCell },
189191
{ tester: materialNumberCellTester, cell: MaterialNumberCell },
190192
{ tester: materialNumberFormatCellTester, cell: MaterialNumberFormatCell },
193+
{ tester: materialOneOfEnumCellTester, cell: MaterialOneOfEnumCell },
191194
{ tester: materialTextCellTester, cell: MaterialTextCell },
192195
{ tester: materialTimeCellTester, cell: MaterialTimeCell }
193196
];
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
The MIT License
3+
4+
Copyright (c) 2017-2019 EclipseSource Munich
5+
https://github.com/eclipsesource/jsonforms
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
*/
25+
import './MatchMediaMock';
26+
import * as React from 'react';
27+
import {
28+
ControlElement
29+
} from '@jsonforms/core';
30+
import MaterialOneOfEnumCell, {
31+
materialOneOfEnumCellTester
32+
} from '../../src/cells/MaterialOneOfEnumCell';
33+
import { materialRenderers } from '../../src';
34+
35+
import Enzyme, { mount } from 'enzyme';
36+
import Adapter from 'enzyme-adapter-react-16';
37+
import { JsonFormsStateProvider } from '@jsonforms/react';
38+
import { initCore } from './util';
39+
40+
Enzyme.configure({ adapter: new Adapter() });
41+
42+
const data = { country: 'AU' };
43+
const schema = {
44+
type: 'string',
45+
oneOf: [
46+
{
47+
const: 'AU',
48+
title: 'Australia'
49+
},
50+
{
51+
const: 'NZ',
52+
title: 'New Zealand'
53+
}
54+
]
55+
};
56+
const uischema: ControlElement = {
57+
type: 'Control',
58+
scope: '#/properties/country'
59+
};
60+
61+
describe('Material one of enum cell tester', () => {
62+
it('should succeed with matching prop type', () => {
63+
const control: ControlElement = {
64+
type: 'Control',
65+
scope: '#/properties/country'
66+
};
67+
expect(
68+
materialOneOfEnumCellTester(control, {
69+
type: 'object',
70+
properties: {
71+
country: schema
72+
}
73+
})
74+
).toBe(2);
75+
});
76+
});
77+
78+
describe('Material enum cell', () => {
79+
it('should select an item from dropdown list', () => {
80+
const core = initCore(schema, uischema, data);
81+
const wrapper = mount(
82+
<JsonFormsStateProvider initState={{ renderers: materialRenderers, core }}>
83+
<MaterialOneOfEnumCell
84+
schema={schema}
85+
uischema={uischema}
86+
path='country'
87+
/>
88+
</JsonFormsStateProvider>
89+
);
90+
const input = wrapper.find('input');
91+
expect(input.props().value).toBe('AU');
92+
});
93+
});

packages/react/src/JsonFormsContext.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import {
6565
mapStateToMasterListItemProps,
6666
mapStateToOneOfProps,
6767
mapStateToOneOfEnumControlProps,
68+
mapStateToOneOfEnumCellProps,
6869
mapDispatchToMultiEnumProps,
6970
update,
7071
mapStateToMultiEnumControlProps,
@@ -271,6 +272,13 @@ export const ctxToEnumCellProps = (
271272
return defaultMapStateToEnumCellProps({ jsonforms: { ...ctx } }, ownProps);
272273
};
273274

275+
export const ctxToOneOfEnumCellProps = (
276+
ctx: JsonFormsStateContext,
277+
props: OwnPropsOfControl
278+
) => {
279+
return mapStateToOneOfEnumCellProps({jsonforms: {...ctx}}, props);
280+
};
281+
274282
export const ctxToDispatchCellProps = (
275283
ctx: JsonFormsStateContext,
276284
ownProps: OwnPropsOfCell
@@ -402,6 +410,14 @@ const withContextToEnumProps =
402410
return (<Component {...props} {...dispatchProps} {...stateProps} options={stateProps.options} />);
403411
};
404412

413+
const withContextToOneOfEnumCellProps =
414+
(Component: ComponentType<EnumCellProps>): ComponentType<OwnPropsOfEnumCell> =>
415+
({ ctx, props }: JsonFormsStateContext & EnumCellProps) => {
416+
const cellProps = ctxToOneOfEnumCellProps(ctx, props);
417+
const dispatchProps = ctxDispatchToControlProps(ctx.dispatch);
418+
return (<Component {...props} {...dispatchProps} {...cellProps} />);
419+
};
420+
405421
const withContextToOneOfEnumProps =
406422
(Component: ComponentType<ControlProps & OwnPropsOfEnum>): ComponentType<OwnPropsOfControl & OwnPropsOfEnum> =>
407423
({ ctx, props }: JsonFormsStateContext & ControlProps & OwnPropsOfEnum) => {
@@ -532,6 +548,13 @@ export const withJsonFormsEnumProps =
532548
(prevProps: ControlProps & OwnPropsOfEnum, nextProps: ControlProps & OwnPropsOfEnum) => areEqual(prevProps, nextProps)
533549
)));
534550

551+
export const withJsonFormsOneOfEnumCellProps =
552+
(Component: ComponentType<EnumCellProps>): ComponentType<OwnPropsOfEnumCell> =>
553+
withJsonFormsContext(withContextToOneOfEnumCellProps(React.memo(
554+
Component,
555+
(prevProps: EnumCellProps, nextProps: EnumCellProps) => areEqual(prevProps, nextProps)
556+
)));
557+
535558
export const withJsonFormsOneOfEnumProps =
536559
(Component: ComponentType<ControlProps & OwnPropsOfEnum>): ComponentType<OwnPropsOfControl & OwnPropsOfEnum> =>
537560
withJsonFormsContext(withContextToOneOfEnumProps(React.memo(

0 commit comments

Comments
 (0)