@govuk-one-login/frontend-ui 1.0.2 → 1.2.0

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 (41) hide show
  1. package/README.md +33 -7
  2. package/build/all.css +1 -1
  3. package/build/cjs/index-fe.cjs +122 -0
  4. package/build/cjs/index.cjs +44 -8
  5. package/build/cjs/index.d.cts +38 -0
  6. package/build/cjs/index.d.ts +25 -16
  7. package/build/cjs/index.d.ts.map +1 -1
  8. package/build/components/_all.scss +2 -0
  9. package/build/components/bases/auth/auth-base.njk +137 -0
  10. package/build/components/bases/home/home-base.njk +135 -0
  11. package/build/components/bases/identity/identity-base-form.njk +14 -1
  12. package/build/components/bases/identity/identity-base-page.njk +11 -4
  13. package/build/components/bases/ipv-core/ipv-core-base.njk +154 -0
  14. package/build/components/bases/mobile/mobile-base.njk +129 -0
  15. package/build/components/cookie-banner/template.njk +1 -1
  16. package/build/components/footer/_index.scss +8 -0
  17. package/build/components/footer/template.njk +7 -4
  18. package/build/components/header/_index.scss +54 -0
  19. package/build/components/header/template.njk +36 -85
  20. package/build/components/macros/logo.njk +75 -0
  21. package/build/components/phase-banner/_index.scss +16 -16
  22. package/build/components/phase-banner/template.njk +4 -0
  23. package/build/components/spinner/README.md +107 -0
  24. package/build/components/spinner/_index.scss +64 -0
  25. package/build/components/spinner/api.njk +27 -0
  26. package/build/components/spinner/javascript/spinner.js +118 -0
  27. package/build/components/spinner/macro.njk +3 -0
  28. package/build/components/spinner/template.njk +11 -0
  29. package/build/esm/index-fe.js +120 -0
  30. package/build/esm/index.d.ts +25 -16
  31. package/build/esm/index.d.ts.map +1 -1
  32. package/build/esm/index.js +43 -9
  33. package/build/frontendUiAssets/images/favicon.ico +0 -0
  34. package/build/frontendUiAssets/images/favicon.svg +1 -0
  35. package/build/frontendUiAssets/images/govuk-crest.svg +1 -0
  36. package/build/frontendUiAssets/images/govuk-icon-180.png +0 -0
  37. package/build/frontendUiAssets/images/govuk-icon-192.png +0 -0
  38. package/build/frontendUiAssets/images/govuk-icon-512.png +0 -0
  39. package/build/frontendUiAssets/images/govuk-icon-mask.svg +1 -0
  40. package/build/frontendUiAssets/images/govuk-opengraph-image.png +0 -0
  41. package/package.json +15 -5
package/README.md CHANGED
@@ -27,7 +27,7 @@ app.set(
27
27
  'view engine',
28
28
  configureNunjucks(app, [
29
29
  // ... other view paths ...
30
- path.resolve('node_modules/@govuk-one-login/frontend-ui'),
30
+ path.resolve('node_modules/@govuk-one-login/frontend-ui')
31
31
  ]),
32
32
  );
33
33
  ```
@@ -44,20 +44,25 @@ const frontendUi = require("@govuk-one-login/frontend-ui");
44
44
 
45
45
  nunjucksEnv.addGlobal("addLanguageParam", frontendUi.addLanguageParam);
46
46
  nunjucksEnv.addGlobal("contactUsUrl", frontendUi.contactUsUrl);
47
+ nunjucksEnv.addGlobal("May_2025_Rebrand", process.env.May_2025_Rebrand == "true");
47
48
  ```
48
49
  Typescript:
49
50
  ```typescript
50
51
  import {contactUsUrl, addLanguageParam } from "@govuk-one-login/frontend-ui";
51
52
  nunjucksEnv.addGlobal("addLanguageParam", addLanguageParam);
52
53
  nunjucksEnv.addGlobal("contactUsUrl", contactUsUrl);
54
+ nunjucksEnv.addGlobal("May_2025_Rebrand", process.env.May_2025_Rebrand == "true");
53
55
  ```
54
56
 
57
+ In order to use the `May_2025_Rebrand` variable you will need to create or add to your `.env` file the following `May_2025_Rebrand=`[true/false]
58
+
55
59
  ### 4. Load Translations and Configure Middleware
56
60
 
57
61
  In your `app.js`, import necessary functions and load translations after initializing i18next (Identity teams may need to use the bypass function as their i18n setup is different):
58
62
 
59
63
  ```javascript
60
64
  const {
65
+ setBaseTranslations
61
66
  setFrontendUiTranslations,
62
67
  frontendUiMiddleware,
63
68
  } = require('@govuk-one-login/frontend-ui');
@@ -74,8 +79,6 @@ i18next
74
79
  ),
75
80
  },
76
81
  (err) => {
77
- // Load Frontend UI translations after i18next initialization and pass current instance of i18next
78
- setFrontendUiTranslations(i18next);
79
82
 
80
83
  if (err) {
81
84
  console.error('i18next init failed:', err);
@@ -84,6 +87,9 @@ i18next
84
87
  );
85
88
 
86
89
  // Apply the middleware
90
+ setBaseTranslations(i18next);
91
+ setFrontendUiTranslations(i18next);
92
+ app.use(i18nextMiddleware.handle(i18next));
87
93
  app.use(frontendUiMiddleware);
88
94
 
89
95
  // For Identity teams a language setting bypass may be required, first import the bypass function and then configure router to use the new function at the top of your router.use functions
@@ -116,19 +122,39 @@ or
116
122
  ```
117
123
 
118
124
  ### 6. Import all.css
119
- Import the css into your service in the `package.json` via the `build-sass` script
125
+ Import the css into your service in the `package.json` via the `build-sass` script.
126
+ ```
127
+ sass --no-source-map ../../node_modules/@govuk-one-login/frontend-ui/build/all.css [WhereYouStoreStyleSheets]/frontendUi.css --style compressed"
128
+ ```
129
+
130
+ You will also need to add the following in order to ensure that the assets all load properly across the basefiles
120
131
  ```
121
- sass --no-source-map node_modules/@govuk-one-login/frontend-ui/build/all.css *destination*/frontend-ui.css --style compressed"
132
+ "cp -R ../../node_modules/@govuk-one-login/frontend-ui/build/frontendUiAssets [OneLevelAboveWhereYouStoreStyleSheets/SameFolderAsStyleSheets]/"
122
133
  ```
123
134
 
124
- include a link to this in your template file
135
+ include a link to this in your template file, this has been done in the created basefiles already
125
136
  ```html
126
137
  {% block head %}
127
138
  '''
128
- <link href="/stylesheets/frontend-ui.css" rel="stylesheet">
139
+ <link rel="stylesheet" href="/[WhereYouStoreStyleSheets]/frontendUi.css"/>
129
140
  {% endblock %}
130
141
  ```
131
142
 
143
+ Or alternatively you can import the frontend-ui all.css directly into your exisiting css file if you are having a 'flickering' issue
144
+ ``` scss
145
+ $govuk-assets-path: "/public/";
146
+ $hmpo-summary-list: false;
147
+
148
+ @import "../../../node_modules/govuk-frontend/govuk/all";
149
+ @import "../../../node_modules/hmpo-components/all";
150
+ @import "../../../node_modules/@govuk-one-login/frontend-language-toggle/stylesheet/styles";
151
+ @import "../../../node_modules/accessible-autocomplete/dist/accessible-autocomplete.min";
152
+ @import "components/button-spinner";
153
+ @import "components/country-picker";
154
+
155
+ @import "../../../node_modules/@govuk-one-login/frontend-ui/build/all.css"; // <--- HERE>
156
+
157
+ ```
132
158
 
133
159
  ### 7. Add Component to Template
134
160
 
package/build/all.css CHANGED
@@ -1 +1 @@
1
- @media(max-width: 640px){.govuk-header__navigation-item{border-left:none !important}}.frontendUi_header_signOut-item{padding:5px 0px 5px 30px;border-left:1px solid #b1b4b6;margin-left:auto}.frontendUi_header__signOut{display:flex}.frontendUi-header__content{margin-left:auto}.govuk-tag{font-family:"GDS Transport",arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-weight:700;font-size:14px;font-size:.875rem;line-height:1;display:inline-block;padding-top:5px;padding-right:8px;padding-bottom:4px;padding-left:8px;outline:2px solid rgba(0,0,0,0);outline-offset:-2px;color:#fff !important;background-color:#1d70b8 !important;letter-spacing:1px !important;text-decoration:none !important;text-transform:uppercase !important}/*! Copyright (c) 2011 by Margaret Calvert & Henrik Kubel. All rights reserved. The font has been customised for exclusive use on gov.uk. This cut is not commercially available. */@font-face{font-family:"GDS Transport";font-style:normal;font-weight:normal;src:url("/assets/fonts/light-94a07e06a1-v2.woff2") format("woff2"),url("/assets/fonts/light-f591b13f7d-v2.woff") format("woff");font-display:fallback}@font-face{font-family:"GDS Transport";font-style:normal;font-weight:bold;src:url("/assets/fonts/bold-b542beb274-v2.woff2") format("woff2"),url("/assets/fonts/bold-affa96571d-v2.woff") format("woff");font-display:fallback}@media print{.govuk-tag{font-family:sans-serif}}@media(min-width: 40.0625em){.govuk-tag{font-size:16px;font-size:1rem;line-height:1}}@media print{.govuk-tag{font-size:14pt;line-height:1}}.language-select{margin:15px 0 15px 0}.language-select__list{margin-top:1em;text-align:right}.language-select__list-item{display:inline-block}.language-select__list-item:first-child::after{content:"";display:inline-block;position:relative;top:.1875em;height:1em;border-right:.09375em solid #000}.language-select__list-item a,.language-select__list-item [aria-current]{padding:.3125em}@media screen and (max-width: 641px){.language-select__list{float:none;text-align:left;padding-bottom:10px;border-bottom:1px solid #b1b4b6}}
1
+ @media(max-width: 640px){.govuk-header__navigation-item{border-left:none !important}.govuk-template--rebranded .govuk-header__navigation-list{padding-bottom:0px !important}.govuk-header__navigation-item{padding-top:5px !important}}.frontendUi_header_signOut-item{padding:5px 0px 5px 30px;border-left:1px solid #b1b4b6;margin-left:auto}.frontendUi_header_signOut-item--rebrand{border-left:none;padding:5px 0px 5px 30px;margin-left:auto;font-weight:700 !important}.frontendUi_header__signOut{display:flex;flex-wrap:wrap}.frontendUi-header__content{margin-left:auto}.govuk-header__navigation--signOut{padding:15px 0 15px !important}.govuk-template--rebranded .govuk-header__navigation{padding:15px 0 15px !important}@media(max-width: 640px){.govuk-header__navigation--signOut{padding:18px 0 8px !important}.govuk-template--rebranded .govuk-header__navigation{padding:18px 0 8px !important}}.govuk-template--rebranded .govuk-header__navigation-item a{font-weight:700 !important}.govuk-template--rebranded .govuk-header__navigation-item{padding-top:5px !important}@media(min-width: 20em)and (max-width: 48.0525em){.govuk-template--rebranded .govuk-header__navigation-list{padding-bottom:0px}}@media(max-width: 340px){.frontendUi-header__content{margin-left:unset}}.govuk-tag{font-family:"GDS Transport",arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-weight:700;font-size:14px;font-size:.875rem;line-height:1;display:inline-block;padding-top:5px;padding-right:8px;padding-bottom:4px;padding-left:8px;outline:2px solid rgba(0,0,0,0);outline-offset:-2px;color:#fff !important;background-color:#1d70b8 !important;letter-spacing:1px !important;text-decoration:none !important;text-transform:uppercase !important}/*! Copyright (c) 2011 by Margaret Calvert & Henrik Kubel. All rights reserved. The font has been customised for exclusive use on gov.uk. This cut is not commercially available. */@font-face{font-family:"GDS Transport";font-style:normal;font-weight:normal;src:url("/assets/fonts/light-94a07e06a1-v2.woff2") format("woff2"),url("/assets/fonts/light-f591b13f7d-v2.woff") format("woff");font-display:fallback}@font-face{font-family:"GDS Transport";font-style:normal;font-weight:bold;src:url("/assets/fonts/bold-b542beb274-v2.woff2") format("woff2"),url("/assets/fonts/bold-affa96571d-v2.woff") format("woff");font-display:fallback}@media print{.govuk-tag{font-family:sans-serif}}@media(min-width: 40.0625em){.govuk-tag{font-size:16px;font-size:1rem;line-height:1}}@media print{.govuk-tag{font-size:14pt;line-height:1}}.language-select{margin:15px 0 15px 0}.language-select__list{margin-top:1em;text-align:right}.language-select__list-item{display:inline-block}.language-select__list-item:first-child::after{content:"";display:inline-block;position:relative;top:.1875em;height:1em;border-right:.09375em solid #000}.language-select__list-item a,.language-select__list-item [aria-current]{padding:.3125em}@media screen and (max-width: 641px){.language-select__list{float:none;text-align:left;padding-bottom:10px;border-bottom:1px solid #b1b4b6}}.spinner{width:80px;height:80px;border-radius:50%;border-width:12px;border-style:solid;border-color:#dee0e2;border-top-color:#005ea5;margin-bottom:govuk-spacing(3)}@media(forced-colors: active){.spinner{forced-color-adjust:none;border-top-color:rgba(0,0,0,0) !important}}@media not (prefers-reduced-motion){.spinner{-webkit-animation:spin 2s linear infinite;animation:spin 2s linear infinite}}@media(prefers-reduced-motion){.spinner{transform:rotate(0.125turn)}}.spinner__ready{border-color:#005ea5;-webkit-animation:none;animation:none}#spinner-container__error .spinner,#spinner-container__error .spinner-state-text{display:none}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.centre{margin-left:auto;margin-right:auto;text-align:center;display:block}.govuk-footer__copyright-logo::before{background-image:url("../frontendUiAssets/images/govuk-crest.svg"),url("/frontendUiAssets/images/govuk-crest.svg") !important}.govuk-footer__copyright-logo{background-image:url("../frontendUiAssets/images/govuk-crest.svg"),url("/frontendUiAssets/images/govuk-crest.svg") !important}
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ const WaitInteractions = (() => {
4
+ const content = {
5
+ initial: {
6
+ spinnerState: "pending",
7
+ },
8
+ complete: { spinnerState: "completed" },
9
+ };
10
+
11
+ const state = {
12
+ spinnerState: content.initial.spinnerState,
13
+ done: false,
14
+ virtualDom: [],
15
+ };
16
+
17
+ const timers = {};
18
+
19
+ const createVirtualDom = () => {
20
+ const initialState = [
21
+ {
22
+ nodeName: "div",
23
+ id: "spinner",
24
+ classes: ["spinner", "spinner__pending", "centre", state.spinnerState],
25
+ },
26
+ ];
27
+
28
+ return initialState;
29
+ };
30
+
31
+ const vDomHasChanged = (currentVDom, nextVDom) => {
32
+ return JSON.stringify(currentVDom) !== JSON.stringify(nextVDom);
33
+ };
34
+
35
+ const updateDom = () => {
36
+ const vDomChanged = vDomHasChanged(state.virtualDom, createVirtualDom());
37
+ const container = document.getElementById("spinner-container");
38
+
39
+ if (vDomChanged) {
40
+ state.virtualDom = createVirtualDom();
41
+ const elements = state?.virtualDom?.map(convert);
42
+ container?.replaceChildren(...elements);
43
+ }
44
+
45
+ if (state.error) {
46
+ container?.classList.add("spinner-container__error");
47
+ }
48
+
49
+ if (state.done) {
50
+ clearInterval(timers.updateDomTimer);
51
+ }
52
+ };
53
+
54
+ const reflectCompletion = () => {
55
+ state.spinnerState = "spinner__ready";
56
+ state.spinnerStateText = content.complete.spinnerState;
57
+ state.done = true;
58
+ };
59
+
60
+ const reflectError = () => {
61
+ state.spinnerState = "spinner__failed";
62
+ state.done = true;
63
+ state.error = true;
64
+ };
65
+
66
+ const convert = (node) => {
67
+ const el = document.createElement(node.nodeName);
68
+ if (node.text) el.textContent = node.text;
69
+ if (node.innerHTML) el.innerHTML = node.innerHTML;
70
+ if (node.id) el.id = node.id;
71
+ if (node.classes) el.classList.add(...node.classes);
72
+ return el;
73
+ };
74
+
75
+ const notInErrorOrDoneState = () => {
76
+ return !(state.done || state.error);
77
+ };
78
+
79
+ const requestIDProcessingStatus = async () => {
80
+ const apiRoute =
81
+ document?.getElementById("spinner-container")?.dataset.apiRoute;
82
+ try {
83
+ const response = await fetch(apiRoute);
84
+
85
+ if (response.status !== 200) {
86
+ throw new Error(`Status code ${response.status} received`);
87
+ }
88
+
89
+ const data = await response.json();
90
+
91
+ if (data.status === "Clear to proceed") {
92
+ reflectCompletion();
93
+ } else if (notInErrorOrDoneState()) {
94
+ setTimeout(async () => {
95
+ await requestIDProcessingStatus();
96
+ }, 1000);
97
+ }
98
+ } catch (e) {
99
+ console.log(e);
100
+ reflectError();
101
+ }
102
+ };
103
+
104
+ return {
105
+ state: state,
106
+ init: () => {
107
+ timers.updateDomTimer = setInterval(updateDom, 2000);
108
+
109
+ timers.abortUnresponsiveRequest = setTimeout(() => {
110
+ reflectError();
111
+ }, 15000);
112
+
113
+ updateDom();
114
+
115
+ requestIDProcessingStatus().then(() => {
116
+ updateDom();
117
+ });
118
+ },
119
+ };
120
+ })();
121
+
122
+ exports.WaitInteractions = WaitInteractions;
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ var path = require('path');
4
+ var fs = require('fs');
5
+
3
6
  var cookieBanner$1 = {
4
7
  body1: "Rydym yn defnyddio rhai cwcis hanfodol i wneud i'r gwasanaeth hwn weithio.",
5
8
  body2: "Hoffem osod cwcis ychwanegol er mwyn i ni allu cofio eich gosodiadau, deall sut mae pobl yn defnyddio'r gwasanaeth a gwneud gwelliannau.",
@@ -56,7 +59,8 @@ var footer$1 = {
56
59
  };
57
60
  var header$1 = {
58
61
  signOut: "Allgofnodi",
59
- ariaLabel: "GOV.UK One Login dewislen"
62
+ ariaLabel: "GOV.UK One Login dewislen",
63
+ signOutAriaLabel: "Allgofnodi"
60
64
  };
61
65
  var languageSelect$1 = {
62
66
  ariaLabel: "Switcher iaith"
@@ -134,7 +138,8 @@ var footer = {
134
138
  };
135
139
  var header = {
136
140
  signOut: "Sign Out",
137
- ariaLabel: "GOV.UK One Login menu"
141
+ ariaLabel: "GOV.UK One Login menu",
142
+ signOutAriaLabel: "Sign out"
138
143
  };
139
144
  var languageSelect = {
140
145
  ariaLabel: "Select language"
@@ -156,20 +161,24 @@ var translationEn = {
156
161
  skipLink: skipLink
157
162
  };
158
163
 
164
+ // Implementation
165
+ function frontendUiMiddleware(req, res, next) {
166
+ res.locals.translations = req.i18n.store.data[req.i18n.language];
167
+ res.locals.basePath = process.cwd();
168
+ next();
169
+ }
159
170
  const setFrontendUiTranslations = (instanceI18n) => {
160
171
  instanceI18n.addResourceBundle("en", "translation", translationEn, true, false);
161
172
  instanceI18n.addResourceBundle("cy", "translation", translationCy, true, false);
162
173
  };
163
- const frontendUiMiddleware = (req, res, next) => {
164
- res.locals.translations = req.i18n.store.data[req.i18n.language];
165
- next();
166
- };
167
174
  const frontendUiMiddlewareIdentityBypass = (req, res, next) => {
168
175
  const localTranslations = {
169
176
  en: translationEn,
170
- cy: translationCy
177
+ cy: translationCy,
171
178
  };
172
- res.locals.translations = localTranslations[req.i18n.language];
179
+ const language = req.i18n.language;
180
+ res.locals.translations = localTranslations[language] || {};
181
+ res.locals.basePath = process.cwd();
173
182
  next();
174
183
  };
175
184
  function addLanguageParam(language, url) {
@@ -187,9 +196,36 @@ function contactUsUrl(baseUrl, urlToAppend) {
187
196
  const searchParams = new URLSearchParams({ fromURL: urlToAppend });
188
197
  return `${baseUrl}?${searchParams.toString()}`;
189
198
  }
199
+ const setBaseTranslations = (instanceI18n, filePath) => {
200
+ ["cy", "en"].forEach((locale) => {
201
+ instanceI18n.addResourceBundle(locale, "translation", getTranslationObject(locale, filePath));
202
+ });
203
+ };
204
+ const getTranslationObject = (locale, filepath) => {
205
+ const possiblePaths = [
206
+ path.resolve("locales", locale, "translation.json"),
207
+ path.resolve("src/locales", locale, "translation.json"),
208
+ path.resolve(filepath !== null && filepath !== void 0 ? filepath : "", locale, "translation.json"),
209
+ ];
210
+ for (const filePath of possiblePaths) {
211
+ if (fs.existsSync(filePath)) {
212
+ try {
213
+ const fileContent = fs.readFileSync(filePath, "utf8");
214
+ return JSON.parse(fileContent);
215
+ }
216
+ catch (error) {
217
+ console.error(`Error reading or parsing translation file at ${filePath}:`, error);
218
+ }
219
+ }
220
+ }
221
+ console.warn(`No translation file found for locale: ${locale}`);
222
+ return {}; // Return an empty object as a fallback
223
+ };
190
224
 
191
225
  exports.addLanguageParam = addLanguageParam;
192
226
  exports.contactUsUrl = contactUsUrl;
193
227
  exports.frontendUiMiddleware = frontendUiMiddleware;
194
228
  exports.frontendUiMiddlewareIdentityBypass = frontendUiMiddlewareIdentityBypass;
229
+ exports.getTranslationObject = getTranslationObject;
230
+ exports.setBaseTranslations = setBaseTranslations;
195
231
  exports.setFrontendUiTranslations = setFrontendUiTranslations;
@@ -0,0 +1,38 @@
1
+ import i18next from "i18next";
2
+ import { NextFunction, Request, Response } from "express";
3
+ interface I18nData {
4
+ language: string;
5
+ store: {
6
+ data: {
7
+ [key: string]: unknown;
8
+ };
9
+ };
10
+ }
11
+ interface ExpressRequest extends Request {
12
+ i18n: I18nData;
13
+ }
14
+ interface ExpressResponse extends Response {
15
+ locals: {
16
+ translations: unknown;
17
+ basePath?: string;
18
+ };
19
+ }
20
+ interface PlainRequest {
21
+ i18n: I18nData;
22
+ }
23
+ interface PlainResponse {
24
+ locals: {
25
+ translations: unknown;
26
+ basePath?: string;
27
+ };
28
+ }
29
+ export declare function frontendUiMiddleware(req: ExpressRequest, res: ExpressResponse, next: NextFunction): void;
30
+ export declare function frontendUiMiddleware(req: PlainRequest, res: PlainResponse, next: NextFunction): void;
31
+ export declare const setFrontendUiTranslations: (instanceI18n: typeof i18next) => void;
32
+ export declare const frontendUiMiddlewareIdentityBypass: (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => void;
33
+ export declare function addLanguageParam(language: string, url?: URL): string;
34
+ export declare function contactUsUrl(baseUrl: string, urlToAppend: string): string | null;
35
+ export declare const setBaseTranslations: (instanceI18n: typeof i18next, filePath?: string) => void;
36
+ export declare const getTranslationObject: (locale: string, filepath?: string) => Record<string, unknown>;
37
+ export {};
38
+ //# sourceMappingURL=index.d.ts.map
@@ -1,29 +1,38 @@
1
1
  import i18next from "i18next";
2
2
  import { NextFunction, Request, Response } from "express";
3
- export declare const setFrontendUiTranslations: (instanceI18n: typeof i18next) => void;
4
- export declare const frontendUiMiddleware: (req: Request & {
5
- i18n: {
6
- language: string;
7
- store: {
8
- data: {
9
- [key: string]: unknown;
10
- };
3
+ interface I18nData {
4
+ language: string;
5
+ store: {
6
+ data: {
7
+ [key: string]: unknown;
11
8
  };
12
9
  };
13
- }, res: Response & {
10
+ }
11
+ interface ExpressRequest extends Request {
12
+ i18n: I18nData;
13
+ }
14
+ interface ExpressResponse extends Response {
14
15
  locals: {
15
16
  translations: unknown;
17
+ basePath?: string;
16
18
  };
17
- }, next: NextFunction) => void;
18
- export declare const frontendUiMiddlewareIdentityBypass: (req: Request & {
19
- i18n: {
20
- language: "en" | "cy";
21
- };
22
- }, res: Response & {
19
+ }
20
+ interface PlainRequest {
21
+ i18n: I18nData;
22
+ }
23
+ interface PlainResponse {
23
24
  locals: {
24
25
  translations: unknown;
26
+ basePath?: string;
25
27
  };
26
- }, next: NextFunction) => void;
28
+ }
29
+ export declare function frontendUiMiddleware(req: ExpressRequest, res: ExpressResponse, next: NextFunction): void;
30
+ export declare function frontendUiMiddleware(req: PlainRequest, res: PlainResponse, next: NextFunction): void;
31
+ export declare const setFrontendUiTranslations: (instanceI18n: typeof i18next) => void;
32
+ export declare const frontendUiMiddlewareIdentityBypass: (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => void;
27
33
  export declare function addLanguageParam(language: string, url?: URL): string;
28
34
  export declare function contactUsUrl(baseUrl: string, urlToAppend: string): string | null;
35
+ export declare const setBaseTranslations: (instanceI18n: typeof i18next, filePath?: string) => void;
36
+ export declare const getTranslationObject: (locale: string, filepath?: string) => Record<string, unknown>;
37
+ export {};
29
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI1D,eAAO,MAAM,yBAAyB,GAAI,cAAc,OAAO,OAAO,SAerE,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,KAAK,OAAO,GAAG;IACb,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE;YAAE,IAAI,EAAE;gBAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,CAAC;CACzE,EACD,KAAK,QAAQ,GAAG;IAAE,MAAM,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,EACrD,MAAM,YAAY,SAInB,CAAC;AAEF,eAAO,MAAM,kCAAkC,GAC7C,KAAK,OAAO,GAAG;IACb,IAAI,EAAE;QACJ,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAA;KACxB,CAAA;CAAC,EACF,KAAK,QAAQ,GAAG;IAAE,MAAM,EAAE;QAAE,YAAY,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,EACrD,MAAM,YAAY,SAQnB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,UAU3D;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAMhE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAQ1D,UAAU,QAAQ;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE;QACL,IAAI,EAAE;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC;KAClC,CAAC;CACH;AAED,UAAU,cAAe,SAAQ,OAAO;IACtC,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,UAAU,eAAgB,SAAQ,QAAQ;IACxC,MAAM,EAAE;QACN,YAAY,EAAE,OAAO,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,QAAQ,CAAC;CAChB;AAED,UAAU,aAAa;IACrB,MAAM,EAAE;QACN,YAAY,EAAE,OAAO,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAGD,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,YAAY,GACjB,IAAI,CAAC;AAER,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,YAAY,EACjB,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,YAAY,GACjB,IAAI,CAAC;AAaR,eAAO,MAAM,yBAAyB,GAAI,cAAc,OAAO,OAAO,SAerE,CAAC;AAGF,eAAO,MAAM,kCAAkC,GAC7C,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,YAAY,SAUnB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,UAU3D;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,iBAMhE;AAED,eAAO,MAAM,mBAAmB,GAC9B,cAAc,OAAO,OAAO,EAC5B,WAAW,MAAM,SASlB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,MAAM,EACd,WAAW,MAAM,KAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAuBxB,CAAC"}
@@ -1,3 +1,5 @@
1
1
  @use "./header";
2
2
  @use "./phase-banner";
3
3
  @use "./language-select";
4
+ @use "./spinner";
5
+ @use "./footer";
@@ -0,0 +1,137 @@
1
+ {% extends "govuk/template.njk" %}
2
+
3
+ {% from "frontend-ui/build/components/cookie-banner/macro.njk" import frontendUiCookieBanner %}
4
+ {% from "frontend-ui/build/components/phase-banner/macro.njk" import frontendUiPhaseBanner %}
5
+ {% from "frontend-ui/build/components/header/macro.njk" import frontendUiHeader %}
6
+ {% from "frontend-ui/build/components/footer/macro.njk" import frontendUiFooter %}
7
+ {% from "govuk/components/back-link/macro.njk" import govukBackLink %}
8
+ {% from "frontend-ui/build/components/language-select/macro.njk" import frontendUiLanguageSelect %}
9
+
10
+ {% if May_2025_Rebrand %}
11
+ {%set htmlClasses = 'govuk-template--rebranded'%}
12
+ {% endif %}
13
+
14
+ {% if strategicAppChannel == true %}
15
+ {% set htmlClasses = 'govuk-template__mobile' %}
16
+ {% endif %}
17
+
18
+ {% block head %}
19
+ <!--[if !IE 8]><!-->
20
+ <link href="/public/style.css" rel="stylesheet">
21
+ <link rel="stylesheet" href="/public/frontendUi.css"/>
22
+ <!--<![endif]-->
23
+
24
+ <!--[if IE 8]>
25
+ <link href="/govuk-frontend/all-ie8.css" rel="stylesheet">
26
+ <![endif]-->
27
+
28
+ <!--[if lt IE 9]>
29
+ <script src="/html5-shiv/html5shiv.js"></script>
30
+ <![endif]-->
31
+
32
+ {% block headMetaData %}{% endblock %}
33
+ {% endblock %}
34
+
35
+ {% block pageTitle %}
36
+ {% if error or errors %}
37
+ {{ 'general.errorTitlePrefix' | translate }} -
38
+ {% endif %}
39
+ {% if pageTitleName %}
40
+ {{ pageTitleName }} -
41
+ {% endif %}
42
+ {{ 'general.serviceNameTitle' | translate }}
43
+ {% endblock %}
44
+
45
+ {% block bodyStart %}
46
+ {% block cookieBanner %}
47
+ {{ frontendUiCookieBanner({
48
+ translations: translations.translation.cookieBanner
49
+ }) }}
50
+ {% endblock %}
51
+ {% endblock %}
52
+
53
+ {% set phaseBannerClasses = "test-banner" if showTestBanner %}
54
+
55
+ {% block header %}
56
+ {{ frontendUiHeader({
57
+ translations: translations.translation.header,
58
+ homepageUrl: "https://www.gov.uk",
59
+ classes: phaseBannerClasses
60
+ }) }}
61
+ {% endblock %}
62
+
63
+ {% if showTestBanner %}
64
+ {% set phaseBannerText = 'general.phaseBanner.testEnvironmentMessage' | translate %}
65
+ {% else %}
66
+ {% set phaseBannerText = 'phaseBanner.text' | translate %}
67
+ {% endif %}
68
+
69
+ {% if showTestBanner %}
70
+ {% set phaseBannerTag = 'general.phaseBanner.tag.test' | translate %}
71
+ {% else %}
72
+ {% set phaseBannerTag = 'general.phaseBanner.tag.beta' | translate %}
73
+ {% endif %}
74
+
75
+ {% block main %}
76
+ <div class="govuk-width-container {{ containerClasses }}">
77
+ {{ frontendUiPhaseBanner({
78
+ translations: translations.translation.phaseBanner,
79
+ url: currentUrl,
80
+ contactUrl: contactUsLinkUrl,
81
+ tag: phaseBannerTag,
82
+ phaseBannerText: phaseBannerText
83
+ }) }}
84
+ {% block beforeContent %}{% endblock %}
85
+ {% if languageToggleEnabled %}
86
+ {{ frontendUiLanguageSelect({
87
+ translations: translations.translation.languageSelect,
88
+ url: currentUrl,
89
+ activeLanguage: htmlLang
90
+ }) }}
91
+ {% endif %}
92
+ {% if showBack %}
93
+ {{ govukBackLink({
94
+ text: "general.back" | translate,
95
+ href: hrefBack
96
+ }) }}
97
+ {% endif %}
98
+ <main class="govuk-main-wrapper {{ mainClasses }}" id="main-content" role="main" {% if mainLang %} lang="{{ mainLang }}"{% endif %}>
99
+ <div class="govuk-grid-row">
100
+ <div class="govuk-grid-column-two-thirds {{ rowClasses }}">
101
+ {% block content %}{% endblock %}
102
+ </div>
103
+ </div>
104
+ </main>
105
+ </div>
106
+ {% endblock %}
107
+
108
+ {% block footer %}
109
+ {% if strategicAppChannel === true %}
110
+ {% else %}
111
+ {{ frontendUiFooter({
112
+ translations: translations.translation.footer
113
+ }) }}
114
+ {% endif %}
115
+ {% endblock %}
116
+
117
+ {% block bodyEnd %}
118
+ {% block scripts %}{% endblock %}
119
+ <script type="text/javascript" src="/public/scripts/dataLayerEvents.js"></script>
120
+ <script type="text/javascript" src="/public/scripts/application.js"></script>
121
+ <script type="text/javascript" src="/public/scripts/all.js"></script>
122
+ <script type="text/javascript" src="/public/scripts/analytics.js"></script>
123
+ <script type="text/javascript" {% if scriptNonce %} nonce="{{ scriptNonce }}"{% endif %}>
124
+ if (window.DI) {
125
+ if (window.DI.appInit) {
126
+ window.DI.appInit({
127
+ ga4ContainerId: "{{ga4ContainerId}}",
128
+ uaContainerId: "not used" // We don't use UA anymore, but it's a required param
129
+ }, {
130
+ isDataSensitive: false,
131
+ enableGa4Tracking: {{isGa4Enabled}},
132
+ cookieDomain: "{{analyticsCookieDomain}}"
133
+ });
134
+ }
135
+ }
136
+ </script>
137
+ {% endblock %}