hof 20.0.0-beta.1 → 20.0.0-beta.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. package/.github/workflows/automate-publish.yml +1 -1
  2. package/.github/workflows/automate-tag.yml +1 -1
  3. package/.nyc_output/e2fdc3eb-4fd2-47e0-a392-fe5f665776a4.json +1 -0
  4. package/.nyc_output/processinfo/e2fdc3eb-4fd2-47e0-a392-fe5f665776a4.json +1 -0
  5. package/.nyc_output/processinfo/index.json +1 -1
  6. package/build/lib/mkdir.js +2 -2
  7. package/components/date/index.js +37 -24
  8. package/components/date/templates/date.html +3 -3
  9. package/components/emailer/index.js +49 -41
  10. package/components/emailer/transports/debug.js +1 -2
  11. package/components/summary/index.js +18 -0
  12. package/config/hof-defaults.js +5 -3
  13. package/config/rate-limits.js +20 -0
  14. package/config/sanitisation-rules.js +29 -0
  15. package/controller/base-controller.js +26 -8
  16. package/controller/controller.js +11 -15
  17. package/frontend/govuk-template/build/config.js +1 -1
  18. package/frontend/template-mixins/mixins/template-mixins.js +8 -8
  19. package/frontend/template-mixins/partials/forms/checkbox-group.html +3 -3
  20. package/frontend/template-mixins/partials/forms/input-text-group.html +3 -3
  21. package/frontend/template-mixins/partials/forms/option-group.html +3 -3
  22. package/frontend/template-mixins/partials/forms/select.html +2 -2
  23. package/frontend/template-mixins/partials/forms/textarea-group.html +2 -2
  24. package/frontend/template-mixins/partials/mixins/panel.html +3 -4
  25. package/frontend/template-partials/translations/src/en/errors.json +12 -0
  26. package/frontend/template-partials/views/rate-limit-error.html +10 -0
  27. package/frontend/themes/gov-uk/client-js/govuk-cookies.js +43 -44
  28. package/frontend/themes/gov-uk/client-js/index.js +2 -2
  29. package/frontend/themes/gov-uk/client-js/skip-to-main.js +18 -17
  30. package/frontend/themes/gov-uk/styles/_cookie-banner.scss +51 -1
  31. package/frontend/toolkit/assets/javascript/form-focus.js +10 -1
  32. package/frontend/toolkit/assets/javascript/validation.js +6 -1
  33. package/index.js +9 -4
  34. package/lib/router.js +2 -1
  35. package/lib/settings.js +18 -2
  36. package/middleware/errors.js +32 -0
  37. package/middleware/index.js +2 -1
  38. package/middleware/rate-limiter.js +98 -0
  39. package/package.json +5 -6
  40. package/sandbox/.env +1 -1
  41. package/sandbox/apps/sandbox/fields.js +14 -9
  42. package/sandbox/apps/sandbox/index.js +1 -5
  43. package/sandbox/apps/sandbox/translations/en/default.json +4 -13
  44. package/sandbox/apps/sandbox/translations/src/en/fields.json +3 -2
  45. package/sandbox/apps/sandbox/translations/src/en/pages.json +2 -12
  46. package/sandbox/assets/scss/app.scss +0 -55
  47. package/sandbox/public/css/app.css +52 -62
  48. package/sandbox/public/js/bundle.js +79 -65
  49. package/sandbox/server.js +5 -0
  50. package/transpiler/lib/write-files.js +1 -2
  51. package/utilities/helpers/index.js +16 -1
  52. package/wizard/index.js +1 -0
  53. package/.nyc_output/65af88d9-aebe-4d1b-a21d-6fbf7f2bbda4.json +0 -1
  54. package/.nyc_output/processinfo/65af88d9-aebe-4d1b-a21d-6fbf7f2bbda4.json +0 -1
@@ -146,9 +146,10 @@ module.exports = {
146
146
  };
147
147
 
148
148
  },{}],2:[function(require,module,exports){
149
+ /* eslint-disable no-undef, no-param-reassign, no-unused-vars */
149
150
  (function () {
150
- "use strict"
151
- var root = this;
151
+ 'use strict';
152
+ const root = this;
152
153
  if(typeof root.GOVUK === 'undefined') { root.GOVUK = {}; }
153
154
 
154
155
  /*
@@ -167,37 +168,35 @@ module.exports = {
167
168
  GOVUK.cookie('hobnob', null);
168
169
  */
169
170
  GOVUK.cookie = function (name, value, options) {
170
- if(typeof value !== 'undefined'){
171
+ if(typeof value !== 'undefined') {
171
172
  if(value === false || value === null) {
172
173
  return GOVUK.setCookie(name, '', { days: -1 });
173
- } else {
174
- return GOVUK.setCookie(name, value, options);
175
174
  }
176
- } else {
177
- return GOVUK.getCookie(name);
175
+ return GOVUK.setCookie(name, value, options);
178
176
  }
177
+ return GOVUK.getCookie(name);
179
178
  };
180
179
  GOVUK.setCookie = function (name, value, options) {
181
180
  if(typeof options === 'undefined') {
182
181
  options = {};
183
182
  }
184
- var cookieString = name + "=" + value + "; path=/";
183
+ let cookieString = name + '=' + value + '; path=/';
185
184
  if (options.days) {
186
- var date = new Date();
185
+ const date = new Date();
187
186
  date.setTime(date.getTime() + (options.days * 24 * 60 * 60 * 1000));
188
- cookieString = cookieString + "; expires=" + date.toGMTString();
187
+ cookieString = cookieString + '; expires=' + date.toGMTString();
189
188
  }
190
- if (document.location.protocol == 'https:'){
191
- cookieString = cookieString + "; Secure";
189
+ if (document.location.protocol === 'https:') {
190
+ cookieString = cookieString + '; Secure';
192
191
  }
193
192
  document.cookie = cookieString;
194
193
  };
195
194
  GOVUK.getCookie = function (name) {
196
- var nameEQ = name + "=";
197
- var cookies = document.cookie.split(';');
198
- for(var i = 0, len = cookies.length; i < len; i++) {
199
- var cookie = cookies[i];
200
- while (cookie.charAt(0) == ' ') {
195
+ const nameEQ = name + '=';
196
+ const cookies = document.cookie.split(';');
197
+ for(let i = 0, len = cookies.length; i < len; i++) {
198
+ let cookie = cookies[i];
199
+ while (cookie.charAt(0) === ' ') {
201
200
  cookie = cookie.substring(1, cookie.length);
202
201
  }
203
202
  if (cookie.indexOf(nameEQ) === 0) {
@@ -208,33 +207,33 @@ module.exports = {
208
207
  };
209
208
  }).call(this);
210
209
  (function () {
211
- 'use strict'
212
- var root = this
213
- if (typeof root.GOVUK === 'undefined') { root.GOVUK = {} }
210
+ 'use strict';
211
+ const root = this;
212
+ if (typeof root.GOVUK === 'undefined') { root.GOVUK = {}; }
214
213
 
215
214
  GOVUK.addCookieMessage = function () {
216
- var message = document.getElementById('global-cookie-message')
215
+ const message = document.getElementById('global-cookie-message');
217
216
 
218
- var hasCookieMessage = (message && GOVUK.cookie('seen_cookie_message') === null)
217
+ const hasCookieMessage = (message && GOVUK.cookie('seen_cookie_message') === null);
219
218
 
220
219
  if (hasCookieMessage) {
221
- message.style.display = 'block'
222
- GOVUK.cookie('seen_cookie_message', 'yes', { days: 28 })
220
+ message.style.display = 'block';
221
+ GOVUK.cookie('seen_cookie_message', 'yes', { days: 28 });
223
222
 
224
223
  document.addEventListener('DOMContentLoaded', function (event) {
225
224
  if (GOVUK.analytics && typeof GOVUK.analytics.trackEvent === 'function') {
226
225
  GOVUK.analytics.trackEvent('cookieBanner', 'Cookie banner shown', {
227
226
  value: 1,
228
227
  nonInteraction: true
229
- })
228
+ });
230
229
  }
231
- })
232
- };
233
- }
230
+ });
231
+ }
232
+ };
234
233
  }).call(this)
235
234
  ;
236
- (function() {
237
- "use strict"
235
+ (function () {
236
+ 'use strict';
238
237
 
239
238
  // add cookie message
240
239
  if (window.GOVUK && GOVUK.addCookieMessage) {
@@ -242,35 +241,35 @@ module.exports = {
242
241
  }
243
242
 
244
243
  // header navigation toggle
245
- if (document.querySelectorAll && document.addEventListener){
246
- var els = document.querySelectorAll('.js-header-toggle'),
247
- i, _i;
248
- for(i=0,_i=els.length; i<_i; i++){
249
- els[i].addEventListener('click', function(e){
244
+ if (document.querySelectorAll && document.addEventListener) {
245
+ const els = document.querySelectorAll('.js-header-toggle');
246
+ let i; let _i;
247
+ for(i = 0, _i = els.length; i < _i; i++) {
248
+ els[i].addEventListener('click', function (e) {
250
249
  e.preventDefault();
251
- var target = document.getElementById(this.getAttribute('href').substr(1)),
252
- targetClass = target.getAttribute('class') || '',
253
- sourceClass = this.getAttribute('class') || '';
250
+ const target = document.getElementById(this.getAttribute('href').substr(1));
251
+ const targetClass = target.getAttribute('class') || '';
252
+ const sourceClass = this.getAttribute('class') || '';
254
253
 
255
- if(targetClass.indexOf('js-visible') !== -1){
254
+ if(targetClass.indexOf('js-visible') !== -1) {
256
255
  target.setAttribute('class', targetClass.replace(/(^|\s)js-visible(\s|$)/, ''));
257
256
  } else {
258
- target.setAttribute('class', targetClass + " js-visible");
257
+ target.setAttribute('class', targetClass + ' js-visible');
259
258
  }
260
- if(sourceClass.indexOf('js-visible') !== -1){
259
+ if(sourceClass.indexOf('js-visible') !== -1) {
261
260
  this.setAttribute('class', sourceClass.replace(/(^|\s)js-visible(\s|$)/, ''));
262
261
  } else {
263
- this.setAttribute('class', sourceClass + " js-visible");
262
+ this.setAttribute('class', sourceClass + ' js-visible');
264
263
  }
265
- this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') !== "true");
266
- target.setAttribute('aria-hidden', target.getAttribute('aria-hidden') === "false");
264
+ this.setAttribute('aria-expanded', this.getAttribute('aria-expanded') !== 'true');
265
+ target.setAttribute('aria-hidden', target.getAttribute('aria-hidden') === 'false');
267
266
  });
268
267
  }
269
268
  }
270
269
  }).call(this);
271
270
 
272
271
  },{}],3:[function(require,module,exports){
273
- /* eslint-disable no-var */
272
+ /* eslint-disable no-var, vars-on-top, no-unused-vars */
274
273
  'use strict';
275
274
 
276
275
  var toolkit = require('../../../toolkit');
@@ -280,7 +279,7 @@ var formFocus = toolkit.formFocus;
280
279
  var characterCount = toolkit.characterCount;
281
280
  var validation = toolkit.validation;
282
281
 
283
- var GOVUK = require('govuk-frontend')
282
+ var GOVUK = require('govuk-frontend');
284
283
  GOVUK.initAll();
285
284
  window.GOVUK = GOVUK;
286
285
  var skipToMain = require('./skip-to-main');
@@ -299,23 +298,24 @@ helpers.documentReady(validation);
299
298
 
300
299
  },{"../../../toolkit":11,"./cookieSettings":1,"./govuk-cookies":2,"./skip-to-main":4,"govuk-frontend":12}],4:[function(require,module,exports){
301
300
  const skipToMain = function () {
302
- const skipToMainLink = document.getElementById("skip-to-main");
303
- const firstControlId = skipToMainLink.hash.split('#')[1] ? skipToMainLink.hash.split('#')[1] : "main-content";
304
- if(firstControlId === "main-content"){
305
- skipToMainLink.setAttribute("href", "#main-content");
306
- }
307
- if(firstControlId) {
308
- skipToMainLink.onclick = function(e) {
309
- //here timeout added just to make this functionality asynchronous
310
- //to focus on form as well as non form contents
311
- setTimeout(() => {
312
- const firstControl = document.getElementById(firstControlId);
313
- firstControl.focus();
314
- }, 10);
315
- }
316
- }
317
- };
318
- skipToMain();
301
+ const skipToMainLink = document.getElementById('skip-to-main');
302
+ const firstControlId = skipToMainLink.hash.split('#')[1] ? skipToMainLink.hash.split('#')[1] : 'main-content';
303
+ if(firstControlId === 'main-content') {
304
+ skipToMainLink.setAttribute('href', '#main-content');
305
+ }
306
+ if(firstControlId) {
307
+ // eslint-disable-next-line no-unused-vars
308
+ skipToMainLink.onclick = function (e) {
309
+ // here timeout added just to make this functionality asynchronous
310
+ // to focus on form as well as non form contents
311
+ setTimeout(() => {
312
+ const firstControl = document.getElementById(firstControlId);
313
+ firstControl.focus();
314
+ }, 10);
315
+ };
316
+ }
317
+ };
318
+ skipToMain();
319
319
 
320
320
  },{}],5:[function(require,module,exports){
321
321
  /* eslint-disable max-len, no-var, no-use-before-define, no-param-reassign, vars-on-top, radix */
@@ -492,11 +492,20 @@ function formFocus() {
492
492
  var labels;
493
493
  var summaries;
494
494
 
495
- if (getElementFromSummaryLink && getEditPath === 'edit') {
495
+ var editMode = getElementFromSummaryLink && getEditPath === 'edit';
496
+
497
+ if (getElementFromSummaryLink && document.getElementById(getElementFromSummaryLink) && editMode) {
496
498
  document.getElementById(getElementFromSummaryLink).focus();
499
+ }
500
+
501
+ if (getElementFromSummaryLink && document.getElementById(getElementFromSummaryLink + '-group') && editMode) {
497
502
  document.getElementById(getElementFromSummaryLink + '-group').scrollIntoView();
498
503
  }
499
504
 
505
+ if (document.getElementById(getElementFromSummaryLink + '-day') && forms.length === 1 && editMode) {
506
+ document.getElementById(getElementFromSummaryLink + '-day').focus();
507
+ }
508
+
500
509
  if (forms.length > 0) {
501
510
  labels = document.getElementsByTagName('label');
502
511
  if (labels) {
@@ -790,7 +799,12 @@ function clicked(e) {
790
799
  }
791
800
 
792
801
  if (inputs) {
793
- inputs[0].focus();
802
+ if (inputs[0].getAttribute('type') === 'hidden') {
803
+ var getVisibleElements = group.querySelectorAll('input[type=text]');
804
+ getVisibleElements[0].focus();
805
+ } else {
806
+ inputs[0].focus();
807
+ }
794
808
  }
795
809
  }
796
810
  }
package/sandbox/server.js CHANGED
@@ -8,5 +8,10 @@ bootstrap({
8
8
  routes: [
9
9
  require('./apps/sandbox')
10
10
  ],
11
+ rateLimits: {
12
+ requests: {
13
+ active: true
14
+ }
15
+ },
11
16
  getAccessibility: true
12
17
  });
@@ -2,7 +2,6 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const mkdir = require('mkdirp').sync;
6
5
  const rm = require('rimraf').sync;
7
6
 
8
7
  const debug = require('debug')('hof:transpiler');
@@ -16,7 +15,7 @@ module.exports = (dir, data) => {
16
15
  const outputDir = path.resolve(dir, '..', lang);
17
16
  rm(outputDir);
18
17
  debug(`Emptied directory ${outputDir}`);
19
- mkdir(outputDir);
18
+ fs.mkdirSync(outputDir);
20
19
  debug(`Made directory ${outputDir}`);
21
20
  Object.keys(data[lang]).forEach(namespace => {
22
21
  fs.writeFileSync(path.resolve(outputDir, `${namespace}.json`), JSON.stringify(data[lang][namespace], null, ' '));
@@ -94,7 +94,7 @@ module.exports = class Helpers {
94
94
  /**
95
95
  * utility function which returns undefined on
96
96
  * failed translations instead of returning the key
97
- * @param {Function} translate - the translate funtion
97
+ * @param {Function} translate - the translate function
98
98
  * @param {String} key - the key to translate
99
99
  * @returns {String|undefined} the string result if successful, undefined if not
100
100
  */
@@ -131,4 +131,19 @@ module.exports = class Helpers {
131
131
  `pages.${key}.header`
132
132
  ]) || key;
133
133
  }
134
+ /**
135
+ * utility function which returns true or false on
136
+ * forks depending on whether a value exists on the page
137
+ * @param {Object} req - an http request object
138
+ * @param {Object} res - an http response object
139
+ * @param {Function|Object} condition - a field condition that is either a function or object
140
+ * @returns {Boolean} the boolean result of whether a field value is set on the page or session for a fork
141
+ */
142
+ static isFieldValueInPageOrSessionValid(req, res, condition) {
143
+ return _.isFunction(condition) ?
144
+ condition(req, res) :
145
+ condition.value === (req.form.values[condition.field] ||
146
+ (!Object.keys(req.form.values).includes(condition.field) &&
147
+ _.get(req, `form.historicalValues[${condition.field}]`)));
148
+ }
134
149
  };
package/wizard/index.js CHANGED
@@ -69,6 +69,7 @@ const Wizard = (steps, fields, setts) => {
69
69
  options.confirmStep = settings.confirmStep;
70
70
  options.clearSession = options.clearSession || false;
71
71
  options.fieldsConfig = _.cloneDeep(fields);
72
+ options.sanitiseInputs = settings.sanitiseInputs;
72
73
 
73
74
  options.defaultFormatters = [].concat(settings.formatters);
74
75