@ukhomeoffice/cop-react-form-renderer 3.1.1 → 3.2.4
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/FormComponent/FormComponent.js +3 -2
- package/dist/components/FormPage/FormPage.js +8 -1
- package/dist/components/FormPage/FormPage.test.js +172 -40
- package/dist/utils/Component/getComponent.js +1 -2
- package/dist/utils/Component/getComponentTests/getComponent.time.test.js +106 -0
- package/dist/utils/Component/showComponent.js +6 -0
- package/dist/utils/Component/showComponent.test.js +63 -0
- package/dist/utils/Component/wrapInFormGroup.js +5 -2
- package/dist/utils/Condition/index.js +3 -0
- package/dist/utils/Condition/meetsOneCondition.js +40 -0
- package/dist/utils/Condition/meetsOneCondition.test.js +101 -0
- package/dist/utils/Condition/setupConditions.js +5 -4
- package/dist/utils/Container/showContainer.js +4 -0
- package/dist/utils/Container/showContainer.test.js +51 -0
- package/dist/utils/Data/getOptions.js +19 -2
- package/dist/utils/FormPage/getFormPage.js +0 -40
- package/dist/utils/FormPage/getFormPage.test.js +0 -68
- package/dist/utils/FormPage/showFormPage.js +4 -0
- package/dist/utils/FormPage/showFormPage.test.js +51 -0
- package/dist/utils/Validate/validatePage.js +12 -2
- package/dist/utils/Validate/validatePage.test.js +68 -1
- package/package.json +2 -2
|
@@ -140,8 +140,9 @@ var FormComponent = function FormComponent(_ref) {
|
|
|
140
140
|
return _utils.default.Component.get(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, attrs), component), {}, {
|
|
141
141
|
id: component.full_path || component.id
|
|
142
142
|
}, (0, _helpers.getComponentError)(component, validation === null || validation === void 0 ? void 0 : validation.errors)), {}, {
|
|
143
|
-
label: component.label
|
|
144
|
-
|
|
143
|
+
label: _utils.default.interpolateString(component.label, formData),
|
|
144
|
+
content: _utils.default.interpolateString(component.content, formData),
|
|
145
|
+
hint: _utils.default.interpolateString(component.hint, formData),
|
|
145
146
|
options: options,
|
|
146
147
|
value: value || _utils.default.Component.defaultValue(component),
|
|
147
148
|
onChange: onComponentChangeExtended,
|
|
@@ -51,6 +51,8 @@ var DEFAULT_CLASS = 'hods-form';
|
|
|
51
51
|
exports.DEFAULT_CLASS = DEFAULT_CLASS;
|
|
52
52
|
|
|
53
53
|
var FormPage = function FormPage(_ref) {
|
|
54
|
+
var _page$actions;
|
|
55
|
+
|
|
54
56
|
var page = _ref.page,
|
|
55
57
|
_onAction = _ref.onAction,
|
|
56
58
|
classBlock = _ref.classBlock,
|
|
@@ -81,10 +83,15 @@ var FormPage = function FormPage(_ref) {
|
|
|
81
83
|
|
|
82
84
|
var classes = _utils.default.classBuilder(classBlock, classModifiers, className);
|
|
83
85
|
|
|
86
|
+
page.actions = (_page$actions = page.actions) === null || _page$actions === void 0 ? void 0 : _page$actions.map(function (action) {
|
|
87
|
+
return action.label ? _objectSpread(_objectSpread({}, action), {}, {
|
|
88
|
+
label: _utils.default.interpolateString(action.label, page.formData)
|
|
89
|
+
}) : action;
|
|
90
|
+
});
|
|
84
91
|
return /*#__PURE__*/_react.default.createElement("div", {
|
|
85
92
|
className: classes('page'),
|
|
86
93
|
key: page.id
|
|
87
|
-
}, page.title && /*#__PURE__*/_react.default.createElement(_copReactComponents.LargeHeading, null, page.title), (errors === null || errors === void 0 ? void 0 : errors.length) > 0 && /*#__PURE__*/_react.default.createElement(_copReactComponents.ErrorSummary, {
|
|
94
|
+
}, page.title && /*#__PURE__*/_react.default.createElement(_copReactComponents.LargeHeading, null, _utils.default.interpolateString(page.title, page.formData)), (errors === null || errors === void 0 ? void 0 : errors.length) > 0 && /*#__PURE__*/_react.default.createElement(_copReactComponents.ErrorSummary, {
|
|
88
95
|
errors: errors
|
|
89
96
|
}), page.components.filter(function (c) {
|
|
90
97
|
return _utils.default.Component.show(c, page.formData);
|
|
@@ -24,6 +24,18 @@ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try
|
|
|
24
24
|
|
|
25
25
|
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
26
26
|
|
|
27
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
28
|
+
|
|
29
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
30
|
+
|
|
31
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
32
|
+
|
|
33
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
34
|
+
|
|
35
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
36
|
+
|
|
37
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
38
|
+
|
|
27
39
|
describe('components.FormPage', function () {
|
|
28
40
|
describe('FormPage', function () {
|
|
29
41
|
var TEXT = {
|
|
@@ -32,6 +44,25 @@ describe('components.FormPage', function () {
|
|
|
32
44
|
type: 'text',
|
|
33
45
|
label: 'Text component',
|
|
34
46
|
hint: 'Text hint'
|
|
47
|
+
}; // eslint-disable-next-line no-template-curly-in-string
|
|
48
|
+
|
|
49
|
+
var TEXT_WITH_EXPRESSION = {
|
|
50
|
+
id: 'text1',
|
|
51
|
+
fieldId: 'text1',
|
|
52
|
+
type: 'text',
|
|
53
|
+
label: 'Email to ${wrapper.email}',
|
|
54
|
+
hint: 'Text hint ${currentUser.givenName}'
|
|
55
|
+
}; // eslint-disable-next-line no-template-curly-in-string
|
|
56
|
+
|
|
57
|
+
var AUTO_WITH_EXPRESSION = {
|
|
58
|
+
id: 'text2',
|
|
59
|
+
fieldId: 'text2',
|
|
60
|
+
label: '${text2} ${currentUser.givenName}',
|
|
61
|
+
hint: '${wrapper.email}',
|
|
62
|
+
type: 'autocomplete',
|
|
63
|
+
required: true,
|
|
64
|
+
source: [],
|
|
65
|
+
disabled: false
|
|
35
66
|
};
|
|
36
67
|
var VALUE = 'Text value';
|
|
37
68
|
var PAGE = {
|
|
@@ -43,8 +74,102 @@ describe('components.FormPage', function () {
|
|
|
43
74
|
text: VALUE
|
|
44
75
|
}
|
|
45
76
|
};
|
|
77
|
+
var PAGE_WITH_BUTTON_ACTIONS = {
|
|
78
|
+
id: 'pageId',
|
|
79
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
80
|
+
title: '${title}',
|
|
81
|
+
components: [TEXT_WITH_EXPRESSION, AUTO_WITH_EXPRESSION],
|
|
82
|
+
actions: [// eslint-disable-next-line no-template-curly-in-string
|
|
83
|
+
{
|
|
84
|
+
type: 'submit',
|
|
85
|
+
validate: true,
|
|
86
|
+
label: 'Next ${wrapper.nextAction}'
|
|
87
|
+
}, // eslint-disable-next-line no-template-curly-in-string
|
|
88
|
+
{
|
|
89
|
+
type: 'navigate',
|
|
90
|
+
page: '1',
|
|
91
|
+
label: 'Go back ${wrapper.prevAction}'
|
|
92
|
+
}],
|
|
93
|
+
formData: {
|
|
94
|
+
title: 'Order Form',
|
|
95
|
+
text1: "Text1 ".concat(VALUE),
|
|
96
|
+
text2: "Text2 ".concat(VALUE),
|
|
97
|
+
wrapper: {
|
|
98
|
+
email: 'test@example.email',
|
|
99
|
+
nextAction: 'Payment',
|
|
100
|
+
prevAction: 'User Details'
|
|
101
|
+
},
|
|
102
|
+
currentUser: {
|
|
103
|
+
givenName: 'Doe'
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
46
107
|
var ON_ACTION_CALLS = [];
|
|
47
108
|
|
|
109
|
+
var checkInputField = function checkInputField(formGroup, fieldId, lbl, hnt, val) {
|
|
110
|
+
expect(formGroup.tagName).toEqual('DIV');
|
|
111
|
+
expect(formGroup.classList).toContain('govuk-form-group');
|
|
112
|
+
var label = formGroup.childNodes[0];
|
|
113
|
+
expect(label.tagName).toEqual('LABEL');
|
|
114
|
+
expect(label.classList).toContain('govuk-label');
|
|
115
|
+
expect(label.textContent).toEqual(lbl);
|
|
116
|
+
expect(label.getAttribute('for')).toEqual(fieldId);
|
|
117
|
+
var hint = formGroup.childNodes[1];
|
|
118
|
+
expect(hint.tagName).toEqual('SPAN');
|
|
119
|
+
expect(hint.classList).toContain('govuk-hint');
|
|
120
|
+
expect(hint.textContent).toEqual(hnt);
|
|
121
|
+
var input = formGroup.childNodes[2];
|
|
122
|
+
expect(input.tagName).toEqual('INPUT');
|
|
123
|
+
expect(input.classList).toContain('govuk-input');
|
|
124
|
+
expect(input.id).toEqual(fieldId);
|
|
125
|
+
expect(input.value).toEqual(val);
|
|
126
|
+
return input;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
var checkAutoCompleteField = function checkAutoCompleteField(formGroup, fieldId, lbl, hnt, val) {
|
|
130
|
+
expect(formGroup.tagName).toEqual('DIV');
|
|
131
|
+
expect(formGroup.classList).toContain('govuk-form-group');
|
|
132
|
+
var label = formGroup.childNodes[0];
|
|
133
|
+
expect(label.tagName).toEqual('LABEL');
|
|
134
|
+
expect(label.classList).toContain('govuk-label');
|
|
135
|
+
expect(label.textContent).toEqual(lbl);
|
|
136
|
+
expect(label.getAttribute('for')).toEqual(fieldId);
|
|
137
|
+
var hint = formGroup.childNodes[1];
|
|
138
|
+
expect(hint.tagName).toEqual('SPAN');
|
|
139
|
+
expect(hint.classList).toContain('govuk-hint');
|
|
140
|
+
expect(hint.textContent).toEqual(hnt);
|
|
141
|
+
var outerWrapper = formGroup.childNodes[2];
|
|
142
|
+
expect(outerWrapper.classList).toContain('hods-autocomplete__outer-wrapper');
|
|
143
|
+
var autocomplete = outerWrapper.childNodes[0];
|
|
144
|
+
expect(autocomplete.classList).toContain('hods-autocomplete__wrapper');
|
|
145
|
+
|
|
146
|
+
var input = _toConsumableArray(autocomplete.childNodes).filter(function (e) {
|
|
147
|
+
return e.tagName === 'INPUT';
|
|
148
|
+
})[0];
|
|
149
|
+
|
|
150
|
+
expect(input.classList).toContain('hods-autocomplete__input');
|
|
151
|
+
expect(input.tagName).toEqual('INPUT');
|
|
152
|
+
expect(input.id).toEqual(fieldId);
|
|
153
|
+
expect(input.value).toEqual(val);
|
|
154
|
+
return input;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
var checkFormButtonGroup = function checkFormButtonGroup(buttonGroup, labels) {
|
|
158
|
+
var _buttonGroup$childNod;
|
|
159
|
+
|
|
160
|
+
var buttons = [];
|
|
161
|
+
expect(buttonGroup.tagName).toEqual('DIV');
|
|
162
|
+
expect(buttonGroup.classList).toContain('hods-button-group');
|
|
163
|
+
expect((_buttonGroup$childNod = buttonGroup.childNodes) === null || _buttonGroup$childNod === void 0 ? void 0 : _buttonGroup$childNod.length).toEqual(labels.length);
|
|
164
|
+
buttonGroup.childNodes.forEach(function (button, i) {
|
|
165
|
+
expect(button.tagName).toEqual('BUTTON');
|
|
166
|
+
expect(button.classList).toContain('hods-button');
|
|
167
|
+
expect(button.textContent).toEqual(labels[i]);
|
|
168
|
+
buttons.push(button);
|
|
169
|
+
});
|
|
170
|
+
return buttons;
|
|
171
|
+
};
|
|
172
|
+
|
|
48
173
|
var ON_ACTION = function ON_ACTION(action, patch, onError) {
|
|
49
174
|
ON_ACTION_CALLS.push({
|
|
50
175
|
action: action,
|
|
@@ -60,7 +185,7 @@ describe('components.FormPage', function () {
|
|
|
60
185
|
ON_ACTION_CALLS.length = 0;
|
|
61
186
|
});
|
|
62
187
|
it('should render a submit page correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
|
|
63
|
-
var _renderWithValidation, container, page, heading
|
|
188
|
+
var _renderWithValidation, container, page, heading;
|
|
64
189
|
|
|
65
190
|
return regeneratorRuntime.wrap(function _callee$(_context) {
|
|
66
191
|
while (1) {
|
|
@@ -76,50 +201,57 @@ describe('components.FormPage', function () {
|
|
|
76
201
|
heading = page.childNodes[0];
|
|
77
202
|
expect(heading.classList).toContain('govuk-heading-l');
|
|
78
203
|
expect(heading.textContent).toEqual(PAGE.title);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
expect(label.tagName).toEqual('LABEL');
|
|
84
|
-
expect(label.classList).toContain('govuk-label');
|
|
85
|
-
expect(label.textContent).toEqual("".concat(TEXT.label, " (optional)"));
|
|
86
|
-
expect(label.getAttribute('for')).toEqual(TEXT.fieldId);
|
|
87
|
-
hint = formGroup.childNodes[1];
|
|
88
|
-
expect(hint.tagName).toEqual('SPAN');
|
|
89
|
-
expect(hint.classList).toContain('govuk-hint');
|
|
90
|
-
expect(hint.textContent).toEqual(TEXT.hint);
|
|
91
|
-
input = formGroup.childNodes[2];
|
|
92
|
-
expect(input.tagName).toEqual('INPUT');
|
|
93
|
-
expect(input.classList).toContain('govuk-input');
|
|
94
|
-
expect(input.id).toEqual(TEXT.fieldId);
|
|
95
|
-
expect(input.value).toEqual(VALUE);
|
|
96
|
-
buttonGroup = page.childNodes[2];
|
|
97
|
-
expect(buttonGroup.tagName).toEqual('DIV');
|
|
98
|
-
expect(buttonGroup.classList).toContain('hods-button-group');
|
|
99
|
-
button = buttonGroup.childNodes[0];
|
|
100
|
-
expect(button.tagName).toEqual('BUTTON');
|
|
101
|
-
expect(button.classList).toContain('hods-button');
|
|
102
|
-
expect(button.textContent).toEqual(_ActionButton.DEFAULT_LABEL);
|
|
103
|
-
|
|
104
|
-
case 31:
|
|
204
|
+
checkInputField(page.childNodes[1], TEXT.fieldId, "".concat(TEXT.label, " (optional)"), TEXT.hint, VALUE);
|
|
205
|
+
checkFormButtonGroup(page.childNodes[2], [_ActionButton.DEFAULT_LABEL]);
|
|
206
|
+
|
|
207
|
+
case 9:
|
|
105
208
|
case "end":
|
|
106
209
|
return _context.stop();
|
|
107
210
|
}
|
|
108
211
|
}
|
|
109
212
|
}, _callee);
|
|
110
213
|
})));
|
|
111
|
-
it('should
|
|
112
|
-
var _renderWithValidation2, container, page,
|
|
214
|
+
it('should render a submit page correctly and submit action button has interpolated label', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
|
|
215
|
+
var _renderWithValidation2, container, page, FORM_DATA, heading;
|
|
113
216
|
|
|
114
217
|
return regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
115
218
|
while (1) {
|
|
116
219
|
switch (_context2.prev = _context2.next) {
|
|
117
220
|
case 0:
|
|
118
221
|
_renderWithValidation2 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
119
|
-
page:
|
|
222
|
+
page: PAGE_WITH_BUTTON_ACTIONS,
|
|
120
223
|
onAction: ON_ACTION
|
|
121
224
|
})), container = _renderWithValidation2.container;
|
|
122
225
|
page = container.childNodes[0];
|
|
226
|
+
FORM_DATA = PAGE_WITH_BUTTON_ACTIONS.formData;
|
|
227
|
+
expect(page.tagName).toEqual('DIV');
|
|
228
|
+
expect(page.classList).toContain("".concat(_FormPage.DEFAULT_CLASS, "__page"));
|
|
229
|
+
heading = page.childNodes[0];
|
|
230
|
+
expect(heading.classList).toContain('govuk-heading-l');
|
|
231
|
+
expect(heading.textContent).toEqual(FORM_DATA.title);
|
|
232
|
+
checkInputField(page.childNodes[1], TEXT_WITH_EXPRESSION.fieldId, 'Email to test@example.email (optional)', 'Text hint Doe', 'Text1 Text value');
|
|
233
|
+
checkAutoCompleteField(page.childNodes[2], AUTO_WITH_EXPRESSION.fieldId, "".concat(FORM_DATA.text2, " ").concat(FORM_DATA.currentUser.givenName), FORM_DATA.wrapper.email, "Text2 ".concat(VALUE));
|
|
234
|
+
checkFormButtonGroup(page.childNodes[3], ['Next Payment', 'Go back User Details']);
|
|
235
|
+
|
|
236
|
+
case 11:
|
|
237
|
+
case "end":
|
|
238
|
+
return _context2.stop();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}, _callee2);
|
|
242
|
+
})));
|
|
243
|
+
it('should handle a page change appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {
|
|
244
|
+
var _renderWithValidation3, container, page, input, NEW_VALUE, EVENT;
|
|
245
|
+
|
|
246
|
+
return regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
247
|
+
while (1) {
|
|
248
|
+
switch (_context3.prev = _context3.next) {
|
|
249
|
+
case 0:
|
|
250
|
+
_renderWithValidation3 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
251
|
+
page: PAGE,
|
|
252
|
+
onAction: ON_ACTION
|
|
253
|
+
})), container = _renderWithValidation3.container;
|
|
254
|
+
page = container.childNodes[0];
|
|
123
255
|
expect(page.tagName).toEqual('DIV'); // Change the input.
|
|
124
256
|
|
|
125
257
|
input = page.childNodes[1].childNodes[2];
|
|
@@ -138,22 +270,22 @@ describe('components.FormPage', function () {
|
|
|
138
270
|
|
|
139
271
|
case 8:
|
|
140
272
|
case "end":
|
|
141
|
-
return
|
|
273
|
+
return _context3.stop();
|
|
142
274
|
}
|
|
143
275
|
}
|
|
144
|
-
},
|
|
276
|
+
}, _callee3);
|
|
145
277
|
})));
|
|
146
|
-
it('should handle a page action appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function
|
|
147
|
-
var
|
|
278
|
+
it('should handle a page action appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {
|
|
279
|
+
var _renderWithValidation4, container, page, input, NEW_VALUE, CHANGE_EVENT, button;
|
|
148
280
|
|
|
149
|
-
return regeneratorRuntime.wrap(function
|
|
281
|
+
return regeneratorRuntime.wrap(function _callee4$(_context4) {
|
|
150
282
|
while (1) {
|
|
151
|
-
switch (
|
|
283
|
+
switch (_context4.prev = _context4.next) {
|
|
152
284
|
case 0:
|
|
153
|
-
|
|
285
|
+
_renderWithValidation4 = (0, _setupTests.renderWithValidation)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
154
286
|
page: PAGE,
|
|
155
287
|
onAction: ON_ACTION
|
|
156
|
-
})), container =
|
|
288
|
+
})), container = _renderWithValidation4.container;
|
|
157
289
|
page = container.childNodes[0]; // Change the input.
|
|
158
290
|
|
|
159
291
|
input = page.childNodes[1].childNodes[2];
|
|
@@ -182,10 +314,10 @@ describe('components.FormPage', function () {
|
|
|
182
314
|
|
|
183
315
|
case 12:
|
|
184
316
|
case "end":
|
|
185
|
-
return
|
|
317
|
+
return _context4.stop();
|
|
186
318
|
}
|
|
187
319
|
}
|
|
188
|
-
},
|
|
320
|
+
}, _callee4);
|
|
189
321
|
})));
|
|
190
322
|
});
|
|
191
323
|
});
|
|
@@ -224,8 +224,7 @@ var getComponent = function getComponent(config) {
|
|
|
224
224
|
var component = getComponentByType(config);
|
|
225
225
|
|
|
226
226
|
if (component && wrap && (0, _isEditable.default)(config)) {
|
|
227
|
-
|
|
228
|
-
return (0, _wrapInFormGroup.default)(attrs, component);
|
|
227
|
+
return (0, _wrapInFormGroup.default)(config, component);
|
|
229
228
|
}
|
|
230
229
|
|
|
231
230
|
return component;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _react = require("@testing-library/react");
|
|
4
|
+
|
|
5
|
+
var _models = require("../../../models");
|
|
6
|
+
|
|
7
|
+
var _getComponent = _interopRequireDefault(require("../getComponent"));
|
|
8
|
+
|
|
9
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
|
|
11
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
12
|
+
|
|
13
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
14
|
+
|
|
15
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
16
|
+
|
|
17
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
|
|
18
|
+
|
|
19
|
+
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
|
20
|
+
|
|
21
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
22
|
+
|
|
23
|
+
describe('utils.Component.get', function () {
|
|
24
|
+
it('should return an appropriately rendered time component', function () {
|
|
25
|
+
var ID = 'test-id';
|
|
26
|
+
var FIELD_ID = 'field-id';
|
|
27
|
+
var LABEL = 'label';
|
|
28
|
+
var ON_CHANGE_CALLS = [];
|
|
29
|
+
|
|
30
|
+
var ON_CHANGE = function ON_CHANGE(e) {
|
|
31
|
+
ON_CHANGE_CALLS.push(e.target);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
var COMPONENT = {
|
|
35
|
+
type: _models.ComponentTypes.TIME,
|
|
36
|
+
id: ID,
|
|
37
|
+
fieldId: FIELD_ID,
|
|
38
|
+
label: LABEL,
|
|
39
|
+
onChange: ON_CHANGE,
|
|
40
|
+
'data-testid': ID
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
var _render = (0, _react.render)((0, _getComponent.default)(COMPONENT)),
|
|
44
|
+
container = _render.container;
|
|
45
|
+
|
|
46
|
+
var _getAllByTestId = (0, _react.getAllByTestId)(container, ID),
|
|
47
|
+
_getAllByTestId2 = _slicedToArray(_getAllByTestId, 2),
|
|
48
|
+
formGroup = _getAllByTestId2[0],
|
|
49
|
+
timeInput = _getAllByTestId2[1];
|
|
50
|
+
|
|
51
|
+
expect(formGroup.tagName).toEqual('DIV');
|
|
52
|
+
expect(formGroup.classList).toContain('govuk-form-group');
|
|
53
|
+
var label = formGroup.childNodes[0];
|
|
54
|
+
expect(label.innerHTML).toContain(LABEL);
|
|
55
|
+
expect(label.getAttribute('for')).toEqual(ID);
|
|
56
|
+
expect(timeInput.tagName).toEqual('DIV');
|
|
57
|
+
expect(timeInput.classList).toContain('govuk-date-input');
|
|
58
|
+
expect(timeInput.id).toEqual(ID);
|
|
59
|
+
var onChangeCalls = ON_CHANGE_CALLS.length;
|
|
60
|
+
|
|
61
|
+
var _timeInput$childNodes = _slicedToArray(timeInput.childNodes, 2),
|
|
62
|
+
hourItem = _timeInput$childNodes[0],
|
|
63
|
+
minuteItem = _timeInput$childNodes[1];
|
|
64
|
+
|
|
65
|
+
[{
|
|
66
|
+
id: 'hour',
|
|
67
|
+
label: 'Hour',
|
|
68
|
+
item: hourItem,
|
|
69
|
+
value: '5',
|
|
70
|
+
expectedValue: '5:'
|
|
71
|
+
}, {
|
|
72
|
+
id: 'minute',
|
|
73
|
+
label: 'Minute',
|
|
74
|
+
item: minuteItem,
|
|
75
|
+
value: '11',
|
|
76
|
+
expectedValue: '5:11'
|
|
77
|
+
}].forEach(function (part) {
|
|
78
|
+
expect(part.item.tagName).toEqual('DIV');
|
|
79
|
+
expect(part.item.classList).toContain('govuk-date-input__item');
|
|
80
|
+
|
|
81
|
+
var _part$item$childNodes = _slicedToArray(part.item.childNodes, 2),
|
|
82
|
+
label = _part$item$childNodes[0],
|
|
83
|
+
input = _part$item$childNodes[1];
|
|
84
|
+
|
|
85
|
+
expect(label.tagName).toEqual('LABEL');
|
|
86
|
+
expect(label.classList).toContain('govuk-label');
|
|
87
|
+
expect(label.textContent).toEqual(part.label);
|
|
88
|
+
expect(input.tagName).toEqual('INPUT');
|
|
89
|
+
expect(input.id).toEqual("".concat(ID, "-").concat(part.id)); // Put something in the input and make sure it fires.
|
|
90
|
+
|
|
91
|
+
_react.fireEvent.change(input, {
|
|
92
|
+
target: {
|
|
93
|
+
name: "".concat(FIELD_ID, "-").concat(part.id),
|
|
94
|
+
value: part.value
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
onChangeCalls++;
|
|
99
|
+
expect(ON_CHANGE_CALLS.length).toEqual(onChangeCalls);
|
|
100
|
+
expect(ON_CHANGE_CALLS[onChangeCalls - 1]).toMatchObject({
|
|
101
|
+
name: FIELD_ID,
|
|
102
|
+
value: part.expectedValue
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -11,6 +11,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
11
11
|
|
|
12
12
|
// Local imports
|
|
13
13
|
var showComponent = function showComponent(component, data) {
|
|
14
|
+
var _component$show_when;
|
|
15
|
+
|
|
14
16
|
if (!component) {
|
|
15
17
|
return false;
|
|
16
18
|
}
|
|
@@ -19,6 +21,10 @@ var showComponent = function showComponent(component, data) {
|
|
|
19
21
|
return false;
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
if (((_component$show_when = component.show_when) === null || _component$show_when === void 0 ? void 0 : _component$show_when.type) === "or") {
|
|
25
|
+
return _Condition.default.meetsOne(component, data);
|
|
26
|
+
}
|
|
27
|
+
|
|
22
28
|
return _Condition.default.meetsAll(component, data);
|
|
23
29
|
};
|
|
24
30
|
|
|
@@ -89,6 +89,69 @@ describe('utils', function () {
|
|
|
89
89
|
};
|
|
90
90
|
expect((0, _showComponent.default)(COMPONENT, DATA)).toBeFalsy();
|
|
91
91
|
});
|
|
92
|
+
it('SHOULD be shown when the component has multiple show_when conditions, with type "or" provided and ALL are matched', function () {
|
|
93
|
+
var COMPONENT = {
|
|
94
|
+
show_when: {
|
|
95
|
+
"type": "or",
|
|
96
|
+
"conditions": [{
|
|
97
|
+
field: 'alpha',
|
|
98
|
+
op: '=',
|
|
99
|
+
value: 'Alpha'
|
|
100
|
+
}, {
|
|
101
|
+
field: 'bravo',
|
|
102
|
+
op: '=',
|
|
103
|
+
value: 'Bravo'
|
|
104
|
+
}]
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var DATA = {
|
|
108
|
+
alpha: 'Alpha',
|
|
109
|
+
bravo: 'Bravo'
|
|
110
|
+
};
|
|
111
|
+
expect((0, _showComponent.default)(COMPONENT, DATA)).toBeTruthy();
|
|
112
|
+
});
|
|
113
|
+
it('SHOULD be shown when the component has multiple show_when conditions, with type "or" provided and at least ONE is matched', function () {
|
|
114
|
+
var COMPONENT = {
|
|
115
|
+
show_when: {
|
|
116
|
+
"type": "or",
|
|
117
|
+
"conditions": [{
|
|
118
|
+
field: 'alpha',
|
|
119
|
+
op: '=',
|
|
120
|
+
value: 'Alpha'
|
|
121
|
+
}, {
|
|
122
|
+
field: 'charlie',
|
|
123
|
+
op: '=',
|
|
124
|
+
value: 'Charlie'
|
|
125
|
+
}]
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
var DATA = {
|
|
129
|
+
alpha: 'Alpha',
|
|
130
|
+
bravo: 'Bravo'
|
|
131
|
+
};
|
|
132
|
+
expect((0, _showComponent.default)(COMPONENT, DATA)).toBeTruthy();
|
|
133
|
+
});
|
|
134
|
+
it('should NOT be shown when the component has multiple show_when conditions, with type "or" provided and NONE are matched', function () {
|
|
135
|
+
var COMPONENT = {
|
|
136
|
+
show_when: {
|
|
137
|
+
"type": "or",
|
|
138
|
+
"conditions": [{
|
|
139
|
+
field: 'alpha',
|
|
140
|
+
op: '!=',
|
|
141
|
+
value: 'Alpha'
|
|
142
|
+
}, {
|
|
143
|
+
field: 'charlie',
|
|
144
|
+
op: '=',
|
|
145
|
+
value: 'Charlie'
|
|
146
|
+
}]
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var DATA = {
|
|
150
|
+
alpha: 'Alpha',
|
|
151
|
+
bravo: 'Bravo'
|
|
152
|
+
};
|
|
153
|
+
expect((0, _showComponent.default)(COMPONENT, DATA)).toBeFalsy();
|
|
154
|
+
});
|
|
92
155
|
});
|
|
93
156
|
});
|
|
94
157
|
});
|
|
@@ -5,16 +5,19 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
+
var _copReactComponents = require("@ukhomeoffice/cop-react-components");
|
|
9
|
+
|
|
8
10
|
var _react = _interopRequireDefault(require("react"));
|
|
9
11
|
|
|
10
|
-
var
|
|
12
|
+
var _cleanAttributes = _interopRequireDefault(require("./cleanAttributes"));
|
|
11
13
|
|
|
12
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
15
|
|
|
14
16
|
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
15
17
|
|
|
16
18
|
var wrapInFormGroup = function wrapInFormGroup(config, children) {
|
|
17
|
-
|
|
19
|
+
var attrs = (0, _cleanAttributes.default)(config, ['fieldId', 'displayMenu']);
|
|
20
|
+
return /*#__PURE__*/_react.default.createElement(_copReactComponents.FormGroup, _extends({}, attrs, {
|
|
18
21
|
onChange: null
|
|
19
22
|
}), children);
|
|
20
23
|
};
|
|
@@ -9,9 +9,12 @@ var _meetsAllConditions = _interopRequireDefault(require("./meetsAllConditions")
|
|
|
9
9
|
|
|
10
10
|
var _meetsCondition = _interopRequireDefault(require("./meetsCondition"));
|
|
11
11
|
|
|
12
|
+
var _meetsOneCondition = _interopRequireDefault(require("./meetsOneCondition"));
|
|
13
|
+
|
|
12
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
15
|
|
|
14
16
|
var Condition = {
|
|
17
|
+
meetsOne: _meetsOneCondition.default,
|
|
15
18
|
meetsAll: _meetsAllConditions.default,
|
|
16
19
|
met: _meetsCondition.default
|
|
17
20
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _Data = _interopRequireDefault(require("../Data"));
|
|
9
|
+
|
|
10
|
+
var _meetsCondition = _interopRequireDefault(require("./meetsCondition"));
|
|
11
|
+
|
|
12
|
+
var _setupConditions = _interopRequireDefault(require("./setupConditions"));
|
|
13
|
+
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
|
|
16
|
+
// Local imports
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Evaluates all condition(s) on a page, container, or component.
|
|
20
|
+
* @param {object} options The container to consider.
|
|
21
|
+
* @param {object} data The top-level form data.
|
|
22
|
+
* @returns Boolean true if at least one conditions is met; false otherwise.
|
|
23
|
+
*/
|
|
24
|
+
var meetsOneCondition = function meetsOneCondition(options, data) {
|
|
25
|
+
var conditions = (0, _setupConditions.default)(options);
|
|
26
|
+
|
|
27
|
+
if (conditions) {
|
|
28
|
+
var arr = Array.isArray(conditions) ? conditions : [conditions];
|
|
29
|
+
return arr.some(function (condition) {
|
|
30
|
+
var sourceDataValue = _Data.default.getSource(data, condition.field);
|
|
31
|
+
|
|
32
|
+
return (0, _meetsCondition.default)(condition, sourceDataValue);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return true;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
var _default = meetsOneCondition;
|
|
40
|
+
exports.default = _default;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _meetsOneCondition = _interopRequireDefault(require("./meetsOneCondition"));
|
|
4
|
+
|
|
5
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
6
|
+
|
|
7
|
+
// Local imports
|
|
8
|
+
describe('utils.Condition.meetsOneCondition', function () {
|
|
9
|
+
var DATA = {
|
|
10
|
+
alpha: 'bravo',
|
|
11
|
+
charlie: 'delta'
|
|
12
|
+
};
|
|
13
|
+
it('should evaluate to true when conditions are null', function () {
|
|
14
|
+
expect((0, _meetsOneCondition.default)(null, DATA)).toBeTruthy();
|
|
15
|
+
});
|
|
16
|
+
it('should evaluate to true when conditions are undefined', function () {
|
|
17
|
+
expect((0, _meetsOneCondition.default)(undefined, DATA)).toBeTruthy();
|
|
18
|
+
});
|
|
19
|
+
it('should evaluate to true when conditions is an empty array', function () {
|
|
20
|
+
var CONDITION = {
|
|
21
|
+
"type": "or",
|
|
22
|
+
"conditions": []
|
|
23
|
+
};
|
|
24
|
+
expect((0, _meetsOneCondition.default)(CONDITION, DATA)).toBeTruthy();
|
|
25
|
+
});
|
|
26
|
+
it('should evaluate to true when one condition is provided and one condition is met', function () {
|
|
27
|
+
var CONDITION = {
|
|
28
|
+
"type": "or",
|
|
29
|
+
"conditions": [{
|
|
30
|
+
field: 'alpha',
|
|
31
|
+
op: 'eq',
|
|
32
|
+
value: 'bravo'
|
|
33
|
+
}]
|
|
34
|
+
};
|
|
35
|
+
expect((0, _meetsOneCondition.default)(CONDITION, DATA)).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
it('should evaluate to true when two conditions are provided and one condition is met', function () {
|
|
38
|
+
var CONDITION = {
|
|
39
|
+
"type": "or",
|
|
40
|
+
"conditions": [{
|
|
41
|
+
field: 'alpha',
|
|
42
|
+
op: 'eq',
|
|
43
|
+
value: 'bravo'
|
|
44
|
+
}, {
|
|
45
|
+
field: 'alpha',
|
|
46
|
+
op: 'eq',
|
|
47
|
+
value: "charlie"
|
|
48
|
+
}]
|
|
49
|
+
};
|
|
50
|
+
expect((0, _meetsOneCondition.default)(CONDITION, DATA)).toBeTruthy();
|
|
51
|
+
});
|
|
52
|
+
it('should evaluate to true when three conditions are provided and two conditions are met', function () {
|
|
53
|
+
var CONDITION = {
|
|
54
|
+
"type": "or",
|
|
55
|
+
"conditions": [{
|
|
56
|
+
field: 'alpha',
|
|
57
|
+
op: 'eq',
|
|
58
|
+
value: 'bravo'
|
|
59
|
+
}, {
|
|
60
|
+
field: 'charlie',
|
|
61
|
+
op: 'eq',
|
|
62
|
+
value: 'delta'
|
|
63
|
+
}, {
|
|
64
|
+
field: 'alpha',
|
|
65
|
+
op: 'eq',
|
|
66
|
+
value: "charlie"
|
|
67
|
+
}]
|
|
68
|
+
};
|
|
69
|
+
expect((0, _meetsOneCondition.default)(CONDITION, DATA)).toBeTruthy();
|
|
70
|
+
});
|
|
71
|
+
it('should evaluate to true when all conditions are met', function () {
|
|
72
|
+
var CONDITION = {
|
|
73
|
+
"type": "or",
|
|
74
|
+
"conditions": [{
|
|
75
|
+
field: 'alpha',
|
|
76
|
+
op: 'eq',
|
|
77
|
+
value: 'bravo'
|
|
78
|
+
}, {
|
|
79
|
+
field: 'charlie',
|
|
80
|
+
op: 'eq',
|
|
81
|
+
value: 'delta'
|
|
82
|
+
}]
|
|
83
|
+
};
|
|
84
|
+
expect((0, _meetsOneCondition.default)(CONDITION, DATA)).toBeTruthy();
|
|
85
|
+
});
|
|
86
|
+
it('should evaluate to false when no conditions are met', function () {
|
|
87
|
+
var CONDITION = {
|
|
88
|
+
"type": "or",
|
|
89
|
+
"conditions": [{
|
|
90
|
+
field: 'alpha',
|
|
91
|
+
op: 'eq',
|
|
92
|
+
value: 'delta'
|
|
93
|
+
}, {
|
|
94
|
+
field: 'charlie',
|
|
95
|
+
op: 'eq',
|
|
96
|
+
value: 'bravo'
|
|
97
|
+
}]
|
|
98
|
+
};
|
|
99
|
+
expect((0, _meetsOneCondition.default)(CONDITION, DATA)).toBeTruthy();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -17,12 +17,13 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
|
|
|
17
17
|
|
|
18
18
|
var makeComponentFieldsAbsolute = function makeComponentFieldsAbsolute(component) {
|
|
19
19
|
if (component.show_when) {
|
|
20
|
+
var conditions = component.show_when.type ? component.show_when.conditions : component.show_when;
|
|
20
21
|
var full_path = component.full_path;
|
|
21
|
-
|
|
22
|
-
return
|
|
23
|
-
var field = _Data.default.getDataPath(
|
|
22
|
+
conditions = Array.isArray(conditions) ? conditions : [conditions];
|
|
23
|
+
return conditions.map(function (condition) {
|
|
24
|
+
var field = _Data.default.getDataPath(condition.field, full_path);
|
|
24
25
|
|
|
25
|
-
return _objectSpread(_objectSpread({},
|
|
26
|
+
return _objectSpread(_objectSpread({}, condition), {}, {
|
|
26
27
|
field: field
|
|
27
28
|
});
|
|
28
29
|
});
|
|
@@ -41,6 +41,10 @@ var showContainer = function showContainer(container, data) {
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
if (container.show_when) {
|
|
44
|
+
if (container.show_when.type === "or") {
|
|
45
|
+
return _Condition.default.meetsOne(container, data);
|
|
46
|
+
}
|
|
47
|
+
|
|
44
48
|
return _Condition.default.meetsAll(container, data);
|
|
45
49
|
} // If the container itself doesn't have a show_when, we need to make sure that if it
|
|
46
50
|
// contains ANY editable components, at least one of them is shown.
|
|
@@ -125,4 +125,55 @@ describe('utils.Container.showContainer', function () {
|
|
|
125
125
|
};
|
|
126
126
|
expect((0, _showContainer.default)(CONTAINER, DATA)).toBeTruthy();
|
|
127
127
|
});
|
|
128
|
+
it('SHOULD be shown when the container has multiple show_when conditions, with type "or" provided and ALL are matched', function () {
|
|
129
|
+
var CONTAINER = {
|
|
130
|
+
show_when: {
|
|
131
|
+
"type": "or",
|
|
132
|
+
"conditions": [{
|
|
133
|
+
field: 'alpha',
|
|
134
|
+
op: '=',
|
|
135
|
+
value: 'Alpha'
|
|
136
|
+
}, {
|
|
137
|
+
field: 'bravo',
|
|
138
|
+
op: '=',
|
|
139
|
+
value: 'Bravo'
|
|
140
|
+
}]
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
expect((0, _showContainer.default)(CONTAINER, DATA)).toBeTruthy();
|
|
144
|
+
});
|
|
145
|
+
it('SHOULD be shown when the container has multiple show_when conditions, with type "or" provided and at least ONE is matched', function () {
|
|
146
|
+
var CONTAINER = {
|
|
147
|
+
show_when: {
|
|
148
|
+
"type": "or",
|
|
149
|
+
"conditions": [{
|
|
150
|
+
field: 'alpha',
|
|
151
|
+
op: '=',
|
|
152
|
+
value: 'Alpha'
|
|
153
|
+
}, {
|
|
154
|
+
field: 'charlie',
|
|
155
|
+
op: '=',
|
|
156
|
+
value: 'Charlie'
|
|
157
|
+
}]
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
expect((0, _showContainer.default)(CONTAINER, DATA)).toBeTruthy();
|
|
161
|
+
});
|
|
162
|
+
it('should NOT be shown when the container has multiple show_when conditions, with type "or" provided and NONE are matched', function () {
|
|
163
|
+
var CONTAINER = {
|
|
164
|
+
show_when: {
|
|
165
|
+
"type": "or",
|
|
166
|
+
"conditions": [{
|
|
167
|
+
field: 'alpha',
|
|
168
|
+
op: '!=',
|
|
169
|
+
value: 'Alpha'
|
|
170
|
+
}, {
|
|
171
|
+
field: 'charlie',
|
|
172
|
+
op: '=',
|
|
173
|
+
value: 'Charlie'
|
|
174
|
+
}]
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
expect((0, _showContainer.default)(CONTAINER, DATA)).toBeFalsy();
|
|
178
|
+
});
|
|
128
179
|
});
|
|
@@ -5,12 +5,29 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
+
var _copReactComponents = require("@ukhomeoffice/cop-react-components");
|
|
9
|
+
|
|
10
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
11
|
+
|
|
12
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
13
|
+
|
|
14
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
15
|
+
|
|
16
|
+
var interpolateOptions = function interpolateOptions(config, options) {
|
|
17
|
+
return options.map(function (opt) {
|
|
18
|
+
return _objectSpread(_objectSpread({}, opt), {}, {
|
|
19
|
+
value: _copReactComponents.Utils.interpolateString(opt.value, config.formData),
|
|
20
|
+
label: _copReactComponents.Utils.interpolateString(opt.label, config.formData)
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
8
25
|
var getOptions = function getOptions(config, callback) {
|
|
9
26
|
if (config) {
|
|
10
27
|
if (config.options) {
|
|
11
|
-
return callback(config.options);
|
|
28
|
+
return callback(interpolateOptions(config, config.options));
|
|
12
29
|
} else if (config.data && config.data.options) {
|
|
13
|
-
return callback(config.data.options);
|
|
30
|
+
return callback(interpolateOptions(config, config.data.options));
|
|
14
31
|
}
|
|
15
32
|
}
|
|
16
33
|
|
|
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
-
var _copReactComponents = require("@ukhomeoffice/cop-react-components");
|
|
9
|
-
|
|
10
8
|
var _Container = _interopRequireDefault(require("../Container"));
|
|
11
9
|
|
|
12
10
|
var _Data = _interopRequireDefault(require("../Data"));
|
|
@@ -37,8 +35,6 @@ var getFormPage = function getFormPage(pageOptions, formComponents, formData) {
|
|
|
37
35
|
return null;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
pageOptions = interpolatePageOptions(pageOptions, formData);
|
|
41
|
-
formComponents = interpolateFormComponents(formComponents, formData);
|
|
42
38
|
var components = pageOptions.components.map(function (componentOptions) {
|
|
43
39
|
if (typeof componentOptions === 'string') {
|
|
44
40
|
return (0, _getParagraphFromText.default)(componentOptions);
|
|
@@ -61,42 +57,6 @@ var getFormPage = function getFormPage(pageOptions, formComponents, formData) {
|
|
|
61
57
|
actions: actions
|
|
62
58
|
}));
|
|
63
59
|
};
|
|
64
|
-
/**
|
|
65
|
-
* Interpolate 'Variable Expression' using formData - for local use only.
|
|
66
|
-
* @param {object} pageOptions The JSON page.
|
|
67
|
-
* @param {object} formData The top-level form data, used for setting up components.
|
|
68
|
-
* @returns interpolated pageOptions.
|
|
69
|
-
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
var interpolatePageOptions = function interpolatePageOptions(pageOptions, formData) {
|
|
73
|
-
return JSON.parse(_copReactComponents.Utils.interpolateString(JSON.stringify(pageOptions), formData));
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* Interpolate 'Variable Expression' using formData excluding each component's data block - for local use only.
|
|
77
|
-
* @param {Array} formComponents The components defined at the top-level of the form.
|
|
78
|
-
* @param {object} formData The top-level form data, used for setting up components.
|
|
79
|
-
* @returns interpolated formComponents
|
|
80
|
-
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
var interpolateFormComponents = function interpolateFormComponents(formComponents, formData) {
|
|
84
|
-
return formComponents.map(function (formComponent) {
|
|
85
|
-
var formComponentDataUrl = undefined;
|
|
86
|
-
|
|
87
|
-
if (formComponent.data && formComponent.data.url) {
|
|
88
|
-
formComponentDataUrl = formComponent.data.url;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
var interpolatedFormComponent = JSON.parse(_copReactComponents.Utils.interpolateString(JSON.stringify(formComponent), formData));
|
|
92
|
-
|
|
93
|
-
if (formComponentDataUrl) {
|
|
94
|
-
interpolatedFormComponent.data.url = formComponentDataUrl;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return interpolatedFormComponent;
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
60
|
|
|
101
61
|
var _default = getFormPage;
|
|
102
62
|
exports.default = _default;
|
|
@@ -200,74 +200,6 @@ describe('utils', function () {
|
|
|
200
200
|
formData: {}
|
|
201
201
|
});
|
|
202
202
|
});
|
|
203
|
-
it('should interpolate and handle a page that references a form-level component with formData', function () {
|
|
204
|
-
var PAGE = {
|
|
205
|
-
// eslint-disable-next-line no-template-curly-in-string
|
|
206
|
-
title: 'Page ${postFixTitle}',
|
|
207
|
-
components: ["Opening paragraph", {
|
|
208
|
-
type: 'heading',
|
|
209
|
-
size: 'l',
|
|
210
|
-
content: 'Page heading'
|
|
211
|
-
}, "Closing paragraph", {
|
|
212
|
-
use: 'c'
|
|
213
|
-
}, {
|
|
214
|
-
use: 'd'
|
|
215
|
-
}, "Kevin", {
|
|
216
|
-
use: 'e'
|
|
217
|
-
}]
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
var DATA = _objectSpread(_objectSpread({}, FORM_DATA), {}, {
|
|
221
|
-
currentUser: {
|
|
222
|
-
firstname: 'Bob',
|
|
223
|
-
surname: 'Kevin'
|
|
224
|
-
},
|
|
225
|
-
postFixTitle: 'Everyone'
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
var C = FORM_COMPONENTS[2];
|
|
229
|
-
var D = FORM_COMPONENTS[3];
|
|
230
|
-
var E = FORM_COMPONENTS[4];
|
|
231
|
-
expect((0, _getFormPage.default)(PAGE, FORM_COMPONENTS, DATA)).toEqual({
|
|
232
|
-
title: 'Page Everyone',
|
|
233
|
-
components: [{
|
|
234
|
-
type: 'html',
|
|
235
|
-
tagName: 'p',
|
|
236
|
-
content: PAGE.components[0]
|
|
237
|
-
}, _objectSpread(_objectSpread({}, PAGE.components[1]), {}, {
|
|
238
|
-
full_path: PAGE.components[1].fieldId
|
|
239
|
-
}), {
|
|
240
|
-
type: 'html',
|
|
241
|
-
tagName: 'p',
|
|
242
|
-
content: PAGE.components[2]
|
|
243
|
-
}, _objectSpread(_objectSpread({
|
|
244
|
-
use: 'c'
|
|
245
|
-
}, C), {}, {
|
|
246
|
-
cya_label: C.label,
|
|
247
|
-
data: {
|
|
248
|
-
url: "".concat(FORM_DATA.urls.refData, "/v3/charlies")
|
|
249
|
-
},
|
|
250
|
-
full_path: C.fieldId
|
|
251
|
-
}), _objectSpread(_objectSpread({
|
|
252
|
-
use: 'd'
|
|
253
|
-
}, D), {}, {
|
|
254
|
-
label: 'Roger ' + DATA.currentUser.firstname,
|
|
255
|
-
cya_label: 'Roger ' + DATA.currentUser.firstname,
|
|
256
|
-
full_path: D.fieldId
|
|
257
|
-
}), {
|
|
258
|
-
type: 'html',
|
|
259
|
-
tagName: 'p',
|
|
260
|
-
content: PAGE.components[5]
|
|
261
|
-
}, _objectSpread(_objectSpread({
|
|
262
|
-
use: 'e'
|
|
263
|
-
}, E), {}, {
|
|
264
|
-
label: 'Bravo ' + DATA.currentUser.surname,
|
|
265
|
-
cya_label: 'Bravo ' + DATA.currentUser.surname,
|
|
266
|
-
full_path: E.fieldId
|
|
267
|
-
})],
|
|
268
|
-
formData: DATA
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
203
|
});
|
|
272
204
|
});
|
|
273
205
|
});
|
|
@@ -41,6 +41,10 @@ var showFormPage = function showFormPage(page, data) {
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
if (page.show_when) {
|
|
44
|
+
if (page.show_when.type === "or") {
|
|
45
|
+
return _Condition.default.meetsOne(page, data);
|
|
46
|
+
}
|
|
47
|
+
|
|
44
48
|
return _Condition.default.meetsAll(page, data);
|
|
45
49
|
} // If the page itself doesn't have a show_when, we need to make sure that if it
|
|
46
50
|
// contains ANY editable components, at least one of them is shown.
|
|
@@ -126,6 +126,57 @@ describe('utils', function () {
|
|
|
126
126
|
};
|
|
127
127
|
expect((0, _showFormPage.default)(PAGE, DATA)).toBeTruthy();
|
|
128
128
|
});
|
|
129
|
+
it('SHOULD be shown when the page has multiple show_when conditions, with type "or" provided and ALL are matched', function () {
|
|
130
|
+
var PAGE = {
|
|
131
|
+
show_when: {
|
|
132
|
+
"type": "or",
|
|
133
|
+
"conditions": [{
|
|
134
|
+
field: 'alpha',
|
|
135
|
+
op: '=',
|
|
136
|
+
value: 'Alpha'
|
|
137
|
+
}, {
|
|
138
|
+
field: 'bravo',
|
|
139
|
+
op: '=',
|
|
140
|
+
value: 'Bravo'
|
|
141
|
+
}]
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
expect((0, _showFormPage.default)(PAGE, DATA)).toBeTruthy();
|
|
145
|
+
});
|
|
146
|
+
it('SHOULD be shown when the page has multiple show_when conditions, with type "or" provided and at least ONE is matched', function () {
|
|
147
|
+
var PAGE = {
|
|
148
|
+
show_when: {
|
|
149
|
+
"type": "or",
|
|
150
|
+
"conditions": [{
|
|
151
|
+
field: 'alpha',
|
|
152
|
+
op: '=',
|
|
153
|
+
value: 'Alpha'
|
|
154
|
+
}, {
|
|
155
|
+
field: 'charlie',
|
|
156
|
+
op: '=',
|
|
157
|
+
value: 'Charlie'
|
|
158
|
+
}]
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
expect((0, _showFormPage.default)(PAGE, DATA)).toBeTruthy();
|
|
162
|
+
});
|
|
163
|
+
it('should NOT be shown when the page has multiple show_when conditions, with type "or" provided and NONE are matched', function () {
|
|
164
|
+
var PAGE = {
|
|
165
|
+
show_when: {
|
|
166
|
+
"type": "or",
|
|
167
|
+
"conditions": [{
|
|
168
|
+
field: 'alpha',
|
|
169
|
+
op: '!=',
|
|
170
|
+
value: 'Alpha'
|
|
171
|
+
}, {
|
|
172
|
+
field: 'charlie',
|
|
173
|
+
op: '=',
|
|
174
|
+
value: 'Charlie'
|
|
175
|
+
}]
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
expect((0, _showFormPage.default)(PAGE, DATA)).toBeFalsy();
|
|
179
|
+
});
|
|
129
180
|
});
|
|
130
181
|
});
|
|
131
182
|
});
|
|
@@ -5,13 +5,19 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
+
var _copReactComponents = require("@ukhomeoffice/cop-react-components");
|
|
9
|
+
|
|
8
10
|
var _validateComponent = _interopRequireDefault(require("./validateComponent"));
|
|
9
11
|
|
|
10
12
|
var _showFormPage = _interopRequireDefault(require("../FormPage/showFormPage"));
|
|
11
13
|
|
|
12
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
17
|
+
|
|
18
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
19
|
+
|
|
20
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
15
21
|
|
|
16
22
|
/**
|
|
17
23
|
* Validate all of the components on a page.
|
|
@@ -21,7 +27,11 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
21
27
|
var validatePage = function validatePage(page) {
|
|
22
28
|
if ((0, _showFormPage.default)(page, page.formData) && Array.isArray(page.components)) {
|
|
23
29
|
return page.components.reduce(function (errors, component) {
|
|
24
|
-
return errors.concat((0, _validateComponent.default)(component, page.formData, page.formData))
|
|
30
|
+
return errors.concat((0, _validateComponent.default)(component, page.formData, page.formData)).map(function (err) {
|
|
31
|
+
return !!err ? _objectSpread(_objectSpread({}, err), {}, {
|
|
32
|
+
error: _copReactComponents.Utils.interpolateString(err.error, page.formData)
|
|
33
|
+
}) : err;
|
|
34
|
+
});
|
|
25
35
|
}, []).filter(function (e) {
|
|
26
36
|
return !!e;
|
|
27
37
|
}).flat();
|
|
@@ -6,7 +6,12 @@ var _validatePage = _interopRequireDefault(require("./validatePage"));
|
|
|
6
6
|
|
|
7
7
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
10
|
+
|
|
11
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
12
|
+
|
|
13
|
+
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
14
|
+
|
|
10
15
|
describe('utils', function () {
|
|
11
16
|
describe('Validate', function () {
|
|
12
17
|
describe('page', function () {
|
|
@@ -69,6 +74,46 @@ describe('utils', function () {
|
|
|
69
74
|
error: 'Echo is required'
|
|
70
75
|
});
|
|
71
76
|
});
|
|
77
|
+
it('should return an error for each required component with interpolated label', function () {
|
|
78
|
+
var COMPONENTS = [// eslint-disable-next-line no-template-curly-in-string
|
|
79
|
+
setup('a', _models.ComponentTypes.TEXT, 'Alpha ${tiger}', true), // eslint-disable-next-line no-template-curly-in-string
|
|
80
|
+
setup('b', _models.ComponentTypes.EMAIL, 'Bravo ${panther}', true), // eslint-disable-next-line no-template-curly-in-string
|
|
81
|
+
setup('c', _models.ComponentTypes.AUTOCOMPLETE, 'Charlie ${eagle}', true), // eslint-disable-next-line no-template-curly-in-string
|
|
82
|
+
setup('d', _models.ComponentTypes.CHECKBOXES, 'Delta ${lion}', true), // eslint-disable-next-line no-template-curly-in-string
|
|
83
|
+
setup('e', _models.ComponentTypes.FILE, 'Echo ${zoo}', true)];
|
|
84
|
+
var PAGE = {
|
|
85
|
+
components: COMPONENTS,
|
|
86
|
+
formData: {
|
|
87
|
+
tiger: 'Tiger',
|
|
88
|
+
panther: 'Panther',
|
|
89
|
+
eagle: 'Eagle',
|
|
90
|
+
lion: 'Lion',
|
|
91
|
+
zoo: 'Zoo'
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var RESULT = (0, _validatePage.default)(PAGE);
|
|
95
|
+
expect(RESULT.length).toEqual(5);
|
|
96
|
+
expect(RESULT[0]).toEqual({
|
|
97
|
+
id: 'a',
|
|
98
|
+
error: "Alpha ".concat(PAGE.formData.tiger, " is required")
|
|
99
|
+
});
|
|
100
|
+
expect(RESULT[1]).toEqual({
|
|
101
|
+
id: 'b',
|
|
102
|
+
error: "Bravo ".concat(PAGE.formData.panther, " is required")
|
|
103
|
+
});
|
|
104
|
+
expect(RESULT[2]).toEqual({
|
|
105
|
+
id: 'c',
|
|
106
|
+
error: "Charlie ".concat(PAGE.formData.eagle, " is required")
|
|
107
|
+
});
|
|
108
|
+
expect(RESULT[3]).toEqual({
|
|
109
|
+
id: 'd',
|
|
110
|
+
error: "Delta ".concat(PAGE.formData.lion, " is required")
|
|
111
|
+
});
|
|
112
|
+
expect(RESULT[4]).toEqual({
|
|
113
|
+
id: 'e',
|
|
114
|
+
error: "Echo ".concat(PAGE.formData.zoo, " is required")
|
|
115
|
+
});
|
|
116
|
+
});
|
|
72
117
|
});
|
|
73
118
|
describe('when the form data is fully valid', function () {
|
|
74
119
|
var DATA = {
|
|
@@ -164,6 +209,28 @@ describe('utils', function () {
|
|
|
164
209
|
error: 'Charlie is required'
|
|
165
210
|
});
|
|
166
211
|
});
|
|
212
|
+
it('should return an interpolated error for both invalid fields when all are required and email types', function () {
|
|
213
|
+
var COMPONENTS = [setup('alpha', _models.ComponentTypes.EMAIL, 'Alpha', true), // eslint-disable-next-line no-template-curly-in-string
|
|
214
|
+
setup('bravo', _models.ComponentTypes.EMAIL, 'Bravo ${lion}', true), // eslint-disable-next-line no-template-curly-in-string
|
|
215
|
+
setup('charlie', _models.ComponentTypes.EMAIL, 'Charlie ${panther}', true)];
|
|
216
|
+
var PAGE = {
|
|
217
|
+
components: COMPONENTS,
|
|
218
|
+
formData: _objectSpread(_objectSpread({}, DATA), {}, {
|
|
219
|
+
lion: 'Lion',
|
|
220
|
+
panther: 'Panther'
|
|
221
|
+
})
|
|
222
|
+
};
|
|
223
|
+
var RESULT = (0, _validatePage.default)(PAGE);
|
|
224
|
+
expect(RESULT.length).toEqual(2);
|
|
225
|
+
expect(RESULT[0]).toEqual({
|
|
226
|
+
id: 'bravo',
|
|
227
|
+
error: "Enter bravo ".concat(PAGE.formData.lion, " in the correct format, like jane.doe@homeoffice.gov.uk")
|
|
228
|
+
});
|
|
229
|
+
expect(RESULT[1]).toEqual({
|
|
230
|
+
id: 'charlie',
|
|
231
|
+
error: "Charlie ".concat(PAGE.formData.panther, " is required")
|
|
232
|
+
});
|
|
233
|
+
});
|
|
167
234
|
});
|
|
168
235
|
});
|
|
169
236
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ukhomeoffice/cop-react-form-renderer",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.4",
|
|
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": "1.7.
|
|
19
|
+
"@ukhomeoffice/cop-react-components": "1.7.4",
|
|
20
20
|
"axios": "^0.21.1",
|
|
21
21
|
"dayjs": "^1.11.0",
|
|
22
22
|
"govuk-frontend": "^3.13.0",
|