@ukhomeoffice/cop-react-form-renderer 0.1.0-beta → 0.1.0-gamma
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CheckYourAnswers/Answer.js +48 -0
- package/dist/components/CheckYourAnswers/CheckYourAnswers.js +28 -9
- package/dist/components/CheckYourAnswers/CheckYourAnswers.stories.mdx +23 -0
- package/dist/components/FormComponent/FormComponent.js +7 -7
- package/dist/components/FormComponent/FormComponent.stories.mdx +167 -0
- package/dist/components/FormComponent/FormComponent.test.js +137 -0
- package/dist/components/FormPage/FormPage.js +12 -11
- package/dist/components/FormPage/FormPage.stories.mdx +118 -0
- package/dist/components/FormPage/FormPage.test.js +253 -0
- package/dist/components/FormRenderer/FormRenderer.js +33 -18
- package/dist/components/FormRenderer/FormRenderer.stories.mdx +87 -0
- package/dist/components/FormRenderer/helpers/canActionProceed.js +35 -0
- package/dist/components/FormRenderer/helpers/canActionProceed.test.js +73 -0
- package/dist/components/FormRenderer/helpers/getCYA.js +31 -0
- package/dist/components/FormRenderer/helpers/getCYA.test.js +38 -0
- package/dist/components/FormRenderer/helpers/getFormState.js +32 -0
- package/dist/components/FormRenderer/helpers/getFormState.test.js +61 -0
- package/dist/components/FormRenderer/helpers/getPage.js +34 -0
- package/dist/components/FormRenderer/helpers/getPage.test.js +46 -0
- package/dist/components/FormRenderer/helpers/index.js +23 -0
- package/dist/components/PageActions/ActionButton.js +62 -0
- package/dist/components/PageActions/ActionButton.test.js +114 -0
- package/dist/components/PageActions/PageActions.js +18 -25
- package/dist/components/PageActions/PageActions.stories.mdx +74 -0
- package/dist/components/PageActions/PageActions.test.js +155 -0
- package/dist/components/SummaryList/RowAction.js +13 -22
- package/dist/components/SummaryList/RowAction.test.js +104 -0
- package/dist/components/SummaryList/SummaryList.js +3 -17
- package/dist/components/SummaryList/helpers/getRowActionAttributes.js +27 -0
- package/dist/components/SummaryList/helpers/getRowActionAttributes.test.js +77 -0
- package/dist/components/SummaryList/helpers/index.js +15 -0
- package/dist/components/index.js +8 -0
- package/dist/hooks/useGetRequest.js +16 -1
- package/dist/hooks/useHooks.js +15 -1
- package/dist/utils/Component/getComponent.js +2 -2
- package/dist/utils/Data/setupFormData.js +3 -3
- package/dist/utils/FormPage/getFormPage.js +11 -0
- package/dist/utils/FormPage/getFormPage.test.js +178 -0
- package/dist/utils/FormPage/getFormPages.js +11 -6
- package/dist/utils/FormPage/getFormPages.test.js +95 -0
- package/dist/utils/FormPage/getParagraphFromText.js +6 -0
- package/dist/utils/FormPage/getParagraphFromText.test.js +29 -0
- package/dist/utils/FormPage/useComponent.js +9 -2
- package/dist/utils/FormPage/useComponent.test.js +82 -0
- package/dist/utils/Hub/getFormHub.js +4 -4
- package/dist/utils/Hub/getFormHub.test.js +96 -0
- package/dist/utils/Hub/index.js +0 -3
- package/package.json +1 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
|
4
|
+
|
|
5
|
+
var _react = require("@testing-library/react");
|
|
6
|
+
|
|
7
|
+
var _react2 = _interopRequireDefault(require("react"));
|
|
8
|
+
|
|
9
|
+
var _ActionButton = require("../PageActions/ActionButton");
|
|
10
|
+
|
|
11
|
+
var _FormPage = _interopRequireWildcard(require("./FormPage"));
|
|
12
|
+
|
|
13
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
14
|
+
|
|
15
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
16
|
+
|
|
17
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
|
+
|
|
19
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
20
|
+
|
|
21
|
+
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); }); }; }
|
|
22
|
+
|
|
23
|
+
describe('components', function () {
|
|
24
|
+
describe('FormPage', function () {
|
|
25
|
+
var TEXT = {
|
|
26
|
+
id: 'text',
|
|
27
|
+
fieldId: 'text',
|
|
28
|
+
type: 'text',
|
|
29
|
+
label: 'Text component',
|
|
30
|
+
hint: 'Text hint'
|
|
31
|
+
};
|
|
32
|
+
var VALUE = 'Text value';
|
|
33
|
+
var PAGE = {
|
|
34
|
+
id: 'pageId',
|
|
35
|
+
title: 'Page 1',
|
|
36
|
+
components: [TEXT],
|
|
37
|
+
actions: ['submit'],
|
|
38
|
+
formData: {
|
|
39
|
+
text: VALUE
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var ON_ACTION_CALLS = [];
|
|
43
|
+
|
|
44
|
+
var ON_ACTION = function ON_ACTION(action, patch, onError) {
|
|
45
|
+
ON_ACTION_CALLS.push({
|
|
46
|
+
action: action,
|
|
47
|
+
patch: patch,
|
|
48
|
+
onError: onError
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
beforeEach(function () {
|
|
53
|
+
PAGE.formData = {
|
|
54
|
+
text: VALUE
|
|
55
|
+
};
|
|
56
|
+
ON_ACTION_CALLS.length = 0;
|
|
57
|
+
});
|
|
58
|
+
it('should render a submit page correctly', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
|
|
59
|
+
var _render, container, page, heading, formGroup, label, hint, input, buttonGroup, button;
|
|
60
|
+
|
|
61
|
+
return regeneratorRuntime.wrap(function _callee$(_context) {
|
|
62
|
+
while (1) {
|
|
63
|
+
switch (_context.prev = _context.next) {
|
|
64
|
+
case 0:
|
|
65
|
+
_render = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
66
|
+
page: PAGE,
|
|
67
|
+
onAction: ON_ACTION
|
|
68
|
+
})), container = _render.container;
|
|
69
|
+
page = container.childNodes[0];
|
|
70
|
+
expect(page.tagName).toEqual('DIV');
|
|
71
|
+
expect(page.classList).toContain("".concat(_FormPage.DEFAULT_CLASS, "__page"));
|
|
72
|
+
heading = page.childNodes[0];
|
|
73
|
+
expect(heading.classList).toContain('govuk-heading-l');
|
|
74
|
+
expect(heading.textContent).toEqual(PAGE.title);
|
|
75
|
+
formGroup = page.childNodes[1];
|
|
76
|
+
expect(formGroup.tagName).toEqual('DIV');
|
|
77
|
+
expect(formGroup.classList).toContain('govuk-form-group');
|
|
78
|
+
label = formGroup.childNodes[0];
|
|
79
|
+
expect(label.tagName).toEqual('LABEL');
|
|
80
|
+
expect(label.classList).toContain('govuk-label');
|
|
81
|
+
expect(label.textContent).toEqual("".concat(TEXT.label, " (optional)"));
|
|
82
|
+
expect(label.getAttribute('for')).toEqual(TEXT.fieldId);
|
|
83
|
+
hint = formGroup.childNodes[1];
|
|
84
|
+
expect(hint.tagName).toEqual('SPAN');
|
|
85
|
+
expect(hint.classList).toContain('govuk-hint');
|
|
86
|
+
expect(hint.textContent).toEqual(TEXT.hint);
|
|
87
|
+
input = formGroup.childNodes[2];
|
|
88
|
+
expect(input.tagName).toEqual('INPUT');
|
|
89
|
+
expect(input.classList).toContain('govuk-input');
|
|
90
|
+
expect(input.id).toEqual(TEXT.fieldId);
|
|
91
|
+
expect(input.value).toEqual(VALUE);
|
|
92
|
+
buttonGroup = page.childNodes[2];
|
|
93
|
+
expect(buttonGroup.tagName).toEqual('DIV');
|
|
94
|
+
expect(buttonGroup.classList).toContain('govuk-button-group');
|
|
95
|
+
button = buttonGroup.childNodes[0];
|
|
96
|
+
expect(button.tagName).toEqual('BUTTON');
|
|
97
|
+
expect(button.classList).toContain('govuk-button');
|
|
98
|
+
expect(button.textContent).toEqual(_ActionButton.DEFAULT_LABEL);
|
|
99
|
+
|
|
100
|
+
case 31:
|
|
101
|
+
case "end":
|
|
102
|
+
return _context.stop();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}, _callee);
|
|
106
|
+
})));
|
|
107
|
+
it('should handle a page change appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
|
|
108
|
+
var _render2, container, page, input, NEW_VALUE, EVENT;
|
|
109
|
+
|
|
110
|
+
return regeneratorRuntime.wrap(function _callee2$(_context2) {
|
|
111
|
+
while (1) {
|
|
112
|
+
switch (_context2.prev = _context2.next) {
|
|
113
|
+
case 0:
|
|
114
|
+
_render2 = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
115
|
+
page: PAGE,
|
|
116
|
+
onAction: ON_ACTION
|
|
117
|
+
})), container = _render2.container;
|
|
118
|
+
page = container.childNodes[0];
|
|
119
|
+
expect(page.tagName).toEqual('DIV'); // Change the input.
|
|
120
|
+
|
|
121
|
+
input = page.childNodes[1].childNodes[2];
|
|
122
|
+
NEW_VALUE = "".concat(VALUE, ".");
|
|
123
|
+
EVENT = {
|
|
124
|
+
target: {
|
|
125
|
+
name: TEXT.fieldId,
|
|
126
|
+
value: NEW_VALUE
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
_react.fireEvent.change(input, EVENT); // And confirm the formData has been changed.
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
expect(PAGE.formData.text).toEqual(NEW_VALUE);
|
|
134
|
+
|
|
135
|
+
case 8:
|
|
136
|
+
case "end":
|
|
137
|
+
return _context2.stop();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}, _callee2);
|
|
141
|
+
})));
|
|
142
|
+
it('should handle a page action appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee3() {
|
|
143
|
+
var _render3, container, page, input, NEW_VALUE, CHANGE_EVENT, button;
|
|
144
|
+
|
|
145
|
+
return regeneratorRuntime.wrap(function _callee3$(_context3) {
|
|
146
|
+
while (1) {
|
|
147
|
+
switch (_context3.prev = _context3.next) {
|
|
148
|
+
case 0:
|
|
149
|
+
_render3 = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
150
|
+
page: PAGE,
|
|
151
|
+
onAction: ON_ACTION
|
|
152
|
+
})), container = _render3.container;
|
|
153
|
+
page = container.childNodes[0]; // Change the input.
|
|
154
|
+
|
|
155
|
+
input = page.childNodes[1].childNodes[2];
|
|
156
|
+
NEW_VALUE = "".concat(VALUE, ".");
|
|
157
|
+
CHANGE_EVENT = {
|
|
158
|
+
target: {
|
|
159
|
+
name: TEXT.fieldId,
|
|
160
|
+
value: NEW_VALUE
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
_react.fireEvent.change(input, CHANGE_EVENT); // Then click the action button.
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
button = page.childNodes[2].childNodes[0];
|
|
168
|
+
|
|
169
|
+
_react.fireEvent.click(button, {}); // And confirm an appropriate action was received.
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
expect(ON_ACTION_CALLS.length).toEqual(1);
|
|
173
|
+
expect(PAGE.formData.text).toEqual(NEW_VALUE);
|
|
174
|
+
expect(ON_ACTION_CALLS[0].action).toEqual(_ActionButton.DEFAULT_ACTIONS.submit);
|
|
175
|
+
expect(ON_ACTION_CALLS[0].patch).toEqual({
|
|
176
|
+
text: NEW_VALUE
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
case 12:
|
|
180
|
+
case "end":
|
|
181
|
+
return _context3.stop();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}, _callee3);
|
|
185
|
+
})));
|
|
186
|
+
it('should display errors appropriately', /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee4() {
|
|
187
|
+
var ERRORS, THROW_ERROR_ON_ACTION, _render4, container, page, input, NEW_VALUE, CHANGE_EVENT, button, errorSummary, errorList, error, errorLink;
|
|
188
|
+
|
|
189
|
+
return regeneratorRuntime.wrap(function _callee4$(_context4) {
|
|
190
|
+
while (1) {
|
|
191
|
+
switch (_context4.prev = _context4.next) {
|
|
192
|
+
case 0:
|
|
193
|
+
ERRORS = [{
|
|
194
|
+
id: TEXT.id,
|
|
195
|
+
error: 'Invalid value'
|
|
196
|
+
}];
|
|
197
|
+
|
|
198
|
+
THROW_ERROR_ON_ACTION = function THROW_ERROR_ON_ACTION(action, patch, onError) {
|
|
199
|
+
ON_ACTION_CALLS.push({
|
|
200
|
+
action: action,
|
|
201
|
+
patch: patch,
|
|
202
|
+
onError: onError
|
|
203
|
+
});
|
|
204
|
+
onError(ERRORS);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
_render4 = (0, _react.render)( /*#__PURE__*/_react2.default.createElement(_FormPage.default, {
|
|
208
|
+
page: PAGE,
|
|
209
|
+
onAction: THROW_ERROR_ON_ACTION
|
|
210
|
+
})), container = _render4.container;
|
|
211
|
+
page = container.childNodes[0]; // Change the input.
|
|
212
|
+
|
|
213
|
+
input = page.childNodes[1].childNodes[2];
|
|
214
|
+
NEW_VALUE = "".concat(VALUE, ".");
|
|
215
|
+
CHANGE_EVENT = {
|
|
216
|
+
target: {
|
|
217
|
+
name: TEXT.fieldId,
|
|
218
|
+
value: NEW_VALUE
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
_react.fireEvent.change(input, CHANGE_EVENT); // Then click the action button, which should call the onError callback method.
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
button = page.childNodes[2].childNodes[0];
|
|
226
|
+
|
|
227
|
+
_react.fireEvent.click(button, {}); // And confirm the page error is now displayed.
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
errorSummary = page.childNodes[1];
|
|
231
|
+
expect(errorSummary.tagName).toEqual('DIV');
|
|
232
|
+
expect(errorSummary.id).toEqual('error-summary');
|
|
233
|
+
expect(errorSummary.classList).toContain('govuk-error-summary');
|
|
234
|
+
errorList = errorSummary.childNodes[1].childNodes[0];
|
|
235
|
+
expect(errorList.tagName).toEqual('UL');
|
|
236
|
+
expect(errorList.classList).toContain('govuk-error-summary__list');
|
|
237
|
+
expect(errorList.childNodes.length).toEqual(ERRORS.length);
|
|
238
|
+
error = errorList.childNodes[0];
|
|
239
|
+
expect(error.tagName).toEqual('LI');
|
|
240
|
+
errorLink = error.childNodes[0];
|
|
241
|
+
expect(errorLink.tagName).toEqual('A');
|
|
242
|
+
expect(errorLink.textContent).toEqual(ERRORS[0].error);
|
|
243
|
+
expect(errorLink.getAttribute('href')).toEqual("#".concat(ERRORS[0].id));
|
|
244
|
+
|
|
245
|
+
case 24:
|
|
246
|
+
case "end":
|
|
247
|
+
return _context4.stop();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}, _callee4);
|
|
251
|
+
})));
|
|
252
|
+
});
|
|
253
|
+
});
|
|
@@ -13,7 +13,7 @@ var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
|
13
13
|
|
|
14
14
|
var _react = _interopRequireWildcard(require("react"));
|
|
15
15
|
|
|
16
|
-
var
|
|
16
|
+
var _hooks2 = require("../../hooks");
|
|
17
17
|
|
|
18
18
|
var _models = require("../../models");
|
|
19
19
|
|
|
@@ -63,7 +63,7 @@ var FormRenderer = function FormRenderer(_ref) {
|
|
|
63
63
|
_hub = _ref.hub,
|
|
64
64
|
_cya = _ref.cya,
|
|
65
65
|
_data = _ref.data,
|
|
66
|
-
|
|
66
|
+
_hooks = _ref.hooks,
|
|
67
67
|
classBlock = _ref.classBlock,
|
|
68
68
|
classModifiers = _ref.classModifiers,
|
|
69
69
|
className = _ref.className;
|
|
@@ -100,16 +100,17 @@ var FormRenderer = function FormRenderer(_ref) {
|
|
|
100
100
|
setFormState = _useState12[1]; // Set up hooks.
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
var _useHooks = (0,
|
|
103
|
+
var _useHooks = (0, _hooks2.useHooks)(),
|
|
104
|
+
hooks = _useHooks.hooks,
|
|
104
105
|
addHook = _useHooks.addHook;
|
|
105
106
|
|
|
106
107
|
(0, _react.useEffect)(function () {
|
|
107
|
-
if (
|
|
108
|
-
Object.keys(
|
|
109
|
-
addHook(key,
|
|
108
|
+
if (_hooks) {
|
|
109
|
+
Object.keys(_hooks).forEach(function (key) {
|
|
110
|
+
addHook(key, _hooks[key]);
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
|
-
}, [
|
|
113
|
+
}, [_hooks, addHook]); // Setup data.
|
|
113
114
|
|
|
114
115
|
(0, _react.useEffect)(function () {
|
|
115
116
|
setData(_utils.default.Data.setupForm(_pages, components, _data));
|
|
@@ -120,31 +121,44 @@ var FormRenderer = function FormRenderer(_ref) {
|
|
|
120
121
|
}, [components, _pages, data, redoPages, setPages]); // Setup hub.
|
|
121
122
|
|
|
122
123
|
(0, _react.useEffect)(function () {
|
|
123
|
-
setHub(_utils.default.Hub.get(type, _hub, components));
|
|
124
|
-
}, [type, _hub, components, setHub]); // Form state.
|
|
124
|
+
setHub(_utils.default.Hub.get(type, _hub, components, _objectSpread({}, data)));
|
|
125
|
+
}, [type, _hub, data, components, setHub]); // Form state.
|
|
125
126
|
|
|
126
127
|
(0, _react.useEffect)(function () {
|
|
127
128
|
setFormState((0, _helpers.getFormState)(pageId, pages, hub));
|
|
128
|
-
}, [pages, hub, pageId, setFormState]); //
|
|
129
|
+
}, [pages, hub, pageId, setFormState]); // Call the onFormLoad hook just when this component first renders.
|
|
130
|
+
|
|
131
|
+
(0, _react.useEffect)(function () {
|
|
132
|
+
hooks.onFormLoad();
|
|
133
|
+
}, [hooks]); // Handle actions from pages.
|
|
129
134
|
|
|
130
135
|
var onAction = function onAction(action, patch, onError) {
|
|
131
136
|
// First check the validity of the
|
|
132
137
|
if ((0, _helpers.canActionProceed)(action, formState.page, onError)) {
|
|
133
138
|
if (action.type === 'navigate') {
|
|
134
|
-
|
|
139
|
+
var newPageId = action.url.replace('/', '');
|
|
140
|
+
setPageId(newPageId);
|
|
141
|
+
hooks.onPageChange(newPageId);
|
|
135
142
|
} else if (action.type === 'cancel') {
|
|
136
143
|
setRedoPages(Date.now);
|
|
137
144
|
setPageId('hub');
|
|
145
|
+
hooks.onPageChange('hub');
|
|
138
146
|
} else {
|
|
139
147
|
// Submit.
|
|
140
148
|
if (patch) {
|
|
141
149
|
setData(function (prev) {
|
|
142
150
|
return _objectSpread(_objectSpread({}, prev), patch);
|
|
143
151
|
});
|
|
144
|
-
} // Now submit the data to the backend
|
|
152
|
+
} // Now submit the data to the backend...
|
|
145
153
|
|
|
146
154
|
|
|
147
|
-
|
|
155
|
+
hooks.onSubmit(action.type, _objectSpread(_objectSpread({}, data), patch), function () {
|
|
156
|
+
setPageId('hub');
|
|
157
|
+
hooks.onPageChange('hub');
|
|
158
|
+
}, function (errors) {
|
|
159
|
+
// Handle the errors.
|
|
160
|
+
console.error('Submission errors', errors);
|
|
161
|
+
});
|
|
148
162
|
}
|
|
149
163
|
}
|
|
150
164
|
}; // Handle navigation from "Check your answers".
|
|
@@ -152,12 +166,14 @@ var FormRenderer = function FormRenderer(_ref) {
|
|
|
152
166
|
|
|
153
167
|
var onCheckYourAnswerChange = function onCheckYourAnswerChange(page) {
|
|
154
168
|
var action = page.action;
|
|
169
|
+
var newPageId = page.pageId;
|
|
155
170
|
|
|
156
171
|
if (action && action.href) {
|
|
157
|
-
|
|
158
|
-
} else {
|
|
159
|
-
setPageId(page.pageId);
|
|
172
|
+
newPageId = action.href.replace('/', '');
|
|
160
173
|
}
|
|
174
|
+
|
|
175
|
+
setPageId(newPageId);
|
|
176
|
+
hooks.onPageChange(newPageId);
|
|
161
177
|
};
|
|
162
178
|
|
|
163
179
|
var classes = _utils.default.classBuilder(classBlock, classModifiers, className);
|
|
@@ -170,8 +186,7 @@ var FormRenderer = function FormRenderer(_ref) {
|
|
|
170
186
|
onAction: onCheckYourAnswerChange
|
|
171
187
|
})), formState.page && /*#__PURE__*/_react.default.createElement(_FormPage.default, {
|
|
172
188
|
page: formState.page,
|
|
173
|
-
onAction: onAction
|
|
174
|
-
classes: classes
|
|
189
|
+
onAction: onAction
|
|
175
190
|
}));
|
|
176
191
|
};
|
|
177
192
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<!-- Global imports -->
|
|
2
|
+
import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
|
|
3
|
+
import { Details, Heading, Link } from '@ukhomeoffice/cop-react-components';
|
|
4
|
+
import withMock from 'storybook-addon-mock';
|
|
5
|
+
|
|
6
|
+
<!-- Local imports -->
|
|
7
|
+
import { addHook } from '../../hooks/useHooks';
|
|
8
|
+
import FormRenderer from './FormRenderer';
|
|
9
|
+
|
|
10
|
+
<!-- JSON documents -->
|
|
11
|
+
import CIVIL_SERVANT from '../../json/areYouACivilServant.json';
|
|
12
|
+
import GRADE from '../../json/grade.json';
|
|
13
|
+
import TEAMS from '../../json/team.json';
|
|
14
|
+
import USER_PROFILE_DATA from '../../json/userProfile.data.json';
|
|
15
|
+
import USER_PROFILE from '../../json/userProfile.json';
|
|
16
|
+
import SAVE_AND_CONTINUE from '../../json/saveAndContinue.json';
|
|
17
|
+
|
|
18
|
+
<Meta title="Components/Form renderer" id="D-FormRenderer" component={ FormRenderer } decorators={[withMock]} />
|
|
19
|
+
|
|
20
|
+
<Heading size="xl" caption="Components">Form renderer</Heading>
|
|
21
|
+
|
|
22
|
+
Renders a form with <Link href="https://ukhomeoffice.github.io/cop-react-components/?path=/docs/d-alert--default-story">COP React components</Link>,
|
|
23
|
+
on the basis of a <Link href="/?path=/docs/f-json-form">JSON</Link> that describes which elements are required.
|
|
24
|
+
|
|
25
|
+
<Canvas withToolbar>
|
|
26
|
+
<Story name="Default" parameters={{
|
|
27
|
+
mockData: [
|
|
28
|
+
{
|
|
29
|
+
url: `${USER_PROFILE_DATA.environmentContext.referenceDataUrl}/v2/entities/areYouACivilServant`,
|
|
30
|
+
method: 'GET',
|
|
31
|
+
status: 200,
|
|
32
|
+
response: CIVIL_SERVANT
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
url: `${USER_PROFILE_DATA.environmentContext.referenceDataUrl}/v2/entities/grade`,
|
|
36
|
+
method: 'GET',
|
|
37
|
+
status: 200,
|
|
38
|
+
response: GRADE
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
url: `${USER_PROFILE_DATA.environmentContext.referenceDataUrl}/v1/entities/team`,
|
|
42
|
+
method: 'GET',
|
|
43
|
+
status: 200,
|
|
44
|
+
response: TEAMS
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}}>
|
|
48
|
+
{() => {
|
|
49
|
+
return (
|
|
50
|
+
<FormRenderer {...USER_PROFILE} data={USER_PROFILE_DATA} />
|
|
51
|
+
);
|
|
52
|
+
}}
|
|
53
|
+
</Story>
|
|
54
|
+
</Canvas>
|
|
55
|
+
|
|
56
|
+
<Details summary="Properties" className="no-indent">
|
|
57
|
+
<ArgsTable of={ FormRenderer } />
|
|
58
|
+
</Details>
|
|
59
|
+
|
|
60
|
+
## Custom hooks
|
|
61
|
+
|
|
62
|
+
<Canvas>
|
|
63
|
+
<Story name="Save and continue">
|
|
64
|
+
{() => {
|
|
65
|
+
addHook('onRequest', (req) => {
|
|
66
|
+
// Do whatever you need to do to the request in here, such as adding an Authorization header.
|
|
67
|
+
console.log('onRequest hook called');
|
|
68
|
+
return req;
|
|
69
|
+
});
|
|
70
|
+
addHook('onFormLoad', () => {
|
|
71
|
+
console.log('onFormLoad called');
|
|
72
|
+
});
|
|
73
|
+
addHook('onPageChange', (pageId) => {
|
|
74
|
+
console.log('onPageChange called', pageId);
|
|
75
|
+
});
|
|
76
|
+
// In whatever consumes this, do it as:
|
|
77
|
+
// intercepts.add('onSubmit', (...) => { ... })
|
|
78
|
+
addHook('onSubmit', (type, payload, onSuccess, onError) => {
|
|
79
|
+
console.log('onSubmit called of type', type, 'called, with the payload', payload);
|
|
80
|
+
onSuccess();
|
|
81
|
+
});
|
|
82
|
+
return (
|
|
83
|
+
<FormRenderer {...SAVE_AND_CONTINUE} data={{}} />
|
|
84
|
+
);
|
|
85
|
+
}}
|
|
86
|
+
</Story>
|
|
87
|
+
</Canvas>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = _interopRequireDefault(require("../../../utils"));
|
|
9
|
+
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
|
+
|
|
12
|
+
// Local imports
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Assesses whether the action can proceed when navigating, submitting, etc.
|
|
16
|
+
* Presently, this will validate a page when submitting and, only if the page is
|
|
17
|
+
* valid will it proceed. If it's simple navigation, it simply returns true.
|
|
18
|
+
* @param {object} action The action object we're assessing.
|
|
19
|
+
* @param {object} page The page configuration object this action relates to.
|
|
20
|
+
* @param {Function} onError A function to call with any validation errors.
|
|
21
|
+
* @returns A boolean where `true` means the action can proceed and `false` means it cannot.
|
|
22
|
+
*/
|
|
23
|
+
var canActionProceed = function canActionProceed(action, page, onError) {
|
|
24
|
+
if (action.validate) {
|
|
25
|
+
var errors = _utils.default.Validate.page(page.components, page.formData);
|
|
26
|
+
|
|
27
|
+
onError(errors);
|
|
28
|
+
return errors.length === 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
var _default = canActionProceed;
|
|
35
|
+
exports.default = _default;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _canActionProceed = _interopRequireDefault(require("./canActionProceed"));
|
|
4
|
+
|
|
5
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
6
|
+
|
|
7
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
8
|
+
|
|
9
|
+
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."); }
|
|
10
|
+
|
|
11
|
+
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); }
|
|
12
|
+
|
|
13
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
14
|
+
|
|
15
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
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
|
+
describe('components', function () {
|
|
20
|
+
describe('FormRenderer', function () {
|
|
21
|
+
describe('helpers', function () {
|
|
22
|
+
describe('canActionProceed', function () {
|
|
23
|
+
it('should return true when the action does not require validation', function () {
|
|
24
|
+
var ACTION = {
|
|
25
|
+
validate: false
|
|
26
|
+
};
|
|
27
|
+
expect((0, _canActionProceed.default)(ACTION, {}, function () {})).toBeTruthy();
|
|
28
|
+
});
|
|
29
|
+
it('should return true when the page is valid', function () {
|
|
30
|
+
var ACTION = {
|
|
31
|
+
validate: true
|
|
32
|
+
};
|
|
33
|
+
var PAGE = {
|
|
34
|
+
components: [{
|
|
35
|
+
id: 'a',
|
|
36
|
+
fieldId: 'a',
|
|
37
|
+
label: 'Alpha',
|
|
38
|
+
required: true
|
|
39
|
+
}],
|
|
40
|
+
formData: {
|
|
41
|
+
a: 'Bravo'
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
expect((0, _canActionProceed.default)(ACTION, PAGE, function () {})).toBeTruthy();
|
|
45
|
+
});
|
|
46
|
+
it('should return false when the page is invalid and should have called the onErrors method appropriately', function () {
|
|
47
|
+
var ACTION = {
|
|
48
|
+
validate: true
|
|
49
|
+
};
|
|
50
|
+
var PAGE = {
|
|
51
|
+
components: [{
|
|
52
|
+
id: 'a',
|
|
53
|
+
fieldId: 'a',
|
|
54
|
+
label: 'Alpha',
|
|
55
|
+
required: true
|
|
56
|
+
}],
|
|
57
|
+
formData: {}
|
|
58
|
+
};
|
|
59
|
+
var ERRORS = [];
|
|
60
|
+
|
|
61
|
+
var ON_ERROR = function ON_ERROR(errors) {
|
|
62
|
+
ERRORS.push.apply(ERRORS, _toConsumableArray(errors));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
expect((0, _canActionProceed.default)(ACTION, PAGE, ON_ERROR)).toBeFalsy();
|
|
66
|
+
expect(ERRORS.length).toEqual(1);
|
|
67
|
+
expect(ERRORS[0].id).toEqual('a');
|
|
68
|
+
expect(ERRORS[0].error).toEqual('Alpha is required');
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _models = require("../../../models");
|
|
9
|
+
|
|
10
|
+
// Local imports
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Gets a configuration object for the Check your answers screen.
|
|
14
|
+
* @param {string} pageId The current page identifier.
|
|
15
|
+
* @param {object} hub The hub, if there is one.
|
|
16
|
+
* @returns A configuration object for the Check your answers screen.
|
|
17
|
+
*/
|
|
18
|
+
var getCYA = function getCYA(pageId, hub) {
|
|
19
|
+
if (pageId === 'hub' && hub === _models.HubFormats.CYA) {
|
|
20
|
+
return {
|
|
21
|
+
title: ''
|
|
22
|
+
};
|
|
23
|
+
} else if (pageId === _models.HubFormats.CYA) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return undefined;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
var _default = getCYA;
|
|
31
|
+
exports.default = _default;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _models = require("../../../models");
|
|
4
|
+
|
|
5
|
+
var _getCYA = _interopRequireDefault(require("./getCYA"));
|
|
6
|
+
|
|
7
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
8
|
+
|
|
9
|
+
// Local imports
|
|
10
|
+
describe('components', function () {
|
|
11
|
+
describe('FormRenderer', function () {
|
|
12
|
+
describe('helpers', function () {
|
|
13
|
+
var HUB = {
|
|
14
|
+
id: 'hub',
|
|
15
|
+
components: []
|
|
16
|
+
};
|
|
17
|
+
describe('getCYA', function () {
|
|
18
|
+
it('should give an empty object if the pageId is "CYA"', function () {
|
|
19
|
+
expect((0, _getCYA.default)(_models.HubFormats.CYA)).toEqual({});
|
|
20
|
+
});
|
|
21
|
+
it('should give an object with a blank title if the pageId is "hub" and the hub is the CYA', function () {
|
|
22
|
+
expect((0, _getCYA.default)('hub', _models.HubFormats.CYA)).toEqual({
|
|
23
|
+
title: ''
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
it('should give undefined if pageId is "hub" and the hub is NOT the CYA', function () {
|
|
27
|
+
expect((0, _getCYA.default)('hub', HUB)).toBeUndefined();
|
|
28
|
+
});
|
|
29
|
+
it('should give undefined if pageId is NOT "hub" and the hub is NOT the CYA', function () {
|
|
30
|
+
expect((0, _getCYA.default)('bob', HUB)).toBeUndefined();
|
|
31
|
+
});
|
|
32
|
+
it('should give undefined if pageId is NOT "hub" and the hub is the CYA', function () {
|
|
33
|
+
expect((0, _getCYA.default)('bob', _models.HubFormats.CYA)).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _getCYA = _interopRequireDefault(require("./getCYA"));
|
|
9
|
+
|
|
10
|
+
var _getPage = _interopRequireDefault(require("./getPage"));
|
|
11
|
+
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
|
|
14
|
+
// Local imports
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Sets up the state of the form, including the current page and the Check your answers screen.
|
|
18
|
+
* @param {string} pageId The current page identifier.
|
|
19
|
+
* @param {Array} pages All of the pages in the form.
|
|
20
|
+
* @param {object} hub The hub, if there is one.
|
|
21
|
+
* @returns The current state of the form.
|
|
22
|
+
*/
|
|
23
|
+
var getFormState = function getFormState(pageId, pages, hub) {
|
|
24
|
+
return {
|
|
25
|
+
pageId: pageId,
|
|
26
|
+
cya: (0, _getCYA.default)(pageId, hub),
|
|
27
|
+
page: (0, _getPage.default)(pageId, pages, hub)
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
var _default = getFormState;
|
|
32
|
+
exports.default = _default;
|