@remoteoss/json-schema-form 0.12.0-beta.0 → 0.12.1-dev.20250929100600
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/dist/index.cjs +52 -23
- package/dist/index.js +51 -23
- package/dist/standalone.js +51 -23
- package/package.json +3 -6
- package/src/tests/createHeadlessForm.test.js +107 -0
- package/src/tests/helpers.js +172 -1
- package/src/tests/jsonLogic.fixtures.js +50 -2
- package/src/tests/jsonLogic.test.js +14 -2
- package/LICENSE +0 -21
- package/README.md +0 -44
- package/json-schema-form.schema.json +0 -188
- /package/src/tests/{utils.test.jsx → utils.test.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2025 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.12.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.12.1-dev.20250929100600
|
|
5
|
+
Generated: Mon, 29 Sep 2025 10:07:37 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -27,6 +27,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
27
27
|
SOFTWARE.
|
|
28
28
|
|
|
29
29
|
*/
|
|
30
|
+
"use strict";
|
|
30
31
|
var __create = Object.create;
|
|
31
32
|
var __defProp = Object.defineProperty;
|
|
32
33
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -736,9 +737,7 @@ function buildYupSchema(field, config, logic) {
|
|
|
736
737
|
const fieldSetShape = {};
|
|
737
738
|
innerFields.forEach((fieldSetfield) => {
|
|
738
739
|
if (fieldSetfield.fields) {
|
|
739
|
-
fieldSetShape[fieldSetfield.name] = (0, import_yup.object)().shape(
|
|
740
|
-
buildFieldSetSchema(fieldSetfield.fields)
|
|
741
|
-
);
|
|
740
|
+
fieldSetShape[fieldSetfield.name] = (0, import_yup.object)().shape(buildFieldSetSchema(fieldSetfield.fields)).nullable();
|
|
742
741
|
} else {
|
|
743
742
|
fieldSetShape[fieldSetfield.name] = buildYupSchema(
|
|
744
743
|
{
|
|
@@ -1065,20 +1064,26 @@ function validateInlineRules(jsonSchema, sampleEmptyObject) {
|
|
|
1065
1064
|
});
|
|
1066
1065
|
});
|
|
1067
1066
|
}
|
|
1068
|
-
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}"
|
|
1067
|
+
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`, inReduceOrMap = false) {
|
|
1069
1068
|
Object.entries(rule ?? {}).map(([operator, subRule]) => {
|
|
1070
1069
|
if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
|
|
1071
1070
|
return;
|
|
1072
1071
|
throwIfUnknownOperator(operator, subRule, id);
|
|
1072
|
+
const isReduceOrMap = inReduceOrMap || operator === "reduce" || operator === "map";
|
|
1073
|
+
const validationData = isReduceOrMap ? {
|
|
1074
|
+
...data,
|
|
1075
|
+
accumulator: 0,
|
|
1076
|
+
current: null
|
|
1077
|
+
} : data;
|
|
1073
1078
|
subRule.map((item) => {
|
|
1074
1079
|
const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
|
|
1075
1080
|
if (isVar) {
|
|
1076
|
-
const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) },
|
|
1081
|
+
const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) }, validationData);
|
|
1077
1082
|
if (exists === null) {
|
|
1078
1083
|
throw Error(errorMessage(item));
|
|
1079
1084
|
}
|
|
1080
1085
|
} else {
|
|
1081
|
-
checkRuleIntegrity(item, id, data);
|
|
1086
|
+
checkRuleIntegrity(item, id, data, errorMessage, isReduceOrMap);
|
|
1082
1087
|
}
|
|
1083
1088
|
});
|
|
1084
1089
|
});
|
|
@@ -1259,6 +1264,30 @@ function getPrefillValues(fields, initialValues = {}) {
|
|
|
1259
1264
|
});
|
|
1260
1265
|
return initialValues;
|
|
1261
1266
|
}
|
|
1267
|
+
function preserveNestedFieldsVisibility(field, parentPath = "") {
|
|
1268
|
+
return field.fields?.reduce?.((acc, f) => {
|
|
1269
|
+
const path = parentPath ? `${parentPath}.${f.name}` : f.name;
|
|
1270
|
+
if (!(0, import_isNil.default)(f.isVisible)) {
|
|
1271
|
+
acc[path] = f.isVisible;
|
|
1272
|
+
}
|
|
1273
|
+
if (f.fields) {
|
|
1274
|
+
Object.assign(acc, preserveNestedFieldsVisibility(f, path));
|
|
1275
|
+
}
|
|
1276
|
+
return acc;
|
|
1277
|
+
}, {});
|
|
1278
|
+
}
|
|
1279
|
+
function restoreNestedFieldsVisibility(field, nestedFieldsVisibility, parentPath = "") {
|
|
1280
|
+
field.fields.forEach((f) => {
|
|
1281
|
+
const path = parentPath ? `${parentPath}.${f.name}` : f.name;
|
|
1282
|
+
const visibility = (0, import_get2.default)(nestedFieldsVisibility, path);
|
|
1283
|
+
if (!(0, import_isNil.default)(visibility)) {
|
|
1284
|
+
f.isVisible = visibility;
|
|
1285
|
+
}
|
|
1286
|
+
if (f.fields) {
|
|
1287
|
+
restoreNestedFieldsVisibility(f, nestedFieldsVisibility, path);
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1262
1291
|
function updateField(field, requiredFields, node, formValues, logic, config) {
|
|
1263
1292
|
if (!field) {
|
|
1264
1293
|
return;
|
|
@@ -1270,9 +1299,13 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
|
|
|
1270
1299
|
if (fieldIsRequired) {
|
|
1271
1300
|
field.isVisible = true;
|
|
1272
1301
|
}
|
|
1302
|
+
const nestedFieldsVisibility = preserveNestedFieldsVisibility(field);
|
|
1273
1303
|
const updateAttributes = (fieldAttrs) => {
|
|
1274
1304
|
Object.entries(fieldAttrs).forEach(([key, value]) => {
|
|
1275
1305
|
field[key] = value;
|
|
1306
|
+
if (key === "fields" && !(0, import_isNil.default)(nestedFieldsVisibility)) {
|
|
1307
|
+
restoreNestedFieldsVisibility(field, nestedFieldsVisibility);
|
|
1308
|
+
}
|
|
1276
1309
|
if (key === "schema" && typeof value === "function") {
|
|
1277
1310
|
field[key] = value();
|
|
1278
1311
|
}
|
|
@@ -1324,9 +1357,19 @@ function processNode({
|
|
|
1324
1357
|
logic
|
|
1325
1358
|
}) {
|
|
1326
1359
|
const requiredFields = new Set(accRequired);
|
|
1327
|
-
Object.
|
|
1360
|
+
Object.entries(node.properties ?? []).forEach(([fieldName, nestedNode]) => {
|
|
1328
1361
|
const field = getField(fieldName, formFields);
|
|
1329
1362
|
updateField(field, requiredFields, node, formValues, logic, { parentID });
|
|
1363
|
+
const isFieldset = field?.inputType === supportedTypes.FIELDSET;
|
|
1364
|
+
if (isFieldset) {
|
|
1365
|
+
processNode({
|
|
1366
|
+
node: nestedNode,
|
|
1367
|
+
formValues: formValues[fieldName] || {},
|
|
1368
|
+
formFields: field.fields,
|
|
1369
|
+
parentID,
|
|
1370
|
+
logic
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1330
1373
|
});
|
|
1331
1374
|
node.required?.forEach((fieldName) => {
|
|
1332
1375
|
requiredFields.add(fieldName);
|
|
@@ -1384,20 +1427,6 @@ function processNode({
|
|
|
1384
1427
|
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
1385
1428
|
});
|
|
1386
1429
|
}
|
|
1387
|
-
if (node.properties) {
|
|
1388
|
-
Object.entries(node.properties).forEach(([name, nestedNode]) => {
|
|
1389
|
-
const inputType = getInputType(nestedNode);
|
|
1390
|
-
if (inputType === supportedTypes.FIELDSET) {
|
|
1391
|
-
processNode({
|
|
1392
|
-
node: nestedNode,
|
|
1393
|
-
formValues: formValues[name] || {},
|
|
1394
|
-
formFields: getField(name, formFields).fields,
|
|
1395
|
-
parentID: name,
|
|
1396
|
-
logic
|
|
1397
|
-
});
|
|
1398
|
-
}
|
|
1399
|
-
});
|
|
1400
|
-
}
|
|
1401
1430
|
if (node["x-jsf-logic"]) {
|
|
1402
1431
|
const { required: requiredFromLogic } = processJSONLogicNode({
|
|
1403
1432
|
node: node["x-jsf-logic"],
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2025 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.12.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.12.1-dev.20250929100600
|
|
5
|
+
Generated: Mon, 29 Sep 2025 10:07:37 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -699,9 +699,7 @@ function buildYupSchema(field, config, logic) {
|
|
|
699
699
|
const fieldSetShape = {};
|
|
700
700
|
innerFields.forEach((fieldSetfield) => {
|
|
701
701
|
if (fieldSetfield.fields) {
|
|
702
|
-
fieldSetShape[fieldSetfield.name] = object().shape(
|
|
703
|
-
buildFieldSetSchema(fieldSetfield.fields)
|
|
704
|
-
);
|
|
702
|
+
fieldSetShape[fieldSetfield.name] = object().shape(buildFieldSetSchema(fieldSetfield.fields)).nullable();
|
|
705
703
|
} else {
|
|
706
704
|
fieldSetShape[fieldSetfield.name] = buildYupSchema(
|
|
707
705
|
{
|
|
@@ -1028,20 +1026,26 @@ function validateInlineRules(jsonSchema, sampleEmptyObject) {
|
|
|
1028
1026
|
});
|
|
1029
1027
|
});
|
|
1030
1028
|
}
|
|
1031
|
-
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}"
|
|
1029
|
+
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`, inReduceOrMap = false) {
|
|
1032
1030
|
Object.entries(rule ?? {}).map(([operator, subRule]) => {
|
|
1033
1031
|
if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
|
|
1034
1032
|
return;
|
|
1035
1033
|
throwIfUnknownOperator(operator, subRule, id);
|
|
1034
|
+
const isReduceOrMap = inReduceOrMap || operator === "reduce" || operator === "map";
|
|
1035
|
+
const validationData = isReduceOrMap ? {
|
|
1036
|
+
...data,
|
|
1037
|
+
accumulator: 0,
|
|
1038
|
+
current: null
|
|
1039
|
+
} : data;
|
|
1036
1040
|
subRule.map((item) => {
|
|
1037
1041
|
const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
|
|
1038
1042
|
if (isVar) {
|
|
1039
|
-
const exists = jsonLogic.apply({ var: removeIndicesFromPath(item.var) },
|
|
1043
|
+
const exists = jsonLogic.apply({ var: removeIndicesFromPath(item.var) }, validationData);
|
|
1040
1044
|
if (exists === null) {
|
|
1041
1045
|
throw Error(errorMessage(item));
|
|
1042
1046
|
}
|
|
1043
1047
|
} else {
|
|
1044
|
-
checkRuleIntegrity(item, id, data);
|
|
1048
|
+
checkRuleIntegrity(item, id, data, errorMessage, isReduceOrMap);
|
|
1045
1049
|
}
|
|
1046
1050
|
});
|
|
1047
1051
|
});
|
|
@@ -1222,6 +1226,30 @@ function getPrefillValues(fields, initialValues = {}) {
|
|
|
1222
1226
|
});
|
|
1223
1227
|
return initialValues;
|
|
1224
1228
|
}
|
|
1229
|
+
function preserveNestedFieldsVisibility(field, parentPath = "") {
|
|
1230
|
+
return field.fields?.reduce?.((acc, f) => {
|
|
1231
|
+
const path = parentPath ? `${parentPath}.${f.name}` : f.name;
|
|
1232
|
+
if (!isNil(f.isVisible)) {
|
|
1233
|
+
acc[path] = f.isVisible;
|
|
1234
|
+
}
|
|
1235
|
+
if (f.fields) {
|
|
1236
|
+
Object.assign(acc, preserveNestedFieldsVisibility(f, path));
|
|
1237
|
+
}
|
|
1238
|
+
return acc;
|
|
1239
|
+
}, {});
|
|
1240
|
+
}
|
|
1241
|
+
function restoreNestedFieldsVisibility(field, nestedFieldsVisibility, parentPath = "") {
|
|
1242
|
+
field.fields.forEach((f) => {
|
|
1243
|
+
const path = parentPath ? `${parentPath}.${f.name}` : f.name;
|
|
1244
|
+
const visibility = get2(nestedFieldsVisibility, path);
|
|
1245
|
+
if (!isNil(visibility)) {
|
|
1246
|
+
f.isVisible = visibility;
|
|
1247
|
+
}
|
|
1248
|
+
if (f.fields) {
|
|
1249
|
+
restoreNestedFieldsVisibility(f, nestedFieldsVisibility, path);
|
|
1250
|
+
}
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1225
1253
|
function updateField(field, requiredFields, node, formValues, logic, config) {
|
|
1226
1254
|
if (!field) {
|
|
1227
1255
|
return;
|
|
@@ -1233,9 +1261,13 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
|
|
|
1233
1261
|
if (fieldIsRequired) {
|
|
1234
1262
|
field.isVisible = true;
|
|
1235
1263
|
}
|
|
1264
|
+
const nestedFieldsVisibility = preserveNestedFieldsVisibility(field);
|
|
1236
1265
|
const updateAttributes = (fieldAttrs) => {
|
|
1237
1266
|
Object.entries(fieldAttrs).forEach(([key, value]) => {
|
|
1238
1267
|
field[key] = value;
|
|
1268
|
+
if (key === "fields" && !isNil(nestedFieldsVisibility)) {
|
|
1269
|
+
restoreNestedFieldsVisibility(field, nestedFieldsVisibility);
|
|
1270
|
+
}
|
|
1239
1271
|
if (key === "schema" && typeof value === "function") {
|
|
1240
1272
|
field[key] = value();
|
|
1241
1273
|
}
|
|
@@ -1287,9 +1319,19 @@ function processNode({
|
|
|
1287
1319
|
logic
|
|
1288
1320
|
}) {
|
|
1289
1321
|
const requiredFields = new Set(accRequired);
|
|
1290
|
-
Object.
|
|
1322
|
+
Object.entries(node.properties ?? []).forEach(([fieldName, nestedNode]) => {
|
|
1291
1323
|
const field = getField(fieldName, formFields);
|
|
1292
1324
|
updateField(field, requiredFields, node, formValues, logic, { parentID });
|
|
1325
|
+
const isFieldset = field?.inputType === supportedTypes.FIELDSET;
|
|
1326
|
+
if (isFieldset) {
|
|
1327
|
+
processNode({
|
|
1328
|
+
node: nestedNode,
|
|
1329
|
+
formValues: formValues[fieldName] || {},
|
|
1330
|
+
formFields: field.fields,
|
|
1331
|
+
parentID,
|
|
1332
|
+
logic
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1293
1335
|
});
|
|
1294
1336
|
node.required?.forEach((fieldName) => {
|
|
1295
1337
|
requiredFields.add(fieldName);
|
|
@@ -1347,20 +1389,6 @@ function processNode({
|
|
|
1347
1389
|
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
1348
1390
|
});
|
|
1349
1391
|
}
|
|
1350
|
-
if (node.properties) {
|
|
1351
|
-
Object.entries(node.properties).forEach(([name, nestedNode]) => {
|
|
1352
|
-
const inputType = getInputType(nestedNode);
|
|
1353
|
-
if (inputType === supportedTypes.FIELDSET) {
|
|
1354
|
-
processNode({
|
|
1355
|
-
node: nestedNode,
|
|
1356
|
-
formValues: formValues[name] || {},
|
|
1357
|
-
formFields: getField(name, formFields).fields,
|
|
1358
|
-
parentID: name,
|
|
1359
|
-
logic
|
|
1360
|
-
});
|
|
1361
|
-
}
|
|
1362
|
-
});
|
|
1363
|
-
}
|
|
1364
1392
|
if (node["x-jsf-logic"]) {
|
|
1365
1393
|
const { required: requiredFromLogic } = processJSONLogicNode({
|
|
1366
1394
|
node: node["x-jsf-logic"],
|
package/dist/standalone.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
3
|
Copyright (c) 2025 Remote Technology, Inc.
|
|
4
|
-
NPM Package: @remoteoss/json-schema-form@0.12.
|
|
5
|
-
Generated:
|
|
4
|
+
NPM Package: @remoteoss/json-schema-form@0.12.1-dev.20250929100600
|
|
5
|
+
Generated: Mon, 29 Sep 2025 10:07:37 GMT
|
|
6
6
|
|
|
7
7
|
MIT License
|
|
8
8
|
|
|
@@ -12215,9 +12215,7 @@ function buildYupSchema(field, config, logic) {
|
|
|
12215
12215
|
const fieldSetShape = {};
|
|
12216
12216
|
innerFields.forEach((fieldSetfield) => {
|
|
12217
12217
|
if (fieldSetfield.fields) {
|
|
12218
|
-
fieldSetShape[fieldSetfield.name] = ObjectSchema().shape(
|
|
12219
|
-
buildFieldSetSchema(fieldSetfield.fields)
|
|
12220
|
-
);
|
|
12218
|
+
fieldSetShape[fieldSetfield.name] = ObjectSchema().shape(buildFieldSetSchema(fieldSetfield.fields)).nullable();
|
|
12221
12219
|
} else {
|
|
12222
12220
|
fieldSetShape[fieldSetfield.name] = buildYupSchema(
|
|
12223
12221
|
{
|
|
@@ -12544,20 +12542,26 @@ function validateInlineRules(jsonSchema, sampleEmptyObject) {
|
|
|
12544
12542
|
});
|
|
12545
12543
|
});
|
|
12546
12544
|
}
|
|
12547
|
-
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}"
|
|
12545
|
+
function checkRuleIntegrity(rule, id, data, errorMessage = (item) => `[json-schema-form] json-logic error: rule "${id}" has no variable "${item.var}".`, inReduceOrMap = false) {
|
|
12548
12546
|
Object.entries(rule ?? {}).map(([operator, subRule]) => {
|
|
12549
12547
|
if (!Array.isArray(subRule) && subRule !== null && subRule !== void 0)
|
|
12550
12548
|
return;
|
|
12551
12549
|
throwIfUnknownOperator(operator, subRule, id);
|
|
12550
|
+
const isReduceOrMap = inReduceOrMap || operator === "reduce" || operator === "map";
|
|
12551
|
+
const validationData = isReduceOrMap ? {
|
|
12552
|
+
...data,
|
|
12553
|
+
accumulator: 0,
|
|
12554
|
+
current: null
|
|
12555
|
+
} : data;
|
|
12552
12556
|
subRule.map((item) => {
|
|
12553
12557
|
const isVar = item !== null && typeof item === "object" && Object.hasOwn(item, "var");
|
|
12554
12558
|
if (isVar) {
|
|
12555
|
-
const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) },
|
|
12559
|
+
const exists = import_json_logic_js.default.apply({ var: removeIndicesFromPath(item.var) }, validationData);
|
|
12556
12560
|
if (exists === null) {
|
|
12557
12561
|
throw Error(errorMessage(item));
|
|
12558
12562
|
}
|
|
12559
12563
|
} else {
|
|
12560
|
-
checkRuleIntegrity(item, id, data);
|
|
12564
|
+
checkRuleIntegrity(item, id, data, errorMessage, isReduceOrMap);
|
|
12561
12565
|
}
|
|
12562
12566
|
});
|
|
12563
12567
|
});
|
|
@@ -12738,6 +12742,30 @@ function getPrefillValues(fields, initialValues = {}) {
|
|
|
12738
12742
|
});
|
|
12739
12743
|
return initialValues;
|
|
12740
12744
|
}
|
|
12745
|
+
function preserveNestedFieldsVisibility(field, parentPath = "") {
|
|
12746
|
+
return field.fields?.reduce?.((acc, f) => {
|
|
12747
|
+
const path = parentPath ? `${parentPath}.${f.name}` : f.name;
|
|
12748
|
+
if (!(0, import_isNil.default)(f.isVisible)) {
|
|
12749
|
+
acc[path] = f.isVisible;
|
|
12750
|
+
}
|
|
12751
|
+
if (f.fields) {
|
|
12752
|
+
Object.assign(acc, preserveNestedFieldsVisibility(f, path));
|
|
12753
|
+
}
|
|
12754
|
+
return acc;
|
|
12755
|
+
}, {});
|
|
12756
|
+
}
|
|
12757
|
+
function restoreNestedFieldsVisibility(field, nestedFieldsVisibility, parentPath = "") {
|
|
12758
|
+
field.fields.forEach((f) => {
|
|
12759
|
+
const path = parentPath ? `${parentPath}.${f.name}` : f.name;
|
|
12760
|
+
const visibility = (0, import_get3.default)(nestedFieldsVisibility, path);
|
|
12761
|
+
if (!(0, import_isNil.default)(visibility)) {
|
|
12762
|
+
f.isVisible = visibility;
|
|
12763
|
+
}
|
|
12764
|
+
if (f.fields) {
|
|
12765
|
+
restoreNestedFieldsVisibility(f, nestedFieldsVisibility, path);
|
|
12766
|
+
}
|
|
12767
|
+
});
|
|
12768
|
+
}
|
|
12741
12769
|
function updateField(field, requiredFields, node, formValues, logic, config) {
|
|
12742
12770
|
if (!field) {
|
|
12743
12771
|
return;
|
|
@@ -12749,9 +12777,13 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
|
|
|
12749
12777
|
if (fieldIsRequired) {
|
|
12750
12778
|
field.isVisible = true;
|
|
12751
12779
|
}
|
|
12780
|
+
const nestedFieldsVisibility = preserveNestedFieldsVisibility(field);
|
|
12752
12781
|
const updateAttributes = (fieldAttrs) => {
|
|
12753
12782
|
Object.entries(fieldAttrs).forEach(([key, value]) => {
|
|
12754
12783
|
field[key] = value;
|
|
12784
|
+
if (key === "fields" && !(0, import_isNil.default)(nestedFieldsVisibility)) {
|
|
12785
|
+
restoreNestedFieldsVisibility(field, nestedFieldsVisibility);
|
|
12786
|
+
}
|
|
12755
12787
|
if (key === "schema" && typeof value === "function") {
|
|
12756
12788
|
field[key] = value();
|
|
12757
12789
|
}
|
|
@@ -12803,9 +12835,19 @@ function processNode({
|
|
|
12803
12835
|
logic
|
|
12804
12836
|
}) {
|
|
12805
12837
|
const requiredFields = new Set(accRequired);
|
|
12806
|
-
Object.
|
|
12838
|
+
Object.entries(node.properties ?? []).forEach(([fieldName, nestedNode]) => {
|
|
12807
12839
|
const field = getField(fieldName, formFields);
|
|
12808
12840
|
updateField(field, requiredFields, node, formValues, logic, { parentID });
|
|
12841
|
+
const isFieldset = field?.inputType === supportedTypes.FIELDSET;
|
|
12842
|
+
if (isFieldset) {
|
|
12843
|
+
processNode({
|
|
12844
|
+
node: nestedNode,
|
|
12845
|
+
formValues: formValues[fieldName] || {},
|
|
12846
|
+
formFields: field.fields,
|
|
12847
|
+
parentID,
|
|
12848
|
+
logic
|
|
12849
|
+
});
|
|
12850
|
+
}
|
|
12809
12851
|
});
|
|
12810
12852
|
node.required?.forEach((fieldName) => {
|
|
12811
12853
|
requiredFields.add(fieldName);
|
|
@@ -12863,20 +12905,6 @@ function processNode({
|
|
|
12863
12905
|
allOfItemRequired.forEach(requiredFields.add, requiredFields);
|
|
12864
12906
|
});
|
|
12865
12907
|
}
|
|
12866
|
-
if (node.properties) {
|
|
12867
|
-
Object.entries(node.properties).forEach(([name, nestedNode]) => {
|
|
12868
|
-
const inputType = getInputType(nestedNode);
|
|
12869
|
-
if (inputType === supportedTypes.FIELDSET) {
|
|
12870
|
-
processNode({
|
|
12871
|
-
node: nestedNode,
|
|
12872
|
-
formValues: formValues[name] || {},
|
|
12873
|
-
formFields: getField(name, formFields).fields,
|
|
12874
|
-
parentID: name,
|
|
12875
|
-
logic
|
|
12876
|
-
});
|
|
12877
|
-
}
|
|
12878
|
-
});
|
|
12879
|
-
}
|
|
12880
12908
|
if (node["x-jsf-logic"]) {
|
|
12881
12909
|
const { required: requiredFromLogic } = processJSONLogicNode({
|
|
12882
12910
|
node: node["x-jsf-logic"],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remoteoss/json-schema-form",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1-dev.20250929100600",
|
|
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",
|
|
@@ -27,26 +27,23 @@
|
|
|
27
27
|
"test:watch": "jest --watchAll",
|
|
28
28
|
"lint": "eslint \"src/**/*.{js,ts}\"",
|
|
29
29
|
"format": "npm run format:prettier && npm run format:eslint",
|
|
30
|
-
"prepare": "husky install",
|
|
30
|
+
"prepare": "cd .. && husky install",
|
|
31
31
|
"format:eslint": "eslint --fix \"src/**/*.{js,ts}\"",
|
|
32
32
|
"format:prettier": "prettier --write \"src/**/*.{js,ts}\"",
|
|
33
33
|
"prettier:check": "prettier --check \"src/**/*.{js,ts}\"",
|
|
34
34
|
"check:pr-version": "node scripts/pr_dev_version",
|
|
35
|
-
"check:pr-next-version": "node scripts/pr_next_dev_version",
|
|
36
35
|
"release:local": "node scripts/release_local",
|
|
37
36
|
"release:dev:patch": "node scripts/release_dev patch",
|
|
38
37
|
"release:dev:minor": "node scripts/release_dev minor",
|
|
39
38
|
"release:main:patch": "node scripts/release_main patch",
|
|
40
39
|
"release:main:minor": "node scripts/release_main minor",
|
|
41
|
-
"release:v1:dev": "node scripts/release_v1 dev",
|
|
42
|
-
"release:v1:beta": "node scripts/release_v1 beta",
|
|
43
40
|
"version_as_main": "node scripts/version_as_main.js",
|
|
44
41
|
"psrepublishOnly": "if [[ ! $PWD =~ scripts$ ]]; then npm run publish:nopublish; fi",
|
|
45
42
|
"psublish:nopublish": "echo 'Use `npm release:*` script instead && exit 1"
|
|
46
43
|
},
|
|
47
44
|
"lint-staged": {
|
|
48
45
|
"*.{js,jsx}": [
|
|
49
|
-
"npm run format"
|
|
46
|
+
"npm run format --prefix ./v0"
|
|
50
47
|
]
|
|
51
48
|
},
|
|
52
49
|
"dependencies": {
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
schemaForErrorMessageSpecificity,
|
|
64
64
|
jsfConfigForErrorMessageSpecificity,
|
|
65
65
|
schemaInputTypeFile,
|
|
66
|
+
schemaWithNestedFieldsetsConditionals,
|
|
66
67
|
} from './helpers';
|
|
67
68
|
import { mockConsole, restoreConsoleAndEnsureItWasNotCalled } from './testUtils';
|
|
68
69
|
import { createHeadlessForm } from '@/createHeadlessForm';
|
|
@@ -2200,6 +2201,112 @@ describe('createHeadlessForm', () => {
|
|
|
2200
2201
|
).toBeUndefined();
|
|
2201
2202
|
});
|
|
2202
2203
|
});
|
|
2204
|
+
|
|
2205
|
+
describe('supports conditionals over nested fieldsets', () => {
|
|
2206
|
+
it('retirement_plan fieldset is hidden when no values are provided', () => {
|
|
2207
|
+
const { fields, handleValidation } = createHeadlessForm(
|
|
2208
|
+
schemaWithNestedFieldsetsConditionals,
|
|
2209
|
+
{}
|
|
2210
|
+
);
|
|
2211
|
+
const validateForm = (vals) => friendlyError(handleValidation(vals));
|
|
2212
|
+
|
|
2213
|
+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(false);
|
|
2214
|
+
|
|
2215
|
+
expect(validateForm({ perks: {} })).toEqual({
|
|
2216
|
+
perks: {
|
|
2217
|
+
benefits_package: 'Required field',
|
|
2218
|
+
has_retirement_plan: 'Required field',
|
|
2219
|
+
},
|
|
2220
|
+
});
|
|
2221
|
+
|
|
2222
|
+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(false);
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
it("submits without retirement_plan when user selects 'no' for has_retirement_plan", () => {
|
|
2226
|
+
const { fields, handleValidation } = createHeadlessForm(
|
|
2227
|
+
schemaWithNestedFieldsetsConditionals,
|
|
2228
|
+
{}
|
|
2229
|
+
);
|
|
2230
|
+
const validateForm = (vals) => friendlyError(handleValidation(vals));
|
|
2231
|
+
|
|
2232
|
+
expect(
|
|
2233
|
+
validateForm({ perks: { benefits_package: 'basic', has_retirement_plan: 'no' } })
|
|
2234
|
+
).toBeUndefined();
|
|
2235
|
+
|
|
2236
|
+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(false);
|
|
2237
|
+
});
|
|
2238
|
+
|
|
2239
|
+
it("retirement_plan fieldset is visible when user selects 'yes' for has_retirement_plan", () => {
|
|
2240
|
+
const { fields, handleValidation } = createHeadlessForm(
|
|
2241
|
+
schemaWithNestedFieldsetsConditionals,
|
|
2242
|
+
{}
|
|
2243
|
+
);
|
|
2244
|
+
const validateForm = (vals) => friendlyError(handleValidation(vals));
|
|
2245
|
+
|
|
2246
|
+
expect(
|
|
2247
|
+
validateForm({
|
|
2248
|
+
perks: {
|
|
2249
|
+
benefits_package: 'basic',
|
|
2250
|
+
has_retirement_plan: 'yes',
|
|
2251
|
+
declare_amount: 'yes',
|
|
2252
|
+
retirement_plan: { plan_name: 'test', year: 2025 },
|
|
2253
|
+
},
|
|
2254
|
+
})
|
|
2255
|
+
).toEqual({
|
|
2256
|
+
perks: {
|
|
2257
|
+
retirement_plan: {
|
|
2258
|
+
amount: 'Required field',
|
|
2259
|
+
},
|
|
2260
|
+
},
|
|
2261
|
+
});
|
|
2262
|
+
|
|
2263
|
+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(true);
|
|
2264
|
+
expect(getField(fields, 'perks', 'declare_amount').isVisible).toBe(true);
|
|
2265
|
+
expect(getField(fields, 'perks', 'declare_amount').default).toBe('yes');
|
|
2266
|
+
expect(getField(fields, 'perks', 'retirement_plan', 'amount').isVisible).toBe(true);
|
|
2267
|
+
});
|
|
2268
|
+
|
|
2269
|
+
it("retirement_plan's amount field is hidden when user selects 'no' for declare_amount", () => {
|
|
2270
|
+
const { fields, handleValidation } = createHeadlessForm(
|
|
2271
|
+
schemaWithNestedFieldsetsConditionals,
|
|
2272
|
+
{}
|
|
2273
|
+
);
|
|
2274
|
+
const validateForm = (vals) => friendlyError(handleValidation(vals));
|
|
2275
|
+
|
|
2276
|
+
expect(
|
|
2277
|
+
validateForm({
|
|
2278
|
+
perks: {
|
|
2279
|
+
benefits_package: 'basic',
|
|
2280
|
+
has_retirement_plan: 'yes',
|
|
2281
|
+
declare_amount: 'no',
|
|
2282
|
+
retirement_plan: { plan_name: 'test', year: 2025 },
|
|
2283
|
+
},
|
|
2284
|
+
})
|
|
2285
|
+
).toBeUndefined();
|
|
2286
|
+
|
|
2287
|
+
expect(getField(fields, 'perks', 'retirement_plan').isVisible).toBe(true);
|
|
2288
|
+
expect(getField(fields, 'perks', 'declare_amount').isVisible).toBe(true);
|
|
2289
|
+
expect(getField(fields, 'perks', 'retirement_plan', 'amount').isVisible).toBe(false);
|
|
2290
|
+
});
|
|
2291
|
+
|
|
2292
|
+
it('submits with valid retirement_plan', async () => {
|
|
2293
|
+
const { handleValidation } = createHeadlessForm(
|
|
2294
|
+
schemaWithNestedFieldsetsConditionals,
|
|
2295
|
+
{}
|
|
2296
|
+
);
|
|
2297
|
+
const validateForm = (vals) => friendlyError(handleValidation(vals));
|
|
2298
|
+
|
|
2299
|
+
expect(
|
|
2300
|
+
validateForm({
|
|
2301
|
+
perks: {
|
|
2302
|
+
benefits_package: 'plus',
|
|
2303
|
+
has_retirement_plan: 'yes',
|
|
2304
|
+
retirement_plan: { plan_name: 'test', year: 2025, amount: 1000 },
|
|
2305
|
+
},
|
|
2306
|
+
})
|
|
2307
|
+
).toBeUndefined();
|
|
2308
|
+
});
|
|
2309
|
+
});
|
|
2203
2310
|
});
|
|
2204
2311
|
|
|
2205
2312
|
it('support "email" field type', () => {
|
package/src/tests/helpers.js
CHANGED
|
@@ -1939,7 +1939,6 @@ export const schemaWithConditionalToFieldset = {
|
|
|
1939
1939
|
then: {
|
|
1940
1940
|
properties: {
|
|
1941
1941
|
pto: {
|
|
1942
|
-
$comment: '@BUG: This description does not disappear once activated.',
|
|
1943
1942
|
description: 'Above 30 hours, the PTO needs to be at least 20 days.',
|
|
1944
1943
|
minimum: 20,
|
|
1945
1944
|
},
|
|
@@ -1972,6 +1971,178 @@ export const schemaWithConditionalToFieldset = {
|
|
|
1972
1971
|
required: ['perks', 'work_hours_per_week'],
|
|
1973
1972
|
};
|
|
1974
1973
|
|
|
1974
|
+
export const schemaWithNestedFieldsetsConditionals = {
|
|
1975
|
+
additionalProperties: false,
|
|
1976
|
+
type: 'object',
|
|
1977
|
+
properties: {
|
|
1978
|
+
perks: {
|
|
1979
|
+
additionalProperties: false,
|
|
1980
|
+
properties: {
|
|
1981
|
+
benefits_package: {
|
|
1982
|
+
oneOf: [
|
|
1983
|
+
{
|
|
1984
|
+
const: 'basic',
|
|
1985
|
+
title: 'Basic',
|
|
1986
|
+
},
|
|
1987
|
+
{
|
|
1988
|
+
const: 'plus',
|
|
1989
|
+
title: 'Plus',
|
|
1990
|
+
},
|
|
1991
|
+
],
|
|
1992
|
+
title: 'Benefits package',
|
|
1993
|
+
type: 'string',
|
|
1994
|
+
'x-jsf-presentation': {
|
|
1995
|
+
inputType: 'radio',
|
|
1996
|
+
},
|
|
1997
|
+
},
|
|
1998
|
+
has_retirement_plan: {
|
|
1999
|
+
oneOf: [
|
|
2000
|
+
{
|
|
2001
|
+
const: 'yes',
|
|
2002
|
+
title: 'Yes',
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
const: 'no',
|
|
2006
|
+
title: 'No',
|
|
2007
|
+
},
|
|
2008
|
+
],
|
|
2009
|
+
title: 'Has retirement plan?',
|
|
2010
|
+
type: 'string',
|
|
2011
|
+
'x-jsf-presentation': {
|
|
2012
|
+
inputType: 'radio',
|
|
2013
|
+
},
|
|
2014
|
+
},
|
|
2015
|
+
declare_amount: {
|
|
2016
|
+
oneOf: [
|
|
2017
|
+
{
|
|
2018
|
+
const: 'yes',
|
|
2019
|
+
title: 'Yes',
|
|
2020
|
+
},
|
|
2021
|
+
{
|
|
2022
|
+
const: 'no',
|
|
2023
|
+
title: 'No',
|
|
2024
|
+
},
|
|
2025
|
+
],
|
|
2026
|
+
title: 'Declare retirement amount?',
|
|
2027
|
+
type: 'string',
|
|
2028
|
+
default: 'yes',
|
|
2029
|
+
'x-jsf-presentation': {
|
|
2030
|
+
inputType: 'radio',
|
|
2031
|
+
},
|
|
2032
|
+
},
|
|
2033
|
+
retirement_plan: {
|
|
2034
|
+
type: 'object',
|
|
2035
|
+
title: 'Retirement plan',
|
|
2036
|
+
properties: {
|
|
2037
|
+
plan_name: {
|
|
2038
|
+
type: 'string',
|
|
2039
|
+
title: 'Plan name',
|
|
2040
|
+
},
|
|
2041
|
+
year: {
|
|
2042
|
+
type: 'number',
|
|
2043
|
+
title: 'Year',
|
|
2044
|
+
},
|
|
2045
|
+
amount: {
|
|
2046
|
+
type: 'number',
|
|
2047
|
+
title: 'Amount',
|
|
2048
|
+
},
|
|
2049
|
+
},
|
|
2050
|
+
required: ['plan_name', 'year'],
|
|
2051
|
+
'x-jsf-presentation': {
|
|
2052
|
+
inputType: 'fieldset',
|
|
2053
|
+
},
|
|
2054
|
+
},
|
|
2055
|
+
},
|
|
2056
|
+
required: ['benefits_package', 'has_retirement_plan'],
|
|
2057
|
+
title: 'Perks',
|
|
2058
|
+
type: 'object',
|
|
2059
|
+
'x-jsf-presentation': {
|
|
2060
|
+
inputType: 'fieldset',
|
|
2061
|
+
},
|
|
2062
|
+
},
|
|
2063
|
+
},
|
|
2064
|
+
allOf: [
|
|
2065
|
+
{
|
|
2066
|
+
if: {
|
|
2067
|
+
properties: {
|
|
2068
|
+
perks: {
|
|
2069
|
+
properties: {
|
|
2070
|
+
has_retirement_plan: {
|
|
2071
|
+
const: 'no',
|
|
2072
|
+
},
|
|
2073
|
+
},
|
|
2074
|
+
},
|
|
2075
|
+
},
|
|
2076
|
+
},
|
|
2077
|
+
then: {
|
|
2078
|
+
properties: {
|
|
2079
|
+
perks: {
|
|
2080
|
+
properties: {
|
|
2081
|
+
retirement_plan: false,
|
|
2082
|
+
declare_amount: false,
|
|
2083
|
+
},
|
|
2084
|
+
},
|
|
2085
|
+
},
|
|
2086
|
+
},
|
|
2087
|
+
},
|
|
2088
|
+
{
|
|
2089
|
+
if: {
|
|
2090
|
+
properties: {
|
|
2091
|
+
perks: {
|
|
2092
|
+
properties: {
|
|
2093
|
+
declare_amount: {
|
|
2094
|
+
const: 'no',
|
|
2095
|
+
},
|
|
2096
|
+
},
|
|
2097
|
+
required: ['declare_amount'],
|
|
2098
|
+
},
|
|
2099
|
+
},
|
|
2100
|
+
},
|
|
2101
|
+
then: {
|
|
2102
|
+
properties: {
|
|
2103
|
+
perks: {
|
|
2104
|
+
properties: {
|
|
2105
|
+
retirement_plan: {
|
|
2106
|
+
properties: {
|
|
2107
|
+
amount: false,
|
|
2108
|
+
},
|
|
2109
|
+
},
|
|
2110
|
+
},
|
|
2111
|
+
},
|
|
2112
|
+
},
|
|
2113
|
+
},
|
|
2114
|
+
},
|
|
2115
|
+
{
|
|
2116
|
+
if: {
|
|
2117
|
+
properties: {
|
|
2118
|
+
perks: {
|
|
2119
|
+
properties: {
|
|
2120
|
+
has_retirement_plan: {
|
|
2121
|
+
const: 'yes',
|
|
2122
|
+
},
|
|
2123
|
+
declare_amount: {
|
|
2124
|
+
const: 'yes',
|
|
2125
|
+
},
|
|
2126
|
+
},
|
|
2127
|
+
required: ['has_retirement_plan', 'declare_amount'],
|
|
2128
|
+
},
|
|
2129
|
+
},
|
|
2130
|
+
},
|
|
2131
|
+
then: {
|
|
2132
|
+
properties: {
|
|
2133
|
+
perks: {
|
|
2134
|
+
properties: {
|
|
2135
|
+
retirement_plan: {
|
|
2136
|
+
required: ['amount'],
|
|
2137
|
+
},
|
|
2138
|
+
},
|
|
2139
|
+
},
|
|
2140
|
+
},
|
|
2141
|
+
},
|
|
2142
|
+
},
|
|
2143
|
+
],
|
|
2144
|
+
};
|
|
2145
|
+
|
|
1975
2146
|
export const schemaWorkSchedule = {
|
|
1976
2147
|
type: 'object',
|
|
1977
2148
|
properties: {
|
|
@@ -1061,7 +1061,7 @@ export const schemaWithReduceAccumulator = {
|
|
|
1061
1061
|
type: 'number',
|
|
1062
1062
|
'x-jsf-logic-computedAttrs': {
|
|
1063
1063
|
const: 'computed_work_hours_per_week',
|
|
1064
|
-
|
|
1064
|
+
default: 'computed_work_hours_per_week',
|
|
1065
1065
|
title: '{{computed_work_hours_per_week}} hours per week',
|
|
1066
1066
|
},
|
|
1067
1067
|
},
|
|
@@ -1073,7 +1073,7 @@ export const schemaWithReduceAccumulator = {
|
|
|
1073
1073
|
'*': [
|
|
1074
1074
|
{ var: 'working_hours_per_day' },
|
|
1075
1075
|
{
|
|
1076
|
-
reduce: [{ var: 'work_days' }, { '+': [{ var:
|
|
1076
|
+
reduce: [{ var: 'work_days' }, { '+': [{ var: 'accumulator' }, 1] }, 0],
|
|
1077
1077
|
},
|
|
1078
1078
|
],
|
|
1079
1079
|
},
|
|
@@ -1081,3 +1081,51 @@ export const schemaWithReduceAccumulator = {
|
|
|
1081
1081
|
},
|
|
1082
1082
|
},
|
|
1083
1083
|
};
|
|
1084
|
+
|
|
1085
|
+
export const schemaWithReduceAccumulatorAndMerge = {
|
|
1086
|
+
properties: {
|
|
1087
|
+
work_days: {
|
|
1088
|
+
items: {
|
|
1089
|
+
anyOf: [
|
|
1090
|
+
{ const: 'monday', title: 'Monday' },
|
|
1091
|
+
{ const: 'tuesday', title: 'Tuesday' },
|
|
1092
|
+
{ const: 'wednesday', title: 'Wednesday' },
|
|
1093
|
+
{ const: 'thursday', title: 'Thursday' },
|
|
1094
|
+
{ const: 'friday', title: 'Friday' },
|
|
1095
|
+
{ const: 'saturday', title: 'Saturday' },
|
|
1096
|
+
{ const: 'sunday', title: 'Sunday' },
|
|
1097
|
+
],
|
|
1098
|
+
},
|
|
1099
|
+
type: 'array',
|
|
1100
|
+
uniqueItems: true,
|
|
1101
|
+
'x-jsf-presentation': {
|
|
1102
|
+
inputType: 'select',
|
|
1103
|
+
},
|
|
1104
|
+
},
|
|
1105
|
+
working_hours_per_day: {
|
|
1106
|
+
type: 'number',
|
|
1107
|
+
},
|
|
1108
|
+
working_hours_per_week: {
|
|
1109
|
+
type: 'number',
|
|
1110
|
+
'x-jsf-logic-computedAttrs': {
|
|
1111
|
+
const: 'computed_work_hours_per_week',
|
|
1112
|
+
default: 'computed_work_hours_per_week',
|
|
1113
|
+
title: '{{computed_work_hours_per_week}} hours per week',
|
|
1114
|
+
},
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
'x-jsf-logic': {
|
|
1118
|
+
computedValues: {
|
|
1119
|
+
computed_work_hours_per_week: {
|
|
1120
|
+
rule: {
|
|
1121
|
+
reduce: [
|
|
1122
|
+
{ var: 'work_days' },
|
|
1123
|
+
{ merge: [{ var: 'accumulator' }, [{ var: 'current' }]] },
|
|
1124
|
+
// note we use the wrong accumulator type
|
|
1125
|
+
0,
|
|
1126
|
+
],
|
|
1127
|
+
},
|
|
1128
|
+
},
|
|
1129
|
+
},
|
|
1130
|
+
},
|
|
1131
|
+
};
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
schemaWithValidationThatDoesNotExistOnProperty,
|
|
36
36
|
badSchemaThatWillNotSetAForcedValue,
|
|
37
37
|
schemaWithReduceAccumulator,
|
|
38
|
+
schemaWithReduceAccumulatorAndMerge,
|
|
38
39
|
schemaWithComputedPresentationAttributes,
|
|
39
40
|
} from './jsonLogic.fixtures';
|
|
40
41
|
import { mockConsole, restoreConsoleAndEnsureItWasNotCalled } from './testUtils';
|
|
@@ -245,8 +246,7 @@ describe('jsonLogic: cross-values validations', () => {
|
|
|
245
246
|
});
|
|
246
247
|
});
|
|
247
248
|
|
|
248
|
-
|
|
249
|
-
describe.skip('Reduce', () => {
|
|
249
|
+
describe('Reduce', () => {
|
|
250
250
|
it('reduce: working_hours_per_day * work_days', () => {
|
|
251
251
|
const { fields, handleValidation } = createHeadlessForm(schemaWithReduceAccumulator, {
|
|
252
252
|
strictInputType: false,
|
|
@@ -260,6 +260,18 @@ describe('jsonLogic: cross-values validations', () => {
|
|
|
260
260
|
expect(field.default).toEqual(16);
|
|
261
261
|
expect(field.label).toEqual('16 hours per week');
|
|
262
262
|
});
|
|
263
|
+
|
|
264
|
+
it('reduce: handles when operator is non numerical', () => {
|
|
265
|
+
const { fields, handleValidation } = createHeadlessForm(schemaWithReduceAccumulatorAndMerge, {
|
|
266
|
+
strictInputType: false,
|
|
267
|
+
});
|
|
268
|
+
handleValidation({
|
|
269
|
+
work_days: ['monday', 'tuesday'],
|
|
270
|
+
working_hours_per_day: 8,
|
|
271
|
+
});
|
|
272
|
+
const field = fields.find((i) => i.name === 'working_hours_per_week');
|
|
273
|
+
expect(field.const).toEqual([0, 'monday', 'tuesday']);
|
|
274
|
+
});
|
|
263
275
|
});
|
|
264
276
|
|
|
265
277
|
describe('Logical: ||, &&', () => {
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 Remote Technology, Inc.
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
<p align="center">
|
|
2
|
-
<img src=".github/media/jsf_logo_dark.png" width="600" alt="json-schema-form">
|
|
3
|
-
</p>
|
|
4
|
-
|
|
5
|
-
<p align="center">
|
|
6
|
-
<code>json-schema-form</code> is a headless UI form library powered by <a href="https://json-schema.org/">JSON Schemas</a>.
|
|
7
|
-
<br/>
|
|
8
|
-
It transforms JSON schemas into Javascript `fields` to be more easily consumed by your UI libraries.
|
|
9
|
-
</p>
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
### Why JSON Schemas for forms?
|
|
14
|
-
|
|
15
|
-
JSON Schemas are the SSoT (Single Source of Truth) that allows you to share form's data _structure_ and _validations_ between the server (backend) and the client (frontend), regardless of the language used.
|
|
16
|
-
|
|
17
|
-
You can use it beyond UI Forms, like lists, tables, and any other UI that needs structured JSON data.
|
|
18
|
-
|
|
19
|
-
## Installation
|
|
20
|
-
|
|
21
|
-
Available on [NPM](https://www.npmjs.com/package/@remoteoss/json-schema-form).
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npm install @remoteoss/json-schema-form
|
|
25
|
-
# or
|
|
26
|
-
|
|
27
|
-
yarn install @remoteoss/json-schema-form
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Getting Started
|
|
31
|
-
|
|
32
|
-
Check the 📚 **[JSF website](https://json-schema-form.vercel.app/)** for documentation.
|
|
33
|
-
|
|
34
|
-
### Playground
|
|
35
|
-
|
|
36
|
-
Check the 🕹️ **[JSF Playground](https://json-schema-form.vercel.app/?path=/docs/playground--docs)** for demos.
|
|
37
|
-
|
|
38
|
-
## Contributing
|
|
39
|
-
|
|
40
|
-
Read [CONTRIBUTING](CONTRIBUTING.md) to get started.
|
|
41
|
-
|
|
42
|
-
We are working on the [next version v1.0](/next). A rewrite in TypeScript with major bugfixes and missing features. It aims to support the latest JSON Schema dialect 2020-12.
|
|
43
|
-
|
|
44
|
-
_Backed by [Remote.com](https://remote.com/)_
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"title": "JSON json-schema-form-Schema",
|
|
4
|
-
"definitions": {
|
|
5
|
-
"schemaArray": {
|
|
6
|
-
"allOf": [
|
|
7
|
-
{
|
|
8
|
-
"$ref": "http://json-schema.org/draft-07/schema#/definitions/schemaArray"
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"items": { "$ref": "#" }
|
|
12
|
-
}
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"allOf": [{ "$ref": "http://json-schema.org/draft-07/schema#" }],
|
|
17
|
-
"properties": {
|
|
18
|
-
"additionalItems": { "$ref": "#" },
|
|
19
|
-
"additionalProperties": { "$ref": "#" },
|
|
20
|
-
"dependencies": {
|
|
21
|
-
"additionalProperties": {
|
|
22
|
-
"anyOf": [{ "$ref": "#" }, { "type": "array" }]
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
"items": {
|
|
26
|
-
"anyOf": [{ "$ref": "#" }, { "$ref": "#/definitions/schemaArray" }]
|
|
27
|
-
},
|
|
28
|
-
"definitions": {
|
|
29
|
-
"additionalProperties": { "$ref": "#" }
|
|
30
|
-
},
|
|
31
|
-
"patternProperties": {
|
|
32
|
-
"additionalProperties": { "$ref": "#" }
|
|
33
|
-
},
|
|
34
|
-
"properties": {
|
|
35
|
-
"additionalProperties": { "$ref": "#" }
|
|
36
|
-
},
|
|
37
|
-
"if": { "$ref": "#" },
|
|
38
|
-
"then": { "$ref": "#" },
|
|
39
|
-
"else": { "$ref": "#" },
|
|
40
|
-
"allOf": { "$ref": "#/definitions/schemaArray" },
|
|
41
|
-
"anyOf": { "$ref": "#/definitions/schemaArray" },
|
|
42
|
-
"oneOf": { "$ref": "#/definitions/schemaArray" },
|
|
43
|
-
"not": { "$ref": "#" },
|
|
44
|
-
"contains": { "$ref": "#" },
|
|
45
|
-
"propertyNames": { "$ref": "#" },
|
|
46
|
-
"x-jsf-order": {
|
|
47
|
-
"description": "This keyword defines the order of fields for a given form or fieldset. It's placed at the schema root and inside of each fieldset.",
|
|
48
|
-
"$ref": "http://json-schema.org/draft-07/schema#/definitions/stringArray"
|
|
49
|
-
},
|
|
50
|
-
"x-jsf-errorMessage": {
|
|
51
|
-
"type": "object",
|
|
52
|
-
"additionalProperties": false,
|
|
53
|
-
"properties": {
|
|
54
|
-
"type": {
|
|
55
|
-
"type": "string",
|
|
56
|
-
"description": "Message shown when the value is not of the correct type."
|
|
57
|
-
},
|
|
58
|
-
"required": {
|
|
59
|
-
"type": "string",
|
|
60
|
-
"description": "Message shown when the value is required and not provided."
|
|
61
|
-
},
|
|
62
|
-
"minimum": {
|
|
63
|
-
"type": "string",
|
|
64
|
-
"description": "Message shown when the value is less than the minimum value."
|
|
65
|
-
},
|
|
66
|
-
"minLength": {
|
|
67
|
-
"type": "string",
|
|
68
|
-
"description": "Message shown when the value is less than the minimum length."
|
|
69
|
-
},
|
|
70
|
-
"maximum": {
|
|
71
|
-
"type": "string",
|
|
72
|
-
"description": "Message shown when the value is greater than the maximum value."
|
|
73
|
-
},
|
|
74
|
-
"maxLength": {
|
|
75
|
-
"type": "string",
|
|
76
|
-
"description": "Message shown when the value is greater than the maximum length."
|
|
77
|
-
},
|
|
78
|
-
"pattern": {
|
|
79
|
-
"type": "string",
|
|
80
|
-
"description": "Message shown when the value is not the correct format and does not match the pattern property."
|
|
81
|
-
},
|
|
82
|
-
"maxFileSize": {
|
|
83
|
-
"type": "string",
|
|
84
|
-
"description": "Message shown when the file size is greater than the maximum file size."
|
|
85
|
-
},
|
|
86
|
-
"accept": {
|
|
87
|
-
"type": "string",
|
|
88
|
-
"description": "Message shown when the file type is not accepted."
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
"x-jsf-presentation": {
|
|
93
|
-
"type": "object",
|
|
94
|
-
"description": "Presentation overrides for the schema",
|
|
95
|
-
"properties": {
|
|
96
|
-
"inputType": {
|
|
97
|
-
"description": "Input type for the generated UI field",
|
|
98
|
-
"type": "string",
|
|
99
|
-
"anyOf": [
|
|
100
|
-
{
|
|
101
|
-
"const": "text",
|
|
102
|
-
"description": "Similar to the native HTML input with text type."
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"const": "select",
|
|
106
|
-
"description": "Similar to the native HTML select element."
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
"const": "radio",
|
|
110
|
-
"description": "Similar to a native HTML input with radio type."
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
"const": "number",
|
|
114
|
-
"description": "Similar to the native HTML input with number type."
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
"const": "date",
|
|
118
|
-
"description": "Expects a value with format YYY-MM-DD."
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
"const": "checkbox",
|
|
122
|
-
"description": "Similar to the native HTML input with checkbox type."
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"const": "email",
|
|
126
|
-
"description": "Similar to the native HTML input with email type."
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
"const": "file",
|
|
130
|
-
"description": "Similar to the native HTML input with file type."
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
"const": "fieldset",
|
|
134
|
-
"description": "Groups multiple Fields inside. Its expected value is a nested object."
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"const": "group-array",
|
|
138
|
-
"description": "A list of inputs that can be repeated. Its expected value is an array."
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"type": "string",
|
|
142
|
-
"description": "Any arbitrary custom inputType you might want to represent your UI Field."
|
|
143
|
-
}
|
|
144
|
-
]
|
|
145
|
-
},
|
|
146
|
-
"accept": {
|
|
147
|
-
"description": "For `inputType: \"file\"`. The accepted file types, as a comma separated string.",
|
|
148
|
-
"type": "string"
|
|
149
|
-
},
|
|
150
|
-
"description": {
|
|
151
|
-
"description": "Field description with HTML. If you don't need HTML, please use the native description keyword.",
|
|
152
|
-
"type": "string"
|
|
153
|
-
},
|
|
154
|
-
"statement": {
|
|
155
|
-
"description": "Special message about the field. Useful in cases where this message is based on the field value.",
|
|
156
|
-
"type": "object",
|
|
157
|
-
"properties": {
|
|
158
|
-
"title": {
|
|
159
|
-
"type": "string"
|
|
160
|
-
},
|
|
161
|
-
"description": {
|
|
162
|
-
"description": "The sentence itself. Might include HTML.",
|
|
163
|
-
"type": "string"
|
|
164
|
-
},
|
|
165
|
-
"severity": {
|
|
166
|
-
"description": "Defines the type of message.",
|
|
167
|
-
"anyOf": [
|
|
168
|
-
{ "type": "string", "const": "info" },
|
|
169
|
-
{ "type": "string", "const": "warning" },
|
|
170
|
-
{ "type": "string", "const": "error" },
|
|
171
|
-
{ "type": "string", "const": "success" },
|
|
172
|
-
{}
|
|
173
|
-
]
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
"maxFileSize": {
|
|
178
|
-
"description": "For `inputType: \"file\"`. The maximum file size in KB. It's used to enhanced Yup validation.",
|
|
179
|
-
"type": "number"
|
|
180
|
-
},
|
|
181
|
-
"addFieldText": {
|
|
182
|
-
"description": "Used in `group-array` fields. The button text to add a new field.",
|
|
183
|
-
"type": "string"
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
File without changes
|