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/password-input/password-input.bundle.js
CHANGED
@@ -1,7 +1,7 @@
|
|
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
7
|
function closestAttributeValue($element, attributeName) {
|
@@ -9,76 +9,6 @@
|
|
9
9
|
return $closestElementWithAttribute ? $closestElementWithAttribute.getAttribute(attributeName) : null;
|
10
10
|
}
|
11
11
|
|
12
|
-
function normaliseString(value, property) {
|
13
|
-
const trimmedValue = value ? value.trim() : '';
|
14
|
-
let output;
|
15
|
-
let outputType = property == null ? void 0 : property.type;
|
16
|
-
if (!outputType) {
|
17
|
-
if (['true', 'false'].includes(trimmedValue)) {
|
18
|
-
outputType = 'boolean';
|
19
|
-
}
|
20
|
-
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
21
|
-
outputType = 'number';
|
22
|
-
}
|
23
|
-
}
|
24
|
-
switch (outputType) {
|
25
|
-
case 'boolean':
|
26
|
-
output = trimmedValue === 'true';
|
27
|
-
break;
|
28
|
-
case 'number':
|
29
|
-
output = Number(trimmedValue);
|
30
|
-
break;
|
31
|
-
default:
|
32
|
-
output = value;
|
33
|
-
}
|
34
|
-
return output;
|
35
|
-
}
|
36
|
-
|
37
|
-
/**
|
38
|
-
* @typedef {import('./index.mjs').SchemaProperty} SchemaProperty
|
39
|
-
*/
|
40
|
-
|
41
|
-
function mergeConfigs(...configObjects) {
|
42
|
-
const formattedConfigObject = {};
|
43
|
-
for (const configObject of configObjects) {
|
44
|
-
for (const key of Object.keys(configObject)) {
|
45
|
-
const option = formattedConfigObject[key];
|
46
|
-
const override = configObject[key];
|
47
|
-
if (isObject(option) && isObject(override)) {
|
48
|
-
formattedConfigObject[key] = mergeConfigs(option, override);
|
49
|
-
} else {
|
50
|
-
formattedConfigObject[key] = override;
|
51
|
-
}
|
52
|
-
}
|
53
|
-
}
|
54
|
-
return formattedConfigObject;
|
55
|
-
}
|
56
|
-
function extractConfigByNamespace(Component, dataset, namespace) {
|
57
|
-
const property = Component.schema.properties[namespace];
|
58
|
-
if ((property == null ? void 0 : property.type) !== 'object') {
|
59
|
-
return;
|
60
|
-
}
|
61
|
-
const newObject = {
|
62
|
-
[namespace]: ({})
|
63
|
-
};
|
64
|
-
for (const [key, value] of Object.entries(dataset)) {
|
65
|
-
let current = newObject;
|
66
|
-
const keyParts = key.split('.');
|
67
|
-
for (const [index, name] of keyParts.entries()) {
|
68
|
-
if (typeof current === 'object') {
|
69
|
-
if (index < keyParts.length - 1) {
|
70
|
-
if (!isObject(current[name])) {
|
71
|
-
current[name] = {};
|
72
|
-
}
|
73
|
-
current = current[name];
|
74
|
-
} else if (key !== namespace) {
|
75
|
-
current[name] = normaliseString(value);
|
76
|
-
}
|
77
|
-
}
|
78
|
-
}
|
79
|
-
}
|
80
|
-
return newObject[namespace];
|
81
|
-
}
|
82
12
|
function isInitialised($root, moduleName) {
|
83
13
|
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
84
14
|
}
|
@@ -107,46 +37,13 @@
|
|
107
37
|
function formatErrorMessage(Component, message) {
|
108
38
|
return `${Component.moduleName}: ${message}`;
|
109
39
|
}
|
110
|
-
|
111
|
-
/**
|
112
|
-
* Schema for component config
|
113
|
-
*
|
114
|
-
* @typedef {object} Schema
|
115
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
116
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
117
|
-
*/
|
118
|
-
|
119
|
-
/**
|
120
|
-
* Schema property for component config
|
121
|
-
*
|
122
|
-
* @typedef {object} SchemaProperty
|
123
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
124
|
-
*/
|
125
|
-
|
126
|
-
/**
|
127
|
-
* Schema condition for component config
|
128
|
-
*
|
129
|
-
* @typedef {object} SchemaCondition
|
130
|
-
* @property {string[]} required - List of required config fields
|
131
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
132
|
-
*/
|
133
40
|
/**
|
134
41
|
* @typedef ComponentWithModuleName
|
135
42
|
* @property {string} moduleName - Name of the component
|
136
43
|
*/
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
141
|
-
if (field in dataset) {
|
142
|
-
out[field] = normaliseString(dataset[field], property);
|
143
|
-
}
|
144
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
145
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
146
|
-
}
|
147
|
-
}
|
148
|
-
return out;
|
149
|
-
}
|
44
|
+
/**
|
45
|
+
* @import { ObjectNested } from './configuration.mjs'
|
46
|
+
*/
|
150
47
|
|
151
48
|
class GOVUKFrontendError extends Error {
|
152
49
|
constructor(...args) {
|
@@ -166,6 +63,12 @@
|
|
166
63
|
this.name = 'SupportError';
|
167
64
|
}
|
168
65
|
}
|
66
|
+
class ConfigError extends GOVUKFrontendError {
|
67
|
+
constructor(...args) {
|
68
|
+
super(...args);
|
69
|
+
this.name = 'ConfigError';
|
70
|
+
}
|
71
|
+
}
|
169
72
|
class ElementError extends GOVUKFrontendError {
|
170
73
|
constructor(messageOrOptions) {
|
171
74
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
@@ -192,10 +95,10 @@
|
|
192
95
|
}
|
193
96
|
}
|
194
97
|
/**
|
195
|
-
* @
|
98
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
196
99
|
*/
|
197
100
|
|
198
|
-
class
|
101
|
+
class Component {
|
199
102
|
/**
|
200
103
|
* Returns the root element of the component
|
201
104
|
*
|
@@ -246,9 +149,152 @@
|
|
246
149
|
*/
|
247
150
|
|
248
151
|
/**
|
249
|
-
* @typedef {typeof
|
152
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
153
|
+
*/
|
154
|
+
Component.elementType = HTMLElement;
|
155
|
+
|
156
|
+
const configOverride = Symbol.for('configOverride');
|
157
|
+
class ConfigurableComponent extends Component {
|
158
|
+
[configOverride](param) {
|
159
|
+
return {};
|
160
|
+
}
|
161
|
+
|
162
|
+
/**
|
163
|
+
* Returns the root element of the component
|
164
|
+
*
|
165
|
+
* @protected
|
166
|
+
* @returns {ConfigurationType} - the root element of component
|
167
|
+
*/
|
168
|
+
get config() {
|
169
|
+
return this._config;
|
170
|
+
}
|
171
|
+
constructor($root, config) {
|
172
|
+
super($root);
|
173
|
+
this._config = void 0;
|
174
|
+
const childConstructor = this.constructor;
|
175
|
+
if (!isObject(childConstructor.defaults)) {
|
176
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
177
|
+
}
|
178
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
179
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
180
|
+
}
|
181
|
+
}
|
182
|
+
function normaliseString(value, property) {
|
183
|
+
const trimmedValue = value ? value.trim() : '';
|
184
|
+
let output;
|
185
|
+
let outputType = property == null ? void 0 : property.type;
|
186
|
+
if (!outputType) {
|
187
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
188
|
+
outputType = 'boolean';
|
189
|
+
}
|
190
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
191
|
+
outputType = 'number';
|
192
|
+
}
|
193
|
+
}
|
194
|
+
switch (outputType) {
|
195
|
+
case 'boolean':
|
196
|
+
output = trimmedValue === 'true';
|
197
|
+
break;
|
198
|
+
case 'number':
|
199
|
+
output = Number(trimmedValue);
|
200
|
+
break;
|
201
|
+
default:
|
202
|
+
output = value;
|
203
|
+
}
|
204
|
+
return output;
|
205
|
+
}
|
206
|
+
function normaliseDataset(Component, dataset) {
|
207
|
+
if (!isObject(Component.schema)) {
|
208
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
209
|
+
}
|
210
|
+
const out = {};
|
211
|
+
const entries = Object.entries(Component.schema.properties);
|
212
|
+
for (const entry of entries) {
|
213
|
+
const [namespace, property] = entry;
|
214
|
+
const field = namespace.toString();
|
215
|
+
if (field in dataset) {
|
216
|
+
out[field] = normaliseString(dataset[field], property);
|
217
|
+
}
|
218
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
219
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
220
|
+
}
|
221
|
+
}
|
222
|
+
return out;
|
223
|
+
}
|
224
|
+
function mergeConfigs(...configObjects) {
|
225
|
+
const formattedConfigObject = {};
|
226
|
+
for (const configObject of configObjects) {
|
227
|
+
for (const key of Object.keys(configObject)) {
|
228
|
+
const option = formattedConfigObject[key];
|
229
|
+
const override = configObject[key];
|
230
|
+
if (isObject(option) && isObject(override)) {
|
231
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
232
|
+
} else {
|
233
|
+
formattedConfigObject[key] = override;
|
234
|
+
}
|
235
|
+
}
|
236
|
+
}
|
237
|
+
return formattedConfigObject;
|
238
|
+
}
|
239
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
240
|
+
const property = schema.properties[namespace];
|
241
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
242
|
+
return;
|
243
|
+
}
|
244
|
+
const newObject = {
|
245
|
+
[namespace]: {}
|
246
|
+
};
|
247
|
+
for (const [key, value] of Object.entries(dataset)) {
|
248
|
+
let current = newObject;
|
249
|
+
const keyParts = key.split('.');
|
250
|
+
for (const [index, name] of keyParts.entries()) {
|
251
|
+
if (isObject(current)) {
|
252
|
+
if (index < keyParts.length - 1) {
|
253
|
+
if (!isObject(current[name])) {
|
254
|
+
current[name] = {};
|
255
|
+
}
|
256
|
+
current = current[name];
|
257
|
+
} else if (key !== namespace) {
|
258
|
+
current[name] = normaliseString(value);
|
259
|
+
}
|
260
|
+
}
|
261
|
+
}
|
262
|
+
}
|
263
|
+
return newObject[namespace];
|
264
|
+
}
|
265
|
+
/**
|
266
|
+
* Schema for component config
|
267
|
+
*
|
268
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
269
|
+
* @typedef {object} Schema
|
270
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
271
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
272
|
+
*/
|
273
|
+
/**
|
274
|
+
* Schema property for component config
|
275
|
+
*
|
276
|
+
* @typedef {object} SchemaProperty
|
277
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
278
|
+
*/
|
279
|
+
/**
|
280
|
+
* Schema condition for component config
|
281
|
+
*
|
282
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
283
|
+
* @typedef {object} SchemaCondition
|
284
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
285
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
286
|
+
*/
|
287
|
+
/**
|
288
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
289
|
+
* @typedef ChildClass
|
290
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
291
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
292
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
293
|
+
*/
|
294
|
+
/**
|
295
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
296
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
250
297
|
*/
|
251
|
-
GOVUKFrontendComponent.elementType = HTMLElement;
|
252
298
|
|
253
299
|
class I18n {
|
254
300
|
constructor(translations = {}, config = {}) {
|
@@ -447,15 +493,15 @@
|
|
447
493
|
* Password input component
|
448
494
|
*
|
449
495
|
* @preserve
|
496
|
+
* @augments ConfigurableComponent<PasswordInputConfig>
|
450
497
|
*/
|
451
|
-
class PasswordInput extends
|
498
|
+
class PasswordInput extends ConfigurableComponent {
|
452
499
|
/**
|
453
500
|
* @param {Element | null} $root - HTML element to use for password input
|
454
501
|
* @param {PasswordInputConfig} [config] - Password input config
|
455
502
|
*/
|
456
503
|
constructor($root, config = {}) {
|
457
|
-
super($root);
|
458
|
-
this.config = void 0;
|
504
|
+
super($root, config);
|
459
505
|
this.i18n = void 0;
|
460
506
|
this.$input = void 0;
|
461
507
|
this.$showHideButton = void 0;
|
@@ -486,7 +532,6 @@
|
|
486
532
|
}
|
487
533
|
this.$input = $input;
|
488
534
|
this.$showHideButton = $showHideButton;
|
489
|
-
this.config = mergeConfigs(PasswordInput.defaults, config, normaliseDataset(PasswordInput, this.$root.dataset));
|
490
535
|
this.i18n = new I18n(this.config.i18n, {
|
491
536
|
locale: closestAttributeValue(this.$root, 'lang')
|
492
537
|
});
|
@@ -566,8 +611,7 @@
|
|
566
611
|
*/
|
567
612
|
|
568
613
|
/**
|
569
|
-
* @
|
570
|
-
* @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
|
614
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
571
615
|
*/
|
572
616
|
PasswordInput.moduleName = 'govuk-password-input';
|
573
617
|
PasswordInput.defaults = Object.freeze({
|
data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs
CHANGED
@@ -3,76 +3,6 @@ function closestAttributeValue($element, attributeName) {
|
|
3
3
|
return $closestElementWithAttribute ? $closestElementWithAttribute.getAttribute(attributeName) : null;
|
4
4
|
}
|
5
5
|
|
6
|
-
function normaliseString(value, property) {
|
7
|
-
const trimmedValue = value ? value.trim() : '';
|
8
|
-
let output;
|
9
|
-
let outputType = property == null ? void 0 : property.type;
|
10
|
-
if (!outputType) {
|
11
|
-
if (['true', 'false'].includes(trimmedValue)) {
|
12
|
-
outputType = 'boolean';
|
13
|
-
}
|
14
|
-
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
15
|
-
outputType = 'number';
|
16
|
-
}
|
17
|
-
}
|
18
|
-
switch (outputType) {
|
19
|
-
case 'boolean':
|
20
|
-
output = trimmedValue === 'true';
|
21
|
-
break;
|
22
|
-
case 'number':
|
23
|
-
output = Number(trimmedValue);
|
24
|
-
break;
|
25
|
-
default:
|
26
|
-
output = value;
|
27
|
-
}
|
28
|
-
return output;
|
29
|
-
}
|
30
|
-
|
31
|
-
/**
|
32
|
-
* @typedef {import('./index.mjs').SchemaProperty} SchemaProperty
|
33
|
-
*/
|
34
|
-
|
35
|
-
function mergeConfigs(...configObjects) {
|
36
|
-
const formattedConfigObject = {};
|
37
|
-
for (const configObject of configObjects) {
|
38
|
-
for (const key of Object.keys(configObject)) {
|
39
|
-
const option = formattedConfigObject[key];
|
40
|
-
const override = configObject[key];
|
41
|
-
if (isObject(option) && isObject(override)) {
|
42
|
-
formattedConfigObject[key] = mergeConfigs(option, override);
|
43
|
-
} else {
|
44
|
-
formattedConfigObject[key] = override;
|
45
|
-
}
|
46
|
-
}
|
47
|
-
}
|
48
|
-
return formattedConfigObject;
|
49
|
-
}
|
50
|
-
function extractConfigByNamespace(Component, dataset, namespace) {
|
51
|
-
const property = Component.schema.properties[namespace];
|
52
|
-
if ((property == null ? void 0 : property.type) !== 'object') {
|
53
|
-
return;
|
54
|
-
}
|
55
|
-
const newObject = {
|
56
|
-
[namespace]: ({})
|
57
|
-
};
|
58
|
-
for (const [key, value] of Object.entries(dataset)) {
|
59
|
-
let current = newObject;
|
60
|
-
const keyParts = key.split('.');
|
61
|
-
for (const [index, name] of keyParts.entries()) {
|
62
|
-
if (typeof current === 'object') {
|
63
|
-
if (index < keyParts.length - 1) {
|
64
|
-
if (!isObject(current[name])) {
|
65
|
-
current[name] = {};
|
66
|
-
}
|
67
|
-
current = current[name];
|
68
|
-
} else if (key !== namespace) {
|
69
|
-
current[name] = normaliseString(value);
|
70
|
-
}
|
71
|
-
}
|
72
|
-
}
|
73
|
-
}
|
74
|
-
return newObject[namespace];
|
75
|
-
}
|
76
6
|
function isInitialised($root, moduleName) {
|
77
7
|
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
78
8
|
}
|
@@ -101,46 +31,13 @@ function isObject(option) {
|
|
101
31
|
function formatErrorMessage(Component, message) {
|
102
32
|
return `${Component.moduleName}: ${message}`;
|
103
33
|
}
|
104
|
-
|
105
|
-
/**
|
106
|
-
* Schema for component config
|
107
|
-
*
|
108
|
-
* @typedef {object} Schema
|
109
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
110
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
111
|
-
*/
|
112
|
-
|
113
|
-
/**
|
114
|
-
* Schema property for component config
|
115
|
-
*
|
116
|
-
* @typedef {object} SchemaProperty
|
117
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
118
|
-
*/
|
119
|
-
|
120
|
-
/**
|
121
|
-
* Schema condition for component config
|
122
|
-
*
|
123
|
-
* @typedef {object} SchemaCondition
|
124
|
-
* @property {string[]} required - List of required config fields
|
125
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
126
|
-
*/
|
127
34
|
/**
|
128
35
|
* @typedef ComponentWithModuleName
|
129
36
|
* @property {string} moduleName - Name of the component
|
130
37
|
*/
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
135
|
-
if (field in dataset) {
|
136
|
-
out[field] = normaliseString(dataset[field], property);
|
137
|
-
}
|
138
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
139
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
140
|
-
}
|
141
|
-
}
|
142
|
-
return out;
|
143
|
-
}
|
38
|
+
/**
|
39
|
+
* @import { ObjectNested } from './configuration.mjs'
|
40
|
+
*/
|
144
41
|
|
145
42
|
class GOVUKFrontendError extends Error {
|
146
43
|
constructor(...args) {
|
@@ -160,6 +57,12 @@ class SupportError extends GOVUKFrontendError {
|
|
160
57
|
this.name = 'SupportError';
|
161
58
|
}
|
162
59
|
}
|
60
|
+
class ConfigError extends GOVUKFrontendError {
|
61
|
+
constructor(...args) {
|
62
|
+
super(...args);
|
63
|
+
this.name = 'ConfigError';
|
64
|
+
}
|
65
|
+
}
|
163
66
|
class ElementError extends GOVUKFrontendError {
|
164
67
|
constructor(messageOrOptions) {
|
165
68
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
@@ -186,10 +89,10 @@ class InitError extends GOVUKFrontendError {
|
|
186
89
|
}
|
187
90
|
}
|
188
91
|
/**
|
189
|
-
* @
|
92
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
190
93
|
*/
|
191
94
|
|
192
|
-
class
|
95
|
+
class Component {
|
193
96
|
/**
|
194
97
|
* Returns the root element of the component
|
195
98
|
*
|
@@ -240,9 +143,152 @@ class GOVUKFrontendComponent {
|
|
240
143
|
*/
|
241
144
|
|
242
145
|
/**
|
243
|
-
* @typedef {typeof
|
146
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
147
|
+
*/
|
148
|
+
Component.elementType = HTMLElement;
|
149
|
+
|
150
|
+
const configOverride = Symbol.for('configOverride');
|
151
|
+
class ConfigurableComponent extends Component {
|
152
|
+
[configOverride](param) {
|
153
|
+
return {};
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* Returns the root element of the component
|
158
|
+
*
|
159
|
+
* @protected
|
160
|
+
* @returns {ConfigurationType} - the root element of component
|
161
|
+
*/
|
162
|
+
get config() {
|
163
|
+
return this._config;
|
164
|
+
}
|
165
|
+
constructor($root, config) {
|
166
|
+
super($root);
|
167
|
+
this._config = void 0;
|
168
|
+
const childConstructor = this.constructor;
|
169
|
+
if (!isObject(childConstructor.defaults)) {
|
170
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
171
|
+
}
|
172
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
173
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
function normaliseString(value, property) {
|
177
|
+
const trimmedValue = value ? value.trim() : '';
|
178
|
+
let output;
|
179
|
+
let outputType = property == null ? void 0 : property.type;
|
180
|
+
if (!outputType) {
|
181
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
182
|
+
outputType = 'boolean';
|
183
|
+
}
|
184
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
185
|
+
outputType = 'number';
|
186
|
+
}
|
187
|
+
}
|
188
|
+
switch (outputType) {
|
189
|
+
case 'boolean':
|
190
|
+
output = trimmedValue === 'true';
|
191
|
+
break;
|
192
|
+
case 'number':
|
193
|
+
output = Number(trimmedValue);
|
194
|
+
break;
|
195
|
+
default:
|
196
|
+
output = value;
|
197
|
+
}
|
198
|
+
return output;
|
199
|
+
}
|
200
|
+
function normaliseDataset(Component, dataset) {
|
201
|
+
if (!isObject(Component.schema)) {
|
202
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
203
|
+
}
|
204
|
+
const out = {};
|
205
|
+
const entries = Object.entries(Component.schema.properties);
|
206
|
+
for (const entry of entries) {
|
207
|
+
const [namespace, property] = entry;
|
208
|
+
const field = namespace.toString();
|
209
|
+
if (field in dataset) {
|
210
|
+
out[field] = normaliseString(dataset[field], property);
|
211
|
+
}
|
212
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
213
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
214
|
+
}
|
215
|
+
}
|
216
|
+
return out;
|
217
|
+
}
|
218
|
+
function mergeConfigs(...configObjects) {
|
219
|
+
const formattedConfigObject = {};
|
220
|
+
for (const configObject of configObjects) {
|
221
|
+
for (const key of Object.keys(configObject)) {
|
222
|
+
const option = formattedConfigObject[key];
|
223
|
+
const override = configObject[key];
|
224
|
+
if (isObject(option) && isObject(override)) {
|
225
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
226
|
+
} else {
|
227
|
+
formattedConfigObject[key] = override;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
}
|
231
|
+
return formattedConfigObject;
|
232
|
+
}
|
233
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
234
|
+
const property = schema.properties[namespace];
|
235
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
236
|
+
return;
|
237
|
+
}
|
238
|
+
const newObject = {
|
239
|
+
[namespace]: {}
|
240
|
+
};
|
241
|
+
for (const [key, value] of Object.entries(dataset)) {
|
242
|
+
let current = newObject;
|
243
|
+
const keyParts = key.split('.');
|
244
|
+
for (const [index, name] of keyParts.entries()) {
|
245
|
+
if (isObject(current)) {
|
246
|
+
if (index < keyParts.length - 1) {
|
247
|
+
if (!isObject(current[name])) {
|
248
|
+
current[name] = {};
|
249
|
+
}
|
250
|
+
current = current[name];
|
251
|
+
} else if (key !== namespace) {
|
252
|
+
current[name] = normaliseString(value);
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
}
|
257
|
+
return newObject[namespace];
|
258
|
+
}
|
259
|
+
/**
|
260
|
+
* Schema for component config
|
261
|
+
*
|
262
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
263
|
+
* @typedef {object} Schema
|
264
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
265
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
266
|
+
*/
|
267
|
+
/**
|
268
|
+
* Schema property for component config
|
269
|
+
*
|
270
|
+
* @typedef {object} SchemaProperty
|
271
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
272
|
+
*/
|
273
|
+
/**
|
274
|
+
* Schema condition for component config
|
275
|
+
*
|
276
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
277
|
+
* @typedef {object} SchemaCondition
|
278
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
279
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
280
|
+
*/
|
281
|
+
/**
|
282
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
283
|
+
* @typedef ChildClass
|
284
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
285
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
286
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
287
|
+
*/
|
288
|
+
/**
|
289
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
290
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
244
291
|
*/
|
245
|
-
GOVUKFrontendComponent.elementType = HTMLElement;
|
246
292
|
|
247
293
|
class I18n {
|
248
294
|
constructor(translations = {}, config = {}) {
|
@@ -441,15 +487,15 @@ I18n.pluralRules = {
|
|
441
487
|
* Password input component
|
442
488
|
*
|
443
489
|
* @preserve
|
490
|
+
* @augments ConfigurableComponent<PasswordInputConfig>
|
444
491
|
*/
|
445
|
-
class PasswordInput extends
|
492
|
+
class PasswordInput extends ConfigurableComponent {
|
446
493
|
/**
|
447
494
|
* @param {Element | null} $root - HTML element to use for password input
|
448
495
|
* @param {PasswordInputConfig} [config] - Password input config
|
449
496
|
*/
|
450
497
|
constructor($root, config = {}) {
|
451
|
-
super($root);
|
452
|
-
this.config = void 0;
|
498
|
+
super($root, config);
|
453
499
|
this.i18n = void 0;
|
454
500
|
this.$input = void 0;
|
455
501
|
this.$showHideButton = void 0;
|
@@ -480,7 +526,6 @@ class PasswordInput extends GOVUKFrontendComponent {
|
|
480
526
|
}
|
481
527
|
this.$input = $input;
|
482
528
|
this.$showHideButton = $showHideButton;
|
483
|
-
this.config = mergeConfigs(PasswordInput.defaults, config, normaliseDataset(PasswordInput, this.$root.dataset));
|
484
529
|
this.i18n = new I18n(this.config.i18n, {
|
485
530
|
locale: closestAttributeValue(this.$root, 'lang')
|
486
531
|
});
|
@@ -560,8 +605,7 @@ class PasswordInput extends GOVUKFrontendComponent {
|
|
560
605
|
*/
|
561
606
|
|
562
607
|
/**
|
563
|
-
* @
|
564
|
-
* @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
|
608
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
565
609
|
*/
|
566
610
|
PasswordInput.moduleName = 'govuk-password-input';
|
567
611
|
PasswordInput.defaults = Object.freeze({
|