hof 20.0.0-beta.2 → 20.0.0-beta.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/.github/workflows/automate-publish.yml +1 -1
  2. package/.github/workflows/automate-tag.yml +1 -1
  3. package/README.md +329 -256
  4. package/build/lib/mkdir.js +2 -2
  5. package/components/date/index.js +37 -26
  6. package/components/date/templates/date.html +3 -3
  7. package/components/emailer/index.js +49 -41
  8. package/components/emailer/transports/debug.js +1 -2
  9. package/components/index.js +2 -1
  10. package/components/notify/index.js +60 -0
  11. package/components/notify/notify.js +25 -0
  12. package/components/summary/index.js +18 -0
  13. package/config/hof-defaults.js +5 -3
  14. package/config/rate-limits.js +20 -0
  15. package/config/sanitisation-rules.js +29 -0
  16. package/controller/base-controller.js +26 -8
  17. package/controller/controller.js +11 -15
  18. package/frontend/govuk-template/build/config.js +1 -1
  19. package/frontend/template-mixins/mixins/template-mixins.js +12 -9
  20. package/frontend/template-mixins/partials/forms/checkbox-group.html +12 -3
  21. package/frontend/template-mixins/partials/forms/input-text-date.html +1 -1
  22. package/frontend/template-mixins/partials/forms/input-text-group.html +3 -3
  23. package/frontend/template-mixins/partials/forms/option-group.html +12 -3
  24. package/frontend/template-mixins/partials/forms/select.html +3 -3
  25. package/frontend/template-mixins/partials/forms/textarea-group.html +3 -3
  26. package/frontend/template-mixins/partials/mixins/panel.html +1 -2
  27. package/frontend/template-partials/translations/src/en/errors.json +12 -0
  28. package/frontend/template-partials/views/partials/form.html +2 -1
  29. package/frontend/template-partials/views/rate-limit-error.html +10 -0
  30. package/frontend/themes/gov-uk/client-js/govuk-cookies.js +43 -44
  31. package/frontend/themes/gov-uk/client-js/index.js +2 -2
  32. package/frontend/themes/gov-uk/client-js/skip-to-main.js +18 -17
  33. package/frontend/themes/gov-uk/styles/govuk.scss +4 -0
  34. package/frontend/themes/gov-uk/styles/modules/_validation.scss +2 -2
  35. package/frontend/toolkit/assets/javascript/form-focus.js +10 -1
  36. package/frontend/toolkit/assets/javascript/validation.js +6 -1
  37. package/index.js +9 -4
  38. package/lib/router.js +2 -1
  39. package/lib/settings.js +9 -8
  40. package/middleware/errors.js +32 -0
  41. package/middleware/index.js +2 -1
  42. package/middleware/rate-limiter.js +98 -0
  43. package/package.json +6 -6
  44. package/sandbox/apps/sandbox/fields.js +11 -12
  45. package/sandbox/apps/sandbox/index.js +1 -5
  46. package/sandbox/assets/scss/app.scss +0 -52
  47. package/sandbox/package.json +2 -0
  48. package/sandbox/public/css/app.css +4908 -4965
  49. package/sandbox/public/js/bundle.js +79 -65
  50. package/sandbox/server.js +5 -0
  51. package/sandbox/yarn.lock +25 -1
  52. package/transpiler/lib/write-files.js +1 -2
  53. package/utilities/helpers/index.js +16 -1
  54. package/wizard/index.js +1 -0
  55. package/.nyc_output/65af88d9-aebe-4d1b-a21d-6fbf7f2bbda4.json +0 -1
  56. package/.nyc_output/processinfo/65af88d9-aebe-4d1b-a21d-6fbf7f2bbda4.json +0 -1
  57. package/.nyc_output/processinfo/index.json +0 -1
  58. package/.vscode/settings.json +0 -6
  59. package/sandbox/.env +0 -1
@@ -213,7 +213,7 @@ module.exports = function (options) {
213
213
  date: extension.date,
214
214
  autocomplete: autocomplete,
215
215
  child: field.child,
216
- noHeading: field.noHeading,
216
+ isPageHeading: field.isPageHeading,
217
217
  attributes: field.attributes,
218
218
  renderChild: renderChild.bind(this)
219
219
  });
@@ -224,6 +224,7 @@ module.exports = function (options) {
224
224
  const field = Object.assign({}, this.options.fields[key] || options.fields[key]);
225
225
  const legend = field.legend;
226
226
  const detail = field.detail;
227
+ const warningValue = 'fields.' + key + '.warning';
227
228
  let legendClassName;
228
229
  let legendValue = 'fields.' + key + '.legend';
229
230
  if (legend) {
@@ -240,8 +241,9 @@ module.exports = function (options) {
240
241
  legend: t(legendValue),
241
242
  legendClassName: legendClassName,
242
243
  role: opts.type === 'radio' ? 'radiogroup' : 'group',
243
- radioOption: opts.type === 'radio',
244
- noHeading: field.noHeading,
244
+ isPageHeading: field.isPageHeading,
245
+ isWarning: field.isWarning,
246
+ warning: t(warningValue),
245
247
  detail: detail ? detail : '',
246
248
  hint: conditionalTranslate(getTranslationKey(field, key, 'hint')),
247
249
  options: _.map(field.options, function (obj) {
@@ -276,6 +278,7 @@ module.exports = function (options) {
276
278
  value: value,
277
279
  type: opts.type,
278
280
  selected: selected,
281
+ radioOption: opts.type === 'radio',
279
282
  toggle: toggle,
280
283
  child: child,
281
284
  optionHint: conditionalTranslate(optionHint) || ''
@@ -303,7 +306,7 @@ module.exports = function (options) {
303
306
  invalid: this.errors && this.errors[key] && opts.required,
304
307
  label: t(fieldLabel || 'fields.' + key + '.label'),
305
308
  selected: selected,
306
- className: classNames(field) || 'block-label',
309
+ className: classNames(field) || 'govuk-label govuk-checkboxes__label',
307
310
  child: field.child,
308
311
  renderChild: renderChild.bind(this)
309
312
  });
@@ -431,8 +434,8 @@ module.exports = function (options) {
431
434
  year: autocomplete + '-year'
432
435
  };
433
436
  }
434
- const isRequired = field.validate ? field.validate.indexOf('required') > -1 : false;
435
- const formGroupClassName = (field.formGroup && field.formGroup.className) ? field.formGroup.className : ''
437
+ const isThisRequired = field.validate ? field.validate.indexOf('required') > -1 : false;
438
+ const formGroupClassName = (field.formGroup && field.formGroup.className) ? field.formGroup.className : '';
436
439
  const classNameDay = (field.controlsClass && field.controlsClass.day) ? field.controlsClass.day : 'govuk-date-input__input govuk-input--width-2';
437
440
  const classNameMonth = (field.controlsClass && field.controlsClass.month) ? field.controlsClass.month : 'govuk-date-input__input govuk-input--width-2';
438
441
  const classNameYear = (field.controlsClass && field.controlsClass.year) ? field.controlsClass.year : 'govuk-date-input__input govuk-input--width-4';
@@ -440,12 +443,12 @@ module.exports = function (options) {
440
443
  const parts = [];
441
444
 
442
445
  if (isExact) {
443
- const dayPart = compiled['partials/forms/input-text-date'].render(inputText.call(this, key + '-day', { pattern: '[0-9]*', min: 1, max: 31, maxlength: 2, hintId: key + '-hint', date: true, autocomplete: autocomplete.day, formGroupClassName, className: classNameDay, isRequired }));
446
+ const dayPart = compiled['partials/forms/input-text-date'].render(inputText.call(this, key + '-day', { pattern: '[0-9]*', min: 1, max: 31, maxlength: 2, hintId: key + '-hint', date: true, autocomplete: autocomplete.day, formGroupClassName, className: classNameDay, isThisRequired }));
444
447
  parts.push(dayPart);
445
448
  }
446
449
 
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 }));
450
+ 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 }));
451
+ 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
452
 
450
453
  return parts.concat(monthPart, yearPart).join('\n');
451
454
  };
@@ -1,10 +1,19 @@
1
1
  <div id="{{key}}-group" class="govuk-form-group{{#className}} {{className}} {{/className}}{{#formGroupClassName}} {{formGroupClassName}}{{/formGroupClassName}}{{#error}} govuk-form-group--error{{/error}}">
2
2
  <fieldset class="govuk-fieldset" {{#hint}} aria-describedby="{{key}}-hint"{{/hint}}>
3
- <legend class="govuk-fieldset__legend {{^noHeading}}govuk-fieldset__legend--l{{/noHeading}}{{#legendClassName}} {{legendClassName}}{{/legendClassName}}">
4
- {{^noHeading}}<h1 class="govuk-fieldset__heading">{{/noHeading}}
3
+ <legend class="govuk-fieldset__legend {{#isPageHeading}}govuk-fieldset__legend--l{{/isPageHeading}}{{#legendClassName}} {{legendClassName}}{{/legendClassName}}">
4
+ {{#isPageHeading}}<h1 class="govuk-fieldset__heading">{{/isPageHeading}}
5
5
  {{legend}}
6
- {{^noHeading}}</h1>{{/noHeading}}
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">
@@ -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,8 +1,8 @@
1
1
  <div id="{{id}}-group" class="{{#compound}} form-group-compound{{/compound}}{{#formGroupClassName}}{{formGroupClassName}}{{/formGroupClassName}}{{#error}} govuk-form-group--error{{/error}}">
2
- {{^noHeading}}<h1 class="govuk-label-wrapper">{{/noHeading}}<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
- {{^noHeading}}</h1>{{/noHeading}}
5
+ {{#isPageHeading}}</h1>{{/isPageHeading}}
6
6
  {{#hint}}<span {{$hintId}}id="{{hintId}}" {{/hintId}}class="govuk-hint">{{hint}}</span>{{/hint}}
7
7
  {{#error}}
8
8
  <p class="govuk-error-message">
@@ -19,7 +19,7 @@
19
19
  type="{{type}}"
20
20
  name="{{id}}"
21
21
  id="{{id}}"
22
- class="{{^className}}govuk-input{{/className}}{{#className}}{{className}}{{/className}}{{#error}} invalid-input{{/error}}"
22
+ class="{{^className}}govuk-input{{/className}}{{#className}}{{className}}{{/className}}{{#error}} govuk-input—-error{{/error}}"
23
23
  aria-required="{{required}}"
24
24
  {{#value}} value="{{value}}"{{/value}}
25
25
  {{#min}} min="{{min}}"{{/min}}
@@ -1,10 +1,19 @@
1
1
  <div id="{{key}}-group" class="govuk-form-group{{#className}} {{className}} {{/className}}{{#formGroupClassName}} {{formGroupClassName}}{{/formGroupClassName}}{{#error}} govuk-form-group--error{{/error}}">
2
2
  <fieldset class="govuk-fieldset" {{#hint}} aria-describedby="{{key}}-hint"{{/hint}}>
3
- <legend class="govuk-fieldset__legend {{^noHeading}}govuk-fieldset__legend--l{{/noHeading}}{{#legendClassName}} {{legendClassName}}{{/legendClassName}}">
4
- {{^noHeading}}<h1 class="govuk-fieldset__heading">{{/noHeading}}
3
+ <legend class="govuk-fieldset__legend {{#isPageHeading}}govuk-fieldset__legend--l{{/isPageHeading}}{{#legendClassName}} {{legendClassName}}{{/legendClassName}}">
4
+ {{#isPageHeading}}<h1 class="govuk-fieldset__heading">{{/isPageHeading}}
5
5
  {{legend}}
6
- {{^noHeading}}</h1>{{/noHeading}}
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}}}
@@ -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
- {{^noHeading}}<h1 class="govuk-label-wrapper">{{/noHeading}}<label for="{{id}}" class="{{labelClassName}}{{^noHeading}}govuk-label--l{{/noHeading}}">
2
+ {{#isPageHeading}}<h1 class="govuk-label-wrapper">{{/isPageHeading}}<label for="{{id}}" class="{{labelClassName}}{{#isPageHeading}}govuk-label--l{{/isPageHeading}}">
3
3
  {{{label}}}
4
4
  {{#hint}}<span {{$hintId}}id="{{hintId}}" {{/hintId}}class="govuk-hint">{{hint}}</span>{{/hint}}
5
5
  {{#error}}
@@ -8,8 +8,8 @@
8
8
  </p>
9
9
  {{/error}}
10
10
  </label>
11
- {{^noHeading}}</h1>{{/noHeading}}
12
- <select id="{{id}}" class="govuk-select{{#className}} {{className}}{{/className}}{{#error}} invalid-input{{/error}}" name="{{id}}" aria-required="{{required}}">
11
+ {{#isPageHeading}}</h1>{{/isPageHeading}}
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
- {{^noHeading}}<h1 class="govuk-label-wrapper">{{/noHeading}}<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">
@@ -10,13 +10,13 @@
10
10
  </p>
11
11
  {{/error}}
12
12
  </label>
13
- {{^noHeading}}</h1>{{/noHeading}}
13
+ {{#isPageHeading}}</h1>{{/isPageHeading}}
14
14
  {{#hint}}<div {{$hintId}}id="{{hintId}}" {{/hintId}}class="govuk-hint">{{hint}}</div>{{/hint}}
15
15
  {{#renderChild}}{{/renderChild}}
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}}
@@ -1,5 +1,4 @@
1
- <div id="{{toggle}}-panel"
2
- class="{{#radioOption}}govuk-radios__conditional govuk-radios__conditional--hidden{{/radioOption}}
1
+ <div id="{{toggle}}-panel" class="{{#radioOption}}govuk-radios__conditional govuk-radios__conditional--hidden{{/radioOption}}
3
2
  {{^radioOption}}govuk-checkboxes__conditional govuk-checkboxes__conditional--hidden{{/radioOption}}">
4
3
  {{#renderMixin}}{{/renderMixin}}
5
4
  </div>
@@ -14,5 +14,17 @@
14
14
  "cookies-required": {
15
15
  "title": "Cookies are required to use this service",
16
16
  "message": "Cookies are required in order to use this service.<br /><br /> Please <a href=\"http://www.aboutcookies.org/how-to-control-cookies/\" rel=\"external\">enable cookies</a> and try again. Find out <a href=\"/cookies\">how to we use cookies</a>."
17
+ },
18
+ "ddos-rate-limit": {
19
+ "title": "Too many requests submitted",
20
+ "message": "You have submitted too many requests in quick succession.",
21
+ "pre-time-to-wait": "Please try again in ",
22
+ "post-time-to-wait": " minutes."
23
+ },
24
+ "submission-rate-limit": {
25
+ "title": "Too many submissions",
26
+ "message": "You have submitted too many applications in a short space of time.",
27
+ "pre-time-to-wait": "Please try again in ",
28
+ "post-time-to-wait": " minutes."
17
29
  }
18
30
  }
@@ -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,10 @@
1
+ {{<layout}}
2
+ {{$header}}
3
+ {{content.title}}
4
+ {{/header}}
5
+ {{$content}}
6
+ <p>{{content.message}}</p>
7
+ <p>{{content.preTimeToWait}}{{content.timeToWait}}{{content.postTimeToWait}}</p>
8
+ <a href="/" class="button" role="button">{{#t}}buttons.try-again{{/t}}</a>
9
+ {{/content}}
10
+ {{/layout}}
@@ -1,6 +1,7 @@
1
+ /* eslint-disable no-undef, no-param-reassign, no-unused-vars */
1
2
  (function () {
2
- "use strict"
3
- var root = this;
3
+ 'use strict';
4
+ const root = this;
4
5
  if(typeof root.GOVUK === 'undefined') { root.GOVUK = {}; }
5
6
 
6
7
  /*
@@ -19,37 +20,35 @@
19
20
  GOVUK.cookie('hobnob', null);
20
21
  */
21
22
  GOVUK.cookie = function (name, value, options) {
22
- if(typeof value !== 'undefined'){
23
+ if(typeof value !== 'undefined') {
23
24
  if(value === false || value === null) {
24
25
  return GOVUK.setCookie(name, '', { days: -1 });
25
- } else {
26
- return GOVUK.setCookie(name, value, options);
27
26
  }
28
- } else {
29
- return GOVUK.getCookie(name);
27
+ return GOVUK.setCookie(name, value, options);
30
28
  }
29
+ return GOVUK.getCookie(name);
31
30
  };
32
31
  GOVUK.setCookie = function (name, value, options) {
33
32
  if(typeof options === 'undefined') {
34
33
  options = {};
35
34
  }
36
- var cookieString = name + "=" + value + "; path=/";
35
+ let cookieString = name + '=' + value + '; path=/';
37
36
  if (options.days) {
38
- var date = new Date();
37
+ const date = new Date();
39
38
  date.setTime(date.getTime() + (options.days * 24 * 60 * 60 * 1000));
40
- cookieString = cookieString + "; expires=" + date.toGMTString();
39
+ cookieString = cookieString + '; expires=' + date.toGMTString();
41
40
  }
42
- if (document.location.protocol == 'https:'){
43
- cookieString = cookieString + "; Secure";
41
+ if (document.location.protocol === 'https:') {
42
+ cookieString = cookieString + '; Secure';
44
43
  }
45
44
  document.cookie = cookieString;
46
45
  };
47
46
  GOVUK.getCookie = function (name) {
48
- var nameEQ = name + "=";
49
- var cookies = document.cookie.split(';');
50
- for(var i = 0, len = cookies.length; i < len; i++) {
51
- var cookie = cookies[i];
52
- while (cookie.charAt(0) == ' ') {
47
+ const nameEQ = name + '=';
48
+ const cookies = document.cookie.split(';');
49
+ for(let i = 0, len = cookies.length; i < len; i++) {
50
+ let cookie = cookies[i];
51
+ while (cookie.charAt(0) === ' ') {
53
52
  cookie = cookie.substring(1, cookie.length);
54
53
  }
55
54
  if (cookie.indexOf(nameEQ) === 0) {
@@ -60,33 +59,33 @@
60
59
  };
61
60
  }).call(this);
62
61
  (function () {
63
- 'use strict'
64
- var root = this
65
- if (typeof root.GOVUK === 'undefined') { root.GOVUK = {} }
62
+ 'use strict';
63
+ const root = this;
64
+ if (typeof root.GOVUK === 'undefined') { root.GOVUK = {}; }
66
65
 
67
66
  GOVUK.addCookieMessage = function () {
68
- var message = document.getElementById('global-cookie-message')
67
+ const message = document.getElementById('global-cookie-message');
69
68
 
70
- var hasCookieMessage = (message && GOVUK.cookie('seen_cookie_message') === null)
69
+ const hasCookieMessage = (message && GOVUK.cookie('seen_cookie_message') === null);
71
70
 
72
71
  if (hasCookieMessage) {
73
- message.style.display = 'block'
74
- GOVUK.cookie('seen_cookie_message', 'yes', { days: 28 })
72
+ message.style.display = 'block';
73
+ GOVUK.cookie('seen_cookie_message', 'yes', { days: 28 });
75
74
 
76
75
  document.addEventListener('DOMContentLoaded', function (event) {
77
76
  if (GOVUK.analytics && typeof GOVUK.analytics.trackEvent === 'function') {
78
77
  GOVUK.analytics.trackEvent('cookieBanner', 'Cookie banner shown', {
79
78
  value: 1,
80
79
  nonInteraction: true
81
- })
80
+ });
82
81
  }
83
- })
84
- };
85
- }
82
+ });
83
+ }
84
+ };
86
85
  }).call(this)
87
86
  ;
88
- (function() {
89
- "use strict"
87
+ (function () {
88
+ 'use strict';
90
89
 
91
90
  // add cookie message
92
91
  if (window.GOVUK && GOVUK.addCookieMessage) {
@@ -94,28 +93,28 @@
94
93
  }
95
94
 
96
95
  // header navigation toggle
97
- if (document.querySelectorAll && document.addEventListener){
98
- var els = document.querySelectorAll('.js-header-toggle'),
99
- i, _i;
100
- for(i=0,_i=els.length; i<_i; i++){
101
- els[i].addEventListener('click', function(e){
96
+ if (document.querySelectorAll && document.addEventListener) {
97
+ const els = document.querySelectorAll('.js-header-toggle');
98
+ let i; let _i;
99
+ for(i = 0, _i = els.length; i < _i; i++) {
100
+ els[i].addEventListener('click', function (e) {
102
101
  e.preventDefault();
103
- var target = document.getElementById(this.getAttribute('href').substr(1)),
104
- targetClass = target.getAttribute('class') || '',
105
- sourceClass = this.getAttribute('class') || '';
102
+ const target = document.getElementById(this.getAttribute('href').substr(1));
103
+ const targetClass = target.getAttribute('class') || '';
104
+ const sourceClass = this.getAttribute('class') || '';
106
105
 
107
- if(targetClass.indexOf('js-visible') !== -1){
106
+ if(targetClass.indexOf('js-visible') !== -1) {
108
107
  target.setAttribute('class', targetClass.replace(/(^|\s)js-visible(\s|$)/, ''));
109
108
  } else {
110
- target.setAttribute('class', targetClass + " js-visible");
109
+ target.setAttribute('class', targetClass + ' js-visible');
111
110
  }
112
- if(sourceClass.indexOf('js-visible') !== -1){
111
+ if(sourceClass.indexOf('js-visible') !== -1) {
113
112
  this.setAttribute('class', sourceClass.replace(/(^|\s)js-visible(\s|$)/, ''));
114
113
  } else {
115
- this.setAttribute('class', sourceClass + " js-visible");
114
+ this.setAttribute('class', sourceClass + ' js-visible');
116
115
  }
117
- this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') !== "true");
118
- target.setAttribute('aria-hidden', target.getAttribute('aria-hidden') === "false");
116
+ this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') !== 'true');
117
+ target.setAttribute('aria-hidden', target.getAttribute('aria-hidden') === 'false');
119
118
  });
120
119
  }
121
120
  }
@@ -1,4 +1,4 @@
1
- /* eslint-disable no-var */
1
+ /* eslint-disable no-var, vars-on-top, no-unused-vars */
2
2
  'use strict';
3
3
 
4
4
  var toolkit = require('../../../toolkit');
@@ -8,7 +8,7 @@ var formFocus = toolkit.formFocus;
8
8
  var characterCount = toolkit.characterCount;
9
9
  var validation = toolkit.validation;
10
10
 
11
- var GOVUK = require('govuk-frontend')
11
+ var GOVUK = require('govuk-frontend');
12
12
  GOVUK.initAll();
13
13
  window.GOVUK = GOVUK;
14
14
  var skipToMain = require('./skip-to-main');
@@ -1,18 +1,19 @@
1
1
  const skipToMain = function () {
2
- const skipToMainLink = document.getElementById("skip-to-main");
3
- const firstControlId = skipToMainLink.hash.split('#')[1] ? skipToMainLink.hash.split('#')[1] : "main-content";
4
- if(firstControlId === "main-content"){
5
- skipToMainLink.setAttribute("href", "#main-content");
6
- }
7
- if(firstControlId) {
8
- skipToMainLink.onclick = function(e) {
9
- //here timeout added just to make this functionality asynchronous
10
- //to focus on form as well as non form contents
11
- setTimeout(() => {
12
- const firstControl = document.getElementById(firstControlId);
13
- firstControl.focus();
14
- }, 10);
15
- }
16
- }
17
- };
18
- skipToMain();
2
+ const skipToMainLink = document.getElementById('skip-to-main');
3
+ const firstControlId = skipToMainLink.hash.split('#')[1] ? skipToMainLink.hash.split('#')[1] : 'main-content';
4
+ if(firstControlId === 'main-content') {
5
+ skipToMainLink.setAttribute('href', '#main-content');
6
+ }
7
+ if(firstControlId) {
8
+ // eslint-disable-next-line no-unused-vars
9
+ skipToMainLink.onclick = function (e) {
10
+ // here timeout added just to make this functionality asynchronous
11
+ // to focus on form as well as non form contents
12
+ setTimeout(() => {
13
+ const firstControl = document.getElementById(firstControlId);
14
+ firstControl.focus();
15
+ }, 10);
16
+ };
17
+ }
18
+ };
19
+ skipToMain();
@@ -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 "node_modules/govuk-frontend/govuk/all";
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) {
@@ -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/index.js CHANGED
@@ -120,8 +120,9 @@ const getContentSecurityPolicy = (config, res) => {
120
120
  * @param options.getTerms {boolean} Optional boolean - whether to mount the /terms endpoint
121
121
  * @param options.getCookies {boolean} Optional boolean - whether to mount the /cookies endpoint
122
122
  * @param options.noCache {boolean} Optional boolean - whether to disable caching
123
- * @param options.getAccessibilityStatement {boolean} Optional boolean - whether to mount the /accessibility-statement endpoint
124
- *
123
+ * @param options.getAccessibilityStatement {boolean} Optional boolean - whether to mount the
124
+ * /accessibility-statement endpoint
125
+ *
125
126
  * @returns {object} A new HOF application using the configuration supplied in options
126
127
  */
127
128
  function bootstrap(options) {
@@ -205,9 +206,13 @@ function bootstrap(options) {
205
206
  }));
206
207
  app.use(mixins());
207
208
  app.use(markdown(config.markdown));
208
-
209
+ // rate limits have to be loaded before all routes so it is applied to them
210
+ if (config.rateLimits.requests.active) {
211
+ app.use(hofMiddleware.rateLimiter(config, 'requests'));
212
+ }
213
+
209
214
  // Set up routing so <YOUR-SITE-URL>/assets are served from /node_modules/govuk-frontend/govuk/assets
210
- app.use('/assets', express.static(path.join(__dirname, '/node_modules/govuk-frontend/govuk/assets')))
215
+ app.use('/assets', express.static(path.join(__dirname, '/node_modules/govuk-frontend/govuk/assets')));
211
216
 
212
217
  if (config.getAccessibility === true) {
213
218
  deprecate(
package/lib/router.js CHANGED
@@ -19,7 +19,8 @@ function getWizardConfig(config) {
19
19
  const wizardConfig = {
20
20
  name: config.route.name || (config.route.baseUrl || '').replace('/', ''),
21
21
  protocol: config.protocol,
22
- env: config.env
22
+ env: config.env,
23
+ sanitiseInputs: config.sanitiseInputs
23
24
  };
24
25
 
25
26
  if (config.appConfig) {
package/lib/settings.js CHANGED
@@ -7,19 +7,20 @@ const hoganExpressStrict = require('hogan-express-strict');
7
7
  const expressPartialTemplates = require('express-partial-templates');
8
8
  const bodyParser = require('body-parser');
9
9
 
10
- const filterEmptyViews = (views) => {
11
- return views.filter(view => dirExists(view))
12
- }
13
-
14
- const dirExists = (dir) => {
10
+ const dirExists = dir => {
15
11
  try {
16
12
  if (fs.existsSync(dir)) {
17
13
  return true;
18
14
  }
15
+ return false;
19
16
  } catch(err) {
20
- throw new Error(`${err}: Cannot check if the directory path exists`)
17
+ throw new Error(`${err}: Cannot check if the directory path exists`);
21
18
  }
22
- }
19
+ };
20
+
21
+ const filterEmptyViews = views => {
22
+ return views.filter(view => dirExists(view));
23
+ };
23
24
 
24
25
  module.exports = async (app, config) => {
25
26
  const viewEngine = config.viewEngine || 'html';
@@ -31,7 +32,7 @@ module.exports = async (app, config) => {
31
32
 
32
33
  app.use(config.theme());
33
34
 
34
- const filteredViews = filterEmptyViews(config.theme.views)
35
+ const filteredViews = filterEmptyViews(config.theme.views);
35
36
  const viewPaths = [].concat(filteredViews);
36
37
  app.set('view engine', viewEngine);
37
38
  app.enable('view cache');