govuk_tech_docs 4.2.0 → 4.3.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/govuk_tech_docs.gemspec +1 -1
- data/lib/assets/stylesheets/_govuk_tech_docs.scss +3 -0
- data/lib/govuk_tech_docs/version.rb +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.js +508 -209
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs +505 -208
- data/node_modules/govuk-frontend/dist/govuk/all.mjs +3 -1
- data/node_modules/govuk-frontend/dist/govuk/all.scss +6 -0
- data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs +169 -0
- data/node_modules/govuk-frontend/dist/govuk/common/govuk-frontend-version.mjs +1 -1
- data/node_modules/govuk-frontend/dist/govuk/common/index.mjs +4 -87
- data/node_modules/govuk-frontend/dist/govuk/{govuk-frontend-component.mjs → component.mjs} +5 -5
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs +5 -8
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs +5 -8
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/_index.scss +8 -0
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js +187 -145
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs +186 -144
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs +18 -17
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs +6 -8
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs +5 -8
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/_index.scss +167 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js +754 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs +746 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs +267 -0
- data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss +14 -10
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs +6 -8
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js +161 -117
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs +160 -116
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs +5 -9
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js +10 -30
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs +3 -3
- data/node_modules/govuk-frontend/dist/govuk/components/summary-list/_index.scss +12 -21
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss +1 -1
- data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_colour.scss +2 -2
- data/node_modules/govuk-frontend/dist/govuk/init.mjs +28 -24
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss +18 -5
- data/node_modules/govuk-frontend/dist/govuk/settings/_typography-responsive.scss +5 -10
- data/node_modules/govuk-frontend/dist/govuk-prototype-kit/init.scss +1 -1
- data/package-lock.json +8 -7
- data/package.json +1 -1
- metadata +12 -10
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-dataset.mjs +0 -18
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-string.mjs +0 -31
@@ -1,81 +1,11 @@
|
|
1
1
|
(function (global, factory) {
|
2
2
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
3
3
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = {}));
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = global.GOVUKFrontend || {}));
|
5
5
|
})(this, (function (exports) { 'use strict';
|
6
6
|
|
7
|
-
const version = '5.
|
7
|
+
const version = '5.9.0';
|
8
8
|
|
9
|
-
function normaliseString(value, property) {
|
10
|
-
const trimmedValue = value ? value.trim() : '';
|
11
|
-
let output;
|
12
|
-
let outputType = property == null ? void 0 : property.type;
|
13
|
-
if (!outputType) {
|
14
|
-
if (['true', 'false'].includes(trimmedValue)) {
|
15
|
-
outputType = 'boolean';
|
16
|
-
}
|
17
|
-
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
18
|
-
outputType = 'number';
|
19
|
-
}
|
20
|
-
}
|
21
|
-
switch (outputType) {
|
22
|
-
case 'boolean':
|
23
|
-
output = trimmedValue === 'true';
|
24
|
-
break;
|
25
|
-
case 'number':
|
26
|
-
output = Number(trimmedValue);
|
27
|
-
break;
|
28
|
-
default:
|
29
|
-
output = value;
|
30
|
-
}
|
31
|
-
return output;
|
32
|
-
}
|
33
|
-
|
34
|
-
/**
|
35
|
-
* @typedef {import('./index.mjs').SchemaProperty} SchemaProperty
|
36
|
-
*/
|
37
|
-
|
38
|
-
function mergeConfigs(...configObjects) {
|
39
|
-
const formattedConfigObject = {};
|
40
|
-
for (const configObject of configObjects) {
|
41
|
-
for (const key of Object.keys(configObject)) {
|
42
|
-
const option = formattedConfigObject[key];
|
43
|
-
const override = configObject[key];
|
44
|
-
if (isObject(option) && isObject(override)) {
|
45
|
-
formattedConfigObject[key] = mergeConfigs(option, override);
|
46
|
-
} else {
|
47
|
-
formattedConfigObject[key] = override;
|
48
|
-
}
|
49
|
-
}
|
50
|
-
}
|
51
|
-
return formattedConfigObject;
|
52
|
-
}
|
53
|
-
function extractConfigByNamespace(Component, dataset, namespace) {
|
54
|
-
const property = Component.schema.properties[namespace];
|
55
|
-
if ((property == null ? void 0 : property.type) !== 'object') {
|
56
|
-
return;
|
57
|
-
}
|
58
|
-
const newObject = {
|
59
|
-
[namespace]: ({})
|
60
|
-
};
|
61
|
-
for (const [key, value] of Object.entries(dataset)) {
|
62
|
-
let current = newObject;
|
63
|
-
const keyParts = key.split('.');
|
64
|
-
for (const [index, name] of keyParts.entries()) {
|
65
|
-
if (typeof current === 'object') {
|
66
|
-
if (index < keyParts.length - 1) {
|
67
|
-
if (!isObject(current[name])) {
|
68
|
-
current[name] = {};
|
69
|
-
}
|
70
|
-
current = current[name];
|
71
|
-
} else if (key !== namespace) {
|
72
|
-
current[name] = normaliseString(value);
|
73
|
-
}
|
74
|
-
}
|
75
|
-
}
|
76
|
-
}
|
77
|
-
return newObject[namespace];
|
78
|
-
}
|
79
9
|
function getFragmentFromUrl(url) {
|
80
10
|
if (!url.includes('#')) {
|
81
11
|
return undefined;
|
@@ -133,26 +63,6 @@
|
|
133
63
|
}
|
134
64
|
return $scope.classList.contains('govuk-frontend-supported');
|
135
65
|
}
|
136
|
-
function validateConfig(schema, config) {
|
137
|
-
const validationErrors = [];
|
138
|
-
for (const [name, conditions] of Object.entries(schema)) {
|
139
|
-
const errors = [];
|
140
|
-
if (Array.isArray(conditions)) {
|
141
|
-
for (const {
|
142
|
-
required,
|
143
|
-
errorMessage
|
144
|
-
} of conditions) {
|
145
|
-
if (!required.every(key => !!config[key])) {
|
146
|
-
errors.push(errorMessage);
|
147
|
-
}
|
148
|
-
}
|
149
|
-
if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
|
150
|
-
validationErrors.push(...errors);
|
151
|
-
}
|
152
|
-
}
|
153
|
-
}
|
154
|
-
return validationErrors;
|
155
|
-
}
|
156
66
|
function isArray(option) {
|
157
67
|
return Array.isArray(option);
|
158
68
|
}
|
@@ -162,46 +72,13 @@
|
|
162
72
|
function formatErrorMessage(Component, message) {
|
163
73
|
return `${Component.moduleName}: ${message}`;
|
164
74
|
}
|
165
|
-
|
166
|
-
/**
|
167
|
-
* Schema for component config
|
168
|
-
*
|
169
|
-
* @typedef {object} Schema
|
170
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
171
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
172
|
-
*/
|
173
|
-
|
174
|
-
/**
|
175
|
-
* Schema property for component config
|
176
|
-
*
|
177
|
-
* @typedef {object} SchemaProperty
|
178
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
179
|
-
*/
|
180
|
-
|
181
|
-
/**
|
182
|
-
* Schema condition for component config
|
183
|
-
*
|
184
|
-
* @typedef {object} SchemaCondition
|
185
|
-
* @property {string[]} required - List of required config fields
|
186
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
187
|
-
*/
|
188
75
|
/**
|
189
76
|
* @typedef ComponentWithModuleName
|
190
77
|
* @property {string} moduleName - Name of the component
|
191
78
|
*/
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
196
|
-
if (field in dataset) {
|
197
|
-
out[field] = normaliseString(dataset[field], property);
|
198
|
-
}
|
199
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
200
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
201
|
-
}
|
202
|
-
}
|
203
|
-
return out;
|
204
|
-
}
|
79
|
+
/**
|
80
|
+
* @import { ObjectNested } from './configuration.mjs'
|
81
|
+
*/
|
205
82
|
|
206
83
|
class GOVUKFrontendError extends Error {
|
207
84
|
constructor(...args) {
|
@@ -253,10 +130,10 @@
|
|
253
130
|
}
|
254
131
|
}
|
255
132
|
/**
|
256
|
-
* @
|
133
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
257
134
|
*/
|
258
135
|
|
259
|
-
class
|
136
|
+
class Component {
|
260
137
|
/**
|
261
138
|
* Returns the root element of the component
|
262
139
|
*
|
@@ -307,9 +184,172 @@
|
|
307
184
|
*/
|
308
185
|
|
309
186
|
/**
|
310
|
-
* @typedef {typeof
|
187
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
188
|
+
*/
|
189
|
+
Component.elementType = HTMLElement;
|
190
|
+
|
191
|
+
const configOverride = Symbol.for('configOverride');
|
192
|
+
class ConfigurableComponent extends Component {
|
193
|
+
[configOverride](param) {
|
194
|
+
return {};
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Returns the root element of the component
|
199
|
+
*
|
200
|
+
* @protected
|
201
|
+
* @returns {ConfigurationType} - the root element of component
|
202
|
+
*/
|
203
|
+
get config() {
|
204
|
+
return this._config;
|
205
|
+
}
|
206
|
+
constructor($root, config) {
|
207
|
+
super($root);
|
208
|
+
this._config = void 0;
|
209
|
+
const childConstructor = this.constructor;
|
210
|
+
if (!isObject(childConstructor.defaults)) {
|
211
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
212
|
+
}
|
213
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
214
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
function normaliseString(value, property) {
|
218
|
+
const trimmedValue = value ? value.trim() : '';
|
219
|
+
let output;
|
220
|
+
let outputType = property == null ? void 0 : property.type;
|
221
|
+
if (!outputType) {
|
222
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
223
|
+
outputType = 'boolean';
|
224
|
+
}
|
225
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
226
|
+
outputType = 'number';
|
227
|
+
}
|
228
|
+
}
|
229
|
+
switch (outputType) {
|
230
|
+
case 'boolean':
|
231
|
+
output = trimmedValue === 'true';
|
232
|
+
break;
|
233
|
+
case 'number':
|
234
|
+
output = Number(trimmedValue);
|
235
|
+
break;
|
236
|
+
default:
|
237
|
+
output = value;
|
238
|
+
}
|
239
|
+
return output;
|
240
|
+
}
|
241
|
+
function normaliseDataset(Component, dataset) {
|
242
|
+
if (!isObject(Component.schema)) {
|
243
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
244
|
+
}
|
245
|
+
const out = {};
|
246
|
+
const entries = Object.entries(Component.schema.properties);
|
247
|
+
for (const entry of entries) {
|
248
|
+
const [namespace, property] = entry;
|
249
|
+
const field = namespace.toString();
|
250
|
+
if (field in dataset) {
|
251
|
+
out[field] = normaliseString(dataset[field], property);
|
252
|
+
}
|
253
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
254
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
255
|
+
}
|
256
|
+
}
|
257
|
+
return out;
|
258
|
+
}
|
259
|
+
function mergeConfigs(...configObjects) {
|
260
|
+
const formattedConfigObject = {};
|
261
|
+
for (const configObject of configObjects) {
|
262
|
+
for (const key of Object.keys(configObject)) {
|
263
|
+
const option = formattedConfigObject[key];
|
264
|
+
const override = configObject[key];
|
265
|
+
if (isObject(option) && isObject(override)) {
|
266
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
267
|
+
} else {
|
268
|
+
formattedConfigObject[key] = override;
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
return formattedConfigObject;
|
273
|
+
}
|
274
|
+
function validateConfig(schema, config) {
|
275
|
+
const validationErrors = [];
|
276
|
+
for (const [name, conditions] of Object.entries(schema)) {
|
277
|
+
const errors = [];
|
278
|
+
if (Array.isArray(conditions)) {
|
279
|
+
for (const {
|
280
|
+
required,
|
281
|
+
errorMessage
|
282
|
+
} of conditions) {
|
283
|
+
if (!required.every(key => !!config[key])) {
|
284
|
+
errors.push(errorMessage);
|
285
|
+
}
|
286
|
+
}
|
287
|
+
if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
|
288
|
+
validationErrors.push(...errors);
|
289
|
+
}
|
290
|
+
}
|
291
|
+
}
|
292
|
+
return validationErrors;
|
293
|
+
}
|
294
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
295
|
+
const property = schema.properties[namespace];
|
296
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
297
|
+
return;
|
298
|
+
}
|
299
|
+
const newObject = {
|
300
|
+
[namespace]: {}
|
301
|
+
};
|
302
|
+
for (const [key, value] of Object.entries(dataset)) {
|
303
|
+
let current = newObject;
|
304
|
+
const keyParts = key.split('.');
|
305
|
+
for (const [index, name] of keyParts.entries()) {
|
306
|
+
if (isObject(current)) {
|
307
|
+
if (index < keyParts.length - 1) {
|
308
|
+
if (!isObject(current[name])) {
|
309
|
+
current[name] = {};
|
310
|
+
}
|
311
|
+
current = current[name];
|
312
|
+
} else if (key !== namespace) {
|
313
|
+
current[name] = normaliseString(value);
|
314
|
+
}
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
318
|
+
return newObject[namespace];
|
319
|
+
}
|
320
|
+
/**
|
321
|
+
* Schema for component config
|
322
|
+
*
|
323
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
324
|
+
* @typedef {object} Schema
|
325
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
326
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
327
|
+
*/
|
328
|
+
/**
|
329
|
+
* Schema property for component config
|
330
|
+
*
|
331
|
+
* @typedef {object} SchemaProperty
|
332
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
333
|
+
*/
|
334
|
+
/**
|
335
|
+
* Schema condition for component config
|
336
|
+
*
|
337
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
338
|
+
* @typedef {object} SchemaCondition
|
339
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
340
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
341
|
+
*/
|
342
|
+
/**
|
343
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
344
|
+
* @typedef ChildClass
|
345
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
346
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
347
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
348
|
+
*/
|
349
|
+
/**
|
350
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
351
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
311
352
|
*/
|
312
|
-
GOVUKFrontendComponent.elementType = HTMLElement;
|
313
353
|
|
314
354
|
class I18n {
|
315
355
|
constructor(translations = {}, config = {}) {
|
@@ -517,15 +557,15 @@
|
|
517
557
|
* attribute, which also provides accessibility.
|
518
558
|
*
|
519
559
|
* @preserve
|
560
|
+
* @augments ConfigurableComponent<AccordionConfig>
|
520
561
|
*/
|
521
|
-
class Accordion extends
|
562
|
+
class Accordion extends ConfigurableComponent {
|
522
563
|
/**
|
523
564
|
* @param {Element | null} $root - HTML element to use for accordion
|
524
565
|
* @param {AccordionConfig} [config] - Accordion config
|
525
566
|
*/
|
526
567
|
constructor($root, config = {}) {
|
527
|
-
super($root);
|
528
|
-
this.config = void 0;
|
568
|
+
super($root, config);
|
529
569
|
this.i18n = void 0;
|
530
570
|
this.controlsClass = 'govuk-accordion__controls';
|
531
571
|
this.showAllClass = 'govuk-accordion__show-all';
|
@@ -550,7 +590,6 @@
|
|
550
590
|
this.$showAllButton = null;
|
551
591
|
this.$showAllIcon = null;
|
552
592
|
this.$showAllText = null;
|
553
|
-
this.config = mergeConfigs(Accordion.defaults, config, normaliseDataset(Accordion, this.$root.dataset));
|
554
593
|
this.i18n = new I18n(this.config.i18n);
|
555
594
|
const $sections = this.$root.querySelectorAll(`.${this.sectionClass}`);
|
556
595
|
if (!$sections.length) {
|
@@ -820,7 +859,7 @@
|
|
820
859
|
*/
|
821
860
|
|
822
861
|
/**
|
823
|
-
* @
|
862
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
824
863
|
*/
|
825
864
|
Accordion.moduleName = 'govuk-accordion';
|
826
865
|
Accordion.defaults = Object.freeze({
|
@@ -851,17 +890,16 @@
|
|
851
890
|
* JavaScript enhancements for the Button component
|
852
891
|
*
|
853
892
|
* @preserve
|
893
|
+
* @augments ConfigurableComponent<ButtonConfig>
|
854
894
|
*/
|
855
|
-
class Button extends
|
895
|
+
class Button extends ConfigurableComponent {
|
856
896
|
/**
|
857
897
|
* @param {Element | null} $root - HTML element to use for button
|
858
898
|
* @param {ButtonConfig} [config] - Button config
|
859
899
|
*/
|
860
900
|
constructor($root, config = {}) {
|
861
|
-
super($root);
|
862
|
-
this.config = void 0;
|
901
|
+
super($root, config);
|
863
902
|
this.debounceFormSubmitTimer = null;
|
864
|
-
this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, this.$root.dataset));
|
865
903
|
this.$root.addEventListener('keydown', event => this.handleKeyDown(event));
|
866
904
|
this.$root.addEventListener('click', event => this.debounce(event));
|
867
905
|
}
|
@@ -898,7 +936,7 @@
|
|
898
936
|
*/
|
899
937
|
|
900
938
|
/**
|
901
|
-
* @
|
939
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
902
940
|
*/
|
903
941
|
Button.moduleName = 'govuk-button';
|
904
942
|
Button.defaults = Object.freeze({
|
@@ -928,22 +966,33 @@
|
|
928
966
|
* of the available characters/words has been entered.
|
929
967
|
*
|
930
968
|
* @preserve
|
969
|
+
* @augments ConfigurableComponent<CharacterCountConfig>
|
931
970
|
*/
|
932
|
-
class CharacterCount extends
|
971
|
+
class CharacterCount extends ConfigurableComponent {
|
972
|
+
[configOverride](datasetConfig) {
|
973
|
+
let configOverrides = {};
|
974
|
+
if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
|
975
|
+
configOverrides = {
|
976
|
+
maxlength: undefined,
|
977
|
+
maxwords: undefined
|
978
|
+
};
|
979
|
+
}
|
980
|
+
return configOverrides;
|
981
|
+
}
|
982
|
+
|
933
983
|
/**
|
934
984
|
* @param {Element | null} $root - HTML element to use for character count
|
935
985
|
* @param {CharacterCountConfig} [config] - Character count config
|
936
986
|
*/
|
937
987
|
constructor($root, config = {}) {
|
938
988
|
var _ref, _this$config$maxwords;
|
939
|
-
super($root);
|
989
|
+
super($root, config);
|
940
990
|
this.$textarea = void 0;
|
941
991
|
this.$visibleCountMessage = void 0;
|
942
992
|
this.$screenReaderCountMessage = void 0;
|
943
993
|
this.lastInputTimestamp = null;
|
944
994
|
this.lastInputValue = '';
|
945
995
|
this.valueChecker = null;
|
946
|
-
this.config = void 0;
|
947
996
|
this.i18n = void 0;
|
948
997
|
this.maxLength = void 0;
|
949
998
|
const $textarea = this.$root.querySelector('.govuk-js-character-count');
|
@@ -955,15 +1004,6 @@
|
|
955
1004
|
identifier: 'Form field (`.govuk-js-character-count`)'
|
956
1005
|
});
|
957
1006
|
}
|
958
|
-
const datasetConfig = normaliseDataset(CharacterCount, this.$root.dataset);
|
959
|
-
let configOverrides = {};
|
960
|
-
if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
|
961
|
-
configOverrides = {
|
962
|
-
maxlength: undefined,
|
963
|
-
maxwords: undefined
|
964
|
-
};
|
965
|
-
}
|
966
|
-
this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
|
967
1007
|
const errors = validateConfig(CharacterCount.schema, this.config);
|
968
1008
|
if (errors[0]) {
|
969
1009
|
throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
|
@@ -1149,8 +1189,8 @@
|
|
1149
1189
|
*/
|
1150
1190
|
|
1151
1191
|
/**
|
1152
|
-
* @
|
1153
|
-
* @
|
1192
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1193
|
+
* @import { TranslationPluralForms } from '../../i18n.mjs'
|
1154
1194
|
*/
|
1155
1195
|
CharacterCount.moduleName = 'govuk-character-count';
|
1156
1196
|
CharacterCount.defaults = Object.freeze({
|
@@ -1208,7 +1248,7 @@
|
|
1208
1248
|
*
|
1209
1249
|
* @preserve
|
1210
1250
|
*/
|
1211
|
-
class Checkboxes extends
|
1251
|
+
class Checkboxes extends Component {
|
1212
1252
|
/**
|
1213
1253
|
* Checkboxes can be associated with a 'conditionally revealed' content block
|
1214
1254
|
* – for example, a checkbox for 'Phone' could reveal an additional form field
|
@@ -1316,16 +1356,15 @@
|
|
1316
1356
|
* configuration.
|
1317
1357
|
*
|
1318
1358
|
* @preserve
|
1359
|
+
* @augments ConfigurableComponent<ErrorSummaryConfig>
|
1319
1360
|
*/
|
1320
|
-
class ErrorSummary extends
|
1361
|
+
class ErrorSummary extends ConfigurableComponent {
|
1321
1362
|
/**
|
1322
1363
|
* @param {Element | null} $root - HTML element to use for error summary
|
1323
1364
|
* @param {ErrorSummaryConfig} [config] - Error summary config
|
1324
1365
|
*/
|
1325
1366
|
constructor($root, config = {}) {
|
1326
|
-
super($root);
|
1327
|
-
this.config = void 0;
|
1328
|
-
this.config = mergeConfigs(ErrorSummary.defaults, config, normaliseDataset(ErrorSummary, this.$root.dataset));
|
1367
|
+
super($root, config);
|
1329
1368
|
if (!this.config.disableAutoFocus) {
|
1330
1369
|
setFocus(this.$root);
|
1331
1370
|
}
|
@@ -1392,7 +1431,7 @@
|
|
1392
1431
|
*/
|
1393
1432
|
|
1394
1433
|
/**
|
1395
|
-
* @
|
1434
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1396
1435
|
*/
|
1397
1436
|
ErrorSummary.moduleName = 'govuk-error-summary';
|
1398
1437
|
ErrorSummary.defaults = Object.freeze({
|
@@ -1410,15 +1449,15 @@
|
|
1410
1449
|
* Exit this page component
|
1411
1450
|
*
|
1412
1451
|
* @preserve
|
1452
|
+
* @augments ConfigurableComponent<ExitThisPageConfig>
|
1413
1453
|
*/
|
1414
|
-
class ExitThisPage extends
|
1454
|
+
class ExitThisPage extends ConfigurableComponent {
|
1415
1455
|
/**
|
1416
1456
|
* @param {Element | null} $root - HTML element that wraps the Exit This Page button
|
1417
1457
|
* @param {ExitThisPageConfig} [config] - Exit This Page config
|
1418
1458
|
*/
|
1419
1459
|
constructor($root, config = {}) {
|
1420
|
-
super($root);
|
1421
|
-
this.config = void 0;
|
1460
|
+
super($root, config);
|
1422
1461
|
this.i18n = void 0;
|
1423
1462
|
this.$button = void 0;
|
1424
1463
|
this.$skiplinkButton = null;
|
@@ -1439,7 +1478,6 @@
|
|
1439
1478
|
identifier: 'Button (`.govuk-exit-this-page__button`)'
|
1440
1479
|
});
|
1441
1480
|
}
|
1442
|
-
this.config = mergeConfigs(ExitThisPage.defaults, config, normaliseDataset(ExitThisPage, this.$root.dataset));
|
1443
1481
|
this.i18n = new I18n(this.config.i18n);
|
1444
1482
|
this.$button = $button;
|
1445
1483
|
const $skiplinkButton = document.querySelector('.govuk-js-exit-this-page-skiplink');
|
@@ -1605,7 +1643,7 @@
|
|
1605
1643
|
*/
|
1606
1644
|
|
1607
1645
|
/**
|
1608
|
-
* @
|
1646
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1609
1647
|
*/
|
1610
1648
|
ExitThisPage.moduleName = 'govuk-exit-this-page';
|
1611
1649
|
ExitThisPage.defaults = Object.freeze({
|
@@ -1624,12 +1662,271 @@
|
|
1624
1662
|
}
|
1625
1663
|
});
|
1626
1664
|
|
1665
|
+
/**
|
1666
|
+
* File upload component
|
1667
|
+
*
|
1668
|
+
* @preserve
|
1669
|
+
* @augments ConfigurableComponent<FileUploadConfig>
|
1670
|
+
*/
|
1671
|
+
class FileUpload extends ConfigurableComponent {
|
1672
|
+
/**
|
1673
|
+
* @param {Element | null} $root - File input element
|
1674
|
+
* @param {FileUploadConfig} [config] - File Upload config
|
1675
|
+
*/
|
1676
|
+
constructor($root, config = {}) {
|
1677
|
+
super($root, config);
|
1678
|
+
this.$input = void 0;
|
1679
|
+
this.$button = void 0;
|
1680
|
+
this.$status = void 0;
|
1681
|
+
this.i18n = void 0;
|
1682
|
+
this.id = void 0;
|
1683
|
+
const $input = this.$root.querySelector('input');
|
1684
|
+
if ($input === null) {
|
1685
|
+
throw new ElementError({
|
1686
|
+
component: FileUpload,
|
1687
|
+
identifier: 'File inputs (`<input type="file">`)'
|
1688
|
+
});
|
1689
|
+
}
|
1690
|
+
if ($input.type !== 'file') {
|
1691
|
+
throw new ElementError(formatErrorMessage(FileUpload, 'File input (`<input type="file">`) attribute (`type`) is not `file`'));
|
1692
|
+
}
|
1693
|
+
this.$input = $input;
|
1694
|
+
this.$input.setAttribute('hidden', 'true');
|
1695
|
+
if (!this.$input.id) {
|
1696
|
+
throw new ElementError({
|
1697
|
+
component: FileUpload,
|
1698
|
+
identifier: 'File input (`<input type="file">`) attribute (`id`)'
|
1699
|
+
});
|
1700
|
+
}
|
1701
|
+
this.id = this.$input.id;
|
1702
|
+
this.i18n = new I18n(this.config.i18n, {
|
1703
|
+
locale: closestAttributeValue(this.$root, 'lang')
|
1704
|
+
});
|
1705
|
+
const $label = this.findLabel();
|
1706
|
+
if (!$label.id) {
|
1707
|
+
$label.id = `${this.id}-label`;
|
1708
|
+
}
|
1709
|
+
this.$input.id = `${this.id}-input`;
|
1710
|
+
const $button = document.createElement('button');
|
1711
|
+
$button.classList.add('govuk-file-upload-button');
|
1712
|
+
$button.type = 'button';
|
1713
|
+
$button.id = this.id;
|
1714
|
+
$button.classList.add('govuk-file-upload-button--empty');
|
1715
|
+
const ariaDescribedBy = this.$input.getAttribute('aria-describedby');
|
1716
|
+
if (ariaDescribedBy) {
|
1717
|
+
$button.setAttribute('aria-describedby', ariaDescribedBy);
|
1718
|
+
}
|
1719
|
+
const $status = document.createElement('span');
|
1720
|
+
$status.className = 'govuk-body govuk-file-upload-button__status';
|
1721
|
+
$status.setAttribute('aria-live', 'polite');
|
1722
|
+
$status.innerText = this.i18n.t('noFileChosen');
|
1723
|
+
$button.appendChild($status);
|
1724
|
+
const commaSpan = document.createElement('span');
|
1725
|
+
commaSpan.className = 'govuk-visually-hidden';
|
1726
|
+
commaSpan.innerText = ', ';
|
1727
|
+
commaSpan.id = `${this.id}-comma`;
|
1728
|
+
$button.appendChild(commaSpan);
|
1729
|
+
const containerSpan = document.createElement('span');
|
1730
|
+
containerSpan.className = 'govuk-file-upload-button__pseudo-button-container';
|
1731
|
+
const buttonSpan = document.createElement('span');
|
1732
|
+
buttonSpan.className = 'govuk-button govuk-button--secondary govuk-file-upload-button__pseudo-button';
|
1733
|
+
buttonSpan.innerText = this.i18n.t('chooseFilesButton');
|
1734
|
+
containerSpan.appendChild(buttonSpan);
|
1735
|
+
containerSpan.insertAdjacentText('beforeend', ' ');
|
1736
|
+
const instructionSpan = document.createElement('span');
|
1737
|
+
instructionSpan.className = 'govuk-body govuk-file-upload-button__instruction';
|
1738
|
+
instructionSpan.innerText = this.i18n.t('dropInstruction');
|
1739
|
+
containerSpan.appendChild(instructionSpan);
|
1740
|
+
$button.appendChild(containerSpan);
|
1741
|
+
$button.setAttribute('aria-labelledby', `${$label.id} ${commaSpan.id} ${$button.id}`);
|
1742
|
+
$button.addEventListener('click', this.onClick.bind(this));
|
1743
|
+
$button.addEventListener('dragover', event => {
|
1744
|
+
event.preventDefault();
|
1745
|
+
});
|
1746
|
+
this.$root.insertAdjacentElement('afterbegin', $button);
|
1747
|
+
this.$input.setAttribute('tabindex', '-1');
|
1748
|
+
this.$input.setAttribute('aria-hidden', 'true');
|
1749
|
+
this.$button = $button;
|
1750
|
+
this.$status = $status;
|
1751
|
+
this.$input.addEventListener('change', this.onChange.bind(this));
|
1752
|
+
this.updateDisabledState();
|
1753
|
+
this.observeDisabledState();
|
1754
|
+
this.$announcements = document.createElement('span');
|
1755
|
+
this.$announcements.classList.add('govuk-file-upload-announcements');
|
1756
|
+
this.$announcements.classList.add('govuk-visually-hidden');
|
1757
|
+
this.$announcements.setAttribute('aria-live', 'assertive');
|
1758
|
+
this.$root.insertAdjacentElement('afterend', this.$announcements);
|
1759
|
+
this.$button.addEventListener('drop', this.onDrop.bind(this));
|
1760
|
+
document.addEventListener('dragenter', this.updateDropzoneVisibility.bind(this));
|
1761
|
+
document.addEventListener('dragenter', () => {
|
1762
|
+
this.enteredAnotherElement = true;
|
1763
|
+
});
|
1764
|
+
document.addEventListener('dragleave', () => {
|
1765
|
+
if (!this.enteredAnotherElement && !this.$button.disabled) {
|
1766
|
+
this.hideDraggingState();
|
1767
|
+
this.$announcements.innerText = this.i18n.t('leftDropZone');
|
1768
|
+
}
|
1769
|
+
this.enteredAnotherElement = false;
|
1770
|
+
});
|
1771
|
+
}
|
1772
|
+
|
1773
|
+
/**
|
1774
|
+
* Updates the visibility of the dropzone as users enters the various elements on the page
|
1775
|
+
*
|
1776
|
+
* @param {DragEvent} event - The `dragenter` event
|
1777
|
+
*/
|
1778
|
+
updateDropzoneVisibility(event) {
|
1779
|
+
if (this.$button.disabled) return;
|
1780
|
+
if (event.target instanceof Node) {
|
1781
|
+
if (this.$root.contains(event.target)) {
|
1782
|
+
if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
|
1783
|
+
if (!this.$button.classList.contains('govuk-file-upload-button--dragging')) {
|
1784
|
+
this.showDraggingState();
|
1785
|
+
this.$announcements.innerText = this.i18n.t('enteredDropZone');
|
1786
|
+
}
|
1787
|
+
}
|
1788
|
+
} else {
|
1789
|
+
if (this.$button.classList.contains('govuk-file-upload-button--dragging')) {
|
1790
|
+
this.hideDraggingState();
|
1791
|
+
this.$announcements.innerText = this.i18n.t('leftDropZone');
|
1792
|
+
}
|
1793
|
+
}
|
1794
|
+
}
|
1795
|
+
}
|
1796
|
+
showDraggingState() {
|
1797
|
+
this.$button.classList.add('govuk-file-upload-button--dragging');
|
1798
|
+
}
|
1799
|
+
hideDraggingState() {
|
1800
|
+
this.$button.classList.remove('govuk-file-upload-button--dragging');
|
1801
|
+
}
|
1802
|
+
|
1803
|
+
/**
|
1804
|
+
* Handles user dropping on the component
|
1805
|
+
*
|
1806
|
+
* @param {DragEvent} event - The `dragenter` event
|
1807
|
+
*/
|
1808
|
+
onDrop(event) {
|
1809
|
+
event.preventDefault();
|
1810
|
+
if (event.dataTransfer && isContainingFiles(event.dataTransfer)) {
|
1811
|
+
this.$input.files = event.dataTransfer.files;
|
1812
|
+
this.$input.dispatchEvent(new CustomEvent('change'));
|
1813
|
+
this.hideDraggingState();
|
1814
|
+
}
|
1815
|
+
}
|
1816
|
+
onChange() {
|
1817
|
+
const fileCount = this.$input.files.length;
|
1818
|
+
if (fileCount === 0) {
|
1819
|
+
this.$status.innerText = this.i18n.t('noFileChosen');
|
1820
|
+
this.$button.classList.add('govuk-file-upload-button--empty');
|
1821
|
+
} else {
|
1822
|
+
if (fileCount === 1) {
|
1823
|
+
this.$status.innerText = this.$input.files[0].name;
|
1824
|
+
} else {
|
1825
|
+
this.$status.innerText = this.i18n.t('multipleFilesChosen', {
|
1826
|
+
count: fileCount
|
1827
|
+
});
|
1828
|
+
}
|
1829
|
+
this.$button.classList.remove('govuk-file-upload-button--empty');
|
1830
|
+
}
|
1831
|
+
}
|
1832
|
+
findLabel() {
|
1833
|
+
const $label = document.querySelector(`label[for="${this.$input.id}"]`);
|
1834
|
+
if (!$label) {
|
1835
|
+
throw new ElementError({
|
1836
|
+
component: FileUpload,
|
1837
|
+
identifier: `Field label (\`<label for=${this.$input.id}>\`)`
|
1838
|
+
});
|
1839
|
+
}
|
1840
|
+
return $label;
|
1841
|
+
}
|
1842
|
+
onClick() {
|
1843
|
+
this.$input.click();
|
1844
|
+
}
|
1845
|
+
observeDisabledState() {
|
1846
|
+
const observer = new MutationObserver(mutationList => {
|
1847
|
+
for (const mutation of mutationList) {
|
1848
|
+
if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') {
|
1849
|
+
this.updateDisabledState();
|
1850
|
+
}
|
1851
|
+
}
|
1852
|
+
});
|
1853
|
+
observer.observe(this.$input, {
|
1854
|
+
attributes: true
|
1855
|
+
});
|
1856
|
+
}
|
1857
|
+
updateDisabledState() {
|
1858
|
+
this.$button.disabled = this.$input.disabled;
|
1859
|
+
this.$root.classList.toggle('govuk-drop-zone--disabled', this.$button.disabled);
|
1860
|
+
}
|
1861
|
+
}
|
1862
|
+
FileUpload.moduleName = 'govuk-file-upload';
|
1863
|
+
FileUpload.defaults = Object.freeze({
|
1864
|
+
i18n: {
|
1865
|
+
chooseFilesButton: 'Choose file',
|
1866
|
+
dropInstruction: 'or drop file',
|
1867
|
+
noFileChosen: 'No file chosen',
|
1868
|
+
multipleFilesChosen: {
|
1869
|
+
one: '%{count} file chosen',
|
1870
|
+
other: '%{count} files chosen'
|
1871
|
+
},
|
1872
|
+
enteredDropZone: 'Entered drop zone',
|
1873
|
+
leftDropZone: 'Left drop zone'
|
1874
|
+
}
|
1875
|
+
});
|
1876
|
+
FileUpload.schema = Object.freeze({
|
1877
|
+
properties: {
|
1878
|
+
i18n: {
|
1879
|
+
type: 'object'
|
1880
|
+
}
|
1881
|
+
}
|
1882
|
+
});
|
1883
|
+
function isContainingFiles(dataTransfer) {
|
1884
|
+
const hasNoTypesInfo = dataTransfer.types.length === 0;
|
1885
|
+
const isDraggingFiles = dataTransfer.types.some(type => type === 'Files');
|
1886
|
+
return hasNoTypesInfo || isDraggingFiles;
|
1887
|
+
}
|
1888
|
+
|
1889
|
+
/**
|
1890
|
+
* @typedef {HTMLInputElement & {files: FileList}} HTMLFileInputElement
|
1891
|
+
*/
|
1892
|
+
|
1893
|
+
/**
|
1894
|
+
* File upload config
|
1895
|
+
*
|
1896
|
+
* @see {@link FileUpload.defaults}
|
1897
|
+
* @typedef {object} FileUploadConfig
|
1898
|
+
* @property {FileUploadTranslations} [i18n=FileUpload.defaults.i18n] - File upload translations
|
1899
|
+
*/
|
1900
|
+
|
1901
|
+
/**
|
1902
|
+
* File upload translations
|
1903
|
+
*
|
1904
|
+
* @see {@link FileUpload.defaults.i18n}
|
1905
|
+
* @typedef {object} FileUploadTranslations
|
1906
|
+
*
|
1907
|
+
* Messages used by the component
|
1908
|
+
* @property {string} [chooseFile] - The text of the button that opens the file picker
|
1909
|
+
* @property {string} [dropInstruction] - The text informing users they can drop files
|
1910
|
+
* @property {TranslationPluralForms} [multipleFilesChosen] - The text displayed when multiple files
|
1911
|
+
* have been chosen by the user
|
1912
|
+
* @property {string} [noFileChosen] - The text to displayed when no file has been chosen by the user
|
1913
|
+
* @property {string} [enteredDropZone] - The text announced by assistive technology
|
1914
|
+
* when user drags files and enters the drop zone
|
1915
|
+
* @property {string} [leftDropZone] - The text announced by assistive technology
|
1916
|
+
* when user drags files and leaves the drop zone without dropping
|
1917
|
+
*/
|
1918
|
+
|
1919
|
+
/**
|
1920
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1921
|
+
* @import { TranslationPluralForms } from '../../i18n.mjs'
|
1922
|
+
*/
|
1923
|
+
|
1627
1924
|
/**
|
1628
1925
|
* Header component
|
1629
1926
|
*
|
1630
1927
|
* @preserve
|
1631
1928
|
*/
|
1632
|
-
class Header extends
|
1929
|
+
class Header extends Component {
|
1633
1930
|
/**
|
1634
1931
|
* Apply a matchMedia for desktop which will trigger a state sync if the
|
1635
1932
|
* browser viewport moves between states.
|
@@ -1710,16 +2007,15 @@
|
|
1710
2007
|
* Notification Banner component
|
1711
2008
|
*
|
1712
2009
|
* @preserve
|
2010
|
+
* @augments ConfigurableComponent<NotificationBannerConfig>
|
1713
2011
|
*/
|
1714
|
-
class NotificationBanner extends
|
2012
|
+
class NotificationBanner extends ConfigurableComponent {
|
1715
2013
|
/**
|
1716
2014
|
* @param {Element | null} $root - HTML element to use for notification banner
|
1717
2015
|
* @param {NotificationBannerConfig} [config] - Notification banner config
|
1718
2016
|
*/
|
1719
2017
|
constructor($root, config = {}) {
|
1720
|
-
super($root);
|
1721
|
-
this.config = void 0;
|
1722
|
-
this.config = mergeConfigs(NotificationBanner.defaults, config, normaliseDataset(NotificationBanner, this.$root.dataset));
|
2018
|
+
super($root, config);
|
1723
2019
|
if (this.$root.getAttribute('role') === 'alert' && !this.config.disableAutoFocus) {
|
1724
2020
|
setFocus(this.$root);
|
1725
2021
|
}
|
@@ -1737,7 +2033,7 @@
|
|
1737
2033
|
*/
|
1738
2034
|
|
1739
2035
|
/**
|
1740
|
-
* @
|
2036
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1741
2037
|
*/
|
1742
2038
|
NotificationBanner.moduleName = 'govuk-notification-banner';
|
1743
2039
|
NotificationBanner.defaults = Object.freeze({
|
@@ -1755,15 +2051,15 @@
|
|
1755
2051
|
* Password input component
|
1756
2052
|
*
|
1757
2053
|
* @preserve
|
2054
|
+
* @augments ConfigurableComponent<PasswordInputConfig>
|
1758
2055
|
*/
|
1759
|
-
class PasswordInput extends
|
2056
|
+
class PasswordInput extends ConfigurableComponent {
|
1760
2057
|
/**
|
1761
2058
|
* @param {Element | null} $root - HTML element to use for password input
|
1762
2059
|
* @param {PasswordInputConfig} [config] - Password input config
|
1763
2060
|
*/
|
1764
2061
|
constructor($root, config = {}) {
|
1765
|
-
super($root);
|
1766
|
-
this.config = void 0;
|
2062
|
+
super($root, config);
|
1767
2063
|
this.i18n = void 0;
|
1768
2064
|
this.$input = void 0;
|
1769
2065
|
this.$showHideButton = void 0;
|
@@ -1794,7 +2090,6 @@
|
|
1794
2090
|
}
|
1795
2091
|
this.$input = $input;
|
1796
2092
|
this.$showHideButton = $showHideButton;
|
1797
|
-
this.config = mergeConfigs(PasswordInput.defaults, config, normaliseDataset(PasswordInput, this.$root.dataset));
|
1798
2093
|
this.i18n = new I18n(this.config.i18n, {
|
1799
2094
|
locale: closestAttributeValue(this.$root, 'lang')
|
1800
2095
|
});
|
@@ -1874,8 +2169,7 @@
|
|
1874
2169
|
*/
|
1875
2170
|
|
1876
2171
|
/**
|
1877
|
-
* @
|
1878
|
-
* @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
|
2172
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
1879
2173
|
*/
|
1880
2174
|
PasswordInput.moduleName = 'govuk-password-input';
|
1881
2175
|
PasswordInput.defaults = Object.freeze({
|
@@ -1901,7 +2195,7 @@
|
|
1901
2195
|
*
|
1902
2196
|
* @preserve
|
1903
2197
|
*/
|
1904
|
-
class Radios extends
|
2198
|
+
class Radios extends Component {
|
1905
2199
|
/**
|
1906
2200
|
* Radios can be associated with a 'conditionally revealed' content block –
|
1907
2201
|
* for example, a radio for 'Phone' could reveal an additional form field for
|
@@ -1984,7 +2278,7 @@
|
|
1984
2278
|
*
|
1985
2279
|
* @preserve
|
1986
2280
|
*/
|
1987
|
-
class ServiceNavigation extends
|
2281
|
+
class ServiceNavigation extends Component {
|
1988
2282
|
/**
|
1989
2283
|
* @param {Element | null} $root - HTML element to use for header
|
1990
2284
|
*/
|
@@ -2062,9 +2356,9 @@
|
|
2062
2356
|
* Skip link component
|
2063
2357
|
*
|
2064
2358
|
* @preserve
|
2065
|
-
* @augments
|
2359
|
+
* @augments Component<HTMLAnchorElement>
|
2066
2360
|
*/
|
2067
|
-
class SkipLink extends
|
2361
|
+
class SkipLink extends Component {
|
2068
2362
|
/**
|
2069
2363
|
* @param {Element | null} $root - HTML element to use for skip link
|
2070
2364
|
* @throws {ElementError} when $root is not set or the wrong type
|
@@ -2115,7 +2409,7 @@
|
|
2115
2409
|
*
|
2116
2410
|
* @preserve
|
2117
2411
|
*/
|
2118
|
-
class Tabs extends
|
2412
|
+
class Tabs extends Component {
|
2119
2413
|
/**
|
2120
2414
|
* @param {Element | null} $root - HTML element to use for tabs
|
2121
2415
|
*/
|
@@ -2408,7 +2702,7 @@
|
|
2408
2702
|
}
|
2409
2703
|
return;
|
2410
2704
|
}
|
2411
|
-
const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [ServiceNavigation], [SkipLink], [Tabs]];
|
2705
|
+
const components = [[Accordion, config.accordion], [Button, config.button], [CharacterCount, config.characterCount], [Checkboxes], [ErrorSummary, config.errorSummary], [ExitThisPage, config.exitThisPage], [FileUpload, config.fileUpload], [Header], [NotificationBanner, config.notificationBanner], [PasswordInput, config.passwordInput], [Radios], [ServiceNavigation], [SkipLink], [Tabs]];
|
2412
2706
|
const options = {
|
2413
2707
|
scope: (_config$scope = config.scope) != null ? _config$scope : document,
|
2414
2708
|
onError: config.onError
|
@@ -2427,11 +2721,11 @@
|
|
2427
2721
|
*
|
2428
2722
|
* Any component errors will be caught and logged to the console.
|
2429
2723
|
*
|
2430
|
-
* @template {CompatibleClass}
|
2431
|
-
* @param {
|
2432
|
-
* @param {
|
2433
|
-
* @param {OnErrorCallback<
|
2434
|
-
* @returns {Array<InstanceType<
|
2724
|
+
* @template {CompatibleClass} ComponentClass
|
2725
|
+
* @param {ComponentClass} Component - class of the component to create
|
2726
|
+
* @param {ComponentConfig<ComponentClass>} [config] - Config supplied to component
|
2727
|
+
* @param {OnErrorCallback<ComponentClass> | Element | Document | CreateAllOptions<ComponentClass> } [createAllOptions] - options for createAll including scope of the document to search within and callback function if error throw by component on init
|
2728
|
+
* @returns {Array<InstanceType<ComponentClass>>} - array of instantiated components
|
2435
2729
|
*/
|
2436
2730
|
function createAll(Component, config, createAllOptions) {
|
2437
2731
|
let $scope = document;
|
@@ -2478,7 +2772,7 @@
|
|
2478
2772
|
}).filter(Boolean);
|
2479
2773
|
}
|
2480
2774
|
/**
|
2481
|
-
* @typedef {{new (...args: any[]): any,
|
2775
|
+
* @typedef {{new (...args: any[]): any, moduleName: string}} CompatibleClass
|
2482
2776
|
*/
|
2483
2777
|
/**
|
2484
2778
|
* Config for all components via `initAll()`
|
@@ -2489,22 +2783,21 @@
|
|
2489
2783
|
* @property {CharacterCountConfig} [characterCount] - Character Count config
|
2490
2784
|
* @property {ErrorSummaryConfig} [errorSummary] - Error Summary config
|
2491
2785
|
* @property {ExitThisPageConfig} [exitThisPage] - Exit This Page config
|
2786
|
+
* @property {FileUploadConfig} [fileUpload] - File Upload config
|
2492
2787
|
* @property {NotificationBannerConfig} [notificationBanner] - Notification Banner config
|
2493
2788
|
* @property {PasswordInputConfig} [passwordInput] - Password input config
|
2494
2789
|
*/
|
2495
2790
|
/**
|
2496
2791
|
* Config for individual components
|
2497
2792
|
*
|
2498
|
-
* @
|
2499
|
-
* @
|
2500
|
-
* @
|
2501
|
-
* @
|
2502
|
-
* @
|
2503
|
-
* @
|
2504
|
-
* @
|
2505
|
-
* @
|
2506
|
-
* @typedef {import('./components/notification-banner/notification-banner.mjs').NotificationBannerConfig} NotificationBannerConfig
|
2507
|
-
* @typedef {import('./components/password-input/password-input.mjs').PasswordInputConfig} PasswordInputConfig
|
2793
|
+
* @import { AccordionConfig } from './components/accordion/accordion.mjs'
|
2794
|
+
* @import { ButtonConfig } from './components/button/button.mjs'
|
2795
|
+
* @import { CharacterCountConfig } from './components/character-count/character-count.mjs'
|
2796
|
+
* @import { ErrorSummaryConfig } from './components/error-summary/error-summary.mjs'
|
2797
|
+
* @import { ExitThisPageConfig } from './components/exit-this-page/exit-this-page.mjs'
|
2798
|
+
* @import { NotificationBannerConfig } from './components/notification-banner/notification-banner.mjs'
|
2799
|
+
* @import { PasswordInputConfig } from './components/password-input/password-input.mjs'
|
2800
|
+
* @import { FileUploadConfig } from './components/file-upload/file-upload.mjs'
|
2508
2801
|
*/
|
2509
2802
|
/**
|
2510
2803
|
* Component config keys, e.g. `accordion` and `characterCount`
|
@@ -2512,32 +2805,38 @@
|
|
2512
2805
|
* @typedef {keyof Config} ConfigKey
|
2513
2806
|
*/
|
2514
2807
|
/**
|
2515
|
-
* @template {CompatibleClass}
|
2808
|
+
* @template {CompatibleClass} ComponentClass
|
2809
|
+
* @typedef {ConstructorParameters<ComponentClass>[1]} ComponentConfig
|
2810
|
+
*/
|
2811
|
+
/**
|
2812
|
+
* @template {CompatibleClass} ComponentClass
|
2516
2813
|
* @typedef {object} ErrorContext
|
2517
2814
|
* @property {Element} [element] - Element used for component module initialisation
|
2518
|
-
* @property {
|
2519
|
-
* @property {
|
2815
|
+
* @property {ComponentClass} [component] - Class of component
|
2816
|
+
* @property {ComponentConfig<ComponentClass>} config - Config supplied to component
|
2520
2817
|
*/
|
2521
2818
|
/**
|
2522
|
-
* @template {CompatibleClass}
|
2819
|
+
* @template {CompatibleClass} ComponentClass
|
2523
2820
|
* @callback OnErrorCallback
|
2524
2821
|
* @param {unknown} error - Thrown error
|
2525
|
-
* @param {ErrorContext<
|
2822
|
+
* @param {ErrorContext<ComponentClass>} context - Object containing the element, component class and configuration
|
2526
2823
|
*/
|
2527
2824
|
/**
|
2528
|
-
* @template {CompatibleClass}
|
2825
|
+
* @template {CompatibleClass} ComponentClass
|
2529
2826
|
* @typedef {object} CreateAllOptions
|
2530
2827
|
* @property {Element | Document} [scope] - scope of the document to search within
|
2531
|
-
* @property {OnErrorCallback<
|
2828
|
+
* @property {OnErrorCallback<ComponentClass>} [onError] - callback function if error throw by component on init
|
2532
2829
|
*/
|
2533
2830
|
|
2534
2831
|
exports.Accordion = Accordion;
|
2535
2832
|
exports.Button = Button;
|
2536
2833
|
exports.CharacterCount = CharacterCount;
|
2537
2834
|
exports.Checkboxes = Checkboxes;
|
2538
|
-
exports.Component =
|
2835
|
+
exports.Component = Component;
|
2836
|
+
exports.ConfigurableComponent = ConfigurableComponent;
|
2539
2837
|
exports.ErrorSummary = ErrorSummary;
|
2540
2838
|
exports.ExitThisPage = ExitThisPage;
|
2839
|
+
exports.FileUpload = FileUpload;
|
2541
2840
|
exports.Header = Header;
|
2542
2841
|
exports.NotificationBanner = NotificationBanner;
|
2543
2842
|
exports.PasswordInput = PasswordInput;
|