@remoteoss/json-schema-form 0.5.0-dev.20230719145458 → 0.5.0-dev.20230810172154
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 +12 -0
- package/dist/index.cjs +67 -62
- package/dist/index.js +67 -62
- package/dist/standalone.js +67 -62
- package/package.json +1 -1
- package/src/tests/checkIfConditionMatches.test.js +51 -0
- package/src/tests/conditions.test.js +58 -0
- package/src/tests/createHeadlessForm.test.js +10 -19
- package/src/tests/helpers.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
#### 0.4.3-beta.0 (2023-08-09)
|
|
2
|
+
|
|
3
|
+
##### Bug fixes
|
|
4
|
+
|
|
5
|
+
* **conditions:** Validate a deeply nested if (e.g. checking an object with a number property) in an if property now doesn't break the form. ([#33](https://github.com/remoteoss/json-schema-form/pull/33)) ([e34cfcc](https://github.com/remoteoss/json-schema-form/commit/e34cfccaf45f1460b346f3cff0c797b3d11259e3))
|
|
6
|
+
|
|
7
|
+
#### 0.4.2-beta.0 (2023-07-20)
|
|
8
|
+
|
|
9
|
+
##### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **date:** Validate based on minDate and maxDate ([#30](https://github.com/remoteoss/json-schema-form/pull/30)) ([01c0143e](https://github.com/remoteoss/json-schema-form/commit/01c0143ea4a3775f9489ae6cb8fd99a90b3f1394))
|
|
12
|
+
|
|
1
13
|
#### 0.4.1-beta.0 (2023-07-03)
|
|
2
14
|
|
|
3
15
|
##### Bug Fixes
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2023 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.5.0-dev.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.5.0-dev.20230810172154
|
|
5
|
+
Generated: Thu, 10 Aug 2023 17:22:35 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -84,6 +84,62 @@ var import_omitBy = __toESM(require("lodash/omitBy"));
|
|
|
84
84
|
var import_set = __toESM(require("lodash/set"));
|
|
85
85
|
var import_yup2 = require("yup");
|
|
86
86
|
|
|
87
|
+
// src/utils.js
|
|
88
|
+
function convertDiskSizeFromTo(from, to) {
|
|
89
|
+
const units = ["bytes", "kb", "mb"];
|
|
90
|
+
return function convert(value) {
|
|
91
|
+
return value * Math.pow(1024, units.indexOf(from.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function hasProperty(object2, propertyName) {
|
|
95
|
+
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/checkIfConditionMatches.js
|
|
99
|
+
function checkIfConditionMatches(node, formValues, formFields) {
|
|
100
|
+
return Object.keys(node.if.properties).every((name) => {
|
|
101
|
+
const currentProperty = node.if.properties[name];
|
|
102
|
+
const value = formValues[name];
|
|
103
|
+
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
104
|
+
value === null;
|
|
105
|
+
const hasIfExplicit = node.if.required?.includes(name);
|
|
106
|
+
if (hasEmptyValue && !hasIfExplicit) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
if (hasProperty(currentProperty, "const")) {
|
|
110
|
+
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
111
|
+
}
|
|
112
|
+
if (currentProperty.contains?.pattern) {
|
|
113
|
+
const formValue = value || [];
|
|
114
|
+
if (Array.isArray(formValue)) {
|
|
115
|
+
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
116
|
+
return (value || []).some((item) => pattern.test(item));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (currentProperty.enum) {
|
|
120
|
+
return currentProperty.enum.includes(value);
|
|
121
|
+
}
|
|
122
|
+
if (currentProperty.properties) {
|
|
123
|
+
return checkIfConditionMatches(
|
|
124
|
+
{ if: currentProperty },
|
|
125
|
+
formValues[name],
|
|
126
|
+
getField(name, formFields).fields
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const field = getField(name, formFields);
|
|
130
|
+
return validateFieldSchema(
|
|
131
|
+
{
|
|
132
|
+
options: field.options,
|
|
133
|
+
// @TODO/CODE SMELL. We are passing the property (raw field), but buildYupSchema() expected the output field.
|
|
134
|
+
...currentProperty,
|
|
135
|
+
inputType: field.inputType,
|
|
136
|
+
required: true
|
|
137
|
+
},
|
|
138
|
+
value
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
87
143
|
// src/internals/helpers.js
|
|
88
144
|
var import_merge = __toESM(require("lodash/fp/merge"));
|
|
89
145
|
var import_get = __toESM(require("lodash/get"));
|
|
@@ -343,24 +399,6 @@ function _composeFieldCustomClosure(defaultComposeFn) {
|
|
|
343
399
|
};
|
|
344
400
|
}
|
|
345
401
|
|
|
346
|
-
// src/utils.js
|
|
347
|
-
function convertDiskSizeFromTo(from, to) {
|
|
348
|
-
const units = ["bytes", "kb", "mb"];
|
|
349
|
-
return function convert(value) {
|
|
350
|
-
return value * Math.pow(1024, units.indexOf(from.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
function containsHTML(str = "") {
|
|
354
|
-
return /<[a-z][\s\S]*>/i.test(str);
|
|
355
|
-
}
|
|
356
|
-
function wrapWithSpan(html, properties = {}) {
|
|
357
|
-
const attributes = Object.entries(properties).reduce((acc, [key, value]) => `${acc}${key}="${value}" `, "").trim();
|
|
358
|
-
return `<span ${attributes}>${html}</span>`;
|
|
359
|
-
}
|
|
360
|
-
function hasProperty(object2, propertyName) {
|
|
361
|
-
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
402
|
// src/yupSchema.js
|
|
365
403
|
var import_flow = __toESM(require("lodash/flow"));
|
|
366
404
|
var import_noop = __toESM(require("lodash/noop"));
|
|
@@ -414,7 +452,12 @@ var yupSchemas = {
|
|
|
414
452
|
return `The option ${JSON.stringify(value)} is not valid.`;
|
|
415
453
|
}),
|
|
416
454
|
date: ({ minDate, maxDate }) => {
|
|
417
|
-
let dateString = (0, import_yup.string)().nullable().
|
|
455
|
+
let dateString = (0, import_yup.string)().nullable().transform((value) => {
|
|
456
|
+
if (value === "") {
|
|
457
|
+
return void 0;
|
|
458
|
+
}
|
|
459
|
+
return value === null ? void 0 : value;
|
|
460
|
+
}).trim().matches(
|
|
418
461
|
/(?:\d){4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9]|3[0-1])/,
|
|
419
462
|
`Must be a valid date in ${DEFAULT_DATE_FORMAT.toLocaleLowerCase()} format. e.g. ${todayDateHint}`
|
|
420
463
|
);
|
|
@@ -670,42 +713,6 @@ function compareFormValueWithSchemaValue(formValue, schemaValue) {
|
|
|
670
713
|
const currentPropertyValue = typeof schemaValue === "number" ? schemaValue : schemaValue || void 0;
|
|
671
714
|
return String(formValue) === String(currentPropertyValue);
|
|
672
715
|
}
|
|
673
|
-
function checkIfConditionMatches(node, formValues, formFields) {
|
|
674
|
-
return Object.keys(node.if.properties).every((name) => {
|
|
675
|
-
const currentProperty = node.if.properties[name];
|
|
676
|
-
const value = formValues[name];
|
|
677
|
-
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
678
|
-
value === null;
|
|
679
|
-
const hasIfExplicit = node.if.required?.includes(name);
|
|
680
|
-
if (hasEmptyValue && !hasIfExplicit) {
|
|
681
|
-
return true;
|
|
682
|
-
}
|
|
683
|
-
if (hasProperty(currentProperty, "const")) {
|
|
684
|
-
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
685
|
-
}
|
|
686
|
-
if (currentProperty.contains?.pattern) {
|
|
687
|
-
const formValue = value || [];
|
|
688
|
-
if (Array.isArray(formValue)) {
|
|
689
|
-
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
690
|
-
return (value || []).some((item) => pattern.test(item));
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
if (currentProperty.enum) {
|
|
694
|
-
return currentProperty.enum.includes(value);
|
|
695
|
-
}
|
|
696
|
-
const field = getField(name, formFields);
|
|
697
|
-
return validateFieldSchema(
|
|
698
|
-
{
|
|
699
|
-
options: field.options,
|
|
700
|
-
// @TODO/CODE SMELL. We are passing the property (raw field), but buildYupSchema() expected the output field.
|
|
701
|
-
...currentProperty,
|
|
702
|
-
inputType: field.inputType,
|
|
703
|
-
required: true
|
|
704
|
-
},
|
|
705
|
-
value
|
|
706
|
-
);
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
716
|
function isFieldFilled(fieldValue) {
|
|
710
717
|
return Array.isArray(fieldValue) ? fieldValue.length > 0 : !!fieldValue;
|
|
711
718
|
}
|
|
@@ -917,7 +924,7 @@ function extractParametersFromNode(schemaNode) {
|
|
|
917
924
|
const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
|
|
918
925
|
const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
|
|
919
926
|
const description = presentation?.description || node.description;
|
|
920
|
-
const statementDescription =
|
|
927
|
+
const statementDescription = presentation.statement?.description;
|
|
921
928
|
return (0, import_omitBy.default)(
|
|
922
929
|
{
|
|
923
930
|
label: node.title,
|
|
@@ -954,10 +961,8 @@ function extractParametersFromNode(schemaNode) {
|
|
|
954
961
|
},
|
|
955
962
|
// Handle [name].presentation
|
|
956
963
|
...presentation,
|
|
957
|
-
description
|
|
958
|
-
|
|
959
|
-
}) : description,
|
|
960
|
-
extra: containsHTML(presentation.extra) ? wrapWithSpan(presentation.extra, { class: "jsf-extra" }) : presentation.extra,
|
|
964
|
+
description,
|
|
965
|
+
extra: presentation.extra,
|
|
961
966
|
statement: presentation.statement && {
|
|
962
967
|
...presentation.statement,
|
|
963
968
|
description: statementDescription
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2023 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.5.0-dev.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.5.0-dev.20230810172154
|
|
5
|
+
Generated: Thu, 10 Aug 2023 17:22:35 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -48,6 +48,62 @@ import omitBy from "lodash/omitBy";
|
|
|
48
48
|
import set from "lodash/set";
|
|
49
49
|
import { lazy } from "yup";
|
|
50
50
|
|
|
51
|
+
// src/utils.js
|
|
52
|
+
function convertDiskSizeFromTo(from, to) {
|
|
53
|
+
const units = ["bytes", "kb", "mb"];
|
|
54
|
+
return function convert(value) {
|
|
55
|
+
return value * Math.pow(1024, units.indexOf(from.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function hasProperty(object2, propertyName) {
|
|
59
|
+
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// src/checkIfConditionMatches.js
|
|
63
|
+
function checkIfConditionMatches(node, formValues, formFields) {
|
|
64
|
+
return Object.keys(node.if.properties).every((name) => {
|
|
65
|
+
const currentProperty = node.if.properties[name];
|
|
66
|
+
const value = formValues[name];
|
|
67
|
+
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
68
|
+
value === null;
|
|
69
|
+
const hasIfExplicit = node.if.required?.includes(name);
|
|
70
|
+
if (hasEmptyValue && !hasIfExplicit) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (hasProperty(currentProperty, "const")) {
|
|
74
|
+
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
75
|
+
}
|
|
76
|
+
if (currentProperty.contains?.pattern) {
|
|
77
|
+
const formValue = value || [];
|
|
78
|
+
if (Array.isArray(formValue)) {
|
|
79
|
+
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
80
|
+
return (value || []).some((item) => pattern.test(item));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (currentProperty.enum) {
|
|
84
|
+
return currentProperty.enum.includes(value);
|
|
85
|
+
}
|
|
86
|
+
if (currentProperty.properties) {
|
|
87
|
+
return checkIfConditionMatches(
|
|
88
|
+
{ if: currentProperty },
|
|
89
|
+
formValues[name],
|
|
90
|
+
getField(name, formFields).fields
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const field = getField(name, formFields);
|
|
94
|
+
return validateFieldSchema(
|
|
95
|
+
{
|
|
96
|
+
options: field.options,
|
|
97
|
+
// @TODO/CODE SMELL. We are passing the property (raw field), but buildYupSchema() expected the output field.
|
|
98
|
+
...currentProperty,
|
|
99
|
+
inputType: field.inputType,
|
|
100
|
+
required: true
|
|
101
|
+
},
|
|
102
|
+
value
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
51
107
|
// src/internals/helpers.js
|
|
52
108
|
import merge from "lodash/fp/merge";
|
|
53
109
|
import get from "lodash/get";
|
|
@@ -307,24 +363,6 @@ function _composeFieldCustomClosure(defaultComposeFn) {
|
|
|
307
363
|
};
|
|
308
364
|
}
|
|
309
365
|
|
|
310
|
-
// src/utils.js
|
|
311
|
-
function convertDiskSizeFromTo(from, to) {
|
|
312
|
-
const units = ["bytes", "kb", "mb"];
|
|
313
|
-
return function convert(value) {
|
|
314
|
-
return value * Math.pow(1024, units.indexOf(from.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
function containsHTML(str = "") {
|
|
318
|
-
return /<[a-z][\s\S]*>/i.test(str);
|
|
319
|
-
}
|
|
320
|
-
function wrapWithSpan(html, properties = {}) {
|
|
321
|
-
const attributes = Object.entries(properties).reduce((acc, [key, value]) => `${acc}${key}="${value}" `, "").trim();
|
|
322
|
-
return `<span ${attributes}>${html}</span>`;
|
|
323
|
-
}
|
|
324
|
-
function hasProperty(object2, propertyName) {
|
|
325
|
-
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
366
|
// src/yupSchema.js
|
|
329
367
|
import flow from "lodash/flow";
|
|
330
368
|
import noop from "lodash/noop";
|
|
@@ -378,7 +416,12 @@ var yupSchemas = {
|
|
|
378
416
|
return `The option ${JSON.stringify(value)} is not valid.`;
|
|
379
417
|
}),
|
|
380
418
|
date: ({ minDate, maxDate }) => {
|
|
381
|
-
let dateString = string().nullable().
|
|
419
|
+
let dateString = string().nullable().transform((value) => {
|
|
420
|
+
if (value === "") {
|
|
421
|
+
return void 0;
|
|
422
|
+
}
|
|
423
|
+
return value === null ? void 0 : value;
|
|
424
|
+
}).trim().matches(
|
|
382
425
|
/(?:\d){4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9]|3[0-1])/,
|
|
383
426
|
`Must be a valid date in ${DEFAULT_DATE_FORMAT.toLocaleLowerCase()} format. e.g. ${todayDateHint}`
|
|
384
427
|
);
|
|
@@ -634,42 +677,6 @@ function compareFormValueWithSchemaValue(formValue, schemaValue) {
|
|
|
634
677
|
const currentPropertyValue = typeof schemaValue === "number" ? schemaValue : schemaValue || void 0;
|
|
635
678
|
return String(formValue) === String(currentPropertyValue);
|
|
636
679
|
}
|
|
637
|
-
function checkIfConditionMatches(node, formValues, formFields) {
|
|
638
|
-
return Object.keys(node.if.properties).every((name) => {
|
|
639
|
-
const currentProperty = node.if.properties[name];
|
|
640
|
-
const value = formValues[name];
|
|
641
|
-
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
642
|
-
value === null;
|
|
643
|
-
const hasIfExplicit = node.if.required?.includes(name);
|
|
644
|
-
if (hasEmptyValue && !hasIfExplicit) {
|
|
645
|
-
return true;
|
|
646
|
-
}
|
|
647
|
-
if (hasProperty(currentProperty, "const")) {
|
|
648
|
-
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
649
|
-
}
|
|
650
|
-
if (currentProperty.contains?.pattern) {
|
|
651
|
-
const formValue = value || [];
|
|
652
|
-
if (Array.isArray(formValue)) {
|
|
653
|
-
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
654
|
-
return (value || []).some((item) => pattern.test(item));
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
if (currentProperty.enum) {
|
|
658
|
-
return currentProperty.enum.includes(value);
|
|
659
|
-
}
|
|
660
|
-
const field = getField(name, formFields);
|
|
661
|
-
return validateFieldSchema(
|
|
662
|
-
{
|
|
663
|
-
options: field.options,
|
|
664
|
-
// @TODO/CODE SMELL. We are passing the property (raw field), but buildYupSchema() expected the output field.
|
|
665
|
-
...currentProperty,
|
|
666
|
-
inputType: field.inputType,
|
|
667
|
-
required: true
|
|
668
|
-
},
|
|
669
|
-
value
|
|
670
|
-
);
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
680
|
function isFieldFilled(fieldValue) {
|
|
674
681
|
return Array.isArray(fieldValue) ? fieldValue.length > 0 : !!fieldValue;
|
|
675
682
|
}
|
|
@@ -881,7 +888,7 @@ function extractParametersFromNode(schemaNode) {
|
|
|
881
888
|
const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
|
|
882
889
|
const node = omit(schemaNode, ["x-jsf-presentation", "presentation"]);
|
|
883
890
|
const description = presentation?.description || node.description;
|
|
884
|
-
const statementDescription =
|
|
891
|
+
const statementDescription = presentation.statement?.description;
|
|
885
892
|
return omitBy(
|
|
886
893
|
{
|
|
887
894
|
label: node.title,
|
|
@@ -918,10 +925,8 @@ function extractParametersFromNode(schemaNode) {
|
|
|
918
925
|
},
|
|
919
926
|
// Handle [name].presentation
|
|
920
927
|
...presentation,
|
|
921
|
-
description
|
|
922
|
-
|
|
923
|
-
}) : description,
|
|
924
|
-
extra: containsHTML(presentation.extra) ? wrapWithSpan(presentation.extra, { class: "jsf-extra" }) : presentation.extra,
|
|
928
|
+
description,
|
|
929
|
+
extra: presentation.extra,
|
|
925
930
|
statement: presentation.statement && {
|
|
926
931
|
...presentation.statement,
|
|
927
932
|
description: statementDescription
|
package/dist/standalone.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2023 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.5.0-dev.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.5.0-dev.20230810172154
|
|
5
|
+
Generated: Thu, 10 Aug 2023 17:22:35 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -11025,6 +11025,62 @@ var lazy = function lazy2(fn) {
|
|
|
11025
11025
|
return new Lazy_default(fn);
|
|
11026
11026
|
};
|
|
11027
11027
|
|
|
11028
|
+
// src/utils.js
|
|
11029
|
+
function convertDiskSizeFromTo(from2, to) {
|
|
11030
|
+
const units = ["bytes", "kb", "mb"];
|
|
11031
|
+
return function convert(value) {
|
|
11032
|
+
return value * Math.pow(1024, units.indexOf(from2.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
|
|
11033
|
+
};
|
|
11034
|
+
}
|
|
11035
|
+
function hasProperty(object2, propertyName) {
|
|
11036
|
+
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
11037
|
+
}
|
|
11038
|
+
|
|
11039
|
+
// src/checkIfConditionMatches.js
|
|
11040
|
+
function checkIfConditionMatches(node, formValues, formFields) {
|
|
11041
|
+
return Object.keys(node.if.properties).every((name) => {
|
|
11042
|
+
const currentProperty = node.if.properties[name];
|
|
11043
|
+
const value = formValues[name];
|
|
11044
|
+
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
11045
|
+
value === null;
|
|
11046
|
+
const hasIfExplicit = node.if.required?.includes(name);
|
|
11047
|
+
if (hasEmptyValue && !hasIfExplicit) {
|
|
11048
|
+
return true;
|
|
11049
|
+
}
|
|
11050
|
+
if (hasProperty(currentProperty, "const")) {
|
|
11051
|
+
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
11052
|
+
}
|
|
11053
|
+
if (currentProperty.contains?.pattern) {
|
|
11054
|
+
const formValue = value || [];
|
|
11055
|
+
if (Array.isArray(formValue)) {
|
|
11056
|
+
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
11057
|
+
return (value || []).some((item) => pattern.test(item));
|
|
11058
|
+
}
|
|
11059
|
+
}
|
|
11060
|
+
if (currentProperty.enum) {
|
|
11061
|
+
return currentProperty.enum.includes(value);
|
|
11062
|
+
}
|
|
11063
|
+
if (currentProperty.properties) {
|
|
11064
|
+
return checkIfConditionMatches(
|
|
11065
|
+
{ if: currentProperty },
|
|
11066
|
+
formValues[name],
|
|
11067
|
+
getField(name, formFields).fields
|
|
11068
|
+
);
|
|
11069
|
+
}
|
|
11070
|
+
const field = getField(name, formFields);
|
|
11071
|
+
return validateFieldSchema(
|
|
11072
|
+
{
|
|
11073
|
+
options: field.options,
|
|
11074
|
+
// @TODO/CODE SMELL. We are passing the property (raw field), but buildYupSchema() expected the output field.
|
|
11075
|
+
...currentProperty,
|
|
11076
|
+
inputType: field.inputType,
|
|
11077
|
+
required: true
|
|
11078
|
+
},
|
|
11079
|
+
value
|
|
11080
|
+
);
|
|
11081
|
+
});
|
|
11082
|
+
}
|
|
11083
|
+
|
|
11028
11084
|
// src/internals/helpers.js
|
|
11029
11085
|
var import_merge = __toESM(require_merge2());
|
|
11030
11086
|
var import_get2 = __toESM(require_get());
|
|
@@ -11284,24 +11340,6 @@ function _composeFieldCustomClosure(defaultComposeFn) {
|
|
|
11284
11340
|
};
|
|
11285
11341
|
}
|
|
11286
11342
|
|
|
11287
|
-
// src/utils.js
|
|
11288
|
-
function convertDiskSizeFromTo(from2, to) {
|
|
11289
|
-
const units = ["bytes", "kb", "mb"];
|
|
11290
|
-
return function convert(value) {
|
|
11291
|
-
return value * Math.pow(1024, units.indexOf(from2.toLowerCase())) / Math.pow(1024, units.indexOf(to.toLowerCase()));
|
|
11292
|
-
};
|
|
11293
|
-
}
|
|
11294
|
-
function containsHTML(str = "") {
|
|
11295
|
-
return /<[a-z][\s\S]*>/i.test(str);
|
|
11296
|
-
}
|
|
11297
|
-
function wrapWithSpan(html, properties = {}) {
|
|
11298
|
-
const attributes = Object.entries(properties).reduce((acc, [key, value]) => `${acc}${key}="${value}" `, "").trim();
|
|
11299
|
-
return `<span ${attributes}>${html}</span>`;
|
|
11300
|
-
}
|
|
11301
|
-
function hasProperty(object2, propertyName) {
|
|
11302
|
-
return Object.prototype.hasOwnProperty.call(object2, propertyName);
|
|
11303
|
-
}
|
|
11304
|
-
|
|
11305
11343
|
// src/yupSchema.js
|
|
11306
11344
|
var import_flow = __toESM(require_flow());
|
|
11307
11345
|
var import_noop = __toESM(require_noop());
|
|
@@ -11354,7 +11392,12 @@ var yupSchemas = {
|
|
|
11354
11392
|
return `The option ${JSON.stringify(value)} is not valid.`;
|
|
11355
11393
|
}),
|
|
11356
11394
|
date: ({ minDate, maxDate }) => {
|
|
11357
|
-
let dateString = StringSchema().nullable().
|
|
11395
|
+
let dateString = StringSchema().nullable().transform((value) => {
|
|
11396
|
+
if (value === "") {
|
|
11397
|
+
return void 0;
|
|
11398
|
+
}
|
|
11399
|
+
return value === null ? void 0 : value;
|
|
11400
|
+
}).trim().matches(
|
|
11358
11401
|
/(?:\d){4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9]|3[0-1])/,
|
|
11359
11402
|
`Must be a valid date in ${DEFAULT_DATE_FORMAT.toLocaleLowerCase()} format. e.g. ${todayDateHint}`
|
|
11360
11403
|
);
|
|
@@ -11610,42 +11653,6 @@ function compareFormValueWithSchemaValue(formValue, schemaValue) {
|
|
|
11610
11653
|
const currentPropertyValue = typeof schemaValue === "number" ? schemaValue : schemaValue || void 0;
|
|
11611
11654
|
return String(formValue) === String(currentPropertyValue);
|
|
11612
11655
|
}
|
|
11613
|
-
function checkIfConditionMatches(node, formValues, formFields) {
|
|
11614
|
-
return Object.keys(node.if.properties).every((name) => {
|
|
11615
|
-
const currentProperty = node.if.properties[name];
|
|
11616
|
-
const value = formValues[name];
|
|
11617
|
-
const hasEmptyValue = typeof value === "undefined" || // NOTE: This is a "Remote API" dependency, as empty fields are sent as "null".
|
|
11618
|
-
value === null;
|
|
11619
|
-
const hasIfExplicit = node.if.required?.includes(name);
|
|
11620
|
-
if (hasEmptyValue && !hasIfExplicit) {
|
|
11621
|
-
return true;
|
|
11622
|
-
}
|
|
11623
|
-
if (hasProperty(currentProperty, "const")) {
|
|
11624
|
-
return compareFormValueWithSchemaValue(value, currentProperty.const);
|
|
11625
|
-
}
|
|
11626
|
-
if (currentProperty.contains?.pattern) {
|
|
11627
|
-
const formValue = value || [];
|
|
11628
|
-
if (Array.isArray(formValue)) {
|
|
11629
|
-
const pattern = new RegExp(currentProperty.contains.pattern);
|
|
11630
|
-
return (value || []).some((item) => pattern.test(item));
|
|
11631
|
-
}
|
|
11632
|
-
}
|
|
11633
|
-
if (currentProperty.enum) {
|
|
11634
|
-
return currentProperty.enum.includes(value);
|
|
11635
|
-
}
|
|
11636
|
-
const field = getField(name, formFields);
|
|
11637
|
-
return validateFieldSchema(
|
|
11638
|
-
{
|
|
11639
|
-
options: field.options,
|
|
11640
|
-
// @TODO/CODE SMELL. We are passing the property (raw field), but buildYupSchema() expected the output field.
|
|
11641
|
-
...currentProperty,
|
|
11642
|
-
inputType: field.inputType,
|
|
11643
|
-
required: true
|
|
11644
|
-
},
|
|
11645
|
-
value
|
|
11646
|
-
);
|
|
11647
|
-
});
|
|
11648
|
-
}
|
|
11649
11656
|
function isFieldFilled(fieldValue) {
|
|
11650
11657
|
return Array.isArray(fieldValue) ? fieldValue.length > 0 : !!fieldValue;
|
|
11651
11658
|
}
|
|
@@ -11857,7 +11864,7 @@ function extractParametersFromNode(schemaNode) {
|
|
|
11857
11864
|
const errorMessage = pickXKey(schemaNode, "errorMessage") ?? {};
|
|
11858
11865
|
const node = (0, import_omit.default)(schemaNode, ["x-jsf-presentation", "presentation"]);
|
|
11859
11866
|
const description = presentation?.description || node.description;
|
|
11860
|
-
const statementDescription =
|
|
11867
|
+
const statementDescription = presentation.statement?.description;
|
|
11861
11868
|
return (0, import_omitBy.default)(
|
|
11862
11869
|
{
|
|
11863
11870
|
label: node.title,
|
|
@@ -11894,10 +11901,8 @@ function extractParametersFromNode(schemaNode) {
|
|
|
11894
11901
|
},
|
|
11895
11902
|
// Handle [name].presentation
|
|
11896
11903
|
...presentation,
|
|
11897
|
-
description
|
|
11898
|
-
|
|
11899
|
-
}) : description,
|
|
11900
|
-
extra: containsHTML(presentation.extra) ? wrapWithSpan(presentation.extra, { class: "jsf-extra" }) : presentation.extra,
|
|
11904
|
+
description,
|
|
11905
|
+
extra: presentation.extra,
|
|
11901
11906
|
statement: presentation.statement && {
|
|
11902
11907
|
...presentation.statement,
|
|
11903
11908
|
description: statementDescription
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remoteoss/json-schema-form",
|
|
3
|
-
"version": "0.5.0-dev.
|
|
3
|
+
"version": "0.5.0-dev.20230810172154",
|
|
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",
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { checkIfConditionMatches } from '../checkIfConditionMatches';
|
|
2
|
+
|
|
3
|
+
it('Empty if is always going to be true', () => {
|
|
4
|
+
expect(checkIfConditionMatches({ if: { properties: {} } })).toBe(true);
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
it('Basic if check passes with correct value', () => {
|
|
8
|
+
expect(
|
|
9
|
+
checkIfConditionMatches(
|
|
10
|
+
{ if: { properties: { a: { const: 'hello' } } } },
|
|
11
|
+
{
|
|
12
|
+
a: 'hello',
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('Basic if check fails with incorrect value', () => {
|
|
19
|
+
expect(
|
|
20
|
+
checkIfConditionMatches(
|
|
21
|
+
{ if: { properties: { a: { const: 'hello' } } } },
|
|
22
|
+
{
|
|
23
|
+
a: 'goodbye',
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('Nested properties check passes with correct value', () => {
|
|
30
|
+
expect(
|
|
31
|
+
checkIfConditionMatches(
|
|
32
|
+
{ if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } } },
|
|
33
|
+
{
|
|
34
|
+
parent: { child: 'hello from child' },
|
|
35
|
+
},
|
|
36
|
+
[{ name: 'parent', fields: [] }]
|
|
37
|
+
)
|
|
38
|
+
).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('Nested properties check passes with correct value', () => {
|
|
42
|
+
expect(
|
|
43
|
+
checkIfConditionMatches(
|
|
44
|
+
{ if: { properties: { parent: { properties: { child: { const: 'hello from child' } } } } } },
|
|
45
|
+
{
|
|
46
|
+
parent: { child: 'goodbye from child' },
|
|
47
|
+
},
|
|
48
|
+
[{ name: 'parent', fields: [] }]
|
|
49
|
+
)
|
|
50
|
+
).toBe(false);
|
|
51
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { createHeadlessForm } from '../createHeadlessForm';
|
|
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',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
required: ['child'],
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ['parent'],
|
|
21
|
+
},
|
|
22
|
+
then: { required: ['parent_sibling'] },
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
properties: {
|
|
26
|
+
parent: {
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
properties: {
|
|
29
|
+
child: {
|
|
30
|
+
oneOf: [
|
|
31
|
+
{
|
|
32
|
+
const: 'yes',
|
|
33
|
+
},
|
|
34
|
+
{ const: 'no' },
|
|
35
|
+
],
|
|
36
|
+
type: 'string',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
required: ['child'],
|
|
40
|
+
type: 'object',
|
|
41
|
+
},
|
|
42
|
+
parent_sibling: {
|
|
43
|
+
type: 'integer',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['parent'],
|
|
47
|
+
type: 'object',
|
|
48
|
+
},
|
|
49
|
+
{ strictInputType: false }
|
|
50
|
+
);
|
|
51
|
+
expect(handleValidation({ parent: { child: 'no' } }).formErrors).toEqual(undefined);
|
|
52
|
+
expect(handleValidation({ parent: { child: 'yes' } }).formErrors).toEqual({
|
|
53
|
+
parent_sibling: 'Required field',
|
|
54
|
+
});
|
|
55
|
+
expect(handleValidation({ parent: { child: 'yes' }, parent_sibling: 1 }).formErrors).toEqual(
|
|
56
|
+
undefined
|
|
57
|
+
);
|
|
58
|
+
});
|
|
@@ -1020,7 +1020,7 @@ describe('createHeadlessForm', () => {
|
|
|
1020
1020
|
schema: expect.any(Object),
|
|
1021
1021
|
type: 'date',
|
|
1022
1022
|
minDate: '1922-03-01',
|
|
1023
|
-
maxDate: '2022-03-
|
|
1023
|
+
maxDate: '2022-03-17',
|
|
1024
1024
|
});
|
|
1025
1025
|
|
|
1026
1026
|
const todayDateHint = new Date().toISOString().substring(0, 10);
|
|
@@ -1047,17 +1047,15 @@ describe('createHeadlessForm', () => {
|
|
|
1047
1047
|
schema: expect.any(Object),
|
|
1048
1048
|
type: 'date',
|
|
1049
1049
|
minDate: '1922-03-01',
|
|
1050
|
-
maxDate: '2022-03-
|
|
1050
|
+
maxDate: '2022-03-17',
|
|
1051
1051
|
});
|
|
1052
1052
|
|
|
1053
1053
|
expect(validateForm({})).toEqual({
|
|
1054
1054
|
birthdate: 'Required field',
|
|
1055
1055
|
});
|
|
1056
1056
|
|
|
1057
|
-
const todayDateHint = new Date().toISOString().substring(0, 10);
|
|
1058
|
-
|
|
1059
1057
|
expect(validateForm({ birthdate: '' })).toEqual({
|
|
1060
|
-
birthdate: `
|
|
1058
|
+
birthdate: `Required field`,
|
|
1061
1059
|
});
|
|
1062
1060
|
|
|
1063
1061
|
expect(validateForm({ birthdate: '1922-02-01' })).toEqual({
|
|
@@ -1081,19 +1079,17 @@ describe('createHeadlessForm', () => {
|
|
|
1081
1079
|
schema: expect.any(Object),
|
|
1082
1080
|
type: 'date',
|
|
1083
1081
|
minDate: '1922-03-01',
|
|
1084
|
-
maxDate: '2022-03-
|
|
1082
|
+
maxDate: '2022-03-17',
|
|
1085
1083
|
});
|
|
1086
1084
|
|
|
1087
|
-
const todayDateHint = new Date().toISOString().substring(0, 10);
|
|
1088
|
-
|
|
1089
1085
|
expect(validateForm({ birthdate: '' })).toEqual({
|
|
1090
|
-
birthdate: `
|
|
1086
|
+
birthdate: `Required field`,
|
|
1091
1087
|
});
|
|
1092
1088
|
|
|
1093
1089
|
expect(validateForm({ birthdate: '2022-02-01' })).toBeUndefined();
|
|
1094
1090
|
expect(validateForm({ birthdate: '2022-03-01' })).toBeUndefined();
|
|
1095
1091
|
expect(validateForm({ birthdate: '2022-04-01' })).toEqual({
|
|
1096
|
-
birthdate: 'The date must be 2022-03-
|
|
1092
|
+
birthdate: 'The date must be 2022-03-17 or before.',
|
|
1097
1093
|
});
|
|
1098
1094
|
});
|
|
1099
1095
|
|
|
@@ -2048,10 +2044,7 @@ describe('createHeadlessForm', () => {
|
|
|
2048
2044
|
});
|
|
2049
2045
|
|
|
2050
2046
|
expect(result).toMatchObject({
|
|
2051
|
-
fields: [
|
|
2052
|
-
{ description: 'I am regular' },
|
|
2053
|
-
{ description: '<span class="jsf-description">I am <b>bold</b>.</span>' },
|
|
2054
|
-
],
|
|
2047
|
+
fields: [{ description: 'I am regular' }, { description: 'I am <b>bold</b>.' }],
|
|
2055
2048
|
});
|
|
2056
2049
|
});
|
|
2057
2050
|
|
|
@@ -2510,7 +2503,7 @@ describe('createHeadlessForm', () => {
|
|
|
2510
2503
|
});
|
|
2511
2504
|
|
|
2512
2505
|
describe('when a field has conditional presentation properties', () => {
|
|
2513
|
-
it('adds
|
|
2506
|
+
it('adds the html to nested statement markup when visible', () => {
|
|
2514
2507
|
const { fields } = createHeadlessForm(schemaWithConditionalPresentationProperties, {
|
|
2515
2508
|
initialValues: {
|
|
2516
2509
|
// show the hidden statement
|
|
@@ -2518,9 +2511,7 @@ describe('createHeadlessForm', () => {
|
|
|
2518
2511
|
},
|
|
2519
2512
|
});
|
|
2520
2513
|
|
|
2521
|
-
expect(fields[0].statement.description).toBe(
|
|
2522
|
-
`<span class="jsf-statement"><a href="">conditional statement markup</a></span>`
|
|
2523
|
-
);
|
|
2514
|
+
expect(fields[0].statement.description).toBe(`<a href="">conditional statement markup</a>`);
|
|
2524
2515
|
});
|
|
2525
2516
|
});
|
|
2526
2517
|
|
|
@@ -3697,7 +3688,7 @@ describe('createHeadlessForm', () => {
|
|
|
3697
3688
|
expect(fields).toMatchObject([
|
|
3698
3689
|
{
|
|
3699
3690
|
name: 'time',
|
|
3700
|
-
description: '
|
|
3691
|
+
description: 'Write in <b>hh:ss</b> format', // from presentation
|
|
3701
3692
|
inputType: 'clock', // arbitrary type from presentation
|
|
3702
3693
|
deprecated: {
|
|
3703
3694
|
description: 'In favor of X', // from presentation
|
package/src/tests/helpers.js
CHANGED
|
@@ -991,7 +991,7 @@ export const schemaInputTypeDate = {
|
|
|
991
991
|
additionalProperties: false,
|
|
992
992
|
properties: {
|
|
993
993
|
birthdate: {
|
|
994
|
-
'x-jsf-presentation': { inputType: 'date', maxDate: '2022-03-
|
|
994
|
+
'x-jsf-presentation': { inputType: 'date', maxDate: '2022-03-17', minDate: '1922-03-01' },
|
|
995
995
|
title: 'Birthdate',
|
|
996
996
|
type: 'string',
|
|
997
997
|
format: 'date',
|