@ukhomeoffice/cop-react-form-renderer 3.2.2 → 3.2.5

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.
@@ -140,8 +140,9 @@ var FormComponent = function FormComponent(_ref) {
140
140
  return _utils.default.Component.get(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, attrs), component), {}, {
141
141
  id: component.full_path || component.id
142
142
  }, (0, _helpers.getComponentError)(component, validation === null || validation === void 0 ? void 0 : validation.errors)), {}, {
143
- label: component.label || '',
144
- hint: component.hint || '',
143
+ label: _utils.default.interpolateString(component.label, formData),
144
+ content: _utils.default.interpolateString(component.content, formData),
145
+ hint: _utils.default.interpolateString(component.hint, formData),
145
146
  options: options,
146
147
  value: value || _utils.default.Component.defaultValue(component),
147
148
  onChange: onComponentChangeExtended,
@@ -51,6 +51,8 @@ var DEFAULT_CLASS = 'hods-form';
51
51
  exports.DEFAULT_CLASS = DEFAULT_CLASS;
52
52
 
53
53
  var FormPage = function FormPage(_ref) {
54
+ var _page$actions;
55
+
54
56
  var page = _ref.page,
55
57
  _onAction = _ref.onAction,
56
58
  classBlock = _ref.classBlock,
@@ -81,10 +83,15 @@ var FormPage = function FormPage(_ref) {
81
83
 
82
84
  var classes = _utils.default.classBuilder(classBlock, classModifiers, className);
83
85
 
86
+ page.actions = (_page$actions = page.actions) === null || _page$actions === void 0 ? void 0 : _page$actions.map(function (action) {
87
+ return action.label ? _objectSpread(_objectSpread({}, action), {}, {
88
+ label: _utils.default.interpolateString(action.label, page.formData)
89
+ }) : action;
90
+ });
84
91
  return /*#__PURE__*/_react.default.createElement("div", {
85
92
  className: classes('page'),
86
93
  key: page.id
87
- }, page.title && /*#__PURE__*/_react.default.createElement(_copReactComponents.LargeHeading, null, page.title), (errors === null || errors === void 0 ? void 0 : errors.length) > 0 && /*#__PURE__*/_react.default.createElement(_copReactComponents.ErrorSummary, {
94
+ }, page.title && /*#__PURE__*/_react.default.createElement(_copReactComponents.LargeHeading, null, _utils.default.interpolateString(page.title, page.formData)), (errors === null || errors === void 0 ? void 0 : errors.length) > 0 && /*#__PURE__*/_react.default.createElement(_copReactComponents.ErrorSummary, {
88
95
  errors: errors
89
96
  }), page.components.filter(function (c) {
90
97
  return _utils.default.Component.show(c, page.formData);
@@ -24,6 +24,18 @@ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try
24
24
 
25
25
  function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
26
26
 
27
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
28
+
29
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
30
+
31
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
32
+
33
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
34
+
35
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
36
+
37
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
38
+
27
39
  describe('components.FormPage', function () {
28
40
  describe('FormPage', function () {
29
41
  var TEXT = {
@@ -32,6 +44,25 @@ describe('components.FormPage', function () {
32
44
  type: 'text',
33
45
  label: 'Text component',
34
46
  hint: 'Text hint'
47
+ }; // eslint-disable-next-line no-template-curly-in-string
48
+
49
+ var TEXT_WITH_EXPRESSION = {
50
+ id: 'text1',
51
+ fieldId: 'text1',
52
+ type: 'text',
53
+ label: 'Email to ${wrapper.email}',
54
+ hint: 'Text hint ${currentUser.givenName}'
55
+ }; // eslint-disable-next-line no-template-curly-in-string
56
+
57
+ var AUTO_WITH_EXPRESSION = {
58
+ id: 'text2',
59
+ fieldId: 'text2',
60
+ label: '${text2} ${currentUser.givenName}',
61
+ hint: '${wrapper.email}',
62
+ type: 'autocomplete',
63
+ required: true,
64
+ source: [],
65
+ disabled: false
35
66
  };
36
67
  var VALUE = 'Text value';
37
68
  var PAGE = {
@@ -43,8 +74,102 @@ describe('components.FormPage', function () {
43
74
  text: VALUE
44
75
  }
45
76
  };
77
+ var PAGE_WITH_BUTTON_ACTIONS = {
78
+ id: 'pageId',
79
+ // eslint-disable-next-line no-template-curly-in-string
80
+ title: '${title}',
81
+ components: [TEXT_WITH_EXPRESSION, AUTO_WITH_EXPRESSION],
82
+ actions: [// eslint-disable-next-line no-template-curly-in-string
83
+ {
84
+ type: 'submit',
85
+ validate: true,
86
+ label: 'Next ${wrapper.nextAction}'
87
+ }, // eslint-disable-next-line no-template-curly-in-string
88
+ {
89
+ type: 'navigate',
90
+ page: '1',
91
+ label: 'Go back ${wrapper.prevAction}'
92
+ }],
93
+ formData: {
94
+ title: 'Order Form',
95
+ text1: "Text1 ".concat(VALUE),
96
+ text2: "Text2 ".concat(VALUE),
97
+ wrapper: {
98
+ email: 'test@example.email',
99
+ nextAction: 'Payment',
100
+ prevAction: 'User Details'
101
+ },
102
+ currentUser: {
103
+ givenName: 'Doe'
104
+ }
105
+ }
106
+ };
46
107
  var ON_ACTION_CALLS = [];
47
108
 
109
+ var checkInputField = function checkInputField(formGroup, fieldId, lbl, hnt, val) {
110
+ expect(formGroup.tagName).toEqual('DIV');
111
+ expect(formGroup.classList).toContain('govuk-form-group');
112
+ var label = formGroup.childNodes[0];
113
+ expect(label.tagName).toEqual('LABEL');
114
+ expect(label.classList).toContain('govuk-label');
115
+ expect(label.textContent).toEqual(lbl);
116
+ expect(label.getAttribute('for')).toEqual(fieldId);
117
+ var hint = formGroup.childNodes[1];
118
+ expect(hint.tagName).toEqual('SPAN');
119
+ expect(hint.classList).toContain('govuk-hint');
120
+ expect(hint.textContent).toEqual(hnt);
121
+ var input = formGroup.childNodes[2];
122
+ expect(input.tagName).toEqual('INPUT');
123
+ expect(input.classList).toContain('govuk-input');
124
+ expect(input.id).toEqual(fieldId);
125
+ expect(input.value).toEqual(val);
126
+ return input;
127
+ };
128
+
129
+ var checkAutoCompleteField = function checkAutoCompleteField(formGroup, fieldId, lbl, hnt, val) {
130
+ expect(formGroup.tagName).toEqual('DIV');
131
+ expect(formGroup.classList).toContain('govuk-form-group');
132
+ var label = formGroup.childNodes[0];
133
+ expect(label.tagName).toEqual('LABEL');
134
+ expect(label.classList).toContain('govuk-label');
135
+ expect(label.textContent).toEqual(lbl);
136
+ expect(label.getAttribute('for')).toEqual(fieldId);
137
+ var hint = formGroup.childNodes[1];
138
+ expect(hint.tagName).toEqual('SPAN');
139
+ expect(hint.classList).toContain('govuk-hint');
140
+ expect(hint.textContent).toEqual(hnt);
141
+ var outerWrapper = formGroup.childNodes[2];
142
+ expect(outerWrapper.classList).toContain('hods-autocomplete__outer-wrapper');
143
+ var autocomplete = outerWrapper.childNodes[0];
144
+ expect(autocomplete.classList).toContain('hods-autocomplete__wrapper');
145
+
146
+ var input = _toConsumableArray(autocomplete.childNodes).filter(function (e) {
147
+ return e.tagName === 'INPUT';
148
+ })[0];
149
+
150
+ expect(input.classList).toContain('hods-autocomplete__input');
151
+ expect(input.tagName).toEqual('INPUT');
152
+ expect(input.id).toEqual(fieldId);
153
+ expect(input.value).toEqual(val);
154
+ return input;
155
+ };
156
+
157
+ var checkFormButtonGroup = function checkFormButtonGroup(buttonGroup, labels) {
158
+ var _buttonGroup$childNod;
159
+
160
+ var buttons = [];
161
+ expect(buttonGroup.tagName).toEqual('DIV');
162
+ expect(buttonGroup.classList).toContain('hods-button-group');
163
+ expect((_buttonGroup$childNod = buttonGroup.childNodes) === null || _buttonGroup$childNod === void 0 ? void 0 : _buttonGroup$childNod.length).toEqual(labels.length);
164
+ buttonGroup.childNodes.forEach(function (button, i) {
165
+ expect(button.tagName).toEqual('BUTTON');
166
+ expect(button.classList).toContain('hods-button');
167
+ expect(button.textContent).toEqual(labels[i]);
168
+ buttons.push(button);
169
+ });
170
+ return buttons;
171
+ };
172
+
48
173
  var ON_ACTION = function ON_ACTION(action, patch, onError) {
49
174
  ON_ACTION_CALLS.push({
50
175
  action: action,
@@ -60,7 +185,7 @@ describe('components.FormPage', function () {
60
185
  ON_ACTION_CALLS.length = 0;
61
186
  });
62
187
  it('should render a submit page correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
63
- var _renderWithValidation, container, page, heading, formGroup, label, hint, input, buttonGroup, button;
188
+ var _renderWithValidation, container, page, heading;
64
189
 
65
190
  return regeneratorRuntime.wrap(function _callee$(_context) {
66
191
  while (1) {
@@ -76,50 +201,57 @@ describe('components.FormPage', function () {
76
201
  heading = page.childNodes[0];
77
202
  expect(heading.classList).toContain('govuk-heading-l');
78
203
  expect(heading.textContent).toEqual(PAGE.title);
79
- formGroup = page.childNodes[1];
80
- expect(formGroup.tagName).toEqual('DIV');
81
- expect(formGroup.classList).toContain('govuk-form-group');
82
- label = formGroup.childNodes[0];
83
- expect(label.tagName).toEqual('LABEL');
84
- expect(label.classList).toContain('govuk-label');
85
- expect(label.textContent).toEqual("".concat(TEXT.label, " (optional)"));
86
- expect(label.getAttribute('for')).toEqual(TEXT.fieldId);
87
- hint = formGroup.childNodes[1];
88
- expect(hint.tagName).toEqual('SPAN');
89
- expect(hint.classList).toContain('govuk-hint');
90
- expect(hint.textContent).toEqual(TEXT.hint);
91
- input = formGroup.childNodes[2];
92
- expect(input.tagName).toEqual('INPUT');
93
- expect(input.classList).toContain('govuk-input');
94
- expect(input.id).toEqual(TEXT.fieldId);
95
- expect(input.value).toEqual(VALUE);
96
- buttonGroup = page.childNodes[2];
97
- expect(buttonGroup.tagName).toEqual('DIV');
98
- expect(buttonGroup.classList).toContain('hods-button-group');
99
- button = buttonGroup.childNodes[0];
100
- expect(button.tagName).toEqual('BUTTON');
101
- expect(button.classList).toContain('hods-button');
102
- expect(button.textContent).toEqual(_ActionButton.DEFAULT_LABEL);
103
-
104
- case 31:
204
+ checkInputField(page.childNodes[1], TEXT.fieldId, "".concat(TEXT.label, " (optional)"), TEXT.hint, VALUE);
205
+ checkFormButtonGroup(page.childNodes[2], [_ActionButton.DEFAULT_LABEL]);
206
+
207
+ case 9:
105
208
  case "end":
106
209
  return _context.stop();
107
210
  }
108
211
  }
109
212
  }, _callee);
110
213
  })));
111
- it('should handle a page change appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
112
- var _renderWithValidation2, container, page, input, NEW_VALUE, EVENT;
214
+ it('should render a submit page correctly and submit action button has interpolated label', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
215
+ var _renderWithValidation2, container, page, FORM_DATA, heading;
113
216
 
114
217
  return regeneratorRuntime.wrap(function _callee2$(_context2) {
115
218
  while (1) {
116
219
  switch (_context2.prev = _context2.next) {
117
220
  case 0:
118
221
  _renderWithValidation2 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
119
- page: PAGE,
222
+ page: PAGE_WITH_BUTTON_ACTIONS,
120
223
  onAction: ON_ACTION
121
224
  })), container = _renderWithValidation2.container;
122
225
  page = container.childNodes[0];
226
+ FORM_DATA = PAGE_WITH_BUTTON_ACTIONS.formData;
227
+ expect(page.tagName).toEqual('DIV');
228
+ expect(page.classList).toContain("".concat(_FormPage.DEFAULT_CLASS, "__page"));
229
+ heading = page.childNodes[0];
230
+ expect(heading.classList).toContain('govuk-heading-l');
231
+ expect(heading.textContent).toEqual(FORM_DATA.title);
232
+ checkInputField(page.childNodes[1], TEXT_WITH_EXPRESSION.fieldId, 'Email to test@example.email (optional)', 'Text hint Doe', 'Text1 Text value');
233
+ checkAutoCompleteField(page.childNodes[2], AUTO_WITH_EXPRESSION.fieldId, "".concat(FORM_DATA.text2, " ").concat(FORM_DATA.currentUser.givenName), FORM_DATA.wrapper.email, "Text2 ".concat(VALUE));
234
+ checkFormButtonGroup(page.childNodes[3], ['Next Payment', 'Go back User Details']);
235
+
236
+ case 11:
237
+ case "end":
238
+ return _context2.stop();
239
+ }
240
+ }
241
+ }, _callee2);
242
+ })));
243
+ it('should handle a page change appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {
244
+ var _renderWithValidation3, container, page, input, NEW_VALUE, EVENT;
245
+
246
+ return regeneratorRuntime.wrap(function _callee3$(_context3) {
247
+ while (1) {
248
+ switch (_context3.prev = _context3.next) {
249
+ case 0:
250
+ _renderWithValidation3 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
251
+ page: PAGE,
252
+ onAction: ON_ACTION
253
+ })), container = _renderWithValidation3.container;
254
+ page = container.childNodes[0];
123
255
  expect(page.tagName).toEqual('DIV'); // Change the input.
124
256
 
125
257
  input = page.childNodes[1].childNodes[2];
@@ -138,22 +270,22 @@ describe('components.FormPage', function () {
138
270
 
139
271
  case 8:
140
272
  case "end":
141
- return _context2.stop();
273
+ return _context3.stop();
142
274
  }
143
275
  }
144
- }, _callee2);
276
+ }, _callee3);
145
277
  })));
146
- it('should handle a page action appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {
147
- var _renderWithValidation3, container, page, input, NEW_VALUE, CHANGE_EVENT, button;
278
+ it('should handle a page action appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {
279
+ var _renderWithValidation4, container, page, input, NEW_VALUE, CHANGE_EVENT, button;
148
280
 
149
- return regeneratorRuntime.wrap(function _callee3$(_context3) {
281
+ return regeneratorRuntime.wrap(function _callee4$(_context4) {
150
282
  while (1) {
151
- switch (_context3.prev = _context3.next) {
283
+ switch (_context4.prev = _context4.next) {
152
284
  case 0:
153
- _renderWithValidation3 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
285
+ _renderWithValidation4 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
154
286
  page: PAGE,
155
287
  onAction: ON_ACTION
156
- })), container = _renderWithValidation3.container;
288
+ })), container = _renderWithValidation4.container;
157
289
  page = container.childNodes[0]; // Change the input.
158
290
 
159
291
  input = page.childNodes[1].childNodes[2];
@@ -182,10 +314,10 @@ describe('components.FormPage', function () {
182
314
 
183
315
  case 12:
184
316
  case "end":
185
- return _context3.stop();
317
+ return _context4.stop();
186
318
  }
187
319
  }
188
- }, _callee3);
320
+ }, _callee4);
189
321
  })));
190
322
  });
191
323
  });
@@ -246,27 +246,6 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
246
246
  var onPageAction = function onPageAction(action, patch) {
247
247
  // Check to see whether the action is able to proceed, which in
248
248
  // in the case of a submission will validate the fields in the page.
249
- if (action.type === _models.PageAction.TYPES.SUBMIT) {
250
- if (_helpers.default.canCYASubmit(pages, validate.pages)) {
251
- // Submit.
252
- var submissionData = _utils.default.Format.form({
253
- pages: pages,
254
- components: components
255
- }, _objectSpread({}, data), _models.EventTypes.SUBMIT);
256
-
257
- submissionData.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, submissionData, currentTask, true);
258
- setData(submissionData); // Now submit the data to the backend...
259
-
260
- hooks.onSubmit(action.type, submissionData, function () {
261
- return hooks.onFormComplete();
262
- }, function (errors) {
263
- return _handlers.default.submissionError(errors, addErrors);
264
- });
265
- }
266
- } // Check to see whether the action is able to proceed, which in
267
- // in the case of a submission will validate the fields in the page.
268
-
269
-
270
249
  if (_helpers.default.canActionProceed(action, formState.page, validate.page)) {
271
250
  patch = _helpers.default.cleanHiddenNestedData(patch, formState.page);
272
251
 
@@ -274,15 +253,15 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
274
253
  _handlers.default.navigate(action, pageId, onPageChange);
275
254
  } else {
276
255
  // Save draft or submit.
277
- var _submissionData = _utils.default.Format.form({
256
+ var submissionData = _utils.default.Format.form({
278
257
  pages: pages,
279
258
  components: components
280
259
  }, _objectSpread(_objectSpread({}, data), patch), _models.EventTypes.SUBMIT);
281
260
 
282
- _submissionData.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, _submissionData, currentTask, true);
261
+ submissionData.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, submissionData, currentTask, true);
283
262
 
284
263
  if (patch) {
285
- setData(_submissionData);
264
+ setData(submissionData);
286
265
  }
287
266
 
288
267
  var pageUpdate = function pageUpdate(next) {
@@ -298,10 +277,10 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
298
277
  } // Now submit the data to the backend...
299
278
 
300
279
 
301
- hooks.onSubmit(action.type, _submissionData, function (response) {
280
+ hooks.onSubmit(action.type, submissionData, function (response) {
302
281
  // The backend response may well contain data we need so apply it...
303
282
  // ... but this needs to happen in a useEffect, not right away.
304
- var sData = _objectSpread(_objectSpread({}, _submissionData), response);
283
+ var sData = _objectSpread(_objectSpread({}, submissionData), response);
305
284
 
306
285
  setSubmitted({
307
286
  data: sData
@@ -364,14 +343,14 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
364
343
 
365
344
  if (action.type === _models.PageAction.TYPES.SAVE_AND_CONTINUE && hub === _models.HubFormats.TASK) {
366
345
  if (_helpers.default.canCYASubmit(currentTask.fullPages, validate.pages)) {
367
- var _submissionData2 = _utils.default.Format.form({
346
+ var _submissionData = _utils.default.Format.form({
368
347
  pages: pages,
369
348
  components: components
370
349
  }, _objectSpread({}, data), _models.EventTypes.SUBMIT);
371
350
 
372
- _submissionData2.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, _submissionData2, currentTask, true);
373
- setData(_submissionData2);
374
- hooks.onSubmit(action.type, _submissionData2, function () {
351
+ _submissionData.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, _submissionData, currentTask, true);
352
+ setData(_submissionData);
353
+ hooks.onSubmit(action.type, _submissionData, function () {
375
354
  return onPageChange(_models.FormPages.HUB);
376
355
  }, function (errors) {
377
356
  return _handlers.default.submissionError(errors, addErrors);
@@ -381,14 +360,14 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
381
360
 
382
361
  if (action.type === _models.PageAction.TYPES.SAVE_AND_RETURN) {
383
362
  if (_helpers.default.canCYASubmit(currentTask.fullPages, validate.pages)) {
384
- var _submissionData3 = _utils.default.Format.form({
363
+ var _submissionData2 = _utils.default.Format.form({
385
364
  pages: pages,
386
365
  components: components
387
366
  }, _objectSpread({}, data), _models.EventTypes.SUBMIT);
388
367
 
389
- _submissionData3.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, _submissionData3, currentTask);
390
- setData(_submissionData3);
391
- hooks.onSubmit(action.type, _submissionData3, function () {
368
+ _submissionData2.formStatus = _helpers.default.getSubmissionStatus(type, pages, pageId, action, _submissionData2, currentTask);
369
+ setData(_submissionData2);
370
+ hooks.onSubmit(action.type, _submissionData2, function () {
392
371
  if (type === _models.FormTypes.TASK) {
393
372
  onPageChange(undefined);
394
373
  } else {
@@ -27,9 +27,9 @@ var getCYARow = function getCYARow(page, component, onAction) {
27
27
 
28
28
  if (page.formData && component.fieldId) {
29
29
  value = page.formData[component.fieldId];
30
+ setNestedValue(component, page);
30
31
  }
31
32
 
32
- setNestedValue(component, page);
33
33
  return {
34
34
  pageId: page.id,
35
35
  id: component.id,
@@ -46,9 +46,9 @@ var getCYARow = function getCYARow(page, component, onAction) {
46
46
  var setNestedValue = function setNestedValue(component, page) {
47
47
  var _component$data, _component$data$optio;
48
48
 
49
- (_component$data = component.data) === null || _component$data === void 0 ? void 0 : (_component$data$optio = _component$data.options) === null || _component$data$optio === void 0 ? void 0 : _component$data$optio.forEach(function (option, index) {
49
+ (_component$data = component.data) === null || _component$data === void 0 ? void 0 : (_component$data$optio = _component$data.options) === null || _component$data$optio === void 0 ? void 0 : _component$data$optio.forEach(function (option) {
50
50
  //check if option is selected and has nested component
51
- if (page.formData[component.id] === option.value && option.nested) {
51
+ if (Array.isArray(option.nested) && page.formData[component.id] === option.value) {
52
52
  option.nested.forEach(function (child) {
53
53
  child.value = page.formData[child.id];
54
54
  });
@@ -145,7 +145,7 @@ describe('utils', function () {
145
145
  onAction: ON_ACTION
146
146
  });
147
147
  });
148
- it('should update update any nested components with corresponding values from the stored formdata', function () {
148
+ it('should update any nested components with corresponding values from the stored formdata', function () {
149
149
  var SELECTED_VALUE = 'selectedValue';
150
150
  var NESTED_ID = 'nestedId';
151
151
  var NESTED_VALUE = 'nestedValue';
@@ -159,6 +159,7 @@ describe('utils', function () {
159
159
  var COMPONENT = {
160
160
  type: 'radios',
161
161
  id: 'a',
162
+ fieldId: 'a',
162
163
  data: {
163
164
  options: [{
164
165
  value: SELECTED_VALUE,
@@ -171,6 +172,29 @@ describe('utils', function () {
171
172
  var ROW = (0, _getCYARow.default)(PAGE, COMPONENT, function () {});
172
173
  expect(ROW.component.data.options[0].nested[0].value).toEqual(NESTED_VALUE);
173
174
  });
175
+ it('should handle an undefined formData when attempting to set nested values', function () {
176
+ var SELECTED_VALUE = 'selectedValue';
177
+ var NESTED_ID = 'nestedId';
178
+ var PAGE = {
179
+ id: 'page',
180
+ cya_link: {}
181
+ };
182
+ var COMPONENT = {
183
+ type: 'radios',
184
+ id: 'a',
185
+ fieldId: 'a',
186
+ data: {
187
+ options: [{
188
+ value: SELECTED_VALUE,
189
+ nested: [{
190
+ id: NESTED_ID
191
+ }]
192
+ }]
193
+ }
194
+ };
195
+ var ROW = (0, _getCYARow.default)(PAGE, COMPONENT, function () {});
196
+ expect(ROW.component.data.options[0].nested[0].value).toBeUndefined();
197
+ });
174
198
  });
175
199
  });
176
200
  });
@@ -5,12 +5,29 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
+ var _copReactComponents = require("@ukhomeoffice/cop-react-components");
9
+
10
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
11
+
12
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
13
+
14
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
15
+
16
+ var interpolateOptions = function interpolateOptions(config, options) {
17
+ return options.map(function (opt) {
18
+ return _objectSpread(_objectSpread({}, opt), {}, {
19
+ value: _copReactComponents.Utils.interpolateString(opt.value, config.formData),
20
+ label: _copReactComponents.Utils.interpolateString(opt.label, config.formData)
21
+ });
22
+ });
23
+ };
24
+
8
25
  var getOptions = function getOptions(config, callback) {
9
26
  if (config) {
10
27
  if (config.options) {
11
- return callback(config.options);
28
+ return callback(interpolateOptions(config, config.options));
12
29
  } else if (config.data && config.data.options) {
13
- return callback(config.data.options);
30
+ return callback(interpolateOptions(config, config.data.options));
14
31
  }
15
32
  }
16
33
 
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _copReactComponents = require("@ukhomeoffice/cop-react-components");
9
-
10
8
  var _Container = _interopRequireDefault(require("../Container"));
11
9
 
12
10
  var _Data = _interopRequireDefault(require("../Data"));
@@ -37,8 +35,6 @@ var getFormPage = function getFormPage(pageOptions, formComponents, formData) {
37
35
  return null;
38
36
  }
39
37
 
40
- pageOptions = interpolatePageOptions(pageOptions, formData);
41
- formComponents = interpolateFormComponents(formComponents, formData);
42
38
  var components = pageOptions.components.map(function (componentOptions) {
43
39
  if (typeof componentOptions === 'string') {
44
40
  return (0, _getParagraphFromText.default)(componentOptions);
@@ -61,42 +57,6 @@ var getFormPage = function getFormPage(pageOptions, formComponents, formData) {
61
57
  actions: actions
62
58
  }));
63
59
  };
64
- /**
65
- * Interpolate 'Variable Expression' using formData - for local use only.
66
- * @param {object} pageOptions The JSON page.
67
- * @param {object} formData The top-level form data, used for setting up components.
68
- * @returns interpolated pageOptions.
69
- */
70
-
71
-
72
- var interpolatePageOptions = function interpolatePageOptions(pageOptions, formData) {
73
- return JSON.parse(_copReactComponents.Utils.interpolateString(JSON.stringify(pageOptions), formData));
74
- };
75
- /**
76
- * Interpolate 'Variable Expression' using formData excluding each component's data block - for local use only.
77
- * @param {Array} formComponents The components defined at the top-level of the form.
78
- * @param {object} formData The top-level form data, used for setting up components.
79
- * @returns interpolated formComponents
80
- */
81
-
82
-
83
- var interpolateFormComponents = function interpolateFormComponents(formComponents, formData) {
84
- return formComponents.map(function (formComponent) {
85
- var formComponentDataUrl = undefined;
86
-
87
- if (formComponent.data && formComponent.data.url) {
88
- formComponentDataUrl = formComponent.data.url;
89
- }
90
-
91
- var interpolatedFormComponent = JSON.parse(_copReactComponents.Utils.interpolateString(JSON.stringify(formComponent), formData));
92
-
93
- if (formComponentDataUrl) {
94
- interpolatedFormComponent.data.url = formComponentDataUrl;
95
- }
96
-
97
- return interpolatedFormComponent;
98
- });
99
- };
100
60
 
101
61
  var _default = getFormPage;
102
62
  exports.default = _default;
@@ -200,74 +200,6 @@ describe('utils', function () {
200
200
  formData: {}
201
201
  });
202
202
  });
203
- it('should interpolate and handle a page that references a form-level component with formData', function () {
204
- var PAGE = {
205
- // eslint-disable-next-line no-template-curly-in-string
206
- title: 'Page ${postFixTitle}',
207
- components: ["Opening paragraph", {
208
- type: 'heading',
209
- size: 'l',
210
- content: 'Page heading'
211
- }, "Closing paragraph", {
212
- use: 'c'
213
- }, {
214
- use: 'd'
215
- }, "Kevin", {
216
- use: 'e'
217
- }]
218
- };
219
-
220
- var DATA = _objectSpread(_objectSpread({}, FORM_DATA), {}, {
221
- currentUser: {
222
- firstname: 'Bob',
223
- surname: 'Kevin'
224
- },
225
- postFixTitle: 'Everyone'
226
- });
227
-
228
- var C = FORM_COMPONENTS[2];
229
- var D = FORM_COMPONENTS[3];
230
- var E = FORM_COMPONENTS[4];
231
- expect((0, _getFormPage.default)(PAGE, FORM_COMPONENTS, DATA)).toEqual({
232
- title: 'Page Everyone',
233
- components: [{
234
- type: 'html',
235
- tagName: 'p',
236
- content: PAGE.components[0]
237
- }, _objectSpread(_objectSpread({}, PAGE.components[1]), {}, {
238
- full_path: PAGE.components[1].fieldId
239
- }), {
240
- type: 'html',
241
- tagName: 'p',
242
- content: PAGE.components[2]
243
- }, _objectSpread(_objectSpread({
244
- use: 'c'
245
- }, C), {}, {
246
- cya_label: C.label,
247
- data: {
248
- url: "".concat(FORM_DATA.urls.refData, "/v3/charlies")
249
- },
250
- full_path: C.fieldId
251
- }), _objectSpread(_objectSpread({
252
- use: 'd'
253
- }, D), {}, {
254
- label: 'Roger ' + DATA.currentUser.firstname,
255
- cya_label: 'Roger ' + DATA.currentUser.firstname,
256
- full_path: D.fieldId
257
- }), {
258
- type: 'html',
259
- tagName: 'p',
260
- content: PAGE.components[5]
261
- }, _objectSpread(_objectSpread({
262
- use: 'e'
263
- }, E), {}, {
264
- label: 'Bravo ' + DATA.currentUser.surname,
265
- cya_label: 'Bravo ' + DATA.currentUser.surname,
266
- full_path: E.fieldId
267
- })],
268
- formData: DATA
269
- });
270
- });
271
203
  });
272
204
  });
273
205
  });
@@ -88,7 +88,12 @@ var validateComponent = function validateComponent(component, outerData, formDat
88
88
  }, formData));
89
89
  }
90
90
  });
91
- return nestedErrors;
91
+
92
+ if (nestedErrors.length > 0) {
93
+ return nestedErrors;
94
+ }
95
+
96
+ break;
92
97
 
93
98
  default:
94
99
  break;
@@ -220,7 +220,7 @@ describe('utils.Validate.Component', function () {
220
220
  }]
221
221
  }
222
222
  };
223
- expect((0, _validateComponent.default)(COMPONENT, undefined, FORMDATA)).toEqual([]);
223
+ expect((0, _validateComponent.default)(COMPONENT, undefined, FORMDATA)).toBeUndefined();
224
224
  });
225
225
  it('should return an error when the radio component contains nested components with errors', function () {
226
226
  var NESTED_ID = 'nestedId';
@@ -261,7 +261,7 @@ describe('utils.Validate.Component', function () {
261
261
  }]
262
262
  }
263
263
  };
264
- expect((0, _validateComponent.default)(COMPONENT, undefined, {})).toEqual([]);
264
+ expect((0, _validateComponent.default)(COMPONENT, undefined, {})).toBeUndefined();
265
265
  });
266
266
  });
267
267
  });
@@ -5,13 +5,19 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
+ var _copReactComponents = require("@ukhomeoffice/cop-react-components");
9
+
8
10
  var _validateComponent = _interopRequireDefault(require("./validateComponent"));
9
11
 
10
12
  var _showFormPage = _interopRequireDefault(require("../FormPage/showFormPage"));
11
13
 
12
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
15
 
14
- // Local imports
16
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
17
+
18
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
19
+
20
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
15
21
 
16
22
  /**
17
23
  * Validate all of the components on a page.
@@ -21,7 +27,11 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
21
27
  var validatePage = function validatePage(page) {
22
28
  if ((0, _showFormPage.default)(page, page.formData) && Array.isArray(page.components)) {
23
29
  return page.components.reduce(function (errors, component) {
24
- return errors.concat((0, _validateComponent.default)(component, page.formData, page.formData));
30
+ return errors.concat((0, _validateComponent.default)(component, page.formData, page.formData)).map(function (err) {
31
+ return !!err ? _objectSpread(_objectSpread({}, err), {}, {
32
+ error: _copReactComponents.Utils.interpolateString(err.error, page.formData)
33
+ }) : err;
34
+ });
25
35
  }, []).filter(function (e) {
26
36
  return !!e;
27
37
  }).flat();
@@ -6,7 +6,12 @@ var _validatePage = _interopRequireDefault(require("./validatePage"));
6
6
 
7
7
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
8
8
 
9
- // Local imports
9
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
10
+
11
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
12
+
13
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
14
+
10
15
  describe('utils', function () {
11
16
  describe('Validate', function () {
12
17
  describe('page', function () {
@@ -69,6 +74,46 @@ describe('utils', function () {
69
74
  error: 'Echo is required'
70
75
  });
71
76
  });
77
+ it('should return an error for each required component with interpolated label', function () {
78
+ var COMPONENTS = [// eslint-disable-next-line no-template-curly-in-string
79
+ setup('a', _models.ComponentTypes.TEXT, 'Alpha ${tiger}', true), // eslint-disable-next-line no-template-curly-in-string
80
+ setup('b', _models.ComponentTypes.EMAIL, 'Bravo ${panther}', true), // eslint-disable-next-line no-template-curly-in-string
81
+ setup('c', _models.ComponentTypes.AUTOCOMPLETE, 'Charlie ${eagle}', true), // eslint-disable-next-line no-template-curly-in-string
82
+ setup('d', _models.ComponentTypes.CHECKBOXES, 'Delta ${lion}', true), // eslint-disable-next-line no-template-curly-in-string
83
+ setup('e', _models.ComponentTypes.FILE, 'Echo ${zoo}', true)];
84
+ var PAGE = {
85
+ components: COMPONENTS,
86
+ formData: {
87
+ tiger: 'Tiger',
88
+ panther: 'Panther',
89
+ eagle: 'Eagle',
90
+ lion: 'Lion',
91
+ zoo: 'Zoo'
92
+ }
93
+ };
94
+ var RESULT = (0, _validatePage.default)(PAGE);
95
+ expect(RESULT.length).toEqual(5);
96
+ expect(RESULT[0]).toEqual({
97
+ id: 'a',
98
+ error: "Alpha ".concat(PAGE.formData.tiger, " is required")
99
+ });
100
+ expect(RESULT[1]).toEqual({
101
+ id: 'b',
102
+ error: "Bravo ".concat(PAGE.formData.panther, " is required")
103
+ });
104
+ expect(RESULT[2]).toEqual({
105
+ id: 'c',
106
+ error: "Charlie ".concat(PAGE.formData.eagle, " is required")
107
+ });
108
+ expect(RESULT[3]).toEqual({
109
+ id: 'd',
110
+ error: "Delta ".concat(PAGE.formData.lion, " is required")
111
+ });
112
+ expect(RESULT[4]).toEqual({
113
+ id: 'e',
114
+ error: "Echo ".concat(PAGE.formData.zoo, " is required")
115
+ });
116
+ });
72
117
  });
73
118
  describe('when the form data is fully valid', function () {
74
119
  var DATA = {
@@ -164,6 +209,28 @@ describe('utils', function () {
164
209
  error: 'Charlie is required'
165
210
  });
166
211
  });
212
+ it('should return an interpolated error for both invalid fields when all are required and email types', function () {
213
+ var COMPONENTS = [setup('alpha', _models.ComponentTypes.EMAIL, 'Alpha', true), // eslint-disable-next-line no-template-curly-in-string
214
+ setup('bravo', _models.ComponentTypes.EMAIL, 'Bravo ${lion}', true), // eslint-disable-next-line no-template-curly-in-string
215
+ setup('charlie', _models.ComponentTypes.EMAIL, 'Charlie ${panther}', true)];
216
+ var PAGE = {
217
+ components: COMPONENTS,
218
+ formData: _objectSpread(_objectSpread({}, DATA), {}, {
219
+ lion: 'Lion',
220
+ panther: 'Panther'
221
+ })
222
+ };
223
+ var RESULT = (0, _validatePage.default)(PAGE);
224
+ expect(RESULT.length).toEqual(2);
225
+ expect(RESULT[0]).toEqual({
226
+ id: 'bravo',
227
+ error: "Enter bravo ".concat(PAGE.formData.lion, " in the correct format, like jane.doe@homeoffice.gov.uk")
228
+ });
229
+ expect(RESULT[1]).toEqual({
230
+ id: 'charlie',
231
+ error: "Charlie ".concat(PAGE.formData.panther, " is required")
232
+ });
233
+ });
167
234
  });
168
235
  });
169
236
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ukhomeoffice/cop-react-form-renderer",
3
- "version": "3.2.2",
3
+ "version": "3.2.5",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "clean": "rimraf dist",