@remoteoss/json-schema-form 0.12.1-dev.20250929100600 → 0.12.2-beta.0

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 CHANGED
@@ -1,3 +1,15 @@
1
+ #### 0.12.2-beta.0 (2025-10-23)
2
+
3
+ ##### New Features
4
+
5
+ * **v0:** Support nested fieldset conditionals ([#156](https://github.com/remoteoss/json-schema-form/pull/156)) ([877e82e1](https://github.com/remoteoss/json-schema-form/commit/877e82e148d1ff6964ba634108f8ca821c040565))
6
+
7
+ #### 0.12.1-beta.0 (2025-10-09)
8
+
9
+ ##### Bug Fixes
10
+
11
+ * **checkbox:** Support optional boolean type for v0 ([#236](https://github.com/remoteoss/json-schema-form/pull/236)) ([76e8927f](https://github.com/remoteoss/json-schema-form/commit/76e8927fc2f4615620fe34eef303b2f9c8c81e6e))
12
+
1
13
  #### 0.12.0-beta.0 (2025-06-25)
2
14
 
3
15
  ##### New Features
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.12.1-dev.20250929100600
5
- Generated: Mon, 29 Sep 2025 10:07:37 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.12.2-beta.0
5
+ Generated: Thu, 23 Oct 2025 14:03:06 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -1264,30 +1264,6 @@ function getPrefillValues(fields, initialValues = {}) {
1264
1264
  });
1265
1265
  return initialValues;
1266
1266
  }
1267
- function preserveNestedFieldsVisibility(field, parentPath = "") {
1268
- return field.fields?.reduce?.((acc, f) => {
1269
- const path = parentPath ? `${parentPath}.${f.name}` : f.name;
1270
- if (!(0, import_isNil.default)(f.isVisible)) {
1271
- acc[path] = f.isVisible;
1272
- }
1273
- if (f.fields) {
1274
- Object.assign(acc, preserveNestedFieldsVisibility(f, path));
1275
- }
1276
- return acc;
1277
- }, {});
1278
- }
1279
- function restoreNestedFieldsVisibility(field, nestedFieldsVisibility, parentPath = "") {
1280
- field.fields.forEach((f) => {
1281
- const path = parentPath ? `${parentPath}.${f.name}` : f.name;
1282
- const visibility = (0, import_get2.default)(nestedFieldsVisibility, path);
1283
- if (!(0, import_isNil.default)(visibility)) {
1284
- f.isVisible = visibility;
1285
- }
1286
- if (f.fields) {
1287
- restoreNestedFieldsVisibility(f, nestedFieldsVisibility, path);
1288
- }
1289
- });
1290
- }
1291
1267
  function updateField(field, requiredFields, node, formValues, logic, config) {
1292
1268
  if (!field) {
1293
1269
  return;
@@ -1299,13 +1275,9 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1299
1275
  if (fieldIsRequired) {
1300
1276
  field.isVisible = true;
1301
1277
  }
1302
- const nestedFieldsVisibility = preserveNestedFieldsVisibility(field);
1303
1278
  const updateAttributes = (fieldAttrs) => {
1304
1279
  Object.entries(fieldAttrs).forEach(([key, value]) => {
1305
1280
  field[key] = value;
1306
- if (key === "fields" && !(0, import_isNil.default)(nestedFieldsVisibility)) {
1307
- restoreNestedFieldsVisibility(field, nestedFieldsVisibility);
1308
- }
1309
1281
  if (key === "schema" && typeof value === "function") {
1310
1282
  field[key] = value();
1311
1283
  }
@@ -1334,7 +1306,8 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1334
1306
  const { rootFieldAttrs, newAttributes } = field.calculateConditionalProperties({
1335
1307
  isRequired: fieldIsRequired,
1336
1308
  conditionBranch: node,
1337
- formValues
1309
+ formValues,
1310
+ currentField: field
1338
1311
  });
1339
1312
  updateAttributes(newAttributes);
1340
1313
  removeConditionalStaleAttributes(field, newAttributes, rootFieldAttrs);
@@ -1535,7 +1508,7 @@ function extractParametersFromNode(schemaNode) {
1535
1508
  // — For checkboxes that only accept one value (string)
1536
1509
  ...presentation?.inputType === "checkbox" && { checkboxValue: node.const },
1537
1510
  // - For checkboxes with boolean value
1538
- ...presentation?.inputType === "checkbox" && node.type === "boolean" && {
1511
+ ...presentation?.inputType === "checkbox" && hasType(node.type, "boolean") && {
1539
1512
  // true is what describes this checkbox as a boolean, regardless if its required or not
1540
1513
  checkboxValue: true
1541
1514
  },
@@ -1618,35 +1591,42 @@ function isFieldRequired(node, field) {
1618
1591
  node?.required?.includes(field.name)
1619
1592
  );
1620
1593
  }
1621
- function rebuildFieldset(fields, property) {
1594
+ function rebuildFieldset(fields, currentFields, property) {
1622
1595
  if (property?.properties) {
1623
- return fields.map((field) => {
1596
+ return fields.map((field, index) => {
1624
1597
  const propertyConditionals = property.properties[field.name];
1598
+ const isVisible = currentFields[index].isVisible;
1625
1599
  if (!propertyConditionals) {
1626
- return field;
1600
+ return {
1601
+ ...field,
1602
+ isVisible
1603
+ };
1627
1604
  }
1628
1605
  const newFieldParams = extractParametersFromNode(propertyConditionals);
1629
1606
  if (field.fields) {
1630
1607
  return {
1631
1608
  ...field,
1632
1609
  ...newFieldParams,
1633
- fields: rebuildFieldset(field.fields, propertyConditionals)
1610
+ isVisible,
1611
+ fields: rebuildFieldset(field.fields, currentFields[index].fields, propertyConditionals)
1634
1612
  };
1635
1613
  }
1636
1614
  return {
1637
1615
  ...field,
1638
1616
  ...newFieldParams,
1617
+ isVisible,
1639
1618
  required: isFieldRequired(property, field)
1640
1619
  };
1641
1620
  });
1642
1621
  }
1643
- return fields.map((field) => ({
1622
+ return fields.map((field, index) => ({
1644
1623
  ...field,
1624
+ isVisible: currentFields[index].isVisible,
1645
1625
  required: isFieldRequired(property, field)
1646
1626
  }));
1647
1627
  }
1648
1628
  function calculateConditionalProperties({ fieldParams, customProperties, logic, config }) {
1649
- return ({ isRequired, conditionBranch, formValues }) => {
1629
+ return ({ isRequired, conditionBranch, formValues, currentField }) => {
1650
1630
  const conditionalProperty = conditionBranch?.properties?.[fieldParams.name];
1651
1631
  if (conditionalProperty) {
1652
1632
  const presentation = pickXKey(conditionalProperty, "presentation") ?? {};
@@ -1657,7 +1637,11 @@ function calculateConditionalProperties({ fieldParams, customProperties, logic,
1657
1637
  });
1658
1638
  let fieldSetFields;
1659
1639
  if (fieldParams.inputType === supportedTypes.FIELDSET) {
1660
- fieldSetFields = rebuildFieldset(fieldParams.fields, conditionalProperty);
1640
+ fieldSetFields = rebuildFieldset(
1641
+ fieldParams.fields,
1642
+ currentField.fields,
1643
+ conditionalProperty
1644
+ );
1661
1645
  newFieldParams.fields = fieldSetFields;
1662
1646
  }
1663
1647
  const { computedAttributes, ...restNewFieldParams } = newFieldParams;
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.12.1-dev.20250929100600
5
- Generated: Mon, 29 Sep 2025 10:07:37 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.12.2-beta.0
5
+ Generated: Thu, 23 Oct 2025 14:03:06 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -1226,30 +1226,6 @@ function getPrefillValues(fields, initialValues = {}) {
1226
1226
  });
1227
1227
  return initialValues;
1228
1228
  }
1229
- function preserveNestedFieldsVisibility(field, parentPath = "") {
1230
- return field.fields?.reduce?.((acc, f) => {
1231
- const path = parentPath ? `${parentPath}.${f.name}` : f.name;
1232
- if (!isNil(f.isVisible)) {
1233
- acc[path] = f.isVisible;
1234
- }
1235
- if (f.fields) {
1236
- Object.assign(acc, preserveNestedFieldsVisibility(f, path));
1237
- }
1238
- return acc;
1239
- }, {});
1240
- }
1241
- function restoreNestedFieldsVisibility(field, nestedFieldsVisibility, parentPath = "") {
1242
- field.fields.forEach((f) => {
1243
- const path = parentPath ? `${parentPath}.${f.name}` : f.name;
1244
- const visibility = get2(nestedFieldsVisibility, path);
1245
- if (!isNil(visibility)) {
1246
- f.isVisible = visibility;
1247
- }
1248
- if (f.fields) {
1249
- restoreNestedFieldsVisibility(f, nestedFieldsVisibility, path);
1250
- }
1251
- });
1252
- }
1253
1229
  function updateField(field, requiredFields, node, formValues, logic, config) {
1254
1230
  if (!field) {
1255
1231
  return;
@@ -1261,13 +1237,9 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1261
1237
  if (fieldIsRequired) {
1262
1238
  field.isVisible = true;
1263
1239
  }
1264
- const nestedFieldsVisibility = preserveNestedFieldsVisibility(field);
1265
1240
  const updateAttributes = (fieldAttrs) => {
1266
1241
  Object.entries(fieldAttrs).forEach(([key, value]) => {
1267
1242
  field[key] = value;
1268
- if (key === "fields" && !isNil(nestedFieldsVisibility)) {
1269
- restoreNestedFieldsVisibility(field, nestedFieldsVisibility);
1270
- }
1271
1243
  if (key === "schema" && typeof value === "function") {
1272
1244
  field[key] = value();
1273
1245
  }
@@ -1296,7 +1268,8 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1296
1268
  const { rootFieldAttrs, newAttributes } = field.calculateConditionalProperties({
1297
1269
  isRequired: fieldIsRequired,
1298
1270
  conditionBranch: node,
1299
- formValues
1271
+ formValues,
1272
+ currentField: field
1300
1273
  });
1301
1274
  updateAttributes(newAttributes);
1302
1275
  removeConditionalStaleAttributes(field, newAttributes, rootFieldAttrs);
@@ -1497,7 +1470,7 @@ function extractParametersFromNode(schemaNode) {
1497
1470
  // — For checkboxes that only accept one value (string)
1498
1471
  ...presentation?.inputType === "checkbox" && { checkboxValue: node.const },
1499
1472
  // - For checkboxes with boolean value
1500
- ...presentation?.inputType === "checkbox" && node.type === "boolean" && {
1473
+ ...presentation?.inputType === "checkbox" && hasType(node.type, "boolean") && {
1501
1474
  // true is what describes this checkbox as a boolean, regardless if its required or not
1502
1475
  checkboxValue: true
1503
1476
  },
@@ -1580,35 +1553,42 @@ function isFieldRequired(node, field) {
1580
1553
  node?.required?.includes(field.name)
1581
1554
  );
1582
1555
  }
1583
- function rebuildFieldset(fields, property) {
1556
+ function rebuildFieldset(fields, currentFields, property) {
1584
1557
  if (property?.properties) {
1585
- return fields.map((field) => {
1558
+ return fields.map((field, index) => {
1586
1559
  const propertyConditionals = property.properties[field.name];
1560
+ const isVisible = currentFields[index].isVisible;
1587
1561
  if (!propertyConditionals) {
1588
- return field;
1562
+ return {
1563
+ ...field,
1564
+ isVisible
1565
+ };
1589
1566
  }
1590
1567
  const newFieldParams = extractParametersFromNode(propertyConditionals);
1591
1568
  if (field.fields) {
1592
1569
  return {
1593
1570
  ...field,
1594
1571
  ...newFieldParams,
1595
- fields: rebuildFieldset(field.fields, propertyConditionals)
1572
+ isVisible,
1573
+ fields: rebuildFieldset(field.fields, currentFields[index].fields, propertyConditionals)
1596
1574
  };
1597
1575
  }
1598
1576
  return {
1599
1577
  ...field,
1600
1578
  ...newFieldParams,
1579
+ isVisible,
1601
1580
  required: isFieldRequired(property, field)
1602
1581
  };
1603
1582
  });
1604
1583
  }
1605
- return fields.map((field) => ({
1584
+ return fields.map((field, index) => ({
1606
1585
  ...field,
1586
+ isVisible: currentFields[index].isVisible,
1607
1587
  required: isFieldRequired(property, field)
1608
1588
  }));
1609
1589
  }
1610
1590
  function calculateConditionalProperties({ fieldParams, customProperties, logic, config }) {
1611
- return ({ isRequired, conditionBranch, formValues }) => {
1591
+ return ({ isRequired, conditionBranch, formValues, currentField }) => {
1612
1592
  const conditionalProperty = conditionBranch?.properties?.[fieldParams.name];
1613
1593
  if (conditionalProperty) {
1614
1594
  const presentation = pickXKey(conditionalProperty, "presentation") ?? {};
@@ -1619,7 +1599,11 @@ function calculateConditionalProperties({ fieldParams, customProperties, logic,
1619
1599
  });
1620
1600
  let fieldSetFields;
1621
1601
  if (fieldParams.inputType === supportedTypes.FIELDSET) {
1622
- fieldSetFields = rebuildFieldset(fieldParams.fields, conditionalProperty);
1602
+ fieldSetFields = rebuildFieldset(
1603
+ fieldParams.fields,
1604
+ currentField.fields,
1605
+ conditionalProperty
1606
+ );
1623
1607
  newFieldParams.fields = fieldSetFields;
1624
1608
  }
1625
1609
  const { computedAttributes, ...restNewFieldParams } = newFieldParams;
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
3
  Copyright (c) 2025 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.12.1-dev.20250929100600
5
- Generated: Mon, 29 Sep 2025 10:07:37 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.12.2-beta.0
5
+ Generated: Thu, 23 Oct 2025 14:03:06 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -12742,30 +12742,6 @@ function getPrefillValues(fields, initialValues = {}) {
12742
12742
  });
12743
12743
  return initialValues;
12744
12744
  }
12745
- function preserveNestedFieldsVisibility(field, parentPath = "") {
12746
- return field.fields?.reduce?.((acc, f) => {
12747
- const path = parentPath ? `${parentPath}.${f.name}` : f.name;
12748
- if (!(0, import_isNil.default)(f.isVisible)) {
12749
- acc[path] = f.isVisible;
12750
- }
12751
- if (f.fields) {
12752
- Object.assign(acc, preserveNestedFieldsVisibility(f, path));
12753
- }
12754
- return acc;
12755
- }, {});
12756
- }
12757
- function restoreNestedFieldsVisibility(field, nestedFieldsVisibility, parentPath = "") {
12758
- field.fields.forEach((f) => {
12759
- const path = parentPath ? `${parentPath}.${f.name}` : f.name;
12760
- const visibility = (0, import_get3.default)(nestedFieldsVisibility, path);
12761
- if (!(0, import_isNil.default)(visibility)) {
12762
- f.isVisible = visibility;
12763
- }
12764
- if (f.fields) {
12765
- restoreNestedFieldsVisibility(f, nestedFieldsVisibility, path);
12766
- }
12767
- });
12768
- }
12769
12745
  function updateField(field, requiredFields, node, formValues, logic, config) {
12770
12746
  if (!field) {
12771
12747
  return;
@@ -12777,13 +12753,9 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
12777
12753
  if (fieldIsRequired) {
12778
12754
  field.isVisible = true;
12779
12755
  }
12780
- const nestedFieldsVisibility = preserveNestedFieldsVisibility(field);
12781
12756
  const updateAttributes = (fieldAttrs) => {
12782
12757
  Object.entries(fieldAttrs).forEach(([key, value]) => {
12783
12758
  field[key] = value;
12784
- if (key === "fields" && !(0, import_isNil.default)(nestedFieldsVisibility)) {
12785
- restoreNestedFieldsVisibility(field, nestedFieldsVisibility);
12786
- }
12787
12759
  if (key === "schema" && typeof value === "function") {
12788
12760
  field[key] = value();
12789
12761
  }
@@ -12812,7 +12784,8 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
12812
12784
  const { rootFieldAttrs, newAttributes } = field.calculateConditionalProperties({
12813
12785
  isRequired: fieldIsRequired,
12814
12786
  conditionBranch: node,
12815
- formValues
12787
+ formValues,
12788
+ currentField: field
12816
12789
  });
12817
12790
  updateAttributes(newAttributes);
12818
12791
  removeConditionalStaleAttributes(field, newAttributes, rootFieldAttrs);
@@ -13013,7 +12986,7 @@ function extractParametersFromNode(schemaNode) {
13013
12986
  // — For checkboxes that only accept one value (string)
13014
12987
  ...presentation?.inputType === "checkbox" && { checkboxValue: node.const },
13015
12988
  // - For checkboxes with boolean value
13016
- ...presentation?.inputType === "checkbox" && node.type === "boolean" && {
12989
+ ...presentation?.inputType === "checkbox" && hasType(node.type, "boolean") && {
13017
12990
  // true is what describes this checkbox as a boolean, regardless if its required or not
13018
12991
  checkboxValue: true
13019
12992
  },
@@ -13096,35 +13069,42 @@ function isFieldRequired(node, field) {
13096
13069
  node?.required?.includes(field.name)
13097
13070
  );
13098
13071
  }
13099
- function rebuildFieldset(fields, property2) {
13072
+ function rebuildFieldset(fields, currentFields, property2) {
13100
13073
  if (property2?.properties) {
13101
- return fields.map((field) => {
13074
+ return fields.map((field, index) => {
13102
13075
  const propertyConditionals = property2.properties[field.name];
13076
+ const isVisible = currentFields[index].isVisible;
13103
13077
  if (!propertyConditionals) {
13104
- return field;
13078
+ return {
13079
+ ...field,
13080
+ isVisible
13081
+ };
13105
13082
  }
13106
13083
  const newFieldParams = extractParametersFromNode(propertyConditionals);
13107
13084
  if (field.fields) {
13108
13085
  return {
13109
13086
  ...field,
13110
13087
  ...newFieldParams,
13111
- fields: rebuildFieldset(field.fields, propertyConditionals)
13088
+ isVisible,
13089
+ fields: rebuildFieldset(field.fields, currentFields[index].fields, propertyConditionals)
13112
13090
  };
13113
13091
  }
13114
13092
  return {
13115
13093
  ...field,
13116
13094
  ...newFieldParams,
13095
+ isVisible,
13117
13096
  required: isFieldRequired(property2, field)
13118
13097
  };
13119
13098
  });
13120
13099
  }
13121
- return fields.map((field) => ({
13100
+ return fields.map((field, index) => ({
13122
13101
  ...field,
13102
+ isVisible: currentFields[index].isVisible,
13123
13103
  required: isFieldRequired(property2, field)
13124
13104
  }));
13125
13105
  }
13126
13106
  function calculateConditionalProperties({ fieldParams, customProperties, logic, config }) {
13127
- return ({ isRequired, conditionBranch, formValues }) => {
13107
+ return ({ isRequired, conditionBranch, formValues, currentField }) => {
13128
13108
  const conditionalProperty = conditionBranch?.properties?.[fieldParams.name];
13129
13109
  if (conditionalProperty) {
13130
13110
  const presentation = pickXKey(conditionalProperty, "presentation") ?? {};
@@ -13135,7 +13115,11 @@ function calculateConditionalProperties({ fieldParams, customProperties, logic,
13135
13115
  });
13136
13116
  let fieldSetFields;
13137
13117
  if (fieldParams.inputType === supportedTypes.FIELDSET) {
13138
- fieldSetFields = rebuildFieldset(fieldParams.fields, conditionalProperty);
13118
+ fieldSetFields = rebuildFieldset(
13119
+ fieldParams.fields,
13120
+ currentField.fields,
13121
+ conditionalProperty
13122
+ );
13139
13123
  newFieldParams.fields = fieldSetFields;
13140
13124
  }
13141
13125
  const { computedAttributes, ...restNewFieldParams } = newFieldParams;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remoteoss/json-schema-form",
3
- "version": "0.12.1-dev.20250929100600",
3
+ "version": "0.12.2-beta.0",
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",
@@ -2307,6 +2307,85 @@ describe('createHeadlessForm', () => {
2307
2307
  ).toBeUndefined();
2308
2308
  });
2309
2309
  });
2310
+
2311
+ describe('supports computed values based on values from nested fieldsets', () => {
2312
+ it("computed value for total_contributions is calculated correctly with defaults when user selects 'yes' for has_retirement_plan", () => {
2313
+ const { fields, handleValidation } = createHeadlessForm(
2314
+ schemaWithNestedFieldsetsConditionals,
2315
+ {}
2316
+ );
2317
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
2318
+
2319
+ expect(
2320
+ validateForm({
2321
+ perks: {
2322
+ benefits_package: 'basic',
2323
+ has_retirement_plan: 'yes',
2324
+ declare_amount: 'no',
2325
+ retirement_plan: {
2326
+ plan_name: 'test',
2327
+ create_plan: 'no',
2328
+ },
2329
+ },
2330
+ })
2331
+ ).toEqual({ perks: { retirement_plan: { year: 'Required field' } } });
2332
+
2333
+ expect(getField(fields, 'total_contributions').isVisible).toBe(true);
2334
+ expect(getField(fields, 'total_contributions').default).toBe(0);
2335
+ expect(getField(fields, 'total_contributions').const).toBe(0);
2336
+ });
2337
+
2338
+ it('computed value for total_contributions is calculated correctly based on the selected months', () => {
2339
+ const { fields, handleValidation } = createHeadlessForm(
2340
+ schemaWithNestedFieldsetsConditionals,
2341
+ {}
2342
+ );
2343
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
2344
+
2345
+ expect(
2346
+ validateForm({
2347
+ perks: {
2348
+ benefits_package: 'basic',
2349
+ has_retirement_plan: 'yes',
2350
+ declare_amount: 'no',
2351
+ retirement_plan: {
2352
+ plan_name: 'test',
2353
+ year: 2025,
2354
+ create_plan: 'yes',
2355
+ planned_contributions: {
2356
+ months: ['january', 'february', 'march', 'april', 'may'],
2357
+ },
2358
+ },
2359
+ },
2360
+ })
2361
+ ).toBeUndefined();
2362
+
2363
+ expect(getField(fields, 'total_contributions').isVisible).toBe(true);
2364
+ expect(getField(fields, 'total_contributions').default).toBe(5);
2365
+ expect(getField(fields, 'total_contributions').const).toBe(5);
2366
+
2367
+ expect(
2368
+ validateForm({
2369
+ perks: {
2370
+ benefits_package: 'basic',
2371
+ has_retirement_plan: 'yes',
2372
+ declare_amount: 'no',
2373
+ retirement_plan: {
2374
+ plan_name: 'test',
2375
+ year: 2025,
2376
+ create_plan: 'yes',
2377
+ planned_contributions: {
2378
+ months: ['january', 'february', 'march'],
2379
+ },
2380
+ },
2381
+ },
2382
+ })
2383
+ ).toBeUndefined();
2384
+
2385
+ expect(getField(fields, 'total_contributions').default).toBe(3);
2386
+ expect(getField(fields, 'total_contributions').const).toBe(3);
2387
+ });
2388
+ });
2310
2389
  });
2311
2390
 
2312
2391
  it('support "email" field type', () => {
@@ -2434,6 +2513,28 @@ describe('createHeadlessForm', () => {
2434
2513
  // Explained at "Given values from hidden fields, it does not thrown an error"
2435
2514
  expect(handleValidation({ has_pet: false, pet_is_cat: true }).formErrors).toBeUndefined();
2436
2515
  });
2516
+
2517
+ it('should set checkboxValue: true for optional boolean type', () => {
2518
+ const schema = {
2519
+ type: 'object',
2520
+ properties: {
2521
+ boolean_checkbox: {
2522
+ title: 'Boolean Checkbox Test',
2523
+ type: ['boolean', 'null'],
2524
+ 'x-jsf-presentation': {
2525
+ inputType: 'checkbox',
2526
+ },
2527
+ },
2528
+ },
2529
+ required: [],
2530
+ };
2531
+
2532
+ const result = createHeadlessForm(schema);
2533
+
2534
+ const [optionalCheckbox] = result.fields;
2535
+ expect(optionalCheckbox?.inputType).toBe('checkbox');
2536
+ expect(optionalCheckbox?.checkboxValue).toBe(true);
2537
+ });
2437
2538
  });
2438
2539
  });
2439
2540
 
@@ -2023,7 +2023,7 @@ export const schemaWithNestedFieldsetsConditionals = {
2023
2023
  title: 'No',
2024
2024
  },
2025
2025
  ],
2026
- title: 'Declare retirement amount?',
2026
+ title: 'Declare planned retirement amount?',
2027
2027
  type: 'string',
2028
2028
  default: 'yes',
2029
2029
  'x-jsf-presentation': {
@@ -2031,6 +2031,23 @@ export const schemaWithNestedFieldsetsConditionals = {
2031
2031
  },
2032
2032
  },
2033
2033
  retirement_plan: {
2034
+ allOf: [
2035
+ {
2036
+ if: {
2037
+ properties: {
2038
+ create_plan: {
2039
+ const: 'yes',
2040
+ },
2041
+ },
2042
+ },
2043
+ then: {},
2044
+ else: {
2045
+ properties: {
2046
+ planned_contributions: false,
2047
+ },
2048
+ },
2049
+ },
2050
+ ],
2034
2051
  type: 'object',
2035
2052
  title: 'Retirement plan',
2036
2053
  properties: {
@@ -2041,10 +2058,72 @@ export const schemaWithNestedFieldsetsConditionals = {
2041
2058
  year: {
2042
2059
  type: 'number',
2043
2060
  title: 'Year',
2061
+ default: 2025,
2044
2062
  },
2045
2063
  amount: {
2046
2064
  type: 'number',
2047
- title: 'Amount',
2065
+ title: 'Planned amount',
2066
+ },
2067
+ create_plan: {
2068
+ type: 'radio',
2069
+ title: 'Create plan?',
2070
+ oneOf: [
2071
+ {
2072
+ const: 'yes',
2073
+ title: 'Yes',
2074
+ },
2075
+ {
2076
+ const: 'no',
2077
+ title: 'No',
2078
+ },
2079
+ ],
2080
+ default: 'yes',
2081
+ 'x-jsf-presentation': {
2082
+ inputType: 'radio',
2083
+ },
2084
+ },
2085
+ planned_contributions: {
2086
+ type: 'object',
2087
+ title: 'Planned contributions',
2088
+ properties: {
2089
+ months: {
2090
+ default: ['january', 'february'],
2091
+ oneOf: [
2092
+ {
2093
+ const: 'january',
2094
+ title: 'January',
2095
+ },
2096
+ {
2097
+ const: 'february',
2098
+ title: 'February',
2099
+ },
2100
+ {
2101
+ const: 'march',
2102
+ title: 'March',
2103
+ },
2104
+ {
2105
+ const: 'april',
2106
+ title: 'April',
2107
+ },
2108
+ {
2109
+ const: 'may',
2110
+ title: 'May',
2111
+ },
2112
+ {
2113
+ const: 'june',
2114
+ title: 'June',
2115
+ },
2116
+ ],
2117
+ title: "Select the months when you'll contribute",
2118
+ type: 'array',
2119
+ 'x-jsf-presentation': {
2120
+ inputType: 'checkbox',
2121
+ },
2122
+ },
2123
+ },
2124
+ 'x-jsf-presentation': {
2125
+ inputType: 'fieldset',
2126
+ },
2048
2127
  },
2049
2128
  },
2050
2129
  required: ['plan_name', 'year'],
@@ -2060,6 +2139,19 @@ export const schemaWithNestedFieldsetsConditionals = {
2060
2139
  inputType: 'fieldset',
2061
2140
  },
2062
2141
  },
2142
+ total_contributions: {
2143
+ title: 'Total contributions',
2144
+ description: 'The total contributions for the retirement plan',
2145
+ type: 'number',
2146
+ 'x-jsf-presentation': {
2147
+ inputType: 'number',
2148
+ },
2149
+ 'x-jsf-logic-computedAttrs': {
2150
+ const: 'total_contributions',
2151
+ default: 'total_contributions',
2152
+ description: 'You will contribute {{total_contributions}} times this year',
2153
+ },
2154
+ },
2063
2155
  },
2064
2156
  allOf: [
2065
2157
  {
@@ -2141,6 +2233,125 @@ export const schemaWithNestedFieldsetsConditionals = {
2141
2233
  },
2142
2234
  },
2143
2235
  ],
2236
+ 'x-jsf-logic': {
2237
+ computedValues: {
2238
+ total_contributions: {
2239
+ rule: {
2240
+ if: [
2241
+ {
2242
+ and: [
2243
+ {
2244
+ '===': [
2245
+ {
2246
+ var: 'perks.has_retirement_plan',
2247
+ },
2248
+ 'yes',
2249
+ ],
2250
+ },
2251
+ {
2252
+ '===': [
2253
+ {
2254
+ var: 'perks.retirement_plan.create_plan',
2255
+ },
2256
+ 'yes',
2257
+ ],
2258
+ },
2259
+ ],
2260
+ },
2261
+ {
2262
+ '+': [
2263
+ {
2264
+ if: [
2265
+ {
2266
+ in: [
2267
+ 'january',
2268
+ {
2269
+ var: 'perks.retirement_plan.planned_contributions.months',
2270
+ },
2271
+ ],
2272
+ },
2273
+ 1,
2274
+ 0,
2275
+ ],
2276
+ },
2277
+ {
2278
+ if: [
2279
+ {
2280
+ in: [
2281
+ 'february',
2282
+ {
2283
+ var: 'perks.retirement_plan.planned_contributions.months',
2284
+ },
2285
+ ],
2286
+ },
2287
+ 1,
2288
+ 0,
2289
+ ],
2290
+ },
2291
+ {
2292
+ if: [
2293
+ {
2294
+ in: [
2295
+ 'march',
2296
+ {
2297
+ var: 'perks.retirement_plan.planned_contributions.months',
2298
+ },
2299
+ ],
2300
+ },
2301
+ 1,
2302
+ 0,
2303
+ ],
2304
+ },
2305
+ {
2306
+ if: [
2307
+ {
2308
+ in: [
2309
+ 'april',
2310
+ {
2311
+ var: 'perks.retirement_plan.planned_contributions.months',
2312
+ },
2313
+ ],
2314
+ },
2315
+ 1,
2316
+ 0,
2317
+ ],
2318
+ },
2319
+ {
2320
+ if: [
2321
+ {
2322
+ in: [
2323
+ 'may',
2324
+ {
2325
+ var: 'perks.retirement_plan.planned_contributions.months',
2326
+ },
2327
+ ],
2328
+ },
2329
+ 1,
2330
+ 0,
2331
+ ],
2332
+ },
2333
+ {
2334
+ if: [
2335
+ {
2336
+ in: [
2337
+ 'june',
2338
+ {
2339
+ var: 'perks.retirement_plan.planned_contributions.months',
2340
+ },
2341
+ ],
2342
+ },
2343
+ 1,
2344
+ 0,
2345
+ ],
2346
+ },
2347
+ ],
2348
+ },
2349
+ 0,
2350
+ ],
2351
+ },
2352
+ },
2353
+ },
2354
+ },
2144
2355
  };
2145
2356
 
2146
2357
  export const schemaWorkSchedule = {