@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 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.20250310202108
5
- Generated: Mon, 10 Mar 2025 20:22:24 GMT
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.20250310202108
5
- Generated: Mon, 10 Mar 2025 20:22:24 GMT
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"],
@@ -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.20250310202108
5
- Generated: Mon, 10 Mar 2025 20:22:24 GMT
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.20250310202108",
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', () => {
@@ -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({