@ukhomeoffice/cop-react-form-renderer 6.15.7-alpha → 6.15.8-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.
Files changed (15) hide show
  1. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-component-show-when-in-component-and-page.json +62 -0
  2. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-page-same-component-reused.json +61 -0
  3. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/cop-airpax-change-what-happened-before.json +300 -0
  4. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-component-show-when-in-component-and-page.json +6 -0
  5. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-page-same-component-reused.json +6 -0
  6. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/cop-airpax-change-what-happened-after.json +280 -0
  7. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-component-show-when-in-component-and-page-removed.json +5 -0
  8. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-page-same-component-reused-removed.json +5 -0
  9. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.js +84 -189
  10. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.test.js +64 -27
  11. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutesUtils.js +290 -0
  12. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutesUtils.test.js +502 -0
  13. package/package.json +1 -1
  14. package/dist/components/FormRenderer/helpers/deleteNodeByPath.js +0 -29
  15. package/dist/components/FormRenderer/helpers/deleteNodeByPath.test.js +0 -56
@@ -5,41 +5,44 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
  var _mergeCollectionPages = _interopRequireDefault(require("../../../utils/CollectionPage/mergeCollectionPages"));
8
- var _deleteNodeByPath = _interopRequireDefault(require("./deleteNodeByPath"));
9
- var _Condition = _interopRequireDefault(require("../../../utils/Condition"));
8
+ var Utils = _interopRequireWildcard(require("./clearOutUncompletedRoutesUtils"));
10
9
  var _optionIsSelected = _interopRequireDefault(require("../../../utils/Component/optionIsSelected"));
10
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
11
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
11
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
12
13
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
13
14
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
14
15
  function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
15
16
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
16
17
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable no-param-reassign */
17
- function removeObjectWithSingleIdFieldInPlace(array) {
18
- for (let i = array.length - 1; i >= 0; i -= 1) {
19
- const obj = array[i];
20
- if (Object.keys(obj).length === 1 && Object.keys(obj)[0] === 'id') {
21
- array.splice(i, 1); // Remove the object at index i
22
- }
23
- }
24
- }
25
- const getImmediateParent = path => {
26
- if (typeof path !== 'string' || !path.includes('.')) {
27
- return null;
18
+ const addValue = (key, value, multiMap) => {
19
+ if (!multiMap.has(key)) {
20
+ multiMap.set(key, []);
28
21
  }
29
- const parts = path.split('.');
30
- parts.pop();
31
- return parts.join('.');
22
+ multiMap.get(key).push(value);
32
23
  };
33
-
34
- /**
35
- * The nested fieldIds are placed in the payload at the same level in the heirarchy as the option question they relate to
36
- */
37
- const getNestedQuestionPath = (optionPath, nestedFieldId) => {
38
- const parentPath = getImmediateParent(optionPath);
39
- return parentPath ? "".concat(parentPath, ".").concat(nestedFieldId) : nestedFieldId;
24
+ const pruneSingleComponent = (formData, path, component, componentsToKeep) => {
25
+ // If there is more than one entry for this component, then it is being used elsewhere in a visible page. Don't prune, but reduce the count by 1
26
+ if (componentsToKeep[path] > 1) {
27
+ componentsToKeep[path] -= 1;
28
+ } else {
29
+ var _component$data;
30
+ Utils.deleteNodeByPath(formData, path);
31
+ // 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.
32
+ if (component !== null && component !== void 0 && (_component$data = component.data) !== null && _component$data !== void 0 && _component$data.options) {
33
+ var _component$data2;
34
+ 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 => {
35
+ var _option$nested;
36
+ (_option$nested = option.nested) === null || _option$nested === void 0 || _option$nested.forEach(nested => {
37
+ Utils.deleteNodeByPath(formData, Utils.getNestedQuestionPath(path, nested.fieldId));
38
+ });
39
+ });
40
+ }
41
+ }
40
42
  };
43
+ const toArray = value => Array.isArray(value) ? value : [value];
41
44
  const recursivelyMapFieldsAndDeleteHiddenNested = (page, component, path, allComponents, componentsToKeep, formData) => {
42
- var _component$data;
45
+ var _component$data3;
43
46
  // Many component, such as warnings, html and details do not have data so we can ignore them
44
47
  if (!component.fieldId) return;
45
48
  path = path ? "".concat(path, ".").concat(component.fieldId) : component.fieldId;
@@ -50,143 +53,74 @@ const recursivelyMapFieldsAndDeleteHiddenNested = (page, component, path, allCom
50
53
  // add the fully qualified path to the component, which will be required when resolving the dependencies later
51
54
  component.path = path;
52
55
 
53
- // If the parent page has a rule, this overides the component rule
54
- if (page.show_when) component.show_when = page.show_when;
56
+ // If the parent page has a rule, combine it with any component rule
57
+ if (page.show_when) {
58
+ component.show_when = component.show_when ? [].concat(toArray(page.show_when), toArray(component.show_when)) : page.show_when;
59
+ }
55
60
 
56
- // this is the master repository of all components
57
- allComponents.set(path, component);
61
+ // this is the master repository of all components. Can be more than one component per path, so keep a map of <path, array>
62
+ addValue(path, component, allComponents);
58
63
  if (component.components) {
59
64
  component.components.forEach(c => {
60
65
  recursivelyMapFieldsAndDeleteHiddenNested(page, c, path, allComponents, componentsToKeep, formData);
61
66
  });
62
- } else if (component !== null && component !== void 0 && (_component$data = component.data) !== null && _component$data !== void 0 && _component$data.options) {
63
- var _component$data2;
67
+ } else if (component !== null && component !== void 0 && (_component$data3 = component.data) !== null && _component$data3 !== void 0 && _component$data3.options) {
68
+ var _component$data4;
64
69
  /**
65
70
  * Perform processing for any options, and any options containing nested questions.
66
71
  * - set hidden nested questions when the parent option is not selected
67
72
  * - any nested questions must be treated as components which can be shown/hidden
68
73
  */
69
- 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 => {
74
+ component === null || component === void 0 || (_component$data4 = component.data) === null || _component$data4 === void 0 || (_component$data4 = _component$data4.options) === null || _component$data4 === void 0 || _component$data4.forEach(option => {
70
75
  if (option.nested) {
71
76
  if (!(0, _optionIsSelected.default)(formData[component.id], option)) {
72
77
  option.nested.forEach(nestedComponent => {
73
78
  // It is safe to delete this now, as we know this data item can not be required as the associated option is not selected
74
- (0, _deleteNodeByPath.default)(formData, getNestedQuestionPath(path, nestedComponent.fieldId));
79
+ Utils.deleteNodeByPath(formData, Utils.getNestedQuestionPath(path, nestedComponent.fieldId));
75
80
  });
76
81
  } else {
77
82
  // If the option is selected, then add any nested components to the allComponents list. If the nested block has a show_when, pass it down to the child questions
78
83
  const blockShowWhen = option.show_when;
79
84
  option.nested.forEach(nestedComponent => {
80
85
  if (blockShowWhen) nestedComponent.show_when = blockShowWhen;
81
- recursivelyMapFieldsAndDeleteHiddenNested(page, nestedComponent, getImmediateParent(path), allComponents, componentsToKeep, formData);
86
+ recursivelyMapFieldsAndDeleteHiddenNested(page, nestedComponent, Utils.getImmediateParent(path), allComponents, componentsToKeep, formData);
82
87
  });
83
88
  }
84
89
  }
85
90
  });
86
91
  }
87
92
  };
88
- const findShowWhenFields = (element, showWhenFields) => {
89
- if (typeof element === 'object' && element !== null) {
90
- if (Array.isArray(element)) {
91
- element.forEach(value => {
92
- findShowWhenFields(value, showWhenFields);
93
- });
94
- } else {
95
- Object.keys(element).forEach(key => {
96
- if (key === 'field') {
97
- showWhenFields.push(element[key]);
98
- }
99
- findShowWhenFields(element[key], showWhenFields);
100
- });
101
- }
102
- }
103
- return showWhenFields;
104
- };
105
- function getDependencies(entity) {
106
- let dependencies = null;
107
- if (entity.show_when) {
108
- var _findShowWhenFields;
109
- // Set dependencies. Maybe multiple as show_whens can be compound and nested
110
- dependencies = new Set();
111
- const showWhenFields = [];
112
- (_findShowWhenFields = findShowWhenFields(entity.show_when, showWhenFields)) === null || _findShowWhenFields === void 0 || _findShowWhenFields.forEach(showWhenField => {
113
- dependencies.add(showWhenField);
114
- });
115
- }
116
- return dependencies;
117
- }
118
- function addEntityToGraph(entity, componentDependencies, allDependencyRelationships) {
119
- if (componentDependencies) {
120
- allDependencyRelationships.set(entity.path, componentDependencies);
121
- }
122
- }
123
93
  const recursivelyMapDependencies = (component, allDependencyRelationships) => {
124
94
  // Non data components can be ignored - can we remove this when doing the allcomponents thing?
125
95
  if (!component.fieldId) return;
126
- const componentDependencies = getDependencies(component);
127
- addEntityToGraph(component, componentDependencies, allDependencyRelationships);
96
+ const componentDependencies = Utils.getDependencies(component);
97
+ if (componentDependencies) {
98
+ // This path may already exist (if a component is defined twice in the form) so build up the dependencies
99
+ if (allDependencyRelationships.has(component.path)) {
100
+ const existingSet = allDependencyRelationships.get(component.path);
101
+ componentDependencies.forEach(dep => existingSet.add(dep));
102
+ } else {
103
+ allDependencyRelationships.set(component.path, new Set(componentDependencies));
104
+ }
105
+ }
128
106
  if (component.components) {
129
107
  component.components.forEach(c => {
130
108
  recursivelyMapDependencies(c, allDependencyRelationships);
131
109
  });
132
110
  }
133
111
  };
134
- /**
135
- * Some show_when field values point to data items that are within objects provided by external calls to refdata, eg modeOfTransport.id
136
- * These won't map directly to the path keyed components in allComponents, so go back up levels in the path until we find the component.
137
- * The neighbour component might not be found at all, as the dependency might be on a field that is either provided by cop-ui (eg jobHolderStaffDetails.linemanagerEmail),
138
- * or by the "addToFormData" function (eg epmsSubmitted). These will be leaf level in a dependency chain so component is not required.
139
- */
140
-
141
- const getNeighbourObjectFromPath = (neighbourPath, allComponents) => {
142
- const segments = neighbourPath.split(".");
143
- for (let i = segments.length; i > 0; i -= 1) {
144
- const currentPath = segments.slice(0, i).join(".");
145
- const neighbour = allComponents.get(currentPath);
146
- if (neighbour) return neighbour;
147
- }
148
- return null;
149
- };
150
- const isShowEntity = (entity, data) => {
151
- var _entity$show_when;
152
- // If there is no rule set, then the entity can be shown
153
- if (!entity.show_when) {
154
- return true;
155
- }
156
- if (((_entity$show_when = entity.show_when) === null || _entity$show_when === void 0 ? void 0 : _entity$show_when.type) === "or") {
157
- return _Condition.default.meetsOne(entity, data);
158
- }
159
- return _Condition.default.meetsAll(entity, data);
160
- };
161
- const pruneSingleComponent = (formData, path, component, componentsToKeep) => {
162
- // If there is more than one entry for this component, then it is being used elsewhere in a visible page. Don't prune, but reduce the count by 1
163
- if (componentsToKeep[path] > 1) {
164
- componentsToKeep[path] -= 1;
165
- } else {
166
- var _component$data3;
167
- (0, _deleteNodeByPath.default)(formData, path);
168
- // 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.
169
- if (component !== null && component !== void 0 && (_component$data3 = component.data) !== null && _component$data3 !== void 0 && _component$data3.options) {
170
- var _component$data4;
171
- component === null || component === void 0 || (_component$data4 = component.data) === null || _component$data4 === void 0 || (_component$data4 = _component$data4.options) === null || _component$data4 === void 0 || _component$data4.forEach(option => {
172
- var _option$nested;
173
- (_option$nested = option.nested) === null || _option$nested === void 0 || _option$nested.forEach(nested => {
174
- (0, _deleteNodeByPath.default)(formData, getNestedQuestionPath(path, nested.fieldId));
175
- });
176
- });
177
- }
178
- }
179
- };
180
112
  const recursivelyResolveDependencies = (visited, dependentEntity, dependencyRelationships, form, formData, allComponents, componentsToKeep, allCollections) => {
181
- if (visited.has(dependentEntity.id)) return;
182
- visited.add(dependentEntity.id);
113
+ if (visited.has(dependentEntity)) return;
114
+ visited.add(dependentEntity);
183
115
  const neighbours = dependencyRelationships.get(dependentEntity.path);
184
116
  neighbours === null || neighbours === void 0 || neighbours.forEach(neighbourPath => {
185
- const neighbour = getNeighbourObjectFromPath(neighbourPath, allComponents);
186
- if (neighbour) {
187
- if (!visited.has(neighbour.id)) {
188
- recursivelyResolveDependencies(visited, neighbour, dependencyRelationships, form, formData, allComponents, componentsToKeep, allCollections);
189
- }
117
+ const neighbourArray = Utils.getDependencyObjectFromPath(neighbourPath, allComponents);
118
+ if (neighbourArray) {
119
+ neighbourArray.forEach(neighbour => {
120
+ if (!visited.has(neighbour)) {
121
+ recursivelyResolveDependencies(visited, neighbour, dependencyRelationships, form, formData, allComponents, componentsToKeep, allCollections);
122
+ }
123
+ });
190
124
  } else {
191
125
  // Refactor out helper methods
192
126
  const arrayNamePattern = /([a-zA-Z_$][\w$]*)\[\d+\]/;
@@ -208,38 +142,18 @@ const recursivelyResolveDependencies = (visited, dependentEntity, dependencyRela
208
142
  }
209
143
  });
210
144
  // Whilst unravelling the recursion, we can start pruning from the leaf upwards
211
- if (!isShowEntity(dependentEntity, formData)) {
145
+ if (!Utils.isShowEntity(dependentEntity, formData)) {
212
146
  pruneSingleComponent(formData, dependentEntity.path, dependentEntity, componentsToKeep);
213
147
  }
214
148
  };
215
- const findComponentDefinitionInForm = (useComponentInPage, form) => {
216
- var _ref, _form$components$find;
217
- const componentInForm = (_ref = (_form$components$find = form.components.find(c => c.fieldId === useComponentInPage.use)) !== null && _form$components$find !== void 0 ? _form$components$find : form.components.find(c => c.id === useComponentInPage.use)) !== null && _ref !== void 0 ? _ref : useComponentInPage;
218
- // Retun clone of component, so subsequent processing can make changes to it without changing the form
219
- return JSON.parse(JSON.stringify(componentInForm));
220
- };
221
- const deleteCorrespondingMetaInfo = (component, collectionDataObject, formData) => {
222
- const {
223
- meta: {
224
- documents
225
- }
226
- } = formData;
227
- const fileDataBeingDeleted = collectionDataObject[component.fieldId];
228
- if (!documents || !fileDataBeingDeleted) return;
229
- const fileDataAsArray = Array.isArray(fileDataBeingDeleted) ? fileDataBeingDeleted : [fileDataBeingDeleted];
230
- const metaDocumentIndex = documents.findIndex(document => fileDataAsArray.some(fileItem => document.url === fileItem.url));
231
- if (metaDocumentIndex !== -1) {
232
- documents.splice(metaDocumentIndex, 1);
233
- }
234
- };
235
149
  const pruneCollectionEntry = (pathsToKeep, componentsToPrune, collectionDataObject, formData) => {
236
150
  componentsToPrune.forEach(component => {
237
151
  if (!pathsToKeep.has(component.fieldId)) {
238
152
  var _component$data5;
239
153
  if (component.type === "multifile") {
240
- deleteCorrespondingMetaInfo(component, collectionDataObject, formData);
154
+ Utils.deleteCorrespondingMetaInfo(component, collectionDataObject, formData);
241
155
  }
242
- (0, _deleteNodeByPath.default)(collectionDataObject, component.fieldId);
156
+ Utils.deleteNodeByPath(collectionDataObject, component.fieldId);
243
157
 
244
158
  // REFACTOR
245
159
  // 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.
@@ -248,42 +162,18 @@ const pruneCollectionEntry = (pathsToKeep, componentsToPrune, collectionDataObje
248
162
  component === null || component === void 0 || (_component$data6 = component.data) === null || _component$data6 === void 0 || (_component$data6 = _component$data6.options) === null || _component$data6 === void 0 || _component$data6.forEach(option => {
249
163
  var _option$nested2;
250
164
  (_option$nested2 = option.nested) === null || _option$nested2 === void 0 || _option$nested2.forEach(nested => {
251
- (0, _deleteNodeByPath.default)(collectionDataObject, getNestedQuestionPath(component.fieldId, nested.fieldId));
165
+ Utils.deleteNodeByPath(collectionDataObject, Utils.getNestedQuestionPath(component.fieldId, nested.fieldId));
252
166
  });
253
167
  });
254
168
  }
255
169
  }
256
170
  });
257
171
  };
258
- function removeEmptyArraysAndUnusedCollectionIDs(payload) {
259
- if (Array.isArray(payload)) {
260
- for (let i = payload.length - 1; i >= 0; i -= 1) {
261
- if (Array.isArray(payload[i]) && payload[i].length === 0) {
262
- payload.splice(i, 1);
263
- } else {
264
- removeEmptyArraysAndUnusedCollectionIDs(payload[i]); // Recurse for nested structures
265
- }
266
- }
267
- } else if (payload !== null && typeof payload === 'object') {
268
- Object.keys(payload).forEach(key => {
269
- if (Array.isArray(payload[key]) && payload[key].length === 0) {
270
- // If the array being removed has an activeId associated with it, remove it
271
- if (payload["".concat(key, "ActiveId")]) {
272
- delete payload["".concat(key, "ActiveId")];
273
- console.log("Deleted element : ".concat(key, "ActiveId}"));
274
- }
275
- delete payload["".concat(key, "ActiveId")];
276
- if (payload[key]) {
277
- delete payload[key];
278
- console.log("Deleted element : ".concat(key));
279
- }
280
- } else {
281
- removeEmptyArraysAndUnusedCollectionIDs(payload[key]); // Recurse for nested structures
282
- }
283
- });
284
- }
285
- }
286
172
  const clearOutUncompletedRoutes = (form, formData) => {
173
+ // Load components into maps keyed on id and field for subsequent efficient access
174
+ const componentByIdMap = new Map(form.components.map(c => [c.id, c]));
175
+ const componentByFieldIdMap = new Map(form.components.map(c => [c.fieldId, c]));
176
+
287
177
  // Recurse through the whole form, building a map of all fields and their owning component id.
288
178
  // This is required to build the component->dependency graph in the next step, as the dependencies reference fields, not components.
289
179
 
@@ -297,24 +187,29 @@ const clearOutUncompletedRoutes = (form, formData) => {
297
187
  } else {
298
188
  var _page$components;
299
189
  (_page$components = page.components) === null || _page$components === void 0 || _page$components.forEach(useComponentInPage => {
300
- const componentObj = findComponentDefinitionInForm(useComponentInPage, form);
190
+ const componentObj = Utils.findComponentDefinitionInForm(useComponentInPage, componentByIdMap, componentByFieldIdMap);
301
191
  recursivelyMapFieldsAndDeleteHiddenNested(page, componentObj, null, allComponents, componentsToKeep, formData);
302
192
  });
303
193
  }
304
194
  });
305
195
  const allDependencyRelationships = new Map();
306
- allComponents === null || allComponents === void 0 || allComponents.forEach(component => {
307
- recursivelyMapDependencies(component, allDependencyRelationships);
196
+ allComponents === null || allComponents === void 0 || allComponents.forEach(componentArray => {
197
+ componentArray === null || componentArray === void 0 || componentArray.forEach(component => {
198
+ recursivelyMapDependencies(component, allDependencyRelationships);
199
+ });
308
200
  });
309
201
 
310
202
  // Visit all components with 'show_whens' and resolve the dependency. Only delete the data at the leaf element, or when un-winding the recursion
311
203
  // to deal with the edge case that an element is dependent on a data item that is hidden by a different constraint
312
204
  const visited = new Set();
313
205
  allDependencyRelationships === null || allDependencyRelationships === void 0 || allDependencyRelationships.forEach((neighbours, dependentEntityPath) => {
314
- const dependentEntity = allComponents.get(dependentEntityPath);
315
- if (!visited.has(dependentEntity.id)) {
316
- recursivelyResolveDependencies(visited, dependentEntity, allDependencyRelationships, form, formData, allComponents, componentsToKeep, allCollections);
317
- }
206
+ // Each path that a component is dependent on may have > 1 possible dependent components, if a field is used twice
207
+ const dependentEntities = allComponents.get(dependentEntityPath);
208
+ dependentEntities === null || dependentEntities === void 0 || dependentEntities.forEach(dependentEntity => {
209
+ if (!visited.has(dependentEntity)) {
210
+ recursivelyResolveDependencies(visited, dependentEntity, allDependencyRelationships, form, formData, allComponents, componentsToKeep, allCollections);
211
+ }
212
+ });
318
213
  });
319
214
  allCollections === null || allCollections === void 0 || allCollections.forEach((collection, collectionName) => {
320
215
  const collectionDataArray = formData[collectionName];
@@ -327,14 +222,14 @@ const clearOutUncompletedRoutes = (form, formData) => {
327
222
  const componentsToPrune = new Map();
328
223
  (_collection$childPage2 = collection.childPages) === null || _collection$childPage2 === void 0 || _collection$childPage2.forEach(childPage => {
329
224
  var _childPage$components;
330
- const showPage = isShowEntity(childPage, dataForEvaluation);
225
+ const showPage = Utils.isShowEntity(childPage, dataForEvaluation);
331
226
  (_childPage$components = childPage.components) === null || _childPage$components === void 0 || _childPage$components.forEach(useComponentInPage => {
332
- const componentObj = findComponentDefinitionInForm(useComponentInPage, form);
227
+ const componentObj = Utils.findComponentDefinitionInForm(useComponentInPage, componentByIdMap, componentByFieldIdMap);
333
228
 
334
229
  // Non-data components can be ignored (eg html)
335
230
  if (!componentObj.fieldId) return;
336
- const showComponentOnPage = isShowEntity(useComponentInPage, dataForEvaluation);
337
- if (showPage && showComponentOnPage && isShowEntity(componentObj, dataForEvaluation)) {
231
+ const showComponentOnPage = Utils.isShowEntity(useComponentInPage, dataForEvaluation);
232
+ if (showPage && showComponentOnPage && Utils.isShowEntity(componentObj, dataForEvaluation)) {
338
233
  // There may be 2 components with the same path (eg quantity in EAB2), so don't delete hidden components if they are required elsewhere
339
234
  pathsToKeep.add(componentObj.fieldId);
340
235
  } else {
@@ -344,10 +239,10 @@ const clearOutUncompletedRoutes = (form, formData) => {
344
239
  });
345
240
  pruneCollectionEntry(pathsToKeep, componentsToPrune, collectionDataEntry, formData);
346
241
  });
347
- // Pruning this collection may have resulted in objects that are only left with their 'id' field, which isn't data but added by the renderer to find the activeId. If so, remove these objects
348
- if (collectionDataArray) removeObjectWithSingleIdFieldInPlace(collectionDataArray);
242
+ //
243
+ if (collectionDataArray) Utils.removeObjectWithSingleIdFieldInPlace(collectionDataArray);
349
244
  });
350
- removeEmptyArraysAndUnusedCollectionIDs(formData);
245
+ Utils.removeEmptyArraysAndUnusedCollectionIDs(formData);
351
246
  return formData;
352
247
  };
353
248
  var _default = exports.default = clearOutUncompletedRoutes;