Skip to content

Commit d7c15f0

Browse files
SaSteffensdirix
andauthored
feat: support descriptions in React Material array renderers
Use a 'FormHelperText' to render array descriptions if they exist in the React Material renderer set. Co-authored-by: Stefan Dirix <sdirix@eclipsesource.com>
1 parent c8d3ecf commit d7c15f0

10 files changed

Lines changed: 237 additions & 67 deletions

File tree

packages/examples/src/examples/arrays-with-detail.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const schema = {
3232
occupation: { type: 'string' },
3333
comments: {
3434
type: 'array',
35+
description: 'Description for array with details',
3536
minItems: 2,
3637
maxItems: 8,
3738
items: {

packages/examples/src/examples/stringArray.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const schema = {
2828
type: 'object',
2929
properties: {
3030
comments: {
31+
description: 'Description for array of String Type',
3132
type: 'array',
3233
items: {
3334
type: 'string',

packages/material-renderers/src/additional/MaterialListWithDetailRenderer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export const MaterialListWithDetailRenderer = ({
6464
config,
6565
rootSchema,
6666
translations,
67+
description,
6768
}: ArrayLayoutProps) => {
6869
const [selectedIndex, setSelectedIndex] = useState(undefined);
6970
const handleRemoveItem = useCallback(
@@ -113,6 +114,7 @@ export const MaterialListWithDetailRenderer = ({
113114
required,
114115
appliedUiSchemaOptions.hideRequiredAsterisk
115116
)}
117+
description={description}
116118
errors={errors}
117119
path={path}
118120
enabled={enabled}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ export class MaterialTableControl extends React.Component<
433433
render() {
434434
const {
435435
label,
436+
description,
436437
path,
437438
schema,
438439
rootSchema,
@@ -458,6 +459,7 @@ export class MaterialTableControl extends React.Component<
458459
<TableToolbar
459460
errors={errors}
460461
label={label}
462+
description={description}
461463
addItem={this.addItem}
462464
numColumns={isObjectSchema ? headerCells.length : 1}
463465
path={path}

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

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,15 @@ import {
2929
JsonSchema,
3030
ArrayTranslations,
3131
} from '@jsonforms/core';
32-
import { IconButton, TableRow, Tooltip, Grid, Typography } from '@mui/material';
32+
import {
33+
IconButton,
34+
TableRow,
35+
Tooltip,
36+
Grid,
37+
Typography,
38+
FormHelperText,
39+
Stack,
40+
} from '@mui/material';
3341
import { Add as AddIcon } from '@mui/icons-material';
3442
import ValidationIcon from './ValidationIcon';
3543
import NoBorderTableCell from './NoBorderTableCell';
@@ -38,6 +46,7 @@ export interface MaterialTableToolbarProps {
3846
numColumns: number;
3947
errors: string;
4048
label: string;
49+
description: string;
4150
path: string;
4251
uischema: ControlElement;
4352
schema: JsonSchema;
@@ -56,6 +65,7 @@ const TableToolbar = React.memo(function TableToolbar({
5665
numColumns,
5766
errors,
5867
label,
68+
description,
5969
path,
6070
addItem,
6171
schema,
@@ -66,26 +76,29 @@ const TableToolbar = React.memo(function TableToolbar({
6676
return (
6777
<TableRow>
6878
<NoBorderTableCell colSpan={numColumns}>
69-
<Grid
70-
container
71-
justifyContent={'flex-start'}
72-
alignItems={'center'}
73-
spacing={2}
74-
>
75-
<Grid item>
76-
<Typography variant={'h6'}>{label}</Typography>
77-
</Grid>
78-
<Grid item>
79-
{errors.length !== 0 && (
80-
<Grid item>
81-
<ValidationIcon
82-
id='tooltip-validation'
83-
errorMessages={errors}
84-
/>
85-
</Grid>
86-
)}
79+
<Stack>
80+
<Grid
81+
container
82+
justifyContent={'flex-start'}
83+
alignItems={'center'}
84+
spacing={2}
85+
>
86+
<Grid item>
87+
<Typography variant={'h6'}>{label}</Typography>
88+
</Grid>
89+
<Grid item>
90+
{errors.length !== 0 && (
91+
<Grid item>
92+
<ValidationIcon
93+
id='tooltip-validation'
94+
errorMessages={errors}
95+
/>
96+
</Grid>
97+
)}
98+
</Grid>
8799
</Grid>
88-
</Grid>
100+
{description && <FormHelperText>{description}</FormHelperText>}
101+
</Stack>
89102
</NoBorderTableCell>
90103
{enabled ? (
91104
<NoBorderTableCell align='right' style={fixedCellSmall}>
Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
import { Grid, IconButton, Toolbar, Tooltip, Typography } from '@mui/material';
2-
import { Add as AddIcon } from '@mui/icons-material';
1+
import {
2+
FormHelperText,
3+
Grid,
4+
IconButton,
5+
Stack,
6+
Toolbar,
7+
Tooltip,
8+
Typography,
9+
} from '@mui/material';
10+
import AddIcon from '@mui/icons-material/Add';
311
import React from 'react';
412
import ValidationIcon from '../complex/ValidationIcon';
513
import { ArrayTranslations } from '@jsonforms/core';
614
export interface ArrayLayoutToolbarProps {
715
label: string;
16+
description: string;
817
errors: string;
918
path: string;
1019
enabled: boolean;
@@ -14,6 +23,7 @@ export interface ArrayLayoutToolbarProps {
1423
}
1524
export const ArrayLayoutToolbar = React.memo(function ArrayLayoutToolbar({
1625
label,
26+
description,
1727
errors,
1828
addItem,
1929
path,
@@ -23,51 +33,54 @@ export const ArrayLayoutToolbar = React.memo(function ArrayLayoutToolbar({
2333
}: ArrayLayoutToolbarProps) {
2434
return (
2535
<Toolbar disableGutters={true}>
26-
<Grid container alignItems='center' justifyContent='space-between'>
27-
<Grid item>
28-
<Grid
29-
container
30-
justifyContent={'flex-start'}
31-
alignItems={'center'}
32-
spacing={2}
33-
>
34-
<Grid item>
35-
<Typography variant={'h6'}>{label}</Typography>
36+
<Stack>
37+
<Grid container alignItems='center' justifyContent='space-between'>
38+
<Grid item>
39+
<Grid
40+
container
41+
justifyContent={'flex-start'}
42+
alignItems={'center'}
43+
spacing={2}
44+
>
45+
<Grid item>
46+
<Typography variant={'h6'}>{label}</Typography>
47+
</Grid>
48+
<Grid item>
49+
{errors.length !== 0 && (
50+
<Grid item>
51+
<ValidationIcon
52+
id='tooltip-validation'
53+
errorMessages={errors}
54+
/>
55+
</Grid>
56+
)}
57+
</Grid>
3658
</Grid>
59+
</Grid>
60+
{enabled && (
3761
<Grid item>
38-
{errors.length !== 0 && (
62+
<Grid container>
3963
<Grid item>
40-
<ValidationIcon
41-
id='tooltip-validation'
42-
errorMessages={errors}
43-
/>
44-
</Grid>
45-
)}
46-
</Grid>
47-
</Grid>
48-
</Grid>
49-
{enabled && (
50-
<Grid item>
51-
<Grid container>
52-
<Grid item>
53-
<Tooltip
54-
id='tooltip-add'
55-
title={translations.addTooltip}
56-
placement='bottom'
57-
>
58-
<IconButton
59-
aria-label={translations.addTooltip}
60-
onClick={addItem(path, createDefault())}
61-
size='large'
64+
<Tooltip
65+
id='tooltip-add'
66+
title={translations.addTooltip}
67+
placement='bottom'
6268
>
63-
<AddIcon />
64-
</IconButton>
65-
</Tooltip>
69+
<IconButton
70+
aria-label={translations.addTooltip}
71+
onClick={addItem(path, createDefault())}
72+
size='large'
73+
>
74+
<AddIcon />
75+
</IconButton>
76+
</Tooltip>
77+
</Grid>
6678
</Grid>
6779
</Grid>
68-
</Grid>
69-
)}
70-
</Grid>
80+
)}
81+
</Grid>
82+
{description && <FormHelperText>{description}</FormHelperText>}
83+
</Stack>
7184
</Toolbar>
7285
);
7386
});

packages/material-renderers/src/layouts/MaterialArrayLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ const MaterialArrayLayoutComponent = (props: ArrayLayoutProps) => {
6666
config,
6767
uischemas,
6868
translations,
69+
description,
6970
} = props;
7071
const appliedUiSchemaOptions = merge({}, config, props.uischema.options);
7172

@@ -78,6 +79,7 @@ const MaterialArrayLayoutComponent = (props: ArrayLayoutProps) => {
7879
required,
7980
appliedUiSchemaOptions.hideRequiredAsterisk
8081
)}
82+
description={description}
8183
errors={errors}
8284
path={path}
8385
enabled={enabled}

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,50 @@ describe('Material array control', () => {
283283
expect(rows).toHaveLength(3);
284284
});
285285

286+
it('should render description', () => {
287+
const descriptionSchema = {
288+
...fixture.schema,
289+
description: 'This is an array description',
290+
};
291+
292+
const core = initCore(descriptionSchema, fixture.uischema, fixture.data);
293+
wrapper = mount(
294+
<JsonFormsStateProvider
295+
initState={{ renderers: materialRenderers, core }}
296+
>
297+
<MaterialArrayControlRenderer
298+
schema={descriptionSchema}
299+
uischema={fixture.uischema}
300+
/>
301+
</JsonFormsStateProvider>
302+
);
303+
expect(
304+
wrapper.text().includes('This is an array description')
305+
).toBeTruthy();
306+
expect(wrapper.find('thead .MuiFormHelperText-root').exists()).toBeTruthy();
307+
});
308+
309+
it('should not render description container if there is none', () => {
310+
const descriptionSchema = {
311+
...fixture.schema,
312+
};
313+
// make sure there is no description
314+
delete descriptionSchema.description;
315+
316+
const core = initCore(descriptionSchema, fixture.uischema, fixture.data);
317+
wrapper = mount(
318+
<JsonFormsStateProvider
319+
initState={{ renderers: materialRenderers, core }}
320+
>
321+
<MaterialArrayControlRenderer
322+
schema={descriptionSchema}
323+
uischema={fixture.uischema}
324+
/>
325+
</JsonFormsStateProvider>
326+
);
327+
expect(wrapper.find('thead .MuiFormHelperText-root').exists()).toBeFalsy();
328+
});
329+
286330
it('should delete an item', () => {
287331
const core = initCore(fixture.schema, fixture.uischema, fixture.data);
288332
const onChangeData: any = {

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

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
THE SOFTWARE.
2424
*/
2525
import './MatchMediaMock';
26-
import { ControlElement } from '@jsonforms/core';
26+
import { ControlElement, JsonSchema7 } from '@jsonforms/core';
2727
import * as React from 'react';
2828

2929
import { materialRenderers } from '../../src';
@@ -50,7 +50,7 @@ const data = [
5050
message2: 'Yolo 2',
5151
},
5252
];
53-
const schema = {
53+
const schema: JsonSchema7 = {
5454
type: 'array',
5555
items: {
5656
type: 'object',
@@ -66,7 +66,7 @@ const schema = {
6666
},
6767
};
6868

69-
const nestedSchema = {
69+
const nestedSchema: JsonSchema7 = {
7070
type: 'array',
7171
items: {
7272
...schema,
@@ -568,4 +568,46 @@ describe('Material array layout', () => {
568568
expect(getChildLabel(wrapper, 0)).toBe('El Barto was here 2');
569569
expect(getChildLabel(wrapper, 1)).toBe('Yolo 2');
570570
});
571+
572+
it('should render description', () => {
573+
const descriptionSchema = {
574+
...nestedSchema,
575+
description: 'This is an array description',
576+
};
577+
578+
wrapper = mount(
579+
<JsonForms
580+
data={data}
581+
schema={descriptionSchema}
582+
uischema={uischema}
583+
renderers={materialRenderers}
584+
/>
585+
);
586+
expect(
587+
wrapper.text().includes('This is an array description')
588+
).toBeTruthy();
589+
expect(
590+
wrapper.find('.MuiToolbar-root .MuiFormHelperText-root').exists()
591+
).toBeTruthy();
592+
});
593+
594+
it('should not render description container if there is none', () => {
595+
const descriptionSchema = {
596+
...nestedSchema,
597+
};
598+
// make sure there is no description
599+
delete descriptionSchema.description;
600+
601+
wrapper = mount(
602+
<JsonForms
603+
data={data}
604+
schema={descriptionSchema}
605+
uischema={uischema}
606+
renderers={materialRenderers}
607+
/>
608+
);
609+
expect(
610+
wrapper.find('.MuiToolbar-root .MuiFormHelperText-root').exists()
611+
).toBeFalsy();
612+
});
571613
});

0 commit comments

Comments
 (0)