@ukhomeoffice/cop-react-form-renderer 5.45.1 → 5.48.1-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -187,6 +187,10 @@ var CollectionSummary = function CollectionSummary(_ref) {
187
187
  },
188
188
  onAction: onAction
189
189
  }), data.map(function (entry, index) {
190
+ var isInError = errors.filter(function (e) {
191
+ return e.entryId === entry.id;
192
+ }).length > 0;
193
+ var finalConfig = _objectSpread(_objectSpread({}, config.card), isInError && config.errorCard ? config.errorCard : {});
190
194
  var key = "".concat(config.fieldId, ".summaryCard").concat(entry.id);
191
195
  return /*#__PURE__*/_react.default.createElement(_SummaryCardValidationContext.default, {
192
196
  entryId: entry.id,
@@ -198,7 +202,7 @@ var CollectionSummary = function CollectionSummary(_ref) {
198
202
  index: index
199
203
  }),
200
204
  masterPage: masterPage,
201
- config: config.card || {},
205
+ config: finalConfig || {},
202
206
  onChange: onSummaryCardChange,
203
207
  onDuplicate: onDuplicate,
204
208
  onDelete: function onDelete() {
@@ -234,6 +238,14 @@ CollectionSummary.propTypes = {
234
238
  fieldsToIgnore: _propTypes.default.arrayOf(_propTypes.default.string)
235
239
  })
236
240
  }),
241
+ errorCard: _propTypes.default.shape({
242
+ banners: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.shape({})])),
243
+ title: _propTypes.default.string,
244
+ details: _propTypes.default.string,
245
+ duplicateAction: _propTypes.default.shape({
246
+ fieldsToIgnore: _propTypes.default.arrayOf(_propTypes.default.string)
247
+ })
248
+ }),
237
249
  confirmation: _propTypes.default.shape({
238
250
  message: _propTypes.default.string,
239
251
  label: _propTypes.default.string
@@ -4,7 +4,7 @@ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" ==
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.default = exports.DEFAULT_TITLE = exports.DEFAULT_REMOVE_BUTTON_LABEL = exports.DEFAULT_FULL_EDIT_BUTTON_LABEL = exports.DEFAULT_EDIT_LABEL = exports.DEFAULT_DUPLICATE_BUTTON_LABEL = exports.DEFAULT_DELETE_BUTTON_LABEL = exports.DEFAULT_CLASS = exports.DEFAULT_CHANGE_BUTTON_LABEL = void 0;
7
+ exports.default = exports.DEFAULT_TITLE = exports.DEFAULT_EDIT_LABEL = exports.DEFAULT_DUPLICATE_BUTTON_LABEL = exports.DEFAULT_DETAILS_TITLE = exports.DEFAULT_DELETE_BUTTON_LABEL = exports.DEFAULT_CLASS = exports.DEFAULT_CHANGE_BUTTON_LABEL = exports.DEFAULT_CHANGE_BUTTON_CLASS = void 0;
8
8
  var _copReactComponents = require("@ukhomeoffice/cop-react-components");
9
9
  var _propTypes = _interopRequireDefault(require("prop-types"));
10
10
  var _react = _interopRequireWildcard(require("react"));
@@ -40,10 +40,10 @@ var DEFAULT_CHANGE_BUTTON_LABEL = exports.DEFAULT_CHANGE_BUTTON_LABEL = 'Change'
40
40
  var DEFAULT_DELETE_BUTTON_LABEL = exports.DEFAULT_DELETE_BUTTON_LABEL = 'Delete';
41
41
  var DEFAULT_EDIT_LABEL = exports.DEFAULT_EDIT_LABEL = 'Quick Edit';
42
42
  var DEFAULT_DUPLICATE_BUTTON_LABEL = exports.DEFAULT_DUPLICATE_BUTTON_LABEL = 'Duplicate';
43
- var DEFAULT_FULL_EDIT_BUTTON_LABEL = exports.DEFAULT_FULL_EDIT_BUTTON_LABEL = 'Full edit';
44
- var DEFAULT_REMOVE_BUTTON_LABEL = exports.DEFAULT_REMOVE_BUTTON_LABEL = 'Remove';
43
+ var DEFAULT_DETAILS_TITLE = exports.DEFAULT_DETAILS_TITLE = 'Full details';
44
+ var DEFAULT_CHANGE_BUTTON_CLASS = exports.DEFAULT_CHANGE_BUTTON_CLASS = 'secondary';
45
45
  var SummaryCard = function SummaryCard(_ref) {
46
- var _config$changeAction2, _config$changeAction3, _config$duplicateActi, _config$deleteAction, _config$deleteAction2, _masterPage$childPage;
46
+ var _config$changeAction2, _config$changeAction3, _config$duplicateActi, _config$deleteAction, _masterPage$childPage;
47
47
  var id = _ref.id,
48
48
  entryData = _ref.entryData,
49
49
  config = _ref.config,
@@ -57,12 +57,12 @@ var SummaryCard = function SummaryCard(_ref) {
57
57
  formData = _ref.formData,
58
58
  masterPage = _ref.masterPage,
59
59
  hideDetails = _ref.hideDetails,
60
- isDuplicated = _ref.isDuplicated;
60
+ inError = _ref.inError;
61
61
  var _useState = (0, _react.useState)(false),
62
62
  _useState2 = _slicedToArray(_useState, 2),
63
63
  quickEdit = _useState2[0],
64
64
  setQuickEdit = _useState2[1];
65
- var classes = _copReactComponents.Utils.classBuilder(DEFAULT_CLASS, classModifiers, isDuplicated ? 'duplicated' : '');
65
+ var classes = _copReactComponents.Utils.classBuilder(DEFAULT_CLASS, classModifiers, inError ? 'error' : '');
66
66
  var quickEditPage = (0, _react.useMemo)(function () {
67
67
  return (0, _getQuickEditPage.default)(config.quickEdit, pages, entryData);
68
68
  }, [pages, config, entryData, quickEdit]);
@@ -152,7 +152,7 @@ var SummaryCard = function SummaryCard(_ref) {
152
152
  className: classes('header-content-detail')
153
153
  }, _copReactComponents.Utils.interpolateString(config.details, entryData))), /*#__PURE__*/_react.default.createElement("div", {
154
154
  className: classes('header-actions')
155
- }, config.quickEdit && isDuplicated && /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
155
+ }, config.quickEdit && /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
156
156
  id: "".concat(id, ".quickEditButton"),
157
157
  onClick: function onClick() {
158
158
  return setQuickEdit(function (prevState) {
@@ -167,8 +167,8 @@ var SummaryCard = function SummaryCard(_ref) {
167
167
  var _config$changeAction;
168
168
  return onChange((_config$changeAction = config.changeAction) === null || _config$changeAction === void 0 ? void 0 : _config$changeAction.page, entryData.id);
169
169
  },
170
- classModifiers: isDuplicated ? 'primary' : 'secondary'
171
- }, isDuplicated ? ((_config$changeAction2 = config.changeAction) === null || _config$changeAction2 === void 0 ? void 0 : _config$changeAction2.isDuplicateLabel) || DEFAULT_FULL_EDIT_BUTTON_LABEL : ((_config$changeAction3 = config.changeAction) === null || _config$changeAction3 === void 0 ? void 0 : _config$changeAction3.label) || DEFAULT_CHANGE_BUTTON_LABEL), config.duplicateAction && !isDuplicated && typeof onDuplicate === 'function' && /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
170
+ classModifiers: ((_config$changeAction2 = config.changeAction) === null || _config$changeAction2 === void 0 ? void 0 : _config$changeAction2.classModifiers) || DEFAULT_CHANGE_BUTTON_CLASS
171
+ }, ((_config$changeAction3 = config.changeAction) === null || _config$changeAction3 === void 0 ? void 0 : _config$changeAction3.label) || DEFAULT_CHANGE_BUTTON_LABEL), config.duplicateAction && typeof onDuplicate === 'function' && /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
172
172
  id: "".concat(id, ".duplicateButton"),
173
173
  onClick: function onClick() {
174
174
  onDuplicate(entryData);
@@ -180,7 +180,7 @@ var SummaryCard = function SummaryCard(_ref) {
180
180
  return onDelete(entryData);
181
181
  },
182
182
  classModifiers: "secondary"
183
- }, isDuplicated ? ((_config$deleteAction = config.deleteAction) === null || _config$deleteAction === void 0 ? void 0 : _config$deleteAction.isDuplicateLabel) || DEFAULT_REMOVE_BUTTON_LABEL : ((_config$deleteAction2 = config.deleteAction) === null || _config$deleteAction2 === void 0 ? void 0 : _config$deleteAction2.label) || DEFAULT_DELETE_BUTTON_LABEL))), quickEdit && /*#__PURE__*/_react.default.createElement(_FormPage.default, {
183
+ }, ((_config$deleteAction = config.deleteAction) === null || _config$deleteAction === void 0 ? void 0 : _config$deleteAction.label) || DEFAULT_DELETE_BUTTON_LABEL))), quickEdit && quickEditPage && /*#__PURE__*/_react.default.createElement(_FormPage.default, {
184
184
  page: quickEditPage,
185
185
  onAction: function onAction(action, patch) {
186
186
  return _onAction(action, patch);
@@ -189,7 +189,7 @@ var SummaryCard = function SummaryCard(_ref) {
189
189
  }), /*#__PURE__*/_react.default.createElement("div", {
190
190
  className: classes('body')
191
191
  }, !hideDetails && /*#__PURE__*/_react.default.createElement(_copReactComponents.Details, {
192
- summary: isDuplicated ? 'View duplicated details' : 'Full details',
192
+ summary: config.detailsTitle || DEFAULT_DETAILS_TITLE,
193
193
  className: "details"
194
194
  }, masterPage === null || masterPage === void 0 || (_masterPage$childPage = masterPage.childPages) === null || _masterPage$childPage === void 0 ? void 0 : _masterPage$childPage.map(function (childPage) {
195
195
  var _childPage$summaryLay;
@@ -235,22 +235,21 @@ SummaryCard.propTypes = {
235
235
  details: _propTypes.default.string,
236
236
  changeAction: _propTypes.default.shape({
237
237
  label: _propTypes.default.string,
238
- isDuplicateLabel: _propTypes.default.string,
239
- page: _propTypes.default.string.isRequired
238
+ page: _propTypes.default.string.isRequired,
239
+ classModifiers: _propTypes.default.string
240
240
  }),
241
241
  deleteAction: _propTypes.default.shape({
242
- label: _propTypes.default.string,
243
- isDuplicateLabel: _propTypes.default.string
242
+ label: _propTypes.default.string
244
243
  }),
245
244
  duplicateAction: _propTypes.default.shape({
246
- label: _propTypes.default.string,
247
- isDuplicateLabel: _propTypes.default.string
245
+ label: _propTypes.default.string
248
246
  }),
249
247
  quickEdit: _propTypes.default.shape({
250
248
  components: _propTypes.default.arrayOf(_propTypes.default.shape({
251
249
  use: _propTypes.default.string
252
250
  }))
253
- })
251
+ }),
252
+ detailsTitle: _propTypes.default.string
254
253
  }).isRequired,
255
254
  masterPage: _propTypes.default.shape({
256
255
  childPages: _propTypes.default.arrayOf(_propTypes.default.shape({
@@ -266,7 +265,7 @@ SummaryCard.propTypes = {
266
265
  parentCollectionName: _propTypes.default.string.isRequired,
267
266
  formData: _propTypes.default.shape({}).isRequired,
268
267
  hideDetails: _propTypes.default.bool,
269
- isDuplicated: _propTypes.default.bool
268
+ inError: _propTypes.default.bool
270
269
  };
271
270
  SummaryCard.defaultProps = {
272
271
  classModifiers: null,
@@ -275,6 +274,6 @@ SummaryCard.defaultProps = {
275
274
  onDuplicate: null,
276
275
  onQuickEdit: null,
277
276
  hideDetails: false,
278
- isDuplicated: false
277
+ inError: false
279
278
  };
280
279
  var _default = exports.default = SummaryCard;
@@ -19,7 +19,7 @@ $govuk-font-family: 'Roboto', arial, sans-serif;
19
19
  padding: 0.75rem;
20
20
  display: block;
21
21
 
22
- &.duplicated {
22
+ &.error {
23
23
  border-left: 7px solid red;
24
24
  }
25
25
 
@@ -204,7 +204,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
204
204
  expect(headerDetails.textContent).toEqual("".concat(ENTRY.detailsText, " that are interpolated"));
205
205
  });
206
206
  describe('Change action button', function () {
207
- var isDuplicatedValue = true;
207
+ var inErrorValue = true;
208
208
  var onChangeArgs = [];
209
209
  var onChangeCalls = 0;
210
210
  var ON_CHANGE = function ON_CHANGE(page, id) {
@@ -213,7 +213,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
213
213
  id: id
214
214
  });
215
215
  onChangeCalls += 1;
216
- isDuplicatedValue = false;
216
+ inErrorValue = false;
217
217
  };
218
218
  beforeEach(function () {
219
219
  onChangeArgs = [];
@@ -252,7 +252,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
252
252
  id: ENTRY.id
253
253
  });
254
254
  });
255
- it('should render correctly when isDuplicated is true', function () {
255
+ it('should render correctly when inError is true', function () {
256
256
  var CONFIG = {
257
257
  changeAction: {
258
258
  label: 'Change label',
@@ -270,7 +270,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
270
270
  masterPage: {
271
271
  childPages: []
272
272
  },
273
- isDuplicated: isDuplicatedValue
273
+ inError: inErrorValue
274
274
  })),
275
275
  container = _renderWithValidation7.container;
276
276
  var _checkSetup7 = checkSetup(container),
@@ -286,8 +286,8 @@ describe('components.CollectionSummary.SummaryCard', function () {
286
286
  id: ENTRY.id
287
287
  });
288
288
 
289
- // Check that the isDuplicated prop is now false
290
- expect(isDuplicatedValue).toBe(false);
289
+ // Check that the inError prop is now false
290
+ expect(inErrorValue).toBe(false);
291
291
  });
292
292
  it('should use the default label when one is not provided', function () {
293
293
  var CONFIG = {
@@ -630,7 +630,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
630
630
  masterPage: {
631
631
  childPages: []
632
632
  },
633
- isDuplicated: true
633
+ inError: true
634
634
  })),
635
635
  container = _renderWithValidation20.container;
636
636
  var _checkSetup19 = checkSetup(container),
@@ -672,7 +672,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
672
672
  masterPage: {
673
673
  childPages: []
674
674
  },
675
- isDuplicated: true
675
+ inError: true
676
676
  })),
677
677
  container = _renderWithValidation21.container;
678
678
  var _checkSetup20 = checkSetup(container),
@@ -735,7 +735,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
735
735
  masterPage: {
736
736
  childPages: []
737
737
  },
738
- isDuplicated: true
738
+ inError: true
739
739
  }))),
740
740
  container = _renderWithValidation22.container;
741
741
  var _checkSetup21 = checkSetup(container),
@@ -782,7 +782,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
782
782
  childPages: []
783
783
  },
784
784
  onQuickEdit: function onQuickEdit() {},
785
- isDuplicated: true
785
+ inError: true
786
786
  }))), container = _renderWithValidation23.container;
787
787
  _checkSetup22 = checkSetup(container), headerActionDiv = _checkSetup22.headerActionDiv;
788
788
  expect(headerActionDiv.children.length).toEqual(1);
@@ -896,7 +896,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
896
896
  masterPage: {
897
897
  childPages: []
898
898
  },
899
- isDuplicated: true
899
+ inError: true
900
900
  })),
901
901
  container = _renderWithValidation24.container;
902
902
  var _checkSetup23 = checkSetup(container),
@@ -944,7 +944,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
944
944
  masterPage: {
945
945
  childPages: []
946
946
  },
947
- isDuplicated: true
947
+ inError: true
948
948
  }), {
949
949
  hooks: hooks
950
950
  }),
@@ -996,7 +996,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
996
996
  masterPage: {
997
997
  childPages: []
998
998
  },
999
- isDuplicated: true
999
+ inError: true
1000
1000
  }), {
1001
1001
  hooks: hooks
1002
1002
  }), container = _renderWithValidation26.container;
@@ -1081,7 +1081,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
1081
1081
  masterPage: {
1082
1082
  childPages: []
1083
1083
  },
1084
- isDuplicated: true
1084
+ inError: true
1085
1085
  }), {
1086
1086
  hooks: hooks
1087
1087
  }), container = _renderWithValidation27.container;
@@ -1122,11 +1122,11 @@ describe('components.CollectionSummary.SummaryCard', function () {
1122
1122
  }, _callee8);
1123
1123
  })));
1124
1124
  it('should apply changes if no errors are present', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee10() {
1125
- var isDuplicatedValue, ON_SUBMIT_CALLS, hooks, ON_QUICK_EDIT_CALLS, ON_QUICK_EDIT, _renderWithValidation28, container, _checkSetup27, headerActionDiv, editButton, quickEdit, component, componentInput, quickEditButtons, saveButton;
1125
+ var inErrorValue, ON_SUBMIT_CALLS, hooks, ON_QUICK_EDIT_CALLS, ON_QUICK_EDIT, _renderWithValidation28, container, _checkSetup27, headerActionDiv, editButton, quickEdit, component, componentInput, quickEditButtons, saveButton;
1126
1126
  return _regeneratorRuntime().wrap(function _callee10$(_context10) {
1127
1127
  while (1) switch (_context10.prev = _context10.next) {
1128
1128
  case 0:
1129
- isDuplicatedValue = true;
1129
+ inErrorValue = true;
1130
1130
  ON_SUBMIT_CALLS = [];
1131
1131
  hooks = {
1132
1132
  onSubmit: function onSubmit(type, patch) {
@@ -1137,7 +1137,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
1137
1137
  ON_QUICK_EDIT = function ON_QUICK_EDIT(_ref12) {
1138
1138
  var target = _ref12.target;
1139
1139
  ON_QUICK_EDIT_CALLS.push(target);
1140
- isDuplicatedValue = false;
1140
+ inErrorValue = false;
1141
1141
  };
1142
1142
  _renderWithValidation28 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_SummaryCard.default, {
1143
1143
  id: ID,
@@ -1157,7 +1157,7 @@ describe('components.CollectionSummary.SummaryCard', function () {
1157
1157
  masterPage: {
1158
1158
  childPages: []
1159
1159
  },
1160
- isDuplicated: isDuplicatedValue
1160
+ inError: inErrorValue
1161
1161
  }), {
1162
1162
  hooks: hooks
1163
1163
  }), container = _renderWithValidation28.container;
@@ -1193,8 +1193,8 @@ describe('components.CollectionSummary.SummaryCard', function () {
1193
1193
  testText: 'new value'
1194
1194
  });
1195
1195
 
1196
- // Check that the isDuplicated prop is now false
1197
- expect(isDuplicatedValue).toBe(false);
1196
+ // Check that the inError prop is now false
1197
+ expect(inErrorValue).toBe(false);
1198
1198
  case 20:
1199
1199
  case "end":
1200
1200
  return _context10.stop();
@@ -157,6 +157,7 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
157
157
  var _useValidation = (0, _hooks.useValidation)(),
158
158
  addErrors = _useValidation.addErrors,
159
159
  clearErrors = _useValidation.clearErrors,
160
+ errors = _useValidation.errors,
160
161
  validate = _useValidation.validate;
161
162
 
162
163
  // Need to set submission data when going back
@@ -356,7 +357,7 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
356
357
  page: formState.page,
357
358
  pages: [].concat(pages),
358
359
  onAction: function onAction(action, patch, patchLabel) {
359
- (0, _onPageAction.default)(action, patch, patchLabel, hooks, data, formState, validate, onPageChange, type, pages, components, pageId, setPagePoint, currentTask, setData, hubDetails, setSubmitted, addErrors, submitting, setSubmitting);
360
+ (0, _onPageAction.default)(action, patch, patchLabel, hooks, data, formState, validate, onPageChange, type, pages, components, pageId, setPagePoint, currentTask, setData, hubDetails, setSubmitted, addErrors, submitting, setSubmitting, errors);
360
361
  },
361
362
  onChange: onChange,
362
363
  hashLink: hashLink,
@@ -367,7 +368,7 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
367
368
  page: formState.page,
368
369
  onCollectionChange: onChange,
369
370
  onAction: function onAction(action, patch, patchLabel) {
370
- (0, _onPageAction.default)(action, patch, patchLabel, hooks, data, formState, validate, onPageChange, type, pages, components, pageId, setPagePoint, currentTask, setData, hubDetails, setSubmitted, addErrors, submitting, setSubmitting);
371
+ (0, _onPageAction.default)(action, patch, patchLabel, hooks, data, formState, validate, onPageChange, type, pages, components, pageId, setPagePoint, currentTask, setData, hubDetails, setSubmitted, addErrors, submitting, setSubmitting, errors);
371
372
  },
372
373
  hashLink: hashLink,
373
374
  classModifiers: formState.page.classModifiers,
@@ -11,10 +11,14 @@ exports.default = void 0;
11
11
  * @param {object} action The action object we're assessing.
12
12
  * @param {object} page The page configuration object this action relates to.
13
13
  * @param {Function} pageValidator A function to validate the page.
14
+ * @param {Array} errors An array of existing errors.
14
15
  * @returns A boolean where `true` means the action can proceed and `false` means it cannot.
15
16
  */
16
- var canActionProceed = function canActionProceed(action, page, pageValidator) {
17
+ var canActionProceed = function canActionProceed(action, page, pageValidator, errors) {
17
18
  if (action.validate) {
19
+ if (action.checkPreexistingErrors && errors.length !== 0) {
20
+ return false;
21
+ }
18
22
  return pageValidator(page).length === 0;
19
23
  }
20
24
  return true;
@@ -27,7 +27,7 @@ describe('components.FormRenderer.helpers.canActionProceed', function () {
27
27
  a: 'Bravo'
28
28
  }
29
29
  };
30
- expect((0, _canActionProceed.default)(ACTION, PAGE, _Validate.default.page)).toBeTruthy();
30
+ expect((0, _canActionProceed.default)(ACTION, PAGE, _Validate.default.page, [])).toBeTruthy();
31
31
  });
32
32
  it('should return false when the page is invalid', function () {
33
33
  var ACTION = {
@@ -42,6 +42,42 @@ describe('components.FormRenderer.helpers.canActionProceed', function () {
42
42
  }],
43
43
  formData: {}
44
44
  };
45
- expect((0, _canActionProceed.default)(ACTION, PAGE, _Validate.default.page)).toBeFalsy();
45
+ expect((0, _canActionProceed.default)(ACTION, PAGE, _Validate.default.page, [])).toBeFalsy();
46
+ });
47
+ it('should return false when the action has checkPreexistingErrors and errors is not empty', function () {
48
+ var ACTION = {
49
+ validate: true,
50
+ checkPreexistingErrors: true
51
+ };
52
+ var PAGE = {
53
+ components: [{
54
+ id: 'a',
55
+ fieldId: 'a',
56
+ label: 'Alpha',
57
+ required: true
58
+ }],
59
+ formData: {
60
+ a: 'value'
61
+ }
62
+ };
63
+ expect((0, _canActionProceed.default)(ACTION, PAGE, _Validate.default.page, ['anError'])).toBeFalsy();
64
+ });
65
+ it('should return true when the action has checkPreexistingErrors and errors is empty', function () {
66
+ var ACTION = {
67
+ validate: true,
68
+ checkPreexistingErrors: true
69
+ };
70
+ var PAGE = {
71
+ components: [{
72
+ id: 'a',
73
+ fieldId: 'a',
74
+ label: 'Alpha',
75
+ required: true
76
+ }],
77
+ formData: {
78
+ a: 'value'
79
+ }
80
+ };
81
+ expect((0, _canActionProceed.default)(ACTION, PAGE, _Validate.default.page, [])).toBeTruthy();
46
82
  });
47
83
  });
@@ -19,7 +19,7 @@ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input ==
19
19
  // (patch captures payload-ready field name and value,
20
20
  // patchLabel captures non-ID values
21
21
  // for display purposes after submission.)
22
- var onPageAction = function onPageAction(action, patch, patchLabel, hooks, data, formState, validate, onPageChange, type, pages, components, pageId, setPagePoint, currentTask, setData, hubDetails, setSubmitted, addErrors, submitting, setSubmitting) {
22
+ var onPageAction = function onPageAction(action, patch, patchLabel, hooks, data, formState, validate, onPageChange, type, pages, components, pageId, setPagePoint, currentTask, setData, hubDetails, setSubmitted, addErrors, submitting, setSubmitting, existingErrors) {
23
23
  // Save a copy of data in case submit errors and we need to revert
24
24
  var preSubmitData = _objectSpread({}, data);
25
25
  var form = formState;
@@ -42,7 +42,7 @@ var onPageAction = function onPageAction(action, patch, patchLabel, hooks, data,
42
42
 
43
43
  // Check to see whether the action is able to proceed, which in
44
44
  // in the case of a submission will validate the fields in the page.
45
- if (_helpers.default.canActionProceed(action, form.page, validate.page) && (!submitting || action.ignoreSubmittingFlag)) {
45
+ if (_helpers.default.canActionProceed(action, form.page, validate.page, existingErrors) && (!submitting || action.ignoreSubmittingFlag)) {
46
46
  _patch = _helpers.default.cleanHiddenNestedData(_patch, form.page);
47
47
  setSubmitting(true);
48
48
  if (action.addToFormData) {
@@ -15,6 +15,7 @@ var _mustBeShorterThan = _interopRequireDefault(require("./mustBeShorterThan"));
15
15
  var _mustBeGreaterThan = _interopRequireDefault(require("./mustBeGreaterThan"));
16
16
  var _mustBeLessThan = _interopRequireDefault(require("./mustBeLessThan"));
17
17
  var _mustBeNumbersOnly = _interopRequireDefault(require("./mustBeNumbersOnly"));
18
+ var _mustBeUniqueInCollection = _interopRequireDefault(require("./mustBeUniqueInCollection"));
18
19
  var _mustEnterAtLeastOne = _interopRequireDefault(require("./mustEnterAtLeastOne"));
19
20
  var _mustHaveLessThanDecimalPlaces = _interopRequireDefault(require("./mustHaveLessThanDecimalPlaces"));
20
21
  var _mustNotContainSql = _interopRequireDefault(require("./mustNotContainSql"));
@@ -34,19 +35,20 @@ var functions = {
34
35
  mustBeLongerThan: _mustBeLongerThan.default,
35
36
  mustBeNumbersOnly: _mustBeNumbersOnly.default,
36
37
  mustBeShorterThan: _mustBeShorterThan.default,
38
+ mustBeUniqueInCollection: _mustBeUniqueInCollection.default,
37
39
  mustEnterAtLeastOne: _mustEnterAtLeastOne.default,
38
40
  mustHaveLessThanDecimalPlaces: _mustHaveLessThanDecimalPlaces.default,
39
41
  mustNotContainSql: _mustNotContainSql.default,
40
42
  mustSelectOnlyOne: _mustSelectOnlyOne.default
41
43
  };
42
- var additionalValidation = function additionalValidation(value, config, component) {
44
+ var additionalValidation = function additionalValidation(value, config, component, formData) {
43
45
  var fn = functions[config.function];
44
46
  if (typeof fn === 'function') {
45
- return fn(value, config, component) ? undefined : config.message;
47
+ return fn(value, config, component, formData) ? undefined : config.message;
46
48
  }
47
49
  return undefined;
48
50
  };
49
- var runAdditionalComponentValidation = function runAdditionalComponentValidation(component, value) {
51
+ var runAdditionalComponentValidation = function runAdditionalComponentValidation(component, value, formData) {
50
52
  // We only care when we have a value - if we don't have one but want one, set `required: true`.
51
53
  // eslint-disable-next-line no-extra-boolean-cast
52
54
  if (!!value) {
@@ -55,7 +57,7 @@ var runAdditionalComponentValidation = function runAdditionalComponentValidation
55
57
  component.additionalValidation.forEach(function (config) {
56
58
  // If we've already encountered an error, don't run any more validators.
57
59
  if (!error) {
58
- error = additionalValidation(value, config, component);
60
+ error = additionalValidation(value, config, component, formData);
59
61
  }
60
62
  });
61
63
  return error;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _getCollectionPageActiveId = _interopRequireDefault(require("../../CollectionPage/getCollectionPageActiveId"));
8
+ var _getCollectionPageData = _interopRequireDefault(require("../../CollectionPage/getCollectionPageData"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ /**
11
+ * @param {*} value - the value to check.
12
+ * @param {string} config.collectionPath - the path to the collection within formData
13
+ * @param {object} component - the component definition
14
+ * @param {object} formData - the current form data
15
+ * @returns true if components value is not the same in any other entry in the collection
16
+ */
17
+ var mustBeUniqueInCollection = function mustBeUniqueInCollection(value, config, component, formData) {
18
+ if (!value || !formData) {
19
+ // null, undefined and empty strings should be picked up by the required flag
20
+ // and not considered here as they would be valid for optional fields.
21
+ return true;
22
+ }
23
+ var activeId = (0, _getCollectionPageActiveId.default)(config.collectionPath, formData);
24
+ var collectionData = (0, _getCollectionPageData.default)(config.collectionPath, formData);
25
+ var result = collectionData === null || collectionData === void 0 ? void 0 : collectionData.some(function (entry) {
26
+ // Don't compare it to itself
27
+ if (entry.id === activeId) {
28
+ return false;
29
+ }
30
+ ;
31
+ return entry[component.id] === value;
32
+ });
33
+ return !result;
34
+ };
35
+ var _default = exports.default = mustBeUniqueInCollection;
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+
3
+ var _mustBeUniqueInCollection = _interopRequireDefault(require("./mustBeUniqueInCollection"));
4
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
5
+ 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); }
6
+ 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; }
7
+ 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; }
8
+ 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; }
9
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
10
+ 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); }
11
+ describe('utils', function () {
12
+ describe('Validate', function () {
13
+ describe('additional', function () {
14
+ describe('mustBeUniqueInCollection', function () {
15
+ var COMPONENT = {
16
+ id: 'firstName',
17
+ type: 'text',
18
+ label: 'First name',
19
+ fieldId: 'firstName',
20
+ additionalValidation: [{
21
+ function: 'mustBeUniqueInCollection',
22
+ collectionPath: 'names',
23
+ message: 'Should be unique'
24
+ }]
25
+ };
26
+ var CONFIG = {
27
+ function: 'mustBeUniqueInCollection',
28
+ collectionPath: 'names',
29
+ message: 'Should be unique'
30
+ };
31
+ var VALUE = 'NAME';
32
+ test('should return true given no value', function () {
33
+ var result = (0, _mustBeUniqueInCollection.default)(undefined, CONFIG, COMPONENT, {});
34
+ expect(result).toBeTruthy();
35
+ });
36
+ test('should return true given no formData', function () {
37
+ var result = (0, _mustBeUniqueInCollection.default)(1, CONFIG, COMPONENT, undefined);
38
+ expect(result).toBeTruthy();
39
+ });
40
+ test('should return true for first entry in a collection', function () {
41
+ var FORM_DATA = {
42
+ namesActiveId: 1,
43
+ names: [{
44
+ id: 1,
45
+ firstName: VALUE
46
+ }]
47
+ };
48
+ var result = (0, _mustBeUniqueInCollection.default)(VALUE, CONFIG, COMPONENT, FORM_DATA);
49
+ expect(result).toBeTruthy();
50
+ });
51
+ test('should return true if other entries do not have the same value for this field', function () {
52
+ var FORM_DATA = {
53
+ namesActiveId: 3,
54
+ names: [{
55
+ id: 1,
56
+ firstName: 'alpha'
57
+ }, {
58
+ id: 2,
59
+ firstName: 'bravo'
60
+ }, {
61
+ id: 3,
62
+ firstName: VALUE
63
+ }]
64
+ };
65
+ var result = (0, _mustBeUniqueInCollection.default)(VALUE, CONFIG, COMPONENT, FORM_DATA);
66
+ expect(result).toBeTruthy();
67
+ });
68
+ test('should return false if other entries do have the same value for this field', function () {
69
+ var FORM_DATA = {
70
+ namesActiveId: 3,
71
+ names: [{
72
+ id: 1,
73
+ firstName: VALUE
74
+ }, {
75
+ id: 2,
76
+ firstName: 'bravo'
77
+ }, {
78
+ id: 3,
79
+ firstName: VALUE
80
+ }]
81
+ };
82
+ var result = (0, _mustBeUniqueInCollection.default)(VALUE, CONFIG, COMPONENT, FORM_DATA);
83
+ expect(result).toBeFalsy();
84
+ });
85
+ test('should return false if other entries do have the same value for this field within a nested collection', function () {
86
+ var NESTED_VALUE = 'delta';
87
+ var FORM_DATA = {
88
+ namesActiveId: 3,
89
+ names: [{
90
+ id: 1,
91
+ firstName: 'alpha'
92
+ }, {
93
+ id: 2,
94
+ firstName: 'bravo'
95
+ }, {
96
+ id: 3,
97
+ firstName: VALUE,
98
+ surnamesActiveId: 2,
99
+ surnames: [{
100
+ id: 1,
101
+ surname: 'delta'
102
+ }, {
103
+ id: 2,
104
+ surname: 'delta'
105
+ }]
106
+ }]
107
+ };
108
+ var COMPONENT_NESTED = {
109
+ id: 'surname',
110
+ type: 'text',
111
+ label: 'Surname',
112
+ fieldId: 'surname',
113
+ additionalValidation: [{
114
+ function: 'mustBeUniqueInCollection',
115
+ collectionPath: 'names.surname',
116
+ message: 'Should be unique'
117
+ }]
118
+ };
119
+ var result = (0, _mustBeUniqueInCollection.default)(NESTED_VALUE, _objectSpread(_objectSpread({}, CONFIG), {}, {
120
+ collectionPath: 'names.surnames'
121
+ }), COMPONENT_NESTED, FORM_DATA);
122
+ expect(result).toBeFalsy();
123
+ });
124
+ });
125
+ });
126
+ });
127
+ });
@@ -95,7 +95,7 @@ var validateComponent = function validateComponent(component, outerData, formDat
95
95
  error = (0, _validateRegex.default)(value, component.label, component.pattern, component.custom_errors);
96
96
  }
97
97
  if (!error && component.additionalValidation) {
98
- error = (0, _additional.default)(component, value);
98
+ error = (0, _additional.default)(component, value, formData);
99
99
  if (component.type === _models.ComponentTypes.DATE && error) {
100
100
  properties = {
101
101
  day: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ukhomeoffice/cop-react-form-renderer",
3
- "version": "5.45.1",
3
+ "version": "5.48.1-alpha",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "clean": "rimraf dist",
@@ -16,7 +16,7 @@
16
16
  "post-compile": "rimraf dist/*.test.* dist/**/*.test.* dist/**/*.stories.* dist/docs dist/assets"
17
17
  },
18
18
  "dependencies": {
19
- "@ukhomeoffice/cop-react-components": "^3.15.7",
19
+ "@ukhomeoffice/cop-react-components": "^3.15.8-alpha",
20
20
  "axios": "^0.23.0",
21
21
  "dayjs": "^1.11.0",
22
22
  "govuk-frontend": "^4.3.1",