@ukhomeoffice/cop-react-form-renderer 6.15.12 → 6.16.0-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.
- package/dist/components/CheckYourAnswers/CheckYourAnswers.js +5 -1
- package/dist/components/FormRenderer/FormRenderer.js +16 -0
- package/dist/components/FormRenderer/onPageAction.js +36 -0
- package/dist/components/FormRenderer/onPageAction.test.js +113 -0
- package/dist/utils/Validate/validateComponent.js +4 -1
- package/dist/utils/canOverrideFieldRequired.js +18 -0
- package/package.json +1 -1
|
@@ -127,12 +127,16 @@ const CheckYourAnswers = _ref => {
|
|
|
127
127
|
}
|
|
128
128
|
})));
|
|
129
129
|
};
|
|
130
|
+
const getDedupedErrors = () => {
|
|
131
|
+
const dedupedErrors = Array.from(new Set(errors.map(a => a.id))).map(id => errors.find(b => b.id === id));
|
|
132
|
+
return dedupedErrors;
|
|
133
|
+
};
|
|
130
134
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
131
135
|
className: DEFAULT_CLASS
|
|
132
136
|
}, title && !hide_title && /*#__PURE__*/_react.default.createElement(_copReactComponents.LargeHeading, {
|
|
133
137
|
key: "heading"
|
|
134
138
|
}, title), errors && errors.length > 0 && /*#__PURE__*/_react.default.createElement(_copReactComponents.ErrorSummary, {
|
|
135
|
-
errors:
|
|
139
|
+
errors: getDedupedErrors()
|
|
136
140
|
}), type === _models.FormTypes.TASK_CYA && pages.length > 0 && sections && sections.map(section => section.tasks.filter(task => task.state !== _models.TaskStates.TYPES.SKIPPED).map((task, taskIndex) => {
|
|
137
141
|
const filterPages = pages.filter(page => task.pages.some(p => p === page.name));
|
|
138
142
|
return /*#__PURE__*/_react.default.createElement(_react.Fragment, {
|
|
@@ -129,6 +129,17 @@ const InternalFormRenderer = _ref2 => {
|
|
|
129
129
|
validate
|
|
130
130
|
} = (0, _hooks.useValidation)();
|
|
131
131
|
|
|
132
|
+
// Set focus to header for accessibility, Screen reader to anounce the page heading
|
|
133
|
+
const setFocusToHeading = () => {
|
|
134
|
+
if (document) {
|
|
135
|
+
const header = Array.from(document.getElementsByTagName('h1')).pop();
|
|
136
|
+
if (header && document.activeElement !== header) {
|
|
137
|
+
header.setAttribute('tabIndex', '-1');
|
|
138
|
+
header.focus();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
132
143
|
// Need to set submission data when going back
|
|
133
144
|
window.onpopstate = e => {
|
|
134
145
|
var _formState$page;
|
|
@@ -192,6 +203,11 @@ const InternalFormRenderer = _ref2 => {
|
|
|
192
203
|
setHub(_utils.default.Hub.get(type, _hub, components, _objectSpread({}, data)));
|
|
193
204
|
}, [type, _hub, data, components, setHub]);
|
|
194
205
|
|
|
206
|
+
// Set focus to heading for accessibility
|
|
207
|
+
(0, _react.useEffect)(() => {
|
|
208
|
+
setFocusToHeading();
|
|
209
|
+
}, [formState]);
|
|
210
|
+
|
|
195
211
|
// Form state.
|
|
196
212
|
(0, _react.useEffect)(() => {
|
|
197
213
|
var _formState$page2;
|
|
@@ -72,6 +72,13 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
|
|
|
72
72
|
break;
|
|
73
73
|
case _models.PageAction.TYPES.COLLECTION_ADD:
|
|
74
74
|
_utils.default.CollectionPage.addEntry(action.collection, form.page.formData);
|
|
75
|
+
|
|
76
|
+
// We need to delete the collection entry fields from formData as it will be holding previous values.
|
|
77
|
+
if (form.page.formData["".concat(action.collection)] && form.page.formData["".concat(action.collection)].length > 0) {
|
|
78
|
+
const lastIndex = form.page.formData["".concat(action.collection)].length - 1;
|
|
79
|
+
const fieldsToDelete = Object.keys(form.page.formData["".concat(action.collection)][lastIndex]).filter(k => k in form.page.formData && k !== 'id');
|
|
80
|
+
fieldsToDelete.forEach(i => delete form.page.formData[i]);
|
|
81
|
+
}
|
|
75
82
|
pageUpdate = () => _handlers.default.navigate(action, pageId, onPageChange);
|
|
76
83
|
break;
|
|
77
84
|
case _models.PageAction.TYPES.COLLECTION_DUPLICATE:
|
|
@@ -86,6 +93,35 @@ const onPageAction = (action, patch, patchLabel, hooks, data, formState, validat
|
|
|
86
93
|
if (action.recordRemoval) {
|
|
87
94
|
form.page.formData["".concat(action.collection.split('.').pop(), "LastRemoved")] = _objectSpread({}, removedEntry);
|
|
88
95
|
}
|
|
96
|
+
|
|
97
|
+
// The formData needs to be tidied up after removing the collection entry:
|
|
98
|
+
|
|
99
|
+
// 1. Retrieve the formData ID as it will be overwritten by a field with the same name in the collection entry.
|
|
100
|
+
const formDataId = form.page.formData.id;
|
|
101
|
+
|
|
102
|
+
// 2. Build the list of field names to delete and delete them from formData.
|
|
103
|
+
if (removedEntry !== null) {
|
|
104
|
+
const removedEntryFieldsToDelete = Object.keys(removedEntry).filter(k => k in form.page.formData);
|
|
105
|
+
removedEntryFieldsToDelete.forEach(i => delete form.page.formData[i]);
|
|
106
|
+
}
|
|
107
|
+
|
|
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.
|
|
112
|
+
Object.assign(form.page.formData, collectionArray[collectionArray.length - 1]);
|
|
113
|
+
|
|
114
|
+
// 5. Assign the formData ID back to the formData.
|
|
115
|
+
form.page.formData.id = formDataId;
|
|
116
|
+
|
|
117
|
+
// 6. If the collection array has entries set the correct ActiveId
|
|
118
|
+
// otherwise delete it as well as the collection array.
|
|
119
|
+
if (collectionArray.length > 0) {
|
|
120
|
+
form.page.formData["".concat(action.collection, "ActiveId")] = collectionArray[collectionArray.length - 1].id;
|
|
121
|
+
} else {
|
|
122
|
+
delete form.page.formData["".concat(action.collection, "ActiveId")];
|
|
123
|
+
delete form.page.formData[action.collection];
|
|
124
|
+
}
|
|
89
125
|
break;
|
|
90
126
|
default:
|
|
91
127
|
break;
|
|
@@ -380,6 +380,35 @@ 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 clean previous collection entry fields at top level of formData", () => {
|
|
384
|
+
const FORM_STATE = {
|
|
385
|
+
page: {
|
|
386
|
+
formData: {
|
|
387
|
+
'testCollection': [{
|
|
388
|
+
'flightNumber': '1'
|
|
389
|
+
}],
|
|
390
|
+
'flightNumber': '1'
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
const ACTION = {
|
|
395
|
+
type: _models.PageAction.TYPES.COLLECTION_ADD,
|
|
396
|
+
collection: 'testCollection'
|
|
397
|
+
};
|
|
398
|
+
const CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
|
|
399
|
+
action: ACTION,
|
|
400
|
+
formState: FORM_STATE
|
|
401
|
+
});
|
|
402
|
+
_onPageAction.default.apply(void 0, Object.values(CUSTOM_ARGS));
|
|
403
|
+
preActionChecks();
|
|
404
|
+
expect(FORM_STATE.page.formData["".concat(ACTION.collection, "ActiveId")]).toBeDefined();
|
|
405
|
+
expect(FORM_STATE.page.formData.flightNumber).toBeUndefined();
|
|
406
|
+
postActionChecks(CUSTOM_ARGS);
|
|
407
|
+
|
|
408
|
+
// This action type also sets up a call to handlers.navigate
|
|
409
|
+
// that's called as part of the onSubmit hook's onSuccess callback.
|
|
410
|
+
expect(_handlers.default.navigateCalls).toEqual(1);
|
|
411
|
+
});
|
|
383
412
|
describe("should handle the ".concat(_models.PageAction.TYPES.COLLECTION_DUPLICATE, " Page Action type"), () => {
|
|
384
413
|
it("calling handlers.navigate if the duplication was successful", () => {
|
|
385
414
|
const FORM_STATE = {
|
|
@@ -533,6 +562,90 @@ describe('components.FormRenderer.onPageAction', () => {
|
|
|
533
562
|
expect(FORM_STATE.page.formData.testCollectionLastRemoved).toBeUndefined();
|
|
534
563
|
postActionChecks(CUSTOM_ARGS);
|
|
535
564
|
|
|
565
|
+
// This action type uses the default onPageChange.
|
|
566
|
+
expect(onPageChangeCalls).toEqual(1);
|
|
567
|
+
});
|
|
568
|
+
it("should clean collection fields at top level of formData when entries remain", () => {
|
|
569
|
+
// After removing the active entry, the collection data is assigned to the
|
|
570
|
+
// top level of formData and the activeId is reset.
|
|
571
|
+
const FORM_STATE = {
|
|
572
|
+
page: {
|
|
573
|
+
formData: {
|
|
574
|
+
testCollectionActiveId: 'removeMe',
|
|
575
|
+
testCollection: [{
|
|
576
|
+
id: 'leaveMeAlone',
|
|
577
|
+
flightNumber: '1'
|
|
578
|
+
}, {
|
|
579
|
+
id: 'removeMe',
|
|
580
|
+
flightNumber: '2'
|
|
581
|
+
}],
|
|
582
|
+
flightNumber: '2',
|
|
583
|
+
testCollectionLastRemoved: 'previousOne'
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
const ACTION = {
|
|
588
|
+
type: _models.PageAction.TYPES.COLLECTION_REMOVE,
|
|
589
|
+
collection: 'testCollection',
|
|
590
|
+
recordRemoval: true
|
|
591
|
+
};
|
|
592
|
+
const CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
|
|
593
|
+
action: ACTION,
|
|
594
|
+
formState: FORM_STATE
|
|
595
|
+
});
|
|
596
|
+
_onPageAction.default.apply(void 0, Object.values(CUSTOM_ARGS));
|
|
597
|
+
preActionChecks();
|
|
598
|
+
expect(FORM_STATE.page.formData.testCollection).toEqual([{
|
|
599
|
+
id: 'leaveMeAlone',
|
|
600
|
+
flightNumber: '1'
|
|
601
|
+
}]);
|
|
602
|
+
expect(FORM_STATE.page.formData.testCollectionLastRemoved).toEqual({
|
|
603
|
+
id: 'removeMe',
|
|
604
|
+
flightNumber: '2'
|
|
605
|
+
});
|
|
606
|
+
expect(FORM_STATE.page.formData.flightNumber).toEqual('1');
|
|
607
|
+
expect(FORM_STATE.page.formData.testCollectionActiveId).toEqual('leaveMeAlone');
|
|
608
|
+
postActionChecks(CUSTOM_ARGS);
|
|
609
|
+
|
|
610
|
+
// This action type uses the default onPageChange.
|
|
611
|
+
expect(onPageChangeCalls).toEqual(1);
|
|
612
|
+
});
|
|
613
|
+
it("should clean collection fields at top level of formData after last entry removed", () => {
|
|
614
|
+
// After removing the last active entry, the collection array and activeID are deleted,
|
|
615
|
+
// and the collection data is removed from top level of formData.
|
|
616
|
+
const FORM_STATE = {
|
|
617
|
+
page: {
|
|
618
|
+
formData: {
|
|
619
|
+
testCollectionActiveId: 'removeMe',
|
|
620
|
+
testCollection: [{
|
|
621
|
+
id: 'removeMe',
|
|
622
|
+
flightNumber: '1'
|
|
623
|
+
}],
|
|
624
|
+
flightNumber: '1',
|
|
625
|
+
testCollectionLastRemoved: 'previousOne'
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
const ACTION = {
|
|
630
|
+
type: _models.PageAction.TYPES.COLLECTION_REMOVE,
|
|
631
|
+
collection: 'testCollection',
|
|
632
|
+
recordRemoval: true
|
|
633
|
+
};
|
|
634
|
+
const CUSTOM_ARGS = _objectSpread(_objectSpread({}, ARGS), {}, {
|
|
635
|
+
action: ACTION,
|
|
636
|
+
formState: FORM_STATE
|
|
637
|
+
});
|
|
638
|
+
_onPageAction.default.apply(void 0, Object.values(CUSTOM_ARGS));
|
|
639
|
+
preActionChecks();
|
|
640
|
+
expect(FORM_STATE.page.formData.testCollection).toBeUndefined();
|
|
641
|
+
expect(FORM_STATE.page.formData.testCollectionLastRemoved).toEqual({
|
|
642
|
+
id: 'removeMe',
|
|
643
|
+
flightNumber: '1'
|
|
644
|
+
});
|
|
645
|
+
expect(FORM_STATE.page.formData.flightNumber).toBeUndefined();
|
|
646
|
+
expect(FORM_STATE.page.formData.testCollectionActiveId).toBeUndefined();
|
|
647
|
+
postActionChecks(CUSTOM_ARGS);
|
|
648
|
+
|
|
536
649
|
// This action type uses the default onPageChange.
|
|
537
650
|
expect(onPageChangeCalls).toEqual(1);
|
|
538
651
|
});
|
|
@@ -18,6 +18,7 @@ var _validateRegex = _interopRequireDefault(require("./validateRegex"));
|
|
|
18
18
|
var _validateRequired = _interopRequireDefault(require("./validateRequired"));
|
|
19
19
|
var _validateTextArea = _interopRequireDefault(require("./validateTextArea"));
|
|
20
20
|
var _validateTime = _interopRequireDefault(require("./validateTime"));
|
|
21
|
+
var _canOverrideFieldRequired = _interopRequireDefault(require("../canOverrideFieldRequired"));
|
|
21
22
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
23
|
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; }
|
|
23
24
|
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; }
|
|
@@ -47,7 +48,9 @@ const validateComponent = (component, outerData, formData) => {
|
|
|
47
48
|
value = (0, _getCollectionPageData.default)(component.collectionName, fd);
|
|
48
49
|
}
|
|
49
50
|
if (component.required) {
|
|
50
|
-
|
|
51
|
+
if (!component.requiredOverrideConditions || !(0, _canOverrideFieldRequired.default)(component.requiredOverrideConditions, fd)) {
|
|
52
|
+
error = (0, _validateRequired.default)(value, component.label, component.custom_errors);
|
|
53
|
+
}
|
|
51
54
|
}
|
|
52
55
|
if (!error) {
|
|
53
56
|
switch (component.type) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Check to see if the field required status can be overridden.
|
|
9
|
+
* All conditions need to be met in order for the override to be permitted.
|
|
10
|
+
* @param {array} requiredOverrideConditions The conditions to evaluate.
|
|
11
|
+
* @param {object} data The form data to check.
|
|
12
|
+
* @returns true or false.
|
|
13
|
+
*/
|
|
14
|
+
const canOverrideFieldRequired = (requiredOverrideConditions, data) => {
|
|
15
|
+
const conditionsNotMet = requiredOverrideConditions.filter(condition => data[condition.name] !== condition.value);
|
|
16
|
+
return conditionsNotMet.length === 0;
|
|
17
|
+
};
|
|
18
|
+
var _default = exports.default = canOverrideFieldRequired;
|