Skip to content

Commit

Permalink
feat: support descriptions in React Material array renderers
Browse files Browse the repository at this point in the history
Use a 'FormHelperText' to render array descriptions if they exist in
the React Material renderer set.

Co-authored-by: Stefan Dirix <[email protected]>
  • Loading branch information
SaSteffen and sdirix authored Jan 10, 2024
1 parent c8d3ecf commit d7c15f0
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 67 deletions.
1 change: 1 addition & 0 deletions packages/examples/src/examples/arrays-with-detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export const schema = {
occupation: { type: 'string' },
comments: {
type: 'array',
description: 'Description for array with details',
minItems: 2,
maxItems: 8,
items: {
Expand Down
1 change: 1 addition & 0 deletions packages/examples/src/examples/stringArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const schema = {
type: 'object',
properties: {
comments: {
description: 'Description for array of String Type',
type: 'array',
items: {
type: 'string',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const MaterialListWithDetailRenderer = ({
config,
rootSchema,
translations,
description,
}: ArrayLayoutProps) => {
const [selectedIndex, setSelectedIndex] = useState(undefined);
const handleRemoveItem = useCallback(
Expand Down Expand Up @@ -113,6 +114,7 @@ export const MaterialListWithDetailRenderer = ({
required,
appliedUiSchemaOptions.hideRequiredAsterisk
)}
description={description}
errors={errors}
path={path}
enabled={enabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,7 @@ export class MaterialTableControl extends React.Component<
render() {
const {
label,
description,
path,
schema,
rootSchema,
Expand All @@ -458,6 +459,7 @@ export class MaterialTableControl extends React.Component<
<TableToolbar
errors={errors}
label={label}
description={description}
addItem={this.addItem}
numColumns={isObjectSchema ? headerCells.length : 1}
path={path}
Expand Down
53 changes: 33 additions & 20 deletions packages/material-renderers/src/complex/TableToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,15 @@ import {
JsonSchema,
ArrayTranslations,
} from '@jsonforms/core';
import { IconButton, TableRow, Tooltip, Grid, Typography } from '@mui/material';
import {
IconButton,
TableRow,
Tooltip,
Grid,
Typography,
FormHelperText,
Stack,
} from '@mui/material';
import { Add as AddIcon } from '@mui/icons-material';
import ValidationIcon from './ValidationIcon';
import NoBorderTableCell from './NoBorderTableCell';
Expand All @@ -38,6 +46,7 @@ export interface MaterialTableToolbarProps {
numColumns: number;
errors: string;
label: string;
description: string;
path: string;
uischema: ControlElement;
schema: JsonSchema;
Expand All @@ -56,6 +65,7 @@ const TableToolbar = React.memo(function TableToolbar({
numColumns,
errors,
label,
description,
path,
addItem,
schema,
Expand All @@ -66,26 +76,29 @@ const TableToolbar = React.memo(function TableToolbar({
return (
<TableRow>
<NoBorderTableCell colSpan={numColumns}>
<Grid
container
justifyContent={'flex-start'}
alignItems={'center'}
spacing={2}
>
<Grid item>
<Typography variant={'h6'}>{label}</Typography>
</Grid>
<Grid item>
{errors.length !== 0 && (
<Grid item>
<ValidationIcon
id='tooltip-validation'
errorMessages={errors}
/>
</Grid>
)}
<Stack>
<Grid
container
justifyContent={'flex-start'}
alignItems={'center'}
spacing={2}
>
<Grid item>
<Typography variant={'h6'}>{label}</Typography>
</Grid>
<Grid item>
{errors.length !== 0 && (
<Grid item>
<ValidationIcon
id='tooltip-validation'
errorMessages={errors}
/>
</Grid>
)}
</Grid>
</Grid>
</Grid>
{description && <FormHelperText>{description}</FormHelperText>}
</Stack>
</NoBorderTableCell>
{enabled ? (
<NoBorderTableCell align='right' style={fixedCellSmall}>
Expand Down
95 changes: 54 additions & 41 deletions packages/material-renderers/src/layouts/ArrayToolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { Grid, IconButton, Toolbar, Tooltip, Typography } from '@mui/material';
import { Add as AddIcon } from '@mui/icons-material';
import {
FormHelperText,
Grid,
IconButton,
Stack,
Toolbar,
Tooltip,
Typography,
} from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import React from 'react';
import ValidationIcon from '../complex/ValidationIcon';
import { ArrayTranslations } from '@jsonforms/core';
export interface ArrayLayoutToolbarProps {
label: string;
description: string;
errors: string;
path: string;
enabled: boolean;
Expand All @@ -14,6 +23,7 @@ export interface ArrayLayoutToolbarProps {
}
export const ArrayLayoutToolbar = React.memo(function ArrayLayoutToolbar({
label,
description,
errors,
addItem,
path,
Expand All @@ -23,51 +33,54 @@ export const ArrayLayoutToolbar = React.memo(function ArrayLayoutToolbar({
}: ArrayLayoutToolbarProps) {
return (
<Toolbar disableGutters={true}>
<Grid container alignItems='center' justifyContent='space-between'>
<Grid item>
<Grid
container
justifyContent={'flex-start'}
alignItems={'center'}
spacing={2}
>
<Grid item>
<Typography variant={'h6'}>{label}</Typography>
<Stack>
<Grid container alignItems='center' justifyContent='space-between'>
<Grid item>
<Grid
container
justifyContent={'flex-start'}
alignItems={'center'}
spacing={2}
>
<Grid item>
<Typography variant={'h6'}>{label}</Typography>
</Grid>
<Grid item>
{errors.length !== 0 && (
<Grid item>
<ValidationIcon
id='tooltip-validation'
errorMessages={errors}
/>
</Grid>
)}
</Grid>
</Grid>
</Grid>
{enabled && (
<Grid item>
{errors.length !== 0 && (
<Grid container>
<Grid item>
<ValidationIcon
id='tooltip-validation'
errorMessages={errors}
/>
</Grid>
)}
</Grid>
</Grid>
</Grid>
{enabled && (
<Grid item>
<Grid container>
<Grid item>
<Tooltip
id='tooltip-add'
title={translations.addTooltip}
placement='bottom'
>
<IconButton
aria-label={translations.addTooltip}
onClick={addItem(path, createDefault())}
size='large'
<Tooltip
id='tooltip-add'
title={translations.addTooltip}
placement='bottom'
>
<AddIcon />
</IconButton>
</Tooltip>
<IconButton
aria-label={translations.addTooltip}
onClick={addItem(path, createDefault())}
size='large'
>
<AddIcon />
</IconButton>
</Tooltip>
</Grid>
</Grid>
</Grid>
</Grid>
)}
</Grid>
)}
</Grid>
{description && <FormHelperText>{description}</FormHelperText>}
</Stack>
</Toolbar>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const MaterialArrayLayoutComponent = (props: ArrayLayoutProps) => {
config,
uischemas,
translations,
description,
} = props;
const appliedUiSchemaOptions = merge({}, config, props.uischema.options);

Expand All @@ -78,6 +79,7 @@ const MaterialArrayLayoutComponent = (props: ArrayLayoutProps) => {
required,
appliedUiSchemaOptions.hideRequiredAsterisk
)}
description={description}
errors={errors}
path={path}
enabled={enabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,50 @@ describe('Material array control', () => {
expect(rows).toHaveLength(3);
});

it('should render description', () => {
const descriptionSchema = {
...fixture.schema,
description: 'This is an array description',
};

const core = initCore(descriptionSchema, fixture.uischema, fixture.data);
wrapper = mount(
<JsonFormsStateProvider
initState={{ renderers: materialRenderers, core }}
>
<MaterialArrayControlRenderer
schema={descriptionSchema}
uischema={fixture.uischema}
/>
</JsonFormsStateProvider>
);
expect(
wrapper.text().includes('This is an array description')
).toBeTruthy();
expect(wrapper.find('thead .MuiFormHelperText-root').exists()).toBeTruthy();
});

it('should not render description container if there is none', () => {
const descriptionSchema = {
...fixture.schema,
};
// make sure there is no description
delete descriptionSchema.description;

const core = initCore(descriptionSchema, fixture.uischema, fixture.data);
wrapper = mount(
<JsonFormsStateProvider
initState={{ renderers: materialRenderers, core }}
>
<MaterialArrayControlRenderer
schema={descriptionSchema}
uischema={fixture.uischema}
/>
</JsonFormsStateProvider>
);
expect(wrapper.find('thead .MuiFormHelperText-root').exists()).toBeFalsy();
});

it('should delete an item', () => {
const core = initCore(fixture.schema, fixture.uischema, fixture.data);
const onChangeData: any = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
THE SOFTWARE.
*/
import './MatchMediaMock';
import { ControlElement } from '@jsonforms/core';
import { ControlElement, JsonSchema7 } from '@jsonforms/core';
import * as React from 'react';

import { materialRenderers } from '../../src';
Expand All @@ -50,7 +50,7 @@ const data = [
message2: 'Yolo 2',
},
];
const schema = {
const schema: JsonSchema7 = {
type: 'array',
items: {
type: 'object',
Expand All @@ -66,7 +66,7 @@ const schema = {
},
};

const nestedSchema = {
const nestedSchema: JsonSchema7 = {
type: 'array',
items: {
...schema,
Expand Down Expand Up @@ -568,4 +568,46 @@ describe('Material array layout', () => {
expect(getChildLabel(wrapper, 0)).toBe('El Barto was here 2');
expect(getChildLabel(wrapper, 1)).toBe('Yolo 2');
});

it('should render description', () => {
const descriptionSchema = {
...nestedSchema,
description: 'This is an array description',
};

wrapper = mount(
<JsonForms
data={data}
schema={descriptionSchema}
uischema={uischema}
renderers={materialRenderers}
/>
);
expect(
wrapper.text().includes('This is an array description')
).toBeTruthy();
expect(
wrapper.find('.MuiToolbar-root .MuiFormHelperText-root').exists()
).toBeTruthy();
});

it('should not render description container if there is none', () => {
const descriptionSchema = {
...nestedSchema,
};
// make sure there is no description
delete descriptionSchema.description;

wrapper = mount(
<JsonForms
data={data}
schema={descriptionSchema}
uischema={uischema}
renderers={materialRenderers}
/>
);
expect(
wrapper.find('.MuiToolbar-root .MuiFormHelperText-root').exists()
).toBeFalsy();
});
});
Loading

0 comments on commit d7c15f0

Please sign in to comment.