Skip to content

Commit

Permalink
feat: translate enums in React Material list with detail
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxouwell committed Apr 18, 2024
1 parent a672127 commit e1724a8
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 30 deletions.
7 changes: 6 additions & 1 deletion packages/core/src/util/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,12 @@ export const computeChildLabel = (
return '';
}

const currentValue = get(childData, childLabelProp, '');
const currentValue = get(childData, childLabelProp);

// in case there is no value, then we can't map it to an enum or oneOf
if (currentValue === undefined) {
return '';
}

// check whether the value is part of a oneOf or enum and needs to be translated
const childSchema = Resolve.schema(
Expand Down
28 changes: 13 additions & 15 deletions packages/core/src/util/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import {
LabelElement,
UISchemaElement,
} from '../models';
import find from 'lodash/find';
import {
getUISchemas,
getAjv,
Expand All @@ -53,7 +52,7 @@ import type {
} from '../reducers';
import type { RankedTester } from '../testers';
import { hasShowRule, isInherentlyEnabled, isVisible } from './runtime';
import { createLabelDescriptionFrom } from './label';
import { computeChildLabel, createLabelDescriptionFrom } from './label';
import type { CombinatorKeyword } from './combinators';
import { moveDown, moveUp } from './array';
import type { AnyAction, Dispatch } from './type';
Expand Down Expand Up @@ -689,20 +688,17 @@ export const mapStateToMasterListItemProps = (
state: JsonFormsState,
ownProps: OwnPropsOfMasterListItem
): StatePropsOfMasterItem => {
const { schema, path, index } = ownProps;
const firstPrimitiveProp = schema.properties
? find(Object.keys(schema.properties), (propName) => {
const prop = schema.properties[propName];
return (
prop.type === 'string' ||
prop.type === 'number' ||
prop.type === 'integer'
);
})
: undefined;
const { schema, path, uischema, childLabelProp, index } = ownProps;
const childPath = composePaths(path, `${index}`);
const childData = Resolve.data(getData(state), childPath);
const childLabel = firstPrimitiveProp ? childData[firstPrimitiveProp] : '';
const childLabel = computeChildLabel(
getData(state),
childPath,
childLabelProp,
schema,
getSchema(state),
state.jsonforms.i18n.translate,
uischema
);

return {
...ownProps,
Expand All @@ -725,6 +721,8 @@ export interface OwnPropsOfMasterListItem {
path: string;
enabled: boolean;
schema: JsonSchema;
uischema: UISchemaElement;
childLabelProp?: string;
handleSelect(index: number): () => void;
removeItem(path: string, value: number): () => void;
translations: ArrayTranslations;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
ListItemAvatar,
ListItemSecondaryAction,
ListItemText,
Tooltip,
} from '@mui/material';
import { Delete as DeleteIcon } from '@mui/icons-material';
import React from 'react';
Expand All @@ -53,13 +54,19 @@ export const ListWithDetailMasterItem = ({
<ListItemText primary={childLabel} />
{enabled && (
<ListItemSecondaryAction>
<IconButton
aria-label={translations.removeAriaLabel}
onClick={removeItem(path, index)}
size='large'
<Tooltip
id='tooltip-remove'
title={translations.removeTooltip}
placement='bottom'
>
<DeleteIcon />
</IconButton>
<IconButton
aria-label={translations.removeAriaLabel}
onClick={removeItem(path, index)}
size='large'
>
<DeleteIcon />
</IconButton>
</Tooltip>
</ListItemSecondaryAction>
)}
</ListItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,13 @@ export const MaterialListWithDetailRenderer = ({
removeItem={handleRemoveItem}
selected={selectedIndex === index}
key={index}
uischema={foundUISchema}
childLabelProp={appliedUiSchemaOptions.elementLabelProp}
translations={translations}
/>
))
) : (
<p>No data</p>
<p>{translations.noDataMessage}</p>
)}
</List>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,25 @@
THE SOFTWARE.
*/
import './MatchMediaMock';
import { ControlElement, JsonSchema7 } from '@jsonforms/core';
import {
ArrayTranslationEnum,
ControlElement,
JsonSchema7,
Scoped,
UISchemaElement,
} from '@jsonforms/core';
import * as React from 'react';

import { materialRenderers } from '../../src';
import { ArrayLayoutToolbar, materialRenderers } from '../../src';
import MaterialListWithDetailRenderer, {
materialListWithDetailTester,
} from '../../src/additional/MaterialListWithDetailRenderer';
import Enzyme, { mount, ReactWrapper } from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import { JsonFormsStateProvider } from '@jsonforms/react';
import { ListItem } from '@mui/material';
import { initCore } from './util';
import { ListItem, Typography } from '@mui/material';
import { initCore, testTranslator } from './util';
import { checkTooltip, checkTooltipTranslation } from './tooltipChecker';

Enzyme.configure({ adapter: new Adapter() });

Expand All @@ -47,6 +54,23 @@ const data = [
message: 'Yolo',
},
];

const emptyData: any[] = [];

const enumOrOneOfData = [
{
message: 'El Barto was here',
messageType: 'MSG_TYPE_1',
},
{
message: 'El Barto was not here',
messageType: 'MSG_TYPE_2',
},
{
message: 'Yolo',
},
];

const schema: JsonSchema7 = {
type: 'array',
items: {
Expand All @@ -69,6 +93,24 @@ const uischema: ControlElement = {
scope: '#',
};

const uischemaListWithDetail: UISchemaElement & Scoped = {
type: 'ListWithDetail',
scope: '#',
};

const enumSchema: JsonSchema7 = {
type: 'array',
items: {
type: 'object',
properties: {
messageType: {
type: 'string',
enum: ['MSG_TYPE_1', 'MSG_TYPE_2'],
},
},
},
};

const nestedSchema: JsonSchema7 = {
type: 'array',
items: {
Expand Down Expand Up @@ -346,4 +388,131 @@ describe('Material list with detail renderer', () => {
const lis = wrapper.find(ListItem);
expect(lis).toHaveLength(1);
});

it('should render first simple property', () => {
const core = initCore(schema, uischema, data);
wrapper = mount(
<JsonFormsStateProvider
initState={{ renderers: materialRenderers, core }}
>
<MaterialListWithDetailRenderer schema={schema} uischema={uischema} />
</JsonFormsStateProvider>
);

expect(wrapper.find(ListItem)).toHaveLength(2);

expect(wrapper.find(ListItem).find(Typography).at(0).text()).toBe(
'El Barto was here'
);
expect(wrapper.find(ListItem).find(Typography).at(1).text()).toBe('Yolo');
});

it('should render first simple enum property as translated child label', () => {
const core = initCore(enumSchema, uischema, enumOrOneOfData);
wrapper = mount(
<JsonFormsStateProvider
initState={{ renderers: materialRenderers, core, i18n: testTranslator }}
>
<MaterialListWithDetailRenderer
schema={enumSchema}
uischema={uischema}
/>
</JsonFormsStateProvider>
);

expect(wrapper.find(ListItem)).toHaveLength(3);

expect(wrapper.find(ListItem).find(Typography).at(0).text()).toBe(
'MSG_TYPE_1'
);
expect(wrapper.find(ListItem).find(Typography).at(1).text()).toBe(
'MSG_TYPE_2'
);
expect(wrapper.find(ListItem).find(Typography).at(2).text()).toBe('');
});

it('should have no data message when no translator set', () => {
const core = initCore(schema, uischema, emptyData);
wrapper = mount(
<JsonFormsStateProvider
initState={{ renderers: materialRenderers, core }}
>
<MaterialListWithDetailRenderer schema={schema} uischema={uischema} />
</JsonFormsStateProvider>
);

const noDataLabel = wrapper.find('ul>p').text();
expect(noDataLabel).toBe('No data');
});

it('should have a translation for no data', () => {
const core = initCore(schema, uischema, emptyData);
wrapper = mount(
<JsonFormsStateProvider
initState={{
renderers: materialRenderers,
core,
i18n: { translate: testTranslator },
}}
>
<MaterialListWithDetailRenderer schema={schema} uischema={uischema} />
</JsonFormsStateProvider>
);

const noDataLabel = wrapper.find('ul>p').text();
expect(noDataLabel).toBe('translator.root.noDataMessage');
});

it('should have a tooltip for add button', () => {
wrapper = checkTooltip(
schema,
uischemaListWithDetail,
wrapper,
(wrapper) => wrapper.find(ArrayLayoutToolbar),
ArrayTranslationEnum.addTooltip,
{
id: 'tooltip-add',
},
data
);
});
it('should have a translatable tooltip for add button', () => {
wrapper = checkTooltipTranslation(
schema,
uischemaListWithDetail,
wrapper,
(wrapper) => wrapper.find(ArrayLayoutToolbar),
{
id: 'tooltip-add',
},
data
);
});

it('should have a tooltip for delete button', () => {
wrapper = checkTooltip(
schema,
uischemaListWithDetail,
wrapper,
(wrapper) => wrapper.find('Memo(ListWithDetailMasterItem)').at(0),
ArrayTranslationEnum.removeTooltip,
{
id: 'tooltip-remove',
},
data
);
});

it('should have a translatable tooltip for delete button', () => {
wrapper = checkTooltipTranslation(
schema,
uischemaListWithDetail,
wrapper,
(wrapper) => wrapper.find('Memo(ListWithDetailMasterItem)').at(0),
{
id: 'tooltip-remove',
},
data
);
});
});
6 changes: 3 additions & 3 deletions packages/material-renderers/test/renderers/tooltipChecker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { EnzymePropSelector, mount, ReactWrapper } from 'enzyme';
import {
arrayDefaultTranslations,
ArrayTranslationEnum,
ControlElement,
JsonSchema,
UISchemaElement,
} from '@jsonforms/core';
import { JsonForms } from '@jsonforms/react';
import { materialRenderers } from '../../src';
Expand All @@ -12,7 +12,7 @@ import * as React from 'react';

export const checkTooltip = (
schema: JsonSchema,
uiSchema: ControlElement,
uiSchema: UISchemaElement,
wrapper: ReactWrapper<any, any>,
findTooltipWrapper: (
wrapper: ReactWrapper<any, any>
Expand Down Expand Up @@ -42,7 +42,7 @@ export const checkTooltip = (

export const checkTooltipTranslation = (
schema: JsonSchema,
uiSchema: ControlElement,
uiSchema: UISchemaElement,
wrapper: ReactWrapper<any, any>,
findTooltipWrapper: (
wrapper: ReactWrapper<any, any>
Expand Down

0 comments on commit e1724a8

Please sign in to comment.