@ukhomeoffice/cop-react-form-renderer 6.15.3-alpha → 6.15.4-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 (105) hide show
  1. package/dist/components/CollectionSummary/BannerStrip.scss +4 -0
  2. package/dist/components/FormPage/FormPage.js +8 -10
  3. package/dist/components/FormRenderer/FormRenderer.js +11 -10
  4. package/dist/components/FormRenderer/helpers/index.js +1 -3
  5. package/dist/components/FormRenderer/onPageAction.js +0 -8
  6. package/dist/components/FormRenderer/onPageAction.test.js +0 -5
  7. package/dist/hooks/useGetRequest.js +15 -15
  8. package/dist/hooks/useRefData.js +3 -2
  9. package/dist/utils/CollectionPage/mergeCollectionPages.js +0 -1
  10. package/dist/utils/Component/getComponentTests/getComponent.multifile.test.js +2 -1
  11. package/dist/utils/Component/getDefaultValueFromConfig.js +2 -1
  12. package/dist/utils/Condition/meetsCondition.js +26 -12
  13. package/dist/utils/Condition/meetsCondition.test.js +21 -0
  14. package/dist/utils/Data/getAutocompleteSource.js +68 -51
  15. package/dist/utils/Data/getAutocompleteSource.test.js +31 -18
  16. package/dist/utils/Operate/doesContainValue.js +34 -0
  17. package/dist/utils/Operate/doesContainValue.test.js +75 -0
  18. package/dist/utils/Operate/runPageOperations.js +2 -0
  19. package/dist/utils/Validate/validateOnPageLoad.js +23 -0
  20. package/dist/utils/Validate/validateOnPageLoad.test.js +88 -0
  21. package/package.json +4 -4
  22. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-chained-component-show-whens-containerised.json +0 -119
  23. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-chained-component-show-whens-dependent-component-nested-block.json +0 -112
  24. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-chained-component-show-whens-dependent-component-nested.json +0 -105
  25. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-chained-component-show-whens-refdata.json +0 -105
  26. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-chained-component-show-whens-target-component-nested.json +0 -98
  27. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-chained-component-show-whens.json +0 -105
  28. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-chained-show-whens-page-hidden.json +0 -112
  29. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-cop-airpax.json +0 -26407
  30. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-cop-mandec.json +0 -9404
  31. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-collection-component-dependent-on-external-data.json +0 -157
  32. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-collection-component.json +0 -85
  33. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-component-referring-to-collection.json +0 -172
  34. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-component-referring-to-hidden-collection.json +0 -201
  35. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-component-with-nested-questions.json +0 -111
  36. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-component.json +0 -57
  37. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-containerised-component.json +0 -96
  38. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-embedded-collection-component.json +0 -82
  39. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-embedded-component.json +0 -54
  40. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-multilevel-containerised-component-leaf-hidden.json +0 -105
  41. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-multilevel-containerised-component.json +0 -105
  42. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-options.json +0 -98
  43. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-page-collection.json +0 -127
  44. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-page-component-used-elsewhere.json +0 -108
  45. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-hidden-page.json +0 -95
  46. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/forms/form-nested-answers-hidden-by-option.json +0 -111
  47. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/cop-airpax-remove-photos-before.json +0 -384
  48. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/cop-mandec-remove-business-interests-before.json +0 -140
  49. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/cop-mandec-remove-criminality-before.json +0 -186
  50. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/cop-mandec-remove-unspent-convictions-before.json +0 -143
  51. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-chained-component-show-whens-containerised.json +0 -11
  52. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-chained-component-show-whens-dependent-component-nested-block.json +0 -10
  53. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-chained-component-show-whens-dependent-component-nested.json +0 -9
  54. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-chained-component-show-whens-refdata.json +0 -11
  55. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-chained-component-show-whens-target-component-nested.json +0 -9
  56. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-chained-component-show-whens.json +0 -9
  57. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-chained-show-whens-page-hidden.json +0 -9
  58. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-collection-component-dependent-on-external-data.json +0 -26
  59. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-collection-component.json +0 -15
  60. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-component-referring-to-collection.json +0 -25
  61. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-component-referring-to-hidden-collection.json +0 -26
  62. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-component-with-nested-questions.json +0 -12
  63. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-component.json +0 -5
  64. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-containerised-component.json +0 -11
  65. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-embedded-collection-component.json +0 -15
  66. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-embedded-component.json +0 -5
  67. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-multilevel-containerised-component-leaf-hidden.json +0 -14
  68. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-multilevel-containerised-component.json +0 -14
  69. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-options.json +0 -10
  70. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-page-collection.json +0 -18
  71. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-page-component-used-elsewhere.json +0 -9
  72. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-hidden-page.json +0 -9
  73. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/input/data-nested-answers-hidden-by-option.json +0 -12
  74. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/cop-airpax-remove-photos-after.json +0 -355
  75. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/cop-mandec-base-file.json +0 -80
  76. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/cop-mandec-remove-business-interests-after.json +0 -122
  77. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/cop-mandec-remove-criminality-after.json +0 -86
  78. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/cop-mandec-remove-unspent-convictions-after.json +0 -127
  79. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-chained-component-show-whens-containerised-removed.json +0 -11
  80. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-chained-component-show-whens-dependent-component-nested-block-removed.json +0 -7
  81. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-chained-component-show-whens-dependent-component-nested-removed.json +0 -7
  82. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-chained-component-show-whens-refdata-removed.json +0 -7
  83. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-chained-component-show-whens-removed.json +0 -7
  84. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-chained-component-show-whens-target-component-nested-removed.json +0 -6
  85. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-chained-show-whens-page-hidden-removed.json +0 -6
  86. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-collection-component-dependent-on-external-data-removed.json +0 -23
  87. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-collection-component-removed.json +0 -4
  88. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-component-referring-to-collection-removed.json +0 -21
  89. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-component-referring-to-hidden-collection-removed.json +0 -6
  90. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-component-removed.json +0 -4
  91. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-component-with-nested-questions-removed.json +0 -10
  92. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-containerised-component-removed.json +0 -10
  93. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-embedded-collection-component-removed.json +0 -4
  94. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-embedded-component-removed.json +0 -4
  95. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-multilevel-containerised-component-leaf-hidden-removed.json +0 -13
  96. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-multilevel-containerised-component-removed.json +0 -10
  97. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-options-removed.json +0 -8
  98. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-page-collection-removed.json +0 -15
  99. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-page-component-used-elsewhere-removed.json +0 -5
  100. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-hidden-page-removed.json +0 -4
  101. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/output/data-nested-answers-hidden-by-option-removed.json +0 -11
  102. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.js +0 -353
  103. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.test.js +0 -308
  104. package/dist/components/FormRenderer/helpers/deleteNodeByPath.js +0 -29
  105. package/dist/components/FormRenderer/helpers/deleteNodeByPath.test.js +0 -56
@@ -39,6 +39,10 @@
39
39
  }
40
40
  }
41
41
 
42
+ .hods-form-banner-strip__tag--fit-content{
43
+ max-width: fit-content;
44
+ }
45
+
42
46
  @mixin banner-tag-colours($colour) {
43
47
  .hods-form-banner-strip__tag--#{$colour} {
44
48
  @extend .govuk-tag--#{$colour};
@@ -12,6 +12,7 @@ var _utils = _interopRequireDefault(require("../../utils"));
12
12
  var _FormComponent = _interopRequireDefault(require("../FormComponent"));
13
13
  var _PageActions = _interopRequireDefault(require("../PageActions"));
14
14
  require("./FormPage.scss");
15
+ var _validateOnPageLoad = require("../../utils/Validate/validateOnPageLoad");
15
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
17
  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); }
17
18
  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; }
@@ -24,7 +25,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
24
25
  // Styles
25
26
  const DEFAULT_CLASS = exports.DEFAULT_CLASS = 'hods-form__page';
26
27
  const FormPage = _ref => {
27
- var _page$customValidatio, _formPage$actions, _page$actions;
28
+ var _formPage$actions, _page$actions;
28
29
  let {
29
30
  page,
30
31
  pages,
@@ -37,8 +38,7 @@ const FormPage = _ref => {
37
38
  classModifiers,
38
39
  className,
39
40
  submitting,
40
- pageId,
41
- fromTarget
41
+ pageId
42
42
  } = _ref;
43
43
  const [patch, setPatch] = (0, _react.useState)({});
44
44
  const {
@@ -47,12 +47,12 @@ const FormPage = _ref => {
47
47
  } = (0, _hooks.useValidation)();
48
48
  const [patchLabel, setPatchLabel] = (0, _react.useState)({});
49
49
  const [isPageRendered, setIsPageRendered] = (0, _react.useState)(false);
50
- const runOnLoad = Array.isArray(page === null || page === void 0 ? void 0 : page.customValidation) && page.customValidation.length > 0 ? ((_page$customValidatio = page.customValidation[0]) === null || _page$customValidatio === void 0 || (_page$customValidatio = _page$customValidatio.runWhen) === null || _page$customValidatio === void 0 ? void 0 : _page$customValidatio.onLoad) === true : false;
50
+ const runOnLoad = (0, _react.useMemo)(() => (0, _validateOnPageLoad.doValidateOnPageLoad)(page), [page]);
51
51
  (0, _react.useEffect)(() => {
52
52
  setIsPageRendered(true);
53
53
  }, []);
54
54
  (0, _react.useEffect)(() => {
55
- if (isPageRendered && fromTarget && runOnLoad) {
55
+ if (isPageRendered && runOnLoad) {
56
56
  validate.page(page);
57
57
  }
58
58
  }, [pageId, isPageRendered]);
@@ -120,9 +120,9 @@ const FormPage = _ref => {
120
120
  }));
121
121
  let errorMessages = null;
122
122
  if (runOnLoad && (errors === null || errors === void 0 ? void 0 : errors.length) > 0) {
123
- var _page$customValidatio2;
123
+ var _page$customValidatio;
124
124
  errorMessages = [{
125
- error: page === null || page === void 0 || (_page$customValidatio2 = page.customValidation) === null || _page$customValidatio2 === void 0 || (_page$customValidatio2 = _page$customValidatio2[0]) === null || _page$customValidatio2 === void 0 ? void 0 : _page$customValidatio2.message
125
+ error: page === null || page === void 0 || (_page$customValidatio = page.customValidation) === null || _page$customValidatio === void 0 || (_page$customValidatio = _page$customValidatio[0]) === null || _page$customValidatio === void 0 ? void 0 : _page$customValidatio.message
126
126
  }];
127
127
  } else if ((errors === null || errors === void 0 ? void 0 : errors.length) > 0 && !runOnLoad) {
128
128
  errorMessages = errors;
@@ -174,7 +174,6 @@ FormPage.propTypes = {
174
174
  }).isRequired,
175
175
  pages: _propTypes.default.arrayOf(_propTypes.default.shape({})),
176
176
  submitting: _propTypes.default.bool,
177
- fromTarget: _propTypes.default.bool,
178
177
  pageId: _propTypes.default.string.isRequired
179
178
  };
180
179
  FormPage.defaultProps = {
@@ -186,7 +185,6 @@ FormPage.defaultProps = {
186
185
  onTopLevelChange: undefined,
187
186
  onWrapperChange: undefined,
188
187
  pages: [],
189
- submitting: false,
190
- fromTarget: false
188
+ submitting: false
191
189
  };
192
190
  var _default = exports.default = FormPage;
@@ -54,8 +54,7 @@ const FormRenderer = _ref => {
54
54
  noChangeAction,
55
55
  newPageId,
56
56
  viewOnly,
57
- hideBlankRows,
58
- fromTarget
57
+ hideBlankRows
59
58
  } = _ref;
60
59
  return /*#__PURE__*/_react.default.createElement(_context.HooksContextProvider, {
61
60
  overrides: hooks
@@ -76,8 +75,7 @@ const FormRenderer = _ref => {
76
75
  noChangeAction: noChangeAction,
77
76
  newPageId: newPageId,
78
77
  viewOnly: viewOnly,
79
- hideBlankRows: hideBlankRows,
80
- fromTarget: fromTarget
78
+ hideBlankRows: hideBlankRows
81
79
  })));
82
80
  };
83
81
  const DEFAULT_CLASS = exports.DEFAULT_CLASS = 'hods-form';
@@ -100,8 +98,7 @@ const InternalFormRenderer = _ref2 => {
100
98
  noChangeAction,
101
99
  newPageId,
102
100
  viewOnly,
103
- hideBlankRows,
104
- fromTarget
101
+ hideBlankRows
105
102
  } = _ref2;
106
103
  // Set up the initial states.
107
104
  const [data, setData] = (0, _react.useState)({});
@@ -147,6 +144,12 @@ const InternalFormRenderer = _ref2 => {
147
144
  setGoingBack(true);
148
145
  hooks.onGoingBack(e.state ? e.state : null);
149
146
  if (components && pages && data && (_formState$page = formState.page) !== null && _formState$page !== void 0 && _formState$page.formData && pagePoint === undefined) {
147
+ var _formState$page$formD, _formState$page$formD2;
148
+ // clear current formTasks when going back
149
+ if ((_formState$page$formD = formState.page.formData) !== null && _formState$page$formD !== void 0 && (_formState$page$formD = _formState$page$formD.formStatus) !== null && _formState$page$formD !== void 0 && _formState$page$formD.tasks && (_formState$page$formD2 = formState.page.formData) !== null && _formState$page$formD2 !== void 0 && (_formState$page$formD2 = _formState$page$formD2.formStatus) !== null && _formState$page$formD2 !== void 0 && _formState$page$formD2.taskPage) {
150
+ formState.page.formData.formStatus.tasks = {};
151
+ formState.page.formData.formStatus.taskPage = null;
152
+ }
150
153
  const submissionData = _utils.default.Format.form({
151
154
  pages,
152
155
  components
@@ -326,8 +329,7 @@ const InternalFormRenderer = _ref2 => {
326
329
  classModifiers: formState.page.classModifiers,
327
330
  className: formState.page.className,
328
331
  submitting: submitting,
329
- pageId: pageId,
330
- fromTarget: fromTarget
332
+ pageId: pageId
331
333
  }), formState.page && !formState.cya && formState.page.collection && /*#__PURE__*/_react.default.createElement(_CollectionPage.default, {
332
334
  page: formState.page,
333
335
  pages: pages,
@@ -370,8 +372,7 @@ const propTypes = {
370
372
  /** See <a href="/?path=/docs/f-json--page#formtypes">FormTypes</a>. */
371
373
  type: _propTypes.default.oneOf([_models.FormTypes.CYA, _models.FormTypes.FORM, _models.FormTypes.HUB, _models.FormTypes.TASK, _models.FormTypes.WIZARD, _models.FormTypes.TASK_CYA, _models.FormTypes.FORM_WITH_TASK]).isRequired,
372
374
  viewOnly: _propTypes.default.bool,
373
- hideBlankRows: _propTypes.default.bool,
374
- fromTarget: _propTypes.default.bool.isRequired
375
+ hideBlankRows: _propTypes.default.bool
375
376
  };
376
377
  const defaultProps = {
377
378
  classBlock: DEFAULT_CLASS,
@@ -13,7 +13,6 @@ var _getNextPageId = _interopRequireDefault(require("./getNextPageId"));
13
13
  var _getPage = _interopRequireDefault(require("./getPage"));
14
14
  var _getSubmissionStatus = _interopRequireDefault(require("./getSubmissionStatus"));
15
15
  var _getUpdatedSectionStates = _interopRequireDefault(require("./getUpdatedSectionStates"));
16
- var _clearOutUncompletedRoutes = _interopRequireDefault(require("./clearOutUncompletedRoutes"));
17
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
17
  // Local imports
19
18
 
@@ -26,7 +25,6 @@ const helpers = {
26
25
  getNextPageId: _getNextPageId.default,
27
26
  getPage: _getPage.default,
28
27
  getSubmissionStatus: _getSubmissionStatus.default,
29
- getUpdatedSectionStates: _getUpdatedSectionStates.default,
30
- clearOutUncompletedRoutes: _clearOutUncompletedRoutes.default
28
+ getUpdatedSectionStates: _getUpdatedSectionStates.default
31
29
  };
32
30
  var _default = exports.default = helpers;
@@ -107,14 +107,6 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
107
107
  setData(submissionData);
108
108
  }
109
109
  ;
110
- const formPagesAndComponents = {
111
- pages,
112
- components
113
- };
114
- if (action.type === _models.PageAction.TYPES.SUBMIT) {
115
- console.log("Invoking clearOutUncompletedRoutes");
116
- _helpers.default.clearOutUncompletedRoutes(formPagesAndComponents, submissionData);
117
- }
118
110
 
119
111
  // In case of hub-and-spoke if patchLabel has changed then
120
112
  // save name and value to variables for call to onSubmit hook
@@ -46,11 +46,6 @@ jest.mock('./helpers', () => ({
46
46
  this.cleanHiddenNestedDataCalls += 1;
47
47
  return patch;
48
48
  },
49
- clearOutUncompletedRoutesCalls: 0,
50
- clearOutUncompletedRoutes(formPagesAndComponents, formData) {
51
- this.clearOutUncompletedRoutesCalls += 1;
52
- return formData;
53
- },
54
49
  getNextPageIdCalls: 0,
55
50
  getNextPageId() {
56
51
  this.getNextPageIdCalls += 1;
@@ -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 cancelToken = _axios.default.CancelToken.source();
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: cancelToken.token
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, cancelToken.token]);
80
+ }, [axiosInstance, url, caching]);
81
81
  return {
82
82
  status,
83
83
  error,
@@ -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, setData, setStatus]);
56
+ }, [component.id, _status, _data, url]);
56
57
  return {
57
58
  data,
58
59
  status
@@ -31,7 +31,6 @@ const createMasterPage = page => ({
31
31
  id: page.id,
32
32
  name: page.name,
33
33
  orderInForm: page.orderInForm,
34
- deleteCollectionWhenHidden: page.deleteCollectionWhenHidden,
35
34
  collection: _objectSpread({
36
35
  masterPage: true
37
36
  }, page.collection),
@@ -23,7 +23,8 @@ describe('utils.Component.get', () => {
23
23
  fieldId: FIELD_ID,
24
24
  label: LABEL,
25
25
  onChange: ON_CHANGE,
26
- 'data-testid': ID
26
+ 'data-testid': ID,
27
+ allowedTypes: []
27
28
  };
28
29
  const {
29
30
  container
@@ -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
- result = result !== null && result !== void 0 ? result : {};
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
- if (!value || !compare) {
70
- return false;
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
- if (!value || !compare) {
79
- return false;
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', () => {
@@ -7,63 +7,80 @@ exports.default = void 0;
7
7
  var _copReactComponents = require("@ukhomeoffice/cop-react-components");
8
8
  var _getOptions = _interopRequireDefault(require("./getOptions"));
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
- // Global Imports
11
-
12
- // Local imports
10
+ const MAX_NUMBER_RESULTS = 6000; // lower is better for performance, this still includes all ports
13
11
 
12
+ const assignFullMatchScore = (subStringMatchIndex, label) => {
13
+ const matchAtBeginning = subStringMatchIndex === 0;
14
+ let score = 0;
15
+ if (matchAtBeginning) {
16
+ score = 1;
17
+ }
18
+ if (!matchAtBeginning) {
19
+ const isCompleteWord = label.substring(subStringMatchIndex - 1).startsWith(" ");
20
+ score = isCompleteWord ? 2 : 3;
21
+ }
22
+ return score;
23
+ };
24
+ const noMatch = score => score === 0;
25
+ const lowerCaseMatch = (searchTerm, query) => typeof searchTerm !== 'string' ? false : searchTerm.toLowerCase().includes(query);
26
+ const assignSynonymsScore = (option, searchQuery) => {
27
+ let score = 0;
28
+ const isMatch = option.synonyms.some(synonym => lowerCaseMatch(synonym, searchQuery));
29
+ if (isMatch) {
30
+ score = 4;
31
+ }
32
+ return score;
33
+ };
34
+ const assignExtraFieldsScore = (config, option, searchQuery) => {
35
+ let score = 0;
36
+ const isMatch = config.item.extraFieldsToSearch.some(field => {
37
+ const fieldToSearch = option[field];
38
+ return lowerCaseMatch(fieldToSearch, searchQuery);
39
+ });
40
+ if (isMatch) {
41
+ score = 5;
42
+ }
43
+ return score;
44
+ };
45
+ const mapToScoreAndLabel = (option, searchQuery, labelMapper, config) => {
46
+ var _config$item;
47
+ const label = labelMapper ? labelMapper(option).toLowerCase() : option.label.toLowerCase() || '';
48
+ let score = 0;
49
+ const subStringMatchIndex = label.indexOf(searchQuery);
50
+ const isFullMatch = subStringMatchIndex !== -1;
51
+ if (isFullMatch) {
52
+ score = assignFullMatchScore(subStringMatchIndex, label);
53
+ }
54
+ if (noMatch(score) && option.synonyms) {
55
+ score = assignSynonymsScore(option, searchQuery);
56
+ }
57
+ if (noMatch(score) && (_config$item = config.item) !== null && _config$item !== void 0 && _config$item.extraFieldsToSearch) {
58
+ score = assignExtraFieldsScore(config, option, searchQuery);
59
+ }
60
+ return {
61
+ score,
62
+ label,
63
+ option
64
+ };
65
+ };
66
+ const scoreOptions = (searchQuery, labelMapper, config) => (scoredOptions, option) => {
67
+ const scoredOption = mapToScoreAndLabel(option, searchQuery, labelMapper, config);
68
+ if (scoredOption.score > 0) {
69
+ scoredOptions.push(scoredOption);
70
+ }
71
+ return scoredOptions;
72
+ };
73
+ const compareByScoreAndLabel = (r1, r2) => (r1.score - r2.score) * 16 + r1.label.localeCompare(r2.label);
14
74
  const getAutocompleteSource = config => {
15
75
  let options = [];
16
76
  (0, _getOptions.default)(config, val => {
17
77
  options = val;
18
78
  });
19
- const labelMaker = config !== null && config !== void 0 && config.item ? _copReactComponents.Utils.itemLabel(config.item) : null;
20
- return (query, populateResults) => {
21
- const lcQuery = query ? query.toLowerCase() : '';
22
-
23
- // go through all options and give them a grade
24
-
25
- const results = options.map(opt => {
26
- const label = labelMaker ? labelMaker(opt) : opt.label || '';
27
- const lcLabel = label.toLowerCase();
28
-
29
- // result 'score'
30
- let grade = 0;
31
-
32
- // highest result - match at start of string
33
- const index = lcLabel.indexOf(lcQuery);
34
- if (index === 0) {
35
- grade = 1;
36
- } else if (index > 0) {
37
- grade = lcLabel.substring(index - 1).startsWith(" ") ? 2 : 3;
38
- } else {
39
- var _config$item;
40
- if (opt.synonyms) {
41
- const match = opt.synonyms.some(synonym => synonym.toLowerCase().includes(lcQuery));
42
- if (match) {
43
- grade = 4;
44
- }
45
- }
46
- if (!grade && (_config$item = config.item) !== null && _config$item !== void 0 && _config$item.extraFieldsToSearch) {
47
- const found = config.item.extraFieldsToSearch.some(field => {
48
- if (typeof opt[field] !== 'string') {
49
- return false;
50
- }
51
- return opt[field].toLowerCase().includes(lcQuery);
52
- });
53
- if (found) grade = 5;
54
- }
55
- }
56
- return {
57
- grade,
58
- label,
59
- opt
60
- };
61
- }).filter(result => result.grade > 0);
62
-
63
- // sort results and then map to just opts
64
- populateResults(results
65
- // the sort prioritises grade but also sorts by string value
66
- .sort((r1, r2) => (r1.grade - r2.grade) * 16 + r1.label.localeCompare(r2.label)).map(r => r.opt));
79
+ const labelMapper = config !== null && config !== void 0 && config.item ? _copReactComponents.Utils.itemLabel(config.item) : null;
80
+ return (query, renderResult) => {
81
+ const searchQuery = typeof query === 'string' ? query.toLowerCase() : '';
82
+ const result = options.reduce(scoreOptions(searchQuery, labelMapper, config), []).sort(compareByScoreAndLabel).slice(0, MAX_NUMBER_RESULTS).map(scoredOptions => scoredOptions.option);
83
+ renderResult(result);
67
84
  };
68
85
  };
69
86
  var _default = exports.default = getAutocompleteSource;
@@ -6,10 +6,9 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbol
6
6
  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; }
7
7
  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; }
8
8
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
9
- 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); } // Local imports
9
+ 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); }
10
10
  describe('utils', () => {
11
11
  describe('Data', () => {
12
- // test data
13
12
  const gbCurrency = {
14
13
  currencyName: 'Great British Pounds',
15
14
  currencyCode: 'GBP',
@@ -25,7 +24,7 @@ describe('utils', () => {
25
24
  value: 'USD',
26
25
  label: 'USD',
27
26
  slangTerm: 'bucks',
28
- synonyms: ['greenbacks']
27
+ synonyms: ['greenbacks', 11]
29
28
  };
30
29
  const options = {
31
30
  alpha: {
@@ -81,6 +80,15 @@ describe('utils', () => {
81
80
  }]);
82
81
  });
83
82
  });
83
+ it('should only return first 6000 results that have some type of match', () => {
84
+ const moreThanMaxNumberOfOptions = Array(6001).fill(options.alpha);
85
+ const config = {
86
+ data: {
87
+ options: moreThanMaxNumberOfOptions
88
+ }
89
+ };
90
+ (0, _getAutocompleteSource.default)(config)(options.alpha.label, results => expect(results.length).toEqual(6000));
91
+ });
84
92
  it('should handle an empty query', () => {
85
93
  const CONFIG = {
86
94
  data: {
@@ -117,6 +125,18 @@ describe('utils', () => {
117
125
  expect(results).toEqual([options.alpha, options.bravo]);
118
126
  });
119
127
  });
128
+ it('should handle a non string query', () => {
129
+ const CONFIG = {
130
+ data: {
131
+ options: [options.alpha, options.bravo]
132
+ }
133
+ };
134
+ const SOURCE = (0, _getAutocompleteSource.default)(CONFIG);
135
+ SOURCE(1, results => {
136
+ expect(results.length).toEqual(2);
137
+ expect(results).toEqual([options.alpha, options.bravo]);
138
+ });
139
+ });
120
140
  it('should handle a missing label on an option', () => {
121
141
  const CONFIG = {
122
142
  data: {
@@ -171,8 +191,7 @@ describe('utils', () => {
171
191
  item: {
172
192
  value: 'currencyCode',
173
193
  label: 'currencyCode',
174
- // eslint-disable-next-line no-template-curly-in-string
175
- format: '${currencyName} (${currencyCode})'
194
+ format: "${currencyName} (${currencyCode})"
176
195
  },
177
196
  data: {
178
197
  options: testCurrencies
@@ -194,8 +213,7 @@ describe('utils', () => {
194
213
  item: {
195
214
  value: 'currencyCode',
196
215
  label: 'currencyCode',
197
- // eslint-disable-next-line no-template-curly-in-string
198
- format: '${currencyName} (${currencyCode})'
216
+ format: "${currencyName} (${currencyCode})"
199
217
  },
200
218
  data: {
201
219
  options: testCurrencies
@@ -212,8 +230,7 @@ describe('utils', () => {
212
230
  item: {
213
231
  value: 'currencyCode',
214
232
  label: 'currencyCode',
215
- // eslint-disable-next-line no-template-curly-in-string
216
- format: '${currencyName} (${currencyCode})'
233
+ format: "${currencyName} (${currencyCode})"
217
234
  },
218
235
  data: {
219
236
  options: testCurrencies
@@ -230,8 +247,7 @@ describe('utils', () => {
230
247
  item: {
231
248
  value: 'currencyCode',
232
249
  label: 'currencyCode',
233
- // eslint-disable-next-line no-template-curly-in-string
234
- format: '${currencyName} (${currencyCode})'
250
+ format: "${currencyName} (${currencyCode})"
235
251
  },
236
252
  data: {
237
253
  options: testCurrencies
@@ -248,8 +264,7 @@ describe('utils', () => {
248
264
  item: {
249
265
  value: 'currencyCode',
250
266
  label: 'currencyCode',
251
- // eslint-disable-next-line no-template-curly-in-string
252
- format: '${currencyName} (${currencyCode})'
267
+ format: "${currencyName} (${currencyCode})"
253
268
  },
254
269
  data: {
255
270
  options: [gbCurrency, {
@@ -281,8 +296,7 @@ describe('utils', () => {
281
296
  item: {
282
297
  value: 'currencyCode',
283
298
  label: 'currencyCode',
284
- // eslint-disable-next-line no-template-curly-in-string
285
- format: '${currencyName} (${currencyCode})'
299
+ format: "${currencyName} (${currencyCode})"
286
300
  },
287
301
  data: {
288
302
  options: testCurrencies
@@ -300,9 +314,8 @@ describe('utils', () => {
300
314
  item: {
301
315
  value: 'currencyCode',
302
316
  label: 'currencyCode',
303
- // eslint-disable-next-line no-template-curly-in-string
304
- format: '${currencyName} (${currencyCode})',
305
- extraFieldsToSearch: ['slangTerm']
317
+ format: "${currencyName} (${currencyCode})",
318
+ extraFieldsToSearch: ['slangTerm', null]
306
319
  },
307
320
  data: {
308
321
  options: testCurrencies