@ukhomeoffice/cop-react-form-renderer 6.13.2 → 6.14.0
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/README.md +3 -0
- package/dist/components/CollectionSummary/CollectionSummary.js +6 -2
- package/dist/components/CollectionSummary/RenderListView.js +16 -4
- package/dist/components/CollectionSummary/RenderListView.scss +4 -0
- package/dist/components/CollectionSummary/RenderListView.test.js +35 -2
- package/dist/components/CollectionSummary/SummaryCard.js +9 -15
- package/dist/components/CollectionSummary/SummaryCardButtons.js +76 -0
- package/dist/components/CollectionSummary/SummaryCardButtons.test.js +83 -0
- package/dist/components/FormComponent/Collection.js +5 -3
- package/dist/components/FormPage/FormPage.js +42 -8
- package/dist/components/FormPage/FormPage.test.js +61 -0
- package/dist/components/FormRenderer/FormRenderer.js +11 -5
- package/dist/utils/CheckYourAnswers/getCYARowsForCollectionPage.js +3 -0
- package/dist/utils/CheckYourAnswers/getCYARowsForContainer.js +3 -1
- package/dist/utils/CheckYourAnswers/getCYARowsForContainer.test.js +42 -0
- package/dist/utils/Component/getComponent.js +6 -2
- package/dist/utils/Component/getComponentTests/getComponent.nested.test.js +52 -0
- package/dist/utils/Data/getOptions.js +37 -3
- package/dist/utils/Data/getOptions.test.js +48 -0
- package/dist/utils/FormPage/getFormPage.js +7 -0
- package/dist/utils/FormPage/useComponent.js +11 -0
- package/dist/utils/FormPage/useComponent.test.js +36 -0
- package/dist/utils/Validate/validateEmail.js +2 -1
- package/dist/utils/Validate/validateEmail.test.js +3 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -45,6 +45,9 @@ Runs Storybook on [http://localhost:6007](http://localhost:6007), which showcase
|
|
|
45
45
|
|
|
46
46
|
Builds Storybook for production to the `storybook-static` folder.
|
|
47
47
|
|
|
48
|
+
### `npm run yalc-publish`
|
|
49
|
+
This will compile the code with source maps enabled and publish it to the local yalc repository in `~/.yalc` Any linked repositories will be automatically updated.
|
|
50
|
+
|
|
48
51
|
# Using the Service
|
|
49
52
|
## Versioning
|
|
50
53
|
For versioning this project uses SemVer.
|
|
@@ -246,7 +246,9 @@ var CollectionSummary = function CollectionSummary(_ref) {
|
|
|
246
246
|
id: "".concat(config.fieldId, ".addButton"),
|
|
247
247
|
action: {
|
|
248
248
|
collection: config.collectionName,
|
|
249
|
-
label: config.addButton.label || DEFAULT_ADD_BUTTON_LABEL,
|
|
249
|
+
label: _utils.default.interpolateString(config.addButton.label || DEFAULT_ADD_BUTTON_LABEL, {
|
|
250
|
+
documents: data
|
|
251
|
+
}),
|
|
250
252
|
page: config.addButton.page,
|
|
251
253
|
type: _PageAction.PageActionTypes.COLLECTION_ADD,
|
|
252
254
|
classModifiers: ['secondary']
|
|
@@ -303,7 +305,9 @@ var CollectionSummary = function CollectionSummary(_ref) {
|
|
|
303
305
|
id: "".concat(config.fieldId, ".addButton"),
|
|
304
306
|
action: {
|
|
305
307
|
collection: config.collectionName,
|
|
306
|
-
label: config.addButton.label || DEFAULT_ADD_BUTTON_LABEL,
|
|
308
|
+
label: _utils.default.interpolateString(config.addButton.label || DEFAULT_ADD_BUTTON_LABEL, {
|
|
309
|
+
documents: data
|
|
310
|
+
}),
|
|
307
311
|
page: config.addButton.page,
|
|
308
312
|
type: _PageAction.PageActionTypes.COLLECTION_ADD,
|
|
309
313
|
classModifiers: ['secondary'],
|
|
@@ -29,7 +29,7 @@ var DEFAULT_DELETE_BUTTON_LABEL = exports.DEFAULT_DELETE_BUTTON_LABEL = 'Delete'
|
|
|
29
29
|
var DEFAULT_CHANGE_BUTTON_CLASS = exports.DEFAULT_CHANGE_BUTTON_CLASS = 'secondary';
|
|
30
30
|
var DEFAULT_DELETE_BUTTON_CLASS = exports.DEFAULT_DELETE_BUTTON_CLASS = 'warning';
|
|
31
31
|
var RenderListView = function RenderListView(_ref) {
|
|
32
|
-
var _config$changeAction, _config$deleteAction, _masterPage$childPage;
|
|
32
|
+
var _config$changeAction, _config$changeAction2, _config$changeAction3, _config$changeAction4, _config$deleteAction, _masterPage$childPage;
|
|
33
33
|
var id = _ref.id,
|
|
34
34
|
entryData = _ref.entryData,
|
|
35
35
|
config = _ref.config,
|
|
@@ -59,16 +59,28 @@ var RenderListView = function RenderListView(_ref) {
|
|
|
59
59
|
className: classes('actions')
|
|
60
60
|
}, /*#__PURE__*/_react.default.createElement("li", {
|
|
61
61
|
className: classes('action')
|
|
62
|
-
}, config.changeAction && typeof onFullEdit === 'function' &&
|
|
62
|
+
}, config.changeAction && typeof onFullEdit === 'function' &&
|
|
63
|
+
// If passed a value for classModifiers, render a Button with those classModifiers applied -
|
|
64
|
+
// otherwise, revert to the default GOVUK List View styling of rendering a Link button instead:
|
|
65
|
+
config.changeAction.classModifiers ? /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
|
|
66
|
+
id: "".concat(id, ".changeButton"),
|
|
67
|
+
onClick: function onClick() {
|
|
68
|
+
return onFullEdit(config.changeAction.page, entryData.id);
|
|
69
|
+
},
|
|
70
|
+
classModifiers: config.changeAction.classModifiers,
|
|
71
|
+
"aria-label": _copReactComponents.Utils.interpolateString("".concat(((_config$changeAction = config.changeAction) === null || _config$changeAction === void 0 ? void 0 : _config$changeAction.label) || DEFAULT_CHANGE_BUTTON_LABEL, " ").concat(config.title || DEFAULT_TITLE), _objectSpread(_objectSpread({}, entryData), {}, {
|
|
72
|
+
index: entryData.index + 1
|
|
73
|
+
}))
|
|
74
|
+
}, ((_config$changeAction2 = config.changeAction) === null || _config$changeAction2 === void 0 ? void 0 : _config$changeAction2.label) || DEFAULT_CHANGE_BUTTON_LABEL) : /*#__PURE__*/_react.default.createElement("button", {
|
|
63
75
|
type: "button",
|
|
64
76
|
className: "govuk-link govuk-link-button",
|
|
65
77
|
onClick: function onClick() {
|
|
66
78
|
return onFullEdit(config.changeAction.page, entryData.id);
|
|
67
79
|
},
|
|
68
|
-
"aria-label": _copReactComponents.Utils.interpolateString("".concat(((_config$
|
|
80
|
+
"aria-label": _copReactComponents.Utils.interpolateString("".concat(((_config$changeAction3 = config.changeAction) === null || _config$changeAction3 === void 0 ? void 0 : _config$changeAction3.label) || DEFAULT_CHANGE_BUTTON_LABEL, " ").concat(config.title || DEFAULT_TITLE), _objectSpread(_objectSpread({}, entryData), {}, {
|
|
69
81
|
index: entryData.index + 1
|
|
70
82
|
}))
|
|
71
|
-
}, config.changeAction.label || DEFAULT_CHANGE_BUTTON_LABEL)), /*#__PURE__*/_react.default.createElement("li", {
|
|
83
|
+
}, ((_config$changeAction4 = config.changeAction) === null || _config$changeAction4 === void 0 ? void 0 : _config$changeAction4.label) || DEFAULT_CHANGE_BUTTON_LABEL)), /*#__PURE__*/_react.default.createElement("li", {
|
|
72
84
|
className: classes('action')
|
|
73
85
|
}, config.deleteAction && typeof onDelete === 'function' && /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
|
|
74
86
|
id: "".concat(id, ".deleteButton"),
|
|
@@ -224,7 +224,7 @@ describe('components.CollectionSummary.RenderListView', function () {
|
|
|
224
224
|
onDeleteArgs = [];
|
|
225
225
|
onDeleteCalls = 0;
|
|
226
226
|
});
|
|
227
|
-
it('should render and handle action buttons correctly', function () {
|
|
227
|
+
it('should render and handle action buttons correctly, including a GOVUK default Change Link when no classModifiers are passed in', function () {
|
|
228
228
|
var _renderWithValidation4 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_RenderListView.default, {
|
|
229
229
|
id: ID,
|
|
230
230
|
entryData: ENTRY,
|
|
@@ -238,7 +238,7 @@ describe('components.CollectionSummary.RenderListView', function () {
|
|
|
238
238
|
var listViewDiv = container.querySelector('.govuk-summary-card');
|
|
239
239
|
expect(listViewDiv).not.toBeNull();
|
|
240
240
|
|
|
241
|
-
// Check for change
|
|
241
|
+
// Check for change link
|
|
242
242
|
var changeLink = listViewDiv.querySelector('.govuk-link');
|
|
243
243
|
expect(changeLink.textContent).toEqual(CONFIG.changeAction.label);
|
|
244
244
|
expect(changeLink.getAttribute('aria-label')).toEqual("".concat(CONFIG.changeAction.label, " ").concat(CONFIG.title));
|
|
@@ -257,5 +257,38 @@ describe('components.CollectionSummary.RenderListView', function () {
|
|
|
257
257
|
expect(onDeleteCalls).toEqual(1);
|
|
258
258
|
expect(onDeleteArgs[0]).toEqual(ENTRY);
|
|
259
259
|
});
|
|
260
|
+
it('should render and handle an Update button instead of the GOVUK default Change link when classModifiers are passed in', function () {
|
|
261
|
+
var CONFIG_WITH_BUTTON = _objectSpread(_objectSpread({}, CONFIG), {}, {
|
|
262
|
+
changeAction: {
|
|
263
|
+
label: 'Update label',
|
|
264
|
+
page: 'testPage',
|
|
265
|
+
classModifiers: 'primary'
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
var _renderWithValidation5 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_RenderListView.default, {
|
|
269
|
+
id: ID,
|
|
270
|
+
entryData: ENTRY,
|
|
271
|
+
config: CONFIG_WITH_BUTTON,
|
|
272
|
+
onFullEdit: ON_CHANGE,
|
|
273
|
+
onDelete: ON_DELETE,
|
|
274
|
+
masterPage: MASTER_PAGE,
|
|
275
|
+
getComponentRow: getComponentRow
|
|
276
|
+
})),
|
|
277
|
+
container = _renderWithValidation5.container;
|
|
278
|
+
var listViewDiv = container.querySelector('.govuk-summary-card');
|
|
279
|
+
expect(listViewDiv).not.toBeNull();
|
|
280
|
+
|
|
281
|
+
// Check for update button
|
|
282
|
+
var changeButton = listViewDiv.querySelector('.hods-button');
|
|
283
|
+
expect(changeButton.classList).toContain('hods-button--primary');
|
|
284
|
+
expect(changeButton.textContent).toEqual(CONFIG_WITH_BUTTON.changeAction.label);
|
|
285
|
+
expect(changeButton.getAttribute('aria-label')).toEqual("".concat(CONFIG_WITH_BUTTON.changeAction.label, " ").concat(CONFIG_WITH_BUTTON.title));
|
|
286
|
+
_react.fireEvent.click(changeButton, {});
|
|
287
|
+
expect(onChangeCalls).toEqual(1);
|
|
288
|
+
expect(onChangeArgs[0]).toMatchObject({
|
|
289
|
+
page: CONFIG_WITH_BUTTON.changeAction.page,
|
|
290
|
+
id: ENTRY.id
|
|
291
|
+
});
|
|
292
|
+
});
|
|
260
293
|
});
|
|
261
294
|
});
|
|
@@ -15,6 +15,7 @@ var _FormPage = _interopRequireDefault(require("../FormPage"));
|
|
|
15
15
|
var _BannerStrip = _interopRequireDefault(require("./BannerStrip"));
|
|
16
16
|
var _RenderListView = _interopRequireDefault(require("./RenderListView"));
|
|
17
17
|
var _SummaryCardDetails = _interopRequireDefault(require("./SummaryCardDetails"));
|
|
18
|
+
var _SummaryCardButtons = require("./SummaryCardButtons");
|
|
18
19
|
require("./SummaryCard.scss");
|
|
19
20
|
var _excluded = ["isDuplicate"];
|
|
20
21
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
|
|
@@ -47,7 +48,7 @@ var DEFAULT_DUPLICATE_BUTTON_LABEL = exports.DEFAULT_DUPLICATE_BUTTON_LABEL = 'D
|
|
|
47
48
|
var DEFAULT_DETAILS_TITLE = exports.DEFAULT_DETAILS_TITLE = 'Full details';
|
|
48
49
|
var DEFAULT_CHANGE_BUTTON_CLASS = exports.DEFAULT_CHANGE_BUTTON_CLASS = 'secondary';
|
|
49
50
|
var SummaryCard = function SummaryCard(_ref) {
|
|
50
|
-
var _config$
|
|
51
|
+
var _config$deleteAction, _config$deleteAction2, _config$duplicateActi, _config$duplicateActi2, _config$fullDetails2;
|
|
51
52
|
var id = _ref.id,
|
|
52
53
|
entryData = _ref.entryData,
|
|
53
54
|
config = _ref.config,
|
|
@@ -191,18 +192,16 @@ var SummaryCard = function SummaryCard(_ref) {
|
|
|
191
192
|
disabled: quickEdit,
|
|
192
193
|
name: "QuickEdit",
|
|
193
194
|
autoFocus: config.focusOn
|
|
194
|
-
}, DEFAULT_EDIT_LABEL), config.changeAction && typeof onFullEdit === 'function' && /*#__PURE__*/_react.default.createElement(
|
|
195
|
+
}, DEFAULT_EDIT_LABEL), config.changeAction && typeof onFullEdit === 'function' && /*#__PURE__*/_react.default.createElement(_SummaryCardButtons.SummaryCardButtons, {
|
|
195
196
|
id: "".concat(id, ".changeButton"),
|
|
196
197
|
"data-testid": "change-button",
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
classModifiers: ((_config$changeAction2 = config.changeAction) === null || _config$changeAction2 === void 0 ? void 0 : _config$changeAction2.classModifiers) || DEFAULT_CHANGE_BUTTON_CLASS,
|
|
202
|
-
"aria-label": _utils.default.FormPage.getConditionalText((_config$changeAction3 = config.changeAction) === null || _config$changeAction3 === void 0 ? void 0 : _config$changeAction3.aria_label, entryData),
|
|
198
|
+
actions: config.changeAction,
|
|
199
|
+
onFullEdit: onFullEdit,
|
|
200
|
+
entryData: entryData,
|
|
201
|
+
formData: formData,
|
|
203
202
|
name: "Change",
|
|
204
203
|
autoFocus: config.focusOn
|
|
205
|
-
}
|
|
204
|
+
}), config.deleteAction && typeof onDelete === 'function' && /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
|
|
206
205
|
id: "".concat(id, ".deleteButton"),
|
|
207
206
|
onClick: function onClick() {
|
|
208
207
|
return onDelete(entryData);
|
|
@@ -250,12 +249,7 @@ SummaryCard.propTypes = {
|
|
|
250
249
|
banners: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.shape({})])),
|
|
251
250
|
title: _propTypes.default.string,
|
|
252
251
|
details: _propTypes.default.string,
|
|
253
|
-
changeAction: _propTypes.default.shape(
|
|
254
|
-
label: _propTypes.default.string,
|
|
255
|
-
page: _propTypes.default.string.isRequired,
|
|
256
|
-
classModifiers: _propTypes.default.string,
|
|
257
|
-
aria_label: _propTypes.default.string
|
|
258
|
-
}),
|
|
252
|
+
changeAction: _propTypes.default.oneOfType([_propTypes.default.shape(_SummaryCardButtons.actionShape), _propTypes.default.arrayOf(_propTypes.default.shape(_SummaryCardButtons.actionShape))]),
|
|
259
253
|
deleteAction: _propTypes.default.shape({
|
|
260
254
|
label: _propTypes.default.string,
|
|
261
255
|
aria_label: _propTypes.default.string
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.actionShape = exports.SummaryCardButtons = void 0;
|
|
7
|
+
var _react = _interopRequireDefault(require("react"));
|
|
8
|
+
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
9
|
+
var _copReactComponents = require("@ukhomeoffice/cop-react-components");
|
|
10
|
+
var _SummaryCard = require("./SummaryCard");
|
|
11
|
+
var _utils = _interopRequireDefault(require("../../utils"));
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
var actionShape = exports.actionShape = {
|
|
14
|
+
label: _propTypes.default.string,
|
|
15
|
+
page: _propTypes.default.string.isRequired,
|
|
16
|
+
classModifiers: _propTypes.default.string,
|
|
17
|
+
aria_label: _propTypes.default.string
|
|
18
|
+
};
|
|
19
|
+
var SummaryCardButtons = exports.SummaryCardButtons = function SummaryCardButtons(_ref) {
|
|
20
|
+
var id = _ref.id,
|
|
21
|
+
testId = _ref["data-testid"],
|
|
22
|
+
actions = _ref.actions,
|
|
23
|
+
onFullEdit = _ref.onFullEdit,
|
|
24
|
+
entryData = _ref.entryData,
|
|
25
|
+
formData = _ref.formData,
|
|
26
|
+
name = _ref.name,
|
|
27
|
+
autoFocus = _ref.autoFocus;
|
|
28
|
+
if (!Array.isArray(actions)) {
|
|
29
|
+
return /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
|
|
30
|
+
id: id,
|
|
31
|
+
"data-testid": testId,
|
|
32
|
+
onClick: function onClick() {
|
|
33
|
+
return onFullEdit(actions === null || actions === void 0 ? void 0 : actions.page, entryData.id);
|
|
34
|
+
},
|
|
35
|
+
classModifiers: (actions === null || actions === void 0 ? void 0 : actions.classModifiers) || _SummaryCard.DEFAULT_CHANGE_BUTTON_CLASS,
|
|
36
|
+
"aria-label": _utils.default.FormPage.getConditionalText(actions === null || actions === void 0 ? void 0 : actions.aria_label, entryData),
|
|
37
|
+
name: name,
|
|
38
|
+
autoFocus: autoFocus
|
|
39
|
+
}, (actions === null || actions === void 0 ? void 0 : actions.label) || _SummaryCard.DEFAULT_CHANGE_BUTTON_LABEL);
|
|
40
|
+
}
|
|
41
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, actions.filter(function (a) {
|
|
42
|
+
return _utils.default.Component.show(a, formData);
|
|
43
|
+
}).map(function (action) {
|
|
44
|
+
return /*#__PURE__*/_react.default.createElement(_copReactComponents.Button, {
|
|
45
|
+
id: id,
|
|
46
|
+
key: id,
|
|
47
|
+
"data-testid": testId,
|
|
48
|
+
onClick: function onClick() {
|
|
49
|
+
return onFullEdit(action === null || action === void 0 ? void 0 : action.page, entryData.id);
|
|
50
|
+
},
|
|
51
|
+
classModifiers: (action === null || action === void 0 ? void 0 : action.classModifiers) || _SummaryCard.DEFAULT_CHANGE_BUTTON_CLASS,
|
|
52
|
+
"aria-label": _utils.default.FormPage.getConditionalText(action === null || action === void 0 ? void 0 : action.aria_label, entryData),
|
|
53
|
+
name: name,
|
|
54
|
+
autoFocus: autoFocus
|
|
55
|
+
}, (action === null || action === void 0 ? void 0 : action.label) || _SummaryCard.DEFAULT_CHANGE_BUTTON_LABEL);
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
58
|
+
SummaryCardButtons.propTypes = {
|
|
59
|
+
id: _propTypes.default.string.isRequired,
|
|
60
|
+
"data-testid": _propTypes.default.string,
|
|
61
|
+
entryData: _propTypes.default.shape({
|
|
62
|
+
id: _propTypes.default.string.isRequired,
|
|
63
|
+
index: _propTypes.default.number.isRequired
|
|
64
|
+
}).isRequired,
|
|
65
|
+
onFullEdit: _propTypes.default.func,
|
|
66
|
+
autoFocus: _propTypes.default.bool,
|
|
67
|
+
name: _propTypes.default.string,
|
|
68
|
+
actions: _propTypes.default.oneOfType([_propTypes.default.shape(actionShape), _propTypes.default.arrayOf(_propTypes.default.shape(actionShape))]).isRequired,
|
|
69
|
+
formData: _propTypes.default.shape({}).isRequired
|
|
70
|
+
};
|
|
71
|
+
SummaryCardButtons.defaultProps = {
|
|
72
|
+
onFullEdit: null,
|
|
73
|
+
"data-testid": null,
|
|
74
|
+
autoFocus: null,
|
|
75
|
+
name: null
|
|
76
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
require("@testing-library/jest-dom/extend-expect");
|
|
4
|
+
var _react = _interopRequireDefault(require("react"));
|
|
5
|
+
var _setupTests = require("../../setupTests");
|
|
6
|
+
var _SummaryCardButtons = require("./SummaryCardButtons");
|
|
7
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
8
|
+
// Global imports.
|
|
9
|
+
|
|
10
|
+
// Local imports.
|
|
11
|
+
|
|
12
|
+
describe('components.CollectionSummary.SummaryCardButtons', function () {
|
|
13
|
+
var ID = 'summaryCardId';
|
|
14
|
+
var ENTRY = {
|
|
15
|
+
id: '001',
|
|
16
|
+
bannerText: 'A banner',
|
|
17
|
+
titleText: 'A title',
|
|
18
|
+
detailsText: 'Some details',
|
|
19
|
+
index: 0,
|
|
20
|
+
summaryText: 'Full details'
|
|
21
|
+
};
|
|
22
|
+
it('should correctly render a SummaryCardButtons component', function () {
|
|
23
|
+
var CONFIG = {
|
|
24
|
+
changeAction: {
|
|
25
|
+
label: 'Change label',
|
|
26
|
+
page: 'testPage',
|
|
27
|
+
aria_label: [{
|
|
28
|
+
text: 'testText'
|
|
29
|
+
}]
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var _renderWithValidation = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardButtons.SummaryCardButtons, {
|
|
33
|
+
id: "".concat(ID, ".changeButton"),
|
|
34
|
+
"data-testid": "change-button",
|
|
35
|
+
actions: CONFIG.changeAction,
|
|
36
|
+
name: "Change",
|
|
37
|
+
entryData: ENTRY,
|
|
38
|
+
formData: {}
|
|
39
|
+
})),
|
|
40
|
+
container = _renderWithValidation.container;
|
|
41
|
+
var changeButton = container.children[0];
|
|
42
|
+
expect(changeButton.name).toEqual('Change');
|
|
43
|
+
expect(changeButton.tagName).toEqual('BUTTON');
|
|
44
|
+
expect(changeButton.textContent).toEqual(CONFIG.changeAction.label);
|
|
45
|
+
expect(changeButton.getAttribute('aria-label')).toEqual(CONFIG.changeAction.aria_label[0].text);
|
|
46
|
+
});
|
|
47
|
+
it('should correctly render a SummaryCardButtons component when array of buttons', function () {
|
|
48
|
+
var CONFIG = {
|
|
49
|
+
changeAction: [{
|
|
50
|
+
label: 'Change label',
|
|
51
|
+
page: 'testPage',
|
|
52
|
+
aria_label: [{
|
|
53
|
+
text: 'testText'
|
|
54
|
+
}]
|
|
55
|
+
}, {
|
|
56
|
+
label: 'Invalid label',
|
|
57
|
+
page: 'testPage',
|
|
58
|
+
aria_label: [{
|
|
59
|
+
text: 'testText'
|
|
60
|
+
}],
|
|
61
|
+
show_when: {
|
|
62
|
+
field: "test",
|
|
63
|
+
op: "=",
|
|
64
|
+
value: true
|
|
65
|
+
}
|
|
66
|
+
}]
|
|
67
|
+
};
|
|
68
|
+
var _renderWithValidation2 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react.default.createElement(_SummaryCardButtons.SummaryCardButtons, {
|
|
69
|
+
id: "".concat(ID, ".changeButton"),
|
|
70
|
+
"data-testid": "change-button",
|
|
71
|
+
actions: CONFIG.changeAction,
|
|
72
|
+
name: "Change",
|
|
73
|
+
entryData: ENTRY,
|
|
74
|
+
formData: {}
|
|
75
|
+
})),
|
|
76
|
+
container = _renderWithValidation2.container;
|
|
77
|
+
var changeButton = container.children[0];
|
|
78
|
+
expect(changeButton.name).toEqual('Change');
|
|
79
|
+
expect(changeButton.tagName).toEqual('BUTTON');
|
|
80
|
+
expect(changeButton.textContent).toEqual(CONFIG.changeAction[0].label);
|
|
81
|
+
expect(changeButton.getAttribute('aria-label')).toEqual(CONFIG.changeAction[0].aria_label[0].text);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -118,11 +118,13 @@ var Collection = function Collection(_ref) {
|
|
|
118
118
|
(0, _react.useEffect)(function () {
|
|
119
119
|
if (config.focusOnAdd && Array.isArray(value) && value.length) {
|
|
120
120
|
if (value.length > config.minimumEntries || !config.minimumEntries) {
|
|
121
|
-
var _value2, _document$getElementB
|
|
121
|
+
var _value2, _document$getElementB;
|
|
122
122
|
var containerId = (_value2 = value[value.length - 1]) === null || _value2 === void 0 ? void 0 : _value2.id;
|
|
123
123
|
var container = (_document$getElementB = document.getElementById(containerId)) === null || _document$getElementB === void 0 ? void 0 : _document$getElementB.childNodes[0];
|
|
124
124
|
var focusable = container === null || container === void 0 ? void 0 : container.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
125
|
-
(
|
|
125
|
+
if (focusable && focusable.length > 0) {
|
|
126
|
+
focusable[0].focus();
|
|
127
|
+
}
|
|
126
128
|
}
|
|
127
129
|
}
|
|
128
130
|
}, [value.length, config.focusOnAdd, config.minimumEntries]);
|
|
@@ -140,7 +142,7 @@ var Collection = function Collection(_ref) {
|
|
|
140
142
|
id: config.id,
|
|
141
143
|
required: config.required,
|
|
142
144
|
className: classes('title')
|
|
143
|
-
}, config.label), value && value.map(function (item, index) {
|
|
145
|
+
}, config.label), Array.isArray(value) && value.map(function (item, index) {
|
|
144
146
|
var _config$removeLocatio;
|
|
145
147
|
var fullPath = "".concat(config.full_path || config.fieldId, "[").concat(index, "]");
|
|
146
148
|
var labelCount = (config.countOffset || 0) + index + 1;
|
|
@@ -31,7 +31,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } // Global
|
|
|
31
31
|
// Styles
|
|
32
32
|
var DEFAULT_CLASS = exports.DEFAULT_CLASS = 'hods-form__page';
|
|
33
33
|
var FormPage = function FormPage(_ref) {
|
|
34
|
-
var _formPage$actions, _page$actions;
|
|
34
|
+
var _page$customValidatio, _formPage$actions, _page$actions;
|
|
35
35
|
var page = _ref.page,
|
|
36
36
|
pages = _ref.pages,
|
|
37
37
|
_onAction = _ref.onAction,
|
|
@@ -42,17 +42,33 @@ var FormPage = function FormPage(_ref) {
|
|
|
42
42
|
classBlock = _ref.classBlock,
|
|
43
43
|
classModifiers = _ref.classModifiers,
|
|
44
44
|
className = _ref.className,
|
|
45
|
-
submitting = _ref.submitting
|
|
45
|
+
submitting = _ref.submitting,
|
|
46
|
+
pageId = _ref.pageId,
|
|
47
|
+
fromTarget = _ref.fromTarget;
|
|
46
48
|
var _useState = (0, _react.useState)({}),
|
|
47
49
|
_useState2 = _slicedToArray(_useState, 2),
|
|
48
50
|
patch = _useState2[0],
|
|
49
51
|
setPatch = _useState2[1];
|
|
50
52
|
var _useValidation = (0, _hooks.useValidation)(),
|
|
51
|
-
errors = _useValidation.errors
|
|
53
|
+
errors = _useValidation.errors,
|
|
54
|
+
validate = _useValidation.validate;
|
|
52
55
|
var _useState3 = (0, _react.useState)({}),
|
|
53
56
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
54
57
|
patchLabel = _useState4[0],
|
|
55
58
|
setPatchLabel = _useState4[1];
|
|
59
|
+
var _useState5 = (0, _react.useState)(false),
|
|
60
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
61
|
+
isPageRendered = _useState6[0],
|
|
62
|
+
setIsPageRendered = _useState6[1];
|
|
63
|
+
var runOnLoad = Array.isArray(page === null || page === void 0 ? void 0 : page.customValidation) && page.customValidation.length > 0 ? ((_page$customValidatio = page.customValidation[0]) === null || _page$customValidatio === void 0 || (_page$customValidatio = _page$customValidatio.runWhen) === null || _page$customValidatio === void 0 ? void 0 : _page$customValidatio.onLoad) === true : false;
|
|
64
|
+
(0, _react.useEffect)(function () {
|
|
65
|
+
setIsPageRendered(true);
|
|
66
|
+
}, []);
|
|
67
|
+
(0, _react.useEffect)(function () {
|
|
68
|
+
if (isPageRendered && fromTarget && runOnLoad) {
|
|
69
|
+
validate.page(page);
|
|
70
|
+
}
|
|
71
|
+
}, [pageId, isPageRendered]);
|
|
56
72
|
|
|
57
73
|
/**
|
|
58
74
|
* Handle the state of the data directly within the page.
|
|
@@ -121,11 +137,20 @@ var FormPage = function FormPage(_ref) {
|
|
|
121
137
|
submitting: submitting
|
|
122
138
|
});
|
|
123
139
|
});
|
|
140
|
+
var errorMessages = null;
|
|
141
|
+
if (runOnLoad && (errors === null || errors === void 0 ? void 0 : errors.length) > 0) {
|
|
142
|
+
var _page$customValidatio2;
|
|
143
|
+
errorMessages = [{
|
|
144
|
+
error: page === null || page === void 0 || (_page$customValidatio2 = page.customValidation) === null || _page$customValidatio2 === void 0 || (_page$customValidatio2 = _page$customValidatio2[0]) === null || _page$customValidatio2 === void 0 ? void 0 : _page$customValidatio2.message
|
|
145
|
+
}];
|
|
146
|
+
} else if ((errors === null || errors === void 0 ? void 0 : errors.length) > 0 && !runOnLoad) {
|
|
147
|
+
errorMessages = errors;
|
|
148
|
+
}
|
|
124
149
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
125
150
|
className: classes(),
|
|
126
151
|
key: page.id
|
|
127
|
-
},
|
|
128
|
-
errors:
|
|
152
|
+
}, errorMessages && /*#__PURE__*/_react.default.createElement(_copReactComponents.ErrorSummary, {
|
|
153
|
+
errors: errorMessages,
|
|
129
154
|
hashLink: hashLink
|
|
130
155
|
}), page.fieldset && /*#__PURE__*/_react.default.createElement("fieldset", {
|
|
131
156
|
className: "govuk-fieldset"
|
|
@@ -162,10 +187,18 @@ FormPage.propTypes = {
|
|
|
162
187
|
label: _propTypes.default.string,
|
|
163
188
|
required: _propTypes.default.bool,
|
|
164
189
|
actions: _propTypes.default.arrayOf(_propTypes.default.oneOfType([_propTypes.default.shape({}), _propTypes.default.string])),
|
|
165
|
-
formData: _propTypes.default.shape({}).isRequired
|
|
190
|
+
formData: _propTypes.default.shape({}).isRequired,
|
|
191
|
+
customValidation: _propTypes.default.arrayOf(_propTypes.default.shape({
|
|
192
|
+
runWhen: _propTypes.default.shape({
|
|
193
|
+
onLoad: _propTypes.default.bool
|
|
194
|
+
}).isRequired,
|
|
195
|
+
message: _propTypes.default.string.isRequired
|
|
196
|
+
}))
|
|
166
197
|
}).isRequired,
|
|
167
198
|
pages: _propTypes.default.arrayOf(_propTypes.default.shape({})),
|
|
168
|
-
submitting: _propTypes.default.bool
|
|
199
|
+
submitting: _propTypes.default.bool,
|
|
200
|
+
fromTarget: _propTypes.default.bool,
|
|
201
|
+
pageId: _propTypes.default.string.isRequired
|
|
169
202
|
};
|
|
170
203
|
FormPage.defaultProps = {
|
|
171
204
|
classBlock: DEFAULT_CLASS,
|
|
@@ -176,6 +209,7 @@ FormPage.defaultProps = {
|
|
|
176
209
|
onTopLevelChange: undefined,
|
|
177
210
|
onWrapperChange: undefined,
|
|
178
211
|
pages: [],
|
|
179
|
-
submitting: false
|
|
212
|
+
submitting: false,
|
|
213
|
+
fromTarget: false
|
|
180
214
|
};
|
|
181
215
|
var _default = exports.default = FormPage;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
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); }
|
|
4
4
|
var _react = require("@testing-library/react");
|
|
5
|
+
require("@testing-library/jest-dom");
|
|
5
6
|
var _react2 = _interopRequireDefault(require("react"));
|
|
6
7
|
var _models = require("../../models");
|
|
7
8
|
var _setupTests = require("../../setupTests");
|
|
@@ -53,6 +54,30 @@ describe('components.FormPage', function () {
|
|
|
53
54
|
text: VALUE
|
|
54
55
|
}
|
|
55
56
|
};
|
|
57
|
+
var PAGE_WITH_CUSTOM_VALIDATION = {
|
|
58
|
+
id: 'pageId',
|
|
59
|
+
title: 'Page with Custom Validation',
|
|
60
|
+
components: [TEXT],
|
|
61
|
+
actions: [_models.PageAction.TYPES.SUBMIT],
|
|
62
|
+
formData: {
|
|
63
|
+
text: VALUE
|
|
64
|
+
},
|
|
65
|
+
customValidation: [{
|
|
66
|
+
message: 'Custom validation failed',
|
|
67
|
+
runWhen: {
|
|
68
|
+
onLoad: true
|
|
69
|
+
}
|
|
70
|
+
}]
|
|
71
|
+
};
|
|
72
|
+
var PAGE_WITHOUT_CUSTOM_VALIDATION = {
|
|
73
|
+
id: 'pageId',
|
|
74
|
+
title: 'Page without Custom Validation',
|
|
75
|
+
components: [TEXT],
|
|
76
|
+
actions: [_models.PageAction.TYPES.SUBMIT],
|
|
77
|
+
formData: {
|
|
78
|
+
text: VALUE
|
|
79
|
+
}
|
|
80
|
+
};
|
|
56
81
|
var PAGE_WITH_BUTTON_ACTIONS = {
|
|
57
82
|
id: 'pageId',
|
|
58
83
|
// eslint-disable-next-line no-template-curly-in-string
|
|
@@ -328,5 +353,41 @@ describe('components.FormPage', function () {
|
|
|
328
353
|
}
|
|
329
354
|
}, _callee5);
|
|
330
355
|
})));
|
|
356
|
+
it('should not trigger custom validation when fromTarget is false', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee6() {
|
|
357
|
+
var _renderWithValidation6, queryByText;
|
|
358
|
+
return _regeneratorRuntime().wrap(function _callee6$(_context6) {
|
|
359
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
360
|
+
case 0:
|
|
361
|
+
_renderWithValidation6 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
362
|
+
page: PAGE_WITH_CUSTOM_VALIDATION,
|
|
363
|
+
fromTarget: false,
|
|
364
|
+
onAction: ON_ACTION
|
|
365
|
+
})), queryByText = _renderWithValidation6.queryByText;
|
|
366
|
+
_context6.next = 3;
|
|
367
|
+
return (0, _react.waitFor)(function () {
|
|
368
|
+
expect(queryByText('Custom validation failed')).toBeNull();
|
|
369
|
+
});
|
|
370
|
+
case 3:
|
|
371
|
+
case "end":
|
|
372
|
+
return _context6.stop();
|
|
373
|
+
}
|
|
374
|
+
}, _callee6);
|
|
375
|
+
})));
|
|
376
|
+
it('should not trigger custom validation if no customValidation is provided', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee7() {
|
|
377
|
+
return _regeneratorRuntime().wrap(function _callee7$(_context7) {
|
|
378
|
+
while (1) switch (_context7.prev = _context7.next) {
|
|
379
|
+
case 0:
|
|
380
|
+
(0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
381
|
+
page: PAGE_WITHOUT_CUSTOM_VALIDATION,
|
|
382
|
+
fromTarget: true,
|
|
383
|
+
onAction: ON_ACTION
|
|
384
|
+
}));
|
|
385
|
+
expect(ON_ACTION_CALLS.length).toEqual(0);
|
|
386
|
+
case 2:
|
|
387
|
+
case "end":
|
|
388
|
+
return _context7.stop();
|
|
389
|
+
}
|
|
390
|
+
}, _callee7);
|
|
391
|
+
})));
|
|
331
392
|
});
|
|
332
393
|
});
|
|
@@ -60,7 +60,8 @@ var FormRenderer = function FormRenderer(_ref) {
|
|
|
60
60
|
noChangeAction = _ref.noChangeAction,
|
|
61
61
|
newPageId = _ref.newPageId,
|
|
62
62
|
viewOnly = _ref.viewOnly,
|
|
63
|
-
hideBlankRows = _ref.hideBlankRows
|
|
63
|
+
hideBlankRows = _ref.hideBlankRows,
|
|
64
|
+
fromTarget = _ref.fromTarget;
|
|
64
65
|
return /*#__PURE__*/_react.default.createElement(_context.HooksContextProvider, {
|
|
65
66
|
overrides: hooks
|
|
66
67
|
}, /*#__PURE__*/_react.default.createElement(_context.ValidationContextProvider, null, /*#__PURE__*/_react.default.createElement(InternalFormRenderer, {
|
|
@@ -80,7 +81,8 @@ var FormRenderer = function FormRenderer(_ref) {
|
|
|
80
81
|
noChangeAction: noChangeAction,
|
|
81
82
|
newPageId: newPageId,
|
|
82
83
|
viewOnly: viewOnly,
|
|
83
|
-
hideBlankRows: hideBlankRows
|
|
84
|
+
hideBlankRows: hideBlankRows,
|
|
85
|
+
fromTarget: fromTarget
|
|
84
86
|
})));
|
|
85
87
|
};
|
|
86
88
|
var DEFAULT_CLASS = exports.DEFAULT_CLASS = 'hods-form';
|
|
@@ -102,7 +104,8 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
|
|
|
102
104
|
noChangeAction = _ref2.noChangeAction,
|
|
103
105
|
newPageId = _ref2.newPageId,
|
|
104
106
|
viewOnly = _ref2.viewOnly,
|
|
105
|
-
hideBlankRows = _ref2.hideBlankRows
|
|
107
|
+
hideBlankRows = _ref2.hideBlankRows,
|
|
108
|
+
fromTarget = _ref2.fromTarget;
|
|
106
109
|
// Set up the initial states.
|
|
107
110
|
var _useState = (0, _react.useState)({}),
|
|
108
111
|
_useState2 = _slicedToArray(_useState, 2),
|
|
@@ -364,7 +367,9 @@ var InternalFormRenderer = function InternalFormRenderer(_ref2) {
|
|
|
364
367
|
hashLink: hashLink,
|
|
365
368
|
classModifiers: formState.page.classModifiers,
|
|
366
369
|
className: formState.page.className,
|
|
367
|
-
submitting: submitting
|
|
370
|
+
submitting: submitting,
|
|
371
|
+
pageId: pageId,
|
|
372
|
+
fromTarget: fromTarget
|
|
368
373
|
}), formState.page && !formState.cya && formState.page.collection && /*#__PURE__*/_react.default.createElement(_CollectionPage.default, {
|
|
369
374
|
page: formState.page,
|
|
370
375
|
pages: pages,
|
|
@@ -407,7 +412,8 @@ var propTypes = {
|
|
|
407
412
|
/** See <a href="/?path=/docs/f-json--page#formtypes">FormTypes</a>. */
|
|
408
413
|
type: _propTypes.default.oneOf([_models.FormTypes.CYA, _models.FormTypes.FORM, _models.FormTypes.HUB, _models.FormTypes.TASK, _models.FormTypes.WIZARD, _models.FormTypes.TASK_CYA, _models.FormTypes.FORM_WITH_TASK]).isRequired,
|
|
409
414
|
viewOnly: _propTypes.default.bool,
|
|
410
|
-
hideBlankRows: _propTypes.default.bool
|
|
415
|
+
hideBlankRows: _propTypes.default.bool,
|
|
416
|
+
fromTarget: _propTypes.default.bool.isRequired
|
|
411
417
|
};
|
|
412
418
|
var defaultProps = {
|
|
413
419
|
classBlock: DEFAULT_CLASS,
|
|
@@ -215,6 +215,9 @@ var getCYARowsForCollectionPage = function getCYARowsForCollectionPage(page, onA
|
|
|
215
215
|
}
|
|
216
216
|
collectionData.forEach(function (item) {
|
|
217
217
|
var _page$collection2;
|
|
218
|
+
if (!item) {
|
|
219
|
+
return; // Skip this iteration if item is undefined
|
|
220
|
+
}
|
|
218
221
|
// Keep track of the active entry in each collection.
|
|
219
222
|
// This helps us make sure the right entry is being affected
|
|
220
223
|
// by change/remove links.
|
|
@@ -18,7 +18,9 @@ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _ty
|
|
|
18
18
|
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); } // Local imports
|
|
19
19
|
var getCYARowsForContainer = function getCYARowsForContainer(page, container, formData, onAction, fnOverride) {
|
|
20
20
|
if ((0, _showComponentCYA.default)(container, _objectSpread(_objectSpread({}, page.formData), formData))) {
|
|
21
|
-
var allComponents = (0, _elevateNestedComponents.default)(container.components
|
|
21
|
+
var allComponents = (0, _elevateNestedComponents.default)(container.components.filter(function (component) {
|
|
22
|
+
return component.show_on_cya === undefined || component.show_on_cya;
|
|
23
|
+
}), formData);
|
|
22
24
|
return allComponents.filter(function (c) {
|
|
23
25
|
return (0, _showComponentCYA.default)(c, _objectSpread(_objectSpread({}, page.formData), formData));
|
|
24
26
|
}).flatMap(function (component) {
|
|
@@ -151,6 +151,48 @@ describe('utils.CheckYourAnswers.getCYARowsForContainer', function () {
|
|
|
151
151
|
});
|
|
152
152
|
});
|
|
153
153
|
});
|
|
154
|
+
it('should not display nested input components when passed a value of false for "show_on_cya" at the parent component level', function () {
|
|
155
|
+
var FORM_DATA = {
|
|
156
|
+
container: {
|
|
157
|
+
topLevelInput: 'A',
|
|
158
|
+
nestedInput: 'Bravo'
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
var PAGE = {
|
|
162
|
+
id: 'page',
|
|
163
|
+
formData: FORM_DATA,
|
|
164
|
+
cya_link: {}
|
|
165
|
+
};
|
|
166
|
+
var NESTED_COMPONENT = {
|
|
167
|
+
type: 'text',
|
|
168
|
+
id: 'nestedInput',
|
|
169
|
+
fieldId: 'nestedInput',
|
|
170
|
+
label: 'Nested input',
|
|
171
|
+
value: 'Bravo'
|
|
172
|
+
};
|
|
173
|
+
var COMPONENT = {
|
|
174
|
+
type: 'checkboxes',
|
|
175
|
+
id: 'topLevelInput',
|
|
176
|
+
fieldId: 'topLevelInput',
|
|
177
|
+
label: 'Top level input',
|
|
178
|
+
show_on_cya: false,
|
|
179
|
+
options: [{
|
|
180
|
+
value: 'A',
|
|
181
|
+
label: 'Option Alpha',
|
|
182
|
+
nested: [NESTED_COMPONENT]
|
|
183
|
+
}]
|
|
184
|
+
};
|
|
185
|
+
var CONTAINER = {
|
|
186
|
+
id: 'container',
|
|
187
|
+
fieldId: 'container',
|
|
188
|
+
type: _models.ComponentTypes.CONTAINER,
|
|
189
|
+
components: [COMPONENT],
|
|
190
|
+
value: FORM_DATA
|
|
191
|
+
};
|
|
192
|
+
var ON_ACTION = function ON_ACTION() {};
|
|
193
|
+
var ROWS = (0, _getCYARowsForContainer.default)(PAGE, CONTAINER, FORM_DATA.container, ON_ACTION);
|
|
194
|
+
expect(ROWS.length).toEqual(0);
|
|
195
|
+
});
|
|
154
196
|
it('should get an appropriate row for a container with a single readonly text component inside a nested container', function () {
|
|
155
197
|
var FORM_DATA = {
|
|
156
198
|
container: {
|
|
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.getChildrenJsx = exports.default = void 0;
|
|
8
8
|
var _copReactComponents = require("@ukhomeoffice/cop-react-components");
|
|
9
9
|
var _react = _interopRequireDefault(require("react"));
|
|
10
|
+
var _Condition = _interopRequireDefault(require("../Condition"));
|
|
10
11
|
var _models = require("../../models");
|
|
11
12
|
var _Data = _interopRequireDefault(require("../Data"));
|
|
12
13
|
var _cleanAttributes = _interopRequireDefault(require("./cleanAttributes"));
|
|
@@ -265,11 +266,14 @@ var getChildJsx = function getChildJsx(parent, paramChild) {
|
|
|
265
266
|
* @param {*} childrenConfigs array of configurations for the child components
|
|
266
267
|
*/
|
|
267
268
|
var getChildrenJsx = exports.getChildrenJsx = function getChildrenJsx(parentConfig, childrenConfigs) {
|
|
268
|
-
|
|
269
|
+
var validChildren = childrenConfigs.filter(function (config) {
|
|
270
|
+
return !(config.show_when && !_Condition.default.meetsAll(config.show_when, parentConfig.formData));
|
|
271
|
+
}).map(function (config) {
|
|
269
272
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, {
|
|
270
273
|
key: config.id
|
|
271
274
|
}, getChildJsx(parentConfig, config));
|
|
272
|
-
})
|
|
275
|
+
});
|
|
276
|
+
return validChildren.length > 0 ? validChildren : null;
|
|
273
277
|
};
|
|
274
278
|
/**
|
|
275
279
|
* Get a renderable component, based on a configuration object.
|
|
@@ -180,4 +180,56 @@ describe('utils.Component.get', function () {
|
|
|
180
180
|
}
|
|
181
181
|
}, _callee4);
|
|
182
182
|
})));
|
|
183
|
+
it('should correctly show nested component with show_when', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee5() {
|
|
184
|
+
var FIELD_ID, VALUE, VisibleComponent, HiddenComponent, PARENT_CONFIG, _renderWithValidation4, container, child, label, input;
|
|
185
|
+
return _regeneratorRuntime().wrap(function _callee5$(_context5) {
|
|
186
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
187
|
+
case 0:
|
|
188
|
+
FIELD_ID = 'fieldId';
|
|
189
|
+
VALUE = 'nestedValue';
|
|
190
|
+
VisibleComponent = {
|
|
191
|
+
id: 'testId',
|
|
192
|
+
fieldId: FIELD_ID,
|
|
193
|
+
label: 'Test label',
|
|
194
|
+
type: 'text',
|
|
195
|
+
show_when: [{
|
|
196
|
+
field: FIELD_ID,
|
|
197
|
+
op: "=",
|
|
198
|
+
value: VALUE
|
|
199
|
+
}]
|
|
200
|
+
};
|
|
201
|
+
HiddenComponent = {
|
|
202
|
+
id: 'hiddenID',
|
|
203
|
+
fieldId: "hiddenFeild",
|
|
204
|
+
label: 'Hidden label',
|
|
205
|
+
type: 'text',
|
|
206
|
+
show_when: [{
|
|
207
|
+
field: FIELD_ID,
|
|
208
|
+
op: "!=",
|
|
209
|
+
value: VALUE
|
|
210
|
+
}]
|
|
211
|
+
};
|
|
212
|
+
PARENT_CONFIG = {
|
|
213
|
+
onChange: function onChange() {},
|
|
214
|
+
formData: _defineProperty({}, FIELD_ID, VALUE)
|
|
215
|
+
};
|
|
216
|
+
_renderWithValidation4 = (0, _setupTests.renderWithValidation)((0, _getComponent.getChildrenJsx)(PARENT_CONFIG, [VisibleComponent, HiddenComponent])), container = _renderWithValidation4.container;
|
|
217
|
+
expect(container.childNodes.length).toEqual(1);
|
|
218
|
+
child = container.childNodes[0];
|
|
219
|
+
expect(child.childNodes[0].childNodes.length).toEqual(2);
|
|
220
|
+
expect(child.classList).toContain('govuk-form-group');
|
|
221
|
+
label = child.childNodes[0];
|
|
222
|
+
expect(label).toBeDefined();
|
|
223
|
+
expect(label.innerHTML).toContain(VisibleComponent.label);
|
|
224
|
+
input = child.childNodes[2];
|
|
225
|
+
expect(input.tagName).toEqual('INPUT');
|
|
226
|
+
expect(input.classList).toContain('govuk-input');
|
|
227
|
+
expect(input.id).toEqual("".concat(VisibleComponent.id));
|
|
228
|
+
expect(input.value).toEqual(VALUE);
|
|
229
|
+
case 18:
|
|
230
|
+
case "end":
|
|
231
|
+
return _context5.stop();
|
|
232
|
+
}
|
|
233
|
+
}, _callee5);
|
|
234
|
+
})));
|
|
183
235
|
});
|
|
@@ -26,6 +26,9 @@ var interpolateOptions = function interpolateOptions(config, options) {
|
|
|
26
26
|
if (typeof opt === 'string') {
|
|
27
27
|
return opt;
|
|
28
28
|
}
|
|
29
|
+
if (!opt.value) {
|
|
30
|
+
return opt.label;
|
|
31
|
+
}
|
|
29
32
|
if (config.alternativeHintField) {
|
|
30
33
|
// eslint-disable-next-line no-param-reassign
|
|
31
34
|
opt.hint = opt[config.alternativeHintField];
|
|
@@ -39,13 +42,44 @@ var interpolateOptions = function interpolateOptions(config, options) {
|
|
|
39
42
|
return n;
|
|
40
43
|
});
|
|
41
44
|
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns a list of options templated from a collection specified.
|
|
48
|
+
* config requires a item field with the templated options e.g
|
|
49
|
+
* "item": {
|
|
50
|
+
* "value": "${id}"
|
|
51
|
+
* "label": "${name}"
|
|
52
|
+
* "hint": "Select a person name"
|
|
53
|
+
* }
|
|
54
|
+
* @param {*} config
|
|
55
|
+
* @param {*} collectionName
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
var getCollectionOptions = function getCollectionOptions(config, collectionName) {
|
|
59
|
+
var collectionOptions = config.formData[collectionName].map(function (collection) {
|
|
60
|
+
return {
|
|
61
|
+
value: _copReactComponents.Utils.interpolateString(config.item.value, collection),
|
|
62
|
+
label: _copReactComponents.Utils.interpolateString(config.item.label, collection),
|
|
63
|
+
hint: _copReactComponents.Utils.interpolateString(config.item.hint, collection)
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
if (config.data.options) {
|
|
67
|
+
return [].concat(collectionOptions, config.data.options);
|
|
68
|
+
}
|
|
69
|
+
return collectionOptions;
|
|
70
|
+
};
|
|
42
71
|
var getOptions = function getOptions(config, callback) {
|
|
43
72
|
if (config) {
|
|
44
|
-
var _config$data, _config$
|
|
45
|
-
if (config
|
|
46
|
-
var
|
|
73
|
+
var _config$data, _config$data3, _config$data4;
|
|
74
|
+
if (config !== null && config !== void 0 && (_config$data = config.data) !== null && _config$data !== void 0 && _config$data.collection && config !== null && config !== void 0 && config.item) {
|
|
75
|
+
var _config$data2;
|
|
76
|
+
var combinedOptions = getCollectionOptions(config, config === null || config === void 0 || (_config$data2 = config.data) === null || _config$data2 === void 0 ? void 0 : _config$data2.collection);
|
|
47
77
|
return callback(interpolateOptions(config, combinedOptions));
|
|
48
78
|
}
|
|
79
|
+
if (config.options && config !== null && config !== void 0 && (_config$data3 = config.data) !== null && _config$data3 !== void 0 && _config$data3.options && config !== null && config !== void 0 && (_config$data4 = config.data) !== null && _config$data4 !== void 0 && _config$data4.url) {
|
|
80
|
+
var _combinedOptions = (0, _nestInRefdataOptions.default)(config.options, config.data.options);
|
|
81
|
+
return callback(interpolateOptions(config, _combinedOptions));
|
|
82
|
+
}
|
|
49
83
|
if (config.options) {
|
|
50
84
|
return callback(interpolateOptions(config, config.options));
|
|
51
85
|
}
|
|
@@ -28,6 +28,16 @@ describe('utils', function () {
|
|
|
28
28
|
expect(options).toEqual(CONFIG.options);
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
|
+
it('should handle stings in option and if no value is supplied treat it as a string', function () {
|
|
32
|
+
var CONFIG = {
|
|
33
|
+
options: ["or", {
|
|
34
|
+
label: "and"
|
|
35
|
+
}]
|
|
36
|
+
};
|
|
37
|
+
(0, _getOptions.default)(CONFIG, function (options) {
|
|
38
|
+
expect(options).toEqual(["or", "and"]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
31
41
|
it('should get any specified options from the data property of the config', function () {
|
|
32
42
|
var CONFIG = {
|
|
33
43
|
data: {
|
|
@@ -46,6 +56,44 @@ describe('utils', function () {
|
|
|
46
56
|
expect(options).toEqual(CONFIG.data.options);
|
|
47
57
|
});
|
|
48
58
|
});
|
|
59
|
+
it('should get options from form data collection', function () {
|
|
60
|
+
var CONFIG = {
|
|
61
|
+
data: {
|
|
62
|
+
collection: "people"
|
|
63
|
+
},
|
|
64
|
+
item: {
|
|
65
|
+
/* eslint-disable no-template-curly-in-string */
|
|
66
|
+
label: "${name}",
|
|
67
|
+
value: "${name}",
|
|
68
|
+
hint: "${name}"
|
|
69
|
+
/* eslint-enable no-template-curly-in-string */
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
formData: {
|
|
73
|
+
people: [{
|
|
74
|
+
"name": "test"
|
|
75
|
+
}]
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
(0, _getOptions.default)(CONFIG, function (options) {
|
|
79
|
+
expect(options[0].hint).toEqual("test");
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
it('should not show options if there is collection but not item data', function () {
|
|
83
|
+
var CONFIG = {
|
|
84
|
+
data: {
|
|
85
|
+
collection: "people"
|
|
86
|
+
},
|
|
87
|
+
formData: {
|
|
88
|
+
people: [{
|
|
89
|
+
"name": "test"
|
|
90
|
+
}]
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
(0, _getOptions.default)(CONFIG, function (options) {
|
|
94
|
+
expect(options).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
49
97
|
it('should use the top-level options over those in the data property', function () {
|
|
50
98
|
var CONFIG = {
|
|
51
99
|
options: [{
|
|
@@ -9,6 +9,7 @@ var _Data = _interopRequireDefault(require("../Data"));
|
|
|
9
9
|
var _getPageActions = _interopRequireDefault(require("./getPageActions"));
|
|
10
10
|
var _getParagraphFromText = _interopRequireDefault(require("./getParagraphFromText"));
|
|
11
11
|
var _useComponent = _interopRequireDefault(require("./useComponent"));
|
|
12
|
+
var _models = require("../../models");
|
|
12
13
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
14
|
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); }
|
|
14
15
|
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; }
|
|
@@ -32,6 +33,12 @@ var getFormPage = function getFormPage(pageOptions, formComponents, formData) {
|
|
|
32
33
|
return (0, _getParagraphFromText.default)(componentOptions);
|
|
33
34
|
}
|
|
34
35
|
var ret;
|
|
36
|
+
if (componentOptions.type === _models.ComponentTypes.CONTAINER) {
|
|
37
|
+
// eslint-disable-next-line no-param-reassign
|
|
38
|
+
componentOptions.components = componentOptions.components.map(function (cmp) {
|
|
39
|
+
return cmp.use ? (0, _useComponent.default)(cmp, formComponents) : cmp;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
35
42
|
if (componentOptions.use) {
|
|
36
43
|
ret = (0, _useComponent.default)(componentOptions, formComponents);
|
|
37
44
|
} else {
|
|
@@ -53,6 +53,17 @@ var getComponentToUse = function getComponentToUse(toUse, formComponents) {
|
|
|
53
53
|
} else {
|
|
54
54
|
wrapper = component;
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
// this allows us to have the 'use: component' functionality inside item array
|
|
58
|
+
if (component.item && Array.isArray(component.item)) {
|
|
59
|
+
component.item = component.item.map(function (item) {
|
|
60
|
+
if (item.use) {
|
|
61
|
+
// eslint-disable-next-line no-use-before-define
|
|
62
|
+
return useComponent(item, formComponents);
|
|
63
|
+
}
|
|
64
|
+
return item;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
56
67
|
return wrapper;
|
|
57
68
|
}
|
|
58
69
|
|
|
@@ -175,6 +175,42 @@ describe('utils', function () {
|
|
|
175
175
|
label: TO_USE.label
|
|
176
176
|
}));
|
|
177
177
|
});
|
|
178
|
+
it('should handle nested use references within item', function () {
|
|
179
|
+
var SEARCH_METHOD = {
|
|
180
|
+
id: 'searchMethod',
|
|
181
|
+
label: 'Search Method',
|
|
182
|
+
type: _models.ComponentTypes.TEXT
|
|
183
|
+
};
|
|
184
|
+
var SEARCH_TECHNIQUES = {
|
|
185
|
+
id: 'searchTechniques',
|
|
186
|
+
label: 'Search Techniques',
|
|
187
|
+
type: _models.ComponentTypes.CONTAINER,
|
|
188
|
+
item: [{
|
|
189
|
+
use: 'searchMethod',
|
|
190
|
+
data: {
|
|
191
|
+
url: 'https://api.example.com/searchtype'
|
|
192
|
+
}
|
|
193
|
+
}]
|
|
194
|
+
};
|
|
195
|
+
var EXTENDED_FORM_COMPONENTS = [].concat(FORM_COMPONENTS, [SEARCH_METHOD, SEARCH_TECHNIQUES]);
|
|
196
|
+
var TO_USE = {
|
|
197
|
+
use: 'searchTechniques'
|
|
198
|
+
};
|
|
199
|
+
var expectedComponent = _objectSpread(_objectSpread({
|
|
200
|
+
use: 'searchTechniques'
|
|
201
|
+
}, SEARCH_TECHNIQUES), {}, {
|
|
202
|
+
cya_label: SEARCH_TECHNIQUES.label,
|
|
203
|
+
item: [_objectSpread(_objectSpread({
|
|
204
|
+
use: 'searchMethod'
|
|
205
|
+
}, SEARCH_METHOD), {}, {
|
|
206
|
+
cya_label: SEARCH_METHOD.label,
|
|
207
|
+
data: {
|
|
208
|
+
url: 'https://api.example.com/searchtype'
|
|
209
|
+
}
|
|
210
|
+
})]
|
|
211
|
+
});
|
|
212
|
+
expect((0, _useComponent.default)(TO_USE, EXTENDED_FORM_COMPONENTS)).toEqual(expectedComponent);
|
|
213
|
+
});
|
|
178
214
|
});
|
|
179
215
|
});
|
|
180
216
|
});
|
|
@@ -7,6 +7,7 @@ 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
9
|
var HODS_EMAIL_REGEX = /^[a-z0-9._\-']+@(digital\.)?homeoffice.gov.uk$/i;
|
|
10
|
+
var JMSC_EMAIL_REGEX = /^[a-z0-9._\-']+@jmsc.gov.uk$/i;
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Validates an email address, ensuring it is in the correct domain and
|
|
@@ -26,7 +27,7 @@ var validateEmail = function validateEmail(value) {
|
|
|
26
27
|
if (!!value) {
|
|
27
28
|
var name = label ? label.toLowerCase() : 'email address';
|
|
28
29
|
if (typeof value === 'string') {
|
|
29
|
-
if (HODS_EMAIL_REGEX.test(value)) {
|
|
30
|
+
if (HODS_EMAIL_REGEX.test(value) || JMSC_EMAIL_REGEX.test(value)) {
|
|
30
31
|
return undefined;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -14,6 +14,9 @@ describe('utils', function () {
|
|
|
14
14
|
it('should return no error when the value is a valid .gov.uk address', function () {
|
|
15
15
|
expect((0, _validateEmail.default)('alpha@homeoffice.gov.uk', LABEL)).toBeUndefined();
|
|
16
16
|
});
|
|
17
|
+
it('should return no error when the value is a valid jmsc.gov.uk address', function () {
|
|
18
|
+
expect((0, _validateEmail.default)('alpha@jmsc.gov.uk', LABEL)).toBeUndefined();
|
|
19
|
+
});
|
|
17
20
|
it('should return no error when the value is a capitalised digital.homeoffice.gov.uk address', function () {
|
|
18
21
|
expect((0, _validateEmail.default)('ALPHA.BRAVO@DIGITAL.HOMEOFFICE.GOV.UK', LABEL)).toBeUndefined();
|
|
19
22
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ukhomeoffice/cop-react-form-renderer",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.14.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rimraf dist",
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
"storybook": "yarn storybook:start",
|
|
14
14
|
"deploy-storybook": "storybook-to-ghpages -e storybook-static",
|
|
15
15
|
"compile": "yarn clean && cross-env NODE_ENV=production babel src --out-dir dist --copy-files && yarn post-compile",
|
|
16
|
-
"post-compile": "rimraf dist/*.test.* dist/**/*.test.* dist/**/*.stories.* dist/docs dist/assets"
|
|
16
|
+
"post-compile": "rimraf dist/*.test.* dist/**/*.test.* dist/**/*.stories.* dist/docs dist/assets",
|
|
17
|
+
"compile-with-maps": "yarn clean && cross-env NODE_ENV=production babel src --out-dir dist --source-maps true --copy-files && yarn post-compile",
|
|
18
|
+
"yalc-publish": "yarn compile-with-maps && cp -r src dist/src && yalc publish --push"
|
|
17
19
|
},
|
|
18
20
|
"dependencies": {
|
|
19
|
-
"@ukhomeoffice/cop-react-components": "4.7.
|
|
21
|
+
"@ukhomeoffice/cop-react-components": "4.7.3",
|
|
20
22
|
"axios": "^0.23.0",
|
|
21
23
|
"dayjs": "^1.11.0",
|
|
22
24
|
"govuk-frontend": "^5.0.0",
|