@remoteoss/json-schema-form 0.11.9-dev.20241213082826 → 0.11.10-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,29 @@
1
+ #### 0.11.10-beta.0 (2025-01-22)
2
+
3
+ ##### Bug Fixes
4
+
5
+ * **checkbox:** Boolean - allow nullable values, and throw friendly error message ([#109](https://github.com/remoteoss/json-schema-form/pull/109)) ([63286eee](https://github.com/remoteoss/json-schema-form/commit/63286eee57591b97c9c5bbbcfbf9231cfabde864))
6
+
7
+ ##### Chores
8
+
9
+ * **conditionals:** Highlight bugs related to validations and conditional fields ([#107](https://github.com/remoteoss/json-schema-form/pull/107)) ([85dc3d6d](https://github.com/remoteoss/json-schema-form/commit/85dc3d6dbb0a0483a32479563ef9cd65cd36a210))
10
+ * Update eslint dependencies ([#99](https://github.com/remoteoss/json-schema-form/pull/99)) ([ff2e6ed5](https://github.com/remoteoss/json-schema-form/commit/ff2e6ed56ba96d911ccc36dc50485d8cb6ec764b))
11
+
12
+
13
+
14
+ ##### Build System / Dependencies
15
+
16
+ * **next:**
17
+ * Add build step ([#110](https://github.com/remoteoss/json-schema-form/pull/110)) ([6c00ce32](https://github.com/remoteoss/json-schema-form/commit/6c00ce32f6e5edba328d5cdede38a82c330f5f14))
18
+ * Set up lint ([#108](https://github.com/remoteoss/json-schema-form/pull/108)) ([038efcfc](https://github.com/remoteoss/json-schema-form/commit/038efcfcda6cb7b31852e7bc80c9524bffdb94f5))
19
+
20
+
21
+ #### 0.11.9-beta.0 (2024-12-17)
22
+
23
+ ##### Bug Fixes
24
+
25
+ * Add support for boolean values in the if condition ([#100](https://github.com/remoteoss/json-schema-form/pull/100)) ([b3dca67d](https://github.com/remoteoss/json-schema-form/commit/b3dca67da14a7e4f5339a14a1e2f14ac014d2b92))
26
+
1
27
  #### 0.11.8-beta.0 (2024-11-20)
2
28
 
3
29
  ##### Bug Fixes
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- Copyright (c) 2024 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.11.9-dev.20241213082826
5
- Generated: Fri, 13 Dec 2024 08:29:06 GMT
3
+ Copyright (c) 2025 Remote Technology, Inc.
4
+ NPM Package: @remoteoss/json-schema-form@0.11.10-beta.0
5
+ Generated: Wed, 22 Jan 2025 16:30:09 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -96,7 +96,7 @@ function hasProperty(object2, propertyName) {
96
96
  return Object.prototype.hasOwnProperty.call(object2, propertyName);
97
97
  }
98
98
 
99
- // src/checkIfConditionMatches.js
99
+ // src/internals/checkIfConditionMatches.js
100
100
  function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
101
101
  if (typeof node.if === "boolean") {
102
102
  return node.if;
@@ -557,7 +557,7 @@ var yupSchemas = {
557
557
  email: (0, import_yup.string)().trim().email("Please enter a valid email address").nullable(),
558
558
  fieldset: (0, import_yup.object)().nullable(),
559
559
  checkbox: (0, import_yup.string)().trim().nullable(),
560
- checkboxBool: (0, import_yup.boolean)(),
560
+ checkboxBool: (0, import_yup.boolean)().typeError('The value must be a boolean, but received "${value}"').nullable(),
561
561
  multiple: {
562
562
  select: (0, import_yup.array)().nullable(),
563
563
  "group-array": (0, import_yup.array)().nullable()
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- Copyright (c) 2024 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.11.9-dev.20241213082826
5
- Generated: Fri, 13 Dec 2024 08:29:06 GMT
3
+ Copyright (c) 2025 Remote Technology, Inc.
4
+ NPM Package: @remoteoss/json-schema-form@0.11.10-beta.0
5
+ Generated: Wed, 22 Jan 2025 16:30:09 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -59,7 +59,7 @@ function hasProperty(object2, propertyName) {
59
59
  return Object.prototype.hasOwnProperty.call(object2, propertyName);
60
60
  }
61
61
 
62
- // src/checkIfConditionMatches.js
62
+ // src/internals/checkIfConditionMatches.js
63
63
  function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
64
64
  if (typeof node.if === "boolean") {
65
65
  return node.if;
@@ -520,7 +520,7 @@ var yupSchemas = {
520
520
  email: string().trim().email("Please enter a valid email address").nullable(),
521
521
  fieldset: object().nullable(),
522
522
  checkbox: string().trim().nullable(),
523
- checkboxBool: boolean(),
523
+ checkboxBool: boolean().typeError('The value must be a boolean, but received "${value}"').nullable(),
524
524
  multiple: {
525
525
  select: array().nullable(),
526
526
  "group-array": array().nullable()
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- Copyright (c) 2024 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.11.9-dev.20241213082826
5
- Generated: Fri, 13 Dec 2024 08:29:06 GMT
3
+ Copyright (c) 2025 Remote Technology, Inc.
4
+ NPM Package: @remoteoss/json-schema-form@0.11.10-beta.0
5
+ Generated: Wed, 22 Jan 2025 16:30:09 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -11576,7 +11576,7 @@ function hasProperty(object2, propertyName) {
11576
11576
  return Object.prototype.hasOwnProperty.call(object2, propertyName);
11577
11577
  }
11578
11578
 
11579
- // src/checkIfConditionMatches.js
11579
+ // src/internals/checkIfConditionMatches.js
11580
11580
  function checkIfConditionMatchesProperties(node, formValues, formFields, logic) {
11581
11581
  if (typeof node.if === "boolean") {
11582
11582
  return node.if;
@@ -12036,7 +12036,7 @@ var yupSchemas = {
12036
12036
  email: StringSchema().trim().email("Please enter a valid email address").nullable(),
12037
12037
  fieldset: ObjectSchema().nullable(),
12038
12038
  checkbox: StringSchema().trim().nullable(),
12039
- checkboxBool: boolean2(),
12039
+ checkboxBool: boolean2().typeError('The value must be a boolean, but received "${value}"').nullable(),
12040
12040
  multiple: {
12041
12041
  select: array_default().nullable(),
12042
12042
  "group-array": array_default().nullable()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remoteoss/json-schema-form",
3
- "version": "0.11.9-dev.20241213082826",
3
+ "version": "0.11.10-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",
@@ -54,8 +54,8 @@
54
54
  },
55
55
  "devDependencies": {
56
56
  "@babel/core": "^7.21.5",
57
+ "@babel/eslint-parser": "^7.25.9",
57
58
  "@babel/preset-env": "^7.21.5",
58
- "babel-eslint": "^10.1.0",
59
59
  "babel-jest": "^29.5.0",
60
60
  "child-process-promise": "^2.2.1",
61
61
  "esbuild": "^0.17.15",
@@ -1,59 +1,65 @@
1
- import { checkIfConditionMatchesProperties } from '../checkIfConditionMatches';
1
+ import { checkIfConditionMatchesProperties } from '../internals/checkIfConditionMatches';
2
2
 
3
- it('True if is always going to be true', () => {
4
- expect(checkIfConditionMatchesProperties({ if: true })).toBe(true);
5
- });
3
+ describe('checkIfConditionMatchesProperties()', () => {
4
+ it('True if is always going to be true', () => {
5
+ expect(checkIfConditionMatchesProperties({ if: true })).toBe(true);
6
+ });
6
7
 
7
- it('False if is always going to be false', () => {
8
- expect(checkIfConditionMatchesProperties({ if: false })).toBe(false);
9
- });
8
+ it('False if is always going to be false', () => {
9
+ expect(checkIfConditionMatchesProperties({ if: false })).toBe(false);
10
+ });
10
11
 
11
- it('Empty if is always going to be true', () => {
12
- expect(checkIfConditionMatchesProperties({ if: { properties: {} } })).toBe(true);
13
- });
12
+ it('Empty if is always going to be true', () => {
13
+ expect(checkIfConditionMatchesProperties({ if: { properties: {} } })).toBe(true);
14
+ });
14
15
 
15
- it('Basic if check passes with correct value', () => {
16
- expect(
17
- checkIfConditionMatchesProperties(
18
- { if: { properties: { a: { const: 'hello' } } } },
19
- {
20
- a: 'hello',
21
- }
22
- )
23
- ).toBe(true);
24
- });
16
+ it('Basic if check passes with correct value', () => {
17
+ expect(
18
+ checkIfConditionMatchesProperties(
19
+ { if: { properties: { a: { const: 'hello' } } } },
20
+ {
21
+ a: 'hello',
22
+ }
23
+ )
24
+ ).toBe(true);
25
+ });
25
26
 
26
- it('Basic if check fails with incorrect value', () => {
27
- expect(
28
- checkIfConditionMatchesProperties(
29
- { if: { properties: { a: { const: 'hello' } } } },
30
- {
31
- a: 'goodbye',
32
- }
33
- )
34
- ).toBe(false);
35
- });
27
+ it('Basic if check fails with incorrect value', () => {
28
+ expect(
29
+ checkIfConditionMatchesProperties(
30
+ { if: { properties: { a: { const: 'hello' } } } },
31
+ {
32
+ a: 'goodbye',
33
+ }
34
+ )
35
+ ).toBe(false);
36
+ });
36
37
 
37
- it('Nested properties check passes with correct value', () => {
38
- expect(
39
- checkIfConditionMatchesProperties(
40
- { if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } } },
41
- {
42
- parent: { child: 'hello from child' },
43
- },
44
- [{ name: 'parent', fields: [] }]
45
- )
46
- ).toBe(true);
47
- });
38
+ it('Nested properties check passes with correct value', () => {
39
+ expect(
40
+ checkIfConditionMatchesProperties(
41
+ {
42
+ if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } },
43
+ },
44
+ {
45
+ parent: { child: 'hello from child' },
46
+ },
47
+ [{ name: 'parent', fields: [] }]
48
+ )
49
+ ).toBe(true);
50
+ });
48
51
 
49
- it('Nested properties check passes with correct value', () => {
50
- expect(
51
- checkIfConditionMatchesProperties(
52
- { if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } } },
53
- {
54
- parent: { child: 'goodbye from child' },
55
- },
56
- [{ name: 'parent', fields: [] }]
57
- )
58
- ).toBe(false);
52
+ it('Nested properties check passes with correct value', () => {
53
+ expect(
54
+ checkIfConditionMatchesProperties(
55
+ {
56
+ if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } },
57
+ },
58
+ {
59
+ parent: { child: 'goodbye from child' },
60
+ },
61
+ [{ name: 'parent', fields: [] }]
62
+ )
63
+ ).toBe(false);
64
+ });
59
65
  });
@@ -1,58 +1,58 @@
1
1
  import { createHeadlessForm } from '../createHeadlessForm';
2
2
 
3
- it('Should allow check of a nested property in a conditional', () => {
4
- const { handleValidation } = createHeadlessForm(
5
- {
6
- additionalProperties: false,
7
- allOf: [
8
- {
9
- if: {
10
- properties: {
11
- parent: {
12
- properties: {
13
- child: {
14
- const: 'yes',
3
+ describe('Conditional attributes updated', () => {
4
+ it('Should allow check of a nested property in a conditional', () => {
5
+ const { handleValidation } = createHeadlessForm(
6
+ {
7
+ additionalProperties: false,
8
+ allOf: [
9
+ {
10
+ if: {
11
+ properties: {
12
+ parent: {
13
+ properties: {
14
+ child: {
15
+ const: 'yes',
16
+ },
15
17
  },
18
+ required: ['child'],
16
19
  },
17
- required: ['child'],
18
20
  },
21
+ required: ['parent'],
19
22
  },
20
- required: ['parent'],
23
+ then: { required: ['parent_sibling'] },
21
24
  },
22
- then: { required: ['parent_sibling'] },
23
- },
24
- ],
25
- properties: {
26
- parent: {
27
- additionalProperties: false,
28
- properties: {
29
- child: {
30
- oneOf: [{ const: 'yes' }, { const: 'no' }],
31
- type: 'string',
25
+ ],
26
+ properties: {
27
+ parent: {
28
+ additionalProperties: false,
29
+ properties: {
30
+ child: {
31
+ oneOf: [{ const: 'yes' }, { const: 'no' }],
32
+ type: 'string',
33
+ },
32
34
  },
35
+ required: ['child'],
36
+ type: 'object',
37
+ },
38
+ parent_sibling: {
39
+ type: 'integer',
33
40
  },
34
- required: ['child'],
35
- type: 'object',
36
- },
37
- parent_sibling: {
38
- type: 'integer',
39
41
  },
42
+ required: ['parent'],
43
+ type: 'object',
40
44
  },
41
- required: ['parent'],
42
- type: 'object',
43
- },
44
- { strictInputType: false }
45
- );
46
- expect(handleValidation({ parent: { child: 'no' } }).formErrors).toEqual(undefined);
47
- expect(handleValidation({ parent: { child: 'yes' } }).formErrors).toEqual({
48
- parent_sibling: 'Required field',
45
+ { strictInputType: false }
46
+ );
47
+ expect(handleValidation({ parent: { child: 'no' } }).formErrors).toEqual(undefined);
48
+ expect(handleValidation({ parent: { child: 'yes' } }).formErrors).toEqual({
49
+ parent_sibling: 'Required field',
50
+ });
51
+ expect(handleValidation({ parent: { child: 'yes' }, parent_sibling: 1 }).formErrors).toEqual(
52
+ undefined
53
+ );
49
54
  });
50
- expect(handleValidation({ parent: { child: 'yes' }, parent_sibling: 1 }).formErrors).toEqual(
51
- undefined
52
- );
53
- });
54
55
 
55
- describe('Conditional attributes updated', () => {
56
56
  it('Update basic case with const, default, maximum', () => {
57
57
  const { fields, handleValidation } = createHeadlessForm(
58
58
  {
@@ -547,3 +547,165 @@ describe('Conditional with literal booleans', () => {
547
547
  });
548
548
  });
549
549
  });
550
+
551
+ describe('Conditionals - bugs and code-smells', () => {
552
+ // Why do we have these bugs?
553
+ // To be honest we never realized it much later later.
554
+ // We will fix them in the next major version.
555
+
556
+ const schemaHasPet = {
557
+ type: 'object',
558
+ additionalProperties: false,
559
+ properties: {
560
+ has_pet: {
561
+ title: 'Has Pet',
562
+ description: 'Do you have a pet?',
563
+ oneOf: [
564
+ { title: 'Yes', const: 'yes' },
565
+ { title: 'No', const: 'no' },
566
+ ],
567
+ type: 'string',
568
+ },
569
+ pet_name: {
570
+ title: "Pet's name",
571
+ type: 'string',
572
+ },
573
+ },
574
+ required: ['has_pet'],
575
+ allOf: [
576
+ {
577
+ if: {
578
+ properties: {
579
+ has_pet: { const: 'yes' },
580
+ },
581
+ required: ['has_pet'],
582
+ },
583
+ then: {
584
+ required: ['pet_name'],
585
+ },
586
+ else: {
587
+ properties: {
588
+ pet_name: false,
589
+ },
590
+ },
591
+ },
592
+ ],
593
+ };
594
+
595
+ it('Given values from hidden fields, it does not thrown an error (@bug)', () => {
596
+ const { fields, handleValidation } = createHeadlessForm(schemaHasPet, {
597
+ strictInputType: false,
598
+ });
599
+
600
+ const petNameField = fields[1];
601
+
602
+ const validation = handleValidation({ has_pet: 'no', pet_name: 'Max' });
603
+ expect(petNameField.isVisible).toBe(false);
604
+
605
+ // Bug: 🐛 It does not thrown an error,
606
+ // but it should to be compliant with JSON Schema specs.
607
+ expect(validation.formErrors).toBeUndefined();
608
+ // The error should be something like:
609
+ // expect(validation.formErrors).toEqual({ pet_name: 'Not allowed.'});
610
+ });
611
+
612
+ it('Given values from hidden fields, it mutates the values (@bug)', () => {
613
+ const { handleValidation } = createHeadlessForm(schemaHasPet, {
614
+ strictInputType: false,
615
+ });
616
+
617
+ const newValues = { has_pet: 'no', pet_name: 'Max' };
618
+
619
+ const validation = handleValidation(newValues);
620
+
621
+ expect(newValues).toEqual({
622
+ has_pet: 'no',
623
+ pet_name: null, // BUG! 🐛 Should still be "Max", should not be mutated.
624
+ });
625
+
626
+ // Same bug as explained in the previous test.
627
+ expect(validation.formErrors).toBeUndefined();
628
+ });
629
+
630
+ it('Given multiple conditionals to the same field, it only applies the last one (@bug) - case 1', () => {
631
+ const { handleValidation } = createHeadlessForm(
632
+ {
633
+ additionalProperties: false,
634
+ properties: {
635
+ field_a: { type: 'string' },
636
+ field_b: { type: 'string' },
637
+ field_c: { type: 'number' },
638
+ },
639
+ allOf: [
640
+ {
641
+ if: {
642
+ properties: { field_a: { const: 'yes' } },
643
+ required: ['field_a'],
644
+ },
645
+ then: { properties: { field_c: { minimum: 30 } } },
646
+ },
647
+ {
648
+ if: {
649
+ properties: { field_b: { const: 'yes' } },
650
+ required: ['field_b'],
651
+ },
652
+ then: { properties: { field_c: { minimum: 10 } } },
653
+ },
654
+ ],
655
+ },
656
+ {
657
+ strictInputType: false,
658
+ }
659
+ );
660
+
661
+ const validation = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 5 });
662
+ expect(validation.formErrors).toEqual({
663
+ // BUG: 🐛 it should be "Must be greater or equal to 30"
664
+ field_c: 'Must be greater or equal to 10',
665
+ });
666
+ });
667
+
668
+ it('Given multiple conditionals to the same field, it only applies the last one (@bug) - case 2', () => {
669
+ const { handleValidation } = createHeadlessForm(
670
+ {
671
+ additionalProperties: false,
672
+ properties: {
673
+ field_a: { type: 'string' },
674
+ field_b: { type: 'string' },
675
+ field_c: { type: 'number' },
676
+ },
677
+ allOf: [
678
+ {
679
+ if: {
680
+ properties: { field_a: { const: 'yes' } },
681
+ required: ['field_a'],
682
+ },
683
+ then: { properties: { field_c: { minimum: 5 } } },
684
+ },
685
+ {
686
+ if: {
687
+ properties: { field_b: { const: 'yes' } },
688
+ required: ['field_b'],
689
+ },
690
+ then: { properties: { field_c: { maximum: 10 } } },
691
+ },
692
+ ],
693
+ },
694
+ {
695
+ strictInputType: false,
696
+ }
697
+ );
698
+
699
+ const validation1 = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 12 });
700
+ expect(validation1.formErrors).toEqual({
701
+ field_c: 'Must be smaller or equal to 10',
702
+ });
703
+
704
+ const validation2 = handleValidation({ field_a: 'yes', field_b: 'yes', field_c: 3 });
705
+ // BUG: 🐛 it should be "Must be greater or equal to 5"
706
+ expect(validation2.formErrors).toBeUndefined();
707
+ // expect(validation1.formErrors).toEqual({
708
+ // field_c: 'Must be greater or equal to 5',
709
+ // });
710
+ });
711
+ });
@@ -140,7 +140,8 @@ afterAll(() => {
140
140
  console.warn.mockRestore();
141
141
  });
142
142
 
143
- describe('createHeadlessForm() - custom validations', () => {
143
+ // @deprecated - customProperties won't be supported in v2.
144
+ describe('createHeadlessForm() - custom validations (deprecated)', () => {
144
145
  describe('simple validation (eg maximum)', () => {
145
146
  it('works as a number', () => {
146
147
  const { fields, validateForm } = createScenario({