@ukhomeoffice/cop-react-form-renderer 6.0.6-peter → 6.6.1

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 (80) hide show
  1. package/dist/components/CheckYourAnswers/CheckYourAnswers.scss +2 -2
  2. package/dist/components/CollectionPage/CollectionPage.js +8 -2
  3. package/dist/components/CollectionSummary/BannerStrip.js +3 -2
  4. package/dist/components/CollectionSummary/BannerStrip.scss +2 -2
  5. package/dist/components/CollectionSummary/BannerStrip.test.js +39 -4
  6. package/dist/components/CollectionSummary/CollectionSummary.js +82 -63
  7. package/dist/components/CollectionSummary/CollectionSummary.scss +1 -1
  8. package/dist/components/CollectionSummary/CollectionSummary.test.js +40 -80
  9. package/dist/components/CollectionSummary/Confirmation.scss +1 -1
  10. package/dist/components/CollectionSummary/RenderListView.js +23 -19
  11. package/dist/components/CollectionSummary/RenderListView.scss +11 -2
  12. package/dist/components/CollectionSummary/RenderListView.test.js +14 -4
  13. package/dist/components/CollectionSummary/SummaryCard.js +61 -40
  14. package/dist/components/CollectionSummary/SummaryCard.scss +2 -1
  15. package/dist/components/CollectionSummary/SummaryCard.test.js +193 -150
  16. package/dist/components/CollectionSummary/SummaryCardDetails.js +70 -13
  17. package/dist/components/CollectionSummary/SummaryCardDetails.scss +45 -8
  18. package/dist/components/CollectionSummary/SummaryCardDetails.test.js +174 -26
  19. package/dist/components/CollectionSummary/SummaryCardValidationContext.js +15 -5
  20. package/dist/components/CollectionSummary/SummaryCardValidationContext.test.js +5 -4
  21. package/dist/components/FormComponent/Collection.js +24 -17
  22. package/dist/components/FormComponent/Collection.test.js +138 -0
  23. package/dist/components/FormComponent/FormComponent.js +12 -0
  24. package/dist/components/FormPage/FormPage.scss +1 -1
  25. package/dist/components/FormRenderer/FormRenderer.js +7 -4
  26. package/dist/components/FormRenderer/FormRenderer.scss +1 -1
  27. package/dist/components/FormRenderer/helpers/index.js +1 -3
  28. package/dist/components/FormRenderer/onPageAction.js +7 -9
  29. package/dist/components/FormRenderer/onPageAction.test.js +18 -9
  30. package/dist/components/SummaryList/SummaryList.scss +2 -2
  31. package/dist/components/TaskList/TaskList.scss +1 -1
  32. package/dist/context/ValidationContext/ValidationContext.js +49 -5
  33. package/dist/context/ValidationContext/ValidationContext.test.js +16 -7
  34. package/dist/hooks/useRefData.js +1 -1
  35. package/dist/utils/CheckYourAnswers/showComponentCYA.js +1 -2
  36. package/dist/utils/CheckYourAnswers/showComponentCYA.test.js +5 -0
  37. package/dist/utils/CollectionPage/addCollectionPageEntry.js +1 -2
  38. package/dist/utils/CollectionPage/addCollectionPageEntry.test.js +4 -24
  39. package/dist/utils/CollectionPage/duplicateCollectionPageEntry.js +22 -2
  40. package/dist/utils/CollectionPage/duplicateCollectionPageEntry.test.js +39 -4
  41. package/dist/utils/CollectionPage/getErrorsForCollection.js +55 -0
  42. package/dist/utils/CollectionPage/getErrorsForCollection.test.js +155 -0
  43. package/dist/utils/CollectionPage/getQuickEditPage.js +14 -5
  44. package/dist/utils/CollectionPage/getQuickEditPage.test.js +14 -29
  45. package/dist/utils/CollectionPage/index.js +2 -0
  46. package/dist/utils/CollectionPage/mergeCollectionPages.js +0 -1
  47. package/dist/utils/CollectionPage/setCollectionPageData.js +9 -4
  48. package/dist/utils/CollectionPage/setCollectionPageData.test.js +18 -0
  49. package/dist/utils/Component/isEditable.js +1 -1
  50. package/dist/utils/Condition/meetsCondition.js +18 -0
  51. package/dist/utils/Condition/meetsCondition.test.js +100 -0
  52. package/dist/utils/Data/getOptions.js +10 -0
  53. package/dist/utils/Data/getOptions.test.js +73 -0
  54. package/dist/utils/Data/nestInRefdataOptions.js +49 -0
  55. package/dist/utils/Data/nestInRefdataOptions.test.js +236 -0
  56. package/dist/utils/Validate/additional/mustBeUniqueInCollection.js +4 -0
  57. package/dist/utils/Validate/additional/mustBeUniqueInCollection.test.js +36 -0
  58. package/dist/utils/Validate/validateContainer.js +3 -1
  59. package/dist/utils/Validate/validateContainer.test.js +33 -0
  60. package/dist/utils/Validate/validateEmail.js +1 -1
  61. package/dist/utils/Validate/validatePage.js +10 -1
  62. package/dist/utils/Validate/validatePage.test.js +69 -0
  63. package/package.json +4 -4
  64. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/component-used-in-multiple-pages-data.json +0 -4
  65. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/component-used-in-multiple-pages-form.json +0 -61
  66. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/data-with-collection-data-removed.json +0 -4
  67. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/data-with-collections.json +0 -8
  68. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/data-with-components-removed.json +0 -3
  69. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/data-with-components.json +0 -5
  70. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/data-with-entire-collection-removed.json +0 -3
  71. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/data-with-nested-component-removed.json +0 -10
  72. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/data-with-nested-components.json +0 -11
  73. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/form-for-nested-components.json +0 -96
  74. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/form-with-collections-delete-entire.json +0 -47
  75. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/form-with-collections.json +0 -46
  76. package/dist/components/FormRenderer/clear-uncompleted-routes/test-data/form-with-components.json +0 -48
  77. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.js +0 -175
  78. package/dist/components/FormRenderer/helpers/clearOutUncompletedRoutes.test.js +0 -113
  79. package/dist/components/FormRenderer/helpers/deleteNodeByPath.js +0 -20
  80. package/dist/components/FormRenderer/helpers/deleteNodeByPath.test.js +0 -56
@@ -652,6 +652,106 @@ describe('utils.Condition.meetsCondition', function () {
652
652
  expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeTruthy();
653
653
  });
654
654
  });
655
+ describe('operator includesObjectProp', function () {
656
+ it('should accept a property that exists within one of the objects in the value', function () {
657
+ var VALUE = [{
658
+ id: 1
659
+ }, {
660
+ id: 2
661
+ }, {
662
+ id: 3
663
+ }];
664
+ var CONDITION = {
665
+ op: 'includesObjectProp',
666
+ key: 'id',
667
+ value: 2
668
+ };
669
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeTruthy();
670
+ });
671
+ it('should reject a property that doesnt exist within one of the objects in the value', function () {
672
+ var VALUE = [{
673
+ id: 1
674
+ }, {
675
+ id: 2
676
+ }, {
677
+ id: 3
678
+ }];
679
+ var CONDITION = {
680
+ op: 'includesObjectProp',
681
+ key: 'id',
682
+ value: 4
683
+ };
684
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeFalsy();
685
+ });
686
+ it('should reject a value that is not an array', function () {
687
+ var VALUE = 'badValue';
688
+ var CONDITION = {
689
+ op: 'includesObjectProp',
690
+ key: 'id',
691
+ value: 7
692
+ };
693
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeFalsy();
694
+ });
695
+ it('should reject an empty array', function () {
696
+ var VALUE = [];
697
+ var CONDITION = {
698
+ op: 'includesObjectProp',
699
+ key: 'id',
700
+ value: 7
701
+ };
702
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeFalsy();
703
+ });
704
+ });
705
+ describe('operator !includesObjectProp', function () {
706
+ it('should reject a property that exists within one of the objects in the value', function () {
707
+ var VALUE = [{
708
+ id: 1
709
+ }, {
710
+ id: 2
711
+ }, {
712
+ id: 3
713
+ }];
714
+ var CONDITION = {
715
+ op: '!includesObjectProp',
716
+ key: 'id',
717
+ value: 2
718
+ };
719
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeFalsy();
720
+ });
721
+ it('should accept a property that doesnt exist within one of the objects in the value', function () {
722
+ var VALUE = [{
723
+ id: 1
724
+ }, {
725
+ id: 2
726
+ }, {
727
+ id: 3
728
+ }];
729
+ var CONDITION = {
730
+ op: '!includesObjectProp',
731
+ key: 'id',
732
+ value: 4
733
+ };
734
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeTruthy();
735
+ });
736
+ it('should accept a value that is not an array', function () {
737
+ var VALUE = 'badValue';
738
+ var CONDITION = {
739
+ op: '!includesObjectProp',
740
+ key: 'id',
741
+ value: 7
742
+ };
743
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeTruthy();
744
+ });
745
+ it('should accept an empty array', function () {
746
+ var VALUE = [];
747
+ var CONDITION = {
748
+ op: '!includesObjectProp',
749
+ key: 'id',
750
+ value: 7
751
+ };
752
+ expect((0, _meetsCondition.default)(CONDITION, VALUE)).toBeTruthy();
753
+ });
754
+ });
655
755
  describe('operator includesAllOf', function () {
656
756
  it('should reject when value is not an array', function () {
657
757
  var VALUE = 'Not an array';
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _copReactComponents = require("@ukhomeoffice/cop-react-components");
8
8
  var _Condition = _interopRequireDefault(require("../Condition"));
9
+ var _nestInRefdataOptions = _interopRequireDefault(require("./nestInRefdataOptions"));
9
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
11
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
11
12
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -25,6 +26,10 @@ var interpolateOptions = function interpolateOptions(config, options) {
25
26
  if (typeof opt === 'string') {
26
27
  return opt;
27
28
  }
29
+ if (config.alternativeHintField) {
30
+ // eslint-disable-next-line no-param-reassign
31
+ opt.hint = opt[config.alternativeHintField];
32
+ }
28
33
  return _objectSpread(_objectSpread({}, opt), {}, {
29
34
  value: _copReactComponents.Utils.interpolateString(opt.value, config.formData),
30
35
  label: _copReactComponents.Utils.interpolateString(opt.label, config.formData)
@@ -35,6 +40,11 @@ var interpolateOptions = function interpolateOptions(config, options) {
35
40
  };
36
41
  var getOptions = function getOptions(config, callback) {
37
42
  if (config) {
43
+ var _config$data, _config$data2;
44
+ if (config.options && config !== null && config !== void 0 && (_config$data = config.data) !== null && _config$data !== void 0 && _config$data.options && config !== null && config !== void 0 && (_config$data2 = config.data) !== null && _config$data2 !== void 0 && _config$data2.url) {
45
+ var combinedOptions = (0, _nestInRefdataOptions.default)(config.options, config.data.options);
46
+ return callback(interpolateOptions(config, combinedOptions));
47
+ }
38
48
  if (config.options) {
39
49
  return callback(interpolateOptions(config, config.options));
40
50
  }
@@ -135,6 +135,79 @@ describe('utils', function () {
135
135
  expect(options).toEqual([CONFIG.options[0]]);
136
136
  });
137
137
  });
138
+ it('should should use both data from refdata url and options if both present', function () {
139
+ var CONFIG = {
140
+ data: {
141
+ options: [{
142
+ refdata_match: {
143
+ id: 2
144
+ },
145
+ nested: [{
146
+ value: 'a',
147
+ label: 'Alpha'
148
+ }]
149
+ }],
150
+ url: 'refdata.url'
151
+ },
152
+ options: [{
153
+ id: 1,
154
+ description: 'First refdata item',
155
+ value: 'firstValue',
156
+ label: 'First label'
157
+ }, {
158
+ id: 2,
159
+ description: 'Second refdata item',
160
+ value: 'secondValue',
161
+ label: 'Second label'
162
+ }, {
163
+ id: 3,
164
+ description: 'Third refdata item',
165
+ value: 'thirdValue',
166
+ label: 'Third label'
167
+ }]
168
+ };
169
+ var MERGED_CONFIG = [{
170
+ id: 1,
171
+ description: 'First refdata item',
172
+ value: 'firstValue',
173
+ label: 'First label'
174
+ }, {
175
+ id: 2,
176
+ description: 'Second refdata item',
177
+ value: 'secondValue',
178
+ nested: [{
179
+ value: 'a',
180
+ label: 'Alpha'
181
+ }],
182
+ label: 'Second label'
183
+ }, {
184
+ id: 3,
185
+ description: 'Third refdata item',
186
+ value: 'thirdValue',
187
+ label: 'Third label'
188
+ }];
189
+ (0, _getOptions.default)(CONFIG, function (options) {
190
+ expect(options).toEqual(MERGED_CONFIG);
191
+ });
192
+ });
193
+ it('should override the hint if specified in the config', function () {
194
+ var CONFIG = {
195
+ alternativeHintField: 'description',
196
+ options: [{
197
+ value: 'a',
198
+ label: 'Alpha',
199
+ description: 'first'
200
+ }, {
201
+ value: 'b',
202
+ label: 'Bravo',
203
+ description: 'second'
204
+ }]
205
+ };
206
+ (0, _getOptions.default)(CONFIG, function (options) {
207
+ expect(options[0].hint).toEqual(CONFIG.options[0].description);
208
+ expect(options[1].hint).toEqual(CONFIG.options[1].description);
209
+ });
210
+ });
138
211
  });
139
212
  });
140
213
  });
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
8
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
9
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
10
+ 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; }
11
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
12
+ 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); }
13
+ /**
14
+ * Combines refdata options with custom options with the use of `from_refdata` in the custom option
15
+ * Allowing for nesting of components within refdata lists of checkboxes or radios
16
+ * Also allows appending custom options to refdata options list
17
+ * @param {Array} refdataOptions The list of options coming from refdata
18
+ * @param {Array} options The list of options being defined in the form
19
+ * @returns A combined list of refdata options and custom options to be rendered
20
+ */
21
+ var nestInRefdataOptions = function nestInRefdataOptions(refdataOptions, options) {
22
+ var nestedRefdataOptions = refdataOptions;
23
+ options.forEach(function (option, index) {
24
+ if (option.refdata_match) {
25
+ var refdataMatch = option.refdata_match;
26
+ var refdataKey = Object.keys(refdataMatch)[0];
27
+ var refdataIndex = refdataOptions.findIndex(function (refdataOption) {
28
+ return refdataOption[refdataKey] === refdataMatch[refdataKey];
29
+ });
30
+ if (refdataIndex >= 0) {
31
+ nestedRefdataOptions[refdataIndex] = _objectSpread(_objectSpread({}, refdataOptions[refdataIndex]), {}, {
32
+ nested: option.nested
33
+ });
34
+ }
35
+ }
36
+
37
+ // checks if already exists in `nestedRefdataOptions` else
38
+ // re-renders would cause array to grow with each render
39
+ if (option.combineWithRefdata && !nestedRefdataOptions.includes(option)) {
40
+ // adds previous array entry if string e.g. "or" operator
41
+ if (index > 0 && typeof options[index - 1] === 'string') {
42
+ nestedRefdataOptions.push(options[index - 1]);
43
+ }
44
+ nestedRefdataOptions.push(option);
45
+ }
46
+ });
47
+ return nestedRefdataOptions;
48
+ };
49
+ var _default = exports.default = nestInRefdataOptions;
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+
3
+ var _nestInRefdataOptions = _interopRequireDefault(require("./nestInRefdataOptions"));
4
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
5
+ describe('utils.Data.combineOptions', function () {
6
+ it('should combine refdata options with a custom option', function () {
7
+ var refdataOptions = [{
8
+ id: 1,
9
+ description: 'First refdata item',
10
+ value: 'firstValue',
11
+ label: 'First label'
12
+ }, {
13
+ id: 2,
14
+ description: 'Second refdata item',
15
+ value: 'secondValue',
16
+ label: 'Second label'
17
+ }, {
18
+ id: 3,
19
+ description: 'Third refdata item',
20
+ value: 'thirdValue',
21
+ label: 'Third label'
22
+ }];
23
+ var customOptions = [{
24
+ refdata_match: {
25
+ id: 2
26
+ },
27
+ nested: [{
28
+ id: 'nestedComponent',
29
+ fieldId: 'nestedComponent',
30
+ type: 'text',
31
+ label: 'Nested Component'
32
+ }]
33
+ }];
34
+ expect((0, _nestInRefdataOptions.default)(refdataOptions, customOptions)).toEqual([{
35
+ description: 'First refdata item',
36
+ id: 1,
37
+ label: 'First label',
38
+ value: 'firstValue'
39
+ }, {
40
+ description: 'Second refdata item',
41
+ id: 2,
42
+ label: 'Second label',
43
+ nested: [{
44
+ id: 'nestedComponent',
45
+ fieldId: 'nestedComponent',
46
+ type: 'text',
47
+ label: 'Nested Component'
48
+ }],
49
+ value: 'secondValue'
50
+ }, {
51
+ description: 'Third refdata item',
52
+ id: 3,
53
+ label: 'Third label',
54
+ value: 'thirdValue'
55
+ }]);
56
+ });
57
+ it('should not combine options if it can`t find a matching refdata option', function () {
58
+ var refdataOptions = [{
59
+ id: 1,
60
+ description: 'First refdata item',
61
+ value: 'firstValue',
62
+ label: 'First label'
63
+ }, {
64
+ id: 2,
65
+ description: 'Second refdata item',
66
+ value: 'secondValue',
67
+ label: 'Second label'
68
+ }, {
69
+ id: 3,
70
+ description: 'Third refdata item',
71
+ value: 'thirdValue',
72
+ label: 'Third label'
73
+ }];
74
+ var customOptions = [{
75
+ refdata_match: {
76
+ id: 4
77
+ },
78
+ nested: [{
79
+ id: 'nestedComponent',
80
+ fieldId: 'nestedComponent',
81
+ type: 'text',
82
+ label: 'Nested Component'
83
+ }]
84
+ }];
85
+ expect((0, _nestInRefdataOptions.default)(refdataOptions, customOptions)).toEqual([{
86
+ description: 'First refdata item',
87
+ id: 1,
88
+ label: 'First label',
89
+ value: 'firstValue'
90
+ }, {
91
+ description: 'Second refdata item',
92
+ id: 2,
93
+ label: 'Second label',
94
+ value: 'secondValue'
95
+ }, {
96
+ description: 'Third refdata item',
97
+ id: 3,
98
+ label: 'Third label',
99
+ value: 'thirdValue'
100
+ }]);
101
+ });
102
+ it('should append refdata options with a custom option when `combineWithRefdata` flag is set', function () {
103
+ var refdataOptions = [{
104
+ id: 1,
105
+ description: 'First refdata item',
106
+ value: 'firstValue',
107
+ label: 'First label'
108
+ }, {
109
+ id: 2,
110
+ description: 'Second refdata item',
111
+ value: 'secondValue',
112
+ label: 'Second label'
113
+ }, {
114
+ id: 3,
115
+ description: 'Third refdata item',
116
+ value: 'thirdValue',
117
+ label: 'Third label'
118
+ }];
119
+ var customOptions = [{
120
+ combineWithRefdata: true,
121
+ id: 'otherComponent',
122
+ fieldId: 'otherComponent',
123
+ type: 'text',
124
+ label: 'Other Component'
125
+ }];
126
+ expect((0, _nestInRefdataOptions.default)(refdataOptions, customOptions)).toEqual([{
127
+ description: 'First refdata item',
128
+ id: 1,
129
+ label: 'First label',
130
+ value: 'firstValue'
131
+ }, {
132
+ description: 'Second refdata item',
133
+ id: 2,
134
+ label: 'Second label',
135
+ value: 'secondValue'
136
+ }, {
137
+ description: 'Third refdata item',
138
+ id: 3,
139
+ label: 'Third label',
140
+ value: 'thirdValue'
141
+ }, {
142
+ combineWithRefdata: true,
143
+ id: 'otherComponent',
144
+ fieldId: 'otherComponent',
145
+ type: 'text',
146
+ label: 'Other Component'
147
+ }]);
148
+ });
149
+ it('should not append refdata options with a custom option when `combineWithRefdata` flag is not set', function () {
150
+ var refdataOptions = [{
151
+ id: 1,
152
+ description: 'First refdata item',
153
+ value: 'firstValue',
154
+ label: 'First label'
155
+ }, {
156
+ id: 2,
157
+ description: 'Second refdata item',
158
+ value: 'secondValue',
159
+ label: 'Second label'
160
+ }, {
161
+ id: 3,
162
+ description: 'Third refdata item',
163
+ value: 'thirdValue',
164
+ label: 'Third label'
165
+ }];
166
+ var customOptions = [{
167
+ id: 'otherComponent',
168
+ fieldId: 'otherComponent',
169
+ type: 'text',
170
+ label: 'Other Component'
171
+ }];
172
+ expect((0, _nestInRefdataOptions.default)(refdataOptions, customOptions)).toEqual([{
173
+ description: 'First refdata item',
174
+ id: 1,
175
+ label: 'First label',
176
+ value: 'firstValue'
177
+ }, {
178
+ description: 'Second refdata item',
179
+ id: 2,
180
+ label: 'Second label',
181
+ value: 'secondValue'
182
+ }, {
183
+ description: 'Third refdata item',
184
+ id: 3,
185
+ label: 'Third label',
186
+ value: 'thirdValue'
187
+ }]);
188
+ });
189
+ it('should append refdata options with a string when `combineWithRefdata` flag is set and custom option comes after string', function () {
190
+ var refdataOptions = [{
191
+ id: 1,
192
+ description: 'First refdata item',
193
+ value: 'firstValue',
194
+ label: 'First label'
195
+ }, {
196
+ id: 2,
197
+ description: 'Second refdata item',
198
+ value: 'secondValue',
199
+ label: 'Second label'
200
+ }, {
201
+ id: 3,
202
+ description: 'Third refdata item',
203
+ value: 'thirdValue',
204
+ label: 'Third label'
205
+ }];
206
+ var customOptions = ["or", {
207
+ combineWithRefdata: true,
208
+ id: 'otherComponent',
209
+ fieldId: 'otherComponent',
210
+ type: 'text',
211
+ label: 'Other Component'
212
+ }];
213
+ expect((0, _nestInRefdataOptions.default)(refdataOptions, customOptions)).toEqual([{
214
+ description: 'First refdata item',
215
+ id: 1,
216
+ label: 'First label',
217
+ value: 'firstValue'
218
+ }, {
219
+ description: 'Second refdata item',
220
+ id: 2,
221
+ label: 'Second label',
222
+ value: 'secondValue'
223
+ }, {
224
+ description: 'Third refdata item',
225
+ id: 3,
226
+ label: 'Third label',
227
+ value: 'thirdValue'
228
+ }, "or", {
229
+ combineWithRefdata: true,
230
+ id: 'otherComponent',
231
+ fieldId: 'otherComponent',
232
+ type: 'text',
233
+ label: 'Other Component'
234
+ }]);
235
+ });
236
+ });
@@ -10,6 +10,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
10
10
  /**
11
11
  * @param {*} value - the value to check.
12
12
  * @param {string} config.collectionPath - the path to the collection within formData
13
+ * @param {string} config.caseInsensitive - true to ignore case, assumes string values
13
14
  * @param {object} component - the component definition
14
15
  * @param {object} formData - the current form data
15
16
  * @returns true if components value is not the same in any other entry in the collection
@@ -28,6 +29,9 @@ var mustBeUniqueInCollection = function mustBeUniqueInCollection(value, config,
28
29
  return false;
29
30
  }
30
31
  ;
32
+ if (config.caseInsensitive && typeof value === 'string' && typeof entry[component.id] === 'string') {
33
+ return entry[component.id].toUpperCase() === value.toUpperCase();
34
+ }
31
35
  return entry[component.id] === value;
32
36
  });
33
37
  return !result;
@@ -121,6 +121,42 @@ describe('utils', function () {
121
121
  }), COMPONENT_NESTED, FORM_DATA);
122
122
  expect(result).toBeFalsy();
123
123
  });
124
+ test('should return true if other entries have the same value for this field in a different case', function () {
125
+ var FORM_DATA = {
126
+ namesActiveId: 2,
127
+ names: [{
128
+ id: 1,
129
+ firstName: 'ALPHA'
130
+ }, {
131
+ id: 2,
132
+ firstName: 'alpha'
133
+ }, {
134
+ id: 3,
135
+ firstName: 'bravo'
136
+ }]
137
+ };
138
+ var result = (0, _mustBeUniqueInCollection.default)('alpha', CONFIG, COMPONENT, FORM_DATA);
139
+ expect(result).toBeTruthy();
140
+ });
141
+ test('should return false if other entries have the same value for this field in a different case and caseInsensitive is true', function () {
142
+ var FORM_DATA = {
143
+ namesActiveId: 2,
144
+ names: [{
145
+ id: 1,
146
+ firstName: 'ALPHA'
147
+ }, {
148
+ id: 2,
149
+ firstName: 'alpha'
150
+ }, {
151
+ id: 3,
152
+ firstName: 'bravo'
153
+ }]
154
+ };
155
+ var result = (0, _mustBeUniqueInCollection.default)('alpha', _objectSpread(_objectSpread({}, CONFIG), {}, {
156
+ caseInsensitive: true
157
+ }), COMPONENT, FORM_DATA);
158
+ expect(result).toBeFalsy();
159
+ });
124
160
  });
125
161
  });
126
162
  });
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _elevateNestedComponents = _interopRequireDefault(require("../Component/elevateNestedComponents"));
7
8
  var _validateComponent = _interopRequireDefault(require("./validateComponent"));
8
9
  var _additional = _interopRequireDefault(require("./additional"));
9
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -35,7 +36,8 @@ var validateContainer = function validateContainer(container, outerData, formDat
35
36
  var errors = [];
36
37
  if (Array.isArray(container.components)) {
37
38
  var containerData = outerData && container.fieldId ? outerData[container.fieldId] : outerData;
38
- container.components.forEach(function (component) {
39
+ var fullContainerComponents = (0, _elevateNestedComponents.default)(container.components, containerData);
40
+ fullContainerComponents.forEach(function (component) {
39
41
  // It is possible that the container being passed in is a
40
42
  // dummy container just for validation. In that scenario we want
41
43
  // to ignore the full_path and just use the component's fieldId.
@@ -72,4 +72,37 @@ describe('utils.Validate.Container', function () {
72
72
  id: ID
73
73
  });
74
74
  });
75
+ it('should return an array containing errors for nested components if the container has them as children', function () {
76
+ var ID = 'container';
77
+ var LABEL = 'field';
78
+ var CONTAINER = setup(ID, _models.ComponentTypes.CONTAINER, LABEL, false);
79
+ CONTAINER.components = [{
80
+ id: 'testRadio',
81
+ fieldId: 'testRadio',
82
+ type: 'radios',
83
+ data: {
84
+ options: [{
85
+ value: 'yes',
86
+ nested: [{
87
+ id: 'nestedText',
88
+ fieldId: 'nestedText',
89
+ type: 'text',
90
+ required: true,
91
+ custom_errors: [{
92
+ type: 'required',
93
+ message: 'Nested text is required'
94
+ }]
95
+ }]
96
+ }]
97
+ }
98
+ }];
99
+ var DATA = _defineProperty({}, ID, {
100
+ testRadio: 'yes'
101
+ });
102
+ expect((0, _validateContainer.default)(CONTAINER, DATA)).toEqual([{
103
+ id: "".concat(ID, ".nestedText"),
104
+ error: 'Nested text is required',
105
+ properties: undefined
106
+ }]);
107
+ });
75
108
  });
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  // eslint-disable-next-line no-control-regex
8
8
  // const EMAIL_REGEX = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/i;
9
- var HODS_EMAIL_REGEX = /^[a-z0-9._-]+@(digital\.)?homeoffice.gov.uk$/i;
9
+ var HODS_EMAIL_REGEX = /^[a-z0-9._\-']+@(digital\.)?homeoffice.gov.uk$/i;
10
10
 
11
11
  /**
12
12
  * Validates an email address, ensuring it is in the correct domain and
@@ -8,6 +8,7 @@ var _copReactComponents = require("@ukhomeoffice/cop-react-components");
8
8
  var _validateComponent = _interopRequireDefault(require("./validateComponent"));
9
9
  var _CollectionPage = _interopRequireDefault(require("../CollectionPage"));
10
10
  var _showFormPage = _interopRequireDefault(require("../FormPage/showFormPage"));
11
+ var _showComponent = _interopRequireDefault(require("../Component/showComponent"));
11
12
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
13
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
13
14
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -19,9 +20,11 @@ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input ==
19
20
  /**
20
21
  * Validate all of the components on a page.
21
22
  * @param {object} page The page to validate
23
+ * @param {array} queuedErrors Any errors that have been found before this validation.
22
24
  * @returns An array containing all of the errors.
23
25
  */
24
26
  var validatePage = function validatePage(page) {
27
+ var queuedErrors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
25
28
  var data = page.formData;
26
29
  if (page.collection) {
27
30
  var activeIndex = _CollectionPage.default.getActiveIndex(page.collection.name, page.formData);
@@ -32,7 +35,13 @@ var validatePage = function validatePage(page) {
32
35
  }
33
36
  if ((0, _showFormPage.default)(page, data) && Array.isArray(page.components)) {
34
37
  var errs = page.components.reduce(function (errors, component) {
35
- var componentErrors = (0, _validateComponent.default)(component, data, data);
38
+ if (!(0, _showComponent.default)(component, data)) {
39
+ return errors;
40
+ }
41
+ var queuedComponentErrors = queuedErrors.length > 0 ? queuedErrors.filter(function (e) {
42
+ return e.showFor ? e.showFor === component.id : e.id === component.id;
43
+ }) : [];
44
+ var componentErrors = queuedComponentErrors.concat((0, _validateComponent.default)(component, data, data));
36
45
  return errors.concat(componentErrors).flat().map(function (err) {
37
46
  if (err) {
38
47
  return _objectSpread(_objectSpread({}, err), {}, {