hof 20.0.0-beta.9 → 20.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/.nyc_output/7c548a7f-5c40-44b2-b9fb-648341a23d6f.json +1 -0
- package/.nyc_output/processinfo/7c548a7f-5c40-44b2-b9fb-648341a23d6f.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -1
- package/README.md +335 -256
- package/components/index.js +2 -1
- package/components/notify/index.js +60 -0
- package/components/notify/notify.js +25 -0
- package/config/sanitisation-rules.js +20 -17
- package/controller/base-controller.js +5 -3
- package/controller/controller.js +19 -4
- package/frontend/template-mixins/mixins/template-mixins.js +7 -3
- package/frontend/template-mixins/partials/forms/checkbox-group.html +10 -1
- package/frontend/template-mixins/partials/forms/input-text-date.html +1 -1
- package/frontend/template-mixins/partials/forms/input-text-group.html +5 -3
- package/frontend/template-mixins/partials/forms/option-group.html +9 -0
- package/frontend/template-mixins/partials/forms/select.html +1 -1
- package/frontend/template-mixins/partials/forms/textarea-group.html +2 -2
- package/frontend/template-partials/views/layout.html +10 -3
- package/frontend/template-partials/views/partials/cookie-banner.html +1 -1
- package/frontend/template-partials/views/partials/form.html +2 -1
- package/frontend/template-partials/views/partials/maincontent-left.html +2 -2
- package/frontend/template-partials/views/partials/navigation.html +2 -2
- package/frontend/template-partials/views/partials/summary-table-row.html +2 -2
- package/frontend/template-partials/views/partials/warn.html +7 -0
- package/frontend/template-partials/views/session-timeout.html +2 -1
- package/frontend/themes/gov-uk/styles/govuk.scss +4 -0
- package/frontend/themes/gov-uk/styles/modules/_validation.scss +2 -2
- package/frontend/toolkit/assets/javascript/form-focus.js +10 -1
- package/frontend/toolkit/assets/javascript/progressive-reveal.js +3 -1
- package/frontend/toolkit/assets/javascript/validation.js +6 -1
- package/index.js +1 -1
- package/middleware/errors.js +2 -0
- package/package.json +4 -2
- package/sandbox/apps/sandbox/fields.js +1 -0
- package/sandbox/apps/sandbox/index.js +1 -5
- package/sandbox/assets/scss/app.scss +0 -52
- package/sandbox/package.json +2 -0
- package/sandbox/public/css/app.css +4938 -4990
- package/sandbox/public/js/bundle.js +79 -65
- package/sandbox/server.js +2 -1
- package/sandbox/yarn.lock +39 -564
- package/wizard/middleware/check-progress.js +36 -1
- package/.nyc_output/e2fdc3eb-4fd2-47e0-a392-fe5f665776a4.json +0 -1
- package/.nyc_output/processinfo/e2fdc3eb-4fd2-47e0-a392-fe5f665776a4.json +0 -1
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,10 @@ 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
|
61
67
|
|
62
68
|
## Watch
|
63
69
|
|
@@ -98,7 +104,7 @@ Any task can be disabled by setting its configuration to `false` (or any falsy v
|
|
98
104
|
|
99
105
|
```js
|
100
106
|
module.exports = {
|
101
|
-
browserify: false
|
107
|
+
browserify: false,
|
102
108
|
};
|
103
109
|
```
|
104
110
|
|
@@ -106,15 +112,15 @@ module.exports = {
|
|
106
112
|
|
107
113
|
Each task has a common configuration format with the following options:
|
108
114
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
115
|
+
- `src` - defines the input file or files for the build task
|
116
|
+
- `out` - defines the output location of the built code where relevant
|
117
|
+
- `match` - defines the pattern for files to watch to trigger a rebuild of this task
|
118
|
+
- `restart` - defines if this task should result in a server restart
|
113
119
|
|
114
120
|
Additionally the server instance created by `watch` can be configured by setting `server` config. Available options are:
|
115
121
|
|
116
|
-
|
117
|
-
|
122
|
+
- `cmd` - defines the command used to start the server
|
123
|
+
- `extensions` - defines the file extensions which will be watched to trigger a restart
|
118
124
|
|
119
125
|
### Shared Translations
|
120
126
|
|
@@ -123,6 +129,7 @@ By default translations put in the commons directory in a HOF project, i.e. `app
|
|
123
129
|
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
130
|
|
125
131
|
Hof.settings.json example
|
132
|
+
|
126
133
|
```
|
127
134
|
"build": {
|
128
135
|
"translate": {
|
@@ -132,6 +139,7 @@ Hof.settings.json example
|
|
132
139
|
```
|
133
140
|
|
134
141
|
server.js example
|
142
|
+
|
135
143
|
```
|
136
144
|
const hof = require('hof');
|
137
145
|
const settings = { ...behaviours, ...routes };
|
@@ -155,9 +163,10 @@ hof-transpiler [source dir|glob] {OPTIONS}
|
|
155
163
|
|
156
164
|
## Example
|
157
165
|
|
158
|
-
Lets say you have a directory such as:
|
166
|
+
Lets say you have a directory such as: `translations/src/en`
|
159
167
|
|
160
168
|
Which contains:
|
169
|
+
|
161
170
|
```
|
162
171
|
buttons.json
|
163
172
|
emails.json
|
@@ -165,9 +174,9 @@ errors.json
|
|
165
174
|
validation.json
|
166
175
|
```
|
167
176
|
|
168
|
-
If you run hof-transpiler against the directory
|
177
|
+
If you run hof-transpiler against the directory `hof-transpiler ./translations/src`
|
169
178
|
|
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
|
179
|
+
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
180
|
|
172
181
|
Which will look something like
|
173
182
|
|
@@ -192,26 +201,30 @@ This is used further down the hof stack for application translations.
|
|
192
201
|
|
193
202
|
## Advanced example - duplicate keys between source folder and shared folder
|
194
203
|
|
195
|
-
Lets say you have a directory such as:
|
204
|
+
Lets say you have a directory such as: `translations/src/en`
|
196
205
|
|
197
206
|
Which contains:
|
198
207
|
buttons.json containing:
|
208
|
+
|
199
209
|
```json
|
200
210
|
{
|
201
211
|
"unusual-button": "Moo"
|
202
212
|
}
|
203
213
|
```
|
214
|
+
|
204
215
|
emails.json containing:
|
216
|
+
|
205
217
|
```json
|
206
218
|
{
|
207
219
|
"customer-email": "Hi how are you?"
|
208
220
|
}
|
209
221
|
```
|
210
222
|
|
211
|
-
And you also have a directory of shared translations such as:
|
223
|
+
And you also have a directory of shared translations such as: `shared-translations/src/en`
|
212
224
|
|
213
225
|
Which contains:
|
214
226
|
buttons.json containing:
|
227
|
+
|
215
228
|
```json
|
216
229
|
{
|
217
230
|
"common-button": "Click me"
|
@@ -219,11 +232,13 @@ buttons.json containing:
|
|
219
232
|
```
|
220
233
|
|
221
234
|
If you then run:
|
235
|
+
|
222
236
|
```bash
|
223
237
|
hof-transpiler translations/src --shared shared-translations/src
|
224
238
|
```
|
225
239
|
|
226
240
|
Then transpiled translations should appear in translations/en/default.json as follows:
|
241
|
+
|
227
242
|
```json
|
228
243
|
{
|
229
244
|
"buttons": {
|
@@ -245,6 +260,7 @@ hof-transpiler supports multiple shared sources, extending them from left to rig
|
|
245
260
|
If you have the following sources:
|
246
261
|
|
247
262
|
node_modules/hof-template-partials/translations/src/en/buttons.json
|
263
|
+
|
248
264
|
```json
|
249
265
|
{
|
250
266
|
"continue": "Continue",
|
@@ -255,6 +271,7 @@ node_modules/hof-template-partials/translations/src/en/buttons.json
|
|
255
271
|
```
|
256
272
|
|
257
273
|
common/translations/src/en/buttons.json
|
274
|
+
|
258
275
|
```json
|
259
276
|
{
|
260
277
|
"skip": "Skip this step",
|
@@ -263,6 +280,7 @@ common/translations/src/en/buttons.json
|
|
263
280
|
```
|
264
281
|
|
265
282
|
my-application/translations/src/en/buttons.json
|
283
|
+
|
266
284
|
```json
|
267
285
|
{
|
268
286
|
"continue": "Go Forth!"
|
@@ -270,11 +288,13 @@ my-application/translations/src/en/buttons.json
|
|
270
288
|
```
|
271
289
|
|
272
290
|
If you then run:
|
291
|
+
|
273
292
|
```bash
|
274
293
|
hof-transpiler my-application/translations/src --shared node_modules/hof-template-partials/translations/src --shared common/translations/src
|
275
294
|
```
|
276
295
|
|
277
296
|
my-application/translations/en/default.json
|
297
|
+
|
278
298
|
```json
|
279
299
|
{
|
280
300
|
"buttons": {
|
@@ -286,6 +306,7 @@ my-application/translations/en/default.json
|
|
286
306
|
}
|
287
307
|
}
|
288
308
|
```
|
309
|
+
|
289
310
|
#HOF Controller
|
290
311
|
|
291
312
|
Implements a request pipeline for GET and POST of forms, with input cleaning/formatting and validation.
|
@@ -295,18 +316,18 @@ Implements a request pipeline for GET and POST of forms, with input cleaning/for
|
|
295
316
|
Basic usage:
|
296
317
|
|
297
318
|
```javascript
|
298
|
-
var Form = require(
|
319
|
+
var Form = require("./controller");
|
299
320
|
|
300
321
|
var form = new Form({
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
322
|
+
template: "form",
|
323
|
+
fields: {
|
324
|
+
name: {
|
325
|
+
validate: "required",
|
326
|
+
},
|
327
|
+
},
|
307
328
|
});
|
308
329
|
|
309
|
-
app.use(
|
330
|
+
app.use("/", form.requestHandler());
|
310
331
|
```
|
311
332
|
|
312
333
|
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 +349,16 @@ module.exports = MyForm;
|
|
328
349
|
|
329
350
|
The Form class allows for a number of insertion points for extended functionality:
|
330
351
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
352
|
+
- `configure` Allows for dynamic overwriting of particular points of form configuration based on user session
|
353
|
+
- `process` Allows for custom formatting and processing of input prior to validation
|
354
|
+
- `validate` Allows for custom input validation
|
355
|
+
- `getValues` To define what values the fields are populated with on GET
|
356
|
+
- `saveValues` To define what is done with successful form submissions
|
336
357
|
|
337
358
|
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
359
|
|
339
|
-
|
340
|
-
|
360
|
+
- `getErrors/setErrors` Define how errors are persisted between the POST and subsequent GET of a form step.
|
361
|
+
- `locals` Define what additional variables a controller exposes to its template
|
341
362
|
|
342
363
|
These methods are synchronous and take only the request and response obejct as arguments.
|
343
364
|
|
@@ -352,6 +373,7 @@ By default the application of a validator is optional on empty strings. If you n
|
|
352
373
|
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
374
|
|
354
375
|
fields.js
|
376
|
+
|
355
377
|
```js
|
356
378
|
{
|
357
379
|
'field-1': {
|
@@ -435,9 +457,10 @@ For example, for a dynamic address selection component:
|
|
435
457
|
|
436
458
|
```js
|
437
459
|
MyForm.prototype.configure = function configure(req, res, next) {
|
438
|
-
|
439
|
-
|
440
|
-
|
460
|
+
req.form.options.fields["address-select"].options =
|
461
|
+
req.sessionModel.get("addresses");
|
462
|
+
next();
|
463
|
+
};
|
441
464
|
```
|
442
465
|
|
443
466
|
### The FormError class
|
@@ -445,15 +468,14 @@ MyForm.prototype.configure = function configure(req, res, next) {
|
|
445
468
|
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
469
|
Its constructor takes a series of options. `title` and `message` have both getters and public methods to define default values.
|
447
470
|
|
448
|
-
|
449
471
|
```js
|
450
|
-
|
451
472
|
let error = new ErrorClass(this.missingDoB, {
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
473
|
+
key: this.missingDob,
|
474
|
+
type: "required",
|
475
|
+
redirect: "/missingData",
|
476
|
+
title: "Something went wrong",
|
477
|
+
message: "Please supply a valid date of birth",
|
478
|
+
});
|
457
479
|
```
|
458
480
|
|
459
481
|
##hof-behaviour-session
|
@@ -473,6 +495,7 @@ class MyController extends mix(BaseController).with(Session) {
|
|
473
495
|
...
|
474
496
|
}
|
475
497
|
```
|
498
|
+
|
476
499
|
`MyController` now extends `hof-form-controller` and has `hof-behaviour-session` functionality mixed in.
|
477
500
|
|
478
501
|
##Functionality
|
@@ -481,14 +504,13 @@ This mixin extends `hof-form-controller` by persisting the form data to the `ses
|
|
481
504
|
|
482
505
|
The following form controller methods are used:
|
483
506
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
507
|
+
- `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
|
508
|
+
- `saveValues(req, res, cb)` - Called on success. Sets all step fields in `req.form.values` to the sessionModel, unsets `errorValues`.
|
509
|
+
- `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`.
|
510
|
+
- `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`.
|
511
|
+
- `locals(req, res)` - Extends the result of `super.locals` with `baseUrl` (`req.baseUrl`) and `nextPage` (the result of `this.getNextStep(req, res)`).
|
512
|
+
- `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.
|
513
|
+
- `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
514
|
|
493
515
|
##behaviour-hooks
|
494
516
|
|
@@ -507,6 +529,7 @@ class MyController extends mix(BaseController).with(Hooks) {
|
|
507
529
|
...
|
508
530
|
}
|
509
531
|
```
|
532
|
+
|
510
533
|
`MyController` now extends `hof-form-controller` and has `hof-behaviour-hooks` functionality mixed in.
|
511
534
|
|
512
535
|
##Functionality
|
@@ -514,46 +537,52 @@ class MyController extends mix(BaseController).with(Hooks) {
|
|
514
537
|
The following hooks are currently supported, the methods are GET/POST pipeline methods from `hof-form-controller`:
|
515
538
|
|
516
539
|
####GET
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
540
|
+
|
541
|
+
- `_getErrors` - `'pre-getErrors', 'post-getErrors'`
|
542
|
+
- `_getValues` - `'pre-getValues', 'post-getValues'`
|
543
|
+
- `_locals` - `'pre-locals', 'post-locals'`
|
544
|
+
- `render` - `'pre-render', 'post-render'`
|
521
545
|
|
522
546
|
####POST
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
547
|
+
|
548
|
+
- `_process` - `'pre-process', 'post-process'`
|
549
|
+
- `_validate` - `'pre-validate', 'post-validate'`
|
550
|
+
- `saveValues` - `'pre-saveValues', 'post-saveValues'`
|
551
|
+
- `successHandler` - `'pre-successHandler', 'post-successHandler'`
|
527
552
|
|
528
553
|
###In field config
|
529
554
|
|
530
555
|
fields.js
|
556
|
+
|
531
557
|
```js
|
532
558
|
module.exports = {
|
533
|
-
|
559
|
+
"field-1": {
|
534
560
|
hooks: {
|
535
|
-
|
561
|
+
"post-locals": (req, res, next) => {
|
536
562
|
Object.assign(res.locals, {
|
537
|
-
foo:
|
563
|
+
foo: "bar",
|
538
564
|
});
|
539
565
|
next();
|
540
566
|
},
|
541
|
-
|
542
|
-
req.body[
|
567
|
+
"pre-process": (req, res, next) => {
|
568
|
+
req.body["field-1"] = req.body["field-1"].toUpperCase();
|
543
569
|
next();
|
544
|
-
}
|
545
|
-
}
|
546
|
-
}
|
547
|
-
}
|
570
|
+
},
|
571
|
+
},
|
572
|
+
},
|
573
|
+
};
|
548
574
|
```
|
549
575
|
|
550
576
|
# HOF Model
|
577
|
+
|
551
578
|
Simple model for interacting with http/rest apis.
|
552
579
|
|
553
580
|
## Usage
|
581
|
+
|
554
582
|
```javascript
|
555
|
-
const Model = require(
|
583
|
+
const Model = require("./model");
|
556
584
|
```
|
585
|
+
|
557
586
|
## Data Storage
|
558
587
|
|
559
588
|
Models can be used as basic data storage with set/get and change events.
|
@@ -566,10 +595,10 @@ Save a property to a model. Properties can be passed as a separate key/value arg
|
|
566
595
|
|
567
596
|
```javascript
|
568
597
|
const model = new Model();
|
569
|
-
model.set(
|
598
|
+
model.set("key", "value");
|
570
599
|
model.set({
|
571
|
-
firstname:
|
572
|
-
lastname:
|
600
|
+
firstname: "John",
|
601
|
+
lastname: "Smith",
|
573
602
|
});
|
574
603
|
```
|
575
604
|
|
@@ -578,7 +607,7 @@ model.set({
|
|
578
607
|
Retrieve a property from a model:
|
579
608
|
|
580
609
|
```javascript
|
581
|
-
const val = model.get(
|
610
|
+
const val = model.get("key");
|
582
611
|
// val = 'value'
|
583
612
|
```
|
584
613
|
|
@@ -597,7 +626,7 @@ const json = model.toJSON();
|
|
597
626
|
|
598
627
|
```javascript
|
599
628
|
const model = new Model();
|
600
|
-
model.on(
|
629
|
+
model.on("change", (changedFields) => {
|
601
630
|
// changedFields contains a map of the key/value pairs which have changed
|
602
631
|
console.log(changedFields);
|
603
632
|
});
|
@@ -607,10 +636,10 @@ model.on('change', (changedFields) => {
|
|
607
636
|
|
608
637
|
```javascript
|
609
638
|
const model = new Model();
|
610
|
-
model.on(
|
639
|
+
model.on("change:name", (newValue, oldValue) => {
|
611
640
|
// handler is passed the new value and the old value as arguents
|
612
641
|
});
|
613
|
-
model.set(
|
642
|
+
model.set("name", "John Smith");
|
614
643
|
```
|
615
644
|
|
616
645
|
### Referenced Fields
|
@@ -619,12 +648,12 @@ A field can be set to a reference to another field by setting it a value of `$re
|
|
619
648
|
|
620
649
|
```javascript
|
621
650
|
const model = new Model();
|
622
|
-
model.set(
|
623
|
-
model.set(
|
651
|
+
model.set("home-address", "1 Main Street");
|
652
|
+
model.set("contact-address", "$ref:home-address");
|
624
653
|
|
625
|
-
model.get(
|
626
|
-
model.set(
|
627
|
-
model.get(
|
654
|
+
model.get("contact-address"); // => '1 Main Street';
|
655
|
+
model.set("home-address", "2 Main Street");
|
656
|
+
model.get("contact-address"); // => '2 Main Street';
|
628
657
|
|
629
658
|
model.toJSON(); // => { home-address: '2 Main Street', 'contact-address': '2 Main Street' }
|
630
659
|
```
|
@@ -633,26 +662,26 @@ Change events will be fired on the referenced field if the underlying value chan
|
|
633
662
|
|
634
663
|
```javascript
|
635
664
|
const model = new Model();
|
636
|
-
model.set(
|
637
|
-
model.set(
|
638
|
-
model.on(
|
665
|
+
model.set("home-address", "1 Main Street");
|
666
|
+
model.set("contact-address", "$ref:home-address");
|
667
|
+
model.on("change:contact-address", (value, oldValue) => {
|
639
668
|
// this is fired when home-address property changes
|
640
669
|
});
|
641
670
|
|
642
|
-
model.set(
|
671
|
+
model.set("home-address", "2 Main Street");
|
643
672
|
```
|
644
673
|
|
645
674
|
A field can be unreferenced by setting its value to any other value.
|
646
675
|
|
647
676
|
```javascript
|
648
677
|
const model = new Model();
|
649
|
-
model.set(
|
678
|
+
model.set("home-address", "1 Main Street");
|
650
679
|
|
651
680
|
// reference the field
|
652
|
-
model.set(
|
681
|
+
model.set("contact-address", "$ref:home-address");
|
653
682
|
|
654
683
|
// unreference the field
|
655
|
-
model.set(
|
684
|
+
model.set("contact-address", "1 Other Road");
|
656
685
|
```
|
657
686
|
|
658
687
|
## API Client
|
@@ -669,7 +698,7 @@ There are three methods for API interaction corresponding to GET, POST, and DELE
|
|
669
698
|
|
670
699
|
```javascript
|
671
700
|
const model = new Model();
|
672
|
-
model.fetch().then(data => {
|
701
|
+
model.fetch().then((data) => {
|
673
702
|
console.log(data);
|
674
703
|
});
|
675
704
|
```
|
@@ -679,9 +708,9 @@ model.fetch().then(data => {
|
|
679
708
|
```javascript
|
680
709
|
const model = new Model();
|
681
710
|
model.set({
|
682
|
-
property:
|
711
|
+
property: "properties are sent as JSON request body by default",
|
683
712
|
});
|
684
|
-
model.save().then(data => {
|
713
|
+
model.save().then((data) => {
|
685
714
|
console.log(data);
|
686
715
|
});
|
687
716
|
```
|
@@ -691,9 +720,9 @@ The method can also be overwritten by passing options
|
|
691
720
|
```javascript
|
692
721
|
const model = new Model();
|
693
722
|
model.set({
|
694
|
-
property:
|
723
|
+
property: "this will be sent as a PUT request",
|
695
724
|
});
|
696
|
-
model.save({ method:
|
725
|
+
model.save({ method: "PUT" }).then((data) => {
|
697
726
|
console.log(data);
|
698
727
|
});
|
699
728
|
```
|
@@ -702,7 +731,7 @@ model.save({ method: 'PUT' }).then(data => {
|
|
702
731
|
|
703
732
|
```javascript
|
704
733
|
const model = new Model();
|
705
|
-
model.delete().then(data => {
|
734
|
+
model.delete().then((data) => {
|
706
735
|
console.log(data);
|
707
736
|
});
|
708
737
|
```
|
@@ -715,14 +744,16 @@ If no `url` method is defined then the model will use the options parameter and
|
|
715
744
|
const model = new Model();
|
716
745
|
|
717
746
|
// make a GET request to http://example.com:3000/foo/bar
|
718
|
-
model
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
747
|
+
model
|
748
|
+
.fetch({
|
749
|
+
protocol: "http",
|
750
|
+
hostname: "example.com",
|
751
|
+
port: 3000,
|
752
|
+
path: "/foo/bar",
|
753
|
+
})
|
754
|
+
.then((data) => {
|
755
|
+
console.log(data);
|
756
|
+
});
|
726
757
|
```
|
727
758
|
|
728
759
|
### Events
|
@@ -730,29 +761,36 @@ model.fetch({
|
|
730
761
|
API requests will emit events as part of their lifecycle.
|
731
762
|
|
732
763
|
`sync` is emitted when an API request is sent
|
764
|
+
|
733
765
|
```javascript
|
734
|
-
model.on(
|
766
|
+
model.on("sync", function (settings) {});
|
735
767
|
```
|
736
768
|
|
737
769
|
`success` is emitted when an API request successfully completes
|
770
|
+
|
738
771
|
```javascript
|
739
|
-
model.on(
|
772
|
+
model.on("success", function (data, settings, statusCode, responseTime) {});
|
740
773
|
```
|
741
774
|
|
742
775
|
`fail` is emitted when an API request fails
|
776
|
+
|
743
777
|
```javascript
|
744
|
-
model.on(
|
778
|
+
model.on("fail", function (err, data, settings, statusCode, responseTime) {});
|
745
779
|
```
|
746
780
|
|
747
781
|
### HOF Model APIs
|
782
|
+
|
748
783
|
- `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:
|
784
|
+
|
749
785
|
```
|
750
786
|
name: NODE_TLS_REJECT_UNAUTHORIZED
|
751
787
|
value: "0"
|
752
788
|
```
|
789
|
+
|
753
790
|
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
791
|
|
755
792
|
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:
|
793
|
+
|
756
794
|
```
|
757
795
|
const PDFModel = require('hof').apis.pdfConverter;
|
758
796
|
|
@@ -762,6 +800,7 @@ const pdfData = await pdfModel.save();
|
|
762
800
|
```
|
763
801
|
|
764
802
|
# HOF Middleware
|
803
|
+
|
765
804
|
A collection of commonly used HOF middleware, exports `cookies`, `notFound`, and `errors` on `middleware`
|
766
805
|
|
767
806
|
## Arranging the middleware in your app
|
@@ -772,16 +811,20 @@ The Not Found middleware should be placed after all routes and before the Error
|
|
772
811
|
## Cookies
|
773
812
|
|
774
813
|
### Usage
|
814
|
+
|
775
815
|
```js
|
776
|
-
app.use(
|
777
|
-
|
778
|
-
|
779
|
-
|
816
|
+
app.use(
|
817
|
+
require("hof").middleware.cookies({
|
818
|
+
"cookie-name": "my-application-cookie",
|
819
|
+
"param-name": "my-query-param",
|
820
|
+
})
|
821
|
+
);
|
780
822
|
```
|
781
823
|
|
782
824
|
This middleware must be declared before your other routes.
|
783
825
|
|
784
826
|
### Options
|
827
|
+
|
785
828
|
The `cookie-name` can be the same as your session cookie. (The
|
786
829
|
middleware will not overwrite it.) Defaults to `hof-cookie-check`.
|
787
830
|
|
@@ -802,16 +845,22 @@ Kubernetes healthcheck URLs are provided as defaults if no overrides are supplie
|
|
802
845
|
Expects there to be a view called 404 in your configured `/views` directory
|
803
846
|
|
804
847
|
### Usage
|
848
|
+
|
805
849
|
```js
|
806
|
-
app.use(
|
807
|
-
|
808
|
-
|
809
|
-
|
850
|
+
app.use(
|
851
|
+
require("hof").middleware.notFound({
|
852
|
+
logger: require("/logger"),
|
853
|
+
translate: require("hof").i18n({
|
854
|
+
path: path_to_translations / __lng__ / __ns__.json,
|
855
|
+
}).translate,
|
856
|
+
})
|
857
|
+
);
|
810
858
|
```
|
811
859
|
|
812
|
-
This middleware should be declared
|
860
|
+
This middleware should be declared _after_ your other routes but _before_ your errorhandler.
|
813
861
|
|
814
862
|
### Options
|
863
|
+
|
815
864
|
`logger` can be any object with a warn method.
|
816
865
|
|
817
866
|
`translate` can be the HOF i18n translate function
|
@@ -819,42 +868,51 @@ This middleware should be declared *after* your other routes but *before* your e
|
|
819
868
|
## Errors
|
820
869
|
|
821
870
|
### Usage
|
871
|
+
|
822
872
|
```js
|
823
|
-
app.use(
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
873
|
+
app.use(
|
874
|
+
require("hof").middleware.errors({
|
875
|
+
logger: require("/logger"),
|
876
|
+
translate: require("hof").i18n({
|
877
|
+
path: path_to_translations / __lng__ / __ns__.json,
|
878
|
+
}).translate,
|
879
|
+
debug: true,
|
880
|
+
})
|
881
|
+
);
|
828
882
|
```
|
829
883
|
|
830
|
-
This middleware must be declared
|
884
|
+
This middleware must be declared _after_ your other routes.
|
831
885
|
|
832
886
|
### Options
|
887
|
+
|
833
888
|
`logger` can be any object with an error method.
|
834
889
|
|
835
890
|
`translate` can be the HOF i18n translate function
|
836
891
|
|
837
892
|
`debug` set to true will present the stack trace in the form and return the err as the content of the template.
|
838
893
|
|
839
|
-
|
840
|
-
|
894
|
+
# **Note** If `debug === true` translations will not be served, but the error handler default messages
|
895
|
+
|
841
896
|
## Deep translate
|
842
897
|
|
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.
|
898
|
+
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
899
|
|
845
900
|
### Usage
|
846
901
|
|
847
902
|
```js
|
848
|
-
const i18nFuture = require(
|
903
|
+
const i18nFuture = require("hof").i18n;
|
849
904
|
const i18n = i18nFuture({
|
850
|
-
path: path.resolve(__dirname,
|
851
|
-
})
|
852
|
-
app.use(
|
853
|
-
|
854
|
-
|
905
|
+
path: path.resolve(__dirname, "./path/to/translations"),
|
906
|
+
});
|
907
|
+
app.use(
|
908
|
+
require("hof").middleware.deepTranslate({
|
909
|
+
translate: i18n.translate.bind(i18n),
|
910
|
+
})
|
911
|
+
);
|
855
912
|
```
|
856
913
|
|
857
914
|
locales
|
915
|
+
|
858
916
|
```json
|
859
917
|
"fields": {
|
860
918
|
"field-name": {
|
@@ -876,10 +934,10 @@ locales
|
|
876
934
|
|
877
935
|
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
936
|
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
937
|
+
- If both `dependent-field` and `dependent-field-2` have the value `"value-1"`, the label returned will be `"Label 1"`.
|
938
|
+
- 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"`.
|
939
|
+
- If the value of `dependent-field` is `"value-2"` the label returned will be `"Label 3"` regardless of the value of `dependent-field-2`
|
940
|
+
- 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
941
|
|
884
942
|
# HOF Components
|
885
943
|
|
@@ -890,14 +948,15 @@ A component for handling the rendering and processing of 3-input date fields use
|
|
890
948
|
## Usage
|
891
949
|
|
892
950
|
In your fields config:
|
951
|
+
|
893
952
|
```js
|
894
|
-
const dateComponent = require(
|
953
|
+
const dateComponent = require("hof").components.date;
|
895
954
|
|
896
955
|
module.exports = {
|
897
|
-
|
898
|
-
validate: [
|
899
|
-
})
|
900
|
-
}
|
956
|
+
"date-field": dateComponent("date-field", {
|
957
|
+
validate: ["required", "before"],
|
958
|
+
}),
|
959
|
+
};
|
901
960
|
```
|
902
961
|
|
903
962
|
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 +965,17 @@ The above example will create a new date component with the key `'date-field'` a
|
|
906
965
|
|
907
966
|
The following optional configuration options are supported:
|
908
967
|
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
968
|
+
- `validate {String|Array}` - validators to use on the processed date field
|
969
|
+
- `template` - an absolute path to an alternate template.
|
970
|
+
- `dayOptional {Boolean}` - day defaults to `01` if omitted. Defaults to `false`
|
971
|
+
- `monthOptional {Boolean}` - month defaults to `01` if omitted. If true then also forces `dayOptional` to be true. Defaults to `false`
|
913
972
|
|
914
973
|
## Labels
|
915
974
|
|
916
975
|
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
976
|
|
918
977
|
fields.json
|
978
|
+
|
919
979
|
```json
|
920
980
|
{
|
921
981
|
"field-name": {
|
@@ -951,7 +1011,6 @@ If no sections config is passed, then the mixin will create a section for each s
|
|
951
1011
|
}
|
952
1012
|
```
|
953
1013
|
|
954
|
-
|
955
1014
|
Alternatively, sections can be defined manually as follows:
|
956
1015
|
|
957
1016
|
```js
|
@@ -990,9 +1049,9 @@ Fields can be defined as simple strings of the field key, in which case all defa
|
|
990
1049
|
|
991
1050
|
Alternatively, a field can be passed as an object with a `field` property defining the field key, and any additional properties as follows:
|
992
1051
|
|
993
|
-
|
994
|
-
|
995
|
-
|
1052
|
+
- `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.
|
1053
|
+
- `parse` - `Function` can parse the value for the field from the session into a value for display.
|
1054
|
+
- `derivation` - `Object` allows for a new derived field based on a combination of other fields in the form. Note that
|
996
1055
|
if both `derivation` and `parse` are specified then parse will be applied to the result of derivation. E.G.
|
997
1056
|
```javascript
|
998
1057
|
derivation: {
|
@@ -1000,10 +1059,11 @@ Alternatively, a field can be passed as an object with a `field` property defini
|
|
1000
1059
|
combiner: (values) => values.map(it => Number(it)).reduce((a, b) => a + b, 0)
|
1001
1060
|
}
|
1002
1061
|
```
|
1003
|
-
|
1004
|
-
![Firearms Use Original Value Summary Page Example](docs/images/firearms_use_original_value_summary.png)
|
1062
|
+
- `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:
|
1063
|
+
![Firearms Use Original Value Summary Page Example](docs/images/firearms_use_original_value_summary.png)
|
1064
|
+
|
1065
|
+
- `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
1066
|
|
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
1067
|
```javascript
|
1008
1068
|
{
|
1009
1069
|
field: 'location-addresses',
|
@@ -1016,6 +1076,7 @@ Alternatively, a field can be passed as an object with a `field` property defini
|
|
1016
1076
|
}
|
1017
1077
|
}
|
1018
1078
|
```
|
1079
|
+
|
1019
1080
|
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
1081
|
|
1021
1082
|
![Firearms Summary Page Example](docs/images/firearms_summary_page_example.png)
|
@@ -1030,18 +1091,19 @@ The content for section headings and field labels will be loaded from translatio
|
|
1030
1091
|
|
1031
1092
|
Translations for section headings are looked for in the following order:
|
1032
1093
|
|
1033
|
-
|
1034
|
-
|
1094
|
+
- `pages.confirm.sections.${key}.header`
|
1095
|
+
- `pages.${key}.header`
|
1035
1096
|
|
1036
1097
|
### Field labels
|
1037
1098
|
|
1038
1099
|
Translations for field labels are looked for in the following order:
|
1039
1100
|
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1101
|
+
- `pages.confirm.fields.${key}.label`
|
1102
|
+
- `fields.${key}.label`
|
1103
|
+
- `fields.${key}.legend`
|
1043
1104
|
|
1044
1105
|
# Emailer Component
|
1106
|
+
|
1045
1107
|
HOF behaviour to send emails
|
1046
1108
|
|
1047
1109
|
## Usage
|
@@ -1078,10 +1140,10 @@ steps: {
|
|
1078
1140
|
|
1079
1141
|
In addition to the options passed to `hof-emailer`, the following options can be used:
|
1080
1142
|
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1143
|
+
- `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.
|
1144
|
+
- `template` - _Required_ - defines the mustache template used to render the email content.
|
1145
|
+
- `subject` - defines the subject line of the email.
|
1146
|
+
- `parse` - parses the session model into an object used to populate the template.
|
1085
1147
|
|
1086
1148
|
`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
1149
|
|
@@ -1089,7 +1151,7 @@ In addition to the options passed to `hof-emailer`, the following options can be
|
|
1089
1151
|
// use a translated value for the email subject line
|
1090
1152
|
const emailer = EmailBehaviour({
|
1091
1153
|
// ...
|
1092
|
-
subject: (model, translate) => translate(
|
1154
|
+
subject: (model, translate) => translate("email.success.subject"),
|
1093
1155
|
});
|
1094
1156
|
```
|
1095
1157
|
|
@@ -1107,24 +1169,23 @@ $ npm install hof-emailer --save
|
|
1107
1169
|
|
1108
1170
|
```js
|
1109
1171
|
// first create an emailer instance
|
1110
|
-
const Emailer = require(
|
1172
|
+
const Emailer = require("hof").components.email.emailer;
|
1111
1173
|
const emailer = new Emailer({
|
1112
|
-
from:
|
1113
|
-
transport:
|
1174
|
+
from: "sender@example.com",
|
1175
|
+
transport: "smtp",
|
1114
1176
|
transportOptions: {
|
1115
|
-
host:
|
1116
|
-
port: 25
|
1117
|
-
}
|
1177
|
+
host: "my.smtp.host",
|
1178
|
+
port: 25,
|
1179
|
+
},
|
1118
1180
|
});
|
1119
1181
|
|
1120
1182
|
// 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
|
-
});
|
1183
|
+
const to = "recipient@example.com";
|
1184
|
+
const body = "This is the email body";
|
1185
|
+
const subject = "Important email!";
|
1186
|
+
emailer.send(to, body, subject).then(() => {
|
1187
|
+
console.log(`Email sent to ${to}!`);
|
1188
|
+
});
|
1128
1189
|
```
|
1129
1190
|
|
1130
1191
|
## Options
|
@@ -1152,6 +1213,7 @@ The following transport options are available:
|
|
1152
1213
|
- `auth.pass` <String>: Mailserver authorisation password.
|
1153
1214
|
|
1154
1215
|
### `ses`
|
1216
|
+
|
1155
1217
|
[nodemailer-ses-transport](https://github.com/andris9/nodemailer-ses-transport)
|
1156
1218
|
|
1157
1219
|
#### Options
|
@@ -1184,6 +1246,7 @@ transportOptions: {
|
|
1184
1246
|
open: true
|
1185
1247
|
}
|
1186
1248
|
```
|
1249
|
+
|
1187
1250
|
### `stub`
|
1188
1251
|
|
1189
1252
|
Disables sending email. No options are required.
|
@@ -1191,6 +1254,7 @@ Disables sending email. No options are required.
|
|
1191
1254
|
# UTILITIES
|
1192
1255
|
|
1193
1256
|
# Autofill Utility
|
1257
|
+
|
1194
1258
|
A webdriverio plugin to automate filling a form
|
1195
1259
|
|
1196
1260
|
## Usage
|
@@ -1198,10 +1262,10 @@ A webdriverio plugin to automate filling a form
|
|
1198
1262
|
First, add the command to your webdriverio client:
|
1199
1263
|
|
1200
1264
|
```js
|
1201
|
-
const webdriver = require(
|
1265
|
+
const webdriver = require("webdriverio");
|
1202
1266
|
const client = webdriver.remote(options);
|
1203
1267
|
|
1204
|
-
client.addCommand(
|
1268
|
+
client.addCommand("goto", require("hof-util-autofill")(client));
|
1205
1269
|
```
|
1206
1270
|
|
1207
1271
|
The command can be given any name you like, here we've called it `goto`.
|
@@ -1209,32 +1273,36 @@ The command can be given any name you like, here we've called it `goto`.
|
|
1209
1273
|
Then you can use the command as normal as part of your webdriver command chain.
|
1210
1274
|
|
1211
1275
|
```js
|
1212
|
-
it(
|
1213
|
-
return browser
|
1276
|
+
it("completes a form to a certain step automatically", () => {
|
1277
|
+
return browser
|
1278
|
+
.goto("/confirm")
|
1214
1279
|
.getUrl()
|
1215
1280
|
.then((url) => {
|
1216
|
-
assert.ok(url.indexOf(
|
1281
|
+
assert.ok(url.indexOf("/confirm") > -1);
|
1217
1282
|
});
|
1218
1283
|
});
|
1219
1284
|
|
1220
|
-
it(
|
1221
|
-
const inputs = {
|
1222
|
-
return browser
|
1223
|
-
|
1285
|
+
it("uses any data passed as a second argument to fill out the form", () => {
|
1286
|
+
const inputs = { "first-name": "David", "last-name": "Hasselhoff" };
|
1287
|
+
return browser
|
1288
|
+
.goto("/confirm", inputs)
|
1289
|
+
.$("span.full-name")
|
1224
1290
|
.getText()
|
1225
|
-
.then(name => {
|
1226
|
-
assert.equal(name,
|
1291
|
+
.then((name) => {
|
1292
|
+
assert.equal(name, "David HasselHoff");
|
1227
1293
|
});
|
1228
1294
|
});
|
1229
1295
|
|
1230
|
-
it(
|
1296
|
+
it("saves screenshots of errors to specified screenshot location", () => {
|
1231
1297
|
const inputs = {};
|
1232
|
-
return browser.goto(
|
1298
|
+
return browser.goto("/confirm", inputs, {
|
1299
|
+
screenshots: "/path/to/output/dir",
|
1300
|
+
});
|
1233
1301
|
});
|
1234
1302
|
|
1235
|
-
it(
|
1303
|
+
it("tries a pre-specified number of times to get past stuck loops", () => {
|
1236
1304
|
const inputs = {};
|
1237
|
-
return browser.goto(
|
1305
|
+
return browser.goto("/confirm", inputs, { maxLoops: 1 });
|
1238
1306
|
});
|
1239
1307
|
```
|
1240
1308
|
|
@@ -1242,10 +1310,11 @@ it('tries a pre-specified number of times to get past stuck loops', () => {
|
|
1242
1310
|
|
1243
1311
|
Options are passed as a third argument to the exposed method. The following options are available:
|
1244
1312
|
|
1245
|
-
|
1246
|
-
|
1313
|
+
- `maxLoops` - determines how many times a step will retry if it resolves back to itself on submission before failing. Default: `3`
|
1314
|
+
- `screenshots` - specifies a location to save screenshots of the page when it gets stuck. If not specified then no screenshots are saved.
|
1247
1315
|
|
1248
1316
|
# Test-Data Utility
|
1317
|
+
|
1249
1318
|
Generator for test fixtures
|
1250
1319
|
|
1251
1320
|
## Usage
|
@@ -1255,7 +1324,7 @@ The library contains a number of generators for values of certain types. Values
|
|
1255
1324
|
### Example:
|
1256
1325
|
|
1257
1326
|
```js
|
1258
|
-
const TestData = require(
|
1327
|
+
const TestData = require("hof").utils.testData;
|
1259
1328
|
|
1260
1329
|
console.log(TestData.name);
|
1261
1330
|
// "David Fletcher"
|
@@ -1266,22 +1335,22 @@ console.log(TestData.name);
|
|
1266
1335
|
|
1267
1336
|
## Available generators
|
1268
1337
|
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1338
|
+
- `firstname`
|
1339
|
+
- `lastname`
|
1340
|
+
- `name`
|
1341
|
+
- `email`
|
1342
|
+
- `phone`
|
1343
|
+
- `streetname`
|
1344
|
+
- `streetsuffix`
|
1345
|
+
- `address` - `${number(1,100)} ${streetname} ${streetsuffix}`
|
1346
|
+
- `postcode`
|
1347
|
+
- `country` - a random country from [homeoffice-countries](npmjs.com/homeoffice-countries)
|
1279
1348
|
|
1280
1349
|
## Functions
|
1281
1350
|
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1351
|
+
- `number(min, max)` - returns an integer between `min` and `max`
|
1352
|
+
- `number(max)` - returns an integer between 0 and `max`
|
1353
|
+
- `number()` - returns an integer between 0 and 100
|
1285
1354
|
|
1286
1355
|
# Countries Utility
|
1287
1356
|
|
@@ -1307,23 +1376,24 @@ In field configuration:
|
|
1307
1376
|
|
1308
1377
|
If needed, the following options can be passed into the countries function:
|
1309
1378
|
|
1310
|
-
|
1311
|
-
|
1379
|
+
- `filter` - `Function` - applies a filter to the list of country names before mapping them
|
1380
|
+
- `parse` - `Function` - applies a transform to the country name before setting the label
|
1312
1381
|
|
1313
1382
|
## i18n
|
1314
1383
|
|
1315
1384
|
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
1385
|
|
1317
1386
|
```js
|
1318
|
-
const countries = require(
|
1387
|
+
const countries = require("hof").utils.countries;
|
1319
1388
|
const options = countries({
|
1320
|
-
parse: country => `countries.${country.toLowerCase().split(
|
1389
|
+
parse: (country) => `countries.${country.toLowerCase().split(" ").join("-")}`,
|
1321
1390
|
});
|
1322
1391
|
```
|
1323
1392
|
|
1324
1393
|
You can then define a single translation for country names to be used for all country list instances.
|
1325
1394
|
|
1326
1395
|
# FRONTEND
|
1396
|
+
|
1327
1397
|
## Template Mixins
|
1328
1398
|
|
1329
1399
|
A middleware that exposes a series of Mustache mixins on `res.locals` to ease usage of forms, translations, and some other things.
|
@@ -1337,20 +1407,20 @@ npm install [--save] hof-template-mixins;
|
|
1337
1407
|
## Usage
|
1338
1408
|
|
1339
1409
|
```javascript
|
1340
|
-
var express = require(
|
1410
|
+
var express = require("express");
|
1341
1411
|
|
1342
|
-
var i18n = require(
|
1343
|
-
var mixins = require(
|
1412
|
+
var i18n = require("i18n-future");
|
1413
|
+
var mixins = require("hof").frontend.mixins;
|
1344
1414
|
|
1345
|
-
app.set(
|
1346
|
-
app.set(
|
1415
|
+
app.set("view engine", "html");
|
1416
|
+
app.set("views", path.join(__dirname, "/views"));
|
1347
1417
|
|
1348
1418
|
app.use(i18n.middleware());
|
1349
1419
|
app.use(mixins());
|
1350
1420
|
|
1351
1421
|
app.use(function (req, res) {
|
1352
|
-
|
1353
|
-
|
1422
|
+
// NOTE: res.locals.partials has been set.
|
1423
|
+
res.render("example-template");
|
1354
1424
|
});
|
1355
1425
|
```
|
1356
1426
|
|
@@ -1414,6 +1484,7 @@ renderField
|
|
1414
1484
|
```
|
1415
1485
|
|
1416
1486
|
### qs
|
1487
|
+
|
1417
1488
|
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
1489
|
|
1419
1490
|
```html
|
@@ -1425,34 +1496,29 @@ This mixin takes a `key=value` query string and returns a query string with the
|
|
1425
1496
|
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
1497
|
|
1427
1498
|
```html
|
1428
|
-
{{#fields}}
|
1429
|
-
{{#renderField}}{{/renderField}}
|
1430
|
-
{{/fields}}
|
1499
|
+
{{#fields}} {{#renderField}}{{/renderField}} {{/fields}}
|
1431
1500
|
```
|
1432
1501
|
|
1433
1502
|
fields.js
|
1503
|
+
|
1434
1504
|
```js
|
1435
1505
|
module.exports = {
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
}
|
1506
|
+
"my-field": {
|
1507
|
+
mixin: "input-text",
|
1508
|
+
},
|
1509
|
+
};
|
1440
1510
|
```
|
1441
1511
|
|
1442
1512
|
If mixin is omitted `input-text` will be used
|
1443
1513
|
|
1444
1514
|
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
1515
|
|
1446
|
-
|
1447
|
-
### Render a single field ###
|
1516
|
+
### Render a single field
|
1448
1517
|
|
1449
1518
|
To render a specific fields in your templates use the mixin name (matching those above) and field name like so...
|
1450
1519
|
|
1451
1520
|
```html
|
1452
|
-
{{#input-text}}myTextField{{/input-text}}
|
1453
|
-
|
1454
|
-
{{#select}}mySelectMenu{{/select}}
|
1455
|
-
|
1521
|
+
{{#input-text}}myTextField{{/input-text}} {{#select}}mySelectMenu{{/select}}
|
1456
1522
|
{{#radio-group}}myRadioGroup{{/radio-group}}
|
1457
1523
|
```
|
1458
1524
|
|
@@ -1471,11 +1537,12 @@ To render a specific fields in your templates use the mixin name (matching those
|
|
1471
1537
|
- `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
1538
|
- `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
1539
|
- `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
|
-
|
1540
|
+
- `isPageHeading`: Applicable to `checkbox` and `radio`, `text input` and `textarea` controls. Sets the legend as the page heading on single page questions.
|
1541
|
+
- `isWarning`: Applicable to `checkbox` and `radio` controls. Allows warning text to be placed after page headings on single page questions if required.
|
1475
1542
|
|
1476
1543
|
# HOF-template-partials
|
1477
1544
|
|
1478
|
-
Home Office Forms template partials is a collection of mustache partials commonly used in HOF applications.
|
1545
|
+
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
1546
|
|
1480
1547
|
## Usage
|
1481
1548
|
|
@@ -1486,14 +1553,14 @@ Home Office Forms template partials is a collection of mustache partials commonl
|
|
1486
1553
|
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
1554
|
|
1488
1555
|
```js
|
1489
|
-
var app = require(
|
1556
|
+
var app = require("express")();
|
1490
1557
|
|
1491
|
-
app.set(
|
1492
|
-
app.set(
|
1558
|
+
app.set("view engine", "html");
|
1559
|
+
app.set("views", [
|
1493
1560
|
// your application shared views
|
1494
|
-
path.resolve(__dirname,
|
1561
|
+
path.resolve(__dirname, "./path/to/views"),
|
1495
1562
|
// the module exports paths to views and translations directories
|
1496
|
-
require(
|
1563
|
+
require("hof").frontend.partials.views,
|
1497
1564
|
]);
|
1498
1565
|
```
|
1499
1566
|
|
@@ -1504,14 +1571,14 @@ The views are now available when calling `res.render('view-name')` from express.
|
|
1504
1571
|
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
1572
|
|
1506
1573
|
```js
|
1507
|
-
var app = require(
|
1574
|
+
var app = require("express")();
|
1508
1575
|
|
1509
|
-
app.set(
|
1510
|
-
app.set(
|
1511
|
-
path.resolve(__dirname,
|
1512
|
-
require(
|
1576
|
+
app.set("view engine", "html");
|
1577
|
+
app.set("views", [
|
1578
|
+
path.resolve(__dirname, "./path/to/views"),
|
1579
|
+
require("hof").frontend.partials.views,
|
1513
1580
|
]);
|
1514
|
-
app.use(require(
|
1581
|
+
app.use(require("express-partial-templates")(app));
|
1515
1582
|
|
1516
1583
|
app.use(function (req, res, next) {
|
1517
1584
|
// res.locals.partials contains all views from the views dir in this repo
|
@@ -1527,8 +1594,8 @@ The provided translations are designed to be used in conjunction with a translat
|
|
1527
1594
|
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
1595
|
|
1529
1596
|
```js
|
1530
|
-
const translate = require(
|
1531
|
-
resources: require(
|
1597
|
+
const translate = require("i18n-future").middleware({
|
1598
|
+
resources: require("hof").frontend.partials.resources(),
|
1532
1599
|
});
|
1533
1600
|
app.use(translate);
|
1534
1601
|
```
|
@@ -1536,19 +1603,19 @@ app.use(translate);
|
|
1536
1603
|
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
1604
|
|
1538
1605
|
```js
|
1539
|
-
const translate = require(
|
1540
|
-
resources: require(
|
1541
|
-
fallbackNamespace:
|
1606
|
+
const translate = require("i18n-future").middleware({
|
1607
|
+
resources: require("hof").frontend.partials.resources("hof-common"),
|
1608
|
+
fallbackNamespace: "hof-common",
|
1542
1609
|
});
|
1543
1610
|
app.use(translate);
|
1544
1611
|
```
|
1612
|
+
|
1545
1613
|
### Cookie Banner
|
1546
1614
|
|
1547
1615
|
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
1616
|
|
1549
1617
|
Set `appName` if your hof settings being passed to hof to take advantage of this.
|
1550
1618
|
|
1551
|
-
|
1552
1619
|
# HOF FRONTEND THEME
|
1553
1620
|
|
1554
1621
|
## Usage
|
@@ -1600,11 +1667,13 @@ When used as part of an express app, a middleware is returned which will add a s
|
|
1600
1667
|
It will also add the template as a mustache partial with a name of "govuk-template".
|
1601
1668
|
|
1602
1669
|
### To configure express middleware
|
1670
|
+
|
1603
1671
|
```
|
1604
1672
|
app.use(require('hof').frontend.govUKTemplate([options]);
|
1605
1673
|
```
|
1606
1674
|
|
1607
1675
|
### To use the mustache partial
|
1676
|
+
|
1608
1677
|
```
|
1609
1678
|
{{< govuk-template}}
|
1610
1679
|
{{$pageTitle}}An example page{{/pageTitle}}
|
@@ -1618,11 +1687,12 @@ app.use(require('hof').frontend.govUKTemplate([options]);
|
|
1618
1687
|
|
1619
1688
|
A number of options can be passed with the app into the setup method:
|
1620
1689
|
|
1621
|
-
|
1690
|
+
- `path` - Sets the base path for the location of static assets - Default: `/govuk-assets`
|
1622
1691
|
|
1623
1692
|
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
1693
|
|
1625
1694
|
# Nonce values
|
1695
|
+
|
1626
1696
|
Version 18.0.0 and above of HOF provides and requires a nonce value for all inline javascript, as unsafe-inline is disabled.
|
1627
1697
|
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
1698
|
if a nonce value is provided by the version of HOF.
|
@@ -1636,16 +1706,25 @@ There is an example implementation in [demo application](https://github.com/UKHo
|
|
1636
1706
|
There is a sandbox application for developers to test components directly in hof called [sandbox](/sandbox)
|
1637
1707
|
|
1638
1708
|
# HOF FRONTEND TOOLKIT
|
1709
|
+
|
1639
1710
|
Set of common UI patterns/styles for HOF projects
|
1640
1711
|
|
1641
1712
|
## Images
|
1713
|
+
|
1642
1714
|
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
1715
|
|
1644
1716
|
## Vendor JavaScript
|
1717
|
+
|
1645
1718
|
Additional vendor JavaScript files are included. These are:
|
1646
1719
|
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1720
|
+
- details.polyfill.js
|
1721
|
+
- indexof.polyfill.js
|
1722
|
+
- safari-cachebuster.js
|
1650
1723
|
|
1651
1724
|
Copy `assets/javascript/vendor` into your javascript directory (ie `hmpo/vendor`) and compile them with your JavaScript.
|
1725
|
+
|
1726
|
+
## Journey Header Navigation.html page
|
1727
|
+
|
1728
|
+
- Navigation.html contains a journeyHeaderURL, which is set in the controller.
|
1729
|
+
- getJourneyHeaderURL within the controller translates an empty baseURL to '/'.
|
1730
|
+
- The above helps fix broken journey header URLs in the GRO and UKVIC services which both have a baseURL's set to '/'.
|