@remoteoss/json-schema-form 0.11.12-dev.20250310202108 → 0.11.12-dev.20250411165200
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/dist/index.cjs +102 -89
- package/dist/index.js +102 -89
- package/dist/standalone.js +102 -89
- package/package.json +1 -1
- package/src/tests/conditions.test.js +0 -68
- package/src/tests/createHeadlessForm.test.js +42 -0
- package/src/tests/helpers.js +84 -0
- package/src/tests/jsonLogic.fixtures.js +48 -0
- package/src/tests/jsonLogic.test.js +18 -0
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2025 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.11.12-dev.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.11.12-dev.20250411165200
|
|
5
|
+
Generated: Fri, 11 Apr 2025 16:52:23 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -96,6 +96,75 @@ function hasProperty(object2, propertyName) {
|
|
|
96
96
|
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
// src/internals/checkIfConditionMatches.js
|
|
100
|
+
function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
|
|
101
|
+
if (typeof node.if === "boolean") {
|
|
102
|
+
return node.if;
|
|
103
|
+
}
|
|
104
|
+
if (node.if.anyOf) {
|
|
105
|
+
return node.if.anyOf.some(
|
|
106
|
+
(property) => checkIfConditionMatchesProperties({ if: property }, formValues, formFields, logic)
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return Object.keys(node.if.properties ?? {}).every((name) => {
|
|
110
|
+
const currentProperty = node.if.properties[name];
|
|
111
|
+
const value = formValues[name];
|
|
112
|
+
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
113
|
+
value === null;
|
|
114
|
+
const hasIfExplicit = node.if.required?.includes(name);
|
|
115
|
+
if (hasEmptyValue && !hasIfExplicit) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
if (hasProperty(currentProperty, "const")) {
|
|
119
|
+
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
120
|
+
}
|
|
121
|
+
if (currentProperty.contains?.pattern) {
|
|
122
|
+
const formValue = value || [];
|
|
123
|
+
if (Array.isArray(formValue)) {
|
|
124
|
+
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
125
|
+
return (value || []).some((item) => pattern.test(item));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (currentProperty.enum) {
|
|
129
|
+
return currentProperty.enum.includes(value);
|
|
130
|
+
}
|
|
131
|
+
if (currentProperty.properties) {
|
|
132
|
+
return checkIfConditionMatchesProperties(
|
|
133
|
+
{ if: currentProperty },
|
|
134
|
+
formValues[name],
|
|
135
|
+
getField(name, formFields).fields,
|
|
136
|
+
logic
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
const field = getField(name, formFields);
|
|
140
|
+
return validateFieldSchema(
|
|
141
|
+
{
|
|
142
|
+
...field,
|
|
143
|
+
...currentProperty,
|
|
144
|
+
required: true
|
|
145
|
+
},
|
|
146
|
+
value
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
|
|
151
|
+
const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
|
|
152
|
+
const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
|
|
153
|
+
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
154
|
+
return true;
|
|
155
|
+
return false;
|
|
156
|
+
});
|
|
157
|
+
const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
|
|
158
|
+
([name, property]) => {
|
|
159
|
+
const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
|
|
160
|
+
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
161
|
+
return true;
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
return computedValuesMatch && validationsMatch;
|
|
166
|
+
}
|
|
167
|
+
|
|
99
168
|
// src/internals/helpers.js
|
|
100
169
|
var import_merge = __toESM(require("lodash/fp/merge"));
|
|
101
170
|
var import_get = __toESM(require("lodash/get"));
|
|
@@ -355,76 +424,6 @@ function _composeFieldCustomClosure(defaultComposeFn) {
|
|
|
355
424
|
};
|
|
356
425
|
}
|
|
357
426
|
|
|
358
|
-
// src/internals/checkIfConditionMatches.js
|
|
359
|
-
function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
|
|
360
|
-
if (typeof node.if === "boolean") {
|
|
361
|
-
return node.if;
|
|
362
|
-
}
|
|
363
|
-
if (node.if.anyOf) {
|
|
364
|
-
return node.if.anyOf.some(
|
|
365
|
-
(property) => checkIfConditionMatchesProperties({ if: property }, formValues, formFields, logic)
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
return Object.keys(node.if.properties ?? {}).every((name) => {
|
|
369
|
-
const currentProperty = node.if.properties[name];
|
|
370
|
-
const field = getField(name, formFields ?? []);
|
|
371
|
-
const isFieldsetField = field?.inputType === supportedTypes.FIELDSET;
|
|
372
|
-
const value = formValues?.[name] ?? (isFieldsetField ? {} : void 0);
|
|
373
|
-
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
374
|
-
value === null;
|
|
375
|
-
const hasIfExplicit = node.if.required?.includes(name);
|
|
376
|
-
if (hasEmptyValue && !hasIfExplicit) {
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
379
|
-
if (hasProperty(currentProperty, "const")) {
|
|
380
|
-
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
381
|
-
}
|
|
382
|
-
if (currentProperty.contains?.pattern) {
|
|
383
|
-
const formValue = value || [];
|
|
384
|
-
if (Array.isArray(formValue)) {
|
|
385
|
-
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
386
|
-
return (value || []).some((item) => pattern.test(item));
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (currentProperty.enum) {
|
|
390
|
-
return currentProperty.enum.includes(value);
|
|
391
|
-
}
|
|
392
|
-
if (currentProperty.properties) {
|
|
393
|
-
return checkIfConditionMatchesProperties(
|
|
394
|
-
{ if: currentProperty },
|
|
395
|
-
formValues[name],
|
|
396
|
-
getField(name, formFields).fields,
|
|
397
|
-
logic
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
return validateFieldSchema(
|
|
401
|
-
{
|
|
402
|
-
...field,
|
|
403
|
-
...currentProperty,
|
|
404
|
-
required: true
|
|
405
|
-
},
|
|
406
|
-
value
|
|
407
|
-
);
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
|
|
411
|
-
const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
|
|
412
|
-
const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
|
|
413
|
-
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
414
|
-
return true;
|
|
415
|
-
return false;
|
|
416
|
-
});
|
|
417
|
-
const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
|
|
418
|
-
([name, property]) => {
|
|
419
|
-
const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
|
|
420
|
-
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
421
|
-
return true;
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
);
|
|
425
|
-
return computedValuesMatch && validationsMatch;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
427
|
// src/jsonLogic.js
|
|
429
428
|
var import_json_logic_js = __toESM(require("json-logic-js"));
|
|
430
429
|
|
|
@@ -1309,12 +1308,24 @@ function processNode({
|
|
|
1309
1308
|
formFields,
|
|
1310
1309
|
accRequired = /* @__PURE__ */ new Set(),
|
|
1311
1310
|
parentID = "root",
|
|
1312
|
-
logic
|
|
1311
|
+
logic,
|
|
1312
|
+
processingConditional = false
|
|
1313
1313
|
}) {
|
|
1314
1314
|
const requiredFields = new Set(accRequired);
|
|
1315
1315
|
Object.keys(node.properties ?? []).forEach((fieldName) => {
|
|
1316
1316
|
const field = getField(fieldName, formFields);
|
|
1317
1317
|
updateField(field, requiredFields, node, formValues, logic, { parentID });
|
|
1318
|
+
const nestedNode = node.properties[fieldName];
|
|
1319
|
+
const isFieldset = field.inputType === supportedTypes.FIELDSET;
|
|
1320
|
+
if (isFieldset && processingConditional) {
|
|
1321
|
+
processNode({
|
|
1322
|
+
node: nestedNode,
|
|
1323
|
+
formValues: formValues[fieldName] || {},
|
|
1324
|
+
formFields: field.fields,
|
|
1325
|
+
parentID,
|
|
1326
|
+
logic
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1318
1329
|
});
|
|
1319
1330
|
node.required?.forEach((fieldName) => {
|
|
1320
1331
|
requiredFields.add(fieldName);
|
|
@@ -1331,7 +1342,8 @@ function processNode({
|
|
|
1331
1342
|
formFields,
|
|
1332
1343
|
accRequired: requiredFields,
|
|
1333
1344
|
parentID,
|
|
1334
|
-
logic
|
|
1345
|
+
logic,
|
|
1346
|
+
processingConditional: true
|
|
1335
1347
|
});
|
|
1336
1348
|
branchRequired.forEach((field) => requiredFields.add(field));
|
|
1337
1349
|
} else if (node.else) {
|
|
@@ -1341,7 +1353,8 @@ function processNode({
|
|
|
1341
1353
|
formFields,
|
|
1342
1354
|
accRequired: requiredFields,
|
|
1343
1355
|
parentID,
|
|
1344
|
-
logic
|
|
1356
|
+
logic,
|
|
1357
|
+
processingConditional: true
|
|
1345
1358
|
});
|
|
1346
1359
|
branchRequired.forEach((field) => requiredFields.add(field));
|
|
1347
1360
|
}
|
|
@@ -1358,20 +1371,6 @@ function processNode({
|
|
|
1358
1371
|
});
|
|
1359
1372
|
});
|
|
1360
1373
|
}
|
|
1361
|
-
if (node.allOf) {
|
|
1362
|
-
node.allOf.map(
|
|
1363
|
-
(allOfNode) => processNode({
|
|
1364
|
-
node: allOfNode,
|
|
1365
|
-
formValues,
|
|
1366
|
-
formFields,
|
|
1367
|
-
accRequired: requiredFields,
|
|
1368
|
-
parentID,
|
|
1369
|
-
logic
|
|
1370
|
-
})
|
|
1371
|
-
).forEach(({ required: allOfItemRequired }) => {
|
|
1372
|
-
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
1373
|
-
});
|
|
1374
|
-
}
|
|
1375
1374
|
if (node.properties) {
|
|
1376
1375
|
Object.entries(node.properties).forEach(([name, nestedNode]) => {
|
|
1377
1376
|
const inputType = getInputType(nestedNode);
|
|
@@ -1386,6 +1385,20 @@ function processNode({
|
|
|
1386
1385
|
}
|
|
1387
1386
|
});
|
|
1388
1387
|
}
|
|
1388
|
+
if (node.allOf) {
|
|
1389
|
+
node.allOf.map(
|
|
1390
|
+
(allOfNode) => processNode({
|
|
1391
|
+
node: allOfNode,
|
|
1392
|
+
formValues,
|
|
1393
|
+
formFields,
|
|
1394
|
+
accRequired: requiredFields,
|
|
1395
|
+
parentID,
|
|
1396
|
+
logic
|
|
1397
|
+
})
|
|
1398
|
+
).forEach(({ required: allOfItemRequired }) => {
|
|
1399
|
+
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1389
1402
|
if (node["x-jsf-logic"]) {
|
|
1390
1403
|
const { required: requiredFromLogic } = processJSONLogicNode({
|
|
1391
1404
|
node: node["x-jsf-logic"],
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2025 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.11.12-dev.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.11.12-dev.20250411165200
|
|
5
|
+
Generated: Fri, 11 Apr 2025 16:52:23 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -59,6 +59,75 @@ function hasProperty(object2, propertyName) {
|
|
|
59
59
|
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// src/internals/checkIfConditionMatches.js
|
|
63
|
+
function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
|
|
64
|
+
if (typeof node.if === "boolean") {
|
|
65
|
+
return node.if;
|
|
66
|
+
}
|
|
67
|
+
if (node.if.anyOf) {
|
|
68
|
+
return node.if.anyOf.some(
|
|
69
|
+
(property) => checkIfConditionMatchesProperties({ if: property }, formValues, formFields, logic)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return Object.keys(node.if.properties ?? {}).every((name) => {
|
|
73
|
+
const currentProperty = node.if.properties[name];
|
|
74
|
+
const value = formValues[name];
|
|
75
|
+
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
76
|
+
value === null;
|
|
77
|
+
const hasIfExplicit = node.if.required?.includes(name);
|
|
78
|
+
if (hasEmptyValue && !hasIfExplicit) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if (hasProperty(currentProperty, "const")) {
|
|
82
|
+
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
83
|
+
}
|
|
84
|
+
if (currentProperty.contains?.pattern) {
|
|
85
|
+
const formValue = value || [];
|
|
86
|
+
if (Array.isArray(formValue)) {
|
|
87
|
+
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
88
|
+
return (value || []).some((item) => pattern.test(item));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (currentProperty.enum) {
|
|
92
|
+
return currentProperty.enum.includes(value);
|
|
93
|
+
}
|
|
94
|
+
if (currentProperty.properties) {
|
|
95
|
+
return checkIfConditionMatchesProperties(
|
|
96
|
+
{ if: currentProperty },
|
|
97
|
+
formValues[name],
|
|
98
|
+
getField(name, formFields).fields,
|
|
99
|
+
logic
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const field = getField(name, formFields);
|
|
103
|
+
return validateFieldSchema(
|
|
104
|
+
{
|
|
105
|
+
...field,
|
|
106
|
+
...currentProperty,
|
|
107
|
+
required: true
|
|
108
|
+
},
|
|
109
|
+
value
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
|
|
114
|
+
const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
|
|
115
|
+
const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
|
|
116
|
+
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
117
|
+
return true;
|
|
118
|
+
return false;
|
|
119
|
+
});
|
|
120
|
+
const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
|
|
121
|
+
([name, property]) => {
|
|
122
|
+
const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
|
|
123
|
+
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
124
|
+
return true;
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
);
|
|
128
|
+
return computedValuesMatch && validationsMatch;
|
|
129
|
+
}
|
|
130
|
+
|
|
62
131
|
// src/internals/helpers.js
|
|
63
132
|
import merge from "lodash/fp/merge";
|
|
64
133
|
import get from "lodash/get";
|
|
@@ -318,76 +387,6 @@ function _composeFieldCustomClosure(defaultComposeFn) {
|
|
|
318
387
|
};
|
|
319
388
|
}
|
|
320
389
|
|
|
321
|
-
// src/internals/checkIfConditionMatches.js
|
|
322
|
-
function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
|
|
323
|
-
if (typeof node.if === "boolean") {
|
|
324
|
-
return node.if;
|
|
325
|
-
}
|
|
326
|
-
if (node.if.anyOf) {
|
|
327
|
-
return node.if.anyOf.some(
|
|
328
|
-
(property) => checkIfConditionMatchesProperties({ if: property }, formValues, formFields, logic)
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
return Object.keys(node.if.properties ?? {}).every((name) => {
|
|
332
|
-
const currentProperty = node.if.properties[name];
|
|
333
|
-
const field = getField(name, formFields ?? []);
|
|
334
|
-
const isFieldsetField = field?.inputType === supportedTypes.FIELDSET;
|
|
335
|
-
const value = formValues?.[name] ?? (isFieldsetField ? {} : void 0);
|
|
336
|
-
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
337
|
-
value === null;
|
|
338
|
-
const hasIfExplicit = node.if.required?.includes(name);
|
|
339
|
-
if (hasEmptyValue && !hasIfExplicit) {
|
|
340
|
-
return true;
|
|
341
|
-
}
|
|
342
|
-
if (hasProperty(currentProperty, "const")) {
|
|
343
|
-
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
344
|
-
}
|
|
345
|
-
if (currentProperty.contains?.pattern) {
|
|
346
|
-
const formValue = value || [];
|
|
347
|
-
if (Array.isArray(formValue)) {
|
|
348
|
-
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
349
|
-
return (value || []).some((item) => pattern.test(item));
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (currentProperty.enum) {
|
|
353
|
-
return currentProperty.enum.includes(value);
|
|
354
|
-
}
|
|
355
|
-
if (currentProperty.properties) {
|
|
356
|
-
return checkIfConditionMatchesProperties(
|
|
357
|
-
{ if: currentProperty },
|
|
358
|
-
formValues[name],
|
|
359
|
-
getField(name, formFields).fields,
|
|
360
|
-
logic
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
return validateFieldSchema(
|
|
364
|
-
{
|
|
365
|
-
...field,
|
|
366
|
-
...currentProperty,
|
|
367
|
-
required: true
|
|
368
|
-
},
|
|
369
|
-
value
|
|
370
|
-
);
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
|
|
374
|
-
const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property]) => {
|
|
375
|
-
const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
|
|
376
|
-
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
377
|
-
return true;
|
|
378
|
-
return false;
|
|
379
|
-
});
|
|
380
|
-
const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
|
|
381
|
-
([name, property]) => {
|
|
382
|
-
const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
|
|
383
|
-
if (Object.hasOwn(property, "const") && currentValue === property.const)
|
|
384
|
-
return true;
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
);
|
|
388
|
-
return computedValuesMatch && validationsMatch;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
390
|
// src/jsonLogic.js
|
|
392
391
|
import jsonLogic from "json-logic-js";
|
|
393
392
|
|
|
@@ -1272,12 +1271,24 @@ function processNode({
|
|
|
1272
1271
|
formFields,
|
|
1273
1272
|
accRequired = /* @__PURE__ */ new Set(),
|
|
1274
1273
|
parentID = "root",
|
|
1275
|
-
logic
|
|
1274
|
+
logic,
|
|
1275
|
+
processingConditional = false
|
|
1276
1276
|
}) {
|
|
1277
1277
|
const requiredFields = new Set(accRequired);
|
|
1278
1278
|
Object.keys(node.properties ?? []).forEach((fieldName) => {
|
|
1279
1279
|
const field = getField(fieldName, formFields);
|
|
1280
1280
|
updateField(field, requiredFields, node, formValues, logic, { parentID });
|
|
1281
|
+
const nestedNode = node.properties[fieldName];
|
|
1282
|
+
const isFieldset = field.inputType === supportedTypes.FIELDSET;
|
|
1283
|
+
if (isFieldset && processingConditional) {
|
|
1284
|
+
processNode({
|
|
1285
|
+
node: nestedNode,
|
|
1286
|
+
formValues: formValues[fieldName] || {},
|
|
1287
|
+
formFields: field.fields,
|
|
1288
|
+
parentID,
|
|
1289
|
+
logic
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1281
1292
|
});
|
|
1282
1293
|
node.required?.forEach((fieldName) => {
|
|
1283
1294
|
requiredFields.add(fieldName);
|
|
@@ -1294,7 +1305,8 @@ function processNode({
|
|
|
1294
1305
|
formFields,
|
|
1295
1306
|
accRequired: requiredFields,
|
|
1296
1307
|
parentID,
|
|
1297
|
-
logic
|
|
1308
|
+
logic,
|
|
1309
|
+
processingConditional: true
|
|
1298
1310
|
});
|
|
1299
1311
|
branchRequired.forEach((field) => requiredFields.add(field));
|
|
1300
1312
|
} else if (node.else) {
|
|
@@ -1304,7 +1316,8 @@ function processNode({
|
|
|
1304
1316
|
formFields,
|
|
1305
1317
|
accRequired: requiredFields,
|
|
1306
1318
|
parentID,
|
|
1307
|
-
logic
|
|
1319
|
+
logic,
|
|
1320
|
+
processingConditional: true
|
|
1308
1321
|
});
|
|
1309
1322
|
branchRequired.forEach((field) => requiredFields.add(field));
|
|
1310
1323
|
}
|
|
@@ -1321,20 +1334,6 @@ function processNode({
|
|
|
1321
1334
|
});
|
|
1322
1335
|
});
|
|
1323
1336
|
}
|
|
1324
|
-
if (node.allOf) {
|
|
1325
|
-
node.allOf.map(
|
|
1326
|
-
(allOfNode) => processNode({
|
|
1327
|
-
node: allOfNode,
|
|
1328
|
-
formValues,
|
|
1329
|
-
formFields,
|
|
1330
|
-
accRequired: requiredFields,
|
|
1331
|
-
parentID,
|
|
1332
|
-
logic
|
|
1333
|
-
})
|
|
1334
|
-
).forEach(({ required: allOfItemRequired }) => {
|
|
1335
|
-
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
1336
|
-
});
|
|
1337
|
-
}
|
|
1338
1337
|
if (node.properties) {
|
|
1339
1338
|
Object.entries(node.properties).forEach(([name, nestedNode]) => {
|
|
1340
1339
|
const inputType = getInputType(nestedNode);
|
|
@@ -1349,6 +1348,20 @@ function processNode({
|
|
|
1349
1348
|
}
|
|
1350
1349
|
});
|
|
1351
1350
|
}
|
|
1351
|
+
if (node.allOf) {
|
|
1352
|
+
node.allOf.map(
|
|
1353
|
+
(allOfNode) => processNode({
|
|
1354
|
+
node: allOfNode,
|
|
1355
|
+
formValues,
|
|
1356
|
+
formFields,
|
|
1357
|
+
accRequired: requiredFields,
|
|
1358
|
+
parentID,
|
|
1359
|
+
logic
|
|
1360
|
+
})
|
|
1361
|
+
).forEach(({ required: allOfItemRequired }) => {
|
|
1362
|
+
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1352
1365
|
if (node["x-jsf-logic"]) {
|
|
1353
1366
|
const { required: requiredFromLogic } = processJSONLogicNode({
|
|
1354
1367
|
node: node["x-jsf-logic"],
|
package/dist/standalone.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2025 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.11.12-dev.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.11.12-dev.20250411165200
|
|
5
|
+
Generated: Fri, 11 Apr 2025 16:52:23 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -11576,6 +11576,75 @@ function hasProperty(object2, propertyName) {
|
|
|
11576
11576
|
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
11577
11577
|
}
|
|
11578
11578
|
|
|
11579
|
+
// src/internals/checkIfConditionMatches.js
|
|
11580
|
+
function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
|
|
11581
|
+
if (typeof node.if === "boolean") {
|
|
11582
|
+
return node.if;
|
|
11583
|
+
}
|
|
11584
|
+
if (node.if.anyOf) {
|
|
11585
|
+
return node.if.anyOf.some(
|
|
11586
|
+
(property2) => checkIfConditionMatchesProperties({ if: property2 }, formValues, formFields, logic)
|
|
11587
|
+
);
|
|
11588
|
+
}
|
|
11589
|
+
return Object.keys(node.if.properties ?? {}).every((name) => {
|
|
11590
|
+
const currentProperty = node.if.properties[name];
|
|
11591
|
+
const value = formValues[name];
|
|
11592
|
+
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
11593
|
+
value === null;
|
|
11594
|
+
const hasIfExplicit = node.if.required?.includes(name);
|
|
11595
|
+
if (hasEmptyValue && !hasIfExplicit) {
|
|
11596
|
+
return true;
|
|
11597
|
+
}
|
|
11598
|
+
if (hasProperty(currentProperty, "const")) {
|
|
11599
|
+
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
11600
|
+
}
|
|
11601
|
+
if (currentProperty.contains?.pattern) {
|
|
11602
|
+
const formValue = value || [];
|
|
11603
|
+
if (Array.isArray(formValue)) {
|
|
11604
|
+
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
11605
|
+
return (value || []).some((item) => pattern.test(item));
|
|
11606
|
+
}
|
|
11607
|
+
}
|
|
11608
|
+
if (currentProperty.enum) {
|
|
11609
|
+
return currentProperty.enum.includes(value);
|
|
11610
|
+
}
|
|
11611
|
+
if (currentProperty.properties) {
|
|
11612
|
+
return checkIfConditionMatchesProperties(
|
|
11613
|
+
{ if: currentProperty },
|
|
11614
|
+
formValues[name],
|
|
11615
|
+
getField(name, formFields).fields,
|
|
11616
|
+
logic
|
|
11617
|
+
);
|
|
11618
|
+
}
|
|
11619
|
+
const field = getField(name, formFields);
|
|
11620
|
+
return validateFieldSchema(
|
|
11621
|
+
{
|
|
11622
|
+
...field,
|
|
11623
|
+
...currentProperty,
|
|
11624
|
+
required: true
|
|
11625
|
+
},
|
|
11626
|
+
value
|
|
11627
|
+
);
|
|
11628
|
+
});
|
|
11629
|
+
}
|
|
11630
|
+
function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
|
|
11631
|
+
const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property2]) => {
|
|
11632
|
+
const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
|
|
11633
|
+
if (Object.hasOwn(property2, "const") && currentValue === property2.const)
|
|
11634
|
+
return true;
|
|
11635
|
+
return false;
|
|
11636
|
+
});
|
|
11637
|
+
const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
|
|
11638
|
+
([name, property2]) => {
|
|
11639
|
+
const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
|
|
11640
|
+
if (Object.hasOwn(property2, "const") && currentValue === property2.const)
|
|
11641
|
+
return true;
|
|
11642
|
+
return false;
|
|
11643
|
+
}
|
|
11644
|
+
);
|
|
11645
|
+
return computedValuesMatch && validationsMatch;
|
|
11646
|
+
}
|
|
11647
|
+
|
|
11579
11648
|
// src/internals/helpers.js
|
|
11580
11649
|
var import_merge = __toESM(require_merge2());
|
|
11581
11650
|
var import_get2 = __toESM(require_get());
|
|
@@ -11835,76 +11904,6 @@ function _composeFieldCustomClosure(defaultComposeFn) {
|
|
|
11835
11904
|
};
|
|
11836
11905
|
}
|
|
11837
11906
|
|
|
11838
|
-
// src/internals/checkIfConditionMatches.js
|
|
11839
|
-
function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
|
|
11840
|
-
if (typeof node.if === "boolean") {
|
|
11841
|
-
return node.if;
|
|
11842
|
-
}
|
|
11843
|
-
if (node.if.anyOf) {
|
|
11844
|
-
return node.if.anyOf.some(
|
|
11845
|
-
(property2) => checkIfConditionMatchesProperties({ if: property2 }, formValues, formFields, logic)
|
|
11846
|
-
);
|
|
11847
|
-
}
|
|
11848
|
-
return Object.keys(node.if.properties ?? {}).every((name) => {
|
|
11849
|
-
const currentProperty = node.if.properties[name];
|
|
11850
|
-
const field = getField(name, formFields ?? []);
|
|
11851
|
-
const isFieldsetField = field?.inputType === supportedTypes.FIELDSET;
|
|
11852
|
-
const value = formValues?.[name] ?? (isFieldsetField ? {} : void 0);
|
|
11853
|
-
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
11854
|
-
value === null;
|
|
11855
|
-
const hasIfExplicit = node.if.required?.includes(name);
|
|
11856
|
-
if (hasEmptyValue && !hasIfExplicit) {
|
|
11857
|
-
return true;
|
|
11858
|
-
}
|
|
11859
|
-
if (hasProperty(currentProperty, "const")) {
|
|
11860
|
-
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
11861
|
-
}
|
|
11862
|
-
if (currentProperty.contains?.pattern) {
|
|
11863
|
-
const formValue = value || [];
|
|
11864
|
-
if (Array.isArray(formValue)) {
|
|
11865
|
-
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
11866
|
-
return (value || []).some((item) => pattern.test(item));
|
|
11867
|
-
}
|
|
11868
|
-
}
|
|
11869
|
-
if (currentProperty.enum) {
|
|
11870
|
-
return currentProperty.enum.includes(value);
|
|
11871
|
-
}
|
|
11872
|
-
if (currentProperty.properties) {
|
|
11873
|
-
return checkIfConditionMatchesProperties(
|
|
11874
|
-
{ if: currentProperty },
|
|
11875
|
-
formValues[name],
|
|
11876
|
-
getField(name, formFields).fields,
|
|
11877
|
-
logic
|
|
11878
|
-
);
|
|
11879
|
-
}
|
|
11880
|
-
return validateFieldSchema(
|
|
11881
|
-
{
|
|
11882
|
-
...field,
|
|
11883
|
-
...currentProperty,
|
|
11884
|
-
required: true
|
|
11885
|
-
},
|
|
11886
|
-
value
|
|
11887
|
-
);
|
|
11888
|
-
});
|
|
11889
|
-
}
|
|
11890
|
-
function checkIfMatchesValidationsAndComputedValues(node, formValues, logic, parentID) {
|
|
11891
|
-
const validationsMatch = Object.entries(node.if.validations ?? {}).every(([name, property2]) => {
|
|
11892
|
-
const currentValue = logic.getScope(parentID).applyValidationRuleInCondition(name, formValues);
|
|
11893
|
-
if (Object.hasOwn(property2, "const") && currentValue === property2.const)
|
|
11894
|
-
return true;
|
|
11895
|
-
return false;
|
|
11896
|
-
});
|
|
11897
|
-
const computedValuesMatch = Object.entries(node.if.computedValues ?? {}).every(
|
|
11898
|
-
([name, property2]) => {
|
|
11899
|
-
const currentValue = logic.getScope(parentID).applyComputedValueRuleInCondition(name, formValues);
|
|
11900
|
-
if (Object.hasOwn(property2, "const") && currentValue === property2.const)
|
|
11901
|
-
return true;
|
|
11902
|
-
return false;
|
|
11903
|
-
}
|
|
11904
|
-
);
|
|
11905
|
-
return computedValuesMatch && validationsMatch;
|
|
11906
|
-
}
|
|
11907
|
-
|
|
11908
11907
|
// src/jsonLogic.js
|
|
11909
11908
|
var import_json_logic_js = __toESM(require_logic());
|
|
11910
11909
|
|
|
@@ -12788,12 +12787,24 @@ function processNode({
|
|
|
12788
12787
|
formFields,
|
|
12789
12788
|
accRequired = /* @__PURE__ */ new Set(),
|
|
12790
12789
|
parentID = "root",
|
|
12791
|
-
logic
|
|
12790
|
+
logic,
|
|
12791
|
+
processingConditional = false
|
|
12792
12792
|
}) {
|
|
12793
12793
|
const requiredFields = new Set(accRequired);
|
|
12794
12794
|
Object.keys(node.properties ?? []).forEach((fieldName) => {
|
|
12795
12795
|
const field = getField(fieldName, formFields);
|
|
12796
12796
|
updateField(field, requiredFields, node, formValues, logic, { parentID });
|
|
12797
|
+
const nestedNode = node.properties[fieldName];
|
|
12798
|
+
const isFieldset = field.inputType === supportedTypes.FIELDSET;
|
|
12799
|
+
if (isFieldset && processingConditional) {
|
|
12800
|
+
processNode({
|
|
12801
|
+
node: nestedNode,
|
|
12802
|
+
formValues: formValues[fieldName] || {},
|
|
12803
|
+
formFields: field.fields,
|
|
12804
|
+
parentID,
|
|
12805
|
+
logic
|
|
12806
|
+
});
|
|
12807
|
+
}
|
|
12797
12808
|
});
|
|
12798
12809
|
node.required?.forEach((fieldName) => {
|
|
12799
12810
|
requiredFields.add(fieldName);
|
|
@@ -12810,7 +12821,8 @@ function processNode({
|
|
|
12810
12821
|
formFields,
|
|
12811
12822
|
accRequired: requiredFields,
|
|
12812
12823
|
parentID,
|
|
12813
|
-
logic
|
|
12824
|
+
logic,
|
|
12825
|
+
processingConditional: true
|
|
12814
12826
|
});
|
|
12815
12827
|
branchRequired.forEach((field) => requiredFields.add(field));
|
|
12816
12828
|
} else if (node.else) {
|
|
@@ -12820,7 +12832,8 @@ function processNode({
|
|
|
12820
12832
|
formFields,
|
|
12821
12833
|
accRequired: requiredFields,
|
|
12822
12834
|
parentID,
|
|
12823
|
-
logic
|
|
12835
|
+
logic,
|
|
12836
|
+
processingConditional: true
|
|
12824
12837
|
});
|
|
12825
12838
|
branchRequired.forEach((field) => requiredFields.add(field));
|
|
12826
12839
|
}
|
|
@@ -12837,20 +12850,6 @@ function processNode({
|
|
|
12837
12850
|
});
|
|
12838
12851
|
});
|
|
12839
12852
|
}
|
|
12840
|
-
if (node.allOf) {
|
|
12841
|
-
node.allOf.map(
|
|
12842
|
-
(allOfNode) => processNode({
|
|
12843
|
-
node: allOfNode,
|
|
12844
|
-
formValues,
|
|
12845
|
-
formFields,
|
|
12846
|
-
accRequired: requiredFields,
|
|
12847
|
-
parentID,
|
|
12848
|
-
logic
|
|
12849
|
-
})
|
|
12850
|
-
).forEach(({ required: allOfItemRequired }) => {
|
|
12851
|
-
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
12852
|
-
});
|
|
12853
|
-
}
|
|
12854
12853
|
if (node.properties) {
|
|
12855
12854
|
Object.entries(node.properties).forEach(([name, nestedNode]) => {
|
|
12856
12855
|
const inputType = getInputType(nestedNode);
|
|
@@ -12865,6 +12864,20 @@ function processNode({
|
|
|
12865
12864
|
}
|
|
12866
12865
|
});
|
|
12867
12866
|
}
|
|
12867
|
+
if (node.allOf) {
|
|
12868
|
+
node.allOf.map(
|
|
12869
|
+
(allOfNode) => processNode({
|
|
12870
|
+
node: allOfNode,
|
|
12871
|
+
formValues,
|
|
12872
|
+
formFields,
|
|
12873
|
+
accRequired: requiredFields,
|
|
12874
|
+
parentID,
|
|
12875
|
+
logic
|
|
12876
|
+
})
|
|
12877
|
+
).forEach(({ required: allOfItemRequired }) => {
|
|
12878
|
+
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
12879
|
+
});
|
|
12880
|
+
}
|
|
12868
12881
|
if (node["x-jsf-logic"]) {
|
|
12869
12882
|
const { required: requiredFromLogic } = processJSONLogicNode({
|
|
12870
12883
|
node: node["x-jsf-logic"],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remoteoss/json-schema-form",
|
|
3
|
-
"version": "0.11.12-dev.
|
|
3
|
+
"version": "0.11.12-dev.20250411165200",
|
|
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",
|
|
@@ -596,74 +596,6 @@ describe('Conditional with anyOf', () => {
|
|
|
596
596
|
});
|
|
597
597
|
});
|
|
598
598
|
|
|
599
|
-
describe('Conditional with fieldset', () => {
|
|
600
|
-
const schema = {
|
|
601
|
-
additionalProperties: false,
|
|
602
|
-
type: 'object',
|
|
603
|
-
properties: {
|
|
604
|
-
field_a: {
|
|
605
|
-
type: 'object',
|
|
606
|
-
properties: {
|
|
607
|
-
min: { type: 'number' },
|
|
608
|
-
max: { type: 'number' },
|
|
609
|
-
},
|
|
610
|
-
},
|
|
611
|
-
field_b: { type: 'string' },
|
|
612
|
-
},
|
|
613
|
-
allOf: [
|
|
614
|
-
{
|
|
615
|
-
if: {
|
|
616
|
-
properties: {
|
|
617
|
-
field_a: {
|
|
618
|
-
properties: {
|
|
619
|
-
min: {
|
|
620
|
-
minimum: 10,
|
|
621
|
-
},
|
|
622
|
-
},
|
|
623
|
-
required: ['min'],
|
|
624
|
-
},
|
|
625
|
-
},
|
|
626
|
-
required: ['field_a'],
|
|
627
|
-
},
|
|
628
|
-
then: {
|
|
629
|
-
required: ['field_b'],
|
|
630
|
-
},
|
|
631
|
-
else: {
|
|
632
|
-
properties: {
|
|
633
|
-
field_b: false,
|
|
634
|
-
},
|
|
635
|
-
},
|
|
636
|
-
},
|
|
637
|
-
],
|
|
638
|
-
};
|
|
639
|
-
|
|
640
|
-
it('handles true case', () => {
|
|
641
|
-
const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
642
|
-
|
|
643
|
-
expect(fields[1].isVisible).toBe(false);
|
|
644
|
-
expect(handleValidation({ field_a: { min: 100 } }).formErrors).toEqual({
|
|
645
|
-
field_b: 'Required field',
|
|
646
|
-
});
|
|
647
|
-
expect(fields[1].isVisible).toBe(true);
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
it('handles false case', () => {
|
|
651
|
-
const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
652
|
-
|
|
653
|
-
expect(fields[1].isVisible).toBe(false);
|
|
654
|
-
expect(handleValidation({ field_a: { min: 1 } }).formErrors).toBeUndefined();
|
|
655
|
-
expect(fields[1].isVisible).toBe(false);
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
it('handles undefined fieldset case', () => {
|
|
659
|
-
const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
660
|
-
|
|
661
|
-
expect(fields[1].isVisible).toBe(false);
|
|
662
|
-
expect(handleValidation({}).formErrors).toBeUndefined();
|
|
663
|
-
expect(fields[1].isVisible).toBe(false);
|
|
664
|
-
});
|
|
665
|
-
});
|
|
666
|
-
|
|
667
599
|
describe('Conditionals - bugs and code-smells', () => {
|
|
668
600
|
// Why do we have these bugs?
|
|
669
601
|
// To be honest we never realized it much later later.
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
schemaForErrorMessageSpecificity,
|
|
64
64
|
jsfConfigForErrorMessageSpecificity,
|
|
65
65
|
schemaInputTypeFile,
|
|
66
|
+
schemaWithRootFieldsetsConditionals,
|
|
66
67
|
} from './helpers';
|
|
67
68
|
import { mockConsole, restoreConsoleAndEnsureItWasNotCalled } from './testUtils';
|
|
68
69
|
import { createHeadlessForm } from '@/createHeadlessForm';
|
|
@@ -2162,6 +2163,47 @@ describe('createHeadlessForm', () => {
|
|
|
2162
2163
|
).toBeUndefined();
|
|
2163
2164
|
});
|
|
2164
2165
|
});
|
|
2166
|
+
|
|
2167
|
+
describe('supports root fieldsets conditionals', () => {
|
|
2168
|
+
it('Given a basic retirement, the perks.has_pension is hidden', async () => {
|
|
2169
|
+
const { fields, handleValidation } = createHeadlessForm(
|
|
2170
|
+
schemaWithRootFieldsetsConditionals,
|
|
2171
|
+
{}
|
|
2172
|
+
);
|
|
2173
|
+
const validateForm = (vals) => friendlyError(handleValidation(vals));
|
|
2174
|
+
|
|
2175
|
+
expect(validateForm({})).toEqual({
|
|
2176
|
+
perks: {
|
|
2177
|
+
retirement: 'Required field',
|
|
2178
|
+
},
|
|
2179
|
+
});
|
|
2180
|
+
|
|
2181
|
+
// has_pension is not visible
|
|
2182
|
+
expect(getField(fields, 'perks', 'has_pension').isVisible).toBe(false);
|
|
2183
|
+
|
|
2184
|
+
expect(
|
|
2185
|
+
validateForm({
|
|
2186
|
+
perks: { retirement: 'plus' },
|
|
2187
|
+
})
|
|
2188
|
+
).toEqual({
|
|
2189
|
+
perks: {
|
|
2190
|
+
has_pension: 'Required field',
|
|
2191
|
+
},
|
|
2192
|
+
});
|
|
2193
|
+
|
|
2194
|
+
// field becomes visible
|
|
2195
|
+
expect(getField(fields, 'perks', 'has_pension').isVisible).toBe(true);
|
|
2196
|
+
|
|
2197
|
+
expect(
|
|
2198
|
+
validateForm({
|
|
2199
|
+
perks: { retirement: 'basic' },
|
|
2200
|
+
})
|
|
2201
|
+
).toBeUndefined();
|
|
2202
|
+
|
|
2203
|
+
// field becomes invisible
|
|
2204
|
+
expect(getField(fields, 'perks', 'has_pension').isVisible).toBe(false);
|
|
2205
|
+
});
|
|
2206
|
+
});
|
|
2165
2207
|
});
|
|
2166
2208
|
|
|
2167
2209
|
it('support "email" field type', () => {
|
package/src/tests/helpers.js
CHANGED
|
@@ -1973,6 +1973,90 @@ export const schemaWithConditionalToFieldset = {
|
|
|
1973
1973
|
required: ['perks', 'work_hours_per_week'],
|
|
1974
1974
|
};
|
|
1975
1975
|
|
|
1976
|
+
export const schemaWithRootFieldsetsConditionals = {
|
|
1977
|
+
additionalProperties: false,
|
|
1978
|
+
type: 'object',
|
|
1979
|
+
properties: {
|
|
1980
|
+
perks: {
|
|
1981
|
+
additionalProperties: false,
|
|
1982
|
+
properties: {
|
|
1983
|
+
retirement: {
|
|
1984
|
+
oneOf: [
|
|
1985
|
+
{
|
|
1986
|
+
const: 'basic',
|
|
1987
|
+
title: 'Basic',
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
const: 'plus',
|
|
1991
|
+
title: 'Plus',
|
|
1992
|
+
},
|
|
1993
|
+
],
|
|
1994
|
+
title: 'Retirement',
|
|
1995
|
+
type: 'string',
|
|
1996
|
+
'x-jsf-presentation': {
|
|
1997
|
+
inputType: 'radio',
|
|
1998
|
+
},
|
|
1999
|
+
},
|
|
2000
|
+
has_pension: {
|
|
2001
|
+
oneOf: [
|
|
2002
|
+
{
|
|
2003
|
+
const: 'yes',
|
|
2004
|
+
title: 'Yes',
|
|
2005
|
+
},
|
|
2006
|
+
{
|
|
2007
|
+
const: 'no',
|
|
2008
|
+
title: 'No',
|
|
2009
|
+
},
|
|
2010
|
+
],
|
|
2011
|
+
title: 'Has pension',
|
|
2012
|
+
type: 'string',
|
|
2013
|
+
'x-jsf-presentation': {
|
|
2014
|
+
inputType: 'radio',
|
|
2015
|
+
},
|
|
2016
|
+
},
|
|
2017
|
+
},
|
|
2018
|
+
required: ['retirement'],
|
|
2019
|
+
title: 'Perks',
|
|
2020
|
+
type: 'object',
|
|
2021
|
+
'x-jsf-presentation': {
|
|
2022
|
+
inputType: 'fieldset',
|
|
2023
|
+
},
|
|
2024
|
+
},
|
|
2025
|
+
},
|
|
2026
|
+
required: ['perks'],
|
|
2027
|
+
allOf: [
|
|
2028
|
+
{
|
|
2029
|
+
if: {
|
|
2030
|
+
properties: {
|
|
2031
|
+
perks: {
|
|
2032
|
+
properties: {
|
|
2033
|
+
retirement: {
|
|
2034
|
+
const: 'basic',
|
|
2035
|
+
},
|
|
2036
|
+
},
|
|
2037
|
+
},
|
|
2038
|
+
},
|
|
2039
|
+
},
|
|
2040
|
+
then: {
|
|
2041
|
+
properties: {
|
|
2042
|
+
perks: {
|
|
2043
|
+
properties: {
|
|
2044
|
+
has_pension: false,
|
|
2045
|
+
},
|
|
2046
|
+
},
|
|
2047
|
+
},
|
|
2048
|
+
},
|
|
2049
|
+
else: {
|
|
2050
|
+
properties: {
|
|
2051
|
+
perks: {
|
|
2052
|
+
required: ['has_pension'],
|
|
2053
|
+
},
|
|
2054
|
+
},
|
|
2055
|
+
},
|
|
2056
|
+
},
|
|
2057
|
+
],
|
|
2058
|
+
};
|
|
2059
|
+
|
|
1976
2060
|
export const schemaWorkSchedule = {
|
|
1977
2061
|
type: 'object',
|
|
1978
2062
|
properties: {
|
|
@@ -984,3 +984,51 @@ export const schemaWithTwoValidationsWhereOneOfThemIsAppliedConditionally = {
|
|
|
984
984
|
},
|
|
985
985
|
],
|
|
986
986
|
};
|
|
987
|
+
|
|
988
|
+
export const schemaWithReduceAccumulator = {
|
|
989
|
+
properties: {
|
|
990
|
+
work_days: {
|
|
991
|
+
items: {
|
|
992
|
+
anyOf: [
|
|
993
|
+
{ const: 'monday', title: 'Monday' },
|
|
994
|
+
{ const: 'tuesday', title: 'Tuesday' },
|
|
995
|
+
{ const: 'wednesday', title: 'Wednesday' },
|
|
996
|
+
{ const: 'thursday', title: 'Thursday' },
|
|
997
|
+
{ const: 'friday', title: 'Friday' },
|
|
998
|
+
{ const: 'saturday', title: 'Saturday' },
|
|
999
|
+
{ const: 'sunday', title: 'Sunday' },
|
|
1000
|
+
],
|
|
1001
|
+
},
|
|
1002
|
+
type: 'array',
|
|
1003
|
+
uniqueItems: true,
|
|
1004
|
+
'x-jsf-presentation': {
|
|
1005
|
+
inputType: 'select',
|
|
1006
|
+
},
|
|
1007
|
+
},
|
|
1008
|
+
working_hours_per_day: {
|
|
1009
|
+
type: 'number',
|
|
1010
|
+
},
|
|
1011
|
+
working_hours_per_week: {
|
|
1012
|
+
type: 'number',
|
|
1013
|
+
'x-jsf-logic-computedAttrs': {
|
|
1014
|
+
const: 'computed_work_hours_per_week',
|
|
1015
|
+
defaultValue: 'computed_work_hours_per_week',
|
|
1016
|
+
title: '{{computed_work_hours_per_week}} hours per week',
|
|
1017
|
+
},
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
'x-jsf-logic': {
|
|
1021
|
+
computedValues: {
|
|
1022
|
+
computed_work_hours_per_week: {
|
|
1023
|
+
rule: {
|
|
1024
|
+
'*': [
|
|
1025
|
+
{ var: 'working_hours_per_day' },
|
|
1026
|
+
{
|
|
1027
|
+
reduce: [{ var: 'work_days' }, { '+': [{ var: ['accumulator', 0] }, 1] }, 0],
|
|
1028
|
+
},
|
|
1029
|
+
],
|
|
1030
|
+
},
|
|
1031
|
+
},
|
|
1032
|
+
},
|
|
1033
|
+
},
|
|
1034
|
+
};
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
schemaWithUnknownVariableInValidations,
|
|
35
35
|
schemaWithValidationThatDoesNotExistOnProperty,
|
|
36
36
|
badSchemaThatWillNotSetAForcedValue,
|
|
37
|
+
schemaWithReduceAccumulator,
|
|
37
38
|
} from './jsonLogic.fixtures';
|
|
38
39
|
import { mockConsole, restoreConsoleAndEnsureItWasNotCalled } from './testUtils';
|
|
39
40
|
import { createHeadlessForm } from '@/createHeadlessForm';
|
|
@@ -243,6 +244,23 @@ describe('jsonLogic: cross-values validations', () => {
|
|
|
243
244
|
});
|
|
244
245
|
});
|
|
245
246
|
|
|
247
|
+
// TODO: Implement this test.
|
|
248
|
+
describe.skip('Reduce', () => {
|
|
249
|
+
it('reduce: working_hours_per_day * work_days', () => {
|
|
250
|
+
const { fields, handleValidation } = createHeadlessForm(schemaWithReduceAccumulator, {
|
|
251
|
+
strictInputType: false,
|
|
252
|
+
});
|
|
253
|
+
handleValidation({
|
|
254
|
+
work_days: ['monday', 'tuesday'],
|
|
255
|
+
working_hours_per_day: 8,
|
|
256
|
+
});
|
|
257
|
+
const field = fields.find((i) => i.name === 'working_hours_per_week');
|
|
258
|
+
expect(field.const).toEqual(16);
|
|
259
|
+
expect(field.default).toEqual(16);
|
|
260
|
+
expect(field.label).toEqual('16 hours per week');
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
246
264
|
describe('Logical: ||, &&', () => {
|
|
247
265
|
it('AND: field_a > field_b && field_a > field_c (implicit with multiple rules in a single field)', () => {
|
|
248
266
|
const schema = createSchemaWithThreePropertiesWithRuleOnFieldA({
|