Skip to content

Commit 30c1b79

Browse files
authored
react-material: Add Form Label and description to enum array renderer (#2297)
- Add form labels and description to MaterialEnumArrayRenderer - Improve ids to be unique for checkboxes and their labels - Add example for enum array with label and description - Remove various obsolete `as` typecasts for FormLabel components
1 parent 21dd591 commit 30c1b79

File tree

6 files changed

+191
-8
lines changed

6 files changed

+191
-8
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
The MIT License
3+
4+
Copyright (c) 2017-2020 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 { registerExamples } from '../register';
26+
27+
export const schema = {
28+
type: 'object',
29+
properties: {
30+
oneOfMultiEnum: {
31+
type: 'array',
32+
uniqueItems: true,
33+
description: 'Description',
34+
items: {
35+
oneOf: [
36+
{ const: 'foo', title: 'My Foo' },
37+
{ const: 'bar', title: 'My Bar' },
38+
{ const: 'foobar', title: 'My FooBar' },
39+
],
40+
},
41+
},
42+
multiEnum: {
43+
type: 'array',
44+
uniqueItems: true,
45+
description: 'Description',
46+
items: {
47+
type: 'string',
48+
enum: ['foo', 'bar', 'foobar'],
49+
},
50+
},
51+
},
52+
};
53+
54+
export const uischema = {
55+
type: 'VerticalLayout',
56+
elements: [
57+
{
58+
type: 'Control',
59+
scope: '#/properties/oneOfMultiEnum',
60+
label: 'Form Label',
61+
options: {
62+
showUnfocusedDescription: true,
63+
},
64+
},
65+
{
66+
type: 'Control',
67+
scope: '#/properties/multiEnum',
68+
label: 'Form Label',
69+
options: {
70+
showUnfocusedDescription: true,
71+
},
72+
},
73+
],
74+
};
75+
76+
export const data = { oneOfMultiEnum: ['foo'], multiEnum: ['bar'] };
77+
78+
registerExamples([
79+
{
80+
name: 'multi-enum-with-label-and-desc',
81+
label: 'Enum - Multi selection with label and desc',
82+
data,
83+
schema,
84+
uischema,
85+
},
86+
]);

packages/examples/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import * as readonly from './examples/readonly';
7171
import * as listWithDetailPrimitives from './examples/list-with-detail-primitives';
7272
import * as conditionalSchemaComposition from './examples/conditional-schema-compositions';
7373
import * as additionalErrors from './examples/additional-errors';
74+
import * as multiEnumWithLabelAndDesc from './examples/enum-multi-with-label-and-desc';
7475
export * from './register';
7576
export * from './example';
7677

@@ -120,6 +121,7 @@ export {
120121
enumExample,
121122
radioGroupExample,
122123
multiEnum,
124+
multiEnumWithLabelAndDesc,
123125
enumInArray,
124126
readonly,
125127
listWithDetailPrimitives,

packages/material-renderers/src/complex/MaterialEnumArrayRenderer.tsx

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
ControlProps,
44
DispatchPropsOfMultiEnumControl,
55
hasType,
6+
isDescriptionHidden,
67
JsonSchema,
78
OwnPropsOfEnum,
89
Paths,
@@ -11,6 +12,7 @@ import {
1112
resolveSchema,
1213
schemaMatches,
1314
schemaSubPathMatches,
15+
showAsRequired,
1416
uiTypeIs,
1517
} from '@jsonforms/core';
1618

@@ -21,15 +23,23 @@ import {
2123
FormControlLabel,
2224
FormGroup,
2325
FormHelperText,
26+
FormLabel,
2427
Hidden,
2528
} from '@mui/material';
2629
import isEmpty from 'lodash/isEmpty';
2730
import React from 'react';
31+
import merge from 'lodash/merge';
32+
import { useFocus } from '../util';
2833

2934
export const MaterialEnumArrayRenderer = ({
35+
config,
36+
id,
3037
schema,
3138
visible,
3239
errors,
40+
description,
41+
label,
42+
required,
3343
path,
3444
options,
3545
data,
@@ -38,9 +48,34 @@ export const MaterialEnumArrayRenderer = ({
3848
handleChange: _handleChange,
3949
...otherProps
4050
}: ControlProps & OwnPropsOfEnum & DispatchPropsOfMultiEnumControl) => {
51+
const [focused, onFocus, onBlur] = useFocus();
52+
const isValid = errors.length === 0;
53+
const appliedUiSchemaOptions = merge({}, config, otherProps.uischema.options);
54+
const showDescription = !isDescriptionHidden(
55+
visible,
56+
description,
57+
focused,
58+
appliedUiSchemaOptions.showUnfocusedDescription
59+
);
60+
4161
return (
4262
<Hidden xsUp={!visible}>
43-
<FormControl component='fieldset'>
63+
<FormControl
64+
component='fieldset'
65+
fullWidth={!appliedUiSchemaOptions.trim}
66+
onFocus={onFocus}
67+
onBlur={onBlur}
68+
>
69+
<FormLabel
70+
error={!isValid}
71+
component='legend'
72+
required={showAsRequired(
73+
required,
74+
appliedUiSchemaOptions.hideRequiredAsterisk
75+
)}
76+
>
77+
{label}
78+
</FormLabel>
4479
<FormGroup row>
4580
{options.map((option: any, index: number) => {
4681
const optionPath = Paths.compose(path, `${index}`);
@@ -49,10 +84,11 @@ export const MaterialEnumArrayRenderer = ({
4984
: undefined;
5085
return (
5186
<FormControlLabel
52-
id={option.value}
87+
id={id + '-label-' + option.value}
5388
key={option.value}
5489
control={
5590
<MuiCheckbox
91+
id={id + '-' + option.value}
5692
key={'checkbox-' + option.value}
5793
isValid={isEmpty(errors)}
5894
path={optionPath}
@@ -73,7 +109,9 @@ export const MaterialEnumArrayRenderer = ({
73109
);
74110
})}
75111
</FormGroup>
76-
<FormHelperText error>{errors}</FormHelperText>
112+
<FormHelperText error={!isValid}>
113+
{!isValid ? errors : showDescription ? description : null}
114+
</FormHelperText>
77115
</FormControl>
78116
</Hidden>
79117
);

packages/material-renderers/src/controls/MaterialRadioGroup.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export const MaterialRadioGroup = (props: ControlProps & OwnPropsOfEnum) => {
4545
const [focused, onFocus, onBlur] = useFocus();
4646
const {
4747
config,
48-
id,
4948
label,
5049
required,
5150
description,
@@ -69,15 +68,14 @@ export const MaterialRadioGroup = (props: ControlProps & OwnPropsOfEnum) => {
6968
return (
7069
<Hidden xsUp={!visible}>
7170
<FormControl
72-
component={'fieldset' as 'div'}
71+
component='fieldset'
7372
fullWidth={!appliedUiSchemaOptions.trim}
7473
onFocus={onFocus}
7574
onBlur={onBlur}
7675
>
7776
<FormLabel
78-
htmlFor={id}
7977
error={!isValid}
80-
component={'legend' as 'label'}
78+
component='legend'
8179
required={showAsRequired(
8280
required,
8381
appliedUiSchemaOptions.hideRequiredAsterisk

packages/material-renderers/src/controls/MaterialSliderControl.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export const MaterialSliderControl = (props: ControlProps) => {
101101
<FormLabel
102102
htmlFor={id}
103103
error={!isValid}
104-
component={'legend' as 'label'}
104+
component='legend'
105105
required={showAsRequired(
106106
required,
107107
appliedUiSchemaOptions.hideRequiredAsterisk

packages/material-renderers/test/renderers/MaterialEnumArrayRenderer.test.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
materialEnumArrayRendererTester,
99
MaterialEnumArrayRenderer,
1010
} from '../../src';
11+
import { FormHelperText, FormLabel } from '@mui/material';
1112

1213
const MaterialEnumArrayRendererRegistration = {
1314
tester: materialEnumArrayRendererTester,
@@ -41,6 +42,10 @@ const enumSchema = {
4142
const uischema: ControlElement = {
4243
type: 'Control',
4344
scope: '#',
45+
label: 'Label',
46+
options: {
47+
showUnfocusedDescription: true,
48+
},
4449
};
4550

4651
Enzyme.configure({ adapter: new Adapter() });
@@ -212,4 +217,58 @@ describe('EnumArrayControl', () => {
212217
done();
213218
}, 50);
214219
});
220+
221+
test('oneOf items - renders label for the form group', () => {
222+
wrapper = mount(
223+
<JsonForms
224+
schema={oneOfSchema}
225+
uischema={uischema}
226+
data={data}
227+
renderers={[MaterialEnumArrayRendererRegistration]}
228+
/>
229+
);
230+
expect(wrapper.find(FormLabel).text()).toBe('Label');
231+
});
232+
233+
test('enum items - renders label for the form group', () => {
234+
wrapper = mount(
235+
<JsonForms
236+
schema={enumSchema}
237+
uischema={uischema}
238+
data={data}
239+
renderers={[MaterialEnumArrayRendererRegistration]}
240+
/>
241+
);
242+
expect(wrapper.find(FormLabel).text()).toBe('Label');
243+
});
244+
245+
test('oneOf items - renders description for the form group', () => {
246+
wrapper = mount(
247+
<JsonForms
248+
schema={{
249+
...oneOfSchema,
250+
description: 'Description',
251+
}}
252+
uischema={uischema}
253+
data={data}
254+
renderers={[MaterialEnumArrayRendererRegistration]}
255+
/>
256+
);
257+
expect(wrapper.find(FormHelperText).text()).toBe('Description');
258+
});
259+
260+
test('enum items - renders description for the form group', () => {
261+
wrapper = mount(
262+
<JsonForms
263+
schema={{
264+
...enumSchema,
265+
description: 'Description',
266+
}}
267+
uischema={uischema}
268+
data={data}
269+
renderers={[MaterialEnumArrayRendererRegistration]}
270+
/>
271+
);
272+
expect(wrapper.find(FormHelperText).text()).toBe('Description');
273+
});
215274
});

0 commit comments

Comments
 (0)