govuk_tech_docs 4.2.0 → 4.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -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/lib/source/layouts/core.erb +16 -5
- 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
data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js
CHANGED
@@ -1,79 +1,9 @@
|
|
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
|
-
function normaliseString(value, property) {
|
8
|
-
const trimmedValue = value ? value.trim() : '';
|
9
|
-
let output;
|
10
|
-
let outputType = property == null ? void 0 : property.type;
|
11
|
-
if (!outputType) {
|
12
|
-
if (['true', 'false'].includes(trimmedValue)) {
|
13
|
-
outputType = 'boolean';
|
14
|
-
}
|
15
|
-
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
16
|
-
outputType = 'number';
|
17
|
-
}
|
18
|
-
}
|
19
|
-
switch (outputType) {
|
20
|
-
case 'boolean':
|
21
|
-
output = trimmedValue === 'true';
|
22
|
-
break;
|
23
|
-
case 'number':
|
24
|
-
output = Number(trimmedValue);
|
25
|
-
break;
|
26
|
-
default:
|
27
|
-
output = value;
|
28
|
-
}
|
29
|
-
return output;
|
30
|
-
}
|
31
|
-
|
32
|
-
/**
|
33
|
-
* @typedef {import('./index.mjs').SchemaProperty} SchemaProperty
|
34
|
-
*/
|
35
|
-
|
36
|
-
function mergeConfigs(...configObjects) {
|
37
|
-
const formattedConfigObject = {};
|
38
|
-
for (const configObject of configObjects) {
|
39
|
-
for (const key of Object.keys(configObject)) {
|
40
|
-
const option = formattedConfigObject[key];
|
41
|
-
const override = configObject[key];
|
42
|
-
if (isObject(option) && isObject(override)) {
|
43
|
-
formattedConfigObject[key] = mergeConfigs(option, override);
|
44
|
-
} else {
|
45
|
-
formattedConfigObject[key] = override;
|
46
|
-
}
|
47
|
-
}
|
48
|
-
}
|
49
|
-
return formattedConfigObject;
|
50
|
-
}
|
51
|
-
function extractConfigByNamespace(Component, dataset, namespace) {
|
52
|
-
const property = Component.schema.properties[namespace];
|
53
|
-
if ((property == null ? void 0 : property.type) !== 'object') {
|
54
|
-
return;
|
55
|
-
}
|
56
|
-
const newObject = {
|
57
|
-
[namespace]: ({})
|
58
|
-
};
|
59
|
-
for (const [key, value] of Object.entries(dataset)) {
|
60
|
-
let current = newObject;
|
61
|
-
const keyParts = key.split('.');
|
62
|
-
for (const [index, name] of keyParts.entries()) {
|
63
|
-
if (typeof current === 'object') {
|
64
|
-
if (index < keyParts.length - 1) {
|
65
|
-
if (!isObject(current[name])) {
|
66
|
-
current[name] = {};
|
67
|
-
}
|
68
|
-
current = current[name];
|
69
|
-
} else if (key !== namespace) {
|
70
|
-
current[name] = normaliseString(value);
|
71
|
-
}
|
72
|
-
}
|
73
|
-
}
|
74
|
-
}
|
75
|
-
return newObject[namespace];
|
76
|
-
}
|
77
7
|
function isInitialised($root, moduleName) {
|
78
8
|
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
79
9
|
}
|
@@ -102,46 +32,13 @@
|
|
102
32
|
function formatErrorMessage(Component, message) {
|
103
33
|
return `${Component.moduleName}: ${message}`;
|
104
34
|
}
|
105
|
-
|
106
|
-
/**
|
107
|
-
* Schema for component config
|
108
|
-
*
|
109
|
-
* @typedef {object} Schema
|
110
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
111
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
112
|
-
*/
|
113
|
-
|
114
|
-
/**
|
115
|
-
* Schema property for component config
|
116
|
-
*
|
117
|
-
* @typedef {object} SchemaProperty
|
118
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
119
|
-
*/
|
120
|
-
|
121
|
-
/**
|
122
|
-
* Schema condition for component config
|
123
|
-
*
|
124
|
-
* @typedef {object} SchemaCondition
|
125
|
-
* @property {string[]} required - List of required config fields
|
126
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
127
|
-
*/
|
128
35
|
/**
|
129
36
|
* @typedef ComponentWithModuleName
|
130
37
|
* @property {string} moduleName - Name of the component
|
131
38
|
*/
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
136
|
-
if (field in dataset) {
|
137
|
-
out[field] = normaliseString(dataset[field], property);
|
138
|
-
}
|
139
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
140
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
141
|
-
}
|
142
|
-
}
|
143
|
-
return out;
|
144
|
-
}
|
39
|
+
/**
|
40
|
+
* @import { ObjectNested } from './configuration.mjs'
|
41
|
+
*/
|
145
42
|
|
146
43
|
class GOVUKFrontendError extends Error {
|
147
44
|
constructor(...args) {
|
@@ -161,6 +58,12 @@
|
|
161
58
|
this.name = 'SupportError';
|
162
59
|
}
|
163
60
|
}
|
61
|
+
class ConfigError extends GOVUKFrontendError {
|
62
|
+
constructor(...args) {
|
63
|
+
super(...args);
|
64
|
+
this.name = 'ConfigError';
|
65
|
+
}
|
66
|
+
}
|
164
67
|
class ElementError extends GOVUKFrontendError {
|
165
68
|
constructor(messageOrOptions) {
|
166
69
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
@@ -187,10 +90,10 @@
|
|
187
90
|
}
|
188
91
|
}
|
189
92
|
/**
|
190
|
-
* @
|
93
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
191
94
|
*/
|
192
95
|
|
193
|
-
class
|
96
|
+
class Component {
|
194
97
|
/**
|
195
98
|
* Returns the root element of the component
|
196
99
|
*
|
@@ -241,9 +144,152 @@
|
|
241
144
|
*/
|
242
145
|
|
243
146
|
/**
|
244
|
-
* @typedef {typeof
|
147
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
148
|
+
*/
|
149
|
+
Component.elementType = HTMLElement;
|
150
|
+
|
151
|
+
const configOverride = Symbol.for('configOverride');
|
152
|
+
class ConfigurableComponent extends Component {
|
153
|
+
[configOverride](param) {
|
154
|
+
return {};
|
155
|
+
}
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Returns the root element of the component
|
159
|
+
*
|
160
|
+
* @protected
|
161
|
+
* @returns {ConfigurationType} - the root element of component
|
162
|
+
*/
|
163
|
+
get config() {
|
164
|
+
return this._config;
|
165
|
+
}
|
166
|
+
constructor($root, config) {
|
167
|
+
super($root);
|
168
|
+
this._config = void 0;
|
169
|
+
const childConstructor = this.constructor;
|
170
|
+
if (!isObject(childConstructor.defaults)) {
|
171
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
172
|
+
}
|
173
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
174
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
function normaliseString(value, property) {
|
178
|
+
const trimmedValue = value ? value.trim() : '';
|
179
|
+
let output;
|
180
|
+
let outputType = property == null ? void 0 : property.type;
|
181
|
+
if (!outputType) {
|
182
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
183
|
+
outputType = 'boolean';
|
184
|
+
}
|
185
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
186
|
+
outputType = 'number';
|
187
|
+
}
|
188
|
+
}
|
189
|
+
switch (outputType) {
|
190
|
+
case 'boolean':
|
191
|
+
output = trimmedValue === 'true';
|
192
|
+
break;
|
193
|
+
case 'number':
|
194
|
+
output = Number(trimmedValue);
|
195
|
+
break;
|
196
|
+
default:
|
197
|
+
output = value;
|
198
|
+
}
|
199
|
+
return output;
|
200
|
+
}
|
201
|
+
function normaliseDataset(Component, dataset) {
|
202
|
+
if (!isObject(Component.schema)) {
|
203
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
204
|
+
}
|
205
|
+
const out = {};
|
206
|
+
const entries = Object.entries(Component.schema.properties);
|
207
|
+
for (const entry of entries) {
|
208
|
+
const [namespace, property] = entry;
|
209
|
+
const field = namespace.toString();
|
210
|
+
if (field in dataset) {
|
211
|
+
out[field] = normaliseString(dataset[field], property);
|
212
|
+
}
|
213
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
214
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
215
|
+
}
|
216
|
+
}
|
217
|
+
return out;
|
218
|
+
}
|
219
|
+
function mergeConfigs(...configObjects) {
|
220
|
+
const formattedConfigObject = {};
|
221
|
+
for (const configObject of configObjects) {
|
222
|
+
for (const key of Object.keys(configObject)) {
|
223
|
+
const option = formattedConfigObject[key];
|
224
|
+
const override = configObject[key];
|
225
|
+
if (isObject(option) && isObject(override)) {
|
226
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
227
|
+
} else {
|
228
|
+
formattedConfigObject[key] = override;
|
229
|
+
}
|
230
|
+
}
|
231
|
+
}
|
232
|
+
return formattedConfigObject;
|
233
|
+
}
|
234
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
235
|
+
const property = schema.properties[namespace];
|
236
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
237
|
+
return;
|
238
|
+
}
|
239
|
+
const newObject = {
|
240
|
+
[namespace]: {}
|
241
|
+
};
|
242
|
+
for (const [key, value] of Object.entries(dataset)) {
|
243
|
+
let current = newObject;
|
244
|
+
const keyParts = key.split('.');
|
245
|
+
for (const [index, name] of keyParts.entries()) {
|
246
|
+
if (isObject(current)) {
|
247
|
+
if (index < keyParts.length - 1) {
|
248
|
+
if (!isObject(current[name])) {
|
249
|
+
current[name] = {};
|
250
|
+
}
|
251
|
+
current = current[name];
|
252
|
+
} else if (key !== namespace) {
|
253
|
+
current[name] = normaliseString(value);
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
}
|
258
|
+
return newObject[namespace];
|
259
|
+
}
|
260
|
+
/**
|
261
|
+
* Schema for component config
|
262
|
+
*
|
263
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
264
|
+
* @typedef {object} Schema
|
265
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
266
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
267
|
+
*/
|
268
|
+
/**
|
269
|
+
* Schema property for component config
|
270
|
+
*
|
271
|
+
* @typedef {object} SchemaProperty
|
272
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
273
|
+
*/
|
274
|
+
/**
|
275
|
+
* Schema condition for component config
|
276
|
+
*
|
277
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
278
|
+
* @typedef {object} SchemaCondition
|
279
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
280
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
281
|
+
*/
|
282
|
+
/**
|
283
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
284
|
+
* @typedef ChildClass
|
285
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
286
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
287
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
288
|
+
*/
|
289
|
+
/**
|
290
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
291
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
245
292
|
*/
|
246
|
-
GOVUKFrontendComponent.elementType = HTMLElement;
|
247
293
|
|
248
294
|
class I18n {
|
249
295
|
constructor(translations = {}, config = {}) {
|
@@ -442,15 +488,15 @@
|
|
442
488
|
* Exit this page component
|
443
489
|
*
|
444
490
|
* @preserve
|
491
|
+
* @augments ConfigurableComponent<ExitThisPageConfig>
|
445
492
|
*/
|
446
|
-
class ExitThisPage extends
|
493
|
+
class ExitThisPage extends ConfigurableComponent {
|
447
494
|
/**
|
448
495
|
* @param {Element | null} $root - HTML element that wraps the Exit This Page button
|
449
496
|
* @param {ExitThisPageConfig} [config] - Exit This Page config
|
450
497
|
*/
|
451
498
|
constructor($root, config = {}) {
|
452
|
-
super($root);
|
453
|
-
this.config = void 0;
|
499
|
+
super($root, config);
|
454
500
|
this.i18n = void 0;
|
455
501
|
this.$button = void 0;
|
456
502
|
this.$skiplinkButton = null;
|
@@ -471,7 +517,6 @@
|
|
471
517
|
identifier: 'Button (`.govuk-exit-this-page__button`)'
|
472
518
|
});
|
473
519
|
}
|
474
|
-
this.config = mergeConfigs(ExitThisPage.defaults, config, normaliseDataset(ExitThisPage, this.$root.dataset));
|
475
520
|
this.i18n = new I18n(this.config.i18n);
|
476
521
|
this.$button = $button;
|
477
522
|
const $skiplinkButton = document.querySelector('.govuk-js-exit-this-page-skiplink');
|
@@ -637,7 +682,7 @@
|
|
637
682
|
*/
|
638
683
|
|
639
684
|
/**
|
640
|
-
* @
|
685
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
641
686
|
*/
|
642
687
|
ExitThisPage.moduleName = 'govuk-exit-this-page';
|
643
688
|
ExitThisPage.defaults = Object.freeze({
|
data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs
CHANGED
@@ -1,73 +1,3 @@
|
|
1
|
-
function normaliseString(value, property) {
|
2
|
-
const trimmedValue = value ? value.trim() : '';
|
3
|
-
let output;
|
4
|
-
let outputType = property == null ? void 0 : property.type;
|
5
|
-
if (!outputType) {
|
6
|
-
if (['true', 'false'].includes(trimmedValue)) {
|
7
|
-
outputType = 'boolean';
|
8
|
-
}
|
9
|
-
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
10
|
-
outputType = 'number';
|
11
|
-
}
|
12
|
-
}
|
13
|
-
switch (outputType) {
|
14
|
-
case 'boolean':
|
15
|
-
output = trimmedValue === 'true';
|
16
|
-
break;
|
17
|
-
case 'number':
|
18
|
-
output = Number(trimmedValue);
|
19
|
-
break;
|
20
|
-
default:
|
21
|
-
output = value;
|
22
|
-
}
|
23
|
-
return output;
|
24
|
-
}
|
25
|
-
|
26
|
-
/**
|
27
|
-
* @typedef {import('./index.mjs').SchemaProperty} SchemaProperty
|
28
|
-
*/
|
29
|
-
|
30
|
-
function mergeConfigs(...configObjects) {
|
31
|
-
const formattedConfigObject = {};
|
32
|
-
for (const configObject of configObjects) {
|
33
|
-
for (const key of Object.keys(configObject)) {
|
34
|
-
const option = formattedConfigObject[key];
|
35
|
-
const override = configObject[key];
|
36
|
-
if (isObject(option) && isObject(override)) {
|
37
|
-
formattedConfigObject[key] = mergeConfigs(option, override);
|
38
|
-
} else {
|
39
|
-
formattedConfigObject[key] = override;
|
40
|
-
}
|
41
|
-
}
|
42
|
-
}
|
43
|
-
return formattedConfigObject;
|
44
|
-
}
|
45
|
-
function extractConfigByNamespace(Component, dataset, namespace) {
|
46
|
-
const property = Component.schema.properties[namespace];
|
47
|
-
if ((property == null ? void 0 : property.type) !== 'object') {
|
48
|
-
return;
|
49
|
-
}
|
50
|
-
const newObject = {
|
51
|
-
[namespace]: ({})
|
52
|
-
};
|
53
|
-
for (const [key, value] of Object.entries(dataset)) {
|
54
|
-
let current = newObject;
|
55
|
-
const keyParts = key.split('.');
|
56
|
-
for (const [index, name] of keyParts.entries()) {
|
57
|
-
if (typeof current === 'object') {
|
58
|
-
if (index < keyParts.length - 1) {
|
59
|
-
if (!isObject(current[name])) {
|
60
|
-
current[name] = {};
|
61
|
-
}
|
62
|
-
current = current[name];
|
63
|
-
} else if (key !== namespace) {
|
64
|
-
current[name] = normaliseString(value);
|
65
|
-
}
|
66
|
-
}
|
67
|
-
}
|
68
|
-
}
|
69
|
-
return newObject[namespace];
|
70
|
-
}
|
71
1
|
function isInitialised($root, moduleName) {
|
72
2
|
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
73
3
|
}
|
@@ -96,46 +26,13 @@ function isObject(option) {
|
|
96
26
|
function formatErrorMessage(Component, message) {
|
97
27
|
return `${Component.moduleName}: ${message}`;
|
98
28
|
}
|
99
|
-
|
100
|
-
/**
|
101
|
-
* Schema for component config
|
102
|
-
*
|
103
|
-
* @typedef {object} Schema
|
104
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
105
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
106
|
-
*/
|
107
|
-
|
108
|
-
/**
|
109
|
-
* Schema property for component config
|
110
|
-
*
|
111
|
-
* @typedef {object} SchemaProperty
|
112
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
113
|
-
*/
|
114
|
-
|
115
|
-
/**
|
116
|
-
* Schema condition for component config
|
117
|
-
*
|
118
|
-
* @typedef {object} SchemaCondition
|
119
|
-
* @property {string[]} required - List of required config fields
|
120
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
121
|
-
*/
|
122
29
|
/**
|
123
30
|
* @typedef ComponentWithModuleName
|
124
31
|
* @property {string} moduleName - Name of the component
|
125
32
|
*/
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
130
|
-
if (field in dataset) {
|
131
|
-
out[field] = normaliseString(dataset[field], property);
|
132
|
-
}
|
133
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
134
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
135
|
-
}
|
136
|
-
}
|
137
|
-
return out;
|
138
|
-
}
|
33
|
+
/**
|
34
|
+
* @import { ObjectNested } from './configuration.mjs'
|
35
|
+
*/
|
139
36
|
|
140
37
|
class GOVUKFrontendError extends Error {
|
141
38
|
constructor(...args) {
|
@@ -155,6 +52,12 @@ class SupportError extends GOVUKFrontendError {
|
|
155
52
|
this.name = 'SupportError';
|
156
53
|
}
|
157
54
|
}
|
55
|
+
class ConfigError extends GOVUKFrontendError {
|
56
|
+
constructor(...args) {
|
57
|
+
super(...args);
|
58
|
+
this.name = 'ConfigError';
|
59
|
+
}
|
60
|
+
}
|
158
61
|
class ElementError extends GOVUKFrontendError {
|
159
62
|
constructor(messageOrOptions) {
|
160
63
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
@@ -181,10 +84,10 @@ class InitError extends GOVUKFrontendError {
|
|
181
84
|
}
|
182
85
|
}
|
183
86
|
/**
|
184
|
-
* @
|
87
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
185
88
|
*/
|
186
89
|
|
187
|
-
class
|
90
|
+
class Component {
|
188
91
|
/**
|
189
92
|
* Returns the root element of the component
|
190
93
|
*
|
@@ -235,9 +138,152 @@ class GOVUKFrontendComponent {
|
|
235
138
|
*/
|
236
139
|
|
237
140
|
/**
|
238
|
-
* @typedef {typeof
|
141
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
142
|
+
*/
|
143
|
+
Component.elementType = HTMLElement;
|
144
|
+
|
145
|
+
const configOverride = Symbol.for('configOverride');
|
146
|
+
class ConfigurableComponent extends Component {
|
147
|
+
[configOverride](param) {
|
148
|
+
return {};
|
149
|
+
}
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Returns the root element of the component
|
153
|
+
*
|
154
|
+
* @protected
|
155
|
+
* @returns {ConfigurationType} - the root element of component
|
156
|
+
*/
|
157
|
+
get config() {
|
158
|
+
return this._config;
|
159
|
+
}
|
160
|
+
constructor($root, config) {
|
161
|
+
super($root);
|
162
|
+
this._config = void 0;
|
163
|
+
const childConstructor = this.constructor;
|
164
|
+
if (!isObject(childConstructor.defaults)) {
|
165
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
166
|
+
}
|
167
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
168
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
function normaliseString(value, property) {
|
172
|
+
const trimmedValue = value ? value.trim() : '';
|
173
|
+
let output;
|
174
|
+
let outputType = property == null ? void 0 : property.type;
|
175
|
+
if (!outputType) {
|
176
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
177
|
+
outputType = 'boolean';
|
178
|
+
}
|
179
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
180
|
+
outputType = 'number';
|
181
|
+
}
|
182
|
+
}
|
183
|
+
switch (outputType) {
|
184
|
+
case 'boolean':
|
185
|
+
output = trimmedValue === 'true';
|
186
|
+
break;
|
187
|
+
case 'number':
|
188
|
+
output = Number(trimmedValue);
|
189
|
+
break;
|
190
|
+
default:
|
191
|
+
output = value;
|
192
|
+
}
|
193
|
+
return output;
|
194
|
+
}
|
195
|
+
function normaliseDataset(Component, dataset) {
|
196
|
+
if (!isObject(Component.schema)) {
|
197
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
198
|
+
}
|
199
|
+
const out = {};
|
200
|
+
const entries = Object.entries(Component.schema.properties);
|
201
|
+
for (const entry of entries) {
|
202
|
+
const [namespace, property] = entry;
|
203
|
+
const field = namespace.toString();
|
204
|
+
if (field in dataset) {
|
205
|
+
out[field] = normaliseString(dataset[field], property);
|
206
|
+
}
|
207
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
208
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
209
|
+
}
|
210
|
+
}
|
211
|
+
return out;
|
212
|
+
}
|
213
|
+
function mergeConfigs(...configObjects) {
|
214
|
+
const formattedConfigObject = {};
|
215
|
+
for (const configObject of configObjects) {
|
216
|
+
for (const key of Object.keys(configObject)) {
|
217
|
+
const option = formattedConfigObject[key];
|
218
|
+
const override = configObject[key];
|
219
|
+
if (isObject(option) && isObject(override)) {
|
220
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
221
|
+
} else {
|
222
|
+
formattedConfigObject[key] = override;
|
223
|
+
}
|
224
|
+
}
|
225
|
+
}
|
226
|
+
return formattedConfigObject;
|
227
|
+
}
|
228
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
229
|
+
const property = schema.properties[namespace];
|
230
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
231
|
+
return;
|
232
|
+
}
|
233
|
+
const newObject = {
|
234
|
+
[namespace]: {}
|
235
|
+
};
|
236
|
+
for (const [key, value] of Object.entries(dataset)) {
|
237
|
+
let current = newObject;
|
238
|
+
const keyParts = key.split('.');
|
239
|
+
for (const [index, name] of keyParts.entries()) {
|
240
|
+
if (isObject(current)) {
|
241
|
+
if (index < keyParts.length - 1) {
|
242
|
+
if (!isObject(current[name])) {
|
243
|
+
current[name] = {};
|
244
|
+
}
|
245
|
+
current = current[name];
|
246
|
+
} else if (key !== namespace) {
|
247
|
+
current[name] = normaliseString(value);
|
248
|
+
}
|
249
|
+
}
|
250
|
+
}
|
251
|
+
}
|
252
|
+
return newObject[namespace];
|
253
|
+
}
|
254
|
+
/**
|
255
|
+
* Schema for component config
|
256
|
+
*
|
257
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
258
|
+
* @typedef {object} Schema
|
259
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
260
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
261
|
+
*/
|
262
|
+
/**
|
263
|
+
* Schema property for component config
|
264
|
+
*
|
265
|
+
* @typedef {object} SchemaProperty
|
266
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
267
|
+
*/
|
268
|
+
/**
|
269
|
+
* Schema condition for component config
|
270
|
+
*
|
271
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
272
|
+
* @typedef {object} SchemaCondition
|
273
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
274
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
275
|
+
*/
|
276
|
+
/**
|
277
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
278
|
+
* @typedef ChildClass
|
279
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
280
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
281
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
282
|
+
*/
|
283
|
+
/**
|
284
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
285
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
239
286
|
*/
|
240
|
-
GOVUKFrontendComponent.elementType = HTMLElement;
|
241
287
|
|
242
288
|
class I18n {
|
243
289
|
constructor(translations = {}, config = {}) {
|
@@ -436,15 +482,15 @@ I18n.pluralRules = {
|
|
436
482
|
* Exit this page component
|
437
483
|
*
|
438
484
|
* @preserve
|
485
|
+
* @augments ConfigurableComponent<ExitThisPageConfig>
|
439
486
|
*/
|
440
|
-
class ExitThisPage extends
|
487
|
+
class ExitThisPage extends ConfigurableComponent {
|
441
488
|
/**
|
442
489
|
* @param {Element | null} $root - HTML element that wraps the Exit This Page button
|
443
490
|
* @param {ExitThisPageConfig} [config] - Exit This Page config
|
444
491
|
*/
|
445
492
|
constructor($root, config = {}) {
|
446
|
-
super($root);
|
447
|
-
this.config = void 0;
|
493
|
+
super($root, config);
|
448
494
|
this.i18n = void 0;
|
449
495
|
this.$button = void 0;
|
450
496
|
this.$skiplinkButton = null;
|
@@ -465,7 +511,6 @@ class ExitThisPage extends GOVUKFrontendComponent {
|
|
465
511
|
identifier: 'Button (`.govuk-exit-this-page__button`)'
|
466
512
|
});
|
467
513
|
}
|
468
|
-
this.config = mergeConfigs(ExitThisPage.defaults, config, normaliseDataset(ExitThisPage, this.$root.dataset));
|
469
514
|
this.i18n = new I18n(this.config.i18n);
|
470
515
|
this.$button = $button;
|
471
516
|
const $skiplinkButton = document.querySelector('.govuk-js-exit-this-page-skiplink');
|
@@ -631,7 +676,7 @@ class ExitThisPage extends GOVUKFrontendComponent {
|
|
631
676
|
*/
|
632
677
|
|
633
678
|
/**
|
634
|
-
* @
|
679
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
635
680
|
*/
|
636
681
|
ExitThisPage.moduleName = 'govuk-exit-this-page';
|
637
682
|
ExitThisPage.defaults = Object.freeze({
|