@remoteoss/json-schema-form 0.11.11-dev.20250220174843 → 0.11.12-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.11.12-beta.0 (2025-04-15)
2
+
3
+ ##### Chores
4
+
5
+ * change deprecated 'presentation' to 'x-jsf-presentation' in test helpers ([#159](https://github.com/remoteoss/json-schema-form/pull/159)) ([3f03b16d](https://github.com/remoteoss/json-schema-form/commit/3f03b16dce6acd801ff497a9c547cceb1b6347c5))
6
+
7
+ #### 0.11.11-beta.0 (2025-02-20)
8
+
9
+ ##### Bug Fixes
10
+
11
+ * **conditionals:** Add support to `oneOf` ([#136](https://github.com/remoteoss/json-schema-form/pull/136)) ([966cec59](https://github.com/remoteoss/json-schema-form/commit/966cec595ed2a1ff35f9d15e22acaea16cdd5113))
12
+
1
13
  #### 0.11.10-beta.0 (2025-01-22)
2
14
 
3
15
  ##### Bug Fixes
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.11-dev.20250220174843
5
- Generated: Thu, 20 Feb 2025 17:48:57 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.11.12-beta.0
5
+ Generated: Tue, 15 Apr 2025 08:28:29 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -101,6 +101,11 @@ function checkIfConditionMatchesProperties(node, formValues, formFields, logic)
101
101
  if (typeof node.if === "boolean") {
102
102
  return node.if;
103
103
  }
104
+ if (node.if.anyOf) {
105
+ return node.if.anyOf.some(
106
+ (property) => checkIfConditionMatchesProperties({ if: property }, formValues, formFields, logic)
107
+ );
108
+ }
104
109
  return Object.keys(node.if.properties ?? {}).every((name) => {
105
110
  const currentProperty = node.if.properties[name];
106
111
  const value = formValues[name];
@@ -1164,11 +1169,7 @@ function hasType(type, typeName) {
1164
1169
  return Array.isArray(type) ? type.includes(typeName) : type === typeName;
1165
1170
  }
1166
1171
  function getField(fieldName, fields) {
1167
- if (Array.isArray(fields)) {
1168
- return fields.find(({ name }) => name === fieldName);
1169
- } else {
1170
- return fields[fieldName];
1171
- }
1172
+ return fields.find(({ name }) => name === fieldName);
1172
1173
  }
1173
1174
  function validateFieldSchema(field, value, logic) {
1174
1175
  const validator = buildYupSchema(field, {}, logic);
@@ -1382,25 +1383,6 @@ function processNode({
1382
1383
  logic
1383
1384
  });
1384
1385
  }
1385
- if (inputType === supportedTypes.GROUP_ARRAY) {
1386
- const values = formValues[name];
1387
- if (Array.isArray(values)) {
1388
- const newFields = [];
1389
- const field = getField(name, formFields);
1390
- values.forEach((value) => {
1391
- const fields = field.fields();
1392
- processNode({
1393
- node: nestedNode.items,
1394
- formValues: value,
1395
- formFields: fields,
1396
- parentID: name,
1397
- logic
1398
- });
1399
- newFields.push(fields);
1400
- });
1401
- field.dynamicFields = newFields;
1402
- }
1403
- }
1404
1386
  });
1405
1387
  }
1406
1388
  if (node["x-jsf-logic"]) {
@@ -1772,16 +1754,6 @@ function buildFieldParameters(name, fieldProperties, required = [], config = {},
1772
1754
  logic
1773
1755
  );
1774
1756
  }
1775
- if (inputType === supportedTypes.GROUP_ARRAY) {
1776
- fields = () => getFieldsFromJSONSchema(
1777
- fieldProperties.items,
1778
- {
1779
- customProperties: (0, import_get3.default)(config, `customProperties.${name}.customProperties`, {}),
1780
- parentID: name
1781
- },
1782
- logic
1783
- );
1784
- }
1785
1757
  const result = {
1786
1758
  name,
1787
1759
  inputType,
@@ -1875,10 +1847,12 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
1875
1847
  if (fieldParams.inputType === "group-array") {
1876
1848
  const groupArrayItems = convertJSONSchemaPropertiesToFieldParameters(fieldParams.items);
1877
1849
  const groupArrayFields = groupArrayItems.map((groupArrayItem) => {
1850
+ groupArrayItem.nameKey = groupArrayItem.name;
1878
1851
  const customProperties = null;
1879
1852
  const composeFn = getComposeFunctionForField(groupArrayItem, !!customProperties);
1880
1853
  return composeFn(groupArrayItem);
1881
1854
  });
1855
+ fieldParams.nameKey = fieldParams.name;
1882
1856
  fieldParams.nthFieldGroup = {
1883
1857
  name: fieldParams.name,
1884
1858
  label: fieldParams.label,
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.11-dev.20250220174843
5
- Generated: Thu, 20 Feb 2025 17:48:57 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.11.12-beta.0
5
+ Generated: Tue, 15 Apr 2025 08:28:29 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -64,6 +64,11 @@ function checkIfConditionMatchesProperties(node, formValues, formFields, logic)
64
64
  if (typeof node.if === "boolean") {
65
65
  return node.if;
66
66
  }
67
+ if (node.if.anyOf) {
68
+ return node.if.anyOf.some(
69
+ (property) => checkIfConditionMatchesProperties({ if: property }, formValues, formFields, logic)
70
+ );
71
+ }
67
72
  return Object.keys(node.if.properties ?? {}).every((name) => {
68
73
  const currentProperty = node.if.properties[name];
69
74
  const value = formValues[name];
@@ -1127,11 +1132,7 @@ function hasType(type, typeName) {
1127
1132
  return Array.isArray(type) ? type.includes(typeName) : type === typeName;
1128
1133
  }
1129
1134
  function getField(fieldName, fields) {
1130
- if (Array.isArray(fields)) {
1131
- return fields.find(({ name }) => name === fieldName);
1132
- } else {
1133
- return fields[fieldName];
1134
- }
1135
+ return fields.find(({ name }) => name === fieldName);
1135
1136
  }
1136
1137
  function validateFieldSchema(field, value, logic) {
1137
1138
  const validator = buildYupSchema(field, {}, logic);
@@ -1345,25 +1346,6 @@ function processNode({
1345
1346
  logic
1346
1347
  });
1347
1348
  }
1348
- if (inputType === supportedTypes.GROUP_ARRAY) {
1349
- const values = formValues[name];
1350
- if (Array.isArray(values)) {
1351
- const newFields = [];
1352
- const field = getField(name, formFields);
1353
- values.forEach((value) => {
1354
- const fields = field.fields();
1355
- processNode({
1356
- node: nestedNode.items,
1357
- formValues: value,
1358
- formFields: fields,
1359
- parentID: name,
1360
- logic
1361
- });
1362
- newFields.push(fields);
1363
- });
1364
- field.dynamicFields = newFields;
1365
- }
1366
- }
1367
1349
  });
1368
1350
  }
1369
1351
  if (node["x-jsf-logic"]) {
@@ -1735,16 +1717,6 @@ function buildFieldParameters(name, fieldProperties, required = [], config = {},
1735
1717
  logic
1736
1718
  );
1737
1719
  }
1738
- if (inputType === supportedTypes.GROUP_ARRAY) {
1739
- fields = () => getFieldsFromJSONSchema(
1740
- fieldProperties.items,
1741
- {
1742
- customProperties: get3(config, `customProperties.${name}.customProperties`, {}),
1743
- parentID: name
1744
- },
1745
- logic
1746
- );
1747
- }
1748
1720
  const result = {
1749
1721
  name,
1750
1722
  inputType,
@@ -1838,10 +1810,12 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
1838
1810
  if (fieldParams.inputType === "group-array") {
1839
1811
  const groupArrayItems = convertJSONSchemaPropertiesToFieldParameters(fieldParams.items);
1840
1812
  const groupArrayFields = groupArrayItems.map((groupArrayItem) => {
1813
+ groupArrayItem.nameKey = groupArrayItem.name;
1841
1814
  const customProperties = null;
1842
1815
  const composeFn = getComposeFunctionForField(groupArrayItem, !!customProperties);
1843
1816
  return composeFn(groupArrayItem);
1844
1817
  });
1818
+ fieldParams.nameKey = fieldParams.name;
1845
1819
  fieldParams.nthFieldGroup = {
1846
1820
  name: fieldParams.name,
1847
1821
  label: fieldParams.label,
@@ -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.11-dev.20250220174843
5
- Generated: Thu, 20 Feb 2025 17:48:57 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.11.12-beta.0
5
+ Generated: Tue, 15 Apr 2025 08:28:29 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -11581,6 +11581,11 @@ function checkIfConditionMatchesProperties(node, formValues, formFields, logic)
11581
11581
  if (typeof node.if === "boolean") {
11582
11582
  return node.if;
11583
11583
  }
11584
+ if (node.if.anyOf) {
11585
+ return node.if.anyOf.some(
11586
+ (property2) => checkIfConditionMatchesProperties({ if: property2 }, formValues, formFields, logic)
11587
+ );
11588
+ }
11584
11589
  return Object.keys(node.if.properties ?? {}).every((name) => {
11585
11590
  const currentProperty = node.if.properties[name];
11586
11591
  const value = formValues[name];
@@ -12643,11 +12648,7 @@ function hasType(type, typeName) {
12643
12648
  return Array.isArray(type) ? type.includes(typeName) : type === typeName;
12644
12649
  }
12645
12650
  function getField(fieldName, fields) {
12646
- if (Array.isArray(fields)) {
12647
- return fields.find(({ name }) => name === fieldName);
12648
- } else {
12649
- return fields[fieldName];
12650
- }
12651
+ return fields.find(({ name }) => name === fieldName);
12651
12652
  }
12652
12653
  function validateFieldSchema(field, value, logic) {
12653
12654
  const validator = buildYupSchema(field, {}, logic);
@@ -12861,25 +12862,6 @@ function processNode({
12861
12862
  logic
12862
12863
  });
12863
12864
  }
12864
- if (inputType === supportedTypes.GROUP_ARRAY) {
12865
- const values2 = formValues[name];
12866
- if (Array.isArray(values2)) {
12867
- const newFields = [];
12868
- const field = getField(name, formFields);
12869
- values2.forEach((value) => {
12870
- const fields = field.fields();
12871
- processNode({
12872
- node: nestedNode.items,
12873
- formValues: value,
12874
- formFields: fields,
12875
- parentID: name,
12876
- logic
12877
- });
12878
- newFields.push(fields);
12879
- });
12880
- field.dynamicFields = newFields;
12881
- }
12882
- }
12883
12865
  });
12884
12866
  }
12885
12867
  if (node["x-jsf-logic"]) {
@@ -13251,16 +13233,6 @@ function buildFieldParameters(name, fieldProperties, required2 = [], config = {}
13251
13233
  logic
13252
13234
  );
13253
13235
  }
13254
- if (inputType === supportedTypes.GROUP_ARRAY) {
13255
- fields = () => getFieldsFromJSONSchema(
13256
- fieldProperties.items,
13257
- {
13258
- customProperties: (0, import_get4.default)(config, `customProperties.${name}.customProperties`, {}),
13259
- parentID: name
13260
- },
13261
- logic
13262
- );
13263
- }
13264
13236
  const result = {
13265
13237
  name,
13266
13238
  inputType,
@@ -13354,10 +13326,12 @@ function getFieldsFromJSONSchema(scopedJsonSchema, config, logic) {
13354
13326
  if (fieldParams.inputType === "group-array") {
13355
13327
  const groupArrayItems = convertJSONSchemaPropertiesToFieldParameters(fieldParams.items);
13356
13328
  const groupArrayFields = groupArrayItems.map((groupArrayItem) => {
13329
+ groupArrayItem.nameKey = groupArrayItem.name;
13357
13330
  const customProperties = null;
13358
13331
  const composeFn = getComposeFunctionForField(groupArrayItem, !!customProperties);
13359
13332
  return composeFn(groupArrayItem);
13360
13333
  });
13334
+ fieldParams.nameKey = fieldParams.name;
13361
13335
  fieldParams.nthFieldGroup = {
13362
13336
  name: fieldParams.name,
13363
13337
  label: fieldParams.label,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remoteoss/json-schema-form",
3
- "version": "0.11.11-dev.20250220174843",
3
+ "version": "0.11.12-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",
@@ -422,233 +422,6 @@ describe('Conditional attributes updated', () => {
422
422
  handleValidation({ is_full_time: 'no' });
423
423
  expect(fields[1].visibilityCondition).toEqual(expect.any(Function));
424
424
  });
425
-
426
- it('Group array nested condition', () => {
427
- const { fields, handleValidation } = createHeadlessForm({
428
- type: 'object',
429
- additionalProperties: false,
430
- properties: {
431
- companies: {
432
- type: 'array',
433
- 'x-jsf-presentation': {
434
- inputType: 'group-array',
435
- },
436
- items: {
437
- type: 'object',
438
- properties: {
439
- company: {
440
- type: 'string',
441
- oneOf: [
442
- {
443
- title: 'A',
444
- const: 'A',
445
- },
446
- {
447
- title: 'B',
448
- const: 'B',
449
- },
450
- ],
451
- 'x-jsf-presentation': {
452
- inputType: 'select',
453
- },
454
- },
455
- role: {
456
- title: 'Role',
457
- oneOf: [],
458
- 'x-jsf-presentation': {
459
- inputType: 'select',
460
- },
461
- },
462
- },
463
- allOf: [
464
- {
465
- if: {
466
- properties: {
467
- company: {
468
- const: 'A',
469
- },
470
- },
471
- required: ['company'],
472
- },
473
- then: {
474
- properties: {
475
- role: {
476
- oneOf: [
477
- {
478
- title: 'adminA',
479
- const: 'adminA',
480
- },
481
- {
482
- title: 'userA',
483
- const: 'userA',
484
- },
485
- ],
486
- 'x-jsf-presentation': {
487
- inputType: 'select',
488
- },
489
- },
490
- },
491
- required: ['role'],
492
- },
493
- },
494
- {
495
- if: {
496
- properties: {
497
- company: {
498
- const: 'B',
499
- },
500
- },
501
- required: ['company'],
502
- },
503
- then: {
504
- properties: {
505
- role: {
506
- oneOf: [
507
- {
508
- title: 'adminB',
509
- const: 'adminB',
510
- },
511
- {
512
- title: 'userB',
513
- const: 'userB',
514
- },
515
- ],
516
- 'x-jsf-presentation': {
517
- inputType: 'select',
518
- },
519
- },
520
- },
521
- required: ['role'],
522
- },
523
- },
524
- ],
525
- required: ['company', 'role'],
526
- },
527
- },
528
- },
529
- required: ['companies'],
530
- });
531
-
532
- handleValidation({ companies: [{ company: 'A' }, { company: 'B' }] });
533
- expect(fields[0].dynamicFields[0][1].options).toEqual([
534
- { label: 'adminA', value: 'adminA' },
535
- { label: 'userA', value: 'userA' },
536
- ]);
537
- expect(fields[0].dynamicFields[1][1].options).toEqual([
538
- { label: 'adminB', value: 'adminB' },
539
- { label: 'userB', value: 'userB' },
540
- ]);
541
- handleValidation({ companies: [] });
542
- expect(fields[0].dynamicFields).toEqual([]);
543
- });
544
-
545
- it('select multiple conditions', () => {
546
- const { fields, handleValidation } = createHeadlessForm({
547
- type: 'object',
548
- additionalProperties: false,
549
- properties: {
550
- company: {
551
- title: 'Select Company',
552
- type: 'string',
553
- description: 'Choose a company',
554
- oneOf: [
555
- {
556
- title: 'A',
557
- const: 'A',
558
- },
559
- {
560
- title: 'B',
561
- const: 'B',
562
- },
563
- ],
564
- 'x-jsf-presentation': {
565
- inputType: 'select',
566
- },
567
- },
568
- role: {
569
- title: 'Role',
570
- oneOf: [],
571
- 'x-jsf-presentation': {
572
- inputType: 'select',
573
- },
574
- },
575
- },
576
- allOf: [
577
- {
578
- if: {
579
- properties: {
580
- company: {
581
- const: 'A',
582
- },
583
- },
584
- required: ['company'],
585
- },
586
- then: {
587
- properties: {
588
- role: {
589
- oneOf: [
590
- {
591
- title: 'adminA',
592
- const: 'adminA',
593
- },
594
- {
595
- title: 'userA',
596
- const: 'userA',
597
- },
598
- ],
599
- 'x-jsf-presentation': {
600
- inputType: 'select',
601
- },
602
- },
603
- },
604
- required: ['role'],
605
- },
606
- },
607
- {
608
- if: {
609
- properties: {
610
- company: {
611
- const: 'B',
612
- },
613
- },
614
- required: ['company'],
615
- },
616
- then: {
617
- properties: {
618
- role: {
619
- oneOf: [
620
- {
621
- title: 'adminB',
622
- const: 'adminB',
623
- },
624
- {
625
- title: 'userB',
626
- const: 'userB',
627
- },
628
- ],
629
- 'x-jsf-presentation': {
630
- inputType: 'select',
631
- },
632
- },
633
- },
634
- required: ['role'],
635
- },
636
- },
637
- ],
638
- required: ['company', 'role'],
639
- });
640
-
641
- handleValidation({ company: 'A' });
642
- expect(fields[1].options).toEqual([
643
- { label: 'adminA', value: 'adminA' },
644
- { label: 'userA', value: 'userA' },
645
- ]);
646
- handleValidation({ company: 'B' });
647
- expect(fields[1].options).toEqual([
648
- { label: 'adminB', value: 'adminB' },
649
- { label: 'userB', value: 'userB' },
650
- ]);
651
- });
652
425
  });
653
426
 
654
427
  describe('Conditional with a minimum value check', () => {
@@ -775,6 +548,54 @@ describe('Conditional with literal booleans', () => {
775
548
  });
776
549
  });
777
550
 
551
+ describe('Conditional with anyOf', () => {
552
+ const schema = {
553
+ additionalProperties: false,
554
+ type: 'object',
555
+ properties: {
556
+ field_a: { type: 'string' },
557
+ field_b: { type: 'string' },
558
+ field_c: { type: 'string' },
559
+ },
560
+ allOf: [
561
+ {
562
+ if: {
563
+ anyOf: [
564
+ { properties: { field_a: { const: '1' } }, required: ['field_a'] },
565
+ { properties: { field_b: { const: '2' } }, required: ['field_b'] },
566
+ ],
567
+ },
568
+ then: {
569
+ required: ['field_c'],
570
+ },
571
+ else: {
572
+ properties: {
573
+ field_c: false,
574
+ },
575
+ },
576
+ },
577
+ ],
578
+ };
579
+
580
+ it('handles true case', () => {
581
+ const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
582
+
583
+ expect(fields[2].isVisible).toBe(false);
584
+ expect(handleValidation({ field_a: 'x', field_b: '2' }).formErrors).toEqual({
585
+ field_c: 'Required field',
586
+ });
587
+ expect(fields[2].isVisible).toBe(true);
588
+ });
589
+
590
+ it('handles false case', () => {
591
+ const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
592
+
593
+ expect(fields[2].isVisible).toBe(false);
594
+ expect(handleValidation({ field_a: 'x', field_b: 'x' }).formErrors).toBeUndefined();
595
+ expect(fields[2].isVisible).toBe(false);
596
+ });
597
+ });
598
+
778
599
  describe('Conditionals - bugs and code-smells', () => {
779
600
  // Why do we have these bugs?
780
601
  // To be honest we never realized it much later later.
@@ -63,7 +63,6 @@ import {
63
63
  schemaForErrorMessageSpecificity,
64
64
  jsfConfigForErrorMessageSpecificity,
65
65
  schemaInputTypeFile,
66
- nestedGroupArrayForm,
67
66
  } from './helpers';
68
67
  import { mockConsole, restoreConsoleAndEnsureItWasNotCalled } from './testUtils';
69
68
  import { createHeadlessForm } from '@/createHeadlessForm';
@@ -1605,6 +1604,7 @@ describe('createHeadlessForm', () => {
1605
1604
  type: 'text',
1606
1605
  description: 'Enter your child’s full name',
1607
1606
  maxLength: 255,
1607
+ nameKey: 'full_name',
1608
1608
  label: 'Child Full Name',
1609
1609
  name: 'full_name',
1610
1610
  required: true,
@@ -1616,6 +1616,7 @@ describe('createHeadlessForm', () => {
1616
1616
  required: true,
1617
1617
  description: 'Enter your child’s date of birth',
1618
1618
  maxLength: 255,
1619
+ nameKey: 'birthdate',
1619
1620
  },
1620
1621
  {
1621
1622
  type: 'radio',
@@ -1634,6 +1635,7 @@ describe('createHeadlessForm', () => {
1634
1635
  required: true,
1635
1636
  description:
1636
1637
  'We know sex is non-binary but for insurance and payroll purposes, we need to collect this information.',
1638
+ nameKey: 'sex',
1637
1639
  },
1638
1640
  ]);
1639
1641
  });
@@ -1735,63 +1737,6 @@ describe('createHeadlessForm', () => {
1735
1737
  });
1736
1738
  });
1737
1739
 
1738
- it('nested "group-array" fields', () => {
1739
- const result = createHeadlessForm({
1740
- properties: {
1741
- nestedGroupArray: nestedGroupArrayForm,
1742
- },
1743
- });
1744
-
1745
- expect(result.fields[0]).toMatchObject({
1746
- label: 'Parent object',
1747
- name: 'nestedGroupArray',
1748
- required: false,
1749
- type: 'group-array',
1750
- inputType: 'group-array',
1751
- jsonType: 'array',
1752
- fields: expect.any(Function),
1753
- });
1754
-
1755
- expect(result.fields[0].fields()).toMatchObject([
1756
- {
1757
- type: 'text',
1758
- description: 'Simple text field',
1759
- maxLength: 255,
1760
- label: 'Outer Field',
1761
- name: 'notNested',
1762
- required: true,
1763
- },
1764
- {
1765
- label: 'Nested group-array',
1766
- name: 'nested',
1767
- required: true,
1768
- type: 'group-array',
1769
- inputType: 'group-array',
1770
- jsonType: 'array',
1771
- fields: expect.any(Function),
1772
- },
1773
- ]);
1774
-
1775
- expect(result.fields[0].fields()[1].fields()).toMatchObject([
1776
- {
1777
- type: 'text',
1778
- description: 'First nested text field',
1779
- maxLength: 255,
1780
- label: 'Inner Field 1',
1781
- name: 'nestedField1',
1782
- required: true,
1783
- },
1784
- {
1785
- type: 'text',
1786
- description: 'Second nested text field',
1787
- maxLength: 255,
1788
- label: 'Inner Field 2',
1789
- name: 'nestedField2',
1790
- required: true,
1791
- },
1792
- ]);
1793
- });
1794
-
1795
1740
  it('can pass custom field attributes', () => {
1796
1741
  const result = createHeadlessForm(
1797
1742
  {
@@ -2576,7 +2521,7 @@ describe('createHeadlessForm', () => {
2576
2521
  .addInput({
2577
2522
  username: {
2578
2523
  ...mockTextInputDeprecated,
2579
- presentation: {
2524
+ 'x-jsf-presentation': {
2580
2525
  inputType: 'text',
2581
2526
  maskSecret: 2,
2582
2527
  // should override the root level description
@@ -11,7 +11,7 @@ export const schemaInputTypeTextarea = {
11
11
  properties: {
12
12
  comment: {
13
13
  title: 'Your comment',
14
- presentation: {
14
+ 'x-jsf-presentation': {
15
15
  inputType: 'textarea',
16
16
  },
17
17
  maxLength: 250,
@@ -29,7 +29,7 @@ export const inputTypeCountriesSolo = {
29
29
  { title: 'Algeria', const: 'Algeria' },
30
30
  ],
31
31
  type: 'string',
32
- presentation: {
32
+ 'x-jsf-presentation': {
33
33
  inputType: 'countries',
34
34
  },
35
35
  };
@@ -58,7 +58,7 @@ export const schemaInputTypeCountriesMultiple = {
58
58
  ],
59
59
  },
60
60
  type: 'array',
61
- presentation: {
61
+ 'x-jsf-presentation': {
62
62
  inputType: 'countries',
63
63
  },
64
64
  },
@@ -90,7 +90,7 @@ export const schemaInputTypeTel = {
90
90
  type: 'string',
91
91
  pattern: '^(\\+|00)[0-9]{6,}$',
92
92
  maxLength: 30,
93
- presentation: {
93
+ 'x-jsf-presentation': {
94
94
  inputType: 'tel',
95
95
  },
96
96
  errorMessage: {
@@ -106,7 +106,7 @@ const mockTelInput = {
106
106
  title: 'Phone number',
107
107
  description: 'Enter your telephone number',
108
108
  maxLength: 30,
109
- presentation: {
109
+ 'x-jsf-presentation': {
110
110
  inputType: 'tel',
111
111
  },
112
112
  pattern: '^(\\+|00)\\d*$',
@@ -116,7 +116,7 @@ const mockTelInput = {
116
116
  export const mockMoneyInput = {
117
117
  title: 'Weekly salary',
118
118
  description: 'This field has a min and max values. Max has a custom error message.',
119
- presentation: {
119
+ 'x-jsf-presentation': {
120
120
  inputType: 'money',
121
121
  currency: 'EUR',
122
122
  },
@@ -150,7 +150,7 @@ export const schemaCustomComponent = {
150
150
  salary: {
151
151
  title: 'Monthly gross salary',
152
152
  description: 'This field gets represented by a custom UI Component.',
153
- presentation: {
153
+ 'x-jsf-presentation': {
154
154
  inputType: 'money',
155
155
  currency: 'EUR',
156
156
  },
@@ -17,7 +17,7 @@ export const mockTextInputDeprecated = {
17
17
  title: 'Username',
18
18
  description: 'Your username (max 10 characters)',
19
19
  maxLength: 10,
20
- presentation: {
20
+ 'x-jsf-presentation': {
21
21
  inputType: 'text',
22
22
  maskSecret: 2,
23
23
  },
@@ -1284,7 +1284,7 @@ export const schemaForErrorMessageSpecificity = {
1284
1284
  title: 'Weekday',
1285
1285
  description: "This text field has the traditional error message. 'Required field'",
1286
1286
  type: 'string',
1287
- presentation: { inputType: 'text' },
1287
+ 'x-jsf-presentation': { inputType: 'text' },
1288
1288
  },
1289
1289
  day: {
1290
1290
  title: 'Day',
@@ -1293,21 +1293,21 @@ export const schemaForErrorMessageSpecificity = {
1293
1293
  'The remaining fields are numbers and were customized to say "This cannot be empty." instead of "Required field".',
1294
1294
 
1295
1295
  maximum: 31,
1296
- presentation: { inputType: 'number' },
1296
+ 'x-jsf-presentation': { inputType: 'number' },
1297
1297
  },
1298
1298
  month: {
1299
1299
  title: 'Month',
1300
1300
  type: 'number',
1301
1301
  minimum: 1,
1302
1302
  maximum: 12,
1303
- presentation: { inputType: 'number' },
1303
+ 'x-jsf-presentation': { inputType: 'number' },
1304
1304
  },
1305
1305
  year: {
1306
1306
  title: 'Year',
1307
1307
  description:
1308
1308
  "This number field has a custom error message declared in the json schema, which has a higher specificity than the one declared in createHeadlessForm's configuration.",
1309
1309
  type: 'number',
1310
- presentation: { inputType: 'number' },
1310
+ 'x-jsf-presentation': { inputType: 'number' },
1311
1311
  'x-jsf-errorMessage': {
1312
1312
  required: 'The year is mandatory.',
1313
1313
  },
@@ -2327,59 +2327,3 @@ export const schemaWithCustomValidationsAndConditionals = {
2327
2327
  },
2328
2328
  ],
2329
2329
  };
2330
-
2331
- export const nestedGroupArrayForm = {
2332
- items: {
2333
- properties: {
2334
- notNested: {
2335
- description: 'Simple text field',
2336
- 'x-jsf-presentation': {
2337
- inputType: 'text',
2338
- },
2339
- title: 'Outer Field',
2340
- type: 'string',
2341
- maxLength: 255,
2342
- },
2343
- nested: {
2344
- items: {
2345
- properties: {
2346
- nestedField1: {
2347
- description: 'First nested text field',
2348
- 'x-jsf-presentation': {
2349
- inputType: 'text',
2350
- },
2351
- title: 'Inner Field 1',
2352
- type: 'string',
2353
- maxLength: 255,
2354
- },
2355
- nestedField2: {
2356
- description: 'Second nested text field',
2357
- 'x-jsf-presentation': {
2358
- inputType: 'text',
2359
- },
2360
- title: 'Inner Field 2',
2361
- type: 'string',
2362
- maxLength: 255,
2363
- },
2364
- },
2365
- 'x-jsf-order': ['nestedField1', 'nestedField2'],
2366
- required: ['nestedField1', 'nestedField2'],
2367
- type: 'object',
2368
- },
2369
- 'x-jsf-presentation': {
2370
- inputType: 'group-array',
2371
- },
2372
- title: 'Nested group-array',
2373
- type: 'array',
2374
- },
2375
- },
2376
- 'x-jsf-order': ['notNested', 'nested'],
2377
- required: ['notNested', 'nested'],
2378
- type: 'object',
2379
- },
2380
- 'x-jsf-presentation': {
2381
- inputType: 'group-array',
2382
- },
2383
- title: 'Parent object',
2384
- type: 'array',
2385
- };
@@ -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({