hof 20.0.4 → 20.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ # Combine & Loop Fields Behaviour
2
+
3
+ ## What this does
4
+ This allows you to specify fields to loop over and add as objects to a parent array. You can use this for adding multiple addresses, criminal offences, names etc. You can see here in this example, we ask for a set of details prefixed with the word `storage-` for getting address details for multiple storage addresses (see the Home Office's firearms repository). We then can aggregate these addresses as objects into an array called `all-storage-addresses` and get redirected back to `/add-address` whenever we selected `yes` to adding another address,
5
+ ```
6
+ '/add-address': {
7
+ fields: [
8
+ 'storage-building',
9
+ 'storage-street',
10
+ 'storage-townOrCity',
11
+ 'storage-postcodeOrZIPCode'
12
+ ],
13
+ next: '/add-another-address-with-list',
14
+ continueOnEdit: true
15
+ },
16
+ '/add-another-address-with-list': {
17
+ template: 'add-another-address-loop.html',
18
+ behaviours: CombineAndLoopFields({
19
+ groupName: 'all-storage-addresses',
20
+ fieldsToGroup: [
21
+ 'storage-building',
22
+ 'storage-street',
23
+ 'storage-townOrCity',
24
+ 'storage-postcodeOrZIPCode'
25
+ ],
26
+ removePrefix: 'storage-',
27
+ combineValuesToSingleField: 'address',
28
+ returnTo: '/add-address'
29
+ }),
30
+ next: '/confirm'
31
+ ```
32
+ Here are the fields you call this behaviour first to set config for it:
33
+ ```
34
+ `groupName`: (Required) a parent array for storing details for each object you are collecting information for,
35
+ `fieldsToGroup`: (Required) the fields being specified for an object, e.g. house number, street, postcode, that are grouped together,
36
+ `removePrefix`: (Optional) a string which is used to remove consistent prefixes from a collection of fields that are grouped together,
37
+ `combineValuesToSingleField`: (Optional) a new field that is created with its value being the concatenation of values of the fields specified in `fieldsToGroup`,
38
+ `returnTo`: the next step if you want to add another object to this group
39
+ ```
@@ -0,0 +1,153 @@
1
+
2
+ const _ = require('lodash');
3
+ const uuid = require('uuid').v1;
4
+ const path = require('path');
5
+ const express = require('express');
6
+
7
+ module.exports = config => {
8
+ const { returnTo, groupName, fieldsToGroup, combineValuesToSingleField, removePrefix } = config;
9
+
10
+ if (removePrefix && typeof removePrefix !== 'string') {
11
+ throw new Error('removePrefix is a string and is optional for loops');
12
+ }
13
+
14
+ if (combineValuesToSingleField && typeof combineValuesToSingleField !== 'string') {
15
+ throw new Error('combineValuesToSingleField is a string and is optional for loops');
16
+ }
17
+
18
+ if (!returnTo || typeof returnTo !== 'string') {
19
+ throw new Error('returnTo is a string and is required for loops');
20
+ }
21
+
22
+ if (!groupName || typeof groupName !== 'string') {
23
+ throw new Error('groupName is a string and is required for loops');
24
+ }
25
+
26
+ if (!fieldsToGroup ||
27
+ !fieldsToGroup.length ||
28
+ !Array.isArray(fieldsToGroup) ||
29
+ _.some(fieldsToGroup, field => typeof field !== 'string')) {
30
+ throw new Error('fieldsToGroup is an array of strings and is required for loops');
31
+ }
32
+
33
+ return superclass => class extends superclass {
34
+ get(req, res, next) {
35
+ if (req.query.delete) {
36
+ const router = express.Router({ mergeParams: true });
37
+ router.use([
38
+ // eslint-disable-next-line no-underscore-dangle
39
+ this._configure.bind(this),
40
+ this.removeItem.bind(this),
41
+ this.reload.bind(this)
42
+ ]);
43
+ return router.handle(req, res, next);
44
+ }
45
+ return super.get(req, res, next);
46
+ }
47
+
48
+ getLoopFields(req) {
49
+ let loopedFields = _.pick(req.sessionModel.toJSON(), fieldsToGroup);
50
+
51
+ if (removePrefix) {
52
+ loopedFields = _.mapKeys(loopedFields, (value, key) => key.replace(removePrefix, ''));
53
+ }
54
+ return loopedFields;
55
+ }
56
+
57
+ removeItem(req, res, next) {
58
+ const id = req.query.delete;
59
+ const items = req.sessionModel.get(groupName).filter(item => item.id !== id);
60
+ req.sessionModel.set(groupName, items);
61
+ next();
62
+ }
63
+
64
+ // eslint-disable-next-line no-unused-vars
65
+ reload(req, res, next) {
66
+ const items = req.sessionModel.get(groupName);
67
+ if (!items.length) {
68
+ req.sessionModel.set(`${groupName}-saved`, false);
69
+ fieldsToGroup.forEach(field => {
70
+ req.sessionModel.unset(field);
71
+ });
72
+ }
73
+
74
+ const target = items.length ? req.form.options.route : returnTo;
75
+ const action = req.params.action || '';
76
+ res.redirect(path.join(req.baseUrl, target, action));
77
+ }
78
+
79
+ configure(req, res, next) {
80
+ const field = `${groupName}-add-another`;
81
+ // add yes/no field
82
+ req.form.options.fields[field] = Object.assign({
83
+ mixin: 'radio-group',
84
+ validate: ['required'],
85
+ options: [
86
+ 'yes', 'no'
87
+ ]
88
+ }, req.form.options.fieldSettings);
89
+
90
+ // add conditonal fork
91
+ req.form.options.forks = req.form.options.forks || [];
92
+ req.form.options.forks.push({
93
+ target: returnTo,
94
+ continueOnEdit: true,
95
+ condition: {
96
+ field: field,
97
+ value: 'yes'
98
+ }
99
+ });
100
+ next();
101
+ }
102
+
103
+ getValues(req, res, next) {
104
+ const fieldsGroup = req.sessionModel.get(groupName) || [];
105
+ const added = req.sessionModel.get(`${groupName}-saved`);
106
+ return super.getValues(req, res, (err, values) => {
107
+ if (err) {
108
+ return next(err);
109
+ }
110
+ if (!added) {
111
+ const fields = this.getLoopFields(req);
112
+ if (!_.isEmpty(fields)) {
113
+ const newField = Object.assign({id: uuid()}, fields);
114
+
115
+ if (combineValuesToSingleField) {
116
+ const combinedValues = _.filter(fieldsToGroup.map(field => req.sessionModel.get(field))).join(', ');
117
+ newField[combineValuesToSingleField] = combinedValues;
118
+ }
119
+
120
+ fieldsGroup.push(newField);
121
+ values[groupName] = fieldsGroup;
122
+ fieldsToGroup.forEach(field => req.sessionModel.unset(field));
123
+
124
+ req.sessionModel.set(groupName, fieldsGroup);
125
+ req.sessionModel.set(`${groupName}-saved`, true);
126
+ }
127
+ }
128
+ return next(null, values);
129
+ });
130
+ }
131
+
132
+ locals(req, res) {
133
+ const items = req.form.values[groupName] || [];
134
+ return Object.assign({}, super.locals(req, res), {
135
+ items,
136
+ hasItems: items.length > 0,
137
+ field: groupName
138
+ });
139
+ }
140
+
141
+ saveValues(req, res, next) {
142
+ // remove "yes" value from session so it is no pre-populated next time around
143
+ super.saveValues(req, res, err => {
144
+ const field = `${groupName}-add-another`;
145
+ if (req.form.values[field] === 'yes') {
146
+ req.sessionModel.unset(field);
147
+ req.sessionModel.set(`${groupName}-saved`, false);
148
+ }
149
+ next(err);
150
+ });
151
+ }
152
+ };
153
+ };
@@ -3,8 +3,10 @@
3
3
  module.exports = {
4
4
  addressLookup: require('./address-lookup'),
5
5
  clearSession: require('./clear-session'),
6
+ combineAndLoopFields: require('./combine-and-loop-fields'),
6
7
  date: require('./date'),
7
8
  emailer: require('./emailer'),
8
- summary: require('./summary'),
9
- notify: require('./notify')
9
+ homeOfficeCountries: require('./homeoffice-countries'),
10
+ notify: require('./notify'),
11
+ summary: require('./summary')
10
12
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hof",
3
3
  "description": "A bootstrap for HOF projects",
4
- "version": "20.0.4",
4
+ "version": "20.1.1",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
7
7
  "author": "HomeOffice",