govuk_tech_docs 4.2.0 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/govuk_tech_docs.gemspec +1 -1
- data/lib/assets/stylesheets/_govuk_tech_docs.scss +3 -0
- data/lib/govuk_tech_docs/version.rb +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.js +508 -209
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs +505 -208
- data/node_modules/govuk-frontend/dist/govuk/all.mjs +3 -1
- data/node_modules/govuk-frontend/dist/govuk/all.scss +6 -0
- data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs +169 -0
- data/node_modules/govuk-frontend/dist/govuk/common/govuk-frontend-version.mjs +1 -1
- data/node_modules/govuk-frontend/dist/govuk/common/index.mjs +4 -87
- data/node_modules/govuk-frontend/dist/govuk/{govuk-frontend-component.mjs → component.mjs} +5 -5
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs +5 -8
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs +5 -8
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/_index.scss +8 -0
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js +187 -145
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs +186 -144
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs +18 -17
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs +6 -8
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs +5 -8
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/_index.scss +167 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.js +754 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.bundle.mjs +746 -0
- data/node_modules/govuk-frontend/dist/govuk/components/file-upload/file-upload.mjs +267 -0
- data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss +14 -10
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js +161 -116
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs +160 -115
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs +6 -8
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js +161 -117
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs +160 -116
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs +5 -9
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js +10 -30
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs +3 -3
- data/node_modules/govuk-frontend/dist/govuk/components/summary-list/_index.scss +12 -21
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js +9 -29
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs +8 -28
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs +2 -2
- data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss +1 -1
- data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js +1 -1
- data/node_modules/govuk-frontend/dist/govuk/helpers/_colour.scss +2 -2
- data/node_modules/govuk-frontend/dist/govuk/init.mjs +28 -24
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss +18 -5
- data/node_modules/govuk-frontend/dist/govuk/settings/_typography-responsive.scss +5 -10
- data/node_modules/govuk-frontend/dist/govuk-prototype-kit/init.scss +1 -1
- data/package-lock.json +8 -7
- data/package.json +1 -1
- metadata +12 -10
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-dataset.mjs +0 -18
- data/node_modules/govuk-frontend/dist/govuk/common/normalise-string.mjs +0 -31
@@ -5,6 +5,7 @@ export { CharacterCount } from './components/character-count/character-count.mjs
|
|
5
5
|
export { Checkboxes } from './components/checkboxes/checkboxes.mjs';
|
6
6
|
export { ErrorSummary } from './components/error-summary/error-summary.mjs';
|
7
7
|
export { ExitThisPage } from './components/exit-this-page/exit-this-page.mjs';
|
8
|
+
export { FileUpload } from './components/file-upload/file-upload.mjs';
|
8
9
|
export { Header } from './components/header/header.mjs';
|
9
10
|
export { NotificationBanner } from './components/notification-banner/notification-banner.mjs';
|
10
11
|
export { PasswordInput } from './components/password-input/password-input.mjs';
|
@@ -14,5 +15,6 @@ export { SkipLink } from './components/skip-link/skip-link.mjs';
|
|
14
15
|
export { Tabs } from './components/tabs/tabs.mjs';
|
15
16
|
export { createAll, initAll } from './init.mjs';
|
16
17
|
export { isSupported } from './common/index.mjs';
|
17
|
-
export {
|
18
|
+
export { Component } from './component.mjs';
|
19
|
+
export { ConfigurableComponent } from './common/configuration.mjs';
|
18
20
|
//# sourceMappingURL=all.mjs.map
|
@@ -0,0 +1,169 @@
|
|
1
|
+
import { Component } from '../component.mjs';
|
2
|
+
import { ConfigError } from '../errors/index.mjs';
|
3
|
+
import { isObject, formatErrorMessage } from './index.mjs';
|
4
|
+
|
5
|
+
const configOverride = Symbol.for('configOverride');
|
6
|
+
class ConfigurableComponent extends Component {
|
7
|
+
[configOverride](param) {
|
8
|
+
return {};
|
9
|
+
}
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Returns the root element of the component
|
13
|
+
*
|
14
|
+
* @protected
|
15
|
+
* @returns {ConfigurationType} - the root element of component
|
16
|
+
*/
|
17
|
+
get config() {
|
18
|
+
return this._config;
|
19
|
+
}
|
20
|
+
constructor($root, config) {
|
21
|
+
super($root);
|
22
|
+
this._config = void 0;
|
23
|
+
const childConstructor = this.constructor;
|
24
|
+
if (!isObject(childConstructor.defaults)) {
|
25
|
+
throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
|
26
|
+
}
|
27
|
+
const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
|
28
|
+
this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
function normaliseString(value, property) {
|
32
|
+
const trimmedValue = value ? value.trim() : '';
|
33
|
+
let output;
|
34
|
+
let outputType = property == null ? void 0 : property.type;
|
35
|
+
if (!outputType) {
|
36
|
+
if (['true', 'false'].includes(trimmedValue)) {
|
37
|
+
outputType = 'boolean';
|
38
|
+
}
|
39
|
+
if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
|
40
|
+
outputType = 'number';
|
41
|
+
}
|
42
|
+
}
|
43
|
+
switch (outputType) {
|
44
|
+
case 'boolean':
|
45
|
+
output = trimmedValue === 'true';
|
46
|
+
break;
|
47
|
+
case 'number':
|
48
|
+
output = Number(trimmedValue);
|
49
|
+
break;
|
50
|
+
default:
|
51
|
+
output = value;
|
52
|
+
}
|
53
|
+
return output;
|
54
|
+
}
|
55
|
+
function normaliseDataset(Component, dataset) {
|
56
|
+
if (!isObject(Component.schema)) {
|
57
|
+
throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
|
58
|
+
}
|
59
|
+
const out = {};
|
60
|
+
const entries = Object.entries(Component.schema.properties);
|
61
|
+
for (const entry of entries) {
|
62
|
+
const [namespace, property] = entry;
|
63
|
+
const field = namespace.toString();
|
64
|
+
if (field in dataset) {
|
65
|
+
out[field] = normaliseString(dataset[field], property);
|
66
|
+
}
|
67
|
+
if ((property == null ? void 0 : property.type) === 'object') {
|
68
|
+
out[field] = extractConfigByNamespace(Component.schema, dataset, namespace);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
return out;
|
72
|
+
}
|
73
|
+
function mergeConfigs(...configObjects) {
|
74
|
+
const formattedConfigObject = {};
|
75
|
+
for (const configObject of configObjects) {
|
76
|
+
for (const key of Object.keys(configObject)) {
|
77
|
+
const option = formattedConfigObject[key];
|
78
|
+
const override = configObject[key];
|
79
|
+
if (isObject(option) && isObject(override)) {
|
80
|
+
formattedConfigObject[key] = mergeConfigs(option, override);
|
81
|
+
} else {
|
82
|
+
formattedConfigObject[key] = override;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
return formattedConfigObject;
|
87
|
+
}
|
88
|
+
function validateConfig(schema, config) {
|
89
|
+
const validationErrors = [];
|
90
|
+
for (const [name, conditions] of Object.entries(schema)) {
|
91
|
+
const errors = [];
|
92
|
+
if (Array.isArray(conditions)) {
|
93
|
+
for (const {
|
94
|
+
required,
|
95
|
+
errorMessage
|
96
|
+
} of conditions) {
|
97
|
+
if (!required.every(key => !!config[key])) {
|
98
|
+
errors.push(errorMessage);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
|
102
|
+
validationErrors.push(...errors);
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
return validationErrors;
|
107
|
+
}
|
108
|
+
function extractConfigByNamespace(schema, dataset, namespace) {
|
109
|
+
const property = schema.properties[namespace];
|
110
|
+
if ((property == null ? void 0 : property.type) !== 'object') {
|
111
|
+
return;
|
112
|
+
}
|
113
|
+
const newObject = {
|
114
|
+
[namespace]: {}
|
115
|
+
};
|
116
|
+
for (const [key, value] of Object.entries(dataset)) {
|
117
|
+
let current = newObject;
|
118
|
+
const keyParts = key.split('.');
|
119
|
+
for (const [index, name] of keyParts.entries()) {
|
120
|
+
if (isObject(current)) {
|
121
|
+
if (index < keyParts.length - 1) {
|
122
|
+
if (!isObject(current[name])) {
|
123
|
+
current[name] = {};
|
124
|
+
}
|
125
|
+
current = current[name];
|
126
|
+
} else if (key !== namespace) {
|
127
|
+
current[name] = normaliseString(value);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
return newObject[namespace];
|
133
|
+
}
|
134
|
+
/**
|
135
|
+
* Schema for component config
|
136
|
+
*
|
137
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
138
|
+
* @typedef {object} Schema
|
139
|
+
* @property {Record<keyof ConfigurationType, SchemaProperty | undefined>} properties - Schema properties
|
140
|
+
* @property {SchemaCondition<ConfigurationType>[]} [anyOf] - List of schema conditions
|
141
|
+
*/
|
142
|
+
/**
|
143
|
+
* Schema property for component config
|
144
|
+
*
|
145
|
+
* @typedef {object} SchemaProperty
|
146
|
+
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
147
|
+
*/
|
148
|
+
/**
|
149
|
+
* Schema condition for component config
|
150
|
+
*
|
151
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} ConfigurationType
|
152
|
+
* @typedef {object} SchemaCondition
|
153
|
+
* @property {(keyof ConfigurationType)[]} required - List of required config fields
|
154
|
+
* @property {string} errorMessage - Error message when required config fields not provided
|
155
|
+
*/
|
156
|
+
/**
|
157
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
158
|
+
* @typedef ChildClass
|
159
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
160
|
+
* @property {Schema<ConfigurationType>} [schema] - The schema of the component configuration
|
161
|
+
* @property {ConfigurationType} [defaults] - The default values of the configuration of the component
|
162
|
+
*/
|
163
|
+
/**
|
164
|
+
* @template {Partial<Record<keyof ConfigurationType, unknown>>} [ConfigurationType=ObjectNested]
|
165
|
+
* @typedef {typeof Component & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
|
166
|
+
*/
|
167
|
+
|
168
|
+
export { ConfigurableComponent, configOverride, extractConfigByNamespace, mergeConfigs, normaliseDataset, normaliseString, validateConfig };
|
169
|
+
//# sourceMappingURL=configuration.mjs.map
|
@@ -1,46 +1,3 @@
|
|
1
|
-
import { normaliseString } from './normalise-string.mjs';
|
2
|
-
|
3
|
-
function mergeConfigs(...configObjects) {
|
4
|
-
const formattedConfigObject = {};
|
5
|
-
for (const configObject of configObjects) {
|
6
|
-
for (const key of Object.keys(configObject)) {
|
7
|
-
const option = formattedConfigObject[key];
|
8
|
-
const override = configObject[key];
|
9
|
-
if (isObject(option) && isObject(override)) {
|
10
|
-
formattedConfigObject[key] = mergeConfigs(option, override);
|
11
|
-
} else {
|
12
|
-
formattedConfigObject[key] = override;
|
13
|
-
}
|
14
|
-
}
|
15
|
-
}
|
16
|
-
return formattedConfigObject;
|
17
|
-
}
|
18
|
-
function extractConfigByNamespace(Component, dataset, namespace) {
|
19
|
-
const property = Component.schema.properties[namespace];
|
20
|
-
if ((property == null ? void 0 : property.type) !== 'object') {
|
21
|
-
return;
|
22
|
-
}
|
23
|
-
const newObject = {
|
24
|
-
[namespace]: ({})
|
25
|
-
};
|
26
|
-
for (const [key, value] of Object.entries(dataset)) {
|
27
|
-
let current = newObject;
|
28
|
-
const keyParts = key.split('.');
|
29
|
-
for (const [index, name] of keyParts.entries()) {
|
30
|
-
if (typeof current === 'object') {
|
31
|
-
if (index < keyParts.length - 1) {
|
32
|
-
if (!isObject(current[name])) {
|
33
|
-
current[name] = {};
|
34
|
-
}
|
35
|
-
current = current[name];
|
36
|
-
} else if (key !== namespace) {
|
37
|
-
current[name] = normaliseString(value);
|
38
|
-
}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
}
|
42
|
-
return newObject[namespace];
|
43
|
-
}
|
44
1
|
function getFragmentFromUrl(url) {
|
45
2
|
if (!url.includes('#')) {
|
46
3
|
return undefined;
|
@@ -98,26 +55,6 @@ function isSupported($scope = document.body) {
|
|
98
55
|
}
|
99
56
|
return $scope.classList.contains('govuk-frontend-supported');
|
100
57
|
}
|
101
|
-
function validateConfig(schema, config) {
|
102
|
-
const validationErrors = [];
|
103
|
-
for (const [name, conditions] of Object.entries(schema)) {
|
104
|
-
const errors = [];
|
105
|
-
if (Array.isArray(conditions)) {
|
106
|
-
for (const {
|
107
|
-
required,
|
108
|
-
errorMessage
|
109
|
-
} of conditions) {
|
110
|
-
if (!required.every(key => !!config[key])) {
|
111
|
-
errors.push(errorMessage);
|
112
|
-
}
|
113
|
-
}
|
114
|
-
if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
|
115
|
-
validationErrors.push(...errors);
|
116
|
-
}
|
117
|
-
}
|
118
|
-
}
|
119
|
-
return validationErrors;
|
120
|
-
}
|
121
58
|
function isArray(option) {
|
122
59
|
return Array.isArray(option);
|
123
60
|
}
|
@@ -127,33 +64,13 @@ function isObject(option) {
|
|
127
64
|
function formatErrorMessage(Component, message) {
|
128
65
|
return `${Component.moduleName}: ${message}`;
|
129
66
|
}
|
130
|
-
|
131
|
-
/**
|
132
|
-
* Schema for component config
|
133
|
-
*
|
134
|
-
* @typedef {object} Schema
|
135
|
-
* @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
|
136
|
-
* @property {SchemaCondition[]} [anyOf] - List of schema conditions
|
137
|
-
*/
|
138
|
-
|
139
|
-
/**
|
140
|
-
* Schema property for component config
|
141
|
-
*
|
142
|
-
* @typedef {object} SchemaProperty
|
143
|
-
* @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
|
144
|
-
*/
|
145
|
-
|
146
|
-
/**
|
147
|
-
* Schema condition for component config
|
148
|
-
*
|
149
|
-
* @typedef {object} SchemaCondition
|
150
|
-
* @property {string[]} required - List of required config fields
|
151
|
-
* @property {string} errorMessage - Error message when required config fields not provided
|
152
|
-
*/
|
153
67
|
/**
|
154
68
|
* @typedef ComponentWithModuleName
|
155
69
|
* @property {string} moduleName - Name of the component
|
156
70
|
*/
|
71
|
+
/**
|
72
|
+
* @import { ObjectNested } from './configuration.mjs'
|
73
|
+
*/
|
157
74
|
|
158
|
-
export {
|
75
|
+
export { formatErrorMessage, getBreakpoint, getFragmentFromUrl, isInitialised, isObject, isSupported, setFocus };
|
159
76
|
//# sourceMappingURL=index.mjs.map
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { isInitialised, isSupported } from './common/index.mjs';
|
2
2
|
import { InitError, ElementError, SupportError } from './errors/index.mjs';
|
3
3
|
|
4
|
-
class
|
4
|
+
class Component {
|
5
5
|
/**
|
6
6
|
* Returns the root element of the component
|
7
7
|
*
|
@@ -52,9 +52,9 @@ class GOVUKFrontendComponent {
|
|
52
52
|
*/
|
53
53
|
|
54
54
|
/**
|
55
|
-
* @typedef {typeof
|
55
|
+
* @typedef {typeof Component & ChildClass} ChildClassConstructor
|
56
56
|
*/
|
57
|
-
|
57
|
+
Component.elementType = HTMLElement;
|
58
58
|
|
59
|
-
export {
|
60
|
-
//# sourceMappingURL=
|
59
|
+
export { Component };
|
60
|
+
//# sourceMappingURL=component.mjs.map
|
@@ -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 = {}) {
|
@@ -451,15 +497,15 @@
|
|
451
497
|
* attribute, which also provides accessibility.
|
452
498
|
*
|
453
499
|
* @preserve
|
500
|
+
* @augments ConfigurableComponent<AccordionConfig>
|
454
501
|
*/
|
455
|
-
class Accordion extends
|
502
|
+
class Accordion extends ConfigurableComponent {
|
456
503
|
/**
|
457
504
|
* @param {Element | null} $root - HTML element to use for accordion
|
458
505
|
* @param {AccordionConfig} [config] - Accordion config
|
459
506
|
*/
|
460
507
|
constructor($root, config = {}) {
|
461
|
-
super($root);
|
462
|
-
this.config = void 0;
|
508
|
+
super($root, config);
|
463
509
|
this.i18n = void 0;
|
464
510
|
this.controlsClass = 'govuk-accordion__controls';
|
465
511
|
this.showAllClass = 'govuk-accordion__show-all';
|
@@ -484,7 +530,6 @@
|
|
484
530
|
this.$showAllButton = null;
|
485
531
|
this.$showAllIcon = null;
|
486
532
|
this.$showAllText = null;
|
487
|
-
this.config = mergeConfigs(Accordion.defaults, config, normaliseDataset(Accordion, this.$root.dataset));
|
488
533
|
this.i18n = new I18n(this.config.i18n);
|
489
534
|
const $sections = this.$root.querySelectorAll(`.${this.sectionClass}`);
|
490
535
|
if (!$sections.length) {
|
@@ -754,7 +799,7 @@
|
|
754
799
|
*/
|
755
800
|
|
756
801
|
/**
|
757
|
-
* @
|
802
|
+
* @import { Schema } from '../../common/configuration.mjs'
|
758
803
|
*/
|
759
804
|
Accordion.moduleName = 'govuk-accordion';
|
760
805
|
Accordion.defaults = Object.freeze({
|