@remoteoss/json-schema-form 0.7.1-dev.20231031082524 → 0.7.2-dev.20231106093837

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ #### 0.7.1-beta.0 (2023-10-31)
2
+
3
+ ##### Bug Fixes
4
+
5
+ - **select:** support string type ([#54](https://github.com/remoteoss/json-schema-form/pull/54)) ([b00f8776](https://github.com/remoteoss/json-schema-form/commit/b00f8776aa244803375ba64fbdd52c8fff9b9bd8))
6
+
1
7
  #### 0.7.0-beta.0 (2023-10-23)
2
8
 
3
9
  ##### Chores
@@ -27,10 +33,8 @@ BREAKING CHANGES:
27
33
 
28
34
  - json-logic: Add conditional logic checking. ([#41](https://github.com/remoteoss/json-schema-form/pull/41)) ([6292b01e](https://github.com/remoteoss/json-schema-form/commit/6292b01e3f77a9038328d7375080ffc4cb30dbc8))
29
35
 
30
-
31
36
  ###### Full API additions now supported from 0.6.5 onwards.
32
37
 
33
-
34
38
  - New custom JSON Schema keyword `x-jsf-logic` added to support cross-field validations. Built on top of [JsonLogic](https://jsonlogic.com/).
35
39
 
36
40
  - `x-jsf-logic` can contain:
@@ -49,16 +53,11 @@ While docs are underway, you can read examples from all the [tests](https://gith
49
53
 
50
54
  In short: `x-jsf-logic` is added to support complex conditional cross-field validations. Properties like `x-jsf-logic-validations` allow hooking those up to individual fields.
51
55
 
52
-
53
56
  #### 0.6.4-beta.0 (2023-09-15)
54
57
 
55
-
56
-
57
58
  ##### Changes
58
- - json-logic: computedAttrs - handle inline rules ([#40](https://github.com/remoteoss/json-schema-form/pull/40)) ([860ad91b](https://github.com/remoteoss/json-schema-form/commit/860ad91b034ab35d4d4bc51c0c04675f102bf278))
59
-
60
-
61
59
 
60
+ - json-logic: computedAttrs - handle inline rules ([#40](https://github.com/remoteoss/json-schema-form/pull/40)) ([860ad91b](https://github.com/remoteoss/json-schema-form/commit/860ad91b034ab35d4d4bc51c0c04675f102bf278))
62
61
 
63
62
  #### 0.6.1-beta.0 (2023-09-13)
64
63
 
@@ -66,50 +65,43 @@ In short: `x-jsf-logic` is added to support complex conditional cross-field vali
66
65
 
67
66
  - json-logic: Computed string based values ([#37](https://github.com/remoteoss/json-schema-form/pull/37)) ([6e042ea5](https://github.com/remoteoss/json-schema-form/commit/6e042ea579497ea573710c307a6ff7ee2f19b931))
68
67
 
69
-
70
-
71
-
72
68
  #### 0.5.0-beta.0 (2023-09-12)
73
69
 
74
70
  ##### Changes
71
+
75
72
  - json-logic: Computed Attributes ([#36](https://github.com/remoteoss/json-schema-form/pull/36)) ([80c29589](https://github.com/remoteoss/json-schema-form/commit/80c29589ac0972e0f33add70a59df15a46db1b43))
76
73
  - json-logic: Initial skeleton implementation ([#35](https://github.com/remoteoss/json-schema-form/pull/35)) ([63149ae8](https://github.com/remoteoss/json-schema-form/commit/63149ae863cf1b5ad76a3b2a49c7f343e55ce07b))
77
74
 
78
-
79
-
80
-
81
75
  #### 0.4.5-beta.0 (2023-08-31)
76
+
82
77
  ##### Bug fixes
83
78
 
84
79
  - Validate values based on `const` key ([#34](https://github.com/remoteoss/json-schema-form/pull/34)) ([bf07870d](https://github.com/remoteoss/json-schema-form/commit/bf07870d407d9b9b078882a078b9e4c7928df868))
85
80
 
86
-
87
-
88
-
89
81
  #### 0.4.4-beta.0 (2023-08-30)
90
82
 
91
83
  ##### Chores
92
84
 
93
- * **fieldset:** ignore values not matching the field type ([#44](https://github.com/remoteoss/json-schema-form/pull/44)) ([f0af54e5](https://github.com/remoteoss/json-schema-form/commit/f0af54e5d425fb78524ab150bb31629d00369a61))
85
+ - **fieldset:** ignore values not matching the field type ([#44](https://github.com/remoteoss/json-schema-form/pull/44)) ([f0af54e5](https://github.com/remoteoss/json-schema-form/commit/f0af54e5d425fb78524ab150bb31629d00369a61))
94
86
 
95
87
  #### 0.4.3-beta.0 (2023-08-09)
96
88
 
97
89
  ##### Bug fixes
98
90
 
99
- * **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))
91
+ - **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))
100
92
 
101
93
  #### 0.4.2-beta.0 (2023-07-20)
102
94
 
103
95
  ##### Bug Fixes
104
96
 
105
- * **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))
97
+ - **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))
106
98
 
107
99
  #### 0.4.1-beta.0 (2023-07-03)
108
100
 
109
101
  ##### Bug Fixes
110
102
 
111
- * **fieldset:** support root conditionals for fieldsets ([#23](https://github.com/remoteoss/json-schema-form/pull/23)) ([65d87b3a](https://github.com/remoteoss/json-schema-form/commit/65d87b3a93018f0729aed565000eb2a2ce1f2ce7))
112
- * **select/radio:** Accept just the values in options (plus `''` and `null` for backward-compatibility) ([#18](https://github.com/remoteoss/json-schema-form/pull/18)) ([37501d2d](https://github.com/remoteoss/json-schema-form/commit/37501d2ddafdd5e207b34d2ca3f6b7b7a1006e9d))
103
+ - **fieldset:** support root conditionals for fieldsets ([#23](https://github.com/remoteoss/json-schema-form/pull/23)) ([65d87b3a](https://github.com/remoteoss/json-schema-form/commit/65d87b3a93018f0729aed565000eb2a2ce1f2ce7))
104
+ - **select/radio:** Accept just the values in options (plus `''` and `null` for backward-compatibility) ([#18](https://github.com/remoteoss/json-schema-form/pull/18)) ([37501d2d](https://github.com/remoteoss/json-schema-form/commit/37501d2ddafdd5e207b34d2ca3f6b7b7a1006e9d))
113
105
 
114
106
  #### 0.4.0-beta.0 (2023-06-22)
115
107
 
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.7.1-dev.20231031082524
5
- Generated: Tue, 31 Oct 2023 08:25:53 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.7.2-dev.20231106093837
5
+ Generated: Mon, 06 Nov 2023 09:38:54 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -1037,6 +1037,33 @@ function processJSONLogicNode({
1037
1037
  }
1038
1038
 
1039
1039
  // src/helpers.js
1040
+ var dynamicInternalJsfAttrs = [
1041
+ "isVisible",
1042
+ // Driven from conditionals state
1043
+ "fields",
1044
+ // driven from group-array
1045
+ "getComputedAttributes",
1046
+ // From json-logic
1047
+ "computedAttributes",
1048
+ // From json-logic
1049
+ "calculateConditionalProperties",
1050
+ // driven from conditionals
1051
+ "calculateCustomValidationProperties",
1052
+ // To be deprecated in favor of json-logic
1053
+ "scopedJsonSchema"
1054
+ // The respective JSON Schema
1055
+ ];
1056
+ var dynamicInternalJsfAttrsObj = Object.fromEntries(
1057
+ dynamicInternalJsfAttrs.map((k) => [k, true])
1058
+ );
1059
+ function removeConditionalStaleAttributes(field, conditionalAttrs, rootAttrs) {
1060
+ Object.keys(field).forEach((key) => {
1061
+ if (conditionalAttrs[key] === void 0 && rootAttrs[key] === void 0 && // Don't remove attrs that were declared in the root field.
1062
+ dynamicInternalJsfAttrsObj[key] === void 0) {
1063
+ field[key] = void 0;
1064
+ }
1065
+ });
1066
+ }
1040
1067
  function hasType(type, typeName) {
1041
1068
  return Array.isArray(type) ? type.includes(typeName) : type === typeName;
1042
1069
  }
@@ -1129,19 +1156,21 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1129
1156
  if (fieldIsRequired) {
1130
1157
  field.isVisible = true;
1131
1158
  }
1132
- const updateValues = (fieldValues) => Object.entries(fieldValues).forEach(([key, value]) => {
1133
- field[key] = typeof value === "function" ? value() : value;
1134
- if (key === "value") {
1135
- const readOnlyPropertyWasUpdated = typeof fieldValues.readOnly !== "undefined";
1136
- const isReadonlyByDefault = field.readOnly;
1137
- const isReadonly = readOnlyPropertyWasUpdated ? fieldValues.readOnly : isReadonlyByDefault;
1138
- if (!isReadonly && (value === null || field.inputType === "checkbox")) {
1139
- field.value = void 0;
1159
+ const updateAttributes = (fieldAttrs) => {
1160
+ Object.entries(fieldAttrs).forEach(([key, value]) => {
1161
+ field[key] = typeof value === "function" ? value() : value;
1162
+ if (key === "value") {
1163
+ const readOnlyPropertyWasUpdated = typeof fieldAttrs.readOnly !== "undefined";
1164
+ const isReadonlyByDefault = field.readOnly;
1165
+ const isReadonly = readOnlyPropertyWasUpdated ? fieldAttrs.readOnly : isReadonlyByDefault;
1166
+ if (!isReadonly && (value === null || field.inputType === "checkbox")) {
1167
+ field.value = void 0;
1168
+ }
1140
1169
  }
1141
- }
1142
- });
1170
+ });
1171
+ };
1143
1172
  if (field.getComputedAttributes) {
1144
- const computedFieldValues = field.getComputedAttributes({
1173
+ const newAttributes = field.getComputedAttributes({
1145
1174
  field,
1146
1175
  isRequired: fieldIsRequired,
1147
1176
  node,
@@ -1149,23 +1178,24 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1149
1178
  config,
1150
1179
  logic
1151
1180
  });
1152
- updateValues(computedFieldValues);
1181
+ updateAttributes(newAttributes);
1153
1182
  }
1154
1183
  if (field.calculateConditionalProperties) {
1155
- const newFieldValues = field.calculateConditionalProperties({
1184
+ const { rootFieldAttrs, newAttributes } = field.calculateConditionalProperties({
1156
1185
  isRequired: fieldIsRequired,
1157
1186
  conditionBranch: node,
1158
1187
  formValues
1159
1188
  });
1160
- updateValues(newFieldValues);
1189
+ updateAttributes(newAttributes, rootFieldAttrs);
1190
+ removeConditionalStaleAttributes(field, newAttributes, rootFieldAttrs);
1161
1191
  }
1162
1192
  if (field.calculateCustomValidationProperties) {
1163
- const newFieldValues = field.calculateCustomValidationProperties(
1193
+ const newAttributes = field.calculateCustomValidationProperties(
1164
1194
  fieldIsRequired,
1165
1195
  node,
1166
1196
  formValues
1167
1197
  );
1168
- updateValues(newFieldValues);
1198
+ updateAttributes(newAttributes);
1169
1199
  }
1170
1200
  }
1171
1201
  function processNode({
@@ -1507,17 +1537,23 @@ function calculateConditionalProperties({ fieldParams, customProperties, logic,
1507
1537
  logic
1508
1538
  )
1509
1539
  };
1510
- return (0, import_omit2.default)((0, import_merge2.default)(base, presentation, newFieldParams), ["inputType"]);
1540
+ return {
1541
+ rootFieldAttrs: fieldParams,
1542
+ newAttributes: (0, import_omit2.default)((0, import_merge2.default)(base, presentation, newFieldParams), ["inputType"])
1543
+ };
1511
1544
  }
1512
1545
  const isVisible = isRequired;
1513
1546
  return {
1514
- isVisible,
1515
- required: isRequired,
1516
- schema: buildYupSchema({
1517
- ...fieldParams,
1518
- ...extractParametersFromNode(conditionBranch),
1519
- required: isRequired
1520
- })
1547
+ rootFieldAttrs: fieldParams,
1548
+ newAttributes: {
1549
+ isVisible,
1550
+ required: isRequired,
1551
+ schema: buildYupSchema({
1552
+ ...fieldParams,
1553
+ ...extractParametersFromNode(conditionBranch),
1554
+ required: isRequired
1555
+ })
1556
+ }
1521
1557
  };
1522
1558
  };
1523
1559
  }
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.7.1-dev.20231031082524
5
- Generated: Tue, 31 Oct 2023 08:25:53 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.7.2-dev.20231106093837
5
+ Generated: Mon, 06 Nov 2023 09:38:54 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -1001,6 +1001,33 @@ function processJSONLogicNode({
1001
1001
  }
1002
1002
 
1003
1003
  // src/helpers.js
1004
+ var dynamicInternalJsfAttrs = [
1005
+ "isVisible",
1006
+ // Driven from conditionals state
1007
+ "fields",
1008
+ // driven from group-array
1009
+ "getComputedAttributes",
1010
+ // From json-logic
1011
+ "computedAttributes",
1012
+ // From json-logic
1013
+ "calculateConditionalProperties",
1014
+ // driven from conditionals
1015
+ "calculateCustomValidationProperties",
1016
+ // To be deprecated in favor of json-logic
1017
+ "scopedJsonSchema"
1018
+ // The respective JSON Schema
1019
+ ];
1020
+ var dynamicInternalJsfAttrsObj = Object.fromEntries(
1021
+ dynamicInternalJsfAttrs.map((k) => [k, true])
1022
+ );
1023
+ function removeConditionalStaleAttributes(field, conditionalAttrs, rootAttrs) {
1024
+ Object.keys(field).forEach((key) => {
1025
+ if (conditionalAttrs[key] === void 0 && rootAttrs[key] === void 0 && // Don't remove attrs that were declared in the root field.
1026
+ dynamicInternalJsfAttrsObj[key] === void 0) {
1027
+ field[key] = void 0;
1028
+ }
1029
+ });
1030
+ }
1004
1031
  function hasType(type, typeName) {
1005
1032
  return Array.isArray(type) ? type.includes(typeName) : type === typeName;
1006
1033
  }
@@ -1093,19 +1120,21 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1093
1120
  if (fieldIsRequired) {
1094
1121
  field.isVisible = true;
1095
1122
  }
1096
- const updateValues = (fieldValues) => Object.entries(fieldValues).forEach(([key, value]) => {
1097
- field[key] = typeof value === "function" ? value() : value;
1098
- if (key === "value") {
1099
- const readOnlyPropertyWasUpdated = typeof fieldValues.readOnly !== "undefined";
1100
- const isReadonlyByDefault = field.readOnly;
1101
- const isReadonly = readOnlyPropertyWasUpdated ? fieldValues.readOnly : isReadonlyByDefault;
1102
- if (!isReadonly && (value === null || field.inputType === "checkbox")) {
1103
- field.value = void 0;
1123
+ const updateAttributes = (fieldAttrs) => {
1124
+ Object.entries(fieldAttrs).forEach(([key, value]) => {
1125
+ field[key] = typeof value === "function" ? value() : value;
1126
+ if (key === "value") {
1127
+ const readOnlyPropertyWasUpdated = typeof fieldAttrs.readOnly !== "undefined";
1128
+ const isReadonlyByDefault = field.readOnly;
1129
+ const isReadonly = readOnlyPropertyWasUpdated ? fieldAttrs.readOnly : isReadonlyByDefault;
1130
+ if (!isReadonly && (value === null || field.inputType === "checkbox")) {
1131
+ field.value = void 0;
1132
+ }
1104
1133
  }
1105
- }
1106
- });
1134
+ });
1135
+ };
1107
1136
  if (field.getComputedAttributes) {
1108
- const computedFieldValues = field.getComputedAttributes({
1137
+ const newAttributes = field.getComputedAttributes({
1109
1138
  field,
1110
1139
  isRequired: fieldIsRequired,
1111
1140
  node,
@@ -1113,23 +1142,24 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
1113
1142
  config,
1114
1143
  logic
1115
1144
  });
1116
- updateValues(computedFieldValues);
1145
+ updateAttributes(newAttributes);
1117
1146
  }
1118
1147
  if (field.calculateConditionalProperties) {
1119
- const newFieldValues = field.calculateConditionalProperties({
1148
+ const { rootFieldAttrs, newAttributes } = field.calculateConditionalProperties({
1120
1149
  isRequired: fieldIsRequired,
1121
1150
  conditionBranch: node,
1122
1151
  formValues
1123
1152
  });
1124
- updateValues(newFieldValues);
1153
+ updateAttributes(newAttributes, rootFieldAttrs);
1154
+ removeConditionalStaleAttributes(field, newAttributes, rootFieldAttrs);
1125
1155
  }
1126
1156
  if (field.calculateCustomValidationProperties) {
1127
- const newFieldValues = field.calculateCustomValidationProperties(
1157
+ const newAttributes = field.calculateCustomValidationProperties(
1128
1158
  fieldIsRequired,
1129
1159
  node,
1130
1160
  formValues
1131
1161
  );
1132
- updateValues(newFieldValues);
1162
+ updateAttributes(newAttributes);
1133
1163
  }
1134
1164
  }
1135
1165
  function processNode({
@@ -1471,17 +1501,23 @@ function calculateConditionalProperties({ fieldParams, customProperties, logic,
1471
1501
  logic
1472
1502
  )
1473
1503
  };
1474
- return omit2(merge2(base, presentation, newFieldParams), ["inputType"]);
1504
+ return {
1505
+ rootFieldAttrs: fieldParams,
1506
+ newAttributes: omit2(merge2(base, presentation, newFieldParams), ["inputType"])
1507
+ };
1475
1508
  }
1476
1509
  const isVisible = isRequired;
1477
1510
  return {
1478
- isVisible,
1479
- required: isRequired,
1480
- schema: buildYupSchema({
1481
- ...fieldParams,
1482
- ...extractParametersFromNode(conditionBranch),
1483
- required: isRequired
1484
- })
1511
+ rootFieldAttrs: fieldParams,
1512
+ newAttributes: {
1513
+ isVisible,
1514
+ required: isRequired,
1515
+ schema: buildYupSchema({
1516
+ ...fieldParams,
1517
+ ...extractParametersFromNode(conditionBranch),
1518
+ required: isRequired
1519
+ })
1520
+ }
1485
1521
  };
1486
1522
  };
1487
1523
  }
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
3
  Copyright (c) 2023 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.7.1-dev.20231031082524
5
- Generated: Tue, 31 Oct 2023 08:25:53 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.7.2-dev.20231106093837
5
+ Generated: Mon, 06 Nov 2023 09:38:54 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -12355,6 +12355,33 @@ function processJSONLogicNode({
12355
12355
  }
12356
12356
 
12357
12357
  // src/helpers.js
12358
+ var dynamicInternalJsfAttrs = [
12359
+ "isVisible",
12360
+ // Driven from conditionals state
12361
+ "fields",
12362
+ // driven from group-array
12363
+ "getComputedAttributes",
12364
+ // From json-logic
12365
+ "computedAttributes",
12366
+ // From json-logic
12367
+ "calculateConditionalProperties",
12368
+ // driven from conditionals
12369
+ "calculateCustomValidationProperties",
12370
+ // To be deprecated in favor of json-logic
12371
+ "scopedJsonSchema"
12372
+ // The respective JSON Schema
12373
+ ];
12374
+ var dynamicInternalJsfAttrsObj = Object.fromEntries(
12375
+ dynamicInternalJsfAttrs.map((k) => [k, true])
12376
+ );
12377
+ function removeConditionalStaleAttributes(field, conditionalAttrs, rootAttrs) {
12378
+ Object.keys(field).forEach((key) => {
12379
+ if (conditionalAttrs[key] === void 0 && rootAttrs[key] === void 0 && // Don't remove attrs that were declared in the root field.
12380
+ dynamicInternalJsfAttrsObj[key] === void 0) {
12381
+ field[key] = void 0;
12382
+ }
12383
+ });
12384
+ }
12358
12385
  function hasType(type, typeName) {
12359
12386
  return Array.isArray(type) ? type.includes(typeName) : type === typeName;
12360
12387
  }
@@ -12447,19 +12474,21 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
12447
12474
  if (fieldIsRequired) {
12448
12475
  field.isVisible = true;
12449
12476
  }
12450
- const updateValues = (fieldValues) => Object.entries(fieldValues).forEach(([key, value]) => {
12451
- field[key] = typeof value === "function" ? value() : value;
12452
- if (key === "value") {
12453
- const readOnlyPropertyWasUpdated = typeof fieldValues.readOnly !== "undefined";
12454
- const isReadonlyByDefault = field.readOnly;
12455
- const isReadonly = readOnlyPropertyWasUpdated ? fieldValues.readOnly : isReadonlyByDefault;
12456
- if (!isReadonly && (value === null || field.inputType === "checkbox")) {
12457
- field.value = void 0;
12477
+ const updateAttributes = (fieldAttrs) => {
12478
+ Object.entries(fieldAttrs).forEach(([key, value]) => {
12479
+ field[key] = typeof value === "function" ? value() : value;
12480
+ if (key === "value") {
12481
+ const readOnlyPropertyWasUpdated = typeof fieldAttrs.readOnly !== "undefined";
12482
+ const isReadonlyByDefault = field.readOnly;
12483
+ const isReadonly = readOnlyPropertyWasUpdated ? fieldAttrs.readOnly : isReadonlyByDefault;
12484
+ if (!isReadonly && (value === null || field.inputType === "checkbox")) {
12485
+ field.value = void 0;
12486
+ }
12458
12487
  }
12459
- }
12460
- });
12488
+ });
12489
+ };
12461
12490
  if (field.getComputedAttributes) {
12462
- const computedFieldValues = field.getComputedAttributes({
12491
+ const newAttributes = field.getComputedAttributes({
12463
12492
  field,
12464
12493
  isRequired: fieldIsRequired,
12465
12494
  node,
@@ -12467,23 +12496,24 @@ function updateField(field, requiredFields, node, formValues, logic, config) {
12467
12496
  config,
12468
12497
  logic
12469
12498
  });
12470
- updateValues(computedFieldValues);
12499
+ updateAttributes(newAttributes);
12471
12500
  }
12472
12501
  if (field.calculateConditionalProperties) {
12473
- const newFieldValues = field.calculateConditionalProperties({
12502
+ const { rootFieldAttrs, newAttributes } = field.calculateConditionalProperties({
12474
12503
  isRequired: fieldIsRequired,
12475
12504
  conditionBranch: node,
12476
12505
  formValues
12477
12506
  });
12478
- updateValues(newFieldValues);
12507
+ updateAttributes(newAttributes, rootFieldAttrs);
12508
+ removeConditionalStaleAttributes(field, newAttributes, rootFieldAttrs);
12479
12509
  }
12480
12510
  if (field.calculateCustomValidationProperties) {
12481
- const newFieldValues = field.calculateCustomValidationProperties(
12511
+ const newAttributes = field.calculateCustomValidationProperties(
12482
12512
  fieldIsRequired,
12483
12513
  node,
12484
12514
  formValues
12485
12515
  );
12486
- updateValues(newFieldValues);
12516
+ updateAttributes(newAttributes);
12487
12517
  }
12488
12518
  }
12489
12519
  function processNode({
@@ -12825,17 +12855,23 @@ function calculateConditionalProperties({ fieldParams, customProperties, logic,
12825
12855
  logic
12826
12856
  )
12827
12857
  };
12828
- return (0, import_omit2.default)((0, import_merge2.default)(base, presentation, newFieldParams), ["inputType"]);
12858
+ return {
12859
+ rootFieldAttrs: fieldParams,
12860
+ newAttributes: (0, import_omit2.default)((0, import_merge2.default)(base, presentation, newFieldParams), ["inputType"])
12861
+ };
12829
12862
  }
12830
12863
  const isVisible = isRequired;
12831
12864
  return {
12832
- isVisible,
12833
- required: isRequired,
12834
- schema: buildYupSchema({
12835
- ...fieldParams,
12836
- ...extractParametersFromNode(conditionBranch),
12837
- required: isRequired
12838
- })
12865
+ rootFieldAttrs: fieldParams,
12866
+ newAttributes: {
12867
+ isVisible,
12868
+ required: isRequired,
12869
+ schema: buildYupSchema({
12870
+ ...fieldParams,
12871
+ ...extractParametersFromNode(conditionBranch),
12872
+ required: isRequired
12873
+ })
12874
+ }
12839
12875
  };
12840
12876
  };
12841
12877
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remoteoss/json-schema-form",
3
- "version": "0.7.1-dev.20231031082524",
3
+ "version": "0.7.2-dev.20231106093837",
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,12 +27,7 @@ it('Should allow check of a nested property in a conditional', () => {
27
27
  additionalProperties: false,
28
28
  properties: {
29
29
  child: {
30
- oneOf: [
31
- {
32
- const: 'yes',
33
- },
34
- { const: 'no' },
35
- ],
30
+ oneOf: [{ const: 'yes' }, { const: 'no' }],
36
31
  type: 'string',
37
32
  },
38
33
  },
@@ -56,3 +51,311 @@ it('Should allow check of a nested property in a conditional', () => {
56
51
  undefined
57
52
  );
58
53
  });
54
+
55
+ describe('Conditional attributes updated', () => {
56
+ it('Update basic case with const, default, maximum', () => {
57
+ const { fields, handleValidation } = createHeadlessForm(
58
+ {
59
+ properties: {
60
+ is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
61
+ hours: { type: 'number' },
62
+ },
63
+ allOf: [
64
+ {
65
+ if: {
66
+ properties: { is_full_time: { const: 'yes' } },
67
+ required: ['is_full_time'],
68
+ },
69
+ then: {
70
+ properties: {
71
+ hours: {
72
+ const: 8,
73
+ default: 8,
74
+ },
75
+ },
76
+ },
77
+ else: {
78
+ properties: {
79
+ hours: {
80
+ maximum: 4,
81
+ },
82
+ },
83
+ },
84
+ },
85
+ ],
86
+ },
87
+ { strictInputType: false }
88
+ );
89
+
90
+ // Given "Yes" it applies "const" and "default"
91
+ expect(handleValidation({ is_full_time: 'yes', hours: 4 }).formErrors).toEqual({
92
+ hours: 'The only accepted value is 8.',
93
+ });
94
+ expect(fields[1]).toMatchObject({ const: 8, default: 8 });
95
+ expect(fields[1].maximum).toBeUndefined();
96
+
97
+ // Changing to "No", applies the "maximum" and cleans "const" and "default"
98
+ expect(handleValidation({ is_full_time: 'no', hours: 4 }).formErrors).toBeUndefined();
99
+ expect(fields[1]).toMatchObject({ maximum: 4 });
100
+ expect(fields[1].const).toBeUndefined();
101
+ expect(fields[1].default).toBeUndefined();
102
+
103
+ // Changing back to "Yes", it removes "maximum", and applies "const" and "default"
104
+ expect(handleValidation({}).formErrors).toBeUndefined();
105
+ expect(handleValidation({ is_full_time: 'yes', hours: 8 }).formErrors).toBeUndefined();
106
+ expect(fields[1].maximum).toBeUndefined();
107
+ expect(fields[1]).toMatchObject({ const: 8, default: 8 });
108
+ });
109
+
110
+ it('Update a new attribute (eg description)', () => {
111
+ const { fields, handleValidation } = createHeadlessForm(
112
+ {
113
+ properties: {
114
+ is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
115
+ hours: {
116
+ type: 'number',
117
+ },
118
+ },
119
+ allOf: [
120
+ {
121
+ if: {
122
+ properties: { is_full_time: { const: 'yes' } },
123
+ required: ['is_full_time'],
124
+ },
125
+ then: {
126
+ properties: {
127
+ hours: {
128
+ description: 'We recommend 8 hours.',
129
+ },
130
+ },
131
+ },
132
+ },
133
+ ],
134
+ },
135
+ { strictInputType: false }
136
+ );
137
+
138
+ // By default the attribute is not set.
139
+ expect(fields[1].description).toBeUndefined();
140
+
141
+ // Given "Yes" it applies the conditional attribute
142
+ expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
143
+ expect(fields[1].description).toBe('We recommend 8 hours.');
144
+
145
+ // Changing to "No", removes the description
146
+ expect(handleValidation({ is_full_time: 'no' }).formErrors).toBeUndefined();
147
+ expect(fields[1].description).toBeUndefined();
148
+
149
+ // Changing back to "Yes", it sets the attribute again
150
+ expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
151
+ expect(fields[1].description).toBe('We recommend 8 hours.');
152
+ });
153
+
154
+ it('Update an existing attribute (eg description)', () => {
155
+ const { fields, handleValidation } = createHeadlessForm(
156
+ {
157
+ properties: {
158
+ is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
159
+ hours: {
160
+ type: 'number',
161
+ description: 'Any value works.',
162
+ },
163
+ },
164
+ allOf: [
165
+ {
166
+ if: {
167
+ properties: { is_full_time: { const: 'yes' } },
168
+ required: ['is_full_time'],
169
+ },
170
+ then: {
171
+ properties: {
172
+ hours: {
173
+ description: 'We recommend 8 hours.',
174
+ },
175
+ },
176
+ },
177
+ },
178
+ ],
179
+ },
180
+ { strictInputType: false }
181
+ );
182
+
183
+ // By default the attribute is set the base value.
184
+ expect(fields[1].description).toBe('Any value works.');
185
+
186
+ // Given "Yes" it applies the conditional attribute
187
+ expect(handleValidation({ is_full_time: 'yes', hours: 4 }).formErrors).toBeUndefined();
188
+ expect(fields[1].description).toBe('We recommend 8 hours.');
189
+
190
+ // Changing to "No", it applies the base value again.
191
+ expect(handleValidation({ is_full_time: 'no', hours: 4 }).formErrors).toBeUndefined();
192
+ expect(fields[1].description).toBe('Any value works.');
193
+
194
+ // Changing back to "Yes", it sets the attribute again
195
+ expect(handleValidation({ is_full_time: 'yes', hours: 8 }).formErrors).toBeUndefined();
196
+ expect(fields[1].description).toBe('We recommend 8 hours.');
197
+ });
198
+
199
+ it('Update a nested attribute', () => {
200
+ const { fields, handleValidation } = createHeadlessForm(
201
+ {
202
+ properties: {
203
+ is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
204
+ hours: {
205
+ type: 'number',
206
+ presentation: {
207
+ inputType: 'number',
208
+ anything: 'info',
209
+ },
210
+ },
211
+ },
212
+ allOf: [
213
+ {
214
+ if: {
215
+ properties: { is_full_time: { const: 'yes' } },
216
+ required: ['is_full_time'],
217
+ },
218
+ then: {
219
+ properties: {
220
+ hours: {
221
+ presentation: {
222
+ anything: 'danger',
223
+ },
224
+ },
225
+ },
226
+ },
227
+ },
228
+ ],
229
+ },
230
+ { strictInputType: false }
231
+ );
232
+
233
+ // By default the attribute is set the base value.
234
+ expect(fields[1].anything).toBe('info');
235
+
236
+ // Given "Yes" it applies the conditional attribute
237
+ expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
238
+ expect(fields[1].anything).toBe('danger');
239
+
240
+ // Changing to "No", it applies the base value again.
241
+ expect(handleValidation({ is_full_time: 'no' }).formErrors).toBeUndefined();
242
+ expect(fields[1].anything).toBe('info');
243
+
244
+ // Changing back to "Yes", it sets the attribute again
245
+ expect(handleValidation({ is_full_time: 'yes' }).formErrors).toBeUndefined();
246
+ expect(fields[1].anything).toBe('danger');
247
+ });
248
+
249
+ it('Keeps existing attributes in matches that dont change the attr', () => {
250
+ const { fields, handleValidation } = createHeadlessForm(
251
+ {
252
+ properties: {
253
+ is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
254
+ hours: {
255
+ type: 'number',
256
+ description: 'Any value works.',
257
+ },
258
+ },
259
+ allOf: [
260
+ {
261
+ if: {
262
+ properties: { is_full_time: { const: 'yes' } },
263
+ required: ['is_full_time'],
264
+ },
265
+ then: {
266
+ required: ['hours'],
267
+ },
268
+ else: {
269
+ properties: {
270
+ hours: false,
271
+ },
272
+ },
273
+ },
274
+ ],
275
+ },
276
+ { strictInputType: false }
277
+ );
278
+
279
+ // By default the attribute is set the base value, even though the field is invisible.
280
+ expect(fields[1].description).toBe('Any value works.');
281
+ expect(fields[1].isVisible).toBe(false);
282
+
283
+ // Given "Yes" it keeps the base value
284
+ expect(handleValidation({ is_full_time: 'yes' }).formErrors).toEqual({
285
+ hours: 'Required field',
286
+ });
287
+ expect(fields[1].description).toBe('Any value works.');
288
+ expect(fields[1].isVisible).toBe(true);
289
+
290
+ // Changing to "No" it keeps the base value
291
+ expect(handleValidation({ is_full_time: 'no' }).formErrors).toBeUndefined();
292
+ expect(fields[1].description).toBe('Any value works.');
293
+ expect(fields[1].isVisible).toBe(false);
294
+ });
295
+
296
+ it('Keeps internal attributes (fieldAttrsFromJsf)', () => {
297
+ // This is necessary while we keep supporting "type", even if deprecated
298
+ // otherwise our Remote app will break because it didn't migrate
299
+ // from "type" to "inputType" yet.
300
+ const { fields, handleValidation } = createHeadlessForm(
301
+ {
302
+ properties: {
303
+ is_full_time: { type: 'string', oneOf: [{ const: 'yes' }, { const: 'no' }] },
304
+ salary_period: {
305
+ type: 'string',
306
+ title: 'Salary period',
307
+ oneOf: [
308
+ { title: 'Weekly', const: 'weekly' },
309
+ { title: 'Monthly', const: 'monthly' },
310
+ ],
311
+ },
312
+ },
313
+ allOf: [
314
+ {
315
+ if: {
316
+ properties: { is_full_time: { const: 'yes' } },
317
+ required: ['is_full_time'],
318
+ },
319
+ then: {
320
+ properties: {
321
+ salary_period: {
322
+ description: 'We recommend montlhy.',
323
+ },
324
+ },
325
+ },
326
+ },
327
+ ],
328
+ },
329
+ { strictInputType: false }
330
+ );
331
+
332
+ // Given "Yes" it keeps the "type"
333
+ handleValidation({ is_full_time: 'yes' });
334
+
335
+ // All the following attrs are never removed
336
+ // during conditionals because they are core.
337
+ expect(fields[1]).toMatchObject({
338
+ name: 'salary_period',
339
+ label: 'Salary period',
340
+ required: false,
341
+ type: 'radio',
342
+ inputType: 'radio',
343
+ jsonType: 'string',
344
+ computedAttributes: {},
345
+ calculateConditionalProperties: expect.any(Function),
346
+ schema: expect.any(Object),
347
+ scopedJsonSchema: expect.any(Object),
348
+ isVisible: true,
349
+ options: [
350
+ {
351
+ label: 'Weekly',
352
+ value: 'weekly',
353
+ },
354
+ {
355
+ label: 'Monthly',
356
+ value: 'monthly',
357
+ },
358
+ ],
359
+ });
360
+ });
361
+ });
@@ -445,7 +445,7 @@ describe('createHeadlessForm', () => {
445
445
  [fieldName]: 'The option "blah-blah" is not valid.',
446
446
  });
447
447
 
448
- // Given undefined, it says it's a required field.
448
+ // Given undefined, it says it's a required field.
449
449
  expect(validateForm({})).toEqual({
450
450
  [fieldName]: 'Required field',
451
451
  });
@@ -855,6 +855,7 @@ describe('createHeadlessForm', () => {
855
855
  has_car: 'yes',
856
856
  })
857
857
  ).toBeUndefined();
858
+
858
859
  expect(validateForm({})).toEqual({
859
860
  has_siblings: 'Required field',
860
861
  });
@@ -1418,6 +1419,50 @@ describe('createHeadlessForm', () => {
1418
1419
  ],
1419
1420
  });
1420
1421
  });
1422
+
1423
+ it('can be a conditional field', () => {
1424
+ const { fields, handleValidation } = createHeadlessForm({
1425
+ properties: {
1426
+ yes_or_no: {
1427
+ title: 'Show the dependents or not?',
1428
+ oneOf: [{ const: 'yes' }, { const: 'no' }],
1429
+ 'x-jsf-presentation': { inputType: 'radio' },
1430
+ },
1431
+ dependent_details: mockGroupArrayInput,
1432
+ },
1433
+ allOf: [
1434
+ {
1435
+ if: {
1436
+ properties: {
1437
+ yes_or_no: { const: 'yes' },
1438
+ },
1439
+ required: ['yes_or_no'],
1440
+ },
1441
+ then: {
1442
+ required: ['dependent_details'],
1443
+ },
1444
+ else: {
1445
+ properties: {
1446
+ dependent_details: false,
1447
+ },
1448
+ },
1449
+ },
1450
+ ],
1451
+ });
1452
+
1453
+ // By default is hidden but the fields are accessible
1454
+ expect(getField(fields, 'dependent_details').isVisible).toBe(false);
1455
+ expect(getField(fields, 'dependent_details').fields).toEqual(expect.any(Function));
1456
+
1457
+ // When the condition matches...
1458
+ const { formErrors } = handleValidation({ yes_or_no: 'yes' });
1459
+ expect(formErrors).toEqual({
1460
+ dependent_details: 'Required field',
1461
+ });
1462
+ // it gets visible with its inner fields.
1463
+ expect(getField(fields, 'dependent_details').isVisible).toBe(true);
1464
+ expect(getField(fields, 'dependent_details').fields).toEqual(expect.any(Function));
1465
+ });
1421
1466
  });
1422
1467
 
1423
1468
  it('supports "radio" field type with its "card" and "card-expandable" variants', () => {
@@ -1762,11 +1807,8 @@ describe('createHeadlessForm', () => {
1762
1807
  expect(foodField.options).toHaveLength(4);
1763
1808
  // ...Food description was back to the original
1764
1809
  expect(foodField.description).toBeUndefined();
1765
-
1766
- // @BUG RMT-58 PTO description should disappear, but it didn't.
1767
- expect(getField(fields, 'pto').description).toBe(
1768
- 'Above 30 hours, the PTO needs to be at least 20 days.'
1769
- );
1810
+ // ...PTO Description is removed too.
1811
+ expect(getField(fields, 'pto').description).toBeUndefined();
1770
1812
 
1771
1813
  // Given again "low perks", the form valid.
1772
1814
  expect(