@dwp/govuk-casa 8.1.0 → 8.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +2 -0
  3. package/dist/assets/css/casa-ie8.css +1 -1
  4. package/dist/assets/css/casa.css +1 -1
  5. package/dist/casa.d.ts +214 -0
  6. package/dist/casa.js +103 -0
  7. package/dist/lib/JourneyContext.d.ts +15 -27
  8. package/dist/lib/JourneyContext.js +25 -15
  9. package/dist/lib/configuration-ingestor.d.ts +16 -144
  10. package/dist/lib/configuration-ingestor.js +25 -95
  11. package/dist/lib/configure.d.ts +6 -77
  12. package/dist/lib/configure.js +58 -39
  13. package/dist/lib/nunjucks.d.ts +1 -6
  14. package/dist/lib/nunjucks.js +1 -3
  15. package/dist/lib/utils.d.ts +13 -10
  16. package/dist/lib/utils.js +40 -8
  17. package/dist/lib/waypoint-url.js +8 -2
  18. package/dist/middleware/body-parser.js +1 -1
  19. package/dist/middleware/data.d.ts +1 -2
  20. package/dist/middleware/data.js +12 -2
  21. package/dist/middleware/post.d.ts +1 -3
  22. package/dist/middleware/post.js +2 -2
  23. package/dist/middleware/pre.d.ts +1 -1
  24. package/dist/middleware/pre.js +5 -4
  25. package/dist/middleware/progress-journey.d.ts +1 -2
  26. package/dist/middleware/progress-journey.js +2 -2
  27. package/dist/middleware/session.d.ts +1 -2
  28. package/dist/middleware/session.js +7 -5
  29. package/dist/middleware/skip-waypoint.d.ts +1 -2
  30. package/dist/middleware/skip-waypoint.js +2 -2
  31. package/dist/middleware/steer-journey.d.ts +1 -2
  32. package/dist/middleware/steer-journey.js +2 -2
  33. package/dist/middleware/strip-proxy-path.d.ts +4 -0
  34. package/dist/middleware/strip-proxy-path.js +52 -0
  35. package/dist/middleware/validate-fields.d.ts +1 -2
  36. package/dist/middleware/validate-fields.js +2 -1
  37. package/dist/routes/journey.d.ts +1 -2
  38. package/dist/routes/journey.js +8 -8
  39. package/dist/routes/static.d.ts +1 -6
  40. package/dist/routes/static.js +6 -6
  41. package/package.json +25 -23
  42. package/views/casa/components/journey-form/README.md +3 -0
  43. package/views/casa/components/journey-form/template.njk +1 -1
  44. package/views/casa/partials/scripts.njk +1 -1
  45. package/views/casa/partials/styles.njk +2 -2
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ // Strip any "proxy path" prefix present on the request URL
3
+ //
4
+ // The default "mountUrl" will be match whatever path that the CASA app
5
+ // instance will be mounted onto. So if you mount on `/a/b/c` then all
6
+ // URLs generated for the browser will be prefixed with `/a/b/c`.
7
+ //
8
+ // Defining a `mountUrl` will change that behaviour into a "proxy mode".
9
+ // This mode assumes you have a forwarding proxy that will alter
10
+ // the incoming request paths from `mountUrl` to the original path you
11
+ // have mounted this CASA app instance onto.
12
+ //
13
+ // For example, if you mount the app on `/a/b/c/x`, but want the browser
14
+ // URLs to just use the `/x` prefix, then pass a `mountUrl` of `/x`.
15
+ //
16
+ // See docs in `docs/guides/setup-behind-a-proxy.md`
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ const logger_js_1 = __importDefault(require("../lib/logger.js"));
22
+ const log = (0, logger_js_1.default)('casa:middleware:strip-proxy-path');
23
+ exports.default = ({ mountUrl = '/', }) => [
24
+ (req, res, next) => {
25
+ // Assume everything before `mountUrl` is the proxy path prefix and remove it
26
+ const originalBaseUrl = req.baseUrl;
27
+ req.baseUrl = mountUrl.replace(/\/$/, '');
28
+ // If the app has been mounted directly on the specific `mountUrl`, then
29
+ // there's nothing we need to do and can let this request pass-through.
30
+ if (req.baseUrl === originalBaseUrl) {
31
+ next();
32
+ }
33
+ else if (req.__CASA_BASE_URL_REWRITTEN__) {
34
+ delete req.__CASA_BASE_URL_REWRITTEN__;
35
+ next();
36
+ }
37
+ else {
38
+ // Strip the proxy path prefix from the original URL so that
39
+ // subsequnt middleware sees the URL path as though proxy wasn't there.
40
+ // req.url will already have the proxy prefix and mountUrl removed.
41
+ /* eslint-disable security/detect-non-literal-regexp */
42
+ log.trace(`req.originalUrl before proxy stripping: ${req.originalUrl}`);
43
+ req.originalUrl = req.originalUrl.replace(new RegExp(`^/.+?${mountUrl}`), mountUrl);
44
+ log.trace(`req.originalUrl after proxy stripping: ${req.originalUrl}`);
45
+ /* eslint-enable security/detect-non-literal-regexp */
46
+ // Issuing this call will re-run this same middleware, so we use this
47
+ // `__CASA_BASE_URL_REWRITTEN__` flag to prevent recursion.
48
+ req.__CASA_BASE_URL_REWRITTEN__ = true;
49
+ req.app.handle(req, res, next);
50
+ }
51
+ },
52
+ ];
@@ -1,7 +1,6 @@
1
- declare function _default({ waypoint, fields, mountUrl, plan, }: {
1
+ declare function _default({ waypoint, fields, plan, }: {
2
2
  waypoint: any;
3
3
  fields?: any[] | undefined;
4
- mountUrl: any;
5
4
  plan: any;
6
5
  }): ((req: any, res: any, next: any) => any)[];
7
6
  export default _default;
@@ -16,9 +16,10 @@ const updateContext = ({ waypoint, errors = null, journeyContext, session, }) =>
16
16
  // Save to session
17
17
  JourneyContext_js_1.default.putContext(session, journeyContext);
18
18
  };
19
- exports.default = ({ waypoint, fields = [], mountUrl, plan, }) => [
19
+ exports.default = ({ waypoint, fields = [], plan, }) => [
20
20
  (req, res, next) => {
21
21
  var _a, _b;
22
+ const mountUrl = `${req.baseUrl}/`;
22
23
  let errors = [];
23
24
  for (let i = 0, l = fields.length; i < l; i++) {
24
25
  /* eslint-disable security/detect-object-injection */
@@ -1,8 +1,7 @@
1
- export default function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, }: {
1
+ export default function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, }: {
2
2
  globalHooks: any;
3
3
  pages: any;
4
4
  plan: any;
5
5
  csrfMiddleware: any;
6
- mountUrl: any;
7
6
  }): MutableRouter;
8
7
  import MutableRouter from "../lib/MutableRouter.js";
@@ -29,13 +29,14 @@ const renderMiddlewareFactory = (view, contextFactory) => [
29
29
  });
30
30
  },
31
31
  ];
32
- function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, }) {
32
+ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, }) {
33
33
  // Router
34
34
  const router = new MutableRouter_js_1.default();
35
35
  // Special "_" route which handles redirecting the user between sub-apps
36
36
  // /app1/_/?refmount=app2&route=prev
37
37
  router.all('/_', (req, res) => {
38
38
  var _a, _b;
39
+ const mountUrl = `${req.baseUrl}/`;
39
40
  const refmount = (_a = req.query) === null || _a === void 0 ? void 0 : _a.refmount;
40
41
  const route = (_b = req.query) === null || _b === void 0 ? void 0 : _b.route;
41
42
  log.trace(`App root ${mountUrl}: refmount = ${refmount}, route = ${route}`);
@@ -75,7 +76,6 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
75
76
  ];
76
77
  pages.forEach((page) => {
77
78
  const { waypoint, view, hooks: pageHooks = [], fields } = page;
78
- const formUrl = (0, waypoint_url_js_1.default)({ mountUrl, waypoint });
79
79
  const waypointPath = `/${waypoint}`;
80
80
  let commonWaypointMiddleware = [
81
81
  (req, res, next) => {
@@ -88,17 +88,17 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
88
88
  log.info(`Configuring "${waypoint}" as a skippable waypoint`);
89
89
  commonWaypointMiddleware = [
90
90
  ...commonWaypointMiddleware,
91
- ...(0, skip_waypoint_js_1.default)({ mountUrl, waypoint }),
91
+ ...(0, skip_waypoint_js_1.default)({ waypoint }),
92
92
  ];
93
93
  }
94
- router.get(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => ({
95
- formUrl,
94
+ router.get(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => ({
95
+ formUrl: (0, waypoint_url_js_1.default)({ mountUrl: `${req.baseUrl}/`, waypoint }),
96
96
  formData: req.casa.journeyContext.getDataForPage(waypoint),
97
97
  })));
98
- router.post(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, sanitise_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postsanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.pregather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, gather_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postgather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prevalidate', waypointPath, [...globalHooks, ...pageHooks]), ...(0, validate_fields_js_1.default)({ waypoint, fields, mountUrl, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postvalidate', waypointPath, [...globalHooks, ...pageHooks]),
98
+ router.post(waypointPath, ...commonMiddleware, ...commonWaypointMiddleware, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presteer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, steer_journey_js_1.default)({ waypoint, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.poststeer', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.presanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, sanitise_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postsanitise', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.pregather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, gather_fields_js_1.default)({ waypoint, fields }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postgather', waypointPath, [...globalHooks, ...pageHooks]), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prevalidate', waypointPath, [...globalHooks, ...pageHooks]), ...(0, validate_fields_js_1.default)({ waypoint, fields, plan }), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.postvalidate', waypointPath, [...globalHooks, ...pageHooks]),
99
99
  // If there were validation errors, jump out of this route and into the
100
100
  // next, where the errors will be rendered
101
- (req, res, next) => (req.casa.journeyContext.hasValidationErrorsForPage(waypoint) ? next('route') : next()), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.preredirect', waypointPath, [...globalHooks, ...pageHooks]), ...(0, progress_journey_js_1.default)({ waypoint, plan, mountUrl }));
101
+ (req, res, next) => (req.casa.journeyContext.hasValidationErrorsForPage(waypoint) ? next('route') : next()), ...(0, utils_js_1.resolveMiddlewareHooks)('journey.preredirect', waypointPath, [...globalHooks, ...pageHooks]), ...(0, progress_journey_js_1.default)({ waypoint, plan }));
102
102
  router.post(waypointPath, ...(0, utils_js_1.resolveMiddlewareHooks)('journey.prerender', waypointPath, [...globalHooks, ...pageHooks]), renderMiddlewareFactory(view, (req) => {
103
103
  var _a;
104
104
  const errors = (_a = req.casa.journeyContext.getValidationErrorsForPageByField(waypoint)) !== null && _a !== void 0 ? _a : Object.create(null);
@@ -114,7 +114,7 @@ function journeyRouter({ globalHooks, pages, plan, csrfMiddleware, mountUrl, })
114
114
  href: errors[k][0].fieldHref, /* eslint-disable-line security/detect-object-injection */
115
115
  }));
116
116
  return {
117
- formUrl,
117
+ formUrl: (0, waypoint_url_js_1.default)({ mountUrl: `${req.baseUrl}/`, waypoint }),
118
118
  formData: req.body,
119
119
  formErrors: Object.keys(errors).length ? errors : null,
120
120
  formErrorsGovukArray: govukErrors.length ? govukErrors : null,
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * @typedef {object} StaticOptions Options to configure static router
3
- * @property {string} [mountUrl=/] URL prefix for govuk-frontend static assets (optional, default /)
4
3
  * @property {number} [maxAge=3600000] Cache TTL for all assets (optional, default 1 hour)
5
4
  */
6
5
  /**
@@ -9,15 +8,11 @@
9
8
  * @param {StaticOptions} options Options
10
9
  * @returns {MutableRouter} ExpressJS Router instance
11
10
  */
12
- export default function staticRouter({ mountUrl, maxAge, }: StaticOptions): MutableRouter;
11
+ export default function staticRouter({ maxAge, }?: StaticOptions): MutableRouter;
13
12
  /**
14
13
  * Options to configure static router
15
14
  */
16
15
  export type StaticOptions = {
17
- /**
18
- * )
19
- */
20
- mountUrl?: string | undefined;
21
16
  /**
22
17
  * Cache TTL for all assets (optional, default 1 hour)
23
18
  */
@@ -10,11 +10,11 @@ const path_1 = require("path");
10
10
  const module_1 = require("module");
11
11
  const dirname_cjs_1 = __importDefault(require("./dirname.cjs"));
12
12
  const MutableRouter_js_1 = __importDefault(require("../lib/MutableRouter.js"));
13
+ const utils_js_1 = require("../lib/utils.js");
13
14
  const { static: ExpressStatic } = express_1.default; // CommonJS
14
15
  const oneDay = 86400000;
15
16
  /**
16
17
  * @typedef {object} StaticOptions Options to configure static router
17
- * @property {string} [mountUrl=/] URL prefix for govuk-frontend static assets (optional, default /)
18
18
  * @property {number} [maxAge=3600000] Cache TTL for all assets (optional, default 1 hour)
19
19
  */
20
20
  /**
@@ -23,7 +23,7 @@ const oneDay = 86400000;
23
23
  * @param {StaticOptions} options Options
24
24
  * @returns {MutableRouter} ExpressJS Router instance
25
25
  */
26
- function staticRouter({ mountUrl = '/', maxAge = 3600000, }) {
26
+ function staticRouter({ maxAge = 3600000, } = {}) {
27
27
  const router = new MutableRouter_js_1.default();
28
28
  const notFoundHandler = (req, res, next) => {
29
29
  // Fall through to a general purpose error handler
@@ -52,16 +52,16 @@ function staticRouter({ mountUrl = '/', maxAge = 3600000, }) {
52
52
  // The CASA CSS source contains the placeholder `~~~CASA_MOUNT_URL~~~` which
53
53
  // must be replaced with the dynamic `mountUrl` to ensure govuk-frontend
54
54
  // assets are served from the correct location.
55
- const casaCss = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa.css'), { encoding: 'utf8' }).replace(/~~~CASA_MOUNT_URL~~~/g, mountUrl);
56
- const casaCssIe8 = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa-ie8.css'), { encoding: 'utf8' }).replace(/~~~CASA_MOUNT_URL~~~/g, mountUrl);
55
+ const casaCss = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa.css'), { encoding: 'utf8' });
56
+ const casaCssIe8 = (0, fs_1.readFileSync)((0, path_1.resolve)(dirname_cjs_1.default, '../../dist/assets/css/casa-ie8.css'), { encoding: 'utf8' });
57
57
  // The static middleware will only server GET/HEAD requests, so we can mount
58
58
  // the middleware using `use()` rather than resorting to `get()`
59
59
  const govukFrontendDirectory = (0, path_1.resolve)((0, module_1.createRequire)(dirname_cjs_1.default).resolve('govuk-frontend'), '../../');
60
60
  router.use('/govuk/assets/js/all.js', ExpressStatic(`${govukFrontendDirectory}/govuk/all.js`, staticConfig));
61
61
  router.use('/govuk/assets', ExpressStatic(`${govukFrontendDirectory}/govuk/assets`, staticConfig));
62
62
  router.use('/govuk/assets', notFoundHandler);
63
- router.use('/casa/assets/css/casa.css', setHeaders, (req, res) => res.send(casaCss));
64
- router.use('/casa/assets/css/casa-ie8.css', setHeaders, (req, res) => res.send(casaCssIe8));
63
+ router.get('/casa/assets/css/casa.css', setHeaders, (req, res) => res.send(casaCss.replace(/~~~CASA_MOUNT_URL~~~/g, (0, utils_js_1.validateUrlPath)(`${req.baseUrl}/`))));
64
+ router.get('/casa/assets/css/casa-ie8.css', setHeaders, (req, res) => res.send(casaCssIe8.replace(/~~~CASA_MOUNT_URL~~~/g, (0, utils_js_1.validateUrlPath)(`${req.baseUrl}/`))));
65
65
  router.use('/casa/assets', notFoundHandler);
66
66
  return router;
67
67
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwp/govuk-casa",
3
- "version": "8.1.0",
3
+ "version": "8.2.2",
4
4
  "description": "A framework for building GOVUK Collect-And-Submit-Applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,50 +43,52 @@
43
43
  "dependencies": {
44
44
  "cookie-parser": "1.4.6",
45
45
  "csurf": "1.11.0",
46
- "debug": "4.3.3",
46
+ "debug": "4.3.4",
47
47
  "deepmerge": "4.2.2",
48
- "express": "4.17.3",
49
- "express-session": "1.17.2",
48
+ "express": "4.18.1",
49
+ "express-session": "1.17.3",
50
50
  "govuk-frontend": "4.0.1",
51
51
  "graphlib": "2.1.8",
52
52
  "helmet": "5.0.2",
53
- "i18next": "21.6.14",
53
+ "i18next": "21.8.2",
54
54
  "i18next-http-middleware": "3.2.0",
55
55
  "js-yaml": "4.1.0",
56
56
  "lodash": "4.17.21",
57
- "luxon": "2.3.1",
57
+ "luxon": "2.4.0",
58
58
  "nunjucks": "3.2.3",
59
+ "path-to-regexp": "6.2.1",
59
60
  "uuid": "8.3.2",
60
61
  "validator": "13.7.0"
61
62
  },
62
63
  "devDependencies": {
63
- "@babel/core": "7.17.5",
64
+ "@babel/core": "7.17.12",
64
65
  "@babel/eslint-parser": "7.17.0",
65
- "@babel/preset-env": "7.16.11",
66
- "@commitlint/config-conventional": "16.2.1",
66
+ "@babel/preset-env": "7.17.12",
67
+ "@commitlint/config-conventional": "17.0.0",
67
68
  "@dwp/casa-spiderplan": "2.4.0",
68
69
  "@dwp/casa-spiderplan-a11y-plugin": "0.1.4",
69
70
  "@dwp/casa-spiderplan-zap-plugin": "0.1.1",
70
71
  "@dwp/eslint-config-base": "6.0.0",
71
72
  "@types/express": "4.17.13",
72
- "@types/node": "17.0.21",
73
+ "@types/node": "17.0.34",
73
74
  "@types/nunjucks": "3.2.1",
74
75
  "babel-eslint": "10.1.0",
75
- "c8": "7.11.0",
76
+ "c8": "7.11.3",
76
77
  "chai": "4.3.6",
77
- "commitlint": "16.2.1",
78
- "eslint": "8.10.0",
78
+ "cheerio": "1.0.0-rc.10",
79
+ "commitlint": "17.0.0",
80
+ "eslint": "8.15.0",
79
81
  "eslint-plugin-no-unsafe-regex": "1.0.0",
80
- "eslint-plugin-security": "1.4.0",
81
- "eslint-plugin-sonarjs": "0.12.0",
82
- "fast-check": "2.22.0",
83
- "husky": "7.0.4",
84
- "mocha": "9.2.1",
85
- "sass": "1.49.9",
86
- "sinon": "13.0.1",
82
+ "eslint-plugin-security": "1.5.0",
83
+ "eslint-plugin-sonarjs": "0.13.0",
84
+ "fast-check": "2.25.0",
85
+ "husky": "8.0.1",
86
+ "mocha": "10.0.0",
87
+ "sass": "1.51.0",
88
+ "sinon": "14.0.0",
87
89
  "sinon-chai": "3.7.0",
88
- "standard-version": "9.3.2",
89
- "supertest": "6.2.2",
90
- "typescript": "4.6.2"
90
+ "standard-version": "9.5.0",
91
+ "supertest": "6.2.3",
92
+ "typescript": "4.6.4"
91
93
  }
92
94
  }
@@ -4,6 +4,8 @@ Component to wrap your form inputs in a `<form>` that contains all the required
4
4
 
5
5
  A "Continue" button (and "Cancel" link if in edit mode) will also be added.
6
6
 
7
+ - [1.3.5: Identify Input Purpose](https://www.w3.org/WAI/WCAG21/Understanding/identify-input-purpose.html)
8
+ - [<form>: The Form element - autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#attr-autocomplete)
7
9
  ## Example usage
8
10
 
9
11
  ```nunjucks
@@ -49,6 +51,7 @@ Note that the submit button is configured to prevent double-clicks and avoid dup
49
51
  | Name | Type | Required | Description |
50
52
  |------|------|----------|-------------|
51
53
  | `formUrl` | string | Yes | The form's "action", available in a `formUrl` template variable |
54
+ | `autoComplete` | string | No | Allows you to override the autocomplete paramater - the default value is 'off'
52
55
  | `csrfToken` | string | Yes | Token used to protect form from CSRF (available to user's templates via the global `casa.csrfToken` variable) |
53
56
  | `inEditMode` | boolean | No | Toggle edit-mode of the form (available to page templates using default GET/POST handlers via the local `inEditMode` variable) |
54
57
  | `editOriginUrl` | string | No | Absolute URL to the page from which the edit request came (defaults to `review`) (available to user's templates using default GET/POST handlers via the local `editOriginUrl` variable) |
@@ -1,6 +1,6 @@
1
1
  {% from "govuk/components/button/macro.njk" import govukButton %}
2
2
 
3
- <form action="{{ params.formUrl }}" method="post" autocomplete="off" novalidate class="casa-journey-form">
3
+ <form action="{{ params.formUrl }}" method="post" autocomplete="{{ params.autoComplete | default('off', true) }}" novalidate class="casa-journey-form">
4
4
  <input type="hidden" name="_csrf" value="{{ params.csrfToken }}" />
5
5
  {% if params.inEditMode %}<input type="hidden" name="edit" value="true" />{% endif %}
6
6
  {% if params.inEditMode and params.editOriginUrl %}<input type="hidden" name="editorigin" value="{{ params.editOriginUrl }}" />{% endif %}
@@ -1,5 +1,5 @@
1
1
  <!-- CASA -->
2
- <script src="{{ casa.mountUrl }}govuk/assets/js/all.js?{{ casaVersion }}"></script>
2
+ <script src="{{ casa.staticMountUrl }}govuk/assets/js/all.js?{{ casaVersion }}"></script>
3
3
  <script nonce="{{ cspNonce }}">
4
4
  window.GOVUKFrontend.initAll();
5
5
  if (window.history.replaceState) {
@@ -1,6 +1,6 @@
1
1
  <!-- CASA -->
2
- <!--[if gt IE 8]><!--><link href="{{ casa.mountUrl }}casa/assets/css/casa.css?{{ casaVersion }}" rel="stylesheet" /><!--<![endif]-->
2
+ <!--[if gt IE 8]><!--><link href="{{ casa.staticMountUrl }}casa/assets/css/casa.css?{{ casaVersion }}" rel="stylesheet" /><!--<![endif]-->
3
3
  <!--[if lte IE 8]>
4
- <link href="{{ casa.mountUrl }}casa/assets/css/casa-ie8.css?{{ casaVersion }}" rel="stylesheet" />
4
+ <link href="{{ casa.staticMountUrl }}casa/assets/css/casa-ie8.css?{{ casaVersion }}" rel="stylesheet" />
5
5
  <![endif]-->
6
6
  <!-- /CASA -->