@ukhomeoffice/cop-react-form-renderer 6.15.8-alpha → 6.15.8
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/components/CollectionSummary/BannerStrip.scss +4 -0
- package/dist/components/FormComponent/FormComponent.js +6 -0
- package/dist/components/FormComponent/helpers/addLabel.js +3 -2
- package/dist/components/FormPage/FormPage.js +28 -17
- package/dist/components/FormPage/FormPage.test.js +53 -0
- package/dist/components/FormRenderer/FormRenderer.js +5 -10
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/cop-reassign-task-to-rcc.json +445 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-page-nested-radio-component.json +200 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-page-same-component-reused-one-shown.json +74 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/cop-airpax-carrier.json +407 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-page-nested-radio-component.json +45 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-page-same-component-reused-one-shown.json +8 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/reassign-to-rcc.json +72 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-page-nested-radio-component-removed.json +45 -0
- package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-page-same-component-reused-one-shown-removed.json +7 -0
- package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.js +248 -171
- package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.test.js +43 -7
- package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutesUtils.js +139 -40
- package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutesUtils.test.js +64 -7
- package/dist/components/FormRenderer/onCYAAction.js +0 -2
- package/dist/components/FormRenderer/onCYAAction.test.js +5 -0
- package/dist/components/FormRenderer/onPageAction.js +0 -1
- package/dist/hooks/useGetRequest.js +15 -15
- package/dist/hooks/useRefData.js +3 -2
- package/dist/utils/Component/getComponentTests/getComponent.multifile.test.js +2 -1
- package/dist/utils/Component/getDefaultValueFromConfig.js +2 -1
- package/dist/utils/Condition/meetsCondition.js +26 -12
- package/dist/utils/Condition/meetsCondition.test.js +21 -0
- package/dist/utils/Data/getAutocompleteSource.js +68 -51
- package/dist/utils/Data/getAutocompleteSource.test.js +31 -18
- package/dist/utils/Operate/doesContainValue.js +34 -0
- package/dist/utils/Operate/doesContainValue.test.js +75 -0
- package/dist/utils/Operate/runPageOperations.js +2 -0
- package/dist/utils/Validate/validateOnPageLoad.js +23 -0
- package/dist/utils/Validate/validateOnPageLoad.test.js +88 -0
- package/package.json +5 -4
|
@@ -3,28 +3,43 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.toArray = exports.removeObjectWithOnlySingleIdField = exports.removeEmptyArraysAndUnusedCollectionIDs = exports.pruneCollectionEntry = exports.isShowEntity = exports.getNestedQuestionPath = exports.getImmediateParent = exports.getDependencyObjectFromPath = exports.getDependencies = exports.findComponentDefinitionInForm = exports.deleteNodeByPath = exports.deleteNodeAndOptions = exports.deleteCorrespondingMetaInfo = exports.deleteComponentData = exports.addValue = void 0;
|
|
7
7
|
var _Condition = _interopRequireDefault(require("../../../utils/Condition"));
|
|
8
8
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
/* eslint-disable no-param-reassign */
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
|
-
*
|
|
12
|
+
* Add a value to a map of arrays. If the key exists, append the value to the array.
|
|
13
|
+
* If not, create the map entry with that key.
|
|
11
14
|
*
|
|
15
|
+
* @param {*} key
|
|
16
|
+
* @param {*} value
|
|
17
|
+
* @param {*} multiMap
|
|
18
|
+
*/
|
|
19
|
+
const addValue = (key, value, multiMap) => {
|
|
20
|
+
if (!multiMap.has(key)) {
|
|
21
|
+
multiMap.set(key, []);
|
|
22
|
+
}
|
|
23
|
+
multiMap.get(key).push(value);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Given a path, remove a node from this path within an object.
|
|
12
28
|
*
|
|
13
|
-
* @param {Object}
|
|
29
|
+
* @param {Object} payload Javascript object from which the node will be deleted. Updated by the method.
|
|
14
30
|
* @param {String} path A string containing a decimal point delimited path specifying the node to delete.
|
|
15
|
-
* @return
|
|
31
|
+
* @return {void}, obj above updated.
|
|
16
32
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
deleteNodeByPath(obj[i], path);
|
|
33
|
+
exports.addValue = addValue;
|
|
34
|
+
const deleteNodeByPath = (payload, path) => {
|
|
35
|
+
if (Array.isArray(payload)) {
|
|
36
|
+
// If payload is an array, recursively call deleteNodeByPath on each element
|
|
37
|
+
for (let i = 0; i < payload.length; i += 1) {
|
|
38
|
+
deleteNodeByPath(payload[i], path);
|
|
24
39
|
}
|
|
25
40
|
}
|
|
26
41
|
const keys = path.split('.');
|
|
27
|
-
let current =
|
|
42
|
+
let current = payload;
|
|
28
43
|
for (let i = 0; i < keys.length - 1; i += 1) {
|
|
29
44
|
current = current[keys[i]];
|
|
30
45
|
if (current === undefined) {
|
|
@@ -32,27 +47,8 @@ const deleteNodeByPath = (obj, path) => {
|
|
|
32
47
|
}
|
|
33
48
|
}
|
|
34
49
|
if (current[keys[keys.length - 1]]) {
|
|
35
|
-
console.log("Deleting element : ".concat(keys[keys.length - 1]));
|
|
36
50
|
delete current[keys[keys.length - 1]];
|
|
37
51
|
}
|
|
38
|
-
return obj;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Pruning a collection payload may have resulted in objects that are only left with their 'id' field, which isn't data
|
|
43
|
-
* but added by the renderer to find the activeId. If so, remove these objects entirely as they have been pruned.
|
|
44
|
-
*
|
|
45
|
-
* @param {Array} array Array of objects. Each object which has only 1 remaining field called 'id' should be removed.
|
|
46
|
-
* @return Nothing, array above updated.
|
|
47
|
-
*/
|
|
48
|
-
exports.deleteNodeByPath = deleteNodeByPath;
|
|
49
|
-
const removeObjectWithSingleIdFieldInPlace = array => {
|
|
50
|
-
for (let i = array.length - 1; i >= 0; i -= 1) {
|
|
51
|
-
const obj = array[i];
|
|
52
|
-
if (Object.keys(obj).length === 1 && Object.keys(obj)[0] === 'id') {
|
|
53
|
-
array.splice(i, 1);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
52
|
};
|
|
57
53
|
|
|
58
54
|
/**
|
|
@@ -63,7 +59,7 @@ const removeObjectWithSingleIdFieldInPlace = array => {
|
|
|
63
59
|
* @param {String} path Decimal point delimited path.
|
|
64
60
|
* @returns {String} Immediate parent of the path passed in.
|
|
65
61
|
*/
|
|
66
|
-
exports.
|
|
62
|
+
exports.deleteNodeByPath = deleteNodeByPath;
|
|
67
63
|
const getImmediateParent = path => {
|
|
68
64
|
if (typeof path !== 'string' || !path.includes('.')) {
|
|
69
65
|
return null;
|
|
@@ -89,6 +85,53 @@ const getNestedQuestionPath = (optionPath, nestedFieldId) => {
|
|
|
89
85
|
return parentPath ? "".concat(parentPath, ".").concat(nestedFieldId) : nestedFieldId;
|
|
90
86
|
};
|
|
91
87
|
|
|
88
|
+
/**
|
|
89
|
+
* If the component has options, go through each option removing the data for any nested fields.
|
|
90
|
+
* Required as nested options are in the payload at the same heirarchical level.
|
|
91
|
+
*
|
|
92
|
+
* There are occasions when a field shouldn't be removed from the payload, eg if a form is being used for 2 purposes
|
|
93
|
+
* eg the cop-reassign-task-to-rcc.json, and the field is only being hidden for display purposes. Allow these fields
|
|
94
|
+
* to be preserved with a property on the component.
|
|
95
|
+
|
|
96
|
+
* @param {Object} payload Javascript object from which the node will be deleted. Updated by the method.
|
|
97
|
+
* @param {String} path A string containing a decimal point delimited path specifying the node to delete.
|
|
98
|
+
* @param {Object} component The form component representing the path being deleted
|
|
99
|
+
* @return {void}, obj above updated.
|
|
100
|
+
*/
|
|
101
|
+
exports.getNestedQuestionPath = getNestedQuestionPath;
|
|
102
|
+
const deleteNodeAndOptions = (payload, path, component) => {
|
|
103
|
+
var _component$data;
|
|
104
|
+
if (component.preserveInPayload) return;
|
|
105
|
+
deleteNodeByPath(payload, path);
|
|
106
|
+
// If the component has options, go through each option removing the data for any nested fields. Required as nested options are in the payload at the same heirarchical level.
|
|
107
|
+
if (component !== null && component !== void 0 && (_component$data = component.data) !== null && _component$data !== void 0 && _component$data.options) {
|
|
108
|
+
var _component$data2;
|
|
109
|
+
component === null || component === void 0 || (_component$data2 = component.data) === null || _component$data2 === void 0 || (_component$data2 = _component$data2.options) === null || _component$data2 === void 0 || _component$data2.forEach(option => {
|
|
110
|
+
var _option$nested;
|
|
111
|
+
(_option$nested = option.nested) === null || _option$nested === void 0 || _option$nested.forEach(nested => {
|
|
112
|
+
deleteNodeByPath(payload, getNestedQuestionPath(path, nested.fieldId));
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Pruning a collection payload may have resulted in objects that are only left with their 'id' field, which isn't data
|
|
120
|
+
* but added by the renderer to find the activeId. If so, remove these objects entirely as they have been pruned.
|
|
121
|
+
*
|
|
122
|
+
* @param {Array} array Array of objects. Each object which has only 1 remaining field called 'id' should be removed.
|
|
123
|
+
* @return {void}, array above updated.
|
|
124
|
+
*/
|
|
125
|
+
exports.deleteNodeAndOptions = deleteNodeAndOptions;
|
|
126
|
+
const removeObjectWithOnlySingleIdField = array => {
|
|
127
|
+
for (let i = array.length - 1; i >= 0; i -= 1) {
|
|
128
|
+
const obj = array[i];
|
|
129
|
+
if (Object.keys(obj).length === 1 && Object.keys(obj)[0] === 'id') {
|
|
130
|
+
array.splice(i, 1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
92
135
|
/**
|
|
93
136
|
* Helper method to establish all the payload paths (dependencies) that an entity (page or component) is
|
|
94
137
|
* dependent on through its show_when rule.
|
|
@@ -102,7 +145,7 @@ const getNestedQuestionPath = (optionPath, nestedFieldId) => {
|
|
|
102
145
|
* @param {Object} entity Entity whose show_when rule is to be searched for 'field' data items within.
|
|
103
146
|
* @returns {Set} Set of payload paths that the entity (page or component) is dependent on.
|
|
104
147
|
*/
|
|
105
|
-
exports.
|
|
148
|
+
exports.removeObjectWithOnlySingleIdField = removeObjectWithOnlySingleIdField;
|
|
106
149
|
const getDependencies = entity => {
|
|
107
150
|
const findShowWhenFields = (showWhenObject, showWhenFields) => {
|
|
108
151
|
if (typeof showWhenObject === 'object' && showWhenObject !== null) {
|
|
@@ -200,11 +243,13 @@ exports.isShowEntity = isShowEntity;
|
|
|
200
243
|
const findComponentDefinitionInForm = (pageComponentDef, componentByIdMap, componentByFieldIdMap) => {
|
|
201
244
|
var _ref, _componentByIdMap$get;
|
|
202
245
|
const componentInForm = (_ref = (_componentByIdMap$get = componentByIdMap.get(pageComponentDef.use)) !== null && _componentByIdMap$get !== void 0 ? _componentByIdMap$get : componentByFieldIdMap.get(pageComponentDef.use)) !== null && _ref !== void 0 ? _ref : pageComponentDef;
|
|
246
|
+
|
|
247
|
+
// Create clone of component, so processing can make changes to it without changing the form
|
|
248
|
+
const componentInFormClone = JSON.parse(JSON.stringify(componentInForm));
|
|
203
249
|
if (pageComponentDef.use && pageComponentDef.show_when) {
|
|
204
|
-
|
|
250
|
+
componentInFormClone.show_when = pageComponentDef.show_when;
|
|
205
251
|
}
|
|
206
|
-
|
|
207
|
-
return JSON.parse(JSON.stringify(componentInForm));
|
|
252
|
+
return componentInFormClone;
|
|
208
253
|
};
|
|
209
254
|
|
|
210
255
|
/**
|
|
@@ -216,7 +261,7 @@ const findComponentDefinitionInForm = (pageComponentDef, componentByIdMap, compo
|
|
|
216
261
|
* @param {*} component The component definition being deleted that needs corresponding meta data also deleted
|
|
217
262
|
* @param {*} collectionDataObject The payload containing the file data being deleted (for which the corresponding meta needs deleting)
|
|
218
263
|
* @param {*} formData The entire payload, which will include the meta section
|
|
219
|
-
* @returns
|
|
264
|
+
* @returns {void}, as the formData will be updated in situ
|
|
220
265
|
*/
|
|
221
266
|
exports.findComponentDefinitionInForm = findComponentDefinitionInForm;
|
|
222
267
|
const deleteCorrespondingMetaInfo = (component, collectionDataObject, formData) => {
|
|
@@ -236,6 +281,7 @@ const deleteCorrespondingMetaInfo = (component, collectionDataObject, formData)
|
|
|
236
281
|
}
|
|
237
282
|
}
|
|
238
283
|
};
|
|
284
|
+
|
|
239
285
|
/**
|
|
240
286
|
* After the payload has been cleansed of individual data items, empty arrays and objects may remain.
|
|
241
287
|
* Removing an array (when the payload for a collection) may leave a redundant activeId field. The active Id
|
|
@@ -252,6 +298,7 @@ const deleteCorrespondingMetaInfo = (component, collectionDataObject, formData)
|
|
|
252
298
|
* 3. The function operates recursively to handle deeply nested structures.
|
|
253
299
|
*
|
|
254
300
|
* @param {any} payload - The input data structure, which can be an array or an object.
|
|
301
|
+
*
|
|
255
302
|
*/
|
|
256
303
|
exports.deleteCorrespondingMetaInfo = deleteCorrespondingMetaInfo;
|
|
257
304
|
const removeEmptyArraysAndUnusedCollectionIDs = payload => {
|
|
@@ -274,12 +321,10 @@ const removeEmptyArraysAndUnusedCollectionIDs = payload => {
|
|
|
274
321
|
// If the array being removed has an activeId associated with it, remove it
|
|
275
322
|
if (payload["".concat(key, "ActiveId")]) {
|
|
276
323
|
delete payload["".concat(key, "ActiveId")];
|
|
277
|
-
console.log("Deleted element : ".concat(key, "ActiveId}"));
|
|
278
324
|
}
|
|
279
325
|
delete payload["".concat(key, "ActiveId")];
|
|
280
326
|
if (payload[key]) {
|
|
281
327
|
delete payload[key];
|
|
282
|
-
console.log("Deleted element : ".concat(key));
|
|
283
328
|
}
|
|
284
329
|
} else {
|
|
285
330
|
removeEmptyArraysAndUnusedCollectionIDs(payload[key]); // Recurse for nested structures
|
|
@@ -287,4 +332,58 @@ const removeEmptyArraysAndUnusedCollectionIDs = payload => {
|
|
|
287
332
|
});
|
|
288
333
|
}
|
|
289
334
|
};
|
|
290
|
-
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Delete a component's payload item from the overall payload.
|
|
338
|
+
* A component can be defined in >1 places in the form spec. To cater for this,
|
|
339
|
+
* if the componentsToKeep counter tells us that there is > 1 uses of this component
|
|
340
|
+
* still unaccounted for in the form then don't delete, but reduce the count by 1.
|
|
341
|
+
*
|
|
342
|
+
* When the counter reaches 1 we know all other occurences of the component have been resolved
|
|
343
|
+
* so it is safe to delete.
|
|
344
|
+
*
|
|
345
|
+
*
|
|
346
|
+
* @param {*} payload The form payload from which to delete the component data
|
|
347
|
+
* @param {*} path The payload path of the component
|
|
348
|
+
* @param {*} component The component whose data we should attempt to delete
|
|
349
|
+
* @param {*} componentsToKeep A list of all components with a count of their number of uses in the form
|
|
350
|
+
*/
|
|
351
|
+
exports.removeEmptyArraysAndUnusedCollectionIDs = removeEmptyArraysAndUnusedCollectionIDs;
|
|
352
|
+
const deleteComponentData = (payload, path, component, componentsToKeep) => {
|
|
353
|
+
if (componentsToKeep[path] > 1) {
|
|
354
|
+
componentsToKeep[path] -= 1;
|
|
355
|
+
} else {
|
|
356
|
+
deleteNodeAndOptions(payload, path, component);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
*
|
|
362
|
+
* Takes a single page collection payload object and removes the payload items specified in the componentsToPrune list
|
|
363
|
+
* as long as they don't appear in the componentsToKeep list.
|
|
364
|
+
* Additionally, if the component type is multifile, remove the corresponding data entries that will have been created
|
|
365
|
+
* in the meta section of the payload by the form renderer.
|
|
366
|
+
*
|
|
367
|
+
* @param {Set} pathsToKeep paths that we cannot delete from the collectionDataObject
|
|
368
|
+
* @param {Map} componentsToPrune paths that we should delete, as long as they are not in the pathsToKeep
|
|
369
|
+
* @param {Object} collectionDataObject the payload from which to delete the paths
|
|
370
|
+
* @param {Object} formData The form data, whose meta section may include corresponding documents entries for multifile entries
|
|
371
|
+
*/
|
|
372
|
+
exports.deleteComponentData = deleteComponentData;
|
|
373
|
+
const pruneCollectionEntry = (pathsToKeep, componentsToPrune, collectionDataObject, formData) => {
|
|
374
|
+
componentsToPrune.forEach(component => {
|
|
375
|
+
if (!pathsToKeep.has(component.fieldId)) {
|
|
376
|
+
if (component.type === "multifile") {
|
|
377
|
+
deleteCorrespondingMetaInfo(component, collectionDataObject, formData);
|
|
378
|
+
}
|
|
379
|
+
deleteNodeAndOptions(collectionDataObject, component.fieldId, component);
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
/*
|
|
385
|
+
* Converts an object to an array if it isn't already, for use when combining show when rules.
|
|
386
|
+
*/
|
|
387
|
+
exports.pruneCollectionEntry = pruneCollectionEntry;
|
|
388
|
+
const toArray = value => Array.isArray(value) ? value : [value];
|
|
389
|
+
exports.toArray = toArray;
|
|
@@ -19,8 +19,8 @@ describe('deleteNodeByPath', () => {
|
|
|
19
19
|
epsilon: "epsilon"
|
|
20
20
|
};
|
|
21
21
|
const path = 'type';
|
|
22
|
-
|
|
23
|
-
expect(
|
|
22
|
+
Utils.deleteNodeByPath(nested, path);
|
|
23
|
+
expect(nested).toEqual({
|
|
24
24
|
alpha: "alpha",
|
|
25
25
|
bravo: "bravo",
|
|
26
26
|
person: {
|
|
@@ -44,8 +44,8 @@ describe('deleteNodeByPath', () => {
|
|
|
44
44
|
epsilon: "epsilon"
|
|
45
45
|
};
|
|
46
46
|
const path = 'person.charlie';
|
|
47
|
-
|
|
48
|
-
expect(
|
|
47
|
+
Utils.deleteNodeByPath(nested, path);
|
|
48
|
+
expect(nested).toEqual({
|
|
49
49
|
type: "Example form",
|
|
50
50
|
alpha: "alpha",
|
|
51
51
|
bravo: "bravo",
|
|
@@ -57,7 +57,7 @@ describe('deleteNodeByPath', () => {
|
|
|
57
57
|
});
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
|
-
describe('
|
|
60
|
+
describe('removeObjectWithOnlySingleIdField', () => {
|
|
61
61
|
it('removes objects with only the "id" field', () => {
|
|
62
62
|
const inputArray = [{
|
|
63
63
|
id: 1
|
|
@@ -73,7 +73,7 @@ describe('removeObjectWithSingleIdFieldInPlace', () => {
|
|
|
73
73
|
id: 4,
|
|
74
74
|
name: 'John'
|
|
75
75
|
}];
|
|
76
|
-
Utils.
|
|
76
|
+
Utils.removeObjectWithOnlySingleIdField(inputArray);
|
|
77
77
|
expect(inputArray).toEqual([{
|
|
78
78
|
name: 'John'
|
|
79
79
|
}, {
|
|
@@ -90,7 +90,7 @@ describe('removeObjectWithSingleIdFieldInPlace', () => {
|
|
|
90
90
|
age: 30
|
|
91
91
|
}];
|
|
92
92
|
const originalArray = [].concat(inputArray);
|
|
93
|
-
Utils.
|
|
93
|
+
Utils.removeObjectWithOnlySingleIdField(inputArray);
|
|
94
94
|
expect(inputArray).toEqual(originalArray);
|
|
95
95
|
});
|
|
96
96
|
});
|
|
@@ -499,4 +499,61 @@ describe("removeEmptyArraysAndUnusedCollectionIDs", () => {
|
|
|
499
499
|
nested: {}
|
|
500
500
|
}]);
|
|
501
501
|
});
|
|
502
|
+
});
|
|
503
|
+
describe("pruneCollectionEntry", () => {
|
|
504
|
+
let pathsToKeep;
|
|
505
|
+
let componentsToPrune;
|
|
506
|
+
let collectionDataObject;
|
|
507
|
+
let formData;
|
|
508
|
+
let expectedFormData;
|
|
509
|
+
beforeEach(() => {
|
|
510
|
+
pathsToKeep = new Set(['keepThis']);
|
|
511
|
+
componentsToPrune = [{
|
|
512
|
+
fieldId: 'removeThis',
|
|
513
|
+
type: 'text'
|
|
514
|
+
}, {
|
|
515
|
+
fieldId: 'keepThis',
|
|
516
|
+
type: 'text'
|
|
517
|
+
}, {
|
|
518
|
+
fieldId: 'removeMultifile',
|
|
519
|
+
type: 'multifile'
|
|
520
|
+
}];
|
|
521
|
+
collectionDataObject = {
|
|
522
|
+
'removeThis': 'value',
|
|
523
|
+
'keepThis': 'value',
|
|
524
|
+
'removeMultifile': {
|
|
525
|
+
'url': 'https://example.com/file1.pdf'
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
formData = {
|
|
529
|
+
meta: {
|
|
530
|
+
documents: [{
|
|
531
|
+
url: 'https://example.com/file1.pdf'
|
|
532
|
+
}, {
|
|
533
|
+
url: 'https://example.com/file2.pdf'
|
|
534
|
+
}, {
|
|
535
|
+
url: 'https://example.com/file3.pdf'
|
|
536
|
+
}]
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
expectedFormData = {
|
|
540
|
+
meta: {
|
|
541
|
+
documents: [{
|
|
542
|
+
url: 'https://example.com/file2.pdf'
|
|
543
|
+
}, {
|
|
544
|
+
url: 'https://example.com/file3.pdf'
|
|
545
|
+
}]
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
it('should remove components not in pathsToKeep', () => {
|
|
550
|
+
Utils.pruneCollectionEntry(pathsToKeep, componentsToPrune, collectionDataObject, formData);
|
|
551
|
+
expect(collectionDataObject).toEqual({
|
|
552
|
+
'keepThis': 'value'
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
it('should delete meta.documents for multifile components', () => {
|
|
556
|
+
Utils.pruneCollectionEntry(pathsToKeep, componentsToPrune, collectionDataObject, formData);
|
|
557
|
+
expect(formData).toEqual(expectedFormData);
|
|
558
|
+
});
|
|
502
559
|
});
|
|
@@ -36,7 +36,6 @@ const onCYAAction = (setPagePoint, action, pages, validate, components, data, se
|
|
|
36
36
|
pages,
|
|
37
37
|
components
|
|
38
38
|
};
|
|
39
|
-
console.log("Invoking clearOutUncompletedRoutes");
|
|
40
39
|
_helpers.default.clearOutUncompletedRoutes(formPagesAndComponents, submissionData);
|
|
41
40
|
submissionData.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, submissionData, currentTask, true);
|
|
42
41
|
setData(submissionData);
|
|
@@ -81,7 +80,6 @@ const onCYAAction = (setPagePoint, action, pages, validate, components, data, se
|
|
|
81
80
|
pages,
|
|
82
81
|
components
|
|
83
82
|
};
|
|
84
|
-
console.log("Invoking clearOutUncompletedRoutes");
|
|
85
83
|
_helpers.default.clearOutUncompletedRoutes(formPagesAndComponents, submissionData);
|
|
86
84
|
submissionData.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, submissionData, currentTask, true);
|
|
87
85
|
setData(submissionData);
|
|
@@ -55,6 +55,11 @@ jest.mock('./helpers', () => ({
|
|
|
55
55
|
this.getNextPageIdCalls = 0;
|
|
56
56
|
this.getFormStateCalls = 0;
|
|
57
57
|
this.getSubmissionStatusCalls = 0;
|
|
58
|
+
},
|
|
59
|
+
clearOutUncompletedRoutesCalls: 0,
|
|
60
|
+
clearOutUncompletedRoutes(formPagesAndComponents, formData) {
|
|
61
|
+
this.clearOutUncompletedRoutesCalls += 1;
|
|
62
|
+
return formData;
|
|
58
63
|
}
|
|
59
64
|
}));
|
|
60
65
|
jest.mock('../../utils', () => ({
|
|
@@ -112,7 +112,6 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
|
|
|
112
112
|
components
|
|
113
113
|
};
|
|
114
114
|
if (action.type === _models.PageAction.TYPES.SUBMIT) {
|
|
115
|
-
console.log("Invoking clearOutUncompletedRoutes");
|
|
116
115
|
_helpers.default.clearOutUncompletedRoutes(formPagesAndComponents, submissionData);
|
|
117
116
|
}
|
|
118
117
|
|
|
@@ -8,10 +8,6 @@ var _axios = _interopRequireDefault(require("axios"));
|
|
|
8
8
|
var _react = require("react");
|
|
9
9
|
var _useAxios = _interopRequireDefault(require("./useAxios"));
|
|
10
10
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
-
// Global imports
|
|
12
|
-
|
|
13
|
-
// Local imports
|
|
14
|
-
|
|
15
11
|
// Caches for responses and errors.
|
|
16
12
|
const cache = {};
|
|
17
13
|
const errorCache = {};
|
|
@@ -31,15 +27,18 @@ const STATUS_IDLE = exports.STATUS_IDLE = 'idle';
|
|
|
31
27
|
const STATUS_FETCHING = exports.STATUS_FETCHING = 'fetching';
|
|
32
28
|
const STATUS_FETCHED = exports.STATUS_FETCHED = 'fetched';
|
|
33
29
|
const STATUS_ERROR = exports.STATUS_ERROR = 'error';
|
|
34
|
-
const useGetRequest = url
|
|
30
|
+
const useGetRequest = function (url) {
|
|
31
|
+
let caching = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
35
32
|
const axiosInstance = (0, _useAxios.default)();
|
|
36
|
-
const
|
|
37
|
-
const cancelRequests = () => {
|
|
38
|
-
if (cancelToken) cancelToken.cancel();
|
|
39
|
-
};
|
|
33
|
+
const cancelTokenRef = (0, _react.useRef)(_axios.default.CancelToken.source());
|
|
40
34
|
const [status, setStatus] = (0, _react.useState)(STATUS_IDLE);
|
|
41
35
|
const [error, setError] = (0, _react.useState)(null);
|
|
42
36
|
const [data, setData] = (0, _react.useState)(null);
|
|
37
|
+
const cancelRequests = () => {
|
|
38
|
+
if (cancelTokenRef.current) {
|
|
39
|
+
cancelTokenRef.current.cancel();
|
|
40
|
+
}
|
|
41
|
+
};
|
|
43
42
|
(0, _react.useEffect)(() => {
|
|
44
43
|
if (!url || !axiosInstance) return;
|
|
45
44
|
const fetchData = async () => {
|
|
@@ -47,9 +46,9 @@ const useGetRequest = url => {
|
|
|
47
46
|
setError(null);
|
|
48
47
|
setStatus(STATUS_FETCHING);
|
|
49
48
|
let fetchedData;
|
|
50
|
-
if (cache[url]) {
|
|
49
|
+
if (caching && cache[url]) {
|
|
51
50
|
fetchedData = cache[url];
|
|
52
|
-
} else if (errorCache[url]) {
|
|
51
|
+
} else if (caching && errorCache[url]) {
|
|
53
52
|
/**
|
|
54
53
|
* This logic is intended to stop multiple requests being made in succession
|
|
55
54
|
* that all fail. Presently, this will only allow the first request to be
|
|
@@ -60,24 +59,25 @@ const useGetRequest = url => {
|
|
|
60
59
|
throw errorCache[url];
|
|
61
60
|
} else {
|
|
62
61
|
const response = await axiosInstance.get(url, {
|
|
63
|
-
cancelToken:
|
|
62
|
+
cancelToken: cancelTokenRef.current.token
|
|
64
63
|
}).catch(e => {
|
|
64
|
+
setError(e);
|
|
65
65
|
throw e;
|
|
66
66
|
});
|
|
67
67
|
fetchedData = response.data;
|
|
68
|
-
cache[url] = fetchedData;
|
|
68
|
+
if (caching) cache[url] = fetchedData;
|
|
69
69
|
}
|
|
70
70
|
setData(fetchedData);
|
|
71
71
|
setStatus(STATUS_FETCHED);
|
|
72
72
|
} catch (e) {
|
|
73
|
-
errorCache[url] = e;
|
|
73
|
+
if (caching) errorCache[url] = e;
|
|
74
74
|
setError(e);
|
|
75
75
|
setData(null);
|
|
76
76
|
setStatus(STATUS_ERROR);
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
79
|
fetchData();
|
|
80
|
-
}, [axiosInstance, url,
|
|
80
|
+
}, [axiosInstance, url, caching]);
|
|
81
81
|
return {
|
|
82
82
|
status,
|
|
83
83
|
error,
|
package/dist/hooks/useRefData.js
CHANGED
|
@@ -28,13 +28,14 @@ const getRefDataUrl = component => {
|
|
|
28
28
|
return undefined;
|
|
29
29
|
};
|
|
30
30
|
const useRefData = (component, formData) => {
|
|
31
|
+
var _component$data;
|
|
31
32
|
const url = getRefDataUrl(_objectSpread(_objectSpread({}, component), {}, {
|
|
32
33
|
formData
|
|
33
34
|
}));
|
|
34
35
|
const {
|
|
35
36
|
status: _status,
|
|
36
37
|
data: _data
|
|
37
|
-
} = (0, _useGetRequest.default)(url);
|
|
38
|
+
} = (0, _useGetRequest.default)(url, component === null || component === void 0 || (_component$data = component.data) === null || _component$data === void 0 ? void 0 : _component$data.useCache);
|
|
38
39
|
const [data, setData] = (0, _react.useState)([]);
|
|
39
40
|
const [status, setStatus] = (0, _react.useState)(STATUS_LOADING);
|
|
40
41
|
(0, _react.useEffect)(() => {
|
|
@@ -52,7 +53,7 @@ const useRefData = (component, formData) => {
|
|
|
52
53
|
setData([]);
|
|
53
54
|
setStatus(STATUS_COMPLETE);
|
|
54
55
|
}
|
|
55
|
-
}, [component, _status, _data, url
|
|
56
|
+
}, [component.id, _status, _data, url]);
|
|
56
57
|
return {
|
|
57
58
|
data,
|
|
58
59
|
status
|
|
@@ -75,7 +75,8 @@ const setupDefaultObjectValue = (defaultObject, data) => {
|
|
|
75
75
|
// defaultObject.sourced is an object with values that should
|
|
76
76
|
// be used as field names to get values from data.
|
|
77
77
|
if (defaultObj.sourced) {
|
|
78
|
-
|
|
78
|
+
var _result;
|
|
79
|
+
result = (_result = result) !== null && _result !== void 0 ? _result : {};
|
|
79
80
|
Object.keys(defaultObj.sourced).every(key => {
|
|
80
81
|
const sourcedValue = typeof defaultObj.sourced[key] === 'string' && (0, _getSourceData.default)(data, defaultObj.sourced[key]);
|
|
81
82
|
if (sourcedValue === undefined && defaultObj.skipIfSourceInvalid) {
|
|
@@ -20,6 +20,26 @@ const getComparisonValue = (condition, data) => {
|
|
|
20
20
|
return condition.value;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Processes a value based on its type.
|
|
25
|
+
* - If it's an array, returns its length.
|
|
26
|
+
* - If it's a string, parses it as a float.
|
|
27
|
+
* - If it's a number, returns it as is.
|
|
28
|
+
* - Returns `undefined` for other types.
|
|
29
|
+
*
|
|
30
|
+
* @param {any} value - The value to process.
|
|
31
|
+
* @returns {number | undefined} - The processed value or `undefined` if not applicable.
|
|
32
|
+
*/
|
|
33
|
+
const getProcessedValue = obj => {
|
|
34
|
+
if (Array.isArray(obj)) return obj.length;
|
|
35
|
+
if (typeof obj === "string") {
|
|
36
|
+
const cleaned = obj.replace(/,/g, ''); // Remove all commas
|
|
37
|
+
return Number.isNaN(cleaned) ? null : parseFloat(cleaned);
|
|
38
|
+
}
|
|
39
|
+
if (typeof obj === "number") return obj;
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
|
|
23
43
|
/**
|
|
24
44
|
* Looks at a condition object to see if the supplied value meets it.
|
|
25
45
|
* The condition object contains an `op` property, along with a comparator
|
|
@@ -66,21 +86,15 @@ const meetsCondition = (condition, value, data) => {
|
|
|
66
86
|
}
|
|
67
87
|
case '<':
|
|
68
88
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const valFloat = parseFloat(value.replace(',', ''));
|
|
73
|
-
const compareFloat = parseFloat(compare.replace(',', ''));
|
|
74
|
-
return valFloat < compareFloat;
|
|
89
|
+
const valFloat = getProcessedValue(value);
|
|
90
|
+
const compareFloat = getProcessedValue(compare);
|
|
91
|
+
return valFloat != null && compareFloat != null && valFloat < compareFloat;
|
|
75
92
|
}
|
|
76
93
|
case '>':
|
|
77
94
|
{
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const valFloat = parseFloat(value.replace(/,/g, ''));
|
|
82
|
-
const compareFloat = parseFloat(compare.replace(/,/g, ''));
|
|
83
|
-
return valFloat > compareFloat;
|
|
95
|
+
const valFloat = getProcessedValue(value);
|
|
96
|
+
const compareFloat = getProcessedValue(compare);
|
|
97
|
+
return valFloat != null && compareFloat != null && valFloat > compareFloat;
|
|
84
98
|
}
|
|
85
99
|
case 'contains':
|
|
86
100
|
{
|
|
@@ -551,6 +551,27 @@ describe('utils.Condition.meetsCondition', () => {
|
|
|
551
551
|
};
|
|
552
552
|
expect((0, _meetsCondition.default)(CONDITION, '10,000,000')).toBeTruthy();
|
|
553
553
|
});
|
|
554
|
+
it('greater than should handle value is 0', () => {
|
|
555
|
+
const CONDITION = {
|
|
556
|
+
op: '>',
|
|
557
|
+
value: 0
|
|
558
|
+
};
|
|
559
|
+
expect((0, _meetsCondition.default)(CONDITION, 1)).toBeTruthy();
|
|
560
|
+
});
|
|
561
|
+
it('greater than should handle compear is 0', () => {
|
|
562
|
+
const CONDITION = {
|
|
563
|
+
op: '<',
|
|
564
|
+
value: 1
|
|
565
|
+
};
|
|
566
|
+
expect((0, _meetsCondition.default)(CONDITION, 0)).toBeTruthy();
|
|
567
|
+
});
|
|
568
|
+
it('greater than should handle array is 0', () => {
|
|
569
|
+
const CONDITION = {
|
|
570
|
+
op: '>',
|
|
571
|
+
value: 1
|
|
572
|
+
};
|
|
573
|
+
expect((0, _meetsCondition.default)(CONDITION, ["A", "B", "C"])).toBeTruthy();
|
|
574
|
+
});
|
|
554
575
|
});
|
|
555
576
|
describe('operator includes', () => {
|
|
556
577
|
it('should accept a string that exists within the value', () => {
|