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/error-summary/error-summary.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 getFragmentFromUrl(url) {
|
78
8
|
if (!url.includes('#')) {
|
79
9
|
return undefined;
|
@@ -132,46 +62,13 @@
|
|
132
62
|
function formatErrorMessage(Component, message) {
|
133
63
|
return `${Component.moduleName}: ${message}`;
|
134
64
|
}
|
135
|
-
|
136
|
-
/**
|
137
|
-
* Schema for component config
|
138
|
-
*
|
139
|
-
* @typedef {object} Schema
|
140
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
141
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
142
|
-
*/
|
143
|
-
|
144
|
-
/**
|
145
|
-
* Schema property for component config
|
146
|
-
*
|
147
|
-
* @typedef {object} SchemaProperty
|
148
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
149
|
-
*/
|
150
|
-
|
151
|
-
/**
|
152
|
-
* Schema condition for component config
|
153
|
-
*
|
154
|
-
* @typedef {object} SchemaCondition
|
155
|
-
* @property {string[]} required - List of required config fields
|
156
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
157
|
-
*/
|
158
65
|
/**
|
159
66
|
* @typedef ComponentWithModuleName
|
160
67
|
* @property {string} moduleName - Name of the component
|
161
68
|
*/
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
166
|
-
if (field in dataset) {
|
167
|
-
out[field] = normaliseString(dataset[field], property);
|
168
|
-
}
|
169
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
170
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
171
|
-
}
|
172
|
-
}
|
173
|
-
return out;
|
174
|
-
}
|
69
|
+
/**
|
70
|
+
* @import { ObjectNested } from './configuration.mjs'
|
71
|
+
*/
|
175
72
|
|
176
73
|
class GOVUKFrontendError extends Error {
|
177
74
|
constructor(...args) {
|
@@ -191,6 +88,12 @@
|
|
191
88
|
this.name = 'SupportError';
|
192
89
|
}
|
193
90
|
}
|
91
|
+
class ConfigError extends GOVUKFrontendError {
|
92
|
+
constructor(...args) {
|
93
|
+
super(...args);
|
94
|
+
this.name = 'ConfigError';
|
95
|
+
}
|
96
|
+
}
|
194
97
|
class ElementError extends GOVUKFrontendError {
|
195
98
|
constructor(messageOrOptions) {
|
196
99
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
@@ -217,10 +120,10 @@
|
|
217
120
|
}
|
218
121
|
}
|
219
122
|
/**
|
220
|
-
* @
|
123
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
221
124
|
*/
|
222
125
|
|
223
|
-
class
|
126
|
+
class Component {
|
224
127
|
/**
|
225
128
|
* Returns the root element of the component
|
226
129
|
*
|
@@ -271,9 +174,152 @@
|
|
271
174
|
*/
|
272
175
|
|
273
176
|
/**
|
274
|
-
* @typedef {typeof
|
177
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
178
|
+
*/
|
179
|
+
Component.elementType = HTMLElement;
|
180
|
+
|
181
|
+
const configOverride = Symbol.for('configOverride');
|
182
|
+
class ConfigurableComponent extends Component {
|
183
|
+
[configOverride](param) {
|
184
|
+
return {};
|
185
|
+
}
|
186
|
+
|
187
|
+
/**
|
188
|
+
* Returns the root element of the component
|
189
|
+
*
|
190
|
+
* @protected
|
191
|
+
* @returns {ConfigurationType} - the root element of component
|
192
|
+
*/
|
193
|
+
get config() {
|
194
|
+
return this._config;
|
195
|
+
}
|
196
|
+
constructor($root, config) {
|
197
|
+
super($root);
|
198
|
+
this._config = void 0;
|
199
|
+
const childConstructor = this.constructor;
|
200
|
+
if (!isObject(childConstructor.defaults)) {
|
201
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
202
|
+
}
|
203
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
204
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
205
|
+
}
|
206
|
+
}
|
207
|
+
function normaliseString(value, property) {
|
208
|
+
const trimmedValue = value ? value.trim() : '';
|
209
|
+
let output;
|
210
|
+
let outputType = property == null ? void 0 : property.type;
|
211
|
+
if (!outputType) {
|
212
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
213
|
+
outputType = 'boolean';
|
214
|
+
}
|
215
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
216
|
+
outputType = 'number';
|
217
|
+
}
|
218
|
+
}
|
219
|
+
switch (outputType) {
|
220
|
+
case 'boolean':
|
221
|
+
output = trimmedValue === 'true';
|
222
|
+
break;
|
223
|
+
case 'number':
|
224
|
+
output = Number(trimmedValue);
|
225
|
+
break;
|
226
|
+
default:
|
227
|
+
output = value;
|
228
|
+
}
|
229
|
+
return output;
|
230
|
+
}
|
231
|
+
function normaliseDataset(Component, dataset) {
|
232
|
+
if (!isObject(Component.schema)) {
|
233
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
234
|
+
}
|
235
|
+
const out = {};
|
236
|
+
const entries = Object.entries(Component.schema.properties);
|
237
|
+
for (const entry of entries) {
|
238
|
+
const [namespace, property] = entry;
|
239
|
+
const field = namespace.toString();
|
240
|
+
if (field in dataset) {
|
241
|
+
out[field] = normaliseString(dataset[field], property);
|
242
|
+
}
|
243
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
244
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
245
|
+
}
|
246
|
+
}
|
247
|
+
return out;
|
248
|
+
}
|
249
|
+
function mergeConfigs(...configObjects) {
|
250
|
+
const formattedConfigObject = {};
|
251
|
+
for (const configObject of configObjects) {
|
252
|
+
for (const key of Object.keys(configObject)) {
|
253
|
+
const option = formattedConfigObject[key];
|
254
|
+
const override = configObject[key];
|
255
|
+
if (isObject(option) && isObject(override)) {
|
256
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
257
|
+
} else {
|
258
|
+
formattedConfigObject[key] = override;
|
259
|
+
}
|
260
|
+
}
|
261
|
+
}
|
262
|
+
return formattedConfigObject;
|
263
|
+
}
|
264
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
265
|
+
const property = schema.properties[namespace];
|
266
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
267
|
+
return;
|
268
|
+
}
|
269
|
+
const newObject = {
|
270
|
+
[namespace]: {}
|
271
|
+
};
|
272
|
+
for (const [key, value] of Object.entries(dataset)) {
|
273
|
+
let current = newObject;
|
274
|
+
const keyParts = key.split('.');
|
275
|
+
for (const [index, name] of keyParts.entries()) {
|
276
|
+
if (isObject(current)) {
|
277
|
+
if (index < keyParts.length - 1) {
|
278
|
+
if (!isObject(current[name])) {
|
279
|
+
current[name] = {};
|
280
|
+
}
|
281
|
+
current = current[name];
|
282
|
+
} else if (key !== namespace) {
|
283
|
+
current[name] = normaliseString(value);
|
284
|
+
}
|
285
|
+
}
|
286
|
+
}
|
287
|
+
}
|
288
|
+
return newObject[namespace];
|
289
|
+
}
|
290
|
+
/**
|
291
|
+
* Schema for component config
|
292
|
+
*
|
293
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
294
|
+
* @typedef {object} Schema
|
295
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
296
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
297
|
+
*/
|
298
|
+
/**
|
299
|
+
* Schema property for component config
|
300
|
+
*
|
301
|
+
* @typedef {object} SchemaProperty
|
302
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
303
|
+
*/
|
304
|
+
/**
|
305
|
+
* Schema condition for component config
|
306
|
+
*
|
307
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
308
|
+
* @typedef {object} SchemaCondition
|
309
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
310
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
311
|
+
*/
|
312
|
+
/**
|
313
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
314
|
+
* @typedef ChildClass
|
315
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
316
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
317
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
318
|
+
*/
|
319
|
+
/**
|
320
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
321
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
275
322
|
*/
|
276
|
-
GOVUKFrontendComponent.elementType = HTMLElement;
|
277
323
|
|
278
324
|
/**
|
279
325
|
* Error summary component
|
@@ -282,16 +328,15 @@
|
|
282
328
|
* configuration.
|
283
329
|
*
|
284
330
|
* @preserve
|
331
|
+
* @augments ConfigurableComponent<ErrorSummaryConfig>
|
285
332
|
*/
|
286
|
-
class ErrorSummary extends
|
333
|
+
class ErrorSummary extends ConfigurableComponent {
|
287
334
|
/**
|
288
335
|
* @param {Element | null} $root - HTML element to use for error summary
|
289
336
|
* @param {ErrorSummaryConfig} [config] - Error summary config
|
290
337
|
*/
|
291
338
|
constructor($root, config = {}) {
|
292
|
-
super($root);
|
293
|
-
this.config = void 0;
|
294
|
-
this.config = mergeConfigs(ErrorSummary.defaults, config, normaliseDataset(ErrorSummary, this.$root.dataset));
|
339
|
+
super($root, config);
|
295
340
|
if (!this.config.disableAutoFocus) {
|
296
341
|
setFocus(this.$root);
|
297
342
|
}
|
@@ -358,7 +403,7 @@
|
|
358
403
|
*/
|
359
404
|
|
360
405
|
/**
|
361
|
-
* @
|
406
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
362
407
|
*/
|
363
408
|
ErrorSummary.moduleName = 'govuk-error-summary';
|
364
409
|
ErrorSummary.defaults = Object.freeze({
|
data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.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 getFragmentFromUrl(url) {
|
72
2
|
if (!url.includes('#')) {
|
73
3
|
return undefined;
|
@@ -126,46 +56,13 @@ function isObject(option) {
|
|
126
56
|
function formatErrorMessage(Component, message) {
|
127
57
|
return `${Component.moduleName}: ${message}`;
|
128
58
|
}
|
129
|
-
|
130
|
-
/**
|
131
|
-
* Schema for component config
|
132
|
-
*
|
133
|
-
* @typedef {object} Schema
|
134
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
135
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
136
|
-
*/
|
137
|
-
|
138
|
-
/**
|
139
|
-
* Schema property for component config
|
140
|
-
*
|
141
|
-
* @typedef {object} SchemaProperty
|
142
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
143
|
-
*/
|
144
|
-
|
145
|
-
/**
|
146
|
-
* Schema condition for component config
|
147
|
-
*
|
148
|
-
* @typedef {object} SchemaCondition
|
149
|
-
* @property {string[]} required - List of required config fields
|
150
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
151
|
-
*/
|
152
59
|
/**
|
153
60
|
* @typedef ComponentWithModuleName
|
154
61
|
* @property {string} moduleName - Name of the component
|
155
62
|
*/
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
for (const [field, property] of Object.entries(Component.schema.properties)) {
|
160
|
-
if (field in dataset) {
|
161
|
-
out[field] = normaliseString(dataset[field], property);
|
162
|
-
}
|
163
|
-
if ((property == null ? void 0 : property.type) === 'object') {
|
164
|
-
out[field] = extractConfigByNamespace(Component, dataset, field);
|
165
|
-
}
|
166
|
-
}
|
167
|
-
return out;
|
168
|
-
}
|
63
|
+
/**
|
64
|
+
* @import { ObjectNested } from './configuration.mjs'
|
65
|
+
*/
|
169
66
|
|
170
67
|
class GOVUKFrontendError extends Error {
|
171
68
|
constructor(...args) {
|
@@ -185,6 +82,12 @@ class SupportError extends GOVUKFrontendError {
|
|
185
82
|
this.name = 'SupportError';
|
186
83
|
}
|
187
84
|
}
|
85
|
+
class ConfigError extends GOVUKFrontendError {
|
86
|
+
constructor(...args) {
|
87
|
+
super(...args);
|
88
|
+
this.name = 'ConfigError';
|
89
|
+
}
|
90
|
+
}
|
188
91
|
class ElementError extends GOVUKFrontendError {
|
189
92
|
constructor(messageOrOptions) {
|
190
93
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
@@ -211,10 +114,10 @@ class InitError extends GOVUKFrontendError {
|
|
211
114
|
}
|
212
115
|
}
|
213
116
|
/**
|
214
|
-
* @
|
117
|
+
* @import { ComponentWithModuleName } from '../common/index.mjs'
|
215
118
|
*/
|
216
119
|
|
217
|
-
class
|
120
|
+
class Component {
|
218
121
|
/**
|
219
122
|
* Returns the root element of the component
|
220
123
|
*
|
@@ -265,9 +168,152 @@ class GOVUKFrontendComponent {
|
|
265
168
|
*/
|
266
169
|
|
267
170
|
/**
|
268
|
-
* @typedef {typeof
|
171
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
172
|
+
*/
|
173
|
+
Component.elementType = HTMLElement;
|
174
|
+
|
175
|
+
const configOverride = Symbol.for('configOverride');
|
176
|
+
class ConfigurableComponent extends Component {
|
177
|
+
[configOverride](param) {
|
178
|
+
return {};
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Returns the root element of the component
|
183
|
+
*
|
184
|
+
* @protected
|
185
|
+
* @returns {ConfigurationType} - the root element of component
|
186
|
+
*/
|
187
|
+
get config() {
|
188
|
+
return this._config;
|
189
|
+
}
|
190
|
+
constructor($root, config) {
|
191
|
+
super($root);
|
192
|
+
this._config = void 0;
|
193
|
+
const childConstructor = this.constructor;
|
194
|
+
if (!isObject(childConstructor.defaults)) {
|
195
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
196
|
+
}
|
197
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
198
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
function normaliseString(value, property) {
|
202
|
+
const trimmedValue = value ? value.trim() : '';
|
203
|
+
let output;
|
204
|
+
let outputType = property == null ? void 0 : property.type;
|
205
|
+
if (!outputType) {
|
206
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
207
|
+
outputType = 'boolean';
|
208
|
+
}
|
209
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
210
|
+
outputType = 'number';
|
211
|
+
}
|
212
|
+
}
|
213
|
+
switch (outputType) {
|
214
|
+
case 'boolean':
|
215
|
+
output = trimmedValue === 'true';
|
216
|
+
break;
|
217
|
+
case 'number':
|
218
|
+
output = Number(trimmedValue);
|
219
|
+
break;
|
220
|
+
default:
|
221
|
+
output = value;
|
222
|
+
}
|
223
|
+
return output;
|
224
|
+
}
|
225
|
+
function normaliseDataset(Component, dataset) {
|
226
|
+
if (!isObject(Component.schema)) {
|
227
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
228
|
+
}
|
229
|
+
const out = {};
|
230
|
+
const entries = Object.entries(Component.schema.properties);
|
231
|
+
for (const entry of entries) {
|
232
|
+
const [namespace, property] = entry;
|
233
|
+
const field = namespace.toString();
|
234
|
+
if (field in dataset) {
|
235
|
+
out[field] = normaliseString(dataset[field], property);
|
236
|
+
}
|
237
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
238
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
239
|
+
}
|
240
|
+
}
|
241
|
+
return out;
|
242
|
+
}
|
243
|
+
function mergeConfigs(...configObjects) {
|
244
|
+
const formattedConfigObject = {};
|
245
|
+
for (const configObject of configObjects) {
|
246
|
+
for (const key of Object.keys(configObject)) {
|
247
|
+
const option = formattedConfigObject[key];
|
248
|
+
const override = configObject[key];
|
249
|
+
if (isObject(option) && isObject(override)) {
|
250
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
251
|
+
} else {
|
252
|
+
formattedConfigObject[key] = override;
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
return formattedConfigObject;
|
257
|
+
}
|
258
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
259
|
+
const property = schema.properties[namespace];
|
260
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
261
|
+
return;
|
262
|
+
}
|
263
|
+
const newObject = {
|
264
|
+
[namespace]: {}
|
265
|
+
};
|
266
|
+
for (const [key, value] of Object.entries(dataset)) {
|
267
|
+
let current = newObject;
|
268
|
+
const keyParts = key.split('.');
|
269
|
+
for (const [index, name] of keyParts.entries()) {
|
270
|
+
if (isObject(current)) {
|
271
|
+
if (index < keyParts.length - 1) {
|
272
|
+
if (!isObject(current[name])) {
|
273
|
+
current[name] = {};
|
274
|
+
}
|
275
|
+
current = current[name];
|
276
|
+
} else if (key !== namespace) {
|
277
|
+
current[name] = normaliseString(value);
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
}
|
282
|
+
return newObject[namespace];
|
283
|
+
}
|
284
|
+
/**
|
285
|
+
* Schema for component config
|
286
|
+
*
|
287
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
288
|
+
* @typedef {object} Schema
|
289
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
290
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
291
|
+
*/
|
292
|
+
/**
|
293
|
+
* Schema property for component config
|
294
|
+
*
|
295
|
+
* @typedef {object} SchemaProperty
|
296
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
297
|
+
*/
|
298
|
+
/**
|
299
|
+
* Schema condition for component config
|
300
|
+
*
|
301
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
302
|
+
* @typedef {object} SchemaCondition
|
303
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
304
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
305
|
+
*/
|
306
|
+
/**
|
307
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
308
|
+
* @typedef ChildClass
|
309
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
310
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
311
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
312
|
+
*/
|
313
|
+
/**
|
314
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
315
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
269
316
|
*/
|
270
|
-
GOVUKFrontendComponent.elementType = HTMLElement;
|
271
317
|
|
272
318
|
/**
|
273
319
|
* Error summary component
|
@@ -276,16 +322,15 @@ GOVUKFrontendComponent.elementType = HTMLElement;
|
|
276
322
|
* configuration.
|
277
323
|
*
|
278
324
|
* @preserve
|
325
|
+
* @augments ConfigurableComponent<ErrorSummaryConfig>
|
279
326
|
*/
|
280
|
-
class ErrorSummary extends
|
327
|
+
class ErrorSummary extends ConfigurableComponent {
|
281
328
|
/**
|
282
329
|
* @param {Element | null} $root - HTML element to use for error summary
|
283
330
|
* @param {ErrorSummaryConfig} [config] - Error summary config
|
284
331
|
*/
|
285
332
|
constructor($root, config = {}) {
|
286
|
-
super($root);
|
287
|
-
this.config = void 0;
|
288
|
-
this.config = mergeConfigs(ErrorSummary.defaults, config, normaliseDataset(ErrorSummary, this.$root.dataset));
|
333
|
+
super($root, config);
|
289
334
|
if (!this.config.disableAutoFocus) {
|
290
335
|
setFocus(this.$root);
|
291
336
|
}
|
@@ -352,7 +397,7 @@ class ErrorSummary extends GOVUKFrontendComponent {
|
|
352
397
|
*/
|
353
398
|
|
354
399
|
/**
|
355
|
-
* @
|
400
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
356
401
|
*/
|
357
402
|
ErrorSummary.moduleName = 'govuk-error-summary';
|
358
403
|
ErrorSummary.defaults = Object.freeze({
|
@@ -1,6 +1,5 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs';
|
1
|
+
import { ConfigurableComponent } from '../../common/configuration.mjs';
|
2
|
+
import { setFocus, getFragmentFromUrl } from '../../common/index.mjs';
|
4
3
|
|
5
4
|
/**
|
6
5
|
* Error summary component
|
@@ -9,16 +8,15 @@ import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs';
|
|
9
8
|
* configuration.
|
10
9
|
*
|
11
10
|
* @preserve
|
11
|
+
* @augments ConfigurableComponent<ErrorSummaryConfig>
|
12
12
|
*/
|
13
|
-
class ErrorSummary extends
|
13
|
+
class ErrorSummary extends ConfigurableComponent {
|
14
14
|
/**
|
15
15
|
* @param {Element | null} $root - HTML element to use for error summary
|
16
16
|
* @param {ErrorSummaryConfig} [config] - Error summary config
|
17
17
|
*/
|
18
18
|
constructor($root, config = {}) {
|
19
|
-
super($root);
|
20
|
-
this.config = void 0;
|
21
|
-
this.config = mergeConfigs(ErrorSummary.defaults, config, normaliseDataset(ErrorSummary, this.$root.dataset));
|
19
|
+
super($root, config);
|
22
20
|
if (!this.config.disableAutoFocus) {
|
23
21
|
setFocus(this.$root);
|
24
22
|
}
|
@@ -85,7 +83,7 @@ class ErrorSummary extends GOVUKFrontendComponent {
|
|
85
83
|
*/
|
86
84
|
|
87
85
|
/**
|
88
|
-
* @
|
86
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
89
87
|
*/
|
90
88
|
ErrorSummary.moduleName = 'govuk-error-summary';
|
91
89
|
ErrorSummary.defaults = Object.freeze({
|