hof 21.0.0-instrumentation-beta.0 → 21.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.github/workflows/automate-publish.yml +1 -1
- package/.github/workflows/automate-tag.yml +4 -4
- package/.nyc_output/39365c14-40b7-4634-b733-940b72a11984.json +1 -0
- package/.nyc_output/processinfo/39365c14-40b7-4634-b733-940b72a11984.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -1
- package/CHANGELOG.md +21 -0
- package/README.md +340 -256
- package/build/tasks/sass/index.js +3 -1
- package/build/tasks/watch/index.js +1 -1
- package/codeReviewChecklist.md +22 -0
- package/components/combine-and-loop-fields/Readme.md +42 -0
- package/components/combine-and-loop-fields/index.js +156 -0
- package/components/date/index.js +3 -1
- package/components/date/templates/date.html +15 -12
- package/components/homeoffice-countries/index.js +22 -0
- package/components/index.js +2 -0
- package/components/notify/notify.js +2 -2
- package/components/summary/index.js +3 -2
- package/config/builder-defaults.js +3 -1
- package/config/component-defaults.js +13 -0
- package/config/hof-defaults.js +8 -0
- package/controller/controller.js +57 -1
- package/controller/formatting/formatters.js +12 -0
- package/controller/validation/index.js +2 -1
- package/controller/validation/validators.js +4 -0
- package/frontend/govuk-template/build/config.js +2 -2
- package/frontend/govuk-template/build/govuk_template.html +102 -0
- package/frontend/govuk-template/build/index.js +2 -2
- package/frontend/govuk-template/govuk_template_generated.html +102 -0
- package/frontend/govuk-template/index.js +4 -4
- package/frontend/template-mixins/mixins/template-mixins.js +40 -11
- package/frontend/template-mixins/partials/forms/checkbox-group.html +47 -0
- package/frontend/template-mixins/partials/forms/checkbox.html +9 -4
- package/frontend/template-mixins/partials/forms/input-submit.html +1 -1
- package/frontend/template-mixins/partials/forms/input-text-date.html +37 -0
- package/frontend/template-mixins/partials/forms/input-text-group.html +15 -10
- package/frontend/template-mixins/partials/forms/option-group.html +42 -26
- package/frontend/template-mixins/partials/forms/select.html +10 -5
- package/frontend/template-mixins/partials/forms/textarea-group.html +37 -23
- package/frontend/template-mixins/partials/mixins/panel.html +3 -4
- package/frontend/template-partials/views/accessibility.html +4 -4
- package/frontend/template-partials/views/cookies.html +1 -1
- package/frontend/template-partials/views/layout.html +24 -17
- package/frontend/template-partials/views/partials/back.html +1 -1
- package/frontend/template-partials/views/partials/bullet-list.html +1 -1
- package/frontend/template-partials/views/partials/confirmation-alert.html +4 -3
- package/frontend/template-partials/views/partials/continue.html +1 -1
- package/frontend/template-partials/views/partials/cookie-banner.html +27 -24
- package/frontend/template-partials/views/partials/cookie-settings-radio.html +6 -6
- package/frontend/template-partials/views/partials/external-link.html +1 -1
- package/frontend/template-partials/views/partials/form.html +2 -1
- package/frontend/template-partials/views/partials/gatag.html +0 -1
- package/frontend/template-partials/views/partials/head.html +23 -0
- package/frontend/template-partials/views/partials/maincontent-left.html +4 -4
- package/frontend/template-partials/views/partials/navigation.html +7 -6
- package/frontend/template-partials/views/partials/session-cookies-table.html +6 -6
- package/frontend/template-partials/views/partials/summary-table-row.html +2 -2
- package/frontend/template-partials/views/partials/table.html +7 -7
- package/frontend/template-partials/views/partials/validation-list.html +2 -2
- package/frontend/template-partials/views/partials/validation-summary.html +14 -13
- package/frontend/template-partials/views/partials/warn.html +7 -0
- package/frontend/template-partials/views/session-timeout.html +3 -2
- package/frontend/themes/gov-uk/client-js/cookieSettings.js +1 -1
- package/frontend/themes/gov-uk/client-js/govuk-cookies.js +121 -0
- package/frontend/themes/gov-uk/client-js/index.js +6 -1
- package/frontend/themes/gov-uk/client-js/skip-to-main.js +19 -0
- package/frontend/themes/gov-uk/styles/_cookie-banner.scss +51 -1
- package/frontend/themes/gov-uk/styles/govuk.scss +4 -0
- package/frontend/themes/gov-uk/styles/modules/_validation.scss +5 -5
- package/frontend/toolkit/assets/javascript/character-count.js +4 -4
- package/frontend/toolkit/assets/javascript/progressive-reveal.js +3 -1
- package/frontend/toolkit/assets/javascript/validation.js +5 -1
- package/frontend/toolkit/assets/stylesheets/modules/_validation.scss +3 -3
- package/index.js +15 -2
- package/lib/ga-tag.js +33 -7
- package/lib/settings.js +18 -2
- package/middleware/cookies.js +2 -0
- package/middleware/errors.js +2 -3
- package/middleware/not-found.js +0 -3
- package/middleware/rate-limiter.js +1 -0
- package/model/apis/axios-settings.js +21 -0
- package/model/apis/html-to-pdf-converter.js +10 -8
- package/model/index.js +102 -87
- package/package.json +18 -17
- package/pull_request.md +16 -0
- package/sandbox/README.md +3 -3
- package/sandbox/apps/sandbox/fields.js +33 -11
- package/sandbox/apps/sandbox/index.js +4 -0
- package/sandbox/apps/sandbox/sections/summary-data-sections.js +3 -0
- package/sandbox/apps/sandbox/translations/en/default.json +220 -0
- package/sandbox/apps/sandbox/translations/src/en/fields.json +11 -4
- package/sandbox/apps/sandbox/translations/src/en/journey.json +4 -1
- package/sandbox/apps/sandbox/translations/src/en/pages.json +7 -25
- package/sandbox/apps/sandbox/translations/src/en/validation.json +5 -1
- package/sandbox/assets/js/index.js +1 -1
- package/sandbox/assets/scss/app.scss +16 -16
- package/sandbox/package.json +7 -2
- package/sandbox/public/css/app.css +9632 -0
- package/sandbox/public/images/icons/icon-caret-left.png +0 -0
- package/sandbox/public/images/icons/icon-complete.png +0 -0
- package/sandbox/public/images/icons/icon-cross-remove-sign.png +0 -0
- package/sandbox/public/js/bundle.js +46721 -0
- package/sandbox/server.js +2 -1
- package/sandbox/yarn.lock +249 -2
- package/wizard/index.js +0 -13
- package/wizard/middleware/check-progress.js +36 -1
- package/.nyc_output/4d5a4574-78fc-4fcb-9412-3658f6ce33ff.json +0 -1
- package/.nyc_output/processinfo/4d5a4574-78fc-4fcb-9412-3658f6ce33ff.json +0 -1
- package/frontend/govuk-template/govuk_template.html +0 -109
- package/frontend/themes/gov-uk/views/partials/form.html +0 -9
- package/frontend/themes/gov-uk/views/partials/forms/option-group.html +0 -28
- package/frontend/themes/gov-uk/views/partials/mixins/panel.html +0 -3
- package/frontend/themes/gov-uk/views/partials/validation-summary.html +0 -24
- package/middleware/monitor.js +0 -20
- package/sandbox/apps/sandbox/views/confirmation.html +0 -15
package/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# HOF (Home Office Forms)
|
2
|
+
|
2
3
|
[![NPM_Publish Actions Status](https://github.com/UKHomeOfficeForms/hof/workflows/Automate_Publish/badge.svg)](https://github.com/UKHomeOfficeForms/hof/actions)
|
3
4
|
[![npm version](https://badge.fury.io/js/hof.svg)](https://badge.fury.io/js/hof)
|
4
5
|
[![Known Vulnerabilities](https://snyk.io/test/npm/hof/badge.svg)](https://snyk.io/test/npm/hof)
|
@@ -6,6 +7,7 @@
|
|
6
7
|
HOF (Home Office Forms) is a framework designed to assist developers in creating form-based workflows in a rapid, repeatable and secure way. It aims to reduce simple applications as much as possible to being configuration-only.
|
7
8
|
|
8
9
|
## Server Settings
|
10
|
+
|
9
11
|
In your `hof.settings.json` file you can add `getTerms: false` and `getCookies: false` to turn off the default cookies, and terms and conditions information provided by the HOF framework. This is if you want to provide more specific material at the service level in regards to these subject matter otherwise the defaults should suffice.
|
10
12
|
|
11
13
|
Also you can set `getAccessibility: true` to get the default accessibility document for this framework if one is not provided at the service level. It is assumed there should have been an accessibility audit carried out for a service already hence why the default setting for this is set to `false`. But if a generic placeholder is needed to ensure the service is legally compliant then this can be set to `true` to provide the default one presented within the framework.
|
@@ -17,9 +19,11 @@ Also you can set `getAccessibility: true` to get the default accessibility docum
|
|
17
19
|
[https://ukhomeofficeforms.github.io/hof-guide/](https://ukhomeofficeforms.github.io/hof-guide/)
|
18
20
|
|
19
21
|
## Content Security Policy
|
22
|
+
|
20
23
|
### Inline JavaScript from 18.0.0
|
24
|
+
|
21
25
|
From version 18.0.0, unsafe-inline has been removed from the content security policy by default. This means scripts
|
22
|
-
must either be referenced using the src attribute,
|
26
|
+
must either be referenced using the src attribute, `<script src='...'></script>` or with a nonce value attribute. A nonce
|
23
27
|
value is generated for every request. You can add this to your own templates' inline scripts as needed:
|
24
28
|
|
25
29
|
```
|
@@ -29,15 +33,17 @@ value is generated for every request. You can add this to your own templates' in
|
|
29
33
|
```
|
30
34
|
|
31
35
|
### Built with HOF
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
|
37
|
+
- https://github.com/UKHomeOffice/gro
|
38
|
+
- https://github.com/UKHomeOffice/end-tenancy
|
39
|
+
- [Firearms Licensing (Home Office)](https://github.com/UKHomeOffice/firearms)
|
40
|
+
- [Contact UK Trade & Investment (UK Trade & Investment)](https://github.com/UKTradeInvestment/contact-ukti)
|
41
|
+
- [Biometric Residence Permit (Home Office)](https://github.com/UKHomeOffice/brp_app)
|
42
|
+
- [Report terrorist material (Home Office)](https://github.com/UKHomeOffice/rotm)
|
43
|
+
- [UKVI Complaints (Home Office)](https://github.com/UKHomeOffice/Complaints)
|
39
44
|
|
40
45
|
## HOF BUILD
|
46
|
+
|
41
47
|
Performs build workflow for hof apps in prod and development
|
42
48
|
|
43
49
|
## Usage
|
@@ -54,10 +60,14 @@ It is recommended to alias `hof-build` to an npm script in your package.json.
|
|
54
60
|
|
55
61
|
## Tasks
|
56
62
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
63
|
+
- `browserify` - compiles client-side js with browserify
|
64
|
+
- `sass` - compiles sass
|
65
|
+
- `images` - copies images from ./assets/images directory to ./public/images
|
66
|
+
- `translate` - compiles translation files
|
67
|
+
|
68
|
+
Note: For SASS compilation it's possible to additionally configure the following options via the hof.settings file (see the configuration section below)
|
69
|
+
- `outputStyle` - Controls whether the CSS output is compressed or not, expanded (default) = non compressed and compressed = compressed CSS output.
|
70
|
+
- `quietDeps` - This controls whether you get deprecation warning shown in the console output, if set to false (default) SASS deprecation warnings will be shown in the console, if set to true then deprecation warnings will not be shown in the console output.
|
61
71
|
|
62
72
|
## Watch
|
63
73
|
|
@@ -98,7 +108,7 @@ Any task can be disabled by setting its configuration to `false` (or any falsy v
|
|
98
108
|
|
99
109
|
```js
|
100
110
|
module.exports = {
|
101
|
-
browserify: false
|
111
|
+
browserify: false,
|
102
112
|
};
|
103
113
|
```
|
104
114
|
|
@@ -106,15 +116,15 @@ module.exports = {
|
|
106
116
|
|
107
117
|
Each task has a common configuration format with the following options:
|
108
118
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
119
|
+
- `src` - defines the input file or files for the build task
|
120
|
+
- `out` - defines the output location of the built code where relevant
|
121
|
+
- `match` - defines the pattern for files to watch to trigger a rebuild of this task
|
122
|
+
- `restart` - defines if this task should result in a server restart
|
113
123
|
|
114
124
|
Additionally the server instance created by `watch` can be configured by setting `server` config. Available options are:
|
115
125
|
|
116
|
-
|
117
|
-
|
126
|
+
- `cmd` - defines the command used to start the server
|
127
|
+
- `extensions` - defines the file extensions which will be watched to trigger a restart
|
118
128
|
|
119
129
|
### Shared Translations
|
120
130
|
|
@@ -123,6 +133,7 @@ By default translations put in the commons directory in a HOF project, i.e. `app
|
|
123
133
|
To override this behaviour you can add the following to your `hof.settings.json` file or to the settings possible to hof on your server.js file
|
124
134
|
|
125
135
|
Hof.settings.json example
|
136
|
+
|
126
137
|
```
|
127
138
|
"build": {
|
128
139
|
"translate": {
|
@@ -132,6 +143,7 @@ Hof.settings.json example
|
|
132
143
|
```
|
133
144
|
|
134
145
|
server.js example
|
146
|
+
|
135
147
|
```
|
136
148
|
const hof = require('hof');
|
137
149
|
const settings = { ...behaviours, ...routes };
|
@@ -155,9 +167,10 @@ hof-transpiler [source dir|glob] {OPTIONS}
|
|
155
167
|
|
156
168
|
## Example
|
157
169
|
|
158
|
-
Lets say you have a directory such as:
|
170
|
+
Lets say you have a directory such as: `translations/src/en`
|
159
171
|
|
160
172
|
Which contains:
|
173
|
+
|
161
174
|
```
|
162
175
|
buttons.json
|
163
176
|
emails.json
|
@@ -165,9 +178,9 @@ errors.json
|
|
165
178
|
validation.json
|
166
179
|
```
|
167
180
|
|
168
|
-
If you run hof-transpiler against the directory
|
181
|
+
If you run hof-transpiler against the directory `hof-transpiler ./translations/src`
|
169
182
|
|
170
|
-
It will iterate through src and for each directory it will create a new directory at the root level with a built default.json file
|
183
|
+
It will iterate through src and for each directory it will create a new directory at the root level with a built default.json file `translations/en/default.json`
|
171
184
|
|
172
185
|
Which will look something like
|
173
186
|
|
@@ -192,26 +205,30 @@ This is used further down the hof stack for application translations.
|
|
192
205
|
|
193
206
|
## Advanced example - duplicate keys between source folder and shared folder
|
194
207
|
|
195
|
-
Lets say you have a directory such as:
|
208
|
+
Lets say you have a directory such as: `translations/src/en`
|
196
209
|
|
197
210
|
Which contains:
|
198
211
|
buttons.json containing:
|
212
|
+
|
199
213
|
```json
|
200
214
|
{
|
201
215
|
"unusual-button": "Moo"
|
202
216
|
}
|
203
217
|
```
|
218
|
+
|
204
219
|
emails.json containing:
|
220
|
+
|
205
221
|
```json
|
206
222
|
{
|
207
223
|
"customer-email": "Hi how are you?"
|
208
224
|
}
|
209
225
|
```
|
210
226
|
|
211
|
-
And you also have a directory of shared translations such as:
|
227
|
+
And you also have a directory of shared translations such as: `shared-translations/src/en`
|
212
228
|
|
213
229
|
Which contains:
|
214
230
|
buttons.json containing:
|
231
|
+
|
215
232
|
```json
|
216
233
|
{
|
217
234
|
"common-button": "Click me"
|
@@ -219,11 +236,13 @@ buttons.json containing:
|
|
219
236
|
```
|
220
237
|
|
221
238
|
If you then run:
|
239
|
+
|
222
240
|
```bash
|
223
241
|
hof-transpiler translations/src --shared shared-translations/src
|
224
242
|
```
|
225
243
|
|
226
244
|
Then transpiled translations should appear in translations/en/default.json as follows:
|
245
|
+
|
227
246
|
```json
|
228
247
|
{
|
229
248
|
"buttons": {
|
@@ -245,6 +264,7 @@ hof-transpiler supports multiple shared sources, extending them from left to rig
|
|
245
264
|
If you have the following sources:
|
246
265
|
|
247
266
|
node_modules/hof-template-partials/translations/src/en/buttons.json
|
267
|
+
|
248
268
|
```json
|
249
269
|
{
|
250
270
|
"continue": "Continue",
|
@@ -255,6 +275,7 @@ node_modules/hof-template-partials/translations/src/en/buttons.json
|
|
255
275
|
```
|
256
276
|
|
257
277
|
common/translations/src/en/buttons.json
|
278
|
+
|
258
279
|
```json
|
259
280
|
{
|
260
281
|
"skip": "Skip this step",
|
@@ -263,6 +284,7 @@ common/translations/src/en/buttons.json
|
|
263
284
|
```
|
264
285
|
|
265
286
|
my-application/translations/src/en/buttons.json
|
287
|
+
|
266
288
|
```json
|
267
289
|
{
|
268
290
|
"continue": "Go Forth!"
|
@@ -270,11 +292,13 @@ my-application/translations/src/en/buttons.json
|
|
270
292
|
```
|
271
293
|
|
272
294
|
If you then run:
|
295
|
+
|
273
296
|
```bash
|
274
297
|
hof-transpiler my-application/translations/src --shared node_modules/hof-template-partials/translations/src --shared common/translations/src
|
275
298
|
```
|
276
299
|
|
277
300
|
my-application/translations/en/default.json
|
301
|
+
|
278
302
|
```json
|
279
303
|
{
|
280
304
|
"buttons": {
|
@@ -286,6 +310,7 @@ my-application/translations/en/default.json
|
|
286
310
|
}
|
287
311
|
}
|
288
312
|
```
|
313
|
+
|
289
314
|
#HOF Controller
|
290
315
|
|
291
316
|
Implements a request pipeline for GET and POST of forms, with input cleaning/formatting and validation.
|
@@ -295,18 +320,18 @@ Implements a request pipeline for GET and POST of forms, with input cleaning/for
|
|
295
320
|
Basic usage:
|
296
321
|
|
297
322
|
```javascript
|
298
|
-
var Form = require(
|
323
|
+
var Form = require("./controller");
|
299
324
|
|
300
325
|
var form = new Form({
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
326
|
+
template: "form",
|
327
|
+
fields: {
|
328
|
+
name: {
|
329
|
+
validate: "required",
|
330
|
+
},
|
331
|
+
},
|
307
332
|
});
|
308
333
|
|
309
|
-
app.use(
|
334
|
+
app.use("/", form.requestHandler());
|
310
335
|
```
|
311
336
|
|
312
337
|
This won't really be very useful though, since all it will do is render the "form" template on `/` and respond to GET and POST requests.
|
@@ -328,16 +353,16 @@ module.exports = MyForm;
|
|
328
353
|
|
329
354
|
The Form class allows for a number of insertion points for extended functionality:
|
330
355
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
356
|
+
- `configure` Allows for dynamic overwriting of particular points of form configuration based on user session
|
357
|
+
- `process` Allows for custom formatting and processing of input prior to validation
|
358
|
+
- `validate` Allows for custom input validation
|
359
|
+
- `getValues` To define what values the fields are populated with on GET
|
360
|
+
- `saveValues` To define what is done with successful form submissions
|
336
361
|
|
337
362
|
All of these methods take three arguments of the request, the response and a callback. In all cases the callback should be called with a first argument representing an error.
|
338
363
|
|
339
|
-
|
340
|
-
|
364
|
+
- `getErrors/setErrors` Define how errors are persisted between the POST and subsequent GET of a form step.
|
365
|
+
- `locals` Define what additional variables a controller exposes to its template
|
341
366
|
|
342
367
|
These methods are synchronous and take only the request and response obejct as arguments.
|
343
368
|
|
@@ -352,6 +377,7 @@ By default the application of a validator is optional on empty strings. If you n
|
|
352
377
|
Custom validator functions can be passed in field config. These must be named functions and the name is used as the error.type for looking up validation error messages.
|
353
378
|
|
354
379
|
fields.js
|
380
|
+
|
355
381
|
```js
|
356
382
|
{
|
357
383
|
'field-1': {
|
@@ -435,9 +461,10 @@ For example, for a dynamic address selection component:
|
|
435
461
|
|
436
462
|
```js
|
437
463
|
MyForm.prototype.configure = function configure(req, res, next) {
|
438
|
-
|
439
|
-
|
440
|
-
|
464
|
+
req.form.options.fields["address-select"].options =
|
465
|
+
req.sessionModel.get("addresses");
|
466
|
+
next();
|
467
|
+
};
|
441
468
|
```
|
442
469
|
|
443
470
|
### The FormError class
|
@@ -445,15 +472,14 @@ MyForm.prototype.configure = function configure(req, res, next) {
|
|
445
472
|
FormError can be used as a façade to normalise different types of error one may receive / trigger, and to be subsequently returned from a controller.
|
446
473
|
Its constructor takes a series of options. `title` and `message` have both getters and public methods to define default values.
|
447
474
|
|
448
|
-
|
449
475
|
```js
|
450
|
-
|
451
476
|
let error = new ErrorClass(this.missingDoB, {
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
477
|
+
key: this.missingDob,
|
478
|
+
type: "required",
|
479
|
+
redirect: "/missingData",
|
480
|
+
title: "Something went wrong",
|
481
|
+
message: "Please supply a valid date of birth",
|
482
|
+
});
|
457
483
|
```
|
458
484
|
|
459
485
|
##hof-behaviour-session
|
@@ -473,6 +499,7 @@ class MyController extends mix(BaseController).with(Session) {
|
|
473
499
|
...
|
474
500
|
}
|
475
501
|
```
|
502
|
+
|
476
503
|
`MyController` now extends `hof-form-controller` and has `hof-behaviour-session` functionality mixed in.
|
477
504
|
|
478
505
|
##Functionality
|
@@ -481,14 +508,13 @@ This mixin extends `hof-form-controller` by persisting the form data to the `ses
|
|
481
508
|
|
482
509
|
The following form controller methods are used:
|
483
510
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
511
|
+
- `getValues(req, res, cb)` - calls callback with `null` and a map of all items in the `sessionModel`, extended with `errorValues` - to persist entered values on current step if validation fails
|
512
|
+
- `saveValues(req, res, cb)` - Called on success. Sets all step fields in `req.form.values` to the sessionModel, unsets `errorValues`.
|
513
|
+
- `getErrors(req)` - returns all errors for fields on the current step (`req.form.options.fields`), excluding redirects. Set to `req.form.errors` in `hof-form-controller`.
|
514
|
+
- `setErrors(err, req)` - called on validation error(s). Sets the current step field values as `errorValues` in sessionModel to be used in `getValues`. Sets `errors` to sessionModel - a map of `field-name: error` to be used in `getErrors`.
|
515
|
+
- `locals(req, res)` - Extends the result of `super.locals` with `baseUrl` (`req.baseUrl`) and `nextPage` (the result of `this.getNextStep(req, res)`).
|
516
|
+
- `missingPrereqHandler(req, res)` - Error handler called when a `MISSING_PREREQ` error is thrown from the [check-progress](https://github.com/UKHomeOfficeForms/hof-form-wizard/blob/master/lib/middleware/check-progress.js) middleware. This occurs if a step is visited out of sequence. This error handler causes the user to be redirected to the last completed step, or the first step if none have been completed.
|
517
|
+
- `errorHandler(err, req, res, next)` - checks if `err.code` is `MISSING_PREREQ`, if so calls `missingPrereqHandler`, if not calls `super` to hand over to parent error handler.
|
492
518
|
|
493
519
|
##behaviour-hooks
|
494
520
|
|
@@ -507,6 +533,7 @@ class MyController extends mix(BaseController).with(Hooks) {
|
|
507
533
|
...
|
508
534
|
}
|
509
535
|
```
|
536
|
+
|
510
537
|
`MyController` now extends `hof-form-controller` and has `hof-behaviour-hooks` functionality mixed in.
|
511
538
|
|
512
539
|
##Functionality
|
@@ -514,46 +541,52 @@ class MyController extends mix(BaseController).with(Hooks) {
|
|
514
541
|
The following hooks are currently supported, the methods are GET/POST pipeline methods from `hof-form-controller`:
|
515
542
|
|
516
543
|
####GET
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
544
|
+
|
545
|
+
- `_getErrors` - `'pre-getErrors', 'post-getErrors'`
|
546
|
+
- `_getValues` - `'pre-getValues', 'post-getValues'`
|
547
|
+
- `_locals` - `'pre-locals', 'post-locals'`
|
548
|
+
- `render` - `'pre-render', 'post-render'`
|
521
549
|
|
522
550
|
####POST
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
551
|
+
|
552
|
+
- `_process` - `'pre-process', 'post-process'`
|
553
|
+
- `_validate` - `'pre-validate', 'post-validate'`
|
554
|
+
- `saveValues` - `'pre-saveValues', 'post-saveValues'`
|
555
|
+
- `successHandler` - `'pre-successHandler', 'post-successHandler'`
|
527
556
|
|
528
557
|
###In field config
|
529
558
|
|
530
559
|
fields.js
|
560
|
+
|
531
561
|
```js
|
532
562
|
module.exports = {
|
533
|
-
|
563
|
+
"field-1": {
|
534
564
|
hooks: {
|
535
|
-
|
565
|
+
"post-locals": (req, res, next) => {
|
536
566
|
Object.assign(res.locals, {
|
537
|
-
foo:
|
567
|
+
foo: "bar",
|
538
568
|
});
|
539
569
|
next();
|
540
570
|
},
|
541
|
-
|
542
|
-
req.body[
|
571
|
+
"pre-process": (req, res, next) => {
|
572
|
+
req.body["field-1"] = req.body["field-1"].toUpperCase();
|
543
573
|
next();
|
544
|
-
}
|
545
|
-
}
|
546
|
-
}
|
547
|
-
}
|
574
|
+
},
|
575
|
+
},
|
576
|
+
},
|
577
|
+
};
|
548
578
|
```
|
549
579
|
|
550
580
|
# HOF Model
|
581
|
+
|
551
582
|
Simple model for interacting with http/rest apis.
|
552
583
|
|
553
584
|
## Usage
|
585
|
+
|
554
586
|
```javascript
|
555
|
-
const Model = require(
|
587
|
+
const Model = require("./model");
|
556
588
|
```
|
589
|
+
|
557
590
|
## Data Storage
|
558
591
|
|
559
592
|
Models can be used as basic data storage with set/get and change events.
|
@@ -566,10 +599,10 @@ Save a property to a model. Properties can be passed as a separate key/value arg
|
|
566
599
|
|
567
600
|
```javascript
|
568
601
|
const model = new Model();
|
569
|
-
model.set(
|
602
|
+
model.set("key", "value");
|
570
603
|
model.set({
|
571
|
-
firstname:
|
572
|
-
lastname:
|
604
|
+
firstname: "John",
|
605
|
+
lastname: "Smith",
|
573
606
|
});
|
574
607
|
```
|
575
608
|
|
@@ -578,7 +611,7 @@ model.set({
|
|
578
611
|
Retrieve a property from a model:
|
579
612
|
|
580
613
|
```javascript
|
581
|
-
const val = model.get(
|
614
|
+
const val = model.get("key");
|
582
615
|
// val = 'value'
|
583
616
|
```
|
584
617
|
|
@@ -597,7 +630,7 @@ const json = model.toJSON();
|
|
597
630
|
|
598
631
|
```javascript
|
599
632
|
const model = new Model();
|
600
|
-
model.on(
|
633
|
+
model.on("change", (changedFields) => {
|
601
634
|
// changedFields contains a map of the key/value pairs which have changed
|
602
635
|
console.log(changedFields);
|
603
636
|
});
|
@@ -607,10 +640,10 @@ model.on('change', (changedFields) => {
|
|
607
640
|
|
608
641
|
```javascript
|
609
642
|
const model = new Model();
|
610
|
-
model.on(
|
643
|
+
model.on("change:name", (newValue, oldValue) => {
|
611
644
|
// handler is passed the new value and the old value as arguents
|
612
645
|
});
|
613
|
-
model.set(
|
646
|
+
model.set("name", "John Smith");
|
614
647
|
```
|
615
648
|
|
616
649
|
### Referenced Fields
|
@@ -619,12 +652,12 @@ A field can be set to a reference to another field by setting it a value of `$re
|
|
619
652
|
|
620
653
|
```javascript
|
621
654
|
const model = new Model();
|
622
|
-
model.set(
|
623
|
-
model.set(
|
655
|
+
model.set("home-address", "1 Main Street");
|
656
|
+
model.set("contact-address", "$ref:home-address");
|
624
657
|
|
625
|
-
model.get(
|
626
|
-
model.set(
|
627
|
-
model.get(
|
658
|
+
model.get("contact-address"); // => '1 Main Street';
|
659
|
+
model.set("home-address", "2 Main Street");
|
660
|
+
model.get("contact-address"); // => '2 Main Street';
|
628
661
|
|
629
662
|
model.toJSON(); // => { home-address: '2 Main Street', 'contact-address': '2 Main Street' }
|
630
663
|
```
|
@@ -633,26 +666,26 @@ Change events will be fired on the referenced field if the underlying value chan
|
|
633
666
|
|
634
667
|
```javascript
|
635
668
|
const model = new Model();
|
636
|
-
model.set(
|
637
|
-
model.set(
|
638
|
-
model.on(
|
669
|
+
model.set("home-address", "1 Main Street");
|
670
|
+
model.set("contact-address", "$ref:home-address");
|
671
|
+
model.on("change:contact-address", (value, oldValue) => {
|
639
672
|
// this is fired when home-address property changes
|
640
673
|
});
|
641
674
|
|
642
|
-
model.set(
|
675
|
+
model.set("home-address", "2 Main Street");
|
643
676
|
```
|
644
677
|
|
645
678
|
A field can be unreferenced by setting its value to any other value.
|
646
679
|
|
647
680
|
```javascript
|
648
681
|
const model = new Model();
|
649
|
-
model.set(
|
682
|
+
model.set("home-address", "1 Main Street");
|
650
683
|
|
651
684
|
// reference the field
|
652
|
-
model.set(
|
685
|
+
model.set("contact-address", "$ref:home-address");
|
653
686
|
|
654
687
|
// unreference the field
|
655
|
-
model.set(
|
688
|
+
model.set("contact-address", "1 Other Road");
|
656
689
|
```
|
657
690
|
|
658
691
|
## API Client
|
@@ -669,7 +702,7 @@ There are three methods for API interaction corresponding to GET, POST, and DELE
|
|
669
702
|
|
670
703
|
```javascript
|
671
704
|
const model = new Model();
|
672
|
-
model.fetch().then(data => {
|
705
|
+
model.fetch().then((data) => {
|
673
706
|
console.log(data);
|
674
707
|
});
|
675
708
|
```
|
@@ -679,9 +712,9 @@ model.fetch().then(data => {
|
|
679
712
|
```javascript
|
680
713
|
const model = new Model();
|
681
714
|
model.set({
|
682
|
-
property:
|
715
|
+
property: "properties are sent as JSON request body by default",
|
683
716
|
});
|
684
|
-
model.save().then(data => {
|
717
|
+
model.save().then((data) => {
|
685
718
|
console.log(data);
|
686
719
|
});
|
687
720
|
```
|
@@ -691,9 +724,9 @@ The method can also be overwritten by passing options
|
|
691
724
|
```javascript
|
692
725
|
const model = new Model();
|
693
726
|
model.set({
|
694
|
-
property:
|
727
|
+
property: "this will be sent as a PUT request",
|
695
728
|
});
|
696
|
-
model.save({ method:
|
729
|
+
model.save({ method: "PUT" }).then((data) => {
|
697
730
|
console.log(data);
|
698
731
|
});
|
699
732
|
```
|
@@ -702,7 +735,7 @@ model.save({ method: 'PUT' }).then(data => {
|
|
702
735
|
|
703
736
|
```javascript
|
704
737
|
const model = new Model();
|
705
|
-
model.delete().then(data => {
|
738
|
+
model.delete().then((data) => {
|
706
739
|
console.log(data);
|
707
740
|
});
|
708
741
|
```
|
@@ -715,14 +748,16 @@ If no `url` method is defined then the model will use the options parameter and
|
|
715
748
|
const model = new Model();
|
716
749
|
|
717
750
|
// make a GET request to http://example.com:3000/foo/bar
|
718
|
-
model
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
751
|
+
model
|
752
|
+
.fetch({
|
753
|
+
protocol: "http",
|
754
|
+
hostname: "example.com",
|
755
|
+
port: 3000,
|
756
|
+
path: "/foo/bar",
|
757
|
+
})
|
758
|
+
.then((data) => {
|
759
|
+
console.log(data);
|
760
|
+
});
|
726
761
|
```
|
727
762
|
|
728
763
|
### Events
|
@@ -730,29 +765,36 @@ model.fetch({
|
|
730
765
|
API requests will emit events as part of their lifecycle.
|
731
766
|
|
732
767
|
`sync` is emitted when an API request is sent
|
768
|
+
|
733
769
|
```javascript
|
734
|
-
model.on(
|
770
|
+
model.on("sync", function (settings) {});
|
735
771
|
```
|
736
772
|
|
737
773
|
`success` is emitted when an API request successfully completes
|
774
|
+
|
738
775
|
```javascript
|
739
|
-
model.on(
|
776
|
+
model.on("success", function (data, settings, statusCode, responseTime) {});
|
740
777
|
```
|
741
778
|
|
742
779
|
`fail` is emitted when an API request fails
|
780
|
+
|
743
781
|
```javascript
|
744
|
-
model.on(
|
782
|
+
model.on("fail", function (err, data, settings, statusCode, responseTime) {});
|
745
783
|
```
|
746
784
|
|
747
785
|
### HOF Model APIs
|
786
|
+
|
748
787
|
- `Html-To-Pdf Converter`: This extends the HOF model to interact with the html-to-pdf converter API https://github.com/UKHomeOffice/html-pdf-converter. The environmental variable `PDF_CONVERTER_URL` needs to be set to its local url when running in the same kube namespace to the service that wants to use it. This is then followed by the default port `10443` and then the URI for which part of the service you want to consume. For example:`https://html-pdf-converter:10443/convert` when the container is named `html-pdf-converter` in your kube deployment file. This has to be set to `https` for communication between services to work on ACP. However, `settings.rejectUnauthorized = false;` is set in the model to circumvent expired certificates due to this. This is preferable to using:
|
788
|
+
|
749
789
|
```
|
750
790
|
name: NODE_TLS_REJECT_UNAUTHORIZED
|
751
791
|
value: "0"
|
752
792
|
```
|
793
|
+
|
753
794
|
which should NOT be used as it sets ignoring TLS at a global level which could present a MITM (Man-In-The-Middle) attack.
|
754
795
|
|
755
796
|
Usage: Example below, as per the converter docs (link above) it accepts html and responds with Buffered data in pdf format which can then be either written to a file or attached to a Gov Notify message:
|
797
|
+
|
756
798
|
```
|
757
799
|
const PDFModel = require('hof').apis.pdfConverter;
|
758
800
|
|
@@ -762,6 +804,7 @@ const pdfData = await pdfModel.save();
|
|
762
804
|
```
|
763
805
|
|
764
806
|
# HOF Middleware
|
807
|
+
|
765
808
|
A collection of commonly used HOF middleware, exports `cookies`, `notFound`, and `errors` on `middleware`
|
766
809
|
|
767
810
|
## Arranging the middleware in your app
|
@@ -772,16 +815,20 @@ The Not Found middleware should be placed after all routes and before the Error
|
|
772
815
|
## Cookies
|
773
816
|
|
774
817
|
### Usage
|
818
|
+
|
775
819
|
```js
|
776
|
-
app.use(
|
777
|
-
|
778
|
-
|
779
|
-
|
820
|
+
app.use(
|
821
|
+
require("hof").middleware.cookies({
|
822
|
+
"cookie-name": "my-application-cookie",
|
823
|
+
"param-name": "my-query-param",
|
824
|
+
})
|
825
|
+
);
|
780
826
|
```
|
781
827
|
|
782
828
|
This middleware must be declared before your other routes.
|
783
829
|
|
784
830
|
### Options
|
831
|
+
|
785
832
|
The `cookie-name` can be the same as your session cookie. (The
|
786
833
|
middleware will not overwrite it.) Defaults to `hof-cookie-check`.
|
787
834
|
|
@@ -802,16 +849,22 @@ Kubernetes healthcheck URLs are provided as defaults if no overrides are supplie
|
|
802
849
|
Expects there to be a view called 404 in your configured `/views` directory
|
803
850
|
|
804
851
|
### Usage
|
852
|
+
|
805
853
|
```js
|
806
|
-
app.use(
|
807
|
-
|
808
|
-
|
809
|
-
|
854
|
+
app.use(
|
855
|
+
require("hof").middleware.notFound({
|
856
|
+
logger: require("/logger"),
|
857
|
+
translate: require("hof").i18n({
|
858
|
+
path: path_to_translations / __lng__ / __ns__.json,
|
859
|
+
}).translate,
|
860
|
+
})
|
861
|
+
);
|
810
862
|
```
|
811
863
|
|
812
|
-
This middleware should be declared
|
864
|
+
This middleware should be declared _after_ your other routes but _before_ your errorhandler.
|
813
865
|
|
814
866
|
### Options
|
867
|
+
|
815
868
|
`logger` can be any object with a warn method.
|
816
869
|
|
817
870
|
`translate` can be the HOF i18n translate function
|
@@ -819,42 +872,51 @@ This middleware should be declared *after* your other routes but *before* your e
|
|
819
872
|
## Errors
|
820
873
|
|
821
874
|
### Usage
|
875
|
+
|
822
876
|
```js
|
823
|
-
app.use(
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
877
|
+
app.use(
|
878
|
+
require("hof").middleware.errors({
|
879
|
+
logger: require("/logger"),
|
880
|
+
translate: require("hof").i18n({
|
881
|
+
path: path_to_translations / __lng__ / __ns__.json,
|
882
|
+
}).translate,
|
883
|
+
debug: true,
|
884
|
+
})
|
885
|
+
);
|
828
886
|
```
|
829
887
|
|
830
|
-
This middleware must be declared
|
888
|
+
This middleware must be declared _after_ your other routes.
|
831
889
|
|
832
890
|
### Options
|
891
|
+
|
833
892
|
`logger` can be any object with an error method.
|
834
893
|
|
835
894
|
`translate` can be the HOF i18n translate function
|
836
895
|
|
837
896
|
`debug` set to true will present the stack trace in the form and return the err as the content of the template.
|
838
897
|
|
839
|
-
|
840
|
-
|
898
|
+
# **Note** If `debug === true` translations will not be served, but the error handler default messages
|
899
|
+
|
841
900
|
## Deep translate
|
842
901
|
|
843
|
-
deepTranslate middleware supports nested conditional translations in order to show different content in different scenarios. The middleware adds a `translate` function to `req` which is used in various points throughout the architecture.
|
902
|
+
deepTranslate middleware supports nested conditional translations in order to show different content in different scenarios. The middleware adds a `translate` function to `req` which is used in various points throughout the architecture. This middleware must be applied before any other middleware which rely on the `req.translate` function. Also when initializing the form wizard, or template mixins, if a `translate` function is provided, this will be used rather than the deepTranslate middleware.
|
844
903
|
|
845
904
|
### Usage
|
846
905
|
|
847
906
|
```js
|
848
|
-
const i18nFuture = require(
|
907
|
+
const i18nFuture = require("hof").i18n;
|
849
908
|
const i18n = i18nFuture({
|
850
|
-
path: path.resolve(__dirname,
|
851
|
-
})
|
852
|
-
app.use(
|
853
|
-
|
854
|
-
|
909
|
+
path: path.resolve(__dirname, "./path/to/translations"),
|
910
|
+
});
|
911
|
+
app.use(
|
912
|
+
require("hof").middleware.deepTranslate({
|
913
|
+
translate: i18n.translate.bind(i18n),
|
914
|
+
})
|
915
|
+
);
|
855
916
|
```
|
856
917
|
|
857
918
|
locales
|
919
|
+
|
858
920
|
```json
|
859
921
|
"fields": {
|
860
922
|
"field-name": {
|
@@ -876,10 +938,10 @@ locales
|
|
876
938
|
|
877
939
|
Using the translation key `fields.field-name.label` will return different values in different situations depending on the values of named fields. In the above example the following are true:
|
878
940
|
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
941
|
+
- If both `dependent-field` and `dependent-field-2` have the value `"value-1"`, the label returned will be `"Label 1"`.
|
942
|
+
- If the value of `dependent-field` is `"value-1"` and the value of `dependent-field-2` is `"value-2"`, the label returned will be `"Label 2"`.
|
943
|
+
- If the value of `dependent-field` is `"value-2"` the label returned will be `"Label 3"` regardless of the value of `dependent-field-2`
|
944
|
+
- The default label `"Fallback label"` will be used if value of `dependent-field` is neither of the given options, or it is `undefined`. It will also be used if the value of `dependent-field` is `"value-1"` and the value of `dependent-field-2` is neither of the given options or it is undefined.
|
883
945
|
|
884
946
|
# HOF Components
|
885
947
|
|
@@ -890,14 +952,15 @@ A component for handling the rendering and processing of 3-input date fields use
|
|
890
952
|
## Usage
|
891
953
|
|
892
954
|
In your fields config:
|
955
|
+
|
893
956
|
```js
|
894
|
-
const dateComponent = require(
|
957
|
+
const dateComponent = require("hof").components.date;
|
895
958
|
|
896
959
|
module.exports = {
|
897
|
-
|
898
|
-
validate: [
|
899
|
-
})
|
900
|
-
}
|
960
|
+
"date-field": dateComponent("date-field", {
|
961
|
+
validate: ["required", "before"],
|
962
|
+
}),
|
963
|
+
};
|
901
964
|
```
|
902
965
|
|
903
966
|
The above example will create a new date component with the key `'date-field'` and will apply the validators `required` and `before` (before today).
|
@@ -906,16 +969,17 @@ The above example will create a new date component with the key `'date-field'` a
|
|
906
969
|
|
907
970
|
The following optional configuration options are supported:
|
908
971
|
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
972
|
+
- `validate {String|Array}` - validators to use on the processed date field
|
973
|
+
- `template` - an absolute path to an alternate template.
|
974
|
+
- `dayOptional {Boolean}` - day defaults to `01` if omitted. Defaults to `false`
|
975
|
+
- `monthOptional {Boolean}` - month defaults to `01` if omitted. If true then also forces `dayOptional` to be true. Defaults to `false`
|
913
976
|
|
914
977
|
## Labels
|
915
978
|
|
916
979
|
The three intermedate fields have fallback labels of Day, Month and Year, however custom labels can be used by including the translation at the following path:
|
917
980
|
|
918
981
|
fields.json
|
982
|
+
|
919
983
|
```json
|
920
984
|
{
|
921
985
|
"field-name": {
|
@@ -951,7 +1015,6 @@ If no sections config is passed, then the mixin will create a section for each s
|
|
951
1015
|
}
|
952
1016
|
```
|
953
1017
|
|
954
|
-
|
955
1018
|
Alternatively, sections can be defined manually as follows:
|
956
1019
|
|
957
1020
|
```js
|
@@ -990,9 +1053,9 @@ Fields can be defined as simple strings of the field key, in which case all defa
|
|
990
1053
|
|
991
1054
|
Alternatively, a field can be passed as an object with a `field` property defining the field key, and any additional properties as follows:
|
992
1055
|
|
993
|
-
|
994
|
-
|
995
|
-
|
1056
|
+
- `step` - `String` defines the step which the user is returned to to edit the field value. By default this is the first step in the form's steps configuration which contains the field.
|
1057
|
+
- `parse` - `Function` can parse the value for the field from the session into a value for display.
|
1058
|
+
- `derivation` - `Object` allows for a new derived field based on a combination of other fields in the form. Note that
|
996
1059
|
if both `derivation` and `parse` are specified then parse will be applied to the result of derivation. E.G.
|
997
1060
|
```javascript
|
998
1061
|
derivation: {
|
@@ -1000,10 +1063,11 @@ Alternatively, a field can be passed as an object with a `field` property defini
|
|
1000
1063
|
combiner: (values) => values.map(it => Number(it)).reduce((a, b) => a + b, 0)
|
1001
1064
|
}
|
1002
1065
|
```
|
1003
|
-
|
1004
|
-
![Firearms Use Original Value Summary Page Example](docs/images/firearms_use_original_value_summary.png)
|
1066
|
+
- `useOriginalValue` - `Object` uses original value of radio button or checkbox label rather than trying to find a translation in the `fields.json` file. This could be due to options that are generated by user input that can not be predicted in advance, which are subsequently used to populate a value in the summary page. One good example is using one of many addresses inputted by a user that is additionally a contact address. See example below:
|
1067
|
+
![Firearms Use Original Value Summary Page Example](docs/images/firearms_use_original_value_summary.png)
|
1068
|
+
|
1069
|
+
- `multipleRowsFromAggregate` - `Object` if this object exists on a field, it uses the `labelCategory`, `valueCategory` and `valueTranslation` values to populate the row's label and value name but also iterates over multiple rows that have been aggregated under one field name. There is one good reference of this in Firearms where the following example is used:
|
1005
1070
|
|
1006
|
-
* `multipleRowsFromAggregate` - `Object` if this object exists on a field, it uses the `labelCategory`, `valueCategory` and `valueTranslation` values to populate the row's label and value name but also iterates over multiple rows that have been aggregated under one field name. There is one good reference of this in Firearms where the following example is used:
|
1007
1071
|
```javascript
|
1008
1072
|
{
|
1009
1073
|
field: 'location-addresses',
|
@@ -1016,6 +1080,7 @@ Alternatively, a field can be passed as an object with a `field` property defini
|
|
1016
1080
|
}
|
1017
1081
|
}
|
1018
1082
|
```
|
1083
|
+
|
1019
1084
|
The `location-addresses` field is one that the application has setup to aggregate and store all addresses labelled with the `address` field. Each address is a storage location for firearms, and so there is a sub-category which lists what firearms type is listed under each address (i.e. Full-bore, small-bore, muzzle-loading), and these are stored under the `address-category` field. Along with translations to them in the `fields.json` file living under the `location-address-category` translation header. By utilising these three values one can achieve the following output on the summary page.
|
1020
1085
|
|
1021
1086
|
![Firearms Summary Page Example](docs/images/firearms_summary_page_example.png)
|
@@ -1030,18 +1095,19 @@ The content for section headings and field labels will be loaded from translatio
|
|
1030
1095
|
|
1031
1096
|
Translations for section headings are looked for in the following order:
|
1032
1097
|
|
1033
|
-
|
1034
|
-
|
1098
|
+
- `pages.confirm.sections.${key}.header`
|
1099
|
+
- `pages.${key}.header`
|
1035
1100
|
|
1036
1101
|
### Field labels
|
1037
1102
|
|
1038
1103
|
Translations for field labels are looked for in the following order:
|
1039
1104
|
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1105
|
+
- `pages.confirm.fields.${key}.label`
|
1106
|
+
- `fields.${key}.label`
|
1107
|
+
- `fields.${key}.legend`
|
1043
1108
|
|
1044
1109
|
# Emailer Component
|
1110
|
+
|
1045
1111
|
HOF behaviour to send emails
|
1046
1112
|
|
1047
1113
|
## Usage
|
@@ -1078,10 +1144,10 @@ steps: {
|
|
1078
1144
|
|
1079
1145
|
In addition to the options passed to `hof-emailer`, the following options can be used:
|
1080
1146
|
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1147
|
+
- `recipient` - _Required_ - defines the address to which email will be sent. This can be set either as a key to retrieve an email address from the session, or explicitly to an email address.
|
1148
|
+
- `template` - _Required_ - defines the mustache template used to render the email content.
|
1149
|
+
- `subject` - defines the subject line of the email.
|
1150
|
+
- `parse` - parses the session model into an object used to populate the template.
|
1085
1151
|
|
1086
1152
|
`recipient` and `subject` options can also be defined as functions, which will be passed a copy of the session model and a translation function as arguments, and should return a string value.
|
1087
1153
|
|
@@ -1089,7 +1155,7 @@ In addition to the options passed to `hof-emailer`, the following options can be
|
|
1089
1155
|
// use a translated value for the email subject line
|
1090
1156
|
const emailer = EmailBehaviour({
|
1091
1157
|
// ...
|
1092
|
-
subject: (model, translate) => translate(
|
1158
|
+
subject: (model, translate) => translate("email.success.subject"),
|
1093
1159
|
});
|
1094
1160
|
```
|
1095
1161
|
|
@@ -1107,24 +1173,23 @@ $ npm install hof-emailer --save
|
|
1107
1173
|
|
1108
1174
|
```js
|
1109
1175
|
// first create an emailer instance
|
1110
|
-
const Emailer = require(
|
1176
|
+
const Emailer = require("hof").components.email.emailer;
|
1111
1177
|
const emailer = new Emailer({
|
1112
|
-
from:
|
1113
|
-
transport:
|
1178
|
+
from: "sender@example.com",
|
1179
|
+
transport: "smtp",
|
1114
1180
|
transportOptions: {
|
1115
|
-
host:
|
1116
|
-
port: 25
|
1117
|
-
}
|
1181
|
+
host: "my.smtp.host",
|
1182
|
+
port: 25,
|
1183
|
+
},
|
1118
1184
|
});
|
1119
1185
|
|
1120
1186
|
// then you can use your emailer to send emails
|
1121
|
-
const to =
|
1122
|
-
const body =
|
1123
|
-
const subject =
|
1124
|
-
emailer.send(to, body, subject)
|
1125
|
-
.
|
1126
|
-
|
1127
|
-
});
|
1187
|
+
const to = "recipient@example.com";
|
1188
|
+
const body = "This is the email body";
|
1189
|
+
const subject = "Important email!";
|
1190
|
+
emailer.send(to, body, subject).then(() => {
|
1191
|
+
console.log(`Email sent to ${to}!`);
|
1192
|
+
});
|
1128
1193
|
```
|
1129
1194
|
|
1130
1195
|
## Options
|
@@ -1152,6 +1217,7 @@ The following transport options are available:
|
|
1152
1217
|
- `auth.pass` <String>: Mailserver authorisation password.
|
1153
1218
|
|
1154
1219
|
### `ses`
|
1220
|
+
|
1155
1221
|
[nodemailer-ses-transport](https://github.com/andris9/nodemailer-ses-transport)
|
1156
1222
|
|
1157
1223
|
#### Options
|
@@ -1184,6 +1250,7 @@ transportOptions: {
|
|
1184
1250
|
open: true
|
1185
1251
|
}
|
1186
1252
|
```
|
1253
|
+
|
1187
1254
|
### `stub`
|
1188
1255
|
|
1189
1256
|
Disables sending email. No options are required.
|
@@ -1191,6 +1258,7 @@ Disables sending email. No options are required.
|
|
1191
1258
|
# UTILITIES
|
1192
1259
|
|
1193
1260
|
# Autofill Utility
|
1261
|
+
|
1194
1262
|
A webdriverio plugin to automate filling a form
|
1195
1263
|
|
1196
1264
|
## Usage
|
@@ -1198,10 +1266,10 @@ A webdriverio plugin to automate filling a form
|
|
1198
1266
|
First, add the command to your webdriverio client:
|
1199
1267
|
|
1200
1268
|
```js
|
1201
|
-
const webdriver = require(
|
1269
|
+
const webdriver = require("webdriverio");
|
1202
1270
|
const client = webdriver.remote(options);
|
1203
1271
|
|
1204
|
-
client.addCommand(
|
1272
|
+
client.addCommand("goto", require("hof-util-autofill")(client));
|
1205
1273
|
```
|
1206
1274
|
|
1207
1275
|
The command can be given any name you like, here we've called it `goto`.
|
@@ -1209,32 +1277,36 @@ The command can be given any name you like, here we've called it `goto`.
|
|
1209
1277
|
Then you can use the command as normal as part of your webdriver command chain.
|
1210
1278
|
|
1211
1279
|
```js
|
1212
|
-
it(
|
1213
|
-
return browser
|
1280
|
+
it("completes a form to a certain step automatically", () => {
|
1281
|
+
return browser
|
1282
|
+
.goto("/confirm")
|
1214
1283
|
.getUrl()
|
1215
1284
|
.then((url) => {
|
1216
|
-
assert.ok(url.indexOf(
|
1285
|
+
assert.ok(url.indexOf("/confirm") > -1);
|
1217
1286
|
});
|
1218
1287
|
});
|
1219
1288
|
|
1220
|
-
it(
|
1221
|
-
const inputs = {
|
1222
|
-
return browser
|
1223
|
-
|
1289
|
+
it("uses any data passed as a second argument to fill out the form", () => {
|
1290
|
+
const inputs = { "first-name": "David", "last-name": "Hasselhoff" };
|
1291
|
+
return browser
|
1292
|
+
.goto("/confirm", inputs)
|
1293
|
+
.$("span.full-name")
|
1224
1294
|
.getText()
|
1225
|
-
.then(name => {
|
1226
|
-
assert.equal(name,
|
1295
|
+
.then((name) => {
|
1296
|
+
assert.equal(name, "David HasselHoff");
|
1227
1297
|
});
|
1228
1298
|
});
|
1229
1299
|
|
1230
|
-
it(
|
1300
|
+
it("saves screenshots of errors to specified screenshot location", () => {
|
1231
1301
|
const inputs = {};
|
1232
|
-
return browser.goto(
|
1302
|
+
return browser.goto("/confirm", inputs, {
|
1303
|
+
screenshots: "/path/to/output/dir",
|
1304
|
+
});
|
1233
1305
|
});
|
1234
1306
|
|
1235
|
-
it(
|
1307
|
+
it("tries a pre-specified number of times to get past stuck loops", () => {
|
1236
1308
|
const inputs = {};
|
1237
|
-
return browser.goto(
|
1309
|
+
return browser.goto("/confirm", inputs, { maxLoops: 1 });
|
1238
1310
|
});
|
1239
1311
|
```
|
1240
1312
|
|
@@ -1242,10 +1314,11 @@ it('tries a pre-specified number of times to get past stuck loops', () => {
|
|
1242
1314
|
|
1243
1315
|
Options are passed as a third argument to the exposed method. The following options are available:
|
1244
1316
|
|
1245
|
-
|
1246
|
-
|
1317
|
+
- `maxLoops` - determines how many times a step will retry if it resolves back to itself on submission before failing. Default: `3`
|
1318
|
+
- `screenshots` - specifies a location to save screenshots of the page when it gets stuck. If not specified then no screenshots are saved.
|
1247
1319
|
|
1248
1320
|
# Test-Data Utility
|
1321
|
+
|
1249
1322
|
Generator for test fixtures
|
1250
1323
|
|
1251
1324
|
## Usage
|
@@ -1255,7 +1328,7 @@ The library contains a number of generators for values of certain types. Values
|
|
1255
1328
|
### Example:
|
1256
1329
|
|
1257
1330
|
```js
|
1258
|
-
const TestData = require(
|
1331
|
+
const TestData = require("hof").utils.testData;
|
1259
1332
|
|
1260
1333
|
console.log(TestData.name);
|
1261
1334
|
// "David Fletcher"
|
@@ -1266,22 +1339,22 @@ console.log(TestData.name);
|
|
1266
1339
|
|
1267
1340
|
## Available generators
|
1268
1341
|
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1342
|
+
- `firstname`
|
1343
|
+
- `lastname`
|
1344
|
+
- `name`
|
1345
|
+
- `email`
|
1346
|
+
- `phone`
|
1347
|
+
- `streetname`
|
1348
|
+
- `streetsuffix`
|
1349
|
+
- `address` - `${number(1,100)} ${streetname} ${streetsuffix}`
|
1350
|
+
- `postcode`
|
1351
|
+
- `country` - a random country from [homeoffice-countries](npmjs.com/homeoffice-countries)
|
1279
1352
|
|
1280
1353
|
## Functions
|
1281
1354
|
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1355
|
+
- `number(min, max)` - returns an integer between `min` and `max`
|
1356
|
+
- `number(max)` - returns an integer between 0 and `max`
|
1357
|
+
- `number()` - returns an integer between 0 and 100
|
1285
1358
|
|
1286
1359
|
# Countries Utility
|
1287
1360
|
|
@@ -1307,23 +1380,24 @@ In field configuration:
|
|
1307
1380
|
|
1308
1381
|
If needed, the following options can be passed into the countries function:
|
1309
1382
|
|
1310
|
-
|
1311
|
-
|
1383
|
+
- `filter` - `Function` - applies a filter to the list of country names before mapping them
|
1384
|
+
- `parse` - `Function` - applies a transform to the country name before setting the label
|
1312
1385
|
|
1313
1386
|
## i18n
|
1314
1387
|
|
1315
1388
|
If you wish to translate the countries into outher languages, you may want the labels to be in the form of translation keys. In this case you can use a `parse` option to convert the country names into a translation key:
|
1316
1389
|
|
1317
1390
|
```js
|
1318
|
-
const countries = require(
|
1391
|
+
const countries = require("hof").utils.countries;
|
1319
1392
|
const options = countries({
|
1320
|
-
parse: country => `countries.${country.toLowerCase().split(
|
1393
|
+
parse: (country) => `countries.${country.toLowerCase().split(" ").join("-")}`,
|
1321
1394
|
});
|
1322
1395
|
```
|
1323
1396
|
|
1324
1397
|
You can then define a single translation for country names to be used for all country list instances.
|
1325
1398
|
|
1326
1399
|
# FRONTEND
|
1400
|
+
|
1327
1401
|
## Template Mixins
|
1328
1402
|
|
1329
1403
|
A middleware that exposes a series of Mustache mixins on `res.locals` to ease usage of forms, translations, and some other things.
|
@@ -1337,20 +1411,20 @@ npm install [--save] hof-template-mixins;
|
|
1337
1411
|
## Usage
|
1338
1412
|
|
1339
1413
|
```javascript
|
1340
|
-
var express = require(
|
1414
|
+
var express = require("express");
|
1341
1415
|
|
1342
|
-
var i18n = require(
|
1343
|
-
var mixins = require(
|
1416
|
+
var i18n = require("i18n-future");
|
1417
|
+
var mixins = require("hof").frontend.mixins;
|
1344
1418
|
|
1345
|
-
app.set(
|
1346
|
-
app.set(
|
1419
|
+
app.set("view engine", "html");
|
1420
|
+
app.set("views", path.join(__dirname, "/views"));
|
1347
1421
|
|
1348
1422
|
app.use(i18n.middleware());
|
1349
1423
|
app.use(mixins());
|
1350
1424
|
|
1351
1425
|
app.use(function (req, res) {
|
1352
|
-
|
1353
|
-
|
1426
|
+
// NOTE: res.locals.partials has been set.
|
1427
|
+
res.render("example-template");
|
1354
1428
|
});
|
1355
1429
|
```
|
1356
1430
|
|
@@ -1414,6 +1488,7 @@ renderField
|
|
1414
1488
|
```
|
1415
1489
|
|
1416
1490
|
### qs
|
1491
|
+
|
1417
1492
|
This mixin takes a `key=value` query string and returns a query string with the extra params appended. If the key is already present in the query string, the value passed to the mixin is used
|
1418
1493
|
|
1419
1494
|
```html
|
@@ -1425,34 +1500,29 @@ This mixin takes a `key=value` query string and returns a query string with the
|
|
1425
1500
|
The renderField mixin can be called in your template to render all fields. This will lookup the field.mixin in res.locals and call it passing the field key.
|
1426
1501
|
|
1427
1502
|
```html
|
1428
|
-
{{#fields}}
|
1429
|
-
{{#renderField}}{{/renderField}}
|
1430
|
-
{{/fields}}
|
1503
|
+
{{#fields}} {{#renderField}}{{/renderField}} {{/fields}}
|
1431
1504
|
```
|
1432
1505
|
|
1433
1506
|
fields.js
|
1507
|
+
|
1434
1508
|
```js
|
1435
1509
|
module.exports = {
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
}
|
1510
|
+
"my-field": {
|
1511
|
+
mixin: "input-text",
|
1512
|
+
},
|
1513
|
+
};
|
1440
1514
|
```
|
1441
1515
|
|
1442
1516
|
If mixin is omitted `input-text` will be used
|
1443
1517
|
|
1444
1518
|
To disable auto-rendering of a field, set `disableRender: true` in the field config. This is required when using the `child` element rendering functionality to prevent the field being rendered multiple times.
|
1445
1519
|
|
1446
|
-
|
1447
|
-
### Render a single field ###
|
1520
|
+
### Render a single field
|
1448
1521
|
|
1449
1522
|
To render a specific fields in your templates use the mixin name (matching those above) and field name like so...
|
1450
1523
|
|
1451
1524
|
```html
|
1452
|
-
{{#input-text}}myTextField{{/input-text}}
|
1453
|
-
|
1454
|
-
{{#select}}mySelectMenu{{/select}}
|
1455
|
-
|
1525
|
+
{{#input-text}}myTextField{{/input-text}} {{#select}}mySelectMenu{{/select}}
|
1456
1526
|
{{#radio-group}}myRadioGroup{{/radio-group}}
|
1457
1527
|
```
|
1458
1528
|
|
@@ -1464,6 +1534,7 @@ To render a specific fields in your templates use the mixin name (matching those
|
|
1464
1534
|
- `required`: Value applied to `aria-required` HTML attribute.
|
1465
1535
|
- `hint`: This adds context to the label, which it is a part of, for input text, radio groups and textarea. It is used within the input by aria-describedby for screen readers.
|
1466
1536
|
- `maxlength`: Applicable to text-based fields and mapped to the `maxlength` HTML attribute.
|
1537
|
+
- `maxword`: Applicable to textarea fields.
|
1467
1538
|
- `options`: Applicable to HTML `select` and `radio` controls and used to generate the items of either HTML element.
|
1468
1539
|
- `selected`: Applicable to `select`, `checkbox`, and `radio` controls. Will render the selected HTML option/element selected or checked.
|
1469
1540
|
- `legend`: Applicable to `radio` button controls, which are wrapped in a HTML `fieldset` with a `legend` element.
|
@@ -1471,11 +1542,12 @@ To render a specific fields in your templates use the mixin name (matching those
|
|
1471
1542
|
- `toggle`: Can be used to toggle the display of the HTML element with a matching `id`. See [hof-frontend-toolkit](https://github.com/UKHomeOfficeForms/hof-frontend-toolkit/blob/master/assets/javascript/progressive-reveal.js) for details.
|
1472
1543
|
- `attributes`: A hash of key/value pairs applicable to a HTML `textarea` field. Each key/value is assigned as an attribute of the `textarea`. For example `spellcheck="true"`.
|
1473
1544
|
- `child`: Render a child partial beneath each option in an `optionGroup`. Accepts a custom mustache template string, a custom partial in the format `partials/{your-partial-name}`, `'html'` which is used to specify the html for the field has already been prerendered, such as in [hof-component-date](https://github.com/UKHomeOfficeForms/hof-component-date) or a template mixin key which will be rendered within a panel element partial.
|
1474
|
-
|
1545
|
+
- `isPageHeading`: Applicable to `checkbox` and `radio`, `text input` and `textarea` controls. Sets the legend as the page heading on single page questions.
|
1546
|
+
- `isWarning`: Applicable to `checkbox` and `radio` controls. Allows warning text to be placed after page headings on single page questions if required.
|
1475
1547
|
|
1476
1548
|
# HOF-template-partials
|
1477
1549
|
|
1478
|
-
Home Office Forms template partials is a collection of mustache partials commonly used in HOF applications.
|
1550
|
+
Home Office Forms template partials is a collection of mustache partials commonly used in HOF applications. It also contains a collection of i18n translations used within the template partials. All contents are designed to be extended in your individual applications.
|
1479
1551
|
|
1480
1552
|
## Usage
|
1481
1553
|
|
@@ -1486,14 +1558,14 @@ Home Office Forms template partials is a collection of mustache partials commonl
|
|
1486
1558
|
Template partials can be used by adding the route to the views directory to your express application views setting. You will need to be using the HTML view engine with Hogan and Mustache.
|
1487
1559
|
|
1488
1560
|
```js
|
1489
|
-
var app = require(
|
1561
|
+
var app = require("express")();
|
1490
1562
|
|
1491
|
-
app.set(
|
1492
|
-
app.set(
|
1563
|
+
app.set("view engine", "html");
|
1564
|
+
app.set("views", [
|
1493
1565
|
// your application shared views
|
1494
|
-
path.resolve(__dirname,
|
1566
|
+
path.resolve(__dirname, "./path/to/views"),
|
1495
1567
|
// the module exports paths to views and translations directories
|
1496
|
-
require(
|
1568
|
+
require("hof").frontend.partials.views,
|
1497
1569
|
]);
|
1498
1570
|
```
|
1499
1571
|
|
@@ -1504,14 +1576,14 @@ The views are now available when calling `res.render('view-name')` from express.
|
|
1504
1576
|
When used in a hof application in conjunction with [express-partial-templates](https://github.com/UKHomeOffice/express-partial-templates) the contents of the views directory are added to `res.locals.partials`. These are added right to left so conflicting views are resolved from the left-most directory.
|
1505
1577
|
|
1506
1578
|
```js
|
1507
|
-
var app = require(
|
1579
|
+
var app = require("express")();
|
1508
1580
|
|
1509
|
-
app.set(
|
1510
|
-
app.set(
|
1511
|
-
path.resolve(__dirname,
|
1512
|
-
require(
|
1581
|
+
app.set("view engine", "html");
|
1582
|
+
app.set("views", [
|
1583
|
+
path.resolve(__dirname, "./path/to/views"),
|
1584
|
+
require("hof").frontend.partials.views,
|
1513
1585
|
]);
|
1514
|
-
app.use(require(
|
1586
|
+
app.use(require("express-partial-templates")(app));
|
1515
1587
|
|
1516
1588
|
app.use(function (req, res, next) {
|
1517
1589
|
// res.locals.partials contains all views from the views dir in this repo
|
@@ -1527,8 +1599,8 @@ The provided translations are designed to be used in conjunction with a translat
|
|
1527
1599
|
The exported `resources` method will return a compiled object containing the translations, which can be passed to an `i18n` instance as a pre-compiled resource.
|
1528
1600
|
|
1529
1601
|
```js
|
1530
|
-
const translate = require(
|
1531
|
-
resources: require(
|
1602
|
+
const translate = require("i18n-future").middleware({
|
1603
|
+
resources: require("hof").frontend.partials.resources(),
|
1532
1604
|
});
|
1533
1605
|
app.use(translate);
|
1534
1606
|
```
|
@@ -1536,19 +1608,19 @@ app.use(translate);
|
|
1536
1608
|
By default the namespace for this translation is `default`. A custom namespace can be specified by passing it as an argument to the `resources` function.
|
1537
1609
|
|
1538
1610
|
```js
|
1539
|
-
const translate = require(
|
1540
|
-
resources: require(
|
1541
|
-
fallbackNamespace:
|
1611
|
+
const translate = require("i18n-future").middleware({
|
1612
|
+
resources: require("hof").frontend.partials.resources("hof-common"),
|
1613
|
+
fallbackNamespace: "hof-common",
|
1542
1614
|
});
|
1543
1615
|
app.use(translate);
|
1544
1616
|
```
|
1617
|
+
|
1545
1618
|
### Cookie Banner
|
1546
1619
|
|
1547
1620
|
The cookie banner has a placeholder named serviceName that you can set within the locals of your hof application so that the appropriate value is displayed.
|
1548
1621
|
|
1549
1622
|
Set `appName` if your hof settings being passed to hof to take advantage of this.
|
1550
1623
|
|
1551
|
-
|
1552
1624
|
# HOF FRONTEND THEME
|
1553
1625
|
|
1554
1626
|
## Usage
|
@@ -1600,11 +1672,13 @@ When used as part of an express app, a middleware is returned which will add a s
|
|
1600
1672
|
It will also add the template as a mustache partial with a name of "govuk-template".
|
1601
1673
|
|
1602
1674
|
### To configure express middleware
|
1675
|
+
|
1603
1676
|
```
|
1604
1677
|
app.use(require('hof').frontend.govUKTemplate([options]);
|
1605
1678
|
```
|
1606
1679
|
|
1607
1680
|
### To use the mustache partial
|
1681
|
+
|
1608
1682
|
```
|
1609
1683
|
{{< govuk-template}}
|
1610
1684
|
{{$pageTitle}}An example page{{/pageTitle}}
|
@@ -1618,11 +1692,12 @@ app.use(require('hof').frontend.govUKTemplate([options]);
|
|
1618
1692
|
|
1619
1693
|
A number of options can be passed with the app into the setup method:
|
1620
1694
|
|
1621
|
-
|
1695
|
+
- `path` - Sets the base path for the location of static assets - Default: `/govuk-assets`
|
1622
1696
|
|
1623
1697
|
Other options are passed onto the [serve-static](https://www.npmjs.com/package/serve-static) configuration, and more details can be found in [the serve-static documentation](https://www.npmjs.com/package/serve-static)
|
1624
1698
|
|
1625
1699
|
# Nonce values
|
1700
|
+
|
1626
1701
|
Version 18.0.0 and above of HOF provides and requires a nonce value for all inline javascript, as unsafe-inline is disabled.
|
1627
1702
|
Older versions (pre 18.0.0) will work with the hof-govuk-template templates as expected as the nonce value fields will only be added
|
1628
1703
|
if a nonce value is provided by the version of HOF.
|
@@ -1636,16 +1711,25 @@ There is an example implementation in [demo application](https://github.com/UKHo
|
|
1636
1711
|
There is a sandbox application for developers to test components directly in hof called [sandbox](/sandbox)
|
1637
1712
|
|
1638
1713
|
# HOF FRONTEND TOOLKIT
|
1714
|
+
|
1639
1715
|
Set of common UI patterns/styles for HOF projects
|
1640
1716
|
|
1641
1717
|
## Images
|
1718
|
+
|
1642
1719
|
Copy `assets/images/hmpo` to your image directory. Images are loaded by using the `file-url` function provided by [GOV.UK frontend toolkit](https://github.com/alphagov/govuk_frontend_toolkit). The `file-url` function uses the `$path` variable which is set before the toolkit's modules are loaded.
|
1643
1720
|
|
1644
1721
|
## Vendor JavaScript
|
1722
|
+
|
1645
1723
|
Additional vendor JavaScript files are included. These are:
|
1646
1724
|
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1725
|
+
- details.polyfill.js
|
1726
|
+
- indexof.polyfill.js
|
1727
|
+
- safari-cachebuster.js
|
1650
1728
|
|
1651
1729
|
Copy `assets/javascript/vendor` into your javascript directory (ie `hmpo/vendor`) and compile them with your JavaScript.
|
1730
|
+
|
1731
|
+
## Journey Header Navigation.html page
|
1732
|
+
|
1733
|
+
- Navigation.html contains a journeyHeaderURL, which is set in the controller.
|
1734
|
+
- getJourneyHeaderURL within the controller translates an empty baseURL to '/'.
|
1735
|
+
- The above helps fix broken journey header URLs in the GRO and UKVIC services which both have a baseURL's set to '/'.
|