@ukhomeoffice/cop-react-form-renderer 7.1.0-alpha → 7.1.0-gamma

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.
@@ -61,6 +61,22 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
61
61
  setSubmitting(false);
62
62
  } else {
63
63
  let pageUpdate = next => onPageChange(_helpers.default.getNextPageId(type, pages, pageId, action, next));
64
+ let collectionName;
65
+ let addAnotherName;
66
+ if (action.collection) {
67
+ collectionName = action.collection;
68
+ const collectionNameCapitalised = collectionName.charAt(0).toUpperCase() + collectionName.slice(1);
69
+ addAnotherName = "addAnother".concat(collectionNameCapitalised).slice(0, -1);
70
+ }
71
+ let collectionArray;
72
+ let index;
73
+ if (collectionName) {
74
+ collectionArray = form.page.formData[collectionName];
75
+ if (collectionArray && collectionArray.length > 0 && form.page.formData["".concat(collectionName, "ActiveId")]) {
76
+ index = collectionArray.findIndex(obj => obj.id === form.page.formData["".concat(collectionName, "ActiveId")]);
77
+ }
78
+ }
79
+
64
80
  /* eslint-disable no-case-declarations */
65
81
  switch (action.type) {
66
82
  case _models.PageAction.TYPES.SUBMIT:
@@ -68,10 +84,33 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
68
84
  break;
69
85
  case _models.PageAction.TYPES.SAVE_AND_NAVIGATE:
70
86
  const state = _objectSpread({}, currentTask);
87
+ // If resetActiveId is true then point active ID to first entry in collection
88
+ if (collectionArray && collectionArray.length > 0 && action.resetActiveId) {
89
+ form.page.formData["".concat(collectionName, "ActiveId")] = collectionArray[0].id;
90
+ }
91
+ // If resetActiveId is false
92
+ if (collectionArray && collectionArray.length > 0 && !action.resetActiveId) {
93
+ // Set addAnother field to yes if not reached last entry in collection
94
+ if (index !== undefined && index + 1 < form.page.formData[collectionName].length) {
95
+ form.page.formData[addAnotherName] = 'Yes';
96
+ }
97
+ // Set addAnother field to no if reached last entry
98
+ if (index !== undefined && index + 1 === form.page.formData[collectionName].length && form.page.formData[addAnotherName]) {
99
+ form.page.formData[addAnotherName] = 'No';
100
+ }
101
+ }
71
102
  pageUpdate = () => _handlers.default.navigate(action, pageId, onPageChange, state);
72
103
  break;
73
104
  case _models.PageAction.TYPES.COLLECTION_ADD:
74
- _utils.default.CollectionPage.addEntry(action.collection, form.page.formData);
105
+ // Only add entry if the index is undefined,
106
+ // the array is empty
107
+ // or the active ID is the last one in the collection,
108
+ // otherwise move to next entry.
109
+ if (index === undefined || collectionArray.length === 0 || index + 1 === form.page.formData[collectionName].length) {
110
+ _utils.default.CollectionPage.addEntry(action.collection, form.page.formData);
111
+ } else {
112
+ form.page.formData["".concat(action.collection, "ActiveId")] = collectionArray[index + 1].id;
113
+ }
75
114
 
76
115
  // We need to delete the collection entry fields from formData as it will be holding previous values.
77
116
  if (form.page.formData["".concat(action.collection)] && form.page.formData["".concat(action.collection)].length > 0) {
@@ -105,16 +144,13 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
105
144
  removedEntryFieldsToDelete.forEach(i => delete form.page.formData[i]);
106
145
  }
107
146
 
108
- // 3. Retrieve the collection array from formData.
109
- const collectionArray = form.page.formData[action.collection];
110
-
111
- // 4. Assign the fields in the last collection entry to formData.
147
+ // 3. Assign the fields in the last collection entry to formData.
112
148
  Object.assign(form.page.formData, collectionArray[collectionArray.length - 1]);
113
149
 
114
- // 5. Assign the formData ID back to the formData.
150
+ // 4. Assign the formData ID back to the formData.
115
151
  form.page.formData.id = formDataId;
116
152
 
117
- // 6. If the collection array has entries set the correct ActiveId
153
+ // 5. If the collection array has entries set the correct ActiveId
118
154
  // otherwise delete it as well as the collection array.
119
155
  if (collectionArray.length > 0) {
120
156
  form.page.formData["".concat(action.collection, "ActiveId")] = collectionArray[collectionArray.length - 1].id;
@@ -122,6 +158,7 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
122
158
  delete form.page.formData["".concat(action.collection, "ActiveId")];
123
159
  delete form.page.formData[action.collection];
124
160
  }
161
+ pageUpdate = () => _handlers.default.navigate(action, pageId, onPageChange);
125
162
  break;
126
163
  default:
127
164
  break;
@@ -380,6 +380,74 @@ describe('components.FormRenderer.onPageAction', () => {
380
380
  // that's called as part of the onSubmit hook's onSuccess callback.
381
381
  expect(_handlers.default.navigateCalls).toEqual(1);
382
382
  });
383
+ it("should handle the ".concat(_models.PageAction.TYPES.COLLECTION_ADD, " Page Action type where active ID is not the last entry in collection"), () => {
384
+ // If the active ID is not the last entry in the collection
385
+ // then a new entry is not added
386
+ // and the active ID moves to the next entry ID.
387
+ const FORM_STATE = {
388
+ page: {
389
+ formData: {
390
+ 'testCollection': [{
391
+ id: '1'
392
+ }, {
393
+ id: '2'
394
+ }],
395
+ testCollectionActiveId: '1'
396
+ }
397
+ }
398
+ };
399
+ const ACTION = {
400
+ type: _models.PageAction.TYPES.COLLECTION_ADD,
401
+ collection: 'testCollection'
402
+ };
403
+ const CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
404
+ action: ACTION,
405
+ formState: FORM_STATE
406
+ });
407
+ _onPageAction.default.apply(void 0, Object.values(CUSTOM_ARGS));
408
+ preActionChecks();
409
+ expect(FORM_STATE.page.formData["".concat(ACTION.collection, "ActiveId")]).toEqual('2');
410
+ expect(FORM_STATE.page.formData[ACTION.collection].length).toEqual(2);
411
+ postActionChecks(CUSTOM_ARGS);
412
+
413
+ // This action type also sets up a call to handlers.navigate
414
+ // that's called as part of the onSubmit hook's onSuccess callback.
415
+ expect(_handlers.default.navigateCalls).toEqual(1);
416
+ });
417
+ it("should handle the ".concat(_models.PageAction.TYPES.COLLECTION_ADD, " Page Action type where active ID is the last entry in collection"), () => {
418
+ // If the active ID is the last entry in the collection
419
+ // then a new entry is added (although not picked up in this test)
420
+ // and the active ID becomes null.
421
+ const FORM_STATE = {
422
+ page: {
423
+ formData: {
424
+ 'testCollection': [{
425
+ id: '1'
426
+ }, {
427
+ id: '2'
428
+ }],
429
+ testCollectionActiveId: '2'
430
+ }
431
+ }
432
+ };
433
+ const ACTION = {
434
+ type: _models.PageAction.TYPES.COLLECTION_ADD,
435
+ collection: 'testCollection'
436
+ };
437
+ const CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
438
+ action: ACTION,
439
+ formState: FORM_STATE
440
+ });
441
+ _onPageAction.default.apply(void 0, Object.values(CUSTOM_ARGS));
442
+ preActionChecks();
443
+ expect(FORM_STATE.page.formData["".concat(ACTION.collection, "ActiveId")]).toEqual(null);
444
+ expect(FORM_STATE.page.formData[ACTION.collection].length).toEqual(2);
445
+ postActionChecks(CUSTOM_ARGS);
446
+
447
+ // This action type also sets up a call to handlers.navigate
448
+ // that's called as part of the onSubmit hook's onSuccess callback.
449
+ expect(_handlers.default.navigateCalls).toEqual(1);
450
+ });
383
451
  it("should clean previous collection entry fields at top level of formData", () => {
384
452
  const FORM_STATE = {
385
453
  page: {
@@ -497,8 +565,8 @@ describe('components.FormRenderer.onPageAction', () => {
497
565
  });
498
566
  postActionChecks(CUSTOM_ARGS);
499
567
 
500
- // This action type uses the default onPageChange.
501
- expect(onPageChangeCalls).toEqual(1);
568
+ // This action type uses handlers navigate callback
569
+ expect(_handlers.default.navigateCalls).toEqual(1);
502
570
  });
503
571
  it("should not store a reference to the removed item if action.recordRemoval is false", () => {
504
572
  const FORM_STATE = {
@@ -530,8 +598,8 @@ describe('components.FormRenderer.onPageAction', () => {
530
598
  expect(FORM_STATE.page.formData.testCollectionLastRemoved).toBeUndefined();
531
599
  postActionChecks(CUSTOM_ARGS);
532
600
 
533
- // This action type uses the default onPageChange.
534
- expect(onPageChangeCalls).toEqual(1);
601
+ // This action type uses handlers navigate callback
602
+ expect(_handlers.default.navigateCalls).toEqual(1);
535
603
  });
536
604
  it("should not store a reference to the removed item if action.recordRemoval is not defined", () => {
537
605
  const FORM_STATE = {
@@ -562,8 +630,8 @@ describe('components.FormRenderer.onPageAction', () => {
562
630
  expect(FORM_STATE.page.formData.testCollectionLastRemoved).toBeUndefined();
563
631
  postActionChecks(CUSTOM_ARGS);
564
632
 
565
- // This action type uses the default onPageChange.
566
- expect(onPageChangeCalls).toEqual(1);
633
+ // This action type uses handlers navigate callback
634
+ expect(_handlers.default.navigateCalls).toEqual(1);
567
635
  });
568
636
  it("should clean collection fields at top level of formData when entries remain", () => {
569
637
  // After removing the active entry, the collection data is assigned to the
@@ -607,8 +675,8 @@ describe('components.FormRenderer.onPageAction', () => {
607
675
  expect(FORM_STATE.page.formData.testCollectionActiveId).toEqual('leaveMeAlone');
608
676
  postActionChecks(CUSTOM_ARGS);
609
677
 
610
- // This action type uses the default onPageChange.
611
- expect(onPageChangeCalls).toEqual(1);
678
+ // This action type uses handlers navigate callback
679
+ expect(_handlers.default.navigateCalls).toEqual(1);
612
680
  });
613
681
  it("should clean collection fields at top level of formData after last entry removed", () => {
614
682
  // After removing the last active entry, the collection array and activeID are deleted,
@@ -646,8 +714,8 @@ describe('components.FormRenderer.onPageAction', () => {
646
714
  expect(FORM_STATE.page.formData.testCollectionActiveId).toBeUndefined();
647
715
  postActionChecks(CUSTOM_ARGS);
648
716
 
649
- // This action type uses the default onPageChange.
650
- expect(onPageChangeCalls).toEqual(1);
717
+ // This action type uses handlers navigate callback
718
+ expect(_handlers.default.navigateCalls).toEqual(1);
651
719
  });
652
720
  });
653
721
  describe('if validation fails', () => {
@@ -708,6 +776,78 @@ describe('components.FormRenderer.onPageAction', () => {
708
776
  expect(FORM_STATE.page.formData.parent[0].child[0].id).toEqual('12345');
709
777
  });
710
778
  });
779
+ it("should work for the ".concat(_models.PageAction.TYPES.SAVE_AND_NAVIGATE, " action type to reset the active ID"), () => {
780
+ // In this test the action includes the resetActiveId field
781
+ // requiring that the active ID is pointed to the first entry in the collection
782
+ // so that user can cycle through the full collection again.
783
+ const FORM_STATE = {
784
+ page: {
785
+ formData: {
786
+ testCollection: [{
787
+ id: '1'
788
+ }, {
789
+ id: '2'
790
+ }, {
791
+ id: '3'
792
+ }],
793
+ testCollectionActiveId: '3'
794
+ }
795
+ }
796
+ };
797
+ const ACTION = {
798
+ type: _models.PageAction.TYPES.SAVE_AND_NAVIGATE,
799
+ collection: 'testCollection',
800
+ resetActiveId: true
801
+ };
802
+ const CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
803
+ formState: FORM_STATE,
804
+ action: ACTION
805
+ });
806
+ _onPageAction.default.apply(void 0, Object.values(CUSTOM_ARGS));
807
+ preActionChecks();
808
+ // All the actions does in this case is set a function
809
+ // to be called in the onSuccess callback for the onSubmit hook.
810
+ postActionChecks(CUSTOM_ARGS);
811
+ expect(_handlers.default.navigateCalls).toEqual(1);
812
+ expect(FORM_STATE.page.formData["".concat(ACTION.collection, "ActiveId")]).toEqual('1');
813
+ });
814
+ it("should work for the ".concat(_models.PageAction.TYPES.SAVE_AND_NAVIGATE, " action type to set addAnother to yes"), () => {
815
+ // If the user is midway through the collection
816
+ // then the addAnother field is set to yes
817
+ // to allow the navigation to move to next entry.
818
+ const FORM_STATE = {
819
+ page: {
820
+ formData: {
821
+ tests: [{
822
+ id: '1'
823
+ }, {
824
+ id: '2'
825
+ }, {
826
+ id: '3'
827
+ }],
828
+ testsActiveId: '2',
829
+ addAnotherTest: 'No'
830
+ }
831
+ }
832
+ };
833
+ const ACTION = {
834
+ type: _models.PageAction.TYPES.SAVE_AND_NAVIGATE,
835
+ collection: 'tests'
836
+ };
837
+ const CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
838
+ formState: FORM_STATE,
839
+ action: ACTION
840
+ });
841
+ _onPageAction.default.apply(void 0, Object.values(CUSTOM_ARGS));
842
+ preActionChecks();
843
+ // All the actions does in this case is set a function
844
+ // to be called in the onSuccess callback for the onSubmit hook.
845
+ postActionChecks(CUSTOM_ARGS);
846
+ expect(_handlers.default.navigateCalls).toEqual(1);
847
+ expect(FORM_STATE.page.formData["".concat(ACTION.collection, "ActiveId")]).toEqual('2');
848
+ expect(FORM_STATE.page.formData[ACTION.collection].length).toEqual(3);
849
+ expect(FORM_STATE.page.formData.addAnotherTest).toEqual('Yes');
850
+ });
711
851
  it("should work for the ".concat(_models.PageAction.TYPES.NAVIGATE, " action type"), () => {
712
852
  const FORM_STATE = {
713
853
  page: {
@@ -19,6 +19,12 @@ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object
19
19
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
20
20
  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); } // Global imports
21
21
  // Local imports
22
+ const showAction = (data, action) => {
23
+ if (!action.show_when) {
24
+ return true;
25
+ }
26
+ return (0, _meetsAllConditions.default)(action.show_when, data);
27
+ };
22
28
  const getContainerForPage = (page, item, labelCount, fullPath) => ({
23
29
  id: item.id,
24
30
  fieldId: item.id,
@@ -74,10 +80,10 @@ const getCollectionNameHeading = (page, titleName, config, changeAction) => ({
74
80
  label: 'Change'
75
81
  }
76
82
  });
77
- const getActionRows = function (page, item, onAction, labelCount) {
83
+ const getActionRows = function (page, item, onAction, labelCount, data) {
78
84
  var _page$collection;
79
- let activeIds = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
80
- return (_page$collection = page.collection) === null || _page$collection === void 0 || (_page$collection = _page$collection.actions) === null || _page$collection === void 0 ? void 0 : _page$collection.map(action => {
85
+ let activeIds = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {};
86
+ return (_page$collection = page.collection) === null || _page$collection === void 0 || (_page$collection = _page$collection.actions) === null || _page$collection === void 0 ? void 0 : _page$collection.filter(action => showAction(data, action)).map(action => {
81
87
  if (action.type === 'remove') {
82
88
  return (0, _getCYACollectionDeleteAction.default)(page, item, onAction, labelCount, action, activeIds);
83
89
  }
@@ -211,7 +217,7 @@ const getCYARowsForCollectionPage = function (page, onAction, fnOverride, data)
211
217
  const labelCount = itemIndex + 1;
212
218
  const actionPosition = ((_page$collection2 = page.collection) === null || _page$collection2 === void 0 ? void 0 : _page$collection2.actionPosition) || 'top';
213
219
  const fullPath = "".concat(page.collection.name, "[").concat(itemIndex, "]");
214
- const actionRows = getActionRows(page, item, onAction, labelCount, activeIds);
220
+ const actionRows = getActionRows(page, item, onAction, labelCount, data, activeIds);
215
221
  if (!hideItemTitles) {
216
222
  const titleRow = getTitleRowForItem(page, item, page.id, labelCount, fullPath);
217
223
  rows = rows.concat(titleRow);
@@ -595,6 +595,55 @@ describe('utils.CheckYourAnswers.getCYARowsForCollectionPage', () => {
595
595
  expect(ROWS[ROWS.length - 2].action.label).toEqual('custom remove label');
596
596
  expect(ROWS[ROWS.length - 1].action.label).toEqual('custom change label');
597
597
  });
598
+ it('Show show the collection action when show_when condition is true', () => {
599
+ const FORM_DATA = {
600
+ collection: [{
601
+ id: '01',
602
+ testText: 'value'
603
+ }]
604
+ };
605
+ const PAGE = _objectSpread(_objectSpread({}, MASTER_PAGE), {}, {
606
+ collection: _objectSpread(_objectSpread({}, MASTER_PAGE.collection), {}, {
607
+ // Add a show_when to the colleciton action.
608
+ actions: MASTER_PAGE.collection.actions.map((action, index) => index === 1 ? _objectSpread(_objectSpread({}, action), {}, {
609
+ show_when: [{
610
+ field: "collection.length",
611
+ op: "=",
612
+ value: 1
613
+ }]
614
+ }) : action)
615
+ }),
616
+ formData: FORM_DATA
617
+ });
618
+ const ROWS = (0, _getCYARowsForCollectionPage.default)(PAGE, null, null, FORM_DATA);
619
+ expect(ROWS[2].action.label).toEqual('Change');
620
+ expect(ROWS[3].action.label).toEqual('custom remove label');
621
+ expect(ROWS[4].action.label).toEqual('custom change label');
622
+ });
623
+ it('Should hide the collection action when show_when condition is false', () => {
624
+ const FORM_DATA = {
625
+ collection: [{
626
+ id: '01',
627
+ testText: 'value'
628
+ }]
629
+ };
630
+ const PAGE = _objectSpread(_objectSpread({}, MASTER_PAGE), {}, {
631
+ collection: _objectSpread(_objectSpread({}, MASTER_PAGE.collection), {}, {
632
+ // Add a show_when to the colleciton action.
633
+ actions: MASTER_PAGE.collection.actions.map((action, index) => index === 1 ? _objectSpread(_objectSpread({}, action), {}, {
634
+ show_when: [{
635
+ field: "collection.length",
636
+ op: "=",
637
+ value: 10
638
+ }]
639
+ }) : action)
640
+ }),
641
+ formData: FORM_DATA
642
+ });
643
+ const ROWS = (0, _getCYARowsForCollectionPage.default)(PAGE, null, null, FORM_DATA);
644
+ expect(ROWS[2].action.label).toEqual('Change');
645
+ expect(ROWS[3].action.label).toEqual('custom change label');
646
+ });
598
647
  it('should ignore the result of the override function if it returns nullish', () => {
599
648
  const FORM_DATA = {
600
649
  collection: [{
@@ -28,6 +28,8 @@ const removeCollectionPageEntry = (collectionName, formData, id) => {
28
28
  }
29
29
  const deletedEntry = collectionData[indexToDelete];
30
30
  collectionData.splice(indexToDelete, 1);
31
+ const data = formData;
32
+ data["".concat(collectionName, "ActiveId")] = collectionData.length > 0 ? collectionData[0].id : null;
31
33
  return deletedEntry;
32
34
  };
33
35
  var _default = exports.default = removeCollectionPageEntry;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ukhomeoffice/cop-react-form-renderer",
3
- "version": "7.1.0-alpha",
3
+ "version": "7.1.0-gamma",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "build-storybook": "storybook build",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@ukhomeoffice/cop-react-components": "5.0.0",
22
- "axios": "^0.23.0",
22
+ "axios": "0.30.0",
23
23
  "dayjs": "^1.11.0",
24
24
  "govuk-frontend": "5.10.2",
25
25
  "uuid": "11.1.0",