hof 20.1.2 → 20.1.4

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -1530,6 +1530,7 @@ To render a specific fields in your templates use the mixin name (matching those
1530
1530
  - `required`: Value applied to `aria-required` HTML attribute.
1531
1531
  - `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.
1532
1532
  - `maxlength`: Applicable to text-based fields and mapped to the `maxlength` HTML attribute.
1533
+ - `maxword`: Applicable to textarea fields.
1533
1534
  - `options`: Applicable to HTML `select` and `radio` controls and used to generate the items of either HTML element.
1534
1535
  - `selected`: Applicable to `select`, `checkbox`, and `radio` controls. Will render the selected HTML option/element selected or checked.
1535
1536
  - `legend`: Applicable to `radio` button controls, which are wrapped in a HTML `fieldset` with a `legend` element.
@@ -25,6 +25,7 @@ This allows you to specify fields to loop over and add as objects to a parent ar
25
25
  ],
26
26
  removePrefix: 'storage-',
27
27
  combineValuesToSingleField: 'address',
28
+ groupOptional: true,
28
29
  returnTo: '/add-address'
29
30
  }),
30
31
  next: '/confirm'
@@ -35,5 +36,6 @@ Here are the fields you call this behaviour first to set config for it:
35
36
  `fieldsToGroup`: (Required) the fields being specified for an object, e.g. house number, street, postcode, that are grouped together,
36
37
  `removePrefix`: (Optional) a string which is used to remove consistent prefixes from a collection of fields that are grouped together,
37
38
  `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
+ `groupOptional`: (Optional) set this to true if you want to land on the radio button question if all records in the group are deleted after creation,
40
+ `returnTo`: (Required) the next step if you want to add another object to this group
39
41
  ```
@@ -5,7 +5,7 @@ const path = require('path');
5
5
  const express = require('express');
6
6
 
7
7
  module.exports = config => {
8
- const { returnTo, groupName, fieldsToGroup, combineValuesToSingleField, removePrefix } = config;
8
+ const { returnTo, groupName, fieldsToGroup, combineValuesToSingleField, removePrefix, groupOptional } = config;
9
9
 
10
10
  if (removePrefix && typeof removePrefix !== 'string') {
11
11
  throw new Error('removePrefix is a string and is optional for loops');
@@ -71,7 +71,7 @@ module.exports = config => {
71
71
  });
72
72
  }
73
73
 
74
- const target = items.length ? req.form.options.route : returnTo;
74
+ const target = (items.length || groupOptional) ? req.form.options.route : returnTo;
75
75
  const action = req.params.action || '';
76
76
  res.redirect(path.join(req.baseUrl, target, action));
77
77
  }
@@ -84,7 +84,10 @@ module.exports = config => {
84
84
  validate: ['required'],
85
85
  options: [
86
86
  'yes', 'no'
87
- ]
87
+ ],
88
+ legend: {
89
+ className: 'visuallyhidden'
90
+ }
88
91
  }, req.form.options.fieldSettings);
89
92
 
90
93
  // add conditonal fork
@@ -95,9 +98,6 @@ module.exports = config => {
95
98
  condition: {
96
99
  field: field,
97
100
  value: 'yes'
98
- },
99
- legend: {
100
- className: 'visuallyhidden'
101
101
  }
102
102
  });
103
103
  next();
@@ -62,6 +62,10 @@ module.exports = Validators = {
62
62
  return Validators.string(value) && (value === '' || value.length <= length);
63
63
  },
64
64
 
65
+ maxword(value, length) {
66
+ return Validators.string(value) && (value === '' || value.split(/\s+/).length <= length);
67
+ },
68
+
65
69
  exactlength(value, length) {
66
70
  return Validators.string(value) && (value === '' || value.length === length);
67
71
  },
@@ -42,6 +42,15 @@ module.exports = function (options) {
42
42
  return null;
43
43
  }
44
44
 
45
+ function maxword(field) {
46
+ const validation = field.validate || [];
47
+ const mw = _.findWhere(validation, { type: 'maxword' });
48
+ if (mw) {
49
+ return _.isArray(mw.arguments) ? mw.arguments[0] : mw.arguments;
50
+ }
51
+ return null;
52
+ }
53
+
45
54
  function type(field) {
46
55
  return field.type || 'text';
47
56
  }
@@ -208,6 +217,7 @@ module.exports = function (options) {
208
217
  hintId: extension.hintId || (hint ? key + '-hint' : null),
209
218
  error: this.errors && this.errors[key],
210
219
  maxlength: maxlength(field) || extension.maxlength,
220
+ maxword: maxword(field) || extension.maxword,
211
221
  required: required,
212
222
  pattern: extension.pattern,
213
223
  date: extension.date,
@@ -216,6 +226,7 @@ module.exports = function (options) {
216
226
  isPageHeading: field.isPageHeading,
217
227
  attributes: field.attributes,
218
228
  isPrefixOrSuffix: _.map(field.attributes, item => {if (item.prefix || item.suffix !== undefined) return true;}),
229
+ isMaxlengthOrMaxword: maxlength(field) || extension.maxlength || maxword(field) || extension.maxword,
219
230
  renderChild: renderChild.bind(this)
220
231
  });
221
232
  }
@@ -1,32 +1,35 @@
1
- {{#maxlength}}
2
- <div class="govuk-character-count" data-module="govuk-character-count" data-maxlength="{{maxlength}}">
3
- {{/maxlength}}
4
- <div id="{{id}}-group" class="{{#compound}}form-group-compound {{/compound}}{{#formGroupClassName}}{{formGroupClassName}}{{/formGroupClassName}}{{#error}} govuk-form-group--error{{/error}}">
5
- {{#isPageHeading}}<h1 class="govuk-label-wrapper">{{/isPageHeading}}<label for="{{id}}" class="{{labelClassName}}{{#isPageHeading}}govuk-label--l{{/isPageHeading}}">
6
- {{{label}}}
7
- {{#error}}
8
- <p id="{{id}}-error" class="govuk-error-message">
9
- <span id="{{id}}-error" class="govuk-visually-hidden">Error:</span>{{error.message}}
10
- </p>
11
- {{/error}}
12
- </label>
13
- {{#isPageHeading}}</h1>{{/isPageHeading}}
14
- {{#hint}}<div {{$hintId}}id="{{hintId}}" {{/hintId}}class="govuk-hint">{{hint}}</div>{{/hint}}
15
- {{#renderChild}}{{/renderChild}}
16
- <textarea
17
- name="{{id}}"
18
- id="{{id}}"
19
- class="govuk-textarea{{#className}} {{className}}{{/className}} {{#maxlength}}maxlength{{/maxlength}}{{#error}} govuk-input--error{{/error}}"
20
- aria-required="{{required}}"
21
- {{#maxlength}} maxlength="{{maxlength}}"{{/maxlength}}
22
- {{#attributes}}
23
- {{attribute}}="{{value}}"
24
- {{/attributes}}
25
- {{^error}}{{#hintId}}{{#maxlength}} aria-describedby="{{id}}-maxlength-hint {{hintId}}"{{/maxlength}}{{/hintId}}{{/error}}
26
- {{^error}}{{#hintId}}{{^maxlength}} aria-describedby="{{hintId}}"{{/maxlength}}{{/hintId}}{{/error}}
27
- {{^error}}{{^hintId}}{{#maxlength}} aria-describedby="{{id}}-maxlength-hint"{{/maxlength}}{{/hintId}}{{/error}}
28
- {{#error}} aria-invalid="true" aria-describedby="{{id}}-error"{{/error}}
29
- >{{value}}</textarea>
30
- {{#maxlength}}<div id="{{id}}-maxlength-hint" class="govuk-character-count__message" aria-live="polite">You have {{maxlength}} characters remaining</div>{{/maxlength}}
31
- </div>
32
- {{#maxlength}}</div>{{/maxlength}}
1
+ {{#isMaxlengthOrMaxword}}<div class="govuk-character-count" data-module="govuk-character-count"
2
+ {{#maxlength}}data-maxlength="{{maxlength}}" {{/maxlength}} {{#maxword}}data-maxwords="{{maxword}}" {{/maxword}}>
3
+ {{/isMaxlengthOrMaxword}}
4
+ <div id="{{id}}-group" class="govuk-form-group {{#formGroupClassName}}{{formGroupClassName}}{{/formGroupClassName}}{{#error}} govuk-form-group--error{{/error}}">
5
+ {{#isPageHeading}}<h1 class="govuk-label-wrapper">{{/isPageHeading}}
6
+ <label for="{{id}}"
7
+ class="{{labelClassName}}{{#isPageHeading}}govuk-label--l{{/isPageHeading}}">
8
+ {{{label}}}
9
+ {{#error}}
10
+ <p id="{{id}}-error" class="govuk-error-message">
11
+ <span id="{{id}}-error" class="govuk-visually-hidden">Error:</span>{{error.message}}
12
+ </p>
13
+ {{/error}}
14
+ </label>
15
+ {{#isPageHeading}}</h1>{{/isPageHeading}}
16
+ {{#hint}}<div {{$hintId}}id="{{hintId}}" {{/hintId}}class="govuk-hint">{{hint}}</div>{{/hint}}
17
+ {{#renderChild}}{{/renderChild}}
18
+ <textarea name="{{id}}" id="{{id}}"
19
+ class="govuk-textarea {{#isMaxlengthOrMaxword}}govuk-js-character-count{{/isMaxlengthOrMaxword}} {{#className}}{{className}}{{/className}} {{#error}}govuk-input--error{{/error}}"
20
+ aria-describedby="{{id}}-info"
21
+ {{#attributes}}
22
+ {{attribute}}="{{value}}"
23
+ {{/attributes}}
24
+ {{^error}}{{#hintId}}{{#maxlength}} aria-describedby="{{id}}-maxlength-hint {{hintId}}"{{/maxlength}}{{/hintId}}{{/error}}
25
+ {{^error}}{{#hintId}}{{^maxlength}} aria-describedby="{{hintId}}"{{/maxlength}}{{/hintId}}{{/error}}
26
+ {{^error}}{{^hintId}}{{#maxlength}}aria-describedby="{{id}}-maxlength-hint" {{/maxlength}}{{/hintId}}{{/error}}
27
+ {{#error}} aria-invalid="true" aria-describedby="{{id}}-error" {{/error}}
28
+ >{{value}}</textarea>
29
+ {{#isMaxlengthOrMaxword}}
30
+ <div id="{{id}}-info" class=" govuk-hint govuk-character-count__message" aria-live="polite">You have {{#maxlength}}{{maxlength}} characters{{/maxlength}}{{#maxword}}{{maxword}}
31
+ words{{/maxword}} remaining
32
+ </div>
33
+ {{/isMaxlengthOrMaxword}}
34
+ </div>
35
+ {{#isMaxlengthOrMaxword}}</div>{{/isMaxlengthOrMaxword}}
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.1.2",
4
+ "version": "20.1.4",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
7
7
  "author": "HomeOffice",
@@ -135,7 +135,7 @@
135
135
  "reporter": "spec",
136
136
  "require": "test/common.js",
137
137
  "recursive": "true",
138
- "timeout": "6000",
138
+ "timeout": "7000",
139
139
  "exit": "true"
140
140
  },
141
141
  "resolutions": {
@@ -16,9 +16,6 @@ module.exports = {
16
16
  },
17
17
  name: {
18
18
  validate: ['required', 'notUrl', { type: 'maxlength', arguments: 200 }],
19
- // need to remove this for the heading to go
20
- labelClassName: ['govuk-label--l'],
21
- isPageHeading: 'true'
22
19
  },
23
20
  'dateOfBirth': dateComponent('dateOfBirth', {
24
21
  mixin: 'input-date',
@@ -70,8 +67,6 @@ module.exports = {
70
67
  ]
71
68
  },
72
69
  email: {
73
- isPageHeading: 'true',
74
- labelClassName: ['govuk-label--l'],
75
70
  validate: ['required', 'email']
76
71
  },
77
72
  phone: {
@@ -95,11 +90,10 @@ module.exports = {
95
90
  mixin: 'textarea',
96
91
  // we want to ignore default formatters as we want
97
92
  // to preserve white space
98
- isPageHeading: 'true',
99
93
  'ignore-defaults': true,
100
94
  // apply the other default formatters
101
95
  formatter: ['trim', 'hyphens'],
102
- labelClassName: ['govuk-label--l'],
96
+ isPageHeading: 'true',
103
97
  // attributes here are passed to the field element
104
98
  validate: ['required', { type: 'maxlength', arguments: 10 }],
105
99
  attributes: [{
@@ -107,6 +101,21 @@ module.exports = {
107
101
  value: 8
108
102
  }]
109
103
  },
104
+ whatHappened: {
105
+ mixin: 'textarea',
106
+ // we want to ignore default formatters as we want
107
+ // to preserve white space
108
+ 'ignore-defaults': true,
109
+ // apply the other default formatters
110
+ formatter: ['trim', 'hyphens'],
111
+ isPageHeading: 'true',
112
+ // attributes here are passed to the field element
113
+ validate: ['required', { type: 'maxword', arguments: 10 }],
114
+ attributes: [{
115
+ attribute: 'rows',
116
+ value: 8
117
+ }]
118
+ },
110
119
  appealStages: {
111
120
  mixin: 'select',
112
121
  isPageHeading: 'true',
@@ -66,6 +66,10 @@ module.exports = {
66
66
  },
67
67
  '/text-input-area': {
68
68
  fields: ['complaintDetails'],
69
+ next: '/word-count'
70
+ },
71
+ '/word-count': {
72
+ fields: ['whatHappened'],
69
73
  next: '/select'
70
74
  },
71
75
  '/select':{
@@ -36,5 +36,8 @@ module.exports = {
36
36
  ],
37
37
  complaintDetails: [
38
38
  'complaintDetails'
39
+ ],
40
+ whatHappened: [
41
+ 'whatHappened'
39
42
  ]
40
43
  };
@@ -15,7 +15,7 @@
15
15
  }
16
16
  },
17
17
  "name": {
18
- "label": "What is your full name?"
18
+ "label": "Full name"
19
19
  },
20
20
  "dateOfBirth": {
21
21
  "legend": "What is your date of birth?",
@@ -70,7 +70,7 @@
70
70
  }
71
71
  },
72
72
  "email" : {
73
- "label": "Enter your email address"
73
+ "label": "Email address"
74
74
  },
75
75
  "phone": {
76
76
  "label": "Phone number",
@@ -83,6 +83,10 @@
83
83
  "label": "Complaint details",
84
84
  "hint": "Briefly summarise your complaint. Include anything that can help our investigation."
85
85
  },
86
+ "whatHappened": {
87
+ "label": "What happened",
88
+ "hint": "Briefly summarise what happened."
89
+ },
86
90
  "countrySelect": {
87
91
  "label": "Which country are you based in?",
88
92
  "hint": "Start to type the country name and options will appear"
@@ -10,6 +10,12 @@
10
10
  "header": "What is your address in the UK?",
11
11
  "intro": "If you have no fixed address, enter an address where we can contact you."
12
12
  },
13
+ "name": {
14
+ "header": "What is your full name?"
15
+ },
16
+ "email": {
17
+ "header": "Enter your email address"
18
+ },
13
19
  "phone-number": {
14
20
  "header": "Enter your phone number"
15
21
  },
@@ -36,6 +42,9 @@
36
42
  },
37
43
  "complaintDetails": {
38
44
  "header": "Complaint details"
45
+ },
46
+ "whatHappened": {
47
+ "header": "What happened"
39
48
  }
40
49
  }
41
50
  },
@@ -44,6 +44,10 @@
44
44
  "default": "Enter details about why you are making a complaint",
45
45
  "maxlength": "Keep to the {{maxlength}} character limit"
46
46
  },
47
+ "whatHappened": {
48
+ "default": "Enter details about what happened",
49
+ "maxword": "Keep to the {{maxword}} word limit"
50
+ },
47
51
  "appealStages": {
48
52
  "required": "Select an appeal stage from the list"
49
53
  }