@remoteoss/json-schema-form 0.11.9-dev.20241210213029 → 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 +26 -0
- package/dist/index.cjs +14 -39
- package/dist/index.js +14 -39
- package/dist/standalone.js +14 -39
- package/package.json +2 -2
- package/src/tests/checkIfConditionMatches.test.js +59 -45
- package/src/tests/conditions.test.js +238 -122
- package/src/tests/createHeadlessForm.customValidations.test.js +2 -1
- package/src/tests/createHeadlessForm.test.js +1077 -1077
- package/src/tests/helpers.js +37 -0
- package/src/tests/utils.test.jsx +1 -1
|
@@ -1,58 +1,58 @@
|
|
|
1
1
|
import { createHeadlessForm } from '../createHeadlessForm';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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: ['
|
|
23
|
+
then: { required: ['parent_sibling'] },
|
|
21
24
|
},
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
{
|
|
@@ -480,116 +480,232 @@ describe('Conditional with a minimum value check', () => {
|
|
|
480
480
|
});
|
|
481
481
|
});
|
|
482
482
|
|
|
483
|
-
describe('
|
|
484
|
-
it('
|
|
483
|
+
describe('Conditional with literal booleans', () => {
|
|
484
|
+
it('handles true case', () => {
|
|
485
485
|
const schema = {
|
|
486
486
|
properties: {
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
},
|
|
490
|
-
allOf: [
|
|
491
|
-
{
|
|
492
|
-
if: { properties: { answer: { const: 'yes' } } },
|
|
493
|
-
then: { properties: { number: { minimum: 10 } } },
|
|
487
|
+
is_full_time: {
|
|
488
|
+
type: 'boolean',
|
|
494
489
|
},
|
|
495
|
-
{
|
|
496
|
-
|
|
497
|
-
then: { properties: { number: { maximum: 20 } } },
|
|
490
|
+
salary: {
|
|
491
|
+
type: 'number',
|
|
498
492
|
},
|
|
499
|
-
|
|
493
|
+
},
|
|
494
|
+
required: [],
|
|
495
|
+
if: true,
|
|
496
|
+
then: {
|
|
497
|
+
required: ['is_full_time'],
|
|
498
|
+
},
|
|
499
|
+
else: {
|
|
500
|
+
required: ['salary'],
|
|
501
|
+
},
|
|
500
502
|
};
|
|
503
|
+
const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
501
504
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
expect(
|
|
505
|
-
|
|
505
|
+
handleValidation({});
|
|
506
|
+
|
|
507
|
+
expect(fields[0]).toMatchObject({
|
|
508
|
+
name: 'is_full_time',
|
|
509
|
+
required: true,
|
|
506
510
|
});
|
|
507
|
-
expect(
|
|
508
|
-
|
|
511
|
+
expect(fields[1]).toMatchObject({
|
|
512
|
+
name: 'salary',
|
|
513
|
+
required: false,
|
|
509
514
|
});
|
|
510
|
-
expect(handleValidation({ answer: 'no', number: 9 }).formErrors).toBeUndefined();
|
|
511
|
-
expect(handleValidation({ answer: 'no', number: 15 }).formErrors).toBeUndefined();
|
|
512
|
-
expect(handleValidation({ answer: 'no', number: 21 }).formErrors).toBeUndefined();
|
|
513
515
|
});
|
|
514
516
|
|
|
515
|
-
it('
|
|
516
|
-
const
|
|
517
|
+
it('handles false case', () => {
|
|
518
|
+
const schema = {
|
|
517
519
|
properties: {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
+
is_full_time: {
|
|
521
|
+
type: 'boolean',
|
|
522
|
+
},
|
|
523
|
+
salary: {
|
|
520
524
|
type: 'number',
|
|
521
|
-
const: 4,
|
|
522
525
|
},
|
|
523
526
|
},
|
|
524
|
-
required: [
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
527
|
+
required: [],
|
|
528
|
+
if: false,
|
|
529
|
+
then: {
|
|
530
|
+
required: ['is_full_time'],
|
|
531
|
+
},
|
|
532
|
+
else: {
|
|
533
|
+
required: ['salary'],
|
|
534
|
+
},
|
|
535
|
+
};
|
|
536
|
+
const { fields, handleValidation } = createHeadlessForm(schema, { strictInputType: false });
|
|
537
|
+
|
|
538
|
+
handleValidation({});
|
|
539
|
+
|
|
540
|
+
expect(fields[0]).toMatchObject({
|
|
541
|
+
name: 'is_full_time',
|
|
542
|
+
required: false,
|
|
543
|
+
});
|
|
544
|
+
expect(fields[1]).toMatchObject({
|
|
545
|
+
name: 'salary',
|
|
546
|
+
required: true,
|
|
547
|
+
});
|
|
548
|
+
});
|
|
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' },
|
|
534
580
|
},
|
|
581
|
+
required: ['has_pet'],
|
|
535
582
|
},
|
|
536
|
-
|
|
537
|
-
|
|
583
|
+
then: {
|
|
584
|
+
required: ['pet_name'],
|
|
585
|
+
},
|
|
586
|
+
else: {
|
|
587
|
+
properties: {
|
|
588
|
+
pet_name: false,
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
};
|
|
538
594
|
|
|
539
|
-
|
|
540
|
-
const { handleValidation } = createHeadlessForm(
|
|
595
|
+
it('Given values from hidden fields, it does not thrown an error (@bug)', () => {
|
|
596
|
+
const { fields, handleValidation } = createHeadlessForm(schemaHasPet, {
|
|
541
597
|
strictInputType: false,
|
|
542
598
|
});
|
|
543
|
-
|
|
544
|
-
|
|
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,
|
|
545
615
|
});
|
|
546
|
-
|
|
547
|
-
|
|
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.
|
|
548
624
|
});
|
|
625
|
+
|
|
626
|
+
// Same bug as explained in the previous test.
|
|
627
|
+
expect(validation.formErrors).toBeUndefined();
|
|
549
628
|
});
|
|
550
629
|
|
|
551
|
-
it('
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
type: '
|
|
557
|
-
'
|
|
558
|
-
|
|
559
|
-
},
|
|
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' },
|
|
560
638
|
},
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
then: {
|
|
567
|
-
properties: {
|
|
568
|
-
books: {
|
|
569
|
-
minimum: 5,
|
|
570
|
-
},
|
|
639
|
+
allOf: [
|
|
640
|
+
{
|
|
641
|
+
if: {
|
|
642
|
+
properties: { field_a: { const: 'yes' } },
|
|
643
|
+
required: ['field_a'],
|
|
571
644
|
},
|
|
645
|
+
then: { properties: { field_c: { minimum: 30 } } },
|
|
572
646
|
},
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
book_space: {
|
|
578
|
-
rule: {
|
|
579
|
-
'*': [2, 2],
|
|
647
|
+
{
|
|
648
|
+
if: {
|
|
649
|
+
properties: { field_b: { const: 'yes' } },
|
|
650
|
+
required: ['field_b'],
|
|
580
651
|
},
|
|
652
|
+
then: { properties: { field_c: { minimum: 10 } } },
|
|
581
653
|
},
|
|
582
|
-
|
|
654
|
+
],
|
|
583
655
|
},
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
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',
|
|
590
665
|
});
|
|
591
|
-
|
|
592
|
-
|
|
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',
|
|
593
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
|
+
// });
|
|
594
710
|
});
|
|
595
711
|
});
|
|
@@ -140,7 +140,8 @@ afterAll(() => {
|
|
|
140
140
|
console.warn.mockRestore();
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
|
|
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({
|