hof 20.0.0-beta.9 → 20.0.0-redis-beta.32-redis-beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. package/.github/workflows/automate-publish.yml +1 -1
  2. package/.github/workflows/automate-tag.yml +4 -4
  3. package/.nyc_output/cb764db8-e9f0-43bb-b3b1-7af57bb79bb5.json +1 -0
  4. package/.nyc_output/processinfo/cb764db8-e9f0-43bb-b3b1-7af57bb79bb5.json +1 -0
  5. package/.nyc_output/processinfo/index.json +1 -1
  6. package/README.md +329 -256
  7. package/components/index.js +2 -1
  8. package/components/notify/index.js +60 -0
  9. package/components/notify/notify.js +25 -0
  10. package/config/hof-defaults.js +2 -1
  11. package/controller/controller.js +14 -4
  12. package/frontend/template-mixins/mixins/template-mixins.js +7 -3
  13. package/frontend/template-mixins/partials/forms/checkbox-group.html +10 -1
  14. package/frontend/template-mixins/partials/forms/input-text-date.html +1 -1
  15. package/frontend/template-mixins/partials/forms/input-text-group.html +5 -3
  16. package/frontend/template-mixins/partials/forms/option-group.html +9 -0
  17. package/frontend/template-mixins/partials/forms/select.html +1 -1
  18. package/frontend/template-mixins/partials/forms/textarea-group.html +2 -2
  19. package/frontend/template-partials/views/layout.html +10 -3
  20. package/frontend/template-partials/views/partials/cookie-banner.html +1 -1
  21. package/frontend/template-partials/views/partials/form.html +2 -1
  22. package/frontend/template-partials/views/partials/warn.html +7 -0
  23. package/frontend/themes/gov-uk/styles/govuk.scss +4 -0
  24. package/frontend/themes/gov-uk/styles/modules/_validation.scss +2 -2
  25. package/frontend/toolkit/assets/javascript/form-focus.js +10 -1
  26. package/frontend/toolkit/assets/javascript/progressive-reveal.js +3 -1
  27. package/frontend/toolkit/assets/javascript/validation.js +6 -1
  28. package/lib/health.js +1 -1
  29. package/lib/sessions.js +1 -1
  30. package/middleware/rate-limiter.js +3 -1
  31. package/package.json +4 -3
  32. package/sandbox/apps/sandbox/fields.js +1 -0
  33. package/sandbox/apps/sandbox/index.js +1 -5
  34. package/sandbox/apps/sandbox/translations/en/default.json +36 -15
  35. package/sandbox/assets/scss/app.scss +0 -52
  36. package/sandbox/package.json +2 -0
  37. package/sandbox/public/css/app.css +21 -6666
  38. package/sandbox/public/js/bundle.js +37 -2793
  39. package/sandbox/server.js +2 -1
  40. package/sandbox/yarn.lock +39 -564
  41. package/.nyc_output/e2fdc3eb-4fd2-47e0-a392-fe5f665776a4.json +0 -1
  42. package/.nyc_output/processinfo/e2fdc3eb-4fd2-47e0-a392-fe5f665776a4.json +0 -1
  43. package/.vscode/settings.json +0 -6
  44. package/frontend/govuk-template/govuk_template_generated.html +0 -104
  45. package/sandbox/.env +0 -1
@@ -5,5 +5,6 @@ module.exports = {
5
5
  clearSession: require('./clear-session'),
6
6
  date: require('./date'),
7
7
  emailer: require('./emailer'),
8
- summary: require('./summary')
8
+ summary: require('./summary'),
9
+ notify: require('./notify')
9
10
  };
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+
3
+ const Notify = require('./notify');
4
+ const Hogan = require('hogan.js');
5
+ const fs = require('fs');
6
+
7
+ module.exports = config => {
8
+ const notify = new Notify(config);
9
+ config.parse = config.parse || (data => data);
10
+
11
+ if (!config.recipient) {
12
+ throw new Error('Email recipient must be defined');
13
+ }
14
+ if (typeof config.template !== 'string') {
15
+ throw new Error('Email template must be defined');
16
+ }
17
+
18
+ return superclass => class NotifyBehaviour extends superclass {
19
+ successHandler(req, res, next) {
20
+ Promise.resolve()
21
+ .then(() => {
22
+ return new Promise((resolve, reject) => {
23
+ fs.readFile(config.template, (err, template) => err ? reject(err) : resolve(template.toString('utf8')));
24
+ });
25
+ })
26
+ .then(template => {
27
+ const data = config.parse(req.sessionModel.toJSON(), req.translate);
28
+ return Hogan.compile(template).render(data);
29
+ })
30
+ .then(body => {
31
+ const settings = { body };
32
+
33
+ if (typeof config.recipient === 'function') {
34
+ settings.recipient = config.recipient(req.sessionModel.toJSON());
35
+ } else {
36
+ settings.recipient = req.sessionModel.get(config.recipient) || config.recipient;
37
+ }
38
+ if (typeof settings.recipient !== 'string' || !settings.recipient.includes('@')) {
39
+ throw new Error('hof-behaviour-emailer: invalid recipient');
40
+ }
41
+
42
+ if (typeof config.subject === 'function') {
43
+ settings.subject = config.subject(req.sessionModel.toJSON(), req.translate);
44
+ } else {
45
+ settings.subject = config.subject;
46
+ }
47
+
48
+ return settings;
49
+ })
50
+ .then(settings => {
51
+ return notify.send(settings);
52
+ })
53
+ .then(() => {
54
+ super.successHandler(req, res, next);
55
+ }, next);
56
+ }
57
+ };
58
+ };
59
+
60
+ module.exports.Notify = Notify;
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+ const NotifyClient = require('notifications-node-client').NotifyClient;
3
+ const {v4: uuidv4} = require('uuid');
4
+
5
+ module.exports = class Notify {
6
+ constructor(opts) {
7
+ const options = opts || {};
8
+ this.options = options;
9
+ this.notifyClient = new NotifyClient(options.notifyApiKey);
10
+ this.notifyTemplate = options.notifyTemplate;
11
+ }
12
+
13
+ send(email) {
14
+ const reference = uuidv4();
15
+
16
+ return this.notifyClient.sendEmail(this.notifyTemplate, email.recipient, {
17
+ personalisation: {
18
+ 'email-subject': email.subject,
19
+ 'email-body': email.body
20
+ },
21
+ reference });
22
+ }
23
+ };
24
+
25
+ module.exports.NotifyClient = NotifyClient;
@@ -27,7 +27,8 @@ const defaults = {
27
27
  ignoreMiddlewareLogs: ['/healthz'],
28
28
  redis: {
29
29
  port: process.env.REDIS_PORT || '6379',
30
- host: process.env.REDIS_HOST || '127.0.0.1'
30
+ host: process.env.REDIS_HOST || '127.0.0.1',
31
+ legacyMode: true
31
32
  },
32
33
  session: {
33
34
  ttl: process.env.SESSION_TTL || 1800,
@@ -115,6 +115,7 @@ module.exports = class Controller extends BaseController {
115
115
  title: this.getTitle(route, lookup, req.form.options.fields, res.locals),
116
116
  header: this.getHeader(route, lookup, res.locals),
117
117
  captionHeading: this.getCaptionHeading(route, lookup, res.locals),
118
+ warning: this.getWarning(route, lookup, res.locals),
118
119
  subHeading: this.getSubHeading(route, lookup, res.locals),
119
120
  intro: this.getIntro(route, lookup, res.locals),
120
121
  backLink: this.getBackLink(req, res),
@@ -143,6 +144,10 @@ module.exports = class Controller extends BaseController {
143
144
  return lookup(`pages.${route}.subHeading`, locals);
144
145
  }
145
146
 
147
+ getWarning(route, lookup, locals) {
148
+ return lookup(`pages.${route}.warning`, locals);
149
+ }
150
+
146
151
  getTitle(route, lookup, fields, locals) {
147
152
  let fieldName = '';
148
153
  if (_.size(fields)) {
@@ -166,13 +171,18 @@ module.exports = class Controller extends BaseController {
166
171
  Object.keys(req.form.errors).forEach(key => {
167
172
  if (req.form && req.form.options && req.form.options.fields) {
168
173
  const field = req.form.options.fields[key];
169
- // get first option for radios
170
- if (field.mixin === 'radio-group') {
171
- req.form.errors[key].errorLinkId = key + '-' + field.options[0];
174
+ // get first option for radios and checkbox
175
+ if (field.mixin === 'radio-group' || field.mixin === 'checkbox-group') {
176
+ // get first option for radios and checkbox where there is a toggle
177
+ if(typeof field.options[0] === 'object') {
178
+ req.form.errors[key].errorLinkId = key + '-' + field.options[0].value;
179
+ } else {
180
+ req.form.errors[key].errorLinkId = key + '-' + field.options[0];
181
+ }
172
182
  // eslint-disable-next-line brace-style
173
183
  }
174
184
  // get first field for date input control
175
- else if (field && field.controlType === 'date-input') {
185
+ else if (field && field.mixin === 'input-date') {
176
186
  req.form.errors[key].errorLinkId = key + '-day';
177
187
  } else {
178
188
  req.form.errors[key].errorLinkId = key;
@@ -215,6 +215,7 @@ module.exports = function (options) {
215
215
  child: field.child,
216
216
  isPageHeading: field.isPageHeading,
217
217
  attributes: field.attributes,
218
+ isPrefixOrSuffix: _.map(field.attributes, item => {if (item.prefix || item.suffix !== undefined) return true;}),
218
219
  renderChild: renderChild.bind(this)
219
220
  });
220
221
  }
@@ -224,6 +225,7 @@ module.exports = function (options) {
224
225
  const field = Object.assign({}, this.options.fields[key] || options.fields[key]);
225
226
  const legend = field.legend;
226
227
  const detail = field.detail;
228
+ const warningValue = 'fields.' + key + '.warning';
227
229
  let legendClassName;
228
230
  let legendValue = 'fields.' + key + '.legend';
229
231
  if (legend) {
@@ -241,6 +243,8 @@ module.exports = function (options) {
241
243
  legendClassName: legendClassName,
242
244
  role: opts.type === 'radio' ? 'radiogroup' : 'group',
243
245
  isPageHeading: field.isPageHeading,
246
+ isWarning: field.isWarning,
247
+ warning: t(warningValue),
244
248
  detail: detail ? detail : '',
245
249
  hint: conditionalTranslate(getTranslationKey(field, key, 'hint')),
246
250
  options: _.map(field.options, function (obj) {
@@ -303,7 +307,7 @@ module.exports = function (options) {
303
307
  invalid: this.errors && this.errors[key] && opts.required,
304
308
  label: t(fieldLabel || 'fields.' + key + '.label'),
305
309
  selected: selected,
306
- className: classNames(field) || 'block-label',
310
+ className: classNames(field) || 'govuk-label govuk-checkboxes__label',
307
311
  child: field.child,
308
312
  renderChild: renderChild.bind(this)
309
313
  });
@@ -444,8 +448,8 @@ module.exports = function (options) {
444
448
  parts.push(dayPart);
445
449
  }
446
450
 
447
- const monthPart = compiled['partials/forms/input-text-date'].render(inputText.call(this, key + '-month', { pattern: '[0-9]*', min: 1, max: 12, maxlength: 2, hintId: key + '-hint', date: true, autocomplete: autocomplete.month, formGroupClassName, className: classNameMonth, isRequired }));
448
- const yearPart = compiled['partials/forms/input-text-date'].render(inputText.call(this, key + '-year', { pattern: '[0-9]*', maxlength: 4, hintId: key + '-hint', date: true, autocomplete: autocomplete.year, formGroupClassName, className: classNameYear, isRequired }));
451
+ const monthPart = compiled['partials/forms/input-text-date'].render(inputText.call(this, key + '-month', { pattern: '[0-9]*', min: 1, max: 12, maxlength: 2, hintId: key + '-hint', date: true, autocomplete: autocomplete.month, formGroupClassName, className: classNameMonth, isThisRequired }));
452
+ const yearPart = compiled['partials/forms/input-text-date'].render(inputText.call(this, key + '-year', { pattern: '[0-9]*', maxlength: 4, hintId: key + '-hint', date: true, autocomplete: autocomplete.year, formGroupClassName, className: classNameYear, isThisRequired }));
449
453
 
450
454
  return parts.concat(monthPart, yearPart).join('\n');
451
455
  };
@@ -5,6 +5,15 @@
5
5
  {{legend}}
6
6
  {{#isPageHeading}}</h1>{{/isPageHeading}}
7
7
  </legend>
8
+ {{#isWarning}}
9
+ <div class="govuk-warning-text">
10
+ <span class="govuk-warning-text__icon" aria-hidden="true">!</span>
11
+ <strong class="govuk-warning-text__text">
12
+ <span class="govuk-warning-text__assistive">Warning</span>
13
+ {{warning}}
14
+ </strong>
15
+ </div>
16
+ {{/isWarning}}
8
17
  {{#hint}}<div id="{{key}}-hint" class="govuk-hint">{{hint}}</div>{{/hint}}
9
18
  {{#error}}
10
19
  <p id="{{key}}-error" class="govuk-error-message">
@@ -28,7 +37,7 @@
28
37
  >
29
38
  <label class="govuk-label govuk-checkboxes__label" for="{{key}}-{{value}}">
30
39
  {{{label}}}
31
- {{#optionHint}}<div id="{{key}}-{{value}}-item-hint" class="govuk-hint govuk-radios__hint">{{optionHint}}</div>{{/optionHint}}
40
+ {{#optionHint}}<div id="{{key}}-{{value}}-item-hint" class="govuk-hint">{{optionHint}}</div>{{/optionHint}}
32
41
  </label>
33
42
  </div>
34
43
  {{#renderChild}}{{/renderChild}}
@@ -14,7 +14,7 @@
14
14
  type="{{type}}"
15
15
  name="{{id}}"
16
16
  id="{{id}}"
17
- class="govuk-input{{#className}} {{className}}{{/className}}{{#error}} invalid-input{{/error}}"
17
+ class="govuk-input{{#className}} {{className}}{{/className}}{{#error}} govuk-input--error{{/error}}"
18
18
  aria-required="{{required}}"
19
19
  {{#value}} value="{{value}}"{{/value}}
20
20
  {{#min}} min="{{min}}"{{/min}}
@@ -1,5 +1,5 @@
1
1
  <div id="{{id}}-group" class="{{#compound}} form-group-compound{{/compound}}{{#formGroupClassName}}{{formGroupClassName}}{{/formGroupClassName}}{{#error}} govuk-form-group--error{{/error}}">
2
- {{#isPageHeading}}<h1 class="govuk-label-wrapper">{{/isPageHeading}}<label for="{{id}}" class="{{labelClassName}}">
2
+ {{#isPageHeading}}<h1 class="govuk-label-wrapper">{{/isPageHeading}}<label for="{{id}}" class="{{labelClassName}}{{#isPageHeading}}govuk-label--l{{/isPageHeading}}">
3
3
  {{{label}}}
4
4
  </label>
5
5
  {{#isPageHeading}}</h1>{{/isPageHeading}}
@@ -10,6 +10,7 @@
10
10
  </p>
11
11
  {{/error}}
12
12
  {{#renderChild}}{{/renderChild}}
13
+ {{#isPrefixOrSuffix}}<div class="govuk-input__wrapper">{{/isPrefixOrSuffix}}
13
14
  {{#attributes}}
14
15
  {{#prefix}}
15
16
  <div class="govuk-input__prefix" aria-hidden="true">{{prefix}}</div>
@@ -19,7 +20,7 @@
19
20
  type="{{type}}"
20
21
  name="{{id}}"
21
22
  id="{{id}}"
22
- class="{{^className}}govuk-input{{/className}}{{#className}}{{className}}{{/className}}{{#error}} invalid-input{{/error}}"
23
+ class="{{^className}}govuk-input{{/className}}{{#className}}{{className}}{{/className}}{{#error}} govuk-input--error{{/error}}"
23
24
  aria-required="{{required}}"
24
25
  {{#value}} value="{{value}}"{{/value}}
25
26
  {{#min}} min="{{min}}"{{/min}}
@@ -35,7 +36,8 @@
35
36
  >
36
37
  {{#attributes}}
37
38
  {{#suffix}}
38
- <div class="govuk-input__prefix" aria-hidden="true">{{suffix}}</div>
39
+ <div class="govuk-input__suffix" aria-hidden="true">{{suffix}}</div>
39
40
  {{/suffix}}
40
41
  {{/attributes}}
42
+ {{#isPrefixOrSuffix}}</div>{{/isPrefixOrSuffix}}
41
43
  </div>
@@ -5,6 +5,15 @@
5
5
  {{legend}}
6
6
  {{#isPageHeading}}</h1>{{/isPageHeading}}
7
7
  </legend>
8
+ {{#isWarning}}
9
+ <div class="govuk-warning-text">
10
+ <span class="govuk-warning-text__icon" aria-hidden="true">!</span>
11
+ <strong class="govuk-warning-text__text">
12
+ <span class="govuk-warning-text__assistive">Warning</span>
13
+ {{warning}}
14
+ </strong>
15
+ </div>
16
+ {{/isWarning}}
8
17
  {{#hint}}<div id="{{key}}-hint" class="govuk-hint">{{hint}}</div>{{/hint}}
9
18
  {{#error}}<p id="{{key}}-error" class="govuk-error-message"><span class="govuk-visually-hidden">Error:</span> {{error.message}}</p>{{/error}}
10
19
  {{{detail}}}
@@ -9,7 +9,7 @@
9
9
  {{/error}}
10
10
  </label>
11
11
  {{#isPageHeading}}</h1>{{/isPageHeading}}
12
- <select id="{{id}}" class="govuk-select{{#className}} {{className}}{{/className}}{{#error}} invalid-input{{/error}}" name="{{id}}" aria-required="{{required}}">
12
+ <select id="{{id}}" class="govuk-select{{#className}} {{className}}{{/className}}{{#error}} govuk-select--error{{/error}}" name="{{id}}" aria-required="{{required}}">
13
13
  {{#options}}
14
14
  <option value="{{value}}" {{#selected}}selected{{/selected}}>{{label}}</option>
15
15
  {{/options}}
@@ -2,7 +2,7 @@
2
2
  <div class="govuk-character-count" data-module="govuk-character-count" data-maxlength="{{maxlength}}">
3
3
  {{/maxlength}}
4
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}}">
5
+ {{#isPageHeading}}<h1 class="govuk-label-wrapper">{{/isPageHeading}}<label for="{{id}}" class="{{labelClassName}}{{#isPageHeading}}govuk-label--l{{/isPageHeading}}">
6
6
  {{{label}}}
7
7
  {{#error}}
8
8
  <p id="{{id}}-error" class="govuk-error-message">
@@ -16,7 +16,7 @@
16
16
  <textarea
17
17
  name="{{id}}"
18
18
  id="{{id}}"
19
- class="govuk-textarea{{#className}} {{className}}{{/className}} {{#maxlength}}maxlength{{/maxlength}}{{#error}} invalid-input{{/error}}"
19
+ class="govuk-textarea{{#className}} {{className}}{{/className}} {{#maxlength}}maxlength{{/maxlength}}{{#error}} govuk-input--error{{/error}}"
20
20
  aria-required="{{required}}"
21
21
  {{#maxlength}} maxlength="{{maxlength}}"{{/maxlength}}
22
22
  {{#attributes}}
@@ -44,9 +44,16 @@
44
44
  {{/gaTagId}}
45
45
  {{/cookieMessage}}
46
46
  {{$footerSupportLinks}}
47
- <li class="govuk-footer__inline-list-item"><a class="govuk-footer__link" href="/cookies">{{#t}}base.cookies{{/t}}</a></li>
48
- <li class="govuk-footer__inline-list-item"><a class="govuk-footer__link" href="/accessibility">{{#t}}base.accessibility{{/t}}</a></li>
49
- <li class="govuk-footer__inline-list-item"><a class="govuk-footer__link" href="/terms-and-conditions">{{#t}}base.terms{{/t}}</a></li>
47
+ <ul>
48
+ {{#footerSupportLinks}}
49
+ <li class="govuk-footer__inline-list-item"><a href="{{path}}">{{#t}}{{property}}{{/t}}</a></li>
50
+ {{/footerSupportLinks}}
51
+ {{^footerSupportLinks}}
52
+ <li class="govuk-footer__inline-list-item"><a class="govuk-footer__link" href="/cookies">{{#t}}base.cookies{{/t}}</a></li>
53
+ <li class="govuk-footer__inline-list-item"><a class="govuk-footer__link" href="/accessibility">{{#t}}base.accessibility{{/t}}</a></li>
54
+ <li class="govuk-footer__inline-list-item"><a class="govuk-footer__link" href="/terms-and-conditions">{{#t}}base.terms{{/t}}</a></li>
55
+ {{/footerSupportLinks}}
56
+ </ul>
50
57
  {{/footerSupportLinks}}
51
58
  {{$bodyEnd}}
52
59
  {{> partials-gatag}}
@@ -20,7 +20,7 @@
20
20
  </div>
21
21
  <div class="gem-c-cookie-banner__confirmation govuk-width-container" tabindex="0" hidden="" id="cookie-banner-submitted" >
22
22
  <p class="gem-c-cookie-banner__confirmation-message" role="alert">
23
- Your cookie preferences have been saved. You can <a class="govuk-link" data-module="gem-track-click" data-track-category="cookieBanner" data-track-action="Cookie banner settings clicked from confirmation" href="/student-help/cookies">change your cookie settings</a> at any time.
23
+ Your cookie preferences have been saved. You can <a class="govuk-link" data-module="gem-track-click" data-track-category="cookieBanner" data-track-action="Cookie banner settings clicked from confirmation" href="/cookies">change your cookie settings</a> at any time.
24
24
  </p>
25
25
  <div class="govuk-button-group">
26
26
  <button class="gem-c-cookie-banner__hide-button govuk-button" id="hide-cookie-banner">Hide this message</button>
@@ -1,7 +1,8 @@
1
1
  <form action="" method="POST" {{$encoding}}{{/encoding}} autocomplete="off" novalidate="true" spellcheck="false">
2
2
  {{$intro}}
3
- {{#intro}}<span class="govuk-hint" >{{intro}}</span>{{/intro}}
3
+ {{#intro}}<p>{{intro}}</p>{{/intro}}
4
4
  {{/intro}}
5
+
5
6
  {{$form}}{{/form}}
6
7
  {{#csrf-token}}
7
8
  <input type="hidden" name="x-csrf-token" value="{{csrf-token}}" />
@@ -0,0 +1,7 @@
1
+ <div class="govuk-warning-text">
2
+ <span class="govuk-warning-text__icon" aria-hidden="true">!</span>
3
+ <strong class="govuk-warning-text__text">
4
+ <span class="govuk-warning-text__assistive">Warning</span>
5
+ {{warning}}
6
+ </strong>
7
+ </div>
@@ -12,6 +12,10 @@ $path: "/public/images/" !default;
12
12
  // https://github.com/alphagov/govuk_elements/blob/master/packages/govuk-elements-sass/public/sass/_elements.scss
13
13
  @import "govuk-elements-sass/public/sass/elements";
14
14
 
15
+ // Govuk frontend
16
+ // https://github.com/alphagov/govuk-frontend-docs
17
+ @import "govuk-frontend";
18
+
15
19
  // Custom
16
20
  @import "base";
17
21
  @import "layout";
@@ -25,7 +25,7 @@
25
25
  .govuk-form-group--error {
26
26
  box-sizing: border-box;
27
27
  padding-left: $gutter-half - $validation-bdr-size;
28
- border-left: $validation-bdr-size-lg solid $error-colour;
28
+ //border-left: $validation-bdr-size-lg solid $error-colour;
29
29
 
30
30
  &:focus {
31
31
  outline: $focus-outline;
@@ -42,7 +42,7 @@
42
42
  margin-bottom: 0.5em;
43
43
  }
44
44
  @include bold-19;
45
- color: $error-colour;
45
+ //color: $error-colour;
46
46
  }
47
47
 
48
48
  .invalid-input,
@@ -71,11 +71,20 @@ function formFocus() {
71
71
  var labels;
72
72
  var summaries;
73
73
 
74
- if (getElementFromSummaryLink && getEditPath === 'edit') {
74
+ var editMode = getElementFromSummaryLink && getEditPath === 'edit';
75
+
76
+ if (getElementFromSummaryLink && document.getElementById(getElementFromSummaryLink) && editMode) {
75
77
  document.getElementById(getElementFromSummaryLink).focus();
78
+ }
79
+
80
+ if (getElementFromSummaryLink && document.getElementById(getElementFromSummaryLink + '-group') && editMode) {
76
81
  document.getElementById(getElementFromSummaryLink + '-group').scrollIntoView();
77
82
  }
78
83
 
84
+ if (document.getElementById(getElementFromSummaryLink + '-day') && forms.length === 1 && editMode) {
85
+ document.getElementById(getElementFromSummaryLink + '-day').focus();
86
+ }
87
+
79
88
  if (forms.length > 0) {
80
89
  labels = document.getElementsByTagName('label');
81
90
  if (labels) {
@@ -7,12 +7,14 @@ var groupBy = require('lodash').groupBy;
7
7
  var helpers = require('./helpers');
8
8
  var inputs; var groups;
9
9
  var toggleAttr = 'data-toggle';
10
- var hiddenClass = 'js-hidden';
10
+ var checkboxHiddenClass = 'govuk-checkboxes__conditional--hidden';
11
+ var radioHiddenClass = 'govuk-radios__conditional--hidden';
11
12
 
12
13
  function inputClicked(e, target) {
13
14
  target = target || helpers.target(e);
14
15
  var shown;
15
16
  each(groups[target.name], function (input) {
17
+ var hiddenClass = (input.type.match(/checkbox/)) ? checkboxHiddenClass : radioHiddenClass;
16
18
  var id = input.getAttribute('aria-controls');
17
19
  var toggle = document.getElementById(id);
18
20
  if (toggle) {
@@ -24,7 +24,12 @@ function clicked(e) {
24
24
  }
25
25
 
26
26
  if (inputs) {
27
- inputs[0].focus();
27
+ if (inputs[0].getAttribute('type') === 'hidden') {
28
+ var getVisibleElements = group.querySelectorAll('input[type=text]');
29
+ getVisibleElements[0].focus();
30
+ } else {
31
+ inputs[0].focus();
32
+ }
28
33
  }
29
34
  }
30
35
  }
package/lib/health.js CHANGED
@@ -15,7 +15,7 @@ module.exports = redis => {
15
15
 
16
16
  router.get('/readiness', healthCheck({
17
17
  test: () => {
18
- if (!redis.connected && !redis.ready) {
18
+ if (!redis.isOpen && !redis.isReady) {
19
19
  return new Error('Session store unhealthy');
20
20
  }
21
21
  return 0;
package/lib/sessions.js CHANGED
@@ -50,7 +50,7 @@ module.exports = (app, config) => {
50
50
  logger.error(e);
51
51
  });
52
52
  }
53
-
53
+ client.connect();
54
54
  const store = new RedisStore({
55
55
  client: client,
56
56
  ttl: config.session.ttl,
@@ -25,9 +25,11 @@ module.exports = (options, rateLimitType) => {
25
25
  }
26
26
 
27
27
  const closeConnection = async err => {
28
- await redisClient.quit();
28
+ await redisClient.v4.QUIT();
29
29
  return next(err);
30
30
  };
31
+ redisClient.on('error', err => logger.log('error', err));
32
+ await redisClient.connect();
31
33
 
32
34
  try {
33
35
  // fetch records of current user using IP address, returns null when no record is found
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.0-beta.9",
4
+ "version": "20.0.0-redis-beta.32-redis-beta",
5
5
  "license": "MIT",
6
6
  "main": "index.js",
7
7
  "author": "HomeOffice",
@@ -42,7 +42,7 @@
42
42
  "callsite": "^1.0.0",
43
43
  "chalk": "^2.0.0",
44
44
  "chokidar": "^3.4.0",
45
- "connect-redis": "^5.2.0",
45
+ "connect-redis": "^6.1.3",
46
46
  "cookie-parser": "^1.4.6",
47
47
  "cp": "^0.2.0",
48
48
  "csrf": "^3.0.2",
@@ -79,7 +79,8 @@
79
79
  "nodemailer-ses-transport": "^1.5.0",
80
80
  "nodemailer-smtp-transport": "^2.7.4",
81
81
  "nodemailer-stub-transport": "^1.1.0",
82
- "redis": "^3.1.2",
82
+ "notifications-node-client": "^5.1.1",
83
+ "redis": "^4.3.1",
83
84
  "reqres": "^3.0.1",
84
85
  "request": "^2.79.0",
85
86
  "rimraf": "^3.0.2",
@@ -21,6 +21,7 @@ module.exports = {
21
21
  isPageHeading: 'true'
22
22
  },
23
23
  'dateOfBirth': dateComponent('dateOfBirth', {
24
+ mixin: 'input-date',
24
25
  isPageHeading: 'true',
25
26
  validate: [
26
27
  'required',
@@ -30,11 +30,7 @@ module.exports = {
30
30
  },
31
31
  '/dob': {
32
32
  fields: ['dateOfBirth'],
33
- next: '/address',
34
- locals: {
35
- step: 'dob',
36
- labelClassName: 'govuk-input'
37
- }
33
+ next: '/address'
38
34
  },
39
35
  '/address': {
40
36
  fields: ['building', 'street', 'townOrCity', 'postcode'],