Skip to content

Commit 4a4e2ec

Browse files
authored
react-material: Fix table cells respecting validation mode (#2400)
* Add validation mode to examples * Add tests for react material array cell validation mode * Material cell should respect ValidationMode Fix #2398
1 parent 5595934 commit 4a4e2ec

File tree

5 files changed

+262
-6
lines changed

5 files changed

+262
-6
lines changed

packages/examples-react/src/App.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ body {
3131
justify-content: center;
3232
align-items: center;
3333
margin-top: 20px;
34+
gap: 20px;
3435
}
3536
.tools .example-selector h4 {
3637
margin: 0 10px 0 0;

packages/examples-react/src/App.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { ExampleDescription } from '@jsonforms/examples';
2929
import {
3030
JsonFormsCellRendererRegistryEntry,
3131
JsonFormsRendererRegistryEntry,
32+
ValidationMode,
3233
} from '@jsonforms/core';
3334
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
3435
import Highlight from 'react-highlight';
@@ -85,6 +86,9 @@ const App = ({
8586
getProps(currentExample, cells, renderers)
8687
);
8788
const [showPanel, setShowPanel] = useState<boolean>(true);
89+
const [validationMode, setValidationMode] = useState<
90+
ValidationMode | undefined
91+
>(undefined);
8892
const schemaAsString = useMemo(
8993
() => JSON.stringify(exampleProps.schema, null, 2),
9094
[exampleProps.schema]
@@ -145,6 +149,28 @@ const App = ({
145149
)
146150
)}
147151
</select>
152+
<h4>Select ValidationMode:</h4>
153+
<select
154+
value={validationMode}
155+
onChange={(ev) =>
156+
setValidationMode(
157+
ev.currentTarget.value as ValidationMode | undefined
158+
)
159+
}
160+
>
161+
<option value={undefined} label='Default'>
162+
Default
163+
</option>
164+
<option value='NoValidation' label='NoValidation'>
165+
NoValidation
166+
</option>
167+
<option value='ValidateAndHide' label='ValidateAndHide'>
168+
ValidateAndHide
169+
</option>
170+
<option value='ValidateAndShow' label='ValidateAndShow'>
171+
ValidateAndShow
172+
</option>
173+
</select>
148174
</div>
149175
<div className='toggle-panel'>
150176
<input
@@ -202,6 +228,7 @@ const App = ({
202228
<JsonForms
203229
key={currentIndex}
204230
{...exampleProps}
231+
validationMode={validationMode}
205232
onChange={({ data }) => changeData(data)}
206233
/>
207234
</Wrapper>

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747
import {
4848
ArrayLayoutProps,
4949
ControlElement,
50-
errorsAt,
50+
errorAt,
5151
formatErrorMessage,
5252
JsonSchema,
5353
Paths,
@@ -177,11 +177,10 @@ const ctxToNonEmptyCellProps = (
177177
(ownProps.schema.type === 'object' ? '.' + ownProps.propName : '');
178178
const errors = formatErrorMessage(
179179
union(
180-
errorsAt(
180+
errorAt(
181181
path,
182-
ownProps.schema,
183-
(p) => p === path
184-
)(ctx.core.errors).map((error: ErrorObject) => error.message)
182+
ownProps.schema
183+
)(ctx.core).map((error: ErrorObject) => error.message)
185184
)
186185
);
187186
return {
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
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 { JsonSchema7 } from '@jsonforms/core';
26+
import * as React from 'react';
27+
28+
import { materialCells, materialRenderers } from '../../src';
29+
import Enzyme, { mount, ReactWrapper } from 'enzyme';
30+
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
31+
import { JsonForms } from '@jsonforms/react';
32+
import { FormHelperText } from '@mui/material';
33+
34+
Enzyme.configure({ adapter: new Adapter() });
35+
36+
const dataWithEmptyMessage = {
37+
nested: [
38+
{
39+
message: '',
40+
},
41+
],
42+
};
43+
44+
const dataWithNullMessage = {
45+
nested: [
46+
{
47+
message: null as string | null,
48+
},
49+
],
50+
};
51+
52+
const dataWithUndefinedMessage = {
53+
nested: [
54+
{
55+
message: undefined as string | undefined,
56+
},
57+
],
58+
};
59+
60+
const schemaWithMinLength: JsonSchema7 = {
61+
type: 'object',
62+
properties: {
63+
nested: {
64+
type: 'array',
65+
items: {
66+
type: 'object',
67+
properties: {
68+
message: { type: 'string', minLength: 3 },
69+
done: { type: 'boolean' },
70+
},
71+
},
72+
},
73+
},
74+
};
75+
76+
const schemaWithRequired: JsonSchema7 = {
77+
type: 'object',
78+
properties: {
79+
nested: {
80+
type: 'array',
81+
items: {
82+
type: 'object',
83+
properties: {
84+
message: { type: 'string' },
85+
done: { type: 'boolean' },
86+
},
87+
required: ['message'],
88+
},
89+
},
90+
},
91+
};
92+
93+
const uischema = {
94+
type: 'VerticalLayout',
95+
elements: [
96+
{
97+
type: 'Control',
98+
scope: '#/properties/nested',
99+
},
100+
],
101+
};
102+
103+
describe('Material table control', () => {
104+
let wrapper: ReactWrapper;
105+
106+
const validSchemaDataPairs = [
107+
{
108+
schema: schemaWithRequired,
109+
data: dataWithEmptyMessage,
110+
},
111+
{
112+
schema: schemaWithMinLength,
113+
data: dataWithUndefinedMessage,
114+
},
115+
];
116+
117+
const invalidSchemaDataPairs = [
118+
{
119+
schema: schemaWithRequired,
120+
data: dataWithNullMessage,
121+
message: 'must be string',
122+
},
123+
{
124+
schema: schemaWithRequired,
125+
data: dataWithUndefinedMessage,
126+
message: "must have required property 'message'",
127+
},
128+
{
129+
schema: schemaWithMinLength,
130+
data: dataWithEmptyMessage,
131+
message: 'must NOT have fewer than 3 characters',
132+
},
133+
];
134+
135+
afterEach(() => wrapper.unmount());
136+
137+
it.each(invalidSchemaDataPairs)(
138+
'should show error message for invalid property with validation mode ValidateAndShow',
139+
({ schema, data, message }) => {
140+
wrapper = mount(
141+
<JsonForms
142+
data={data}
143+
schema={schema}
144+
uischema={uischema}
145+
renderers={materialRenderers}
146+
cells={materialCells}
147+
validationMode='ValidateAndShow'
148+
/>
149+
);
150+
const messageFormHelperText = wrapper.find(FormHelperText).at(0);
151+
expect(messageFormHelperText.text()).toBe(message);
152+
expect(messageFormHelperText.props().error).toBe(true);
153+
154+
const doneFormHelperText = wrapper.find(FormHelperText).at(1);
155+
expect(doneFormHelperText.text()).toBe('');
156+
expect(doneFormHelperText.props().error).toBe(false);
157+
}
158+
);
159+
160+
it.each(invalidSchemaDataPairs)(
161+
'should not show error message for invalid property with validation mode ValidateAndHide',
162+
({ schema, data }) => {
163+
wrapper = mount(
164+
<JsonForms
165+
data={data}
166+
schema={schema}
167+
uischema={uischema}
168+
renderers={materialRenderers}
169+
cells={materialCells}
170+
validationMode='ValidateAndHide'
171+
/>
172+
);
173+
const messageFormHelperText = wrapper.find(FormHelperText).at(0);
174+
expect(messageFormHelperText.text()).toBe('');
175+
expect(messageFormHelperText.props().error).toBe(false);
176+
177+
const doneFormHelperText = wrapper.find(FormHelperText).at(1);
178+
expect(doneFormHelperText.text()).toBe('');
179+
expect(doneFormHelperText.props().error).toBe(false);
180+
}
181+
);
182+
183+
it.each(invalidSchemaDataPairs)(
184+
'should not show error message for invalid property with validation mode NoValidation',
185+
({ schema, data }) => {
186+
wrapper = mount(
187+
<JsonForms
188+
data={data}
189+
schema={schema}
190+
uischema={uischema}
191+
renderers={materialRenderers}
192+
cells={materialCells}
193+
validationMode='NoValidation'
194+
/>
195+
);
196+
const messageFormHelperText = wrapper.find(FormHelperText).at(0);
197+
expect(messageFormHelperText.text()).toBe('');
198+
expect(messageFormHelperText.props().error).toBe(false);
199+
200+
const doneFormHelperText = wrapper.find(FormHelperText).at(1);
201+
expect(doneFormHelperText.text()).toBe('');
202+
expect(doneFormHelperText.props().error).toBe(false);
203+
}
204+
);
205+
206+
it.each(validSchemaDataPairs)(
207+
'should not show error message for valid property',
208+
({ schema, data }) => {
209+
wrapper = mount(
210+
<JsonForms
211+
data={data}
212+
schema={schema}
213+
uischema={uischema}
214+
renderers={materialRenderers}
215+
cells={materialCells}
216+
validationMode='ValidateAndShow'
217+
/>
218+
);
219+
const messageFormHelperText = wrapper.find(FormHelperText).at(0);
220+
expect(messageFormHelperText.text()).toBe('');
221+
expect(messageFormHelperText.props().error).toBe(false);
222+
223+
const doneFormHelperText = wrapper.find(FormHelperText).at(1);
224+
expect(doneFormHelperText.text()).toBe('');
225+
expect(doneFormHelperText.props().error).toBe(false);
226+
}
227+
);
228+
});

packages/material-renderers/test/renderers/util.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import {
2727
createAjv,
28+
JsonFormsCore,
2829
JsonSchema,
2930
TesterContext,
3031
Translator,
@@ -37,7 +38,7 @@ export const initCore = (
3738
schema: JsonSchema,
3839
uischema: UISchemaElement,
3940
data?: any
40-
) => {
41+
): JsonFormsCore => {
4142
return { schema, uischema, data, ajv: createAjv() };
4243
};
4344

0 commit comments

Comments
 (0)