@ukhomeoffice/cop-react-form-renderer 5.62.1 → 5.65.0

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 (22) hide show
  1. package/dist/components/CollectionSummary/BannerStrip.js +3 -2
  2. package/dist/components/CollectionSummary/BannerStrip.test.js +39 -4
  3. package/dist/components/CollectionSummary/CollectionSummary.js +71 -33
  4. package/dist/components/CollectionSummary/CollectionSummary.test.js +40 -80
  5. package/dist/components/CollectionSummary/RenderListView.js +9 -7
  6. package/dist/components/CollectionSummary/RenderListView.scss +4 -0
  7. package/dist/components/CollectionSummary/RenderListView.test.js +13 -4
  8. package/dist/components/CollectionSummary/SummaryCard.js +26 -17
  9. package/dist/components/CollectionSummary/SummaryCard.test.js +177 -146
  10. package/dist/components/CollectionSummary/SummaryCardDetails.js +16 -0
  11. package/dist/components/CollectionSummary/SummaryCardDetails.test.js +77 -8
  12. package/dist/components/CollectionSummary/SummaryCardValidationContext.js +15 -5
  13. package/dist/components/CollectionSummary/SummaryCardValidationContext.test.js +5 -4
  14. package/dist/components/FormRenderer/onPageAction.js +6 -1
  15. package/dist/components/FormRenderer/onPageAction.test.js +18 -4
  16. package/dist/context/ValidationContext/ValidationContext.js +51 -5
  17. package/dist/context/ValidationContext/ValidationContext.test.js +16 -7
  18. package/dist/utils/CollectionPage/duplicateCollectionPageEntry.js +13 -1
  19. package/dist/utils/CollectionPage/duplicateCollectionPageEntry.test.js +17 -2
  20. package/dist/utils/Validate/validatePage.js +6 -1
  21. package/dist/utils/Validate/validatePage.test.js +44 -0
  22. package/package.json +1 -1
@@ -7,7 +7,9 @@ exports.default = exports.DEFAULT_CLASS = void 0;
7
7
  var _propTypes = _interopRequireDefault(require("prop-types"));
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _utils = _interopRequireDefault(require("../../utils"));
10
+ var _models = require("../../models");
10
11
  var _getComponentRowForCYA = _interopRequireDefault(require("../../utils/CheckYourAnswers/getComponentRowForCYA"));
12
+ var _getCYARowsForContainer = _interopRequireDefault(require("../../utils/CheckYourAnswers/getCYARowsForContainer"));
11
13
  require("./SummaryCardDetails.scss");
12
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
13
15
  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 && Object.prototype.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; }
@@ -84,6 +86,20 @@ var SummaryCardDetails = function SummaryCardDetails(_ref) {
84
86
  if (!component) {
85
87
  return null;
86
88
  }
89
+ if (component.type === _models.ComponentTypes.CONTAINER) {
90
+ var containerRows = (0, _getCYARowsForContainer.default)(childPage, component, entryData);
91
+ return /*#__PURE__*/_react.default.createElement("div", {
92
+ key: fieldId,
93
+ className: classes('field')
94
+ }, containerRows.map(function (subComponent) {
95
+ var _entryData$component$;
96
+ // Put value for current subcomponent at top level
97
+ var modEntry = _objectSpread(_objectSpread({}, entryData), _defineProperty({}, subComponent.fieldId, (_entryData$component$ = entryData[component.fieldId]) === null || _entryData$component$ === void 0 ? void 0 : _entryData$component$[subComponent.fieldId]));
98
+ return (0, _getComponentRowForCYA.default)(childPage, _objectSpread(_objectSpread({}, subComponent), {
99
+ label: subComponent.key
100
+ }), classes, modEntry);
101
+ }));
102
+ }
87
103
  return /*#__PURE__*/_react.default.createElement("div", {
88
104
  key: fieldId,
89
105
  className: classes('field')
@@ -82,6 +82,75 @@ describe('components.CollectionSummary.SummaryCardDetails', function () {
82
82
  var section2Content = section2Title.parentNode.querySelector(".".concat(classes('section-content')));
83
83
  expect(section2Content.querySelectorAll(".".concat(classes('field'))).length).toEqual(1);
84
84
  });
85
+ it('should render sections containing containers based on summaryLayout config', function () {
86
+ var CHILD_PAGES = [{
87
+ summaryLayout: {
88
+ sections: [{
89
+ title: 'Section 1',
90
+ columns: 1,
91
+ fields: ['containerComponent']
92
+ }]
93
+ },
94
+ components: [{
95
+ fieldId: 'containerComponent',
96
+ type: 'container',
97
+ label: 'container',
98
+ components: [{
99
+ fieldId: 'subOne',
100
+ label: 'subOneLabel'
101
+ }, {
102
+ fieldId: 'subTwo',
103
+ label: 'subTwoLabel'
104
+ }, {
105
+ fieldId: 'subThree',
106
+ label: 'subThreeLabel'
107
+ }]
108
+ }]
109
+ }];
110
+ var CONTAINER_ENTRY = {
111
+ id: '001',
112
+ bannerText: 'A banner',
113
+ titleText: 'A title',
114
+ detailsText: 'Some details',
115
+ index: 0,
116
+ summaryText: 'Full details',
117
+ containerComponent: {
118
+ subOne: 'alpha',
119
+ subTwo: 'bravo',
120
+ subThree: 'charlie'
121
+ }
122
+ };
123
+ var MASTER_PAGE = {
124
+ childPages: CHILD_PAGES
125
+ };
126
+ var _renderWithValidation2 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
127
+ masterPage: MASTER_PAGE,
128
+ childMasterPages: [],
129
+ formData: {},
130
+ entryData: CONTAINER_ENTRY
131
+ })),
132
+ container = _renderWithValidation2.container;
133
+
134
+ // Function to find an element by its text content
135
+ function getByTextContent(parent, text) {
136
+ return Array.from(parent.getElementsByClassName(classes('section-title'))).find(function (el) {
137
+ return el.textContent === text;
138
+ });
139
+ }
140
+
141
+ // Check for section title
142
+ var section1Title = getByTextContent(container, 'Section 1');
143
+ expect(section1Title).not.toBeUndefined();
144
+
145
+ // All three subcomponents from the container should have labels and values displayed
146
+ var section1Content = section1Title.parentNode.querySelector(".".concat(classes('section-content')));
147
+ expect(section1Content.textContent).toContain('subOneLabel');
148
+ expect(section1Content.textContent).toContain('alpha');
149
+ expect(section1Content.textContent).toContain('subTwoLabel');
150
+ expect(section1Content.textContent).toContain('bravo');
151
+ expect(section1Content.textContent).toContain('subThreeLabel');
152
+ expect(section1Content.textContent).toContain('charlie');
153
+ });
85
154
  it('should render sections if they have show_when checks and pass them', function () {
86
155
  var CHILD_PAGES = [{
87
156
  summaryLayout: {
@@ -114,13 +183,13 @@ describe('components.CollectionSummary.SummaryCardDetails', function () {
114
183
  var CUSTOM_ENTRY = _objectSpread(_objectSpread({}, ENTRY), {}, {
115
184
  showSection1: true
116
185
  });
117
- var _renderWithValidation2 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
186
+ var _renderWithValidation3 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
118
187
  masterPage: MASTER_PAGE,
119
188
  childMasterPages: [],
120
189
  formData: {},
121
190
  entryData: CUSTOM_ENTRY
122
191
  })),
123
- container = _renderWithValidation2.container;
192
+ container = _renderWithValidation3.container;
124
193
 
125
194
  // Function to find an element by its text content
126
195
  function getByTextContent(parent, text) {
@@ -171,13 +240,13 @@ describe('components.CollectionSummary.SummaryCardDetails', function () {
171
240
  var CUSTOM_ENTRY = _objectSpread(_objectSpread({}, ENTRY), {}, {
172
241
  showSection1: false
173
242
  });
174
- var _renderWithValidation3 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
243
+ var _renderWithValidation4 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
175
244
  masterPage: MASTER_PAGE,
176
245
  childMasterPages: [],
177
246
  formData: {},
178
247
  entryData: CUSTOM_ENTRY
179
248
  })),
180
- container = _renderWithValidation3.container;
249
+ container = _renderWithValidation4.container;
181
250
 
182
251
  // Function to find an element by its text content
183
252
  function getByTextContent(parent, text) {
@@ -231,7 +300,7 @@ describe('components.CollectionSummary.SummaryCardDetails', function () {
231
300
  var CUSTOM_ENTRY = _objectSpread(_objectSpread({}, ENTRY), {}, {
232
301
  showSection1: false
233
302
  });
234
- var _renderWithValidation4 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
303
+ var _renderWithValidation5 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
235
304
  masterPage: MASTER_PAGE,
236
305
  childMasterPages: [],
237
306
  formData: {
@@ -239,7 +308,7 @@ describe('components.CollectionSummary.SummaryCardDetails', function () {
239
308
  },
240
309
  entryData: CUSTOM_ENTRY
241
310
  })),
242
- container = _renderWithValidation4.container;
311
+ container = _renderWithValidation5.container;
243
312
 
244
313
  // Function to find an element by its text content
245
314
  function getByTextContent(parent, text) {
@@ -294,13 +363,13 @@ describe('components.CollectionSummary.SummaryCardDetails', function () {
294
363
  childPages: [].concat(CHILD_PAGES, CHILD_MASTER_PAGES)
295
364
  };
296
365
  var CHILD_COLLECTIONS = ['childCollection'];
297
- var _renderWithValidation5 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
366
+ var _renderWithValidation6 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardDetails.default, {
298
367
  masterPage: MASTER_PAGE,
299
368
  childCollections: CHILD_COLLECTIONS,
300
369
  formData: {},
301
370
  entryData: ENTRY
302
371
  })),
303
- container = _renderWithValidation5.container;
372
+ container = _renderWithValidation6.container;
304
373
 
305
374
  // Function to find an element by its text content
306
375
  function getByTextContent(parent, text) {
@@ -7,8 +7,9 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.default = void 0;
8
8
  var _react = _interopRequireWildcard(require("react"));
9
9
  var _propTypes = _interopRequireDefault(require("prop-types"));
10
- var _utils = _interopRequireDefault(require("../../utils"));
11
10
  var _ValidationContext = require("../../context/ValidationContext");
11
+ var _hooks = require("../../hooks");
12
+ var _utils = _interopRequireDefault(require("../../utils"));
12
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
14
15
  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 && Object.prototype.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; }
@@ -22,7 +23,11 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } /* eslint-
22
23
  var SummaryCardValidationContextProvider = function SummaryCardValidationContextProvider(_ref) {
23
24
  var entryId = _ref.entryId,
24
25
  topLevelErrors = _ref.topLevelErrors,
26
+ clearTopLevelErrorsForCard = _ref.clearTopLevelErrorsForCard,
25
27
  children = _ref.children;
28
+ var _useHooks = (0, _hooks.useHooks)(),
29
+ hooks = _useHooks.hooks;
30
+
26
31
  // Errors raised from the Collection Summary.
27
32
  var initialErrors = topLevelErrors.filter(function (e) {
28
33
  return e.entryId === entryId;
@@ -38,8 +43,10 @@ var SummaryCardValidationContextProvider = function SummaryCardValidationContext
38
43
  setErrors(null);
39
44
  };
40
45
  var addErrors = function addErrors(addedErrors) {
41
- var newErrors = errors !== null && errors !== void 0 ? errors : [];
42
- setErrors(newErrors.concat(addedErrors));
46
+ setErrors(function (prev) {
47
+ var newErrors = prev !== null && prev !== void 0 ? prev : [];
48
+ return newErrors.concat(addedErrors);
49
+ });
43
50
  };
44
51
 
45
52
  // This validate is only used by the Quick Edit page
@@ -47,8 +54,9 @@ var SummaryCardValidationContextProvider = function SummaryCardValidationContext
47
54
  var validate = {
48
55
  page: function page(_page) {
49
56
  var pageErrors = _utils.default.Validate.page(_page);
50
- setErrors(pageErrors);
51
- return pageErrors;
57
+ var allErrors = hooks.onValidate(_page, pageErrors);
58
+ setErrors(allErrors);
59
+ return allErrors;
52
60
  }
53
61
  };
54
62
  return /*#__PURE__*/_react.default.createElement(_ValidationContext.ValidationContext.Provider, {
@@ -56,6 +64,7 @@ var SummaryCardValidationContextProvider = function SummaryCardValidationContext
56
64
  addErrors: addErrors,
57
65
  errors: errors === null ? [].concat(initialErrors) : errors,
58
66
  resetQuickEditErrors: resetQuickEditErrors,
67
+ clearTopLevelErrorsForCard: clearTopLevelErrorsForCard,
59
68
  validate: validate
60
69
  }
61
70
  }, children);
@@ -64,6 +73,7 @@ var _default = exports.default = SummaryCardValidationContextProvider;
64
73
  SummaryCardValidationContextProvider.propTypes = {
65
74
  entryId: _propTypes.default.string.isRequired,
66
75
  topLevelErrors: _propTypes.default.arrayOf(_propTypes.default.shape({})),
76
+ clearTopLevelErrorsForCard: _propTypes.default.func.isRequired,
67
77
  children: _propTypes.default.node
68
78
  };
69
79
  SummaryCardValidationContextProvider.defaultProps = {
@@ -6,6 +6,7 @@ var _copReactComponents = require("@ukhomeoffice/cop-react-components");
6
6
  var _react2 = _interopRequireWildcard(require("react"));
7
7
  var _propTypes = _interopRequireDefault(require("prop-types"));
8
8
  var _hooks = require("../../hooks");
9
+ var _context3 = require("../../context");
9
10
  var _SummaryCardValidationContext = _interopRequireDefault(require("./SummaryCardValidationContext"));
10
11
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
12
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
@@ -36,10 +37,10 @@ describe('context.SummaryCardValidationContext', function () {
36
37
  return _regeneratorRuntime().wrap(function _callee$(_context) {
37
38
  while (1) switch (_context.prev = _context.next) {
38
39
  case 0:
39
- _render = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_SummaryCardValidationContext.default, {
40
+ _render = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_context3.HooksContextProvider, null, /*#__PURE__*/_react2.default.createElement(_SummaryCardValidationContext.default, {
40
41
  entryId: "123",
41
42
  topLevelErrors: []
42
- }, /*#__PURE__*/_react2.default.createElement(TestComponent, null))), container = _render.container;
43
+ }, /*#__PURE__*/_react2.default.createElement(TestComponent, null)))), container = _render.container;
43
44
  expect(container.childNodes.length).toEqual(4);
44
45
  expect(container.textContent).toContain('addErrors is a function');
45
46
  expect(container.textContent).toContain('resetQuickEditErrors is a function');
@@ -72,10 +73,10 @@ describe('context.SummaryCardValidationContext', function () {
72
73
  id: 'testid',
73
74
  error: 'error message four'
74
75
  }];
75
- _render2 = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_SummaryCardValidationContext.default, {
76
+ _render2 = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_context3.HooksContextProvider, null, /*#__PURE__*/_react2.default.createElement(_SummaryCardValidationContext.default, {
76
77
  entryId: "123",
77
78
  topLevelErrors: TOP_LEVEL_ERRORS
78
- }, /*#__PURE__*/_react2.default.createElement(TestComponent, null))), container = _render2.container;
79
+ }, /*#__PURE__*/_react2.default.createElement(TestComponent, null)))), container = _render2.container;
79
80
  expect(container.childNodes.length).toEqual(5);
80
81
  expect(container.textContent).toContain('addErrors is a function');
81
82
  expect(container.textContent).toContain('resetQuickEditErrors is a function');
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _models = require("../../models");
8
8
  var _utils = _interopRequireDefault(require("../../utils"));
9
+ var _setCollectionPageData = _interopRequireDefault(require("../../utils/CollectionPage/setCollectionPageData"));
9
10
  var _handlers = _interopRequireDefault(require("./handlers"));
10
11
  var _helpers = _interopRequireDefault(require("./helpers"));
11
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -48,7 +49,11 @@ var onPageAction = function onPageAction(action, patch, patchLabel, hooks, data,
48
49
  if (action.addToFormData) {
49
50
  var operations = Array.isArray(action.addToFormData) ? action.addToFormData : [action.addToFormData];
50
51
  operations.forEach(function (op) {
51
- _utils.default.Data.setDataItem(formState.page.formData, op.field, op.value);
52
+ if (op.isCollection) {
53
+ (0, _setCollectionPageData.default)(op.field, op.value, formState.page.formData);
54
+ } else {
55
+ _utils.default.Data.setDataItem(formState.page.formData, op.field, op.value);
56
+ }
52
57
  });
53
58
  form.page.formData[action.addToFormData.field] = action.addToFormData.value;
54
59
  }
@@ -576,10 +576,16 @@ describe('components.FormRenderer.onPageAction', function () {
576
576
  var ACTION = {
577
577
  type: actionType,
578
578
  collection: 'testCollection',
579
- addToFormData: {
579
+ addToFormData: [{
580
580
  field: 'alpha',
581
581
  value: '123'
582
- }
582
+ }, {
583
+ field: 'parent.child',
584
+ value: [{
585
+ id: '12345'
586
+ }],
587
+ isCollection: true
588
+ }]
583
589
  };
584
590
  var CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
585
591
  formState: FORM_STATE,
@@ -591,6 +597,7 @@ describe('components.FormRenderer.onPageAction', function () {
591
597
  expect(FORM_STATE.page.formData).toMatchObject({
592
598
  alpha: '123'
593
599
  });
600
+ expect(FORM_STATE.page.formData.parent[0].child[0].id).toEqual('12345');
594
601
  });
595
602
  });
596
603
  it("should work for the ".concat(_models.PageAction.TYPES.NAVIGATE, " action type"), function () {
@@ -604,10 +611,16 @@ describe('components.FormRenderer.onPageAction', function () {
604
611
  var ACTION = {
605
612
  type: _models.PageAction.TYPES.NAVIGATE,
606
613
  collection: 'testCollection',
607
- addToFormData: {
614
+ addToFormData: [{
608
615
  field: 'alpha',
609
616
  value: '123'
610
- }
617
+ }, {
618
+ field: 'parent.child',
619
+ value: [{
620
+ id: '12345'
621
+ }],
622
+ isCollection: true
623
+ }]
611
624
  };
612
625
  var CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
613
626
  formState: FORM_STATE,
@@ -621,6 +634,7 @@ describe('components.FormRenderer.onPageAction', function () {
621
634
  expect(FORM_STATE.page.formData).toMatchObject({
622
635
  alpha: '123'
623
636
  });
637
+ expect(FORM_STATE.page.formData.parent[0].child[0].id).toEqual('12345');
624
638
  });
625
639
  it('should work for an array of formData', function () {
626
640
  var FORM_STATE = {
@@ -31,6 +31,45 @@ var ValidationContextProvider = function ValidationContextProvider(_ref) {
31
31
  _useState2 = _slicedToArray(_useState, 2),
32
32
  errors = _useState2[0],
33
33
  setErrors = _useState2[1];
34
+ var _useState3 = (0, _react.useState)([]),
35
+ _useState4 = _slicedToArray(_useState3, 2),
36
+ queuedErrors = _useState4[0],
37
+ setQueuedErrors = _useState4[1];
38
+
39
+ /**
40
+ * Queues errors to be displayed when the next validation takes place.
41
+ *
42
+ * @param {array} errors An array of errors to queue.
43
+ */
44
+ var enqueueErrors = function enqueueErrors(errors) {
45
+ setQueuedErrors(function (prev) {
46
+ return [].concat(prev, errors).filter(function (e) {
47
+ return !!e;
48
+ });
49
+ });
50
+ };
51
+
52
+ /**
53
+ * Removed errors from the queue if they match the given criteria.
54
+ *
55
+ * @param {function} matcher A function that should take an error and return true if that
56
+ * error should be removed from the queue.
57
+ */
58
+ var dequeueErrors = function dequeueErrors(matcher) {
59
+ if (typeof matcher !== 'function') {
60
+ return;
61
+ }
62
+ setQueuedErrors(function (prev) {
63
+ return prev.filter(function (e) {
64
+ return !matcher(e);
65
+ });
66
+ });
67
+ };
68
+ var clearQueuedErrors = function clearQueuedErrors() {
69
+ if (queuedErrors.length) {
70
+ setQueuedErrors([]);
71
+ }
72
+ };
34
73
  var addErrors = /*#__PURE__*/function () {
35
74
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(errors) {
36
75
  return _regeneratorRuntime().wrap(function _callee$(_context) {
@@ -56,8 +95,9 @@ var ValidationContextProvider = function ValidationContextProvider(_ref) {
56
95
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
57
96
  while (1) switch (_context2.prev = _context2.next) {
58
97
  case 0:
98
+ setQueuedErrors([]);
59
99
  setErrors([]);
60
- case 1:
100
+ case 2:
61
101
  case "end":
62
102
  return _context2.stop();
63
103
  }
@@ -69,19 +109,21 @@ var ValidationContextProvider = function ValidationContextProvider(_ref) {
69
109
  }();
70
110
  var validate = {
71
111
  page: function page(_page) {
72
- var pageErrors = _utils.default.Validate.page(_page);
112
+ var pageErrors = _utils.default.Validate.page(_page, queuedErrors);
73
113
  var allErrors = hooks.onValidate(_page, pageErrors);
74
114
  setErrors(allErrors);
115
+ setQueuedErrors([]);
75
116
  return allErrors;
76
117
  },
77
118
  pages: function pages(_pages) {
78
- var allPagesErrors = _pages.map(function (page) {
79
- return _utils.default.Validate.page(page);
119
+ var pageErrors = _pages.map(function (page) {
120
+ return _utils.default.Validate.page(page, queuedErrors);
80
121
  });
81
122
  var allErrors = _pages.flatMap(function (page, index) {
82
- return hooks.onValidate(page, allPagesErrors[index]);
123
+ return hooks.onValidate(page, pageErrors[index]);
83
124
  });
84
125
  setErrors(allErrors);
126
+ setQueuedErrors([]);
85
127
  return allErrors;
86
128
  }
87
129
  };
@@ -90,6 +132,10 @@ var ValidationContextProvider = function ValidationContextProvider(_ref) {
90
132
  errors: errors,
91
133
  addErrors: addErrors,
92
134
  clearErrors: clearErrors,
135
+ queuedErrors: queuedErrors,
136
+ enqueueErrors: enqueueErrors,
137
+ dequeueErrors: dequeueErrors,
138
+ clearQueuedErrors: clearQueuedErrors,
93
139
  validate: validate
94
140
  }
95
141
  }, children);
@@ -6,8 +6,8 @@ var _copReactComponents = require("@ukhomeoffice/cop-react-components");
6
6
  var _react2 = _interopRequireWildcard(require("react"));
7
7
  var _propTypes = _interopRequireDefault(require("prop-types"));
8
8
  var _hooks = require("../../hooks");
9
- var _ValidationContext = _interopRequireDefault(require("./ValidationContext"));
10
9
  var _HooksContext = _interopRequireDefault(require("../HooksContext"));
10
+ var _ValidationContext = _interopRequireDefault(require("./ValidationContext"));
11
11
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
12
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
13
13
  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 && Object.prototype.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; }
@@ -20,6 +20,9 @@ var TestComponent = function TestComponent(_ref) {
20
20
  var _useValidation = (0, _hooks.useValidation)(),
21
21
  addErrors = _useValidation.addErrors,
22
22
  clearErrors = _useValidation.clearErrors,
23
+ enqueueErrors = _useValidation.enqueueErrors,
24
+ dequeueErrors = _useValidation.dequeueErrors,
25
+ clearQueuedErrors = _useValidation.clearQueuedErrors,
23
26
  errors = _useValidation.errors,
24
27
  validate = _useValidation.validate;
25
28
  (0, _react2.useEffect)(function () {
@@ -27,7 +30,7 @@ var TestComponent = function TestComponent(_ref) {
27
30
  addErrors(customErrors);
28
31
  }
29
32
  }, [customErrors]);
30
- return /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, typeof addErrors === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "addErrors is a function"), typeof clearErrors === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "clearErrors is a function"), typeof validate.page === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "validate.page is a function"), Array.isArray(errors) && /*#__PURE__*/_react2.default.createElement("span", null, "errors is an array of length ", errors.length), (errors === null || errors === void 0 ? void 0 : errors.length) > 0 && /*#__PURE__*/_react2.default.createElement(_copReactComponents.ErrorSummary, {
33
+ return /*#__PURE__*/_react2.default.createElement(_react2.default.Fragment, null, typeof addErrors === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "addErrors is a function"), typeof clearErrors === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "clearErrors is a function"), typeof enqueueErrors === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "enqueueErrors is a function"), typeof dequeueErrors === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "dequeueErrors is a function"), typeof clearQueuedErrors === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "clearQueuedErrors is a function"), typeof validate.page === 'function' && /*#__PURE__*/_react2.default.createElement("span", null, "validate.page is a function"), Array.isArray(errors) && /*#__PURE__*/_react2.default.createElement("span", null, "errors is an array of length ", errors.length), (errors === null || errors === void 0 ? void 0 : errors.length) > 0 && /*#__PURE__*/_react2.default.createElement(_copReactComponents.ErrorSummary, {
31
34
  errors: errors
32
35
  }));
33
36
  };
@@ -38,12 +41,15 @@ describe('context.ValidationContext', function () {
38
41
  while (1) switch (_context.prev = _context.next) {
39
42
  case 0:
40
43
  _render = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_HooksContext.default, null, /*#__PURE__*/_react2.default.createElement(_ValidationContext.default, null, /*#__PURE__*/_react2.default.createElement(TestComponent, null)))), container = _render.container;
41
- expect(container.childNodes.length).toEqual(4);
44
+ expect(container.childNodes.length).toEqual(7);
42
45
  expect(container.textContent).toContain('addErrors is a function');
43
46
  expect(container.textContent).toContain('clearErrors is a function');
47
+ expect(container.textContent).toContain('enqueueErrors is a function');
48
+ expect(container.textContent).toContain('dequeueErrors is a function');
49
+ expect(container.textContent).toContain('clearQueuedErrors is a function');
44
50
  expect(container.textContent).toContain('validate.page is a function');
45
51
  expect(container.textContent).toContain('errors is an array of length 0');
46
- case 6:
52
+ case 9:
47
53
  case "end":
48
54
  return _context.stop();
49
55
  }
@@ -61,16 +67,19 @@ describe('context.ValidationContext', function () {
61
67
  _render2 = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_HooksContext.default, null, /*#__PURE__*/_react2.default.createElement(_ValidationContext.default, null, /*#__PURE__*/_react2.default.createElement(TestComponent, {
62
68
  customErrors: CUSTOM_ERRORS
63
69
  })))), container = _render2.container;
64
- expect(container.childNodes.length).toEqual(5);
70
+ expect(container.childNodes.length).toEqual(8);
65
71
  expect(container.textContent).toContain('addErrors is a function');
66
72
  expect(container.textContent).toContain('clearErrors is a function');
73
+ expect(container.textContent).toContain('enqueueErrors is a function');
74
+ expect(container.textContent).toContain('dequeueErrors is a function');
75
+ expect(container.textContent).toContain('clearQueuedErrors is a function');
67
76
  expect(container.textContent).toContain('validate.page is a function');
68
77
  expect(container.textContent).toContain('errors is an array of length 1');
69
- errorSummary = container.childNodes[4];
78
+ errorSummary = container.childNodes[7];
70
79
  expect(errorSummary.tagName).toEqual('DIV');
71
80
  expect(errorSummary.classList).toContain('govuk-error-summary');
72
81
  expect(errorSummary.textContent).toContain(CUSTOM_ERRORS[0].error);
73
- case 11:
82
+ case 14:
74
83
  case "end":
75
84
  return _context2.stop();
76
85
  }
@@ -12,8 +12,20 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
12
12
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
13
13
  function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
14
14
  function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
15
+ /**
16
+ * Duplicates an entry with the given ID in the given collection, optionally
17
+ * ignoring named fields or adding extras to the duplicate if they're provided.
18
+ *
19
+ * @param {string} collectionName The name of the collection the entry to duplicate is in.
20
+ * @param {object} formData The top-level formData object.
21
+ * @param {string} entryId The ID of the entry to duplicate.
22
+ * @param {array} fieldsToIgnore An array of field names that shouldn't be copied to the duplicate.
23
+ * @param {object} fieldsToAdd An object that will be spread to the new entry.
24
+ * @returns The ID of the duplicate entry.
25
+ */
15
26
  var duplicateCollectionPageEntry = function duplicateCollectionPageEntry(collectionName, formData, entryId) {
16
27
  var fieldsToIgnore = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
28
+ var fieldsToAdd = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
17
29
  var collectionData = (0, _getCollectionPageData.default)(collectionName, formData);
18
30
  var entryToDuplicate = collectionData === null || collectionData === void 0 ? void 0 : collectionData.find(function (entry) {
19
31
  return entry.id === entryId;
@@ -22,7 +34,7 @@ var duplicateCollectionPageEntry = function duplicateCollectionPageEntry(collect
22
34
  return null;
23
35
  }
24
36
  var newEntryId = Date.now().toString();
25
- var newEntry = _objectSpread(_objectSpread({}, entryToDuplicate), {}, {
37
+ var newEntry = _objectSpread(_objectSpread(_objectSpread({}, entryToDuplicate), fieldsToAdd), {}, {
26
38
  id: newEntryId
27
39
  });
28
40
  fieldsToIgnore.forEach(function (field) {
@@ -65,10 +65,25 @@ describe('utils.CollectionPage.duplicateCollectionPageEntry', function () {
65
65
  expect(FORM_DATA[COLLECTION_NAME][1].id).toBeTruthy();
66
66
  expect(FORM_DATA[COLLECTION_NAME][1].value).toBeUndefined();
67
67
  });
68
- it('should do nothing if an entry with the given id does not exist', function () {
69
- var _FORM_DATA3, _expect$toEqual;
68
+ it('should add fields from the fieldsToAdd object', function () {
69
+ var _FORM_DATA3;
70
70
  var FORM_DATA = (_FORM_DATA3 = {}, _defineProperty(_FORM_DATA3, "".concat(COLLECTION_NAME, "ActiveId"), '1'), _defineProperty(_FORM_DATA3, COLLECTION_NAME, [OBJ]), _FORM_DATA3);
71
71
  ;
72
+ var FIELDS_TO_ADD = {
73
+ addedKey: 'addedValue'
74
+ };
75
+ (0, _duplicateCollectionPageEntry.default)(COLLECTION_NAME, FORM_DATA, '1', [], FIELDS_TO_ADD);
76
+ expect(FORM_DATA[COLLECTION_NAME].length).toEqual(2);
77
+ expect(FORM_DATA[COLLECTION_NAME][0].id).toBeTruthy();
78
+ expect(FORM_DATA[COLLECTION_NAME][0].value).toBeTruthy();
79
+ expect(FORM_DATA[COLLECTION_NAME][1].id).toBeTruthy();
80
+ expect(FORM_DATA[COLLECTION_NAME][1].value).toBeTruthy();
81
+ expect(FORM_DATA[COLLECTION_NAME][1].addedKey).toEqual('addedValue');
82
+ });
83
+ it('should do nothing if an entry with the given id does not exist', function () {
84
+ var _FORM_DATA4, _expect$toEqual;
85
+ var FORM_DATA = (_FORM_DATA4 = {}, _defineProperty(_FORM_DATA4, "".concat(COLLECTION_NAME, "ActiveId"), '1'), _defineProperty(_FORM_DATA4, COLLECTION_NAME, [OBJ]), _FORM_DATA4);
86
+ ;
72
87
  (0, _duplicateCollectionPageEntry.default)(COLLECTION_NAME, FORM_DATA, '0');
73
88
  expect(FORM_DATA).toEqual((_expect$toEqual = {}, _defineProperty(_expect$toEqual, "".concat(COLLECTION_NAME, "ActiveId"), '1'), _defineProperty(_expect$toEqual, COLLECTION_NAME, [OBJ]), _expect$toEqual));
74
89
  });
@@ -19,9 +19,11 @@ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input ==
19
19
  /**
20
20
  * Validate all of the components on a page.
21
21
  * @param {object} page The page to validate
22
+ * @param {array} queuedErrors Any errors that have been found before this validation.
22
23
  * @returns An array containing all of the errors.
23
24
  */
24
25
  var validatePage = function validatePage(page) {
26
+ var queuedErrors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
25
27
  var data = page.formData;
26
28
  if (page.collection) {
27
29
  var activeIndex = _CollectionPage.default.getActiveIndex(page.collection.name, page.formData);
@@ -32,7 +34,10 @@ var validatePage = function validatePage(page) {
32
34
  }
33
35
  if ((0, _showFormPage.default)(page, data) && Array.isArray(page.components)) {
34
36
  var errs = page.components.reduce(function (errors, component) {
35
- var componentErrors = (0, _validateComponent.default)(component, data, data);
37
+ var queuedComponentErrors = queuedErrors.length > 0 ? queuedErrors.filter(function (e) {
38
+ return e.showFor ? e.showFor === component.id : e.id === component.id;
39
+ }) : [];
40
+ var componentErrors = queuedComponentErrors.concat((0, _validateComponent.default)(component, data, data));
36
41
  return errors.concat(componentErrors).flat().map(function (err) {
37
42
  if (err) {
38
43
  return _objectSpread(_objectSpread({}, err), {}, {
@@ -719,4 +719,48 @@ describe('utils.Validate.Page', function () {
719
719
  });
720
720
  });
721
721
  });
722
+ describe('when there are queued errors', function () {
723
+ it('should return any errors queued for a component as well as errors found during validation', function () {
724
+ var COMPONENTS = [setup('a', _models.ComponentTypes.TEXT, 'Alpha', true)];
725
+ var PAGE = {
726
+ components: COMPONENTS,
727
+ formData: null
728
+ };
729
+ var QUEUED_ERRORS = [{
730
+ id: 'a',
731
+ error: 'Error queued for Alpha 1'
732
+ }, {
733
+ id: 'a',
734
+ error: 'Error queued for Alpha 2'
735
+ }];
736
+ var RESULT = (0, _validatePage.default)(PAGE, QUEUED_ERRORS);
737
+ expect(RESULT.length).toEqual(3);
738
+ expect(RESULT[0]).toEqual(_objectSpread({}, QUEUED_ERRORS[0]));
739
+ expect(RESULT[1]).toEqual(_objectSpread({}, QUEUED_ERRORS[1]));
740
+ expect(RESULT[2]).toEqual({
741
+ id: 'a',
742
+ error: 'Alpha is required'
743
+ });
744
+ });
745
+ it('should return any errors queued for a component even if no errors are found during validation', function () {
746
+ var COMPONENTS = [setup('a', _models.ComponentTypes.TEXT, 'Alpha', true)];
747
+ var PAGE = {
748
+ components: COMPONENTS,
749
+ formData: {
750
+ a: 'Value'
751
+ }
752
+ };
753
+ var QUEUED_ERRORS = [{
754
+ id: 'a',
755
+ error: 'Error queued for Alpha 1'
756
+ }, {
757
+ id: 'a',
758
+ error: 'Error queued for Alpha 2'
759
+ }];
760
+ var RESULT = (0, _validatePage.default)(PAGE, QUEUED_ERRORS);
761
+ expect(RESULT.length).toEqual(2);
762
+ expect(RESULT[0]).toEqual(_objectSpread({}, QUEUED_ERRORS[0]));
763
+ expect(RESULT[1]).toEqual(_objectSpread({}, QUEUED_ERRORS[1]));
764
+ });
765
+ });
722
766
  });