@remoteoss/json-schema-form 0.5.0-dev.20230719162322 → 0.5.0-dev.20230901130231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/index.cjs +278 -86
- package/dist/index.js +278 -86
- package/dist/standalone.js +655 -85
- package/package.json +2 -1
- package/src/tests/checkIfConditionMatches.test.js +51 -0
- package/src/tests/conditions.test.js +58 -0
- package/src/tests/const.test.js +86 -0
- package/src/tests/createHeadlessForm.test.js +31 -10
- package/src/tests/helpers.custom.js +0 -1
- package/src/tests/helpers.js +6 -6
- package/src/tests/jsonLogic.fixtures.js +162 -0
- package/src/tests/jsonLogic.test.js +230 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remoteoss/json-schema-form",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.20230901130231",
|
|
4
4
|
"description": "Headless UI form powered by JSON Schemas",
|
|
5
5
|
"author": "Remote.com <engineering@remote.com> (https://remote.com/)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
]
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
+
"json-logic-js": "^2.0.2",
|
|
50
51
|
"lodash": "^4.17.21",
|
|
51
52
|
"randexp": "^0.5.3",
|
|
52
53
|
"yup": "^0.30.0"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { checkIfConditionMatches } from '../checkIfConditionMatches';
|
|
2
|
+
|
|
3
|
+
it('Empty if is always going to be true', () => {
|
|
4
|
+
expect(checkIfConditionMatches({ if: { properties: {} } })).toBe(true);
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
it('Basic if check passes with correct value', () => {
|
|
8
|
+
expect(
|
|
9
|
+
checkIfConditionMatches(
|
|
10
|
+
{ if: { properties: { a: { const: 'hello' } } } },
|
|
11
|
+
{
|
|
12
|
+
a: 'hello',
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('Basic if check fails with incorrect value', () => {
|
|
19
|
+
expect(
|
|
20
|
+
checkIfConditionMatches(
|
|
21
|
+
{ if: { properties: { a: { const: 'hello' } } } },
|
|
22
|
+
{
|
|
23
|
+
a: 'goodbye',
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('Nested properties check passes with correct value', () => {
|
|
30
|
+
expect(
|
|
31
|
+
checkIfConditionMatches(
|
|
32
|
+
{ if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } } },
|
|
33
|
+
{
|
|
34
|
+
parent: { child: 'hello from child' },
|
|
35
|
+
},
|
|
36
|
+
[{ name: 'parent', fields: [] }]
|
|
37
|
+
)
|
|
38
|
+
).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('Nested properties check passes with correct value', () => {
|
|
42
|
+
expect(
|
|
43
|
+
checkIfConditionMatches(
|
|
44
|
+
{ if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } } },
|
|
45
|
+
{
|
|
46
|
+
parent: { child: 'goodbye from child' },
|
|
47
|
+
},
|
|
48
|
+
[{ name: 'parent', fields: [] }]
|
|
49
|
+
)
|
|
50
|
+
).toBe(false);
|
|
51
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createHeadlessForm } from '../createHeadlessForm';
|
|
2
|
+
|
|
3
|
+
it('Should allow check of a nested property in a conditional', () => {
|
|
4
|
+
const { handleValidation } = createHeadlessForm(
|
|
5
|
+
{
|
|
6
|
+
additionalProperties: false,
|
|
7
|
+
allOf: [
|
|
8
|
+
{
|
|
9
|
+
if: {
|
|
10
|
+
properties: {
|
|
11
|
+
parent: {
|
|
12
|
+
properties: {
|
|
13
|
+
child: {
|
|
14
|
+
const: 'yes',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
required: ['child'],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ['parent'],
|
|
21
|
+
},
|
|
22
|
+
then: { required: ['parent_sibling'] },
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
properties: {
|
|
26
|
+
parent: {
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
properties: {
|
|
29
|
+
child: {
|
|
30
|
+
oneOf: [
|
|
31
|
+
{
|
|
32
|
+
const: 'yes',
|
|
33
|
+
},
|
|
34
|
+
{ const: 'no' },
|
|
35
|
+
],
|
|
36
|
+
type: 'string',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
required: ['child'],
|
|
40
|
+
type: 'object',
|
|
41
|
+
},
|
|
42
|
+
parent_sibling: {
|
|
43
|
+
type: 'integer',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['parent'],
|
|
47
|
+
type: 'object',
|
|
48
|
+
},
|
|
49
|
+
{ strictInputType: false }
|
|
50
|
+
);
|
|
51
|
+
expect(handleValidation({ parent: { child: 'no' } }).formErrors).toEqual(undefined);
|
|
52
|
+
expect(handleValidation({ parent: { child: 'yes' } }).formErrors).toEqual({
|
|
53
|
+
parent_sibling: 'Required field',
|
|
54
|
+
});
|
|
55
|
+
expect(handleValidation({ parent: { child: 'yes' }, parent_sibling: 1 }).formErrors).toEqual(
|
|
56
|
+
undefined
|
|
57
|
+
);
|
|
58
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { createHeadlessForm } from '../createHeadlessForm';
|
|
2
|
+
|
|
3
|
+
describe('validations: const', () => {
|
|
4
|
+
it('Should work for number', () => {
|
|
5
|
+
const { handleValidation } = createHeadlessForm(
|
|
6
|
+
{
|
|
7
|
+
properties: {
|
|
8
|
+
ten_only: { type: 'number', const: 10 },
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
{ strictInputType: false }
|
|
12
|
+
);
|
|
13
|
+
expect(handleValidation({}).formErrors).toEqual(undefined);
|
|
14
|
+
expect(handleValidation({ ten_only: 1 }).formErrors).toEqual({
|
|
15
|
+
ten_only: 'The only accepted value is 10.',
|
|
16
|
+
});
|
|
17
|
+
expect(handleValidation({ ten_only: 10 }).formErrors).toBeUndefined();
|
|
18
|
+
// null is also considered valid until we fix @BUG RMT-518
|
|
19
|
+
// Expectation: To fail with error "The only accepted value is 10."
|
|
20
|
+
expect(handleValidation({ ten_only: null }).formErrors).toBeUndefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('Should work for text', () => {
|
|
24
|
+
const { handleValidation } = createHeadlessForm(
|
|
25
|
+
{
|
|
26
|
+
properties: {
|
|
27
|
+
hello_only: { type: 'string', const: 'hello' },
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{ strictInputType: false }
|
|
31
|
+
);
|
|
32
|
+
expect(handleValidation({}).formErrors).toEqual(undefined);
|
|
33
|
+
expect(handleValidation({ hello_only: 'what' }).formErrors).toEqual({
|
|
34
|
+
hello_only: 'The only accepted value is hello.',
|
|
35
|
+
});
|
|
36
|
+
expect(handleValidation({ hello_only: 'hello' }).formErrors).toEqual(undefined);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('Should work for a conditionally applied const', () => {
|
|
40
|
+
const { handleValidation } = createHeadlessForm(
|
|
41
|
+
{
|
|
42
|
+
properties: {
|
|
43
|
+
answer: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
|
|
44
|
+
amount: {
|
|
45
|
+
description: 'If you select yes, this needs to be exactly 10.',
|
|
46
|
+
type: 'number',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
allOf: [
|
|
50
|
+
{
|
|
51
|
+
if: { properties: { answer: { const: 'yes' } }, required: ['answer'] },
|
|
52
|
+
then: { properties: { amount: { const: 10 } }, required: ['amount'] },
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
{ strictInputType: false }
|
|
57
|
+
);
|
|
58
|
+
expect(handleValidation({}).formErrors).toEqual(undefined);
|
|
59
|
+
expect(handleValidation({ answer: 'no' }).formErrors).toEqual(undefined);
|
|
60
|
+
expect(handleValidation({ answer: 'yes' }).formErrors).toEqual({ amount: 'Required field' });
|
|
61
|
+
expect(handleValidation({ answer: 'yes', amount: 1 }).formErrors).toEqual({
|
|
62
|
+
amount: 'The only accepted value is 10.',
|
|
63
|
+
});
|
|
64
|
+
expect(handleValidation({ answer: 'yes', amount: 10 }).formErrors).toEqual(undefined);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('Should show the custom error message', () => {
|
|
68
|
+
const { handleValidation } = createHeadlessForm(
|
|
69
|
+
{
|
|
70
|
+
properties: {
|
|
71
|
+
string: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
const: 'hello',
|
|
74
|
+
'x-jsf-errorMessage': { const: 'You must say hello!!!' },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{ strictInputType: false }
|
|
79
|
+
);
|
|
80
|
+
expect(handleValidation({}).formErrors).toEqual(undefined);
|
|
81
|
+
expect(handleValidation({ string: 'hi' }).formErrors).toEqual({
|
|
82
|
+
string: 'You must say hello!!!',
|
|
83
|
+
});
|
|
84
|
+
expect(handleValidation({ string: 'hello' }).formErrors).toEqual(undefined);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
schemaInputTypeSelectMultipleDeprecated,
|
|
19
19
|
schemaInputTypeSelectMultiple,
|
|
20
20
|
schemaInputTypeSelectMultipleOptional,
|
|
21
|
+
schemaInputTypeFieldset,
|
|
21
22
|
schemaInputTypeNumber,
|
|
22
23
|
schemaInputTypeNumberZeroMaximum,
|
|
23
24
|
schemaInputTypeDate,
|
|
@@ -92,12 +93,15 @@ const getField = (fields, name, ...subNames) => {
|
|
|
92
93
|
};
|
|
93
94
|
|
|
94
95
|
beforeEach(() => {
|
|
96
|
+
jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
95
97
|
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
96
98
|
});
|
|
97
99
|
|
|
98
100
|
afterEach(() => {
|
|
99
101
|
expect(console.error).not.toHaveBeenCalled();
|
|
100
102
|
console.error.mockRestore();
|
|
103
|
+
expect(console.warn).not.toHaveBeenCalled();
|
|
104
|
+
console.warn.mockRestore();
|
|
101
105
|
});
|
|
102
106
|
|
|
103
107
|
describe('createHeadlessForm', () => {
|
|
@@ -1020,7 +1024,7 @@ describe('createHeadlessForm', () => {
|
|
|
1020
1024
|
schema: expect.any(Object),
|
|
1021
1025
|
type: 'date',
|
|
1022
1026
|
minDate: '1922-03-01',
|
|
1023
|
-
maxDate: '2022-03-
|
|
1027
|
+
maxDate: '2022-03-17',
|
|
1024
1028
|
});
|
|
1025
1029
|
|
|
1026
1030
|
const todayDateHint = new Date().toISOString().substring(0, 10);
|
|
@@ -1047,17 +1051,15 @@ describe('createHeadlessForm', () => {
|
|
|
1047
1051
|
schema: expect.any(Object),
|
|
1048
1052
|
type: 'date',
|
|
1049
1053
|
minDate: '1922-03-01',
|
|
1050
|
-
maxDate: '2022-03-
|
|
1054
|
+
maxDate: '2022-03-17',
|
|
1051
1055
|
});
|
|
1052
1056
|
|
|
1053
1057
|
expect(validateForm({})).toEqual({
|
|
1054
1058
|
birthdate: 'Required field',
|
|
1055
1059
|
});
|
|
1056
1060
|
|
|
1057
|
-
const todayDateHint = new Date().toISOString().substring(0, 10);
|
|
1058
|
-
|
|
1059
1061
|
expect(validateForm({ birthdate: '' })).toEqual({
|
|
1060
|
-
birthdate: `
|
|
1062
|
+
birthdate: `Required field`,
|
|
1061
1063
|
});
|
|
1062
1064
|
|
|
1063
1065
|
expect(validateForm({ birthdate: '1922-02-01' })).toEqual({
|
|
@@ -1081,19 +1083,17 @@ describe('createHeadlessForm', () => {
|
|
|
1081
1083
|
schema: expect.any(Object),
|
|
1082
1084
|
type: 'date',
|
|
1083
1085
|
minDate: '1922-03-01',
|
|
1084
|
-
maxDate: '2022-03-
|
|
1086
|
+
maxDate: '2022-03-17',
|
|
1085
1087
|
});
|
|
1086
1088
|
|
|
1087
|
-
const todayDateHint = new Date().toISOString().substring(0, 10);
|
|
1088
|
-
|
|
1089
1089
|
expect(validateForm({ birthdate: '' })).toEqual({
|
|
1090
|
-
birthdate: `
|
|
1090
|
+
birthdate: `Required field`,
|
|
1091
1091
|
});
|
|
1092
1092
|
|
|
1093
1093
|
expect(validateForm({ birthdate: '2022-02-01' })).toBeUndefined();
|
|
1094
1094
|
expect(validateForm({ birthdate: '2022-03-01' })).toBeUndefined();
|
|
1095
1095
|
expect(validateForm({ birthdate: '2022-04-01' })).toEqual({
|
|
1096
|
-
birthdate: 'The date must be 2022-03-
|
|
1096
|
+
birthdate: 'The date must be 2022-03-17 or before.',
|
|
1097
1097
|
});
|
|
1098
1098
|
});
|
|
1099
1099
|
|
|
@@ -3450,6 +3450,27 @@ describe('createHeadlessForm', () => {
|
|
|
3450
3450
|
],
|
|
3451
3451
|
});
|
|
3452
3452
|
});
|
|
3453
|
+
|
|
3454
|
+
it('should ignore initial values that do not match the field type (eg string vs object)', () => {
|
|
3455
|
+
const result = createHeadlessForm(schemaInputTypeFieldset, {
|
|
3456
|
+
initialValues: {
|
|
3457
|
+
a_fieldset: 'foo', // should be an object instead of string
|
|
3458
|
+
},
|
|
3459
|
+
});
|
|
3460
|
+
|
|
3461
|
+
// It returns fields without errors
|
|
3462
|
+
expect(result.fields).toBeDefined();
|
|
3463
|
+
expect(result.fields[0].fields[0].name).toBe('id_number');
|
|
3464
|
+
expect(result.fields[0].fields[1].name).toBe('tabs');
|
|
3465
|
+
|
|
3466
|
+
// Warn about those missmatched values
|
|
3467
|
+
expect(console.warn).toHaveBeenCalledWith(
|
|
3468
|
+
`Field "a_fieldset"'s value is "foo", but should be type object.`
|
|
3469
|
+
);
|
|
3470
|
+
console.warn.mockClear();
|
|
3471
|
+
|
|
3472
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
3473
|
+
});
|
|
3453
3474
|
});
|
|
3454
3475
|
});
|
|
3455
3476
|
|
package/src/tests/helpers.js
CHANGED
|
@@ -991,7 +991,7 @@ export const schemaInputTypeDate = {
|
|
|
991
991
|
additionalProperties: false,
|
|
992
992
|
properties: {
|
|
993
993
|
birthdate: {
|
|
994
|
-
'x-jsf-presentation': { inputType: 'date', maxDate: '2022-03-
|
|
994
|
+
'x-jsf-presentation': { inputType: 'date', maxDate: '2022-03-17', minDate: '1922-03-01' },
|
|
995
995
|
title: 'Birthdate',
|
|
996
996
|
type: 'string',
|
|
997
997
|
format: 'date',
|
|
@@ -1020,12 +1020,12 @@ export const schemaInputTypeFileWithSkippable = JSONSchemaBuilder()
|
|
|
1020
1020
|
})
|
|
1021
1021
|
.build();
|
|
1022
1022
|
|
|
1023
|
-
export const schemaInputTypeFieldset =
|
|
1024
|
-
|
|
1023
|
+
export const schemaInputTypeFieldset = {
|
|
1024
|
+
properties: {
|
|
1025
1025
|
a_fieldset: mockFieldset,
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
|
|
1026
|
+
},
|
|
1027
|
+
required: ['a_fieldset'],
|
|
1028
|
+
};
|
|
1029
1029
|
|
|
1030
1030
|
export const schemaInputTypeFocusedFieldset = JSONSchemaBuilder()
|
|
1031
1031
|
.addInput({
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export function createSchemaWithRulesOnFieldA(rules) {
|
|
2
|
+
return {
|
|
3
|
+
properties: {
|
|
4
|
+
field_a: {
|
|
5
|
+
type: 'number',
|
|
6
|
+
'x-jsf-logic-validations': Object.keys(rules),
|
|
7
|
+
},
|
|
8
|
+
field_b: {
|
|
9
|
+
type: 'number',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
required: ['field_a', 'field_b'],
|
|
13
|
+
'x-jsf-logic': { validations: rules },
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createSchemaWithThreePropertiesWithRuleOnFieldA(rules) {
|
|
18
|
+
return {
|
|
19
|
+
properties: {
|
|
20
|
+
field_a: {
|
|
21
|
+
type: 'number',
|
|
22
|
+
'x-jsf-logic-validations': Object.keys(rules),
|
|
23
|
+
},
|
|
24
|
+
field_b: {
|
|
25
|
+
type: 'number',
|
|
26
|
+
},
|
|
27
|
+
field_c: {
|
|
28
|
+
type: 'number',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
'x-jsf-logic': { validations: rules },
|
|
32
|
+
required: ['field_a', 'field_b', 'field_c'],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const schemaWithNonRequiredField = {
|
|
37
|
+
properties: {
|
|
38
|
+
field_a: {
|
|
39
|
+
type: 'number',
|
|
40
|
+
},
|
|
41
|
+
field_b: {
|
|
42
|
+
type: 'number',
|
|
43
|
+
'x-jsf-logic-validations': ['a_greater_than_field_b'],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
'x-jsf-logic': {
|
|
47
|
+
validations: {
|
|
48
|
+
a_greater_than_field_b: {
|
|
49
|
+
errorMessage: 'Must be greater than field_a',
|
|
50
|
+
rule: {
|
|
51
|
+
'>': [{ var: 'field_a' }, { var: 'field_b' }],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: [],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const schemaWithNativeAndJSONLogicChecks = {
|
|
60
|
+
properties: {
|
|
61
|
+
field_a: {
|
|
62
|
+
type: 'number',
|
|
63
|
+
minimum: 100,
|
|
64
|
+
'x-jsf-logic-validations': ['a_multiple_of_ten'],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
'x-jsf-logic': {
|
|
68
|
+
validations: {
|
|
69
|
+
a_multiple_of_ten: {
|
|
70
|
+
errorMessage: 'Must be a multiple of 10',
|
|
71
|
+
rule: {
|
|
72
|
+
'===': [{ '%': [{ var: 'field_a' }, 10] }, 0],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
required: ['field_a'],
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const multiRuleSchema = {
|
|
81
|
+
properties: {
|
|
82
|
+
field_a: {
|
|
83
|
+
type: 'number',
|
|
84
|
+
'x-jsf-logic-validations': ['a_bigger_than_b', 'is_even_number'],
|
|
85
|
+
},
|
|
86
|
+
field_b: {
|
|
87
|
+
type: 'number',
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
required: ['field_a', 'field_b'],
|
|
91
|
+
'x-jsf-logic': {
|
|
92
|
+
validations: {
|
|
93
|
+
a_bigger_than_b: {
|
|
94
|
+
errorMessage: 'A must be bigger than B',
|
|
95
|
+
rule: {
|
|
96
|
+
'>': [{ var: 'field_a' }, { var: 'field_b' }],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
is_even_number: {
|
|
100
|
+
errorMessage: 'A must be even',
|
|
101
|
+
rule: {
|
|
102
|
+
'===': [{ '%': [{ var: 'field_a' }, 2] }, 0],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const schemaWithTwoRules = {
|
|
110
|
+
properties: {
|
|
111
|
+
field_a: {
|
|
112
|
+
type: 'number',
|
|
113
|
+
'x-jsf-logic-validations': ['a_bigger_than_b'],
|
|
114
|
+
},
|
|
115
|
+
field_b: {
|
|
116
|
+
type: 'number',
|
|
117
|
+
'x-jsf-logic-validations': ['is_even_number'],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
required: ['field_a', 'field_b'],
|
|
121
|
+
'x-jsf-logic': {
|
|
122
|
+
validations: {
|
|
123
|
+
a_bigger_than_b: {
|
|
124
|
+
errorMessage: 'A must be bigger than B',
|
|
125
|
+
rule: {
|
|
126
|
+
'>': [{ var: 'field_a' }, { var: 'field_b' }],
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
is_even_number: {
|
|
130
|
+
errorMessage: 'B must be even',
|
|
131
|
+
rule: {
|
|
132
|
+
'===': [{ '%': [{ var: 'field_b' }, 2] }, 0],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const schemaWithComputedAttributes = {
|
|
140
|
+
properties: {
|
|
141
|
+
field_a: {
|
|
142
|
+
type: 'number',
|
|
143
|
+
},
|
|
144
|
+
field_b: {
|
|
145
|
+
type: 'number',
|
|
146
|
+
'x-jsf-logic-computedAttrs': {
|
|
147
|
+
const: 'a_times_two',
|
|
148
|
+
default: 'a_times_two',
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
required: ['field_a', 'field_b'],
|
|
153
|
+
'x-jsf-logic': {
|
|
154
|
+
computedValues: {
|
|
155
|
+
a_times_two: {
|
|
156
|
+
rule: {
|
|
157
|
+
'*': [{ var: 'field_a' }, 2],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
};
|