Skip to content

Commit e8ce65b

Browse files
committed
feat(renderer): enhance condition with custom function and comparators
1 parent 5c7e7ca commit e8ce65b

File tree

11 files changed

+385
-12
lines changed

11 files changed

+385
-12
lines changed

packages/react-form-renderer/src/files/default-schema-validator.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,17 @@ const checkCondition = (condition, fieldName, isRoot) => {
114114
!condition.hasOwnProperty('is') &&
115115
!condition.hasOwnProperty('isEmpty') &&
116116
!condition.hasOwnProperty('isNotEmpty') &&
117-
!condition.hasOwnProperty('pattern')
117+
!condition.hasOwnProperty('pattern') &&
118+
!condition.hasOwnProperty('greaterThan') &&
119+
!condition.hasOwnProperty('greaterThanOrEqualTo') &&
120+
!condition.hasOwnProperty('lessThan') &&
121+
!condition.hasOwnProperty('lessThanOrEqualTo')
118122
) {
119123
throw new DefaultSchemaError(`
120124
Error occured in field definition with name: "${fieldName}".
121-
Field condition must have one of "is", "isEmpty", "isNotEmpty", "pattern" property! Properties received: [${Object.keys(condition)}].
125+
Field condition must have one of "is", "isEmpty", "isNotEmpty", "pattern", "greaterThan", "greaterThanOrEqualTo", "lessThan", "lessThanOrEqualTo" property! Properties received: [${Object.keys(
126+
condition
127+
)}].
122128
`);
123129
}
124130

packages/react-form-renderer/src/files/parse-condition.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,44 @@ import get from 'lodash/get';
33

44
const isEmptyValue = (value) => (typeof value === 'number' || value === true ? false : lodashIsEmpty(value));
55

6-
const fieldCondition = (value, { is, isNotEmpty, isEmpty, pattern, notMatch, flags }) => {
7-
if (isNotEmpty) {
6+
const fieldCondition = (value, config) => {
7+
if (config.isNotEmpty) {
88
return !isEmptyValue(value);
99
}
1010

11-
if (isEmpty) {
11+
if (config.isEmpty) {
1212
return isEmptyValue(value);
1313
}
1414

15-
if (pattern) {
16-
const regExpPattern = RegExp(pattern, flags);
15+
if (config.pattern) {
16+
const regExpPattern = RegExp(config.pattern, config.flags);
1717

18-
return notMatch ? !regExpPattern.test(value) : regExpPattern.test(value);
18+
return config.notMatch ? !regExpPattern.test(value) : regExpPattern.test(value);
1919
}
2020

21-
const isMatched = Array.isArray(is) ? !!is.includes(value) : value === is;
21+
if (typeof config.is === 'function') {
22+
return config.is(value, config);
23+
}
24+
25+
if (Object.prototype.hasOwnProperty.call(config, 'greaterThan')) {
26+
return value > config.greaterThan;
27+
}
28+
29+
if (Object.prototype.hasOwnProperty.call(config, 'greaterThanOrEqualTo')) {
30+
return value >= config.greaterThanOrEqualTo;
31+
}
32+
33+
if (Object.prototype.hasOwnProperty.call(config, 'lessThan')) {
34+
return value < config.lessThan;
35+
}
36+
37+
if (Object.prototype.hasOwnProperty.call(config, 'lessThanOrEqualTo')) {
38+
return value <= config.lessThanOrEqualTo;
39+
}
40+
41+
const isMatched = Array.isArray(config.is) ? !!config.is.includes(value) : value === config.is;
2242

23-
return notMatch ? !isMatched : isMatched;
43+
return config.notMatch ? !isMatched : isMatched;
2444
};
2545

2646
export const parseCondition = (condition, values, field) => {

packages/react-form-renderer/src/tests/form-renderer/parse-condition.test.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,4 +543,105 @@ describe('parseCondition', () => {
543543
expect(whenSpyY).toHaveBeenCalledWith(field);
544544
});
545545
});
546+
547+
it('simple condition - custom function', () => {
548+
const customFunction = jest.fn().mockImplementation((value) => Boolean(value));
549+
550+
condition = {
551+
when: 'x',
552+
is: customFunction,
553+
customArg: '123'
554+
};
555+
556+
values = {
557+
x: 1
558+
};
559+
560+
expect(parseCondition(condition, values)).toEqual(positiveResult);
561+
expect(customFunction).toHaveBeenCalledWith(1, { customArg: '123', when: 'x', is: expect.any(Function) });
562+
563+
values = {
564+
x: 0
565+
};
566+
567+
expect(parseCondition(condition, values)).toEqual(negativeResult);
568+
});
569+
570+
describe('math operations', () => {
571+
it('greaterThan', () => {
572+
condition = {
573+
when: 'x',
574+
greaterThan: 0
575+
};
576+
577+
values = {
578+
x: 1
579+
};
580+
581+
expect(parseCondition(condition, values)).toEqual(positiveResult);
582+
583+
values = {
584+
x: 0
585+
};
586+
587+
expect(parseCondition(condition, values)).toEqual(negativeResult);
588+
});
589+
590+
it('greaterThanOrEqualTo', () => {
591+
condition = {
592+
when: 'x',
593+
greaterThanOrEqualTo: 0
594+
};
595+
596+
values = {
597+
x: 0
598+
};
599+
600+
expect(parseCondition(condition, values)).toEqual(positiveResult);
601+
602+
values = {
603+
x: -1
604+
};
605+
606+
expect(parseCondition(condition, values)).toEqual(negativeResult);
607+
});
608+
609+
it('lessThan', () => {
610+
condition = {
611+
when: 'x',
612+
lessThan: 0
613+
};
614+
615+
values = {
616+
x: -1
617+
};
618+
619+
expect(parseCondition(condition, values)).toEqual(positiveResult);
620+
621+
values = {
622+
x: 1
623+
};
624+
625+
expect(parseCondition(condition, values)).toEqual(negativeResult);
626+
});
627+
628+
it('lessThanOrEqualTo', () => {
629+
condition = {
630+
when: 'x',
631+
lessThanOrEqualTo: 0
632+
};
633+
634+
values = {
635+
x: 0
636+
};
637+
638+
expect(parseCondition(condition, values)).toEqual(positiveResult);
639+
640+
values = {
641+
x: 1
642+
};
643+
644+
expect(parseCondition(condition, values)).toEqual(negativeResult);
645+
});
646+
});
546647
});

packages/react-form-renderer/src/tests/parsers/__snapshots__/default-schema-validator.test.js.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ exports[`Default schema validator should fail if field condition have notMatch p
4444
exports[`Default schema validator should fail if field condition is missing is key. 1`] = `
4545
"
4646
Error occured in field definition with name: \\"foo\\".
47-
Field condition must have one of \\"is\\", \\"isEmpty\\", \\"isNotEmpty\\", \\"pattern\\" property! Properties received: [when].
47+
Field condition must have one of \\"is\\", \\"isEmpty\\", \\"isNotEmpty\\", \\"pattern\\", \\"greaterThan\\", \\"greaterThanOrEqualTo\\", \\"lessThan\\", \\"lessThanOrEqualTo\\" property! Properties received: [when].
4848
"
4949
`;
5050

packages/react-form-renderer/src/tests/parsers/default-schema-validator.test.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,91 @@ describe('Default schema validator', () => {
194194
).not.toThrow();
195195
});
196196

197+
it('should not fail if field condition pattern property is a function', () => {
198+
expect(() =>
199+
defaultSchemaValidator(
200+
{
201+
fields: [
202+
{
203+
component: 'foo',
204+
name: 'foo',
205+
condition: { when: 'Foo', is: () => true }
206+
}
207+
]
208+
},
209+
componentMapper
210+
)
211+
).not.toThrow();
212+
});
213+
214+
it('should not fail if field condition pattern property is a greaterThan', () => {
215+
expect(() =>
216+
defaultSchemaValidator(
217+
{
218+
fields: [
219+
{
220+
component: 'foo',
221+
name: 'foo',
222+
condition: { when: 'Foo', greaterThan: 1 }
223+
}
224+
]
225+
},
226+
componentMapper
227+
)
228+
).not.toThrow();
229+
});
230+
231+
it('should not fail if field condition pattern property is a greaterThanOrEqualTo', () => {
232+
expect(() =>
233+
defaultSchemaValidator(
234+
{
235+
fields: [
236+
{
237+
component: 'foo',
238+
name: 'foo',
239+
condition: { when: 'Foo', greaterThanOrEqualTo: 1 }
240+
}
241+
]
242+
},
243+
componentMapper
244+
)
245+
).not.toThrow();
246+
});
247+
248+
it('should not fail if field condition pattern property is a lessThan', () => {
249+
expect(() =>
250+
defaultSchemaValidator(
251+
{
252+
fields: [
253+
{
254+
component: 'foo',
255+
name: 'foo',
256+
condition: { when: 'Foo', lessThan: 1 }
257+
}
258+
]
259+
},
260+
componentMapper
261+
)
262+
).not.toThrow();
263+
});
264+
265+
it('should not fail if field condition pattern property is a lessThanOrEqualTo', () => {
266+
expect(() =>
267+
defaultSchemaValidator(
268+
{
269+
fields: [
270+
{
271+
component: 'foo',
272+
name: 'foo',
273+
condition: { when: 'Foo', lessThanOrEqualTo: 1 }
274+
}
275+
]
276+
},
277+
componentMapper
278+
)
279+
).not.toThrow();
280+
});
281+
197282
it('should fail if field condition have notMatch property and have not is/pattern.', () => {
198283
expect(() =>
199284
defaultSchemaValidator(

packages/react-renderer-demo/src/components/navigation/schemas/schema.schema.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ const schemaNav = [
122122
component: 'pattern',
123123
linkText: 'Pattern'
124124
},
125+
{
126+
component: 'comparators',
127+
linkText: 'Comparators'
128+
},
125129
{
126130
component: 'condition-sequence',
127131
linkText: 'Sequence'
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import FormRenderer from '@data-driven-forms/react-form-renderer/dist/cjs/form-renderer';
3+
import componentTypes from '@data-driven-forms/react-form-renderer/dist/cjs/component-types';
4+
5+
import TextField from '@data-driven-forms/mui-component-mapper/dist/cjs/text-field';
6+
import PlainText from '@data-driven-forms/mui-component-mapper/dist/cjs/plain-text';
7+
import FormTemplate from '@data-driven-forms/mui-component-mapper/dist/cjs/form-template';
8+
9+
const schema = {
10+
fields: [
11+
{
12+
component: componentTypes.TEXT_FIELD,
13+
name: 'field-1',
14+
label: 'Compare your number with 10'
15+
},
16+
{
17+
component: componentTypes.PLAIN_TEXT,
18+
name: 'field-2',
19+
label: 'Greater than 10; ',
20+
condition: { when: 'field-1', greaterThan: 10 }
21+
},
22+
{
23+
component: componentTypes.PLAIN_TEXT,
24+
name: 'field-3',
25+
label: 'Greater than or equal to 10; ',
26+
condition: { when: 'field-1', greaterThanOrEqualTo: 10 }
27+
},
28+
{
29+
component: componentTypes.PLAIN_TEXT,
30+
name: 'field-4',
31+
label: 'Less than 10; ',
32+
condition: { when: 'field-1', lessThan: 10 }
33+
},
34+
{
35+
component: componentTypes.PLAIN_TEXT,
36+
name: 'field-5',
37+
label: 'Less than or equal to 10; ',
38+
condition: { when: 'field-1', lessThanOrEqualTo: 10 }
39+
}
40+
]
41+
};
42+
43+
const componentMapper = {
44+
[componentTypes.TEXT_FIELD]: TextField,
45+
[componentTypes.PLAIN_TEXT]: PlainText
46+
};
47+
48+
const IsCondition = () => <FormRenderer FormTemplate={FormTemplate} componentMapper={componentMapper} schema={schema} onSubmit={console.log} />;
49+
50+
export default IsCondition;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import FormRenderer from '@data-driven-forms/react-form-renderer/dist/cjs/form-renderer';
3+
import componentTypes from '@data-driven-forms/react-form-renderer/dist/cjs/component-types';
4+
5+
import DatePicker from '@data-driven-forms/mui-component-mapper/dist/cjs/date-picker';
6+
import PlainText from '@data-driven-forms/mui-component-mapper/dist/cjs/plain-text';
7+
import FormTemplate from '@data-driven-forms/mui-component-mapper/dist/cjs/form-template';
8+
9+
const calculateAge = (birthday) => {
10+
let ageDifMs = Date.now() - birthday;
11+
let ageDate = new Date(ageDifMs);
12+
return Math.abs(ageDate.getUTCFullYear() - 1970);
13+
};
14+
15+
const isFunction = (value, config) => calculateAge(value) >= config.desiredAge;
16+
17+
const schema = {
18+
title: 'Is function condition',
19+
fields: [
20+
{
21+
component: componentTypes.DATE_PICKER,
22+
name: 'field-1',
23+
label: 'Are you older than 18 years?'
24+
},
25+
{
26+
component: componentTypes.PLAIN_TEXT,
27+
name: 'field-2',
28+
label: 'Older than 18 years!',
29+
condition: { when: 'field-1', is: isFunction, desiredAge: 18 }
30+
}
31+
]
32+
};
33+
34+
const componentMapper = {
35+
[componentTypes.DATE_PICKER]: DatePicker,
36+
[componentTypes.PLAIN_TEXT]: PlainText
37+
};
38+
39+
const IsCondition = () => <FormRenderer FormTemplate={FormTemplate} componentMapper={componentMapper} schema={schema} onSubmit={console.log} />;
40+
41+
export default IsCondition;

packages/react-renderer-demo/src/helpers/generic-mui-component.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ const mapperLinks = {
2727
'text-field': 'text-field',
2828
'time-picker': 'text-field',
2929
'checkbox-multiple': 'checkbox',
30-
'text-area': 'textarea'
30+
'text-area': 'textarea',
31+
'plain-text': 'typography'
3132
},
3233
blueprint: {
3334
[componentTypes.TEXT_FIELD]: '#core/components/text-inputs',

0 commit comments

Comments
 (0)