@ukhomeoffice/cop-react-form-renderer 6.15.8-alpha → 6.15.9-alpha
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/FormPage/FormPage.js +8 -10
- package/dist/components/FormRenderer/FormRenderer.js +5 -10
- 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/data-page-same-component-reused-one-shown.json +8 -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 +239 -174
- package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.test.js +11 -7
- package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutesUtils.js +126 -35
- 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/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 +2 -2
|
@@ -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
31
|
* @return Nothing, 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,48 @@ 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
|
+
* @param {Object} payload Javascript object from which the node will be deleted. Updated by the method.
|
|
93
|
+
* @param {String} path A string containing a decimal point delimited path specifying the node to delete.
|
|
94
|
+
* @param {Object} component The form component representing the path being deleted
|
|
95
|
+
* @return Nothing, obj above updated.
|
|
96
|
+
*/
|
|
97
|
+
exports.getNestedQuestionPath = getNestedQuestionPath;
|
|
98
|
+
const deleteNodeAndOptions = (payload, path, component) => {
|
|
99
|
+
var _component$data;
|
|
100
|
+
deleteNodeByPath(payload, path);
|
|
101
|
+
// 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.
|
|
102
|
+
if (component !== null && component !== void 0 && (_component$data = component.data) !== null && _component$data !== void 0 && _component$data.options) {
|
|
103
|
+
var _component$data2;
|
|
104
|
+
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 => {
|
|
105
|
+
var _option$nested;
|
|
106
|
+
(_option$nested = option.nested) === null || _option$nested === void 0 || _option$nested.forEach(nested => {
|
|
107
|
+
deleteNodeByPath(payload, getNestedQuestionPath(path, nested.fieldId));
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Pruning a collection payload may have resulted in objects that are only left with their 'id' field, which isn't data
|
|
115
|
+
* but added by the renderer to find the activeId. If so, remove these objects entirely as they have been pruned.
|
|
116
|
+
*
|
|
117
|
+
* @param {Array} array Array of objects. Each object which has only 1 remaining field called 'id' should be removed.
|
|
118
|
+
* @return Nothing, array above updated.
|
|
119
|
+
*/
|
|
120
|
+
exports.deleteNodeAndOptions = deleteNodeAndOptions;
|
|
121
|
+
const removeObjectWithOnlySingleIdField = array => {
|
|
122
|
+
for (let i = array.length - 1; i >= 0; i -= 1) {
|
|
123
|
+
const obj = array[i];
|
|
124
|
+
if (Object.keys(obj).length === 1 && Object.keys(obj)[0] === 'id') {
|
|
125
|
+
array.splice(i, 1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
92
130
|
/**
|
|
93
131
|
* Helper method to establish all the payload paths (dependencies) that an entity (page or component) is
|
|
94
132
|
* dependent on through its show_when rule.
|
|
@@ -102,7 +140,7 @@ const getNestedQuestionPath = (optionPath, nestedFieldId) => {
|
|
|
102
140
|
* @param {Object} entity Entity whose show_when rule is to be searched for 'field' data items within.
|
|
103
141
|
* @returns {Set} Set of payload paths that the entity (page or component) is dependent on.
|
|
104
142
|
*/
|
|
105
|
-
exports.
|
|
143
|
+
exports.removeObjectWithOnlySingleIdField = removeObjectWithOnlySingleIdField;
|
|
106
144
|
const getDependencies = entity => {
|
|
107
145
|
const findShowWhenFields = (showWhenObject, showWhenFields) => {
|
|
108
146
|
if (typeof showWhenObject === 'object' && showWhenObject !== null) {
|
|
@@ -236,6 +274,7 @@ const deleteCorrespondingMetaInfo = (component, collectionDataObject, formData)
|
|
|
236
274
|
}
|
|
237
275
|
}
|
|
238
276
|
};
|
|
277
|
+
|
|
239
278
|
/**
|
|
240
279
|
* After the payload has been cleansed of individual data items, empty arrays and objects may remain.
|
|
241
280
|
* Removing an array (when the payload for a collection) may leave a redundant activeId field. The active Id
|
|
@@ -252,6 +291,7 @@ const deleteCorrespondingMetaInfo = (component, collectionDataObject, formData)
|
|
|
252
291
|
* 3. The function operates recursively to handle deeply nested structures.
|
|
253
292
|
*
|
|
254
293
|
* @param {any} payload - The input data structure, which can be an array or an object.
|
|
294
|
+
*
|
|
255
295
|
*/
|
|
256
296
|
exports.deleteCorrespondingMetaInfo = deleteCorrespondingMetaInfo;
|
|
257
297
|
const removeEmptyArraysAndUnusedCollectionIDs = payload => {
|
|
@@ -274,12 +314,10 @@ const removeEmptyArraysAndUnusedCollectionIDs = payload => {
|
|
|
274
314
|
// If the array being removed has an activeId associated with it, remove it
|
|
275
315
|
if (payload["".concat(key, "ActiveId")]) {
|
|
276
316
|
delete payload["".concat(key, "ActiveId")];
|
|
277
|
-
console.log("Deleted element : ".concat(key, "ActiveId}"));
|
|
278
317
|
}
|
|
279
318
|
delete payload["".concat(key, "ActiveId")];
|
|
280
319
|
if (payload[key]) {
|
|
281
320
|
delete payload[key];
|
|
282
|
-
console.log("Deleted element : ".concat(key));
|
|
283
321
|
}
|
|
284
322
|
} else {
|
|
285
323
|
removeEmptyArraysAndUnusedCollectionIDs(payload[key]); // Recurse for nested structures
|
|
@@ -287,4 +325,57 @@ const removeEmptyArraysAndUnusedCollectionIDs = payload => {
|
|
|
287
325
|
});
|
|
288
326
|
}
|
|
289
327
|
};
|
|
290
|
-
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Delete a component's payload item from the overall payload.
|
|
331
|
+
* A component can be defined in >1 places in the form spec. To cater for this,
|
|
332
|
+
* if the componentsToKeep counter tells us that there is > 1 uses of this component
|
|
333
|
+
* still unaccounted for in the form then don't delete, but reduce the count by 1.
|
|
334
|
+
*
|
|
335
|
+
* When the counter reaches 1 we know all other occurences of the component have been resolved
|
|
336
|
+
* so it is safe to delete.
|
|
337
|
+
*
|
|
338
|
+
* @param {*} payload The form payload from which to delete the component data
|
|
339
|
+
* @param {*} path The payload path of the component
|
|
340
|
+
* @param {*} component The component whose data we should attempt to delete
|
|
341
|
+
* @param {*} componentsToKeep A list of all components with a count of their number of uses in the form
|
|
342
|
+
*/
|
|
343
|
+
exports.removeEmptyArraysAndUnusedCollectionIDs = removeEmptyArraysAndUnusedCollectionIDs;
|
|
344
|
+
const deleteComponentData = (payload, path, component, componentsToKeep) => {
|
|
345
|
+
if (componentsToKeep[path] > 1) {
|
|
346
|
+
componentsToKeep[path] -= 1;
|
|
347
|
+
} else {
|
|
348
|
+
deleteNodeAndOptions(payload, path, component);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
*
|
|
354
|
+
* Takes a single page collection payload object and removes the payload items specified in the componentsToPrune list
|
|
355
|
+
* as long as they don't appear in the componentsToKeep list.
|
|
356
|
+
* Additionally, if the component type is multifile, remove the corresponding data entries that will have been created
|
|
357
|
+
* in the meta section of the payload by the form renderer.
|
|
358
|
+
*
|
|
359
|
+
* @param {Set} pathsToKeep paths that we cannot delete from the collectionDataObject
|
|
360
|
+
* @param {Map} componentsToPrune paths that we should delete, as long as they are not in the pathsToKeep
|
|
361
|
+
* @param {Object} collectionDataObject the payload from which to delete the paths
|
|
362
|
+
* @param {Object} formData The form data, whose meta section may include corresponding documents entries for multifile entries
|
|
363
|
+
*/
|
|
364
|
+
exports.deleteComponentData = deleteComponentData;
|
|
365
|
+
const pruneCollectionEntry = (pathsToKeep, componentsToPrune, collectionDataObject, formData) => {
|
|
366
|
+
componentsToPrune.forEach(component => {
|
|
367
|
+
if (!pathsToKeep.has(component.fieldId)) {
|
|
368
|
+
if (component.type === "multifile") {
|
|
369
|
+
deleteCorrespondingMetaInfo(component, collectionDataObject, formData);
|
|
370
|
+
}
|
|
371
|
+
deleteNodeAndOptions(collectionDataObject, component.fieldId, component);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/*
|
|
377
|
+
* Converts an object to an array if it isn't already, for use when combining show when rules.
|
|
378
|
+
*/
|
|
379
|
+
exports.pruneCollectionEntry = pruneCollectionEntry;
|
|
380
|
+
const toArray = value => Array.isArray(value) ? value : [value];
|
|
381
|
+
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', () => {
|