govuk_publishing_components 44.1.0 → 44.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.js +336 -225
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs +334 -226
- data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/all.mjs +3 -0
- data/node_modules/govuk-frontend/dist/govuk/all.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest.svg +1 -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 +21 -1
- data/node_modules/govuk-frontend/dist/govuk/common/index.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/_index.scss +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js +92 -26
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs +92 -26
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs +12 -21
- data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js +86 -20
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs +86 -20
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs +6 -16
- data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js +89 -23
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs +89 -23
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs +10 -19
- data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js +113 -47
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs +113 -47
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs +7 -16
- data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/details/_index.scss +7 -2
- data/node_modules/govuk-frontend/dist/govuk/components/details/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js +86 -20
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs +86 -20
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs +6 -16
- data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js +87 -21
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs +87 -21
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs +7 -16
- data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/footer/_index.scss +8 -10
- data/node_modules/govuk-frontend/dist/govuk/components/footer/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss +8 -0
- data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/fixtures.json +12 -0
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js +87 -21
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs +87 -21
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs +7 -16
- data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-full-width-border.html +24 -0
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js +86 -20
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs +86 -20
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs +6 -16
- data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js +89 -23
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs +89 -23
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs +9 -18
- data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js +113 -47
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs +113 -47
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs +7 -16
- data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/README.md +15 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_index.scss +168 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_index.scss.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_service-navigation.scss +4 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_service-navigation.scss.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/fixtures.json +464 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/macro-options.json +138 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/macro.njk +3 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js +249 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs +241 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs +85 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs.map +1 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-default.html +57 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-html-navigation-items.html +49 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-large-navigation.html +153 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-long-service-name.html +20 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-navigation-with-a-current-item.html +58 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-navigation-with-an-active-item.html +58 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-non-link-navigation-items.html +49 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-link.html +20 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-name-and-navigation.html +63 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-name.html +18 -0
- data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template.njk +102 -0
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js +93 -26
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs +93 -26
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs +13 -21
- data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js +93 -27
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs +93 -27
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs +13 -22
- data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/components/warning-text/_index.scss +4 -3
- data/node_modules/govuk-frontend/dist/govuk/components/warning-text/_index.scss.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss +1 -1
- data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs +16 -3
- data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend-component.mjs +49 -5
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend-component.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css +2 -2
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js +1 -1
- data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/init.mjs +72 -10
- data/node_modules/govuk-frontend/dist/govuk/init.mjs.map +1 -1
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss +3 -0
- data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss.map +1 -1
- data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +5 -1
- data/node_modules/govuk-frontend/package.json +8 -8
- metadata +29 -4
- data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest-2x.png +0 -0
- data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest.png +0 -0
@@ -68,6 +68,19 @@ function extractConfigByNamespace(Component, dataset, namespace) {
|
|
68
68
|
}
|
69
69
|
return newObject[namespace];
|
70
70
|
}
|
71
|
+
function isInitialised($root, moduleName) {
|
72
|
+
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* Checks if GOV.UK Frontend is supported on this page
|
77
|
+
*
|
78
|
+
* Some browsers will load and run our JavaScript but GOV.UK Frontend
|
79
|
+
* won't be supported.
|
80
|
+
*
|
81
|
+
* @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support
|
82
|
+
* @returns {boolean} Whether GOV.UK Frontend is supported on this page
|
83
|
+
*/
|
71
84
|
function isSupported($scope = document.body) {
|
72
85
|
if (!$scope) {
|
73
86
|
return false;
|
@@ -80,6 +93,9 @@ function isArray(option) {
|
|
80
93
|
function isObject(option) {
|
81
94
|
return !!option && typeof option === 'object' && !isArray(option);
|
82
95
|
}
|
96
|
+
function formatErrorMessage(Component, message) {
|
97
|
+
return `${Component.moduleName}: ${message}`;
|
98
|
+
}
|
83
99
|
|
84
100
|
/**
|
85
101
|
* Schema for component config
|
@@ -103,6 +119,10 @@ function isObject(option) {
|
|
103
119
|
* @property {string[]} required - List of required config fields
|
104
120
|
* @property {string} errorMessage - Error message when required config fields not provided
|
105
121
|
*/
|
122
|
+
/**
|
123
|
+
* @typedef ComponentWithModuleName
|
124
|
+
* @property {string} moduleName - Name of the component
|
125
|
+
*/
|
106
126
|
|
107
127
|
function normaliseDataset(Component, dataset) {
|
108
128
|
const out = {};
|
@@ -140,30 +160,85 @@ class ElementError extends GOVUKFrontendError {
|
|
140
160
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
141
161
|
if (typeof messageOrOptions === 'object') {
|
142
162
|
const {
|
143
|
-
|
163
|
+
component,
|
144
164
|
identifier,
|
145
165
|
element,
|
146
166
|
expectedType
|
147
167
|
} = messageOrOptions;
|
148
|
-
message =
|
168
|
+
message = identifier;
|
149
169
|
message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found';
|
170
|
+
message = formatErrorMessage(component, message);
|
150
171
|
}
|
151
172
|
super(message);
|
152
173
|
this.name = 'ElementError';
|
153
174
|
}
|
154
175
|
}
|
176
|
+
class InitError extends GOVUKFrontendError {
|
177
|
+
constructor(componentOrMessage) {
|
178
|
+
const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
|
179
|
+
super(message);
|
180
|
+
this.name = 'InitError';
|
181
|
+
}
|
182
|
+
}
|
183
|
+
/**
|
184
|
+
* @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
|
185
|
+
*/
|
155
186
|
|
156
187
|
class GOVUKFrontendComponent {
|
157
|
-
|
158
|
-
|
188
|
+
/**
|
189
|
+
* Returns the root element of the component
|
190
|
+
*
|
191
|
+
* @protected
|
192
|
+
* @returns {RootElementType} - the root element of component
|
193
|
+
*/
|
194
|
+
get $root() {
|
195
|
+
return this._$root;
|
159
196
|
}
|
160
|
-
|
197
|
+
constructor($root) {
|
198
|
+
this._$root = void 0;
|
199
|
+
const childConstructor = this.constructor;
|
200
|
+
if (typeof childConstructor.moduleName !== 'string') {
|
201
|
+
throw new InitError(`\`moduleName\` not defined in component`);
|
202
|
+
}
|
203
|
+
if (!($root instanceof childConstructor.elementType)) {
|
204
|
+
throw new ElementError({
|
205
|
+
element: $root,
|
206
|
+
component: childConstructor,
|
207
|
+
identifier: 'Root element (`$root`)',
|
208
|
+
expectedType: childConstructor.elementType.name
|
209
|
+
});
|
210
|
+
} else {
|
211
|
+
this._$root = $root;
|
212
|
+
}
|
213
|
+
childConstructor.checkSupport();
|
214
|
+
this.checkInitialised();
|
215
|
+
const moduleName = childConstructor.moduleName;
|
216
|
+
this.$root.setAttribute(`data-${moduleName}-init`, '');
|
217
|
+
}
|
218
|
+
checkInitialised() {
|
219
|
+
const constructor = this.constructor;
|
220
|
+
const moduleName = constructor.moduleName;
|
221
|
+
if (moduleName && isInitialised(this.$root, moduleName)) {
|
222
|
+
throw new InitError(constructor);
|
223
|
+
}
|
224
|
+
}
|
225
|
+
static checkSupport() {
|
161
226
|
if (!isSupported()) {
|
162
227
|
throw new SupportError();
|
163
228
|
}
|
164
229
|
}
|
165
230
|
}
|
166
231
|
|
232
|
+
/**
|
233
|
+
* @typedef ChildClass
|
234
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
235
|
+
*/
|
236
|
+
|
237
|
+
/**
|
238
|
+
* @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor
|
239
|
+
*/
|
240
|
+
GOVUKFrontendComponent.elementType = HTMLElement;
|
241
|
+
|
167
242
|
const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
|
168
243
|
|
169
244
|
/**
|
@@ -173,25 +248,16 @@ const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
|
|
173
248
|
*/
|
174
249
|
class Button extends GOVUKFrontendComponent {
|
175
250
|
/**
|
176
|
-
* @param {Element | null} $
|
251
|
+
* @param {Element | null} $root - HTML element to use for button
|
177
252
|
* @param {ButtonConfig} [config] - Button config
|
178
253
|
*/
|
179
|
-
constructor($
|
180
|
-
super();
|
181
|
-
this.$module = void 0;
|
254
|
+
constructor($root, config = {}) {
|
255
|
+
super($root);
|
182
256
|
this.config = void 0;
|
183
257
|
this.debounceFormSubmitTimer = null;
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
element: $module,
|
188
|
-
identifier: 'Root element (`$module`)'
|
189
|
-
});
|
190
|
-
}
|
191
|
-
this.$module = $module;
|
192
|
-
this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, $module.dataset));
|
193
|
-
this.$module.addEventListener('keydown', event => this.handleKeyDown(event));
|
194
|
-
this.$module.addEventListener('click', event => this.debounce(event));
|
258
|
+
this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, this.$root.dataset));
|
259
|
+
this.$root.addEventListener('keydown', event => this.handleKeyDown(event));
|
260
|
+
this.$root.addEventListener('click', event => this.debounce(event));
|
195
261
|
}
|
196
262
|
handleKeyDown(event) {
|
197
263
|
const $target = event.target;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"button.bundle.mjs","sources":["../../../../src/govuk/common/normalise-string.mjs","../../../../src/govuk/common/index.mjs","../../../../src/govuk/common/normalise-dataset.mjs","../../../../src/govuk/errors/index.mjs","../../../../src/govuk/govuk-frontend-component.mjs","../../../../src/govuk/components/button/button.mjs"],"sourcesContent":["/**\n * Normalise string\n *\n * 'If it looks like a duck, and it quacks like a duck…' 🦆\n *\n * If the passed value looks like a boolean or a number, convert it to a boolean\n * or number.\n *\n * Designed to be used to convert config passed via data attributes (which are\n * always strings) into something sensible.\n *\n * @internal\n * @param {DOMStringMap[string]} value - The value to normalise\n * @param {SchemaProperty} [property] - Component schema property\n * @returns {string | boolean | number | undefined} Normalised data\n */\nexport function normaliseString(value, property) {\n const trimmedValue = value ? value.trim() : ''\n\n let output\n let outputType = property?.type\n\n // No schema type set? Determine automatically\n if (!outputType) {\n if (['true', 'false'].includes(trimmedValue)) {\n outputType = 'boolean'\n }\n\n // Empty / whitespace-only strings are considered finite so we need to check\n // the length of the trimmed string as well\n if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {\n outputType = 'number'\n }\n }\n\n switch (outputType) {\n case 'boolean':\n output = trimmedValue === 'true'\n break\n\n case 'number':\n output = Number(trimmedValue)\n break\n\n default:\n output = value\n }\n\n return output\n}\n\n/**\n * @typedef {import('./index.mjs').SchemaProperty} SchemaProperty\n */\n","import { normaliseString } from './normalise-string.mjs'\n\n/**\n * Common helpers which do not require polyfill.\n *\n * IMPORTANT: If a helper require a polyfill, please isolate it in its own module\n * so that the polyfill can be properly tree-shaken and does not burden\n * the components that do not need that helper\n */\n\n/**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @internal\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\nexport function mergeConfigs(...configObjects) {\n // Start with an empty object as our base\n /** @type {{ [key: string]: unknown }} */\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (isObject(option) && isObject(override)) {\n // @ts-expect-error Index signature for type 'string' is missing\n formattedConfigObject[key] = mergeConfigs(option, override)\n } else {\n // Apply override\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n}\n\n/**\n * Extracts keys starting with a particular namespace from dataset ('data-*')\n * object, removing the namespace in the process, normalising all values\n *\n * @internal\n * @param {{ schema: Schema }} Component - Component class\n * @param {DOMStringMap} dataset - The object to extract key-value pairs from\n * @param {string} namespace - The namespace to filter keys with\n * @returns {ObjectNested | undefined} Nested object with dot-separated key namespace removed\n */\nexport function extractConfigByNamespace(Component, dataset, namespace) {\n const property = Component.schema.properties[namespace]\n\n // Only extract configs for object schema properties\n if (property?.type !== 'object') {\n return\n }\n\n // Add default empty config\n const newObject = {\n [namespace]: /** @type {ObjectNested} */ ({})\n }\n\n for (const [key, value] of Object.entries(dataset)) {\n /** @type {ObjectNested | ObjectNested[NestedKey]} */\n let current = newObject\n\n // Split the key into parts, using . as our namespace separator\n const keyParts = key.split('.')\n\n /**\n * Create new level per part\n *\n * e.g. 'i18n.textareaDescription.other' becomes\n * `{ i18n: { textareaDescription: { other } } }`\n */\n for (const [index, name] of keyParts.entries()) {\n if (typeof current === 'object') {\n // Drop down to nested object until the last part\n if (index < keyParts.length - 1) {\n // New nested object (optionally) replaces existing value\n if (!isObject(current[name])) {\n current[name] = {}\n }\n\n // Drop down into new or existing nested object\n current = current[name]\n } else if (key !== namespace) {\n // Normalised value (optionally) replaces existing value\n current[name] = normaliseString(value)\n }\n }\n }\n }\n\n return newObject[namespace]\n}\n\n/**\n * Get hash fragment from URL\n *\n * Extract the hash fragment (everything after the hash) from a URL,\n * but not including the hash symbol\n *\n * @private\n * @param {string} url - URL\n * @returns {string | undefined} Fragment from URL, without the hash\n */\nexport function getFragmentFromUrl(url) {\n if (!url.includes('#')) {\n return undefined\n }\n\n return url.split('#').pop()\n}\n\n/**\n * Get GOV.UK Frontend breakpoint value from CSS custom property\n *\n * @private\n * @param {string} name - Breakpoint name\n * @returns {{ property: string, value?: string }} Breakpoint object\n */\nexport function getBreakpoint(name) {\n const property = `--govuk-frontend-breakpoint-${name}`\n\n // Get value from `<html>` with breakpoints on CSS :root\n const value = window\n .getComputedStyle(document.documentElement)\n .getPropertyValue(property)\n\n return {\n property,\n value: value || undefined\n }\n}\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @private\n * @template {HTMLElement} FocusElement\n * @param {FocusElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: FocusElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: FocusElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n options.onBlur?.call($element)\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n options.onBeforeFocus?.call($element)\n $element.focus()\n}\n\n/**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * Some browsers will load and run our JavaScript but GOV.UK Frontend\n * won't be supported.\n *\n * @internal\n * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support\n * @returns {boolean} Whether GOV.UK Frontend is supported on this page\n */\nexport function isSupported($scope = document.body) {\n if (!$scope) {\n return false\n }\n\n return $scope.classList.contains('govuk-frontend-supported')\n}\n\n/**\n * Validate component config by schema\n *\n * Follows limited examples in JSON schema for wider support in future\n *\n * {@link https://ajv.js.org/json-schema.html#compound-keywords}\n * {@link https://ajv.js.org/packages/ajv-errors.html#single-message}\n *\n * @internal\n * @param {Schema} schema - Config schema\n * @param {{ [key: string]: unknown }} config - Component config\n * @returns {string[]} List of validation errors\n */\nexport function validateConfig(schema, config) {\n const validationErrors = []\n\n // Check errors for each schema\n for (const [name, conditions] of Object.entries(schema)) {\n const errors = []\n\n // Check errors for each schema condition\n if (Array.isArray(conditions)) {\n for (const { required, errorMessage } of conditions) {\n if (!required.every((key) => !!config[key])) {\n errors.push(errorMessage) // Missing config key value\n }\n }\n\n // Check one condition passes or add errors\n if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {\n validationErrors.push(...errors)\n }\n }\n }\n\n return validationErrors\n}\n\n/**\n * Check for an array\n *\n * @internal\n * @param {unknown} option - Option to check\n * @returns {boolean} Whether the option is an array\n */\nfunction isArray(option) {\n return Array.isArray(option)\n}\n\n/**\n * Check for an object\n *\n * @internal\n * @param {unknown} option - Option to check\n * @returns {boolean} Whether the option is an object\n */\nfunction isObject(option) {\n return !!option && typeof option === 'object' && !isArray(option)\n}\n\n/**\n * Schema for component config\n *\n * @typedef {object} Schema\n * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties\n * @property {SchemaCondition[]} [anyOf] - List of schema conditions\n */\n\n/**\n * Schema property for component config\n *\n * @typedef {object} SchemaProperty\n * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type\n */\n\n/**\n * Schema condition for component config\n *\n * @typedef {object} SchemaCondition\n * @property {string[]} required - List of required config fields\n * @property {string} errorMessage - Error message when required config fields not provided\n */\n\n/**\n * @internal\n * @typedef {keyof ObjectNested} NestedKey\n * @typedef {{ [key: string]: string | boolean | number | ObjectNested | undefined }} ObjectNested\n */\n","import { extractConfigByNamespace } from './index.mjs'\nimport { normaliseString } from './normalise-string.mjs'\n\n/**\n * Normalise dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @internal\n * @param {{ schema: Schema }} Component - Component class\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {ObjectNested} Normalised dataset\n */\nexport function normaliseDataset(Component, dataset) {\n const out = /** @type {ReturnType<typeof normaliseDataset>} */ ({})\n\n // Normalise top-level dataset ('data-*') values using schema types\n for (const [field, property] of Object.entries(Component.schema.properties)) {\n if (field in dataset) {\n out[field] = normaliseString(dataset[field], property)\n }\n\n /**\n * Extract and normalise nested object values automatically using\n * {@link normaliseString} but only schema object types are allowed\n */\n if (property?.type === 'object') {\n out[field] = extractConfigByNamespace(Component, dataset, field)\n }\n }\n\n return out\n}\n\n/**\n * @internal\n * @typedef {import('./index.mjs').ObjectNested} ObjectNested\n * @typedef {import('./index.mjs').Schema} Schema\n */\n","/**\n * GOV.UK Frontend error\n *\n * A base class for `Error`s thrown by GOV.UK Frontend.\n *\n * It is meant to be extended into specific types of errors\n * to be thrown by our code.\n *\n * @example\n * ```js\n * class MissingRootError extends GOVUKFrontendError {\n * // Setting an explicit name is important as extending the class will not\n * // set a new `name` on the subclass. The `name` property is important\n * // to ensure intelligible error names even if the class name gets\n * // mangled by a minifier\n * name = \"MissingRootError\"\n * }\n * ```\n * @abstract\n */\nexport class GOVUKFrontendError extends Error {\n name = 'GOVUKFrontendError'\n}\n\n/**\n * Indicates that GOV.UK Frontend is not supported\n */\nexport class SupportError extends GOVUKFrontendError {\n name = 'SupportError'\n\n /**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support\n */\n constructor($scope = document.body) {\n const supportMessage =\n 'noModule' in HTMLScriptElement.prototype\n ? 'GOV.UK Frontend initialised without `<body class=\"govuk-frontend-supported\">` from template `<script>` snippet'\n : 'GOV.UK Frontend is not supported in this browser'\n\n super(\n $scope\n ? supportMessage\n : 'GOV.UK Frontend initialised without `<script type=\"module\">`'\n )\n }\n}\n\n/**\n * Indicates that a component has received an illegal configuration\n */\nexport class ConfigError extends GOVUKFrontendError {\n name = 'ConfigError'\n}\n\n/**\n * Indicates an issue with an element (possibly `null` or `undefined`)\n */\nexport class ElementError extends GOVUKFrontendError {\n name = 'ElementError'\n\n /**\n * @internal\n * @overload\n * @param {string} message - Element error message\n */\n\n /**\n * @internal\n * @overload\n * @param {ElementErrorOptions} options - Element error options\n */\n\n /**\n * @internal\n * @param {string | ElementErrorOptions} messageOrOptions - Element error message or options\n */\n constructor(messageOrOptions) {\n let message = typeof messageOrOptions === 'string' ? messageOrOptions : ''\n\n // Build message from options\n if (typeof messageOrOptions === 'object') {\n const { componentName, identifier, element, expectedType } =\n messageOrOptions\n\n // Add prefix and identifier\n message = `${componentName}: ${identifier}`\n\n // Append reason\n message += element\n ? ` is not of type ${expectedType ?? 'HTMLElement'}`\n : ' not found'\n }\n\n super(message)\n }\n}\n\n/**\n * Element error options\n *\n * @internal\n * @typedef {object} ElementErrorOptions\n * @property {string} componentName - The name of the component throwing the error\n * @property {string} identifier - An identifier that'll let the user understand which element has an error. This is whatever makes the most sense\n * @property {Element | null} [element] - The element in error\n * @property {string} [expectedType] - The type that was expected for the identifier\n */\n","import { isSupported } from './common/index.mjs'\nimport { SupportError } from './errors/index.mjs'\n\n/**\n * Base Component class\n *\n * Centralises the behaviours shared by our components\n *\n * @internal\n * @abstract\n */\nexport class GOVUKFrontendComponent {\n /**\n * Constructs a new component, validating that GOV.UK Frontend is supported\n *\n * @internal\n */\n constructor() {\n this.checkSupport()\n }\n\n /**\n * Validates whether GOV.UK Frontend is supported\n *\n * @private\n * @throws {SupportError} when GOV.UK Frontend is not supported\n */\n checkSupport() {\n if (!isSupported()) {\n throw new SupportError()\n }\n }\n}\n","import { mergeConfigs } from '../../common/index.mjs'\nimport { normaliseDataset } from '../../common/normalise-dataset.mjs'\nimport { ElementError } from '../../errors/index.mjs'\nimport { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'\n\nconst DEBOUNCE_TIMEOUT_IN_SECONDS = 1\n\n/**\n * JavaScript enhancements for the Button component\n *\n * @preserve\n */\nexport class Button extends GOVUKFrontendComponent {\n /** @private */\n $module\n\n /**\n * @private\n * @type {ButtonConfig}\n */\n config\n\n /**\n * @private\n * @type {number | null}\n */\n debounceFormSubmitTimer = null\n\n /**\n * @param {Element | null} $module - HTML element to use for button\n * @param {ButtonConfig} [config] - Button config\n */\n constructor($module, config = {}) {\n super()\n\n if (!($module instanceof HTMLElement)) {\n throw new ElementError({\n componentName: 'Button',\n element: $module,\n identifier: 'Root element (`$module`)'\n })\n }\n\n this.$module = $module\n\n this.config = mergeConfigs(\n Button.defaults,\n config,\n normaliseDataset(Button, $module.dataset)\n )\n\n this.$module.addEventListener('keydown', (event) =>\n this.handleKeyDown(event)\n )\n this.$module.addEventListener('click', (event) => this.debounce(event))\n }\n\n /**\n * Trigger a click event when the space key is pressed\n *\n * Some screen readers tell users they can use the space bar to activate\n * things with the 'button' role, so we need to match the functionality of\n * native HTML buttons.\n *\n * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270\n *\n * @private\n * @param {KeyboardEvent} event - Keydown event\n */\n handleKeyDown(event) {\n const $target = event.target\n\n // Handle space bar only\n if (event.key !== ' ') {\n return\n }\n\n // Handle elements with [role=\"button\"] only\n if (\n $target instanceof HTMLElement &&\n $target.getAttribute('role') === 'button'\n ) {\n event.preventDefault() // prevent the page from scrolling\n $target.click()\n }\n }\n\n /**\n * Debounce double-clicks\n *\n * If the click quickly succeeds a previous click then nothing will happen.\n * This stops people accidentally causing multiple form submissions by double\n * clicking buttons.\n *\n * @private\n * @param {MouseEvent} event - Mouse click event\n * @returns {undefined | false} Returns undefined, or false when debounced\n */\n debounce(event) {\n // Check the button that was clicked has preventDoubleClick enabled\n if (!this.config.preventDoubleClick) {\n return\n }\n\n // If the timer is still running, prevent the click from submitting the form\n if (this.debounceFormSubmitTimer) {\n event.preventDefault()\n return false\n }\n\n this.debounceFormSubmitTimer = window.setTimeout(() => {\n this.debounceFormSubmitTimer = null\n }, DEBOUNCE_TIMEOUT_IN_SECONDS * 1000)\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-button'\n\n /**\n * Button default config\n *\n * @see {@link ButtonConfig}\n * @constant\n * @type {ButtonConfig}\n */\n static defaults = Object.freeze({\n preventDoubleClick: false\n })\n\n /**\n * Button config schema\n *\n * @constant\n * @satisfies {Schema}\n */\n static schema = Object.freeze({\n properties: {\n preventDoubleClick: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Button config\n *\n * @typedef {object} ButtonConfig\n * @property {boolean} [preventDoubleClick=false] - Prevent accidental double\n * clicks on submit buttons from submitting forms multiple times.\n */\n\n/**\n * @typedef {import('../../common/index.mjs').Schema} Schema\n */\n"],"names":["normaliseString","value","property","trimmedValue","trim","output","outputType","type","includes","length","isFinite","Number","mergeConfigs","configObjects","formattedConfigObject","configObject","key","Object","keys","option","override","isObject","extractConfigByNamespace","Component","dataset","namespace","schema","properties","newObject","entries","current","keyParts","split","index","name","isSupported","$scope","document","body","classList","contains","isArray","Array","normaliseDataset","out","field","GOVUKFrontendError","Error","constructor","args","SupportError","supportMessage","HTMLScriptElement","prototype","ElementError","messageOrOptions","message","componentName","identifier","element","expectedType","GOVUKFrontendComponent","checkSupport","DEBOUNCE_TIMEOUT_IN_SECONDS","Button","$module","config","debounceFormSubmitTimer","HTMLElement","defaults","addEventListener","event","handleKeyDown","debounce","$target","target","getAttribute","preventDefault","click","preventDoubleClick","window","setTimeout","moduleName","freeze"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"button.bundle.mjs","sources":["../../../../src/govuk/common/normalise-string.mjs","../../../../src/govuk/common/index.mjs","../../../../src/govuk/common/normalise-dataset.mjs","../../../../src/govuk/errors/index.mjs","../../../../src/govuk/govuk-frontend-component.mjs","../../../../src/govuk/components/button/button.mjs"],"sourcesContent":["/**\n * Normalise string\n *\n * 'If it looks like a duck, and it quacks like a duck…' 🦆\n *\n * If the passed value looks like a boolean or a number, convert it to a boolean\n * or number.\n *\n * Designed to be used to convert config passed via data attributes (which are\n * always strings) into something sensible.\n *\n * @internal\n * @param {DOMStringMap[string]} value - The value to normalise\n * @param {SchemaProperty} [property] - Component schema property\n * @returns {string | boolean | number | undefined} Normalised data\n */\nexport function normaliseString(value, property) {\n const trimmedValue = value ? value.trim() : ''\n\n let output\n let outputType = property?.type\n\n // No schema type set? Determine automatically\n if (!outputType) {\n if (['true', 'false'].includes(trimmedValue)) {\n outputType = 'boolean'\n }\n\n // Empty / whitespace-only strings are considered finite so we need to check\n // the length of the trimmed string as well\n if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {\n outputType = 'number'\n }\n }\n\n switch (outputType) {\n case 'boolean':\n output = trimmedValue === 'true'\n break\n\n case 'number':\n output = Number(trimmedValue)\n break\n\n default:\n output = value\n }\n\n return output\n}\n\n/**\n * @typedef {import('./index.mjs').SchemaProperty} SchemaProperty\n */\n","import { normaliseString } from './normalise-string.mjs'\n\n/**\n * Common helpers which do not require polyfill.\n *\n * IMPORTANT: If a helper require a polyfill, please isolate it in its own module\n * so that the polyfill can be properly tree-shaken and does not burden\n * the components that do not need that helper\n */\n\n/**\n * Config merging function\n *\n * Takes any number of objects and combines them together, with\n * greatest priority on the LAST item passed in.\n *\n * @internal\n * @param {...{ [key: string]: unknown }} configObjects - Config objects to merge\n * @returns {{ [key: string]: unknown }} A merged config object\n */\nexport function mergeConfigs(...configObjects) {\n // Start with an empty object as our base\n /** @type {{ [key: string]: unknown }} */\n const formattedConfigObject = {}\n\n // Loop through each of the passed objects\n for (const configObject of configObjects) {\n for (const key of Object.keys(configObject)) {\n const option = formattedConfigObject[key]\n const override = configObject[key]\n\n // Push their keys one-by-one into formattedConfigObject. Any duplicate\n // keys with object values will be merged, otherwise the new value will\n // override the existing value.\n if (isObject(option) && isObject(override)) {\n // @ts-expect-error Index signature for type 'string' is missing\n formattedConfigObject[key] = mergeConfigs(option, override)\n } else {\n // Apply override\n formattedConfigObject[key] = override\n }\n }\n }\n\n return formattedConfigObject\n}\n\n/**\n * Extracts keys starting with a particular namespace from dataset ('data-*')\n * object, removing the namespace in the process, normalising all values\n *\n * @internal\n * @param {{ schema: Schema }} Component - Component class\n * @param {DOMStringMap} dataset - The object to extract key-value pairs from\n * @param {string} namespace - The namespace to filter keys with\n * @returns {ObjectNested | undefined} Nested object with dot-separated key namespace removed\n */\nexport function extractConfigByNamespace(Component, dataset, namespace) {\n const property = Component.schema.properties[namespace]\n\n // Only extract configs for object schema properties\n if (property?.type !== 'object') {\n return\n }\n\n // Add default empty config\n const newObject = {\n [namespace]: /** @type {ObjectNested} */ ({})\n }\n\n for (const [key, value] of Object.entries(dataset)) {\n /** @type {ObjectNested | ObjectNested[NestedKey]} */\n let current = newObject\n\n // Split the key into parts, using . as our namespace separator\n const keyParts = key.split('.')\n\n /**\n * Create new level per part\n *\n * e.g. 'i18n.textareaDescription.other' becomes\n * `{ i18n: { textareaDescription: { other } } }`\n */\n for (const [index, name] of keyParts.entries()) {\n if (typeof current === 'object') {\n // Drop down to nested object until the last part\n if (index < keyParts.length - 1) {\n // New nested object (optionally) replaces existing value\n if (!isObject(current[name])) {\n current[name] = {}\n }\n\n // Drop down into new or existing nested object\n current = current[name]\n } else if (key !== namespace) {\n // Normalised value (optionally) replaces existing value\n current[name] = normaliseString(value)\n }\n }\n }\n }\n\n return newObject[namespace]\n}\n\n/**\n * Get hash fragment from URL\n *\n * Extract the hash fragment (everything after the hash) from a URL,\n * but not including the hash symbol\n *\n * @private\n * @param {string} url - URL\n * @returns {string | undefined} Fragment from URL, without the hash\n */\nexport function getFragmentFromUrl(url) {\n if (!url.includes('#')) {\n return undefined\n }\n\n return url.split('#').pop()\n}\n\n/**\n * Get GOV.UK Frontend breakpoint value from CSS custom property\n *\n * @private\n * @param {string} name - Breakpoint name\n * @returns {{ property: string, value?: string }} Breakpoint object\n */\nexport function getBreakpoint(name) {\n const property = `--govuk-frontend-breakpoint-${name}`\n\n // Get value from `<html>` with breakpoints on CSS :root\n const value = window\n .getComputedStyle(document.documentElement)\n .getPropertyValue(property)\n\n return {\n property,\n value: value || undefined\n }\n}\n\n/**\n * Move focus to element\n *\n * Sets tabindex to -1 to make the element programmatically focusable,\n * but removes it on blur as the element doesn't need to be focused again.\n *\n * @private\n * @template {HTMLElement} FocusElement\n * @param {FocusElement} $element - HTML element\n * @param {object} [options] - Handler options\n * @param {function(this: FocusElement): void} [options.onBeforeFocus] - Callback before focus\n * @param {function(this: FocusElement): void} [options.onBlur] - Callback on blur\n */\nexport function setFocus($element, options = {}) {\n const isFocusable = $element.getAttribute('tabindex')\n\n if (!isFocusable) {\n $element.setAttribute('tabindex', '-1')\n }\n\n /**\n * Handle element focus\n */\n function onFocus() {\n $element.addEventListener('blur', onBlur, { once: true })\n }\n\n /**\n * Handle element blur\n */\n function onBlur() {\n options.onBlur?.call($element)\n\n if (!isFocusable) {\n $element.removeAttribute('tabindex')\n }\n }\n\n // Add listener to reset element on blur, after focus\n $element.addEventListener('focus', onFocus, { once: true })\n\n // Focus element\n options.onBeforeFocus?.call($element)\n $element.focus()\n}\n\n/**\n * Checks if component is already initialised\n *\n * @internal\n * @param {Element} $root - HTML element to be checked\n * @param {string} moduleName - name of component module\n * @returns {boolean} Whether component is already initialised\n */\nexport function isInitialised($root, moduleName) {\n return (\n $root instanceof HTMLElement &&\n $root.hasAttribute(`data-${moduleName}-init`)\n )\n}\n\n/**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * Some browsers will load and run our JavaScript but GOV.UK Frontend\n * won't be supported.\n *\n * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support\n * @returns {boolean} Whether GOV.UK Frontend is supported on this page\n */\nexport function isSupported($scope = document.body) {\n if (!$scope) {\n return false\n }\n\n return $scope.classList.contains('govuk-frontend-supported')\n}\n\n/**\n * Validate component config by schema\n *\n * Follows limited examples in JSON schema for wider support in future\n *\n * {@link https://ajv.js.org/json-schema.html#compound-keywords}\n * {@link https://ajv.js.org/packages/ajv-errors.html#single-message}\n *\n * @internal\n * @param {Schema} schema - Config schema\n * @param {{ [key: string]: unknown }} config - Component config\n * @returns {string[]} List of validation errors\n */\nexport function validateConfig(schema, config) {\n const validationErrors = []\n\n // Check errors for each schema\n for (const [name, conditions] of Object.entries(schema)) {\n const errors = []\n\n // Check errors for each schema condition\n if (Array.isArray(conditions)) {\n for (const { required, errorMessage } of conditions) {\n if (!required.every((key) => !!config[key])) {\n errors.push(errorMessage) // Missing config key value\n }\n }\n\n // Check one condition passes or add errors\n if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {\n validationErrors.push(...errors)\n }\n }\n }\n\n return validationErrors\n}\n\n/**\n * Check for an array\n *\n * @internal\n * @param {unknown} option - Option to check\n * @returns {boolean} Whether the option is an array\n */\nfunction isArray(option) {\n return Array.isArray(option)\n}\n\n/**\n * Check for an object\n *\n * @internal\n * @param {unknown} option - Option to check\n * @returns {boolean} Whether the option is an object\n */\nfunction isObject(option) {\n return !!option && typeof option === 'object' && !isArray(option)\n}\n\n/**\n * Format error message\n *\n * @internal\n * @param {ComponentWithModuleName} Component - Component that threw the error\n * @param {string} message - Error message\n * @returns {string} - Formatted error message\n */\nexport function formatErrorMessage(Component, message) {\n return `${Component.moduleName}: ${message}`\n}\n\n/**\n * Schema for component config\n *\n * @typedef {object} Schema\n * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties\n * @property {SchemaCondition[]} [anyOf] - List of schema conditions\n */\n\n/**\n * Schema property for component config\n *\n * @typedef {object} SchemaProperty\n * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type\n */\n\n/**\n * Schema condition for component config\n *\n * @typedef {object} SchemaCondition\n * @property {string[]} required - List of required config fields\n * @property {string} errorMessage - Error message when required config fields not provided\n */\n\n/**\n * @internal\n * @typedef {keyof ObjectNested} NestedKey\n * @typedef {{ [key: string]: string | boolean | number | ObjectNested | undefined }} ObjectNested\n */\n\n/* eslint-disable jsdoc/valid-types --\n * `{new(...args: any[] ): object}` is not recognised as valid\n * https://github.com/gajus/eslint-plugin-jsdoc/issues/145#issuecomment-1308722878\n * https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131\n **/\n\n/**\n * @typedef ComponentWithModuleName\n * @property {string} moduleName - Name of the component\n */\n\n/* eslint-enable jsdoc/valid-types */\n","import { extractConfigByNamespace } from './index.mjs'\nimport { normaliseString } from './normalise-string.mjs'\n\n/**\n * Normalise dataset\n *\n * Loop over an object and normalise each value using {@link normaliseString},\n * optionally expanding nested `i18n.field`\n *\n * @internal\n * @param {{ schema: Schema }} Component - Component class\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {ObjectNested} Normalised dataset\n */\nexport function normaliseDataset(Component, dataset) {\n const out = /** @type {ReturnType<typeof normaliseDataset>} */ ({})\n\n // Normalise top-level dataset ('data-*') values using schema types\n for (const [field, property] of Object.entries(Component.schema.properties)) {\n if (field in dataset) {\n out[field] = normaliseString(dataset[field], property)\n }\n\n /**\n * Extract and normalise nested object values automatically using\n * {@link normaliseString} but only schema object types are allowed\n */\n if (property?.type === 'object') {\n out[field] = extractConfigByNamespace(Component, dataset, field)\n }\n }\n\n return out\n}\n\n/**\n * @internal\n * @typedef {import('./index.mjs').ObjectNested} ObjectNested\n * @typedef {import('./index.mjs').Schema} Schema\n */\n","import { formatErrorMessage } from '../common/index.mjs'\n\n/**\n * GOV.UK Frontend error\n *\n * A base class for `Error`s thrown by GOV.UK Frontend.\n *\n * It is meant to be extended into specific types of errors\n * to be thrown by our code.\n *\n * @example\n * ```js\n * class MissingRootError extends GOVUKFrontendError {\n * // Setting an explicit name is important as extending the class will not\n * // set a new `name` on the subclass. The `name` property is important\n * // to ensure intelligible error names even if the class name gets\n * // mangled by a minifier\n * name = \"MissingRootError\"\n * }\n * ```\n * @virtual\n */\nexport class GOVUKFrontendError extends Error {\n name = 'GOVUKFrontendError'\n}\n\n/**\n * Indicates that GOV.UK Frontend is not supported\n */\nexport class SupportError extends GOVUKFrontendError {\n name = 'SupportError'\n\n /**\n * Checks if GOV.UK Frontend is supported on this page\n *\n * @param {HTMLElement | null} [$scope] - HTML element `<body>` checked for browser support\n */\n constructor($scope = document.body) {\n const supportMessage =\n 'noModule' in HTMLScriptElement.prototype\n ? 'GOV.UK Frontend initialised without `<body class=\"govuk-frontend-supported\">` from template `<script>` snippet'\n : 'GOV.UK Frontend is not supported in this browser'\n\n super(\n $scope\n ? supportMessage\n : 'GOV.UK Frontend initialised without `<script type=\"module\">`'\n )\n }\n}\n\n/**\n * Indicates that a component has received an illegal configuration\n */\nexport class ConfigError extends GOVUKFrontendError {\n name = 'ConfigError'\n}\n\n/**\n * Indicates an issue with an element (possibly `null` or `undefined`)\n */\nexport class ElementError extends GOVUKFrontendError {\n name = 'ElementError'\n\n /**\n * @internal\n * @overload\n * @param {string} message - Element error message\n */\n\n /**\n * @internal\n * @overload\n * @param {ElementErrorOptions} options - Element error options\n */\n\n /**\n * @internal\n * @param {string | ElementErrorOptions} messageOrOptions - Element error message or options\n */\n constructor(messageOrOptions) {\n let message = typeof messageOrOptions === 'string' ? messageOrOptions : ''\n\n // Build message from options\n if (typeof messageOrOptions === 'object') {\n const { component, identifier, element, expectedType } = messageOrOptions\n\n message = identifier\n\n // Append reason\n message += element\n ? ` is not of type ${expectedType ?? 'HTMLElement'}`\n : ' not found'\n\n message = formatErrorMessage(component, message)\n }\n\n super(message)\n }\n}\n\n/**\n * Indicates that a component is already initialised\n */\nexport class InitError extends GOVUKFrontendError {\n name = 'InitError'\n\n /**\n * @internal\n * @param {ComponentWithModuleName | string} componentOrMessage - name of the component module\n */\n constructor(componentOrMessage) {\n const message =\n typeof componentOrMessage === 'string'\n ? componentOrMessage\n : formatErrorMessage(\n componentOrMessage,\n `Root element (\\`$root\\`) already initialised`\n )\n\n super(message)\n }\n}\n\n/**\n * Element error options\n *\n * @internal\n * @typedef {object} ElementErrorOptions\n * @property {string} identifier - An identifier that'll let the user understand which element has an error. This is whatever makes the most sense\n * @property {Element | null} [element] - The element in error\n * @property {string} [expectedType] - The type that was expected for the identifier\n * @property {ComponentWithModuleName} component - Component throwing the error\n */\n\n/**\n * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName\n */\n","import { isInitialised, isSupported } from './common/index.mjs'\nimport { ElementError, InitError, SupportError } from './errors/index.mjs'\n\n/**\n * Base Component class\n *\n * Centralises the behaviours shared by our components\n *\n * @virtual\n * @template {Element} [RootElementType=HTMLElement]\n */\nexport class GOVUKFrontendComponent {\n /**\n * @type {typeof Element}\n */\n static elementType = HTMLElement\n\n // allows Typescript user to work around the lack of types\n // in GOVUKFrontend package, Typescript is not aware of $root\n // in components that extend GOVUKFrontendComponent\n /**\n * Returns the root element of the component\n *\n * @protected\n * @returns {RootElementType} - the root element of component\n */\n get $root() {\n return this._$root\n }\n\n /**\n * @protected\n * @type {RootElementType}\n */\n _$root\n\n /**\n * Constructs a new component, validating that GOV.UK Frontend is supported\n *\n * @internal\n * @param {Element | null} [$root] - HTML element to use for component\n */\n constructor($root) {\n const childConstructor = /** @type {ChildClassConstructor} */ (\n this.constructor\n )\n\n // TypeScript does not enforce that inheriting classes will define a `moduleName`\n // (even if we add a `@virtual` `static moduleName` property to this class).\n // While we trust users to do this correctly, we do a little check to provide them\n // a helpful error message.\n //\n // After this, we'll be sure that `childConstructor` has a `moduleName`\n // as expected of the `ChildClassConstructor` we've cast `this.constructor` to.\n if (typeof childConstructor.moduleName !== 'string') {\n throw new InitError(`\\`moduleName\\` not defined in component`)\n }\n\n if (!($root instanceof childConstructor.elementType)) {\n throw new ElementError({\n element: $root,\n component: childConstructor,\n identifier: 'Root element (`$root`)',\n expectedType: childConstructor.elementType.name\n })\n } else {\n this._$root = /** @type {RootElementType} */ ($root)\n }\n\n childConstructor.checkSupport()\n\n this.checkInitialised()\n\n const moduleName = childConstructor.moduleName\n\n this.$root.setAttribute(`data-${moduleName}-init`, '')\n }\n\n /**\n * Validates whether component is already initialised\n *\n * @private\n * @throws {InitError} when component is already initialised\n */\n checkInitialised() {\n const constructor = /** @type {ChildClassConstructor} */ (this.constructor)\n const moduleName = constructor.moduleName\n\n if (moduleName && isInitialised(this.$root, moduleName)) {\n throw new InitError(constructor)\n }\n }\n\n /**\n * Validates whether components are supported\n *\n * @throws {SupportError} when the components are not supported\n */\n static checkSupport() {\n if (!isSupported()) {\n throw new SupportError()\n }\n }\n}\n\n/**\n * @typedef ChildClass\n * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component\n */\n\n/**\n * @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor\n */\n","import { mergeConfigs } from '../../common/index.mjs'\nimport { normaliseDataset } from '../../common/normalise-dataset.mjs'\nimport { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'\n\nconst DEBOUNCE_TIMEOUT_IN_SECONDS = 1\n\n/**\n * JavaScript enhancements for the Button component\n *\n * @preserve\n */\nexport class Button extends GOVUKFrontendComponent {\n /**\n * @private\n * @type {ButtonConfig}\n */\n config\n\n /**\n * @private\n * @type {number | null}\n */\n debounceFormSubmitTimer = null\n\n /**\n * @param {Element | null} $root - HTML element to use for button\n * @param {ButtonConfig} [config] - Button config\n */\n constructor($root, config = {}) {\n super($root)\n\n this.config = mergeConfigs(\n Button.defaults,\n config,\n normaliseDataset(Button, this.$root.dataset)\n )\n\n this.$root.addEventListener('keydown', (event) => this.handleKeyDown(event))\n this.$root.addEventListener('click', (event) => this.debounce(event))\n }\n\n /**\n * Trigger a click event when the space key is pressed\n *\n * Some screen readers tell users they can use the space bar to activate\n * things with the 'button' role, so we need to match the functionality of\n * native HTML buttons.\n *\n * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270\n *\n * @private\n * @param {KeyboardEvent} event - Keydown event\n */\n handleKeyDown(event) {\n const $target = event.target\n\n // Handle space bar only\n if (event.key !== ' ') {\n return\n }\n\n // Handle elements with [role=\"button\"] only\n if (\n $target instanceof HTMLElement &&\n $target.getAttribute('role') === 'button'\n ) {\n event.preventDefault() // prevent the page from scrolling\n $target.click()\n }\n }\n\n /**\n * Debounce double-clicks\n *\n * If the click quickly succeeds a previous click then nothing will happen.\n * This stops people accidentally causing multiple form submissions by double\n * clicking buttons.\n *\n * @private\n * @param {MouseEvent} event - Mouse click event\n * @returns {undefined | false} Returns undefined, or false when debounced\n */\n debounce(event) {\n // Check the button that was clicked has preventDoubleClick enabled\n if (!this.config.preventDoubleClick) {\n return\n }\n\n // If the timer is still running, prevent the click from submitting the form\n if (this.debounceFormSubmitTimer) {\n event.preventDefault()\n return false\n }\n\n this.debounceFormSubmitTimer = window.setTimeout(() => {\n this.debounceFormSubmitTimer = null\n }, DEBOUNCE_TIMEOUT_IN_SECONDS * 1000)\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-button'\n\n /**\n * Button default config\n *\n * @see {@link ButtonConfig}\n * @constant\n * @type {ButtonConfig}\n */\n static defaults = Object.freeze({\n preventDoubleClick: false\n })\n\n /**\n * Button config schema\n *\n * @constant\n * @satisfies {Schema}\n */\n static schema = Object.freeze({\n properties: {\n preventDoubleClick: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Button config\n *\n * @typedef {object} ButtonConfig\n * @property {boolean} [preventDoubleClick=false] - Prevent accidental double\n * clicks on submit buttons from submitting forms multiple times.\n */\n\n/**\n * @typedef {import('../../common/index.mjs').Schema} Schema\n */\n"],"names":["normaliseString","value","property","trimmedValue","trim","output","outputType","type","includes","length","isFinite","Number","mergeConfigs","configObjects","formattedConfigObject","configObject","key","Object","keys","option","override","isObject","extractConfigByNamespace","Component","dataset","namespace","schema","properties","newObject","entries","current","keyParts","split","index","name","isInitialised","$root","moduleName","HTMLElement","hasAttribute","isSupported","$scope","document","body","classList","contains","isArray","Array","formatErrorMessage","message","normaliseDataset","out","field","GOVUKFrontendError","Error","constructor","args","SupportError","supportMessage","HTMLScriptElement","prototype","ElementError","messageOrOptions","component","identifier","element","expectedType","InitError","componentOrMessage","GOVUKFrontendComponent","_$root","childConstructor","elementType","checkSupport","checkInitialised","setAttribute","DEBOUNCE_TIMEOUT_IN_SECONDS","Button","config","debounceFormSubmitTimer","defaults","addEventListener","event","handleKeyDown","debounce","$target","target","getAttribute","preventDefault","click","preventDoubleClick","window","setTimeout","freeze"],"mappings":"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"}
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import { mergeConfigs } from '../../common/index.mjs';
|
2
2
|
import { normaliseDataset } from '../../common/normalise-dataset.mjs';
|
3
|
-
import { ElementError } from '../../errors/index.mjs';
|
4
3
|
import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs';
|
5
4
|
|
6
5
|
const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
|
@@ -12,25 +11,16 @@ const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
|
|
12
11
|
*/
|
13
12
|
class Button extends GOVUKFrontendComponent {
|
14
13
|
/**
|
15
|
-
* @param {Element | null} $
|
14
|
+
* @param {Element | null} $root - HTML element to use for button
|
16
15
|
* @param {ButtonConfig} [config] - Button config
|
17
16
|
*/
|
18
|
-
constructor($
|
19
|
-
super();
|
20
|
-
this.$module = void 0;
|
17
|
+
constructor($root, config = {}) {
|
18
|
+
super($root);
|
21
19
|
this.config = void 0;
|
22
20
|
this.debounceFormSubmitTimer = null;
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
element: $module,
|
27
|
-
identifier: 'Root element (`$module`)'
|
28
|
-
});
|
29
|
-
}
|
30
|
-
this.$module = $module;
|
31
|
-
this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, $module.dataset));
|
32
|
-
this.$module.addEventListener('keydown', event => this.handleKeyDown(event));
|
33
|
-
this.$module.addEventListener('click', event => this.debounce(event));
|
21
|
+
this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, this.$root.dataset));
|
22
|
+
this.$root.addEventListener('keydown', event => this.handleKeyDown(event));
|
23
|
+
this.$root.addEventListener('click', event => this.debounce(event));
|
34
24
|
}
|
35
25
|
handleKeyDown(event) {
|
36
26
|
const $target = event.target;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"button.mjs","sources":["../../../../src/govuk/components/button/button.mjs"],"sourcesContent":["import { mergeConfigs } from '../../common/index.mjs'\nimport { normaliseDataset } from '../../common/normalise-dataset.mjs'\nimport {
|
1
|
+
{"version":3,"file":"button.mjs","sources":["../../../../src/govuk/components/button/button.mjs"],"sourcesContent":["import { mergeConfigs } from '../../common/index.mjs'\nimport { normaliseDataset } from '../../common/normalise-dataset.mjs'\nimport { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'\n\nconst DEBOUNCE_TIMEOUT_IN_SECONDS = 1\n\n/**\n * JavaScript enhancements for the Button component\n *\n * @preserve\n */\nexport class Button extends GOVUKFrontendComponent {\n /**\n * @private\n * @type {ButtonConfig}\n */\n config\n\n /**\n * @private\n * @type {number | null}\n */\n debounceFormSubmitTimer = null\n\n /**\n * @param {Element | null} $root - HTML element to use for button\n * @param {ButtonConfig} [config] - Button config\n */\n constructor($root, config = {}) {\n super($root)\n\n this.config = mergeConfigs(\n Button.defaults,\n config,\n normaliseDataset(Button, this.$root.dataset)\n )\n\n this.$root.addEventListener('keydown', (event) => this.handleKeyDown(event))\n this.$root.addEventListener('click', (event) => this.debounce(event))\n }\n\n /**\n * Trigger a click event when the space key is pressed\n *\n * Some screen readers tell users they can use the space bar to activate\n * things with the 'button' role, so we need to match the functionality of\n * native HTML buttons.\n *\n * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270\n *\n * @private\n * @param {KeyboardEvent} event - Keydown event\n */\n handleKeyDown(event) {\n const $target = event.target\n\n // Handle space bar only\n if (event.key !== ' ') {\n return\n }\n\n // Handle elements with [role=\"button\"] only\n if (\n $target instanceof HTMLElement &&\n $target.getAttribute('role') === 'button'\n ) {\n event.preventDefault() // prevent the page from scrolling\n $target.click()\n }\n }\n\n /**\n * Debounce double-clicks\n *\n * If the click quickly succeeds a previous click then nothing will happen.\n * This stops people accidentally causing multiple form submissions by double\n * clicking buttons.\n *\n * @private\n * @param {MouseEvent} event - Mouse click event\n * @returns {undefined | false} Returns undefined, or false when debounced\n */\n debounce(event) {\n // Check the button that was clicked has preventDoubleClick enabled\n if (!this.config.preventDoubleClick) {\n return\n }\n\n // If the timer is still running, prevent the click from submitting the form\n if (this.debounceFormSubmitTimer) {\n event.preventDefault()\n return false\n }\n\n this.debounceFormSubmitTimer = window.setTimeout(() => {\n this.debounceFormSubmitTimer = null\n }, DEBOUNCE_TIMEOUT_IN_SECONDS * 1000)\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-button'\n\n /**\n * Button default config\n *\n * @see {@link ButtonConfig}\n * @constant\n * @type {ButtonConfig}\n */\n static defaults = Object.freeze({\n preventDoubleClick: false\n })\n\n /**\n * Button config schema\n *\n * @constant\n * @satisfies {Schema}\n */\n static schema = Object.freeze({\n properties: {\n preventDoubleClick: { type: 'boolean' }\n }\n })\n}\n\n/**\n * Button config\n *\n * @typedef {object} ButtonConfig\n * @property {boolean} [preventDoubleClick=false] - Prevent accidental double\n * clicks on submit buttons from submitting forms multiple times.\n */\n\n/**\n * @typedef {import('../../common/index.mjs').Schema} Schema\n */\n"],"names":["DEBOUNCE_TIMEOUT_IN_SECONDS","Button","GOVUKFrontendComponent","constructor","$root","config","debounceFormSubmitTimer","mergeConfigs","defaults","normaliseDataset","dataset","addEventListener","event","handleKeyDown","debounce","$target","target","key","HTMLElement","getAttribute","preventDefault","click","preventDoubleClick","window","setTimeout","moduleName","Object","freeze","schema","properties","type"],"mappings":";;;;AAIA,MAAMA,2BAA2B,GAAG,CAAC,CAAA;;AAErC;AACA;AACA;AACA;AACA;AACO,MAAMC,MAAM,SAASC,sBAAsB,CAAC;AAajD;AACF;AACA;AACA;AACEC,EAAAA,WAAWA,CAACC,KAAK,EAAEC,MAAM,GAAG,EAAE,EAAE;IAC9B,KAAK,CAACD,KAAK,CAAC,CAAA;AAAA,IAAA,IAAA,CAbdC,MAAM,GAAA,KAAA,CAAA,CAAA;IAAA,IAMNC,CAAAA,uBAAuB,GAAG,IAAI,CAAA;IAS5B,IAAI,CAACD,MAAM,GAAGE,YAAY,CACxBN,MAAM,CAACO,QAAQ,EACfH,MAAM,EACNI,gBAAgB,CAACR,MAAM,EAAE,IAAI,CAACG,KAAK,CAACM,OAAO,CAC7C,CAAC,CAAA;AAED,IAAA,IAAI,CAACN,KAAK,CAACO,gBAAgB,CAAC,SAAS,EAAGC,KAAK,IAAK,IAAI,CAACC,aAAa,CAACD,KAAK,CAAC,CAAC,CAAA;AAC5E,IAAA,IAAI,CAACR,KAAK,CAACO,gBAAgB,CAAC,OAAO,EAAGC,KAAK,IAAK,IAAI,CAACE,QAAQ,CAACF,KAAK,CAAC,CAAC,CAAA;AACvE,GAAA;EAcAC,aAAaA,CAACD,KAAK,EAAE;AACnB,IAAA,MAAMG,OAAO,GAAGH,KAAK,CAACI,MAAM,CAAA;AAG5B,IAAA,IAAIJ,KAAK,CAACK,GAAG,KAAK,GAAG,EAAE;AACrB,MAAA,OAAA;AACF,KAAA;AAGA,IAAA,IACEF,OAAO,YAAYG,WAAW,IAC9BH,OAAO,CAACI,YAAY,CAAC,MAAM,CAAC,KAAK,QAAQ,EACzC;MACAP,KAAK,CAACQ,cAAc,EAAE,CAAA;MACtBL,OAAO,CAACM,KAAK,EAAE,CAAA;AACjB,KAAA;AACF,GAAA;EAaAP,QAAQA,CAACF,KAAK,EAAE;AAEd,IAAA,IAAI,CAAC,IAAI,CAACP,MAAM,CAACiB,kBAAkB,EAAE;AACnC,MAAA,OAAA;AACF,KAAA;IAGA,IAAI,IAAI,CAAChB,uBAAuB,EAAE;MAChCM,KAAK,CAACQ,cAAc,EAAE,CAAA;AACtB,MAAA,OAAO,KAAK,CAAA;AACd,KAAA;AAEA,IAAA,IAAI,CAACd,uBAAuB,GAAGiB,MAAM,CAACC,UAAU,CAAC,MAAM;MACrD,IAAI,CAAClB,uBAAuB,GAAG,IAAI,CAAA;AACrC,KAAC,EAAEN,2BAA2B,GAAG,IAAI,CAAC,CAAA;AACxC,GAAA;AA6BF,CAAA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AA/HaC,MAAM,CA2FVwB,UAAU,GAAG,cAAc,CAAA;AA3FvBxB,MAAM,CAoGVO,QAAQ,GAAGkB,MAAM,CAACC,MAAM,CAAC;AAC9BL,EAAAA,kBAAkB,EAAE,KAAA;AACtB,CAAC,CAAC,CAAA;AAtGSrB,MAAM,CA8GV2B,MAAM,GAAGF,MAAM,CAACC,MAAM,CAAC;AAC5BE,EAAAA,UAAU,EAAE;AACVP,IAAAA,kBAAkB,EAAE;AAAEQ,MAAAA,IAAI,EAAE,SAAA;AAAU,KAAA;AACxC,GAAA;AACF,CAAC,CAAC;;;;"}
|
data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js
CHANGED
@@ -79,6 +79,19 @@
|
|
79
79
|
}
|
80
80
|
return newObject[namespace];
|
81
81
|
}
|
82
|
+
function isInitialised($root, moduleName) {
|
83
|
+
return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Checks if GOV.UK Frontend is supported on this page
|
88
|
+
*
|
89
|
+
* Some browsers will load and run our JavaScript but GOV.UK Frontend
|
90
|
+
* won't be supported.
|
91
|
+
*
|
92
|
+
* @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support
|
93
|
+
* @returns {boolean} Whether GOV.UK Frontend is supported on this page
|
94
|
+
*/
|
82
95
|
function isSupported($scope = document.body) {
|
83
96
|
if (!$scope) {
|
84
97
|
return false;
|
@@ -111,6 +124,9 @@
|
|
111
124
|
function isObject(option) {
|
112
125
|
return !!option && typeof option === 'object' && !isArray(option);
|
113
126
|
}
|
127
|
+
function formatErrorMessage(Component, message) {
|
128
|
+
return `${Component.moduleName}: ${message}`;
|
129
|
+
}
|
114
130
|
|
115
131
|
/**
|
116
132
|
* Schema for component config
|
@@ -134,6 +150,10 @@
|
|
134
150
|
* @property {string[]} required - List of required config fields
|
135
151
|
* @property {string} errorMessage - Error message when required config fields not provided
|
136
152
|
*/
|
153
|
+
/**
|
154
|
+
* @typedef ComponentWithModuleName
|
155
|
+
* @property {string} moduleName - Name of the component
|
156
|
+
*/
|
137
157
|
|
138
158
|
function normaliseDataset(Component, dataset) {
|
139
159
|
const out = {};
|
@@ -177,30 +197,85 @@
|
|
177
197
|
let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
|
178
198
|
if (typeof messageOrOptions === 'object') {
|
179
199
|
const {
|
180
|
-
|
200
|
+
component,
|
181
201
|
identifier,
|
182
202
|
element,
|
183
203
|
expectedType
|
184
204
|
} = messageOrOptions;
|
185
|
-
message =
|
205
|
+
message = identifier;
|
186
206
|
message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found';
|
207
|
+
message = formatErrorMessage(component, message);
|
187
208
|
}
|
188
209
|
super(message);
|
189
210
|
this.name = 'ElementError';
|
190
211
|
}
|
191
212
|
}
|
213
|
+
class InitError extends GOVUKFrontendError {
|
214
|
+
constructor(componentOrMessage) {
|
215
|
+
const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
|
216
|
+
super(message);
|
217
|
+
this.name = 'InitError';
|
218
|
+
}
|
219
|
+
}
|
220
|
+
/**
|
221
|
+
* @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
|
222
|
+
*/
|
192
223
|
|
193
224
|
class GOVUKFrontendComponent {
|
194
|
-
|
195
|
-
|
225
|
+
/**
|
226
|
+
* Returns the root element of the component
|
227
|
+
*
|
228
|
+
* @protected
|
229
|
+
* @returns {RootElementType} - the root element of component
|
230
|
+
*/
|
231
|
+
get $root() {
|
232
|
+
return this._$root;
|
196
233
|
}
|
197
|
-
|
234
|
+
constructor($root) {
|
235
|
+
this._$root = void 0;
|
236
|
+
const childConstructor = this.constructor;
|
237
|
+
if (typeof childConstructor.moduleName !== 'string') {
|
238
|
+
throw new InitError(`\`moduleName\` not defined in component`);
|
239
|
+
}
|
240
|
+
if (!($root instanceof childConstructor.elementType)) {
|
241
|
+
throw new ElementError({
|
242
|
+
element: $root,
|
243
|
+
component: childConstructor,
|
244
|
+
identifier: 'Root element (`$root`)',
|
245
|
+
expectedType: childConstructor.elementType.name
|
246
|
+
});
|
247
|
+
} else {
|
248
|
+
this._$root = $root;
|
249
|
+
}
|
250
|
+
childConstructor.checkSupport();
|
251
|
+
this.checkInitialised();
|
252
|
+
const moduleName = childConstructor.moduleName;
|
253
|
+
this.$root.setAttribute(`data-${moduleName}-init`, '');
|
254
|
+
}
|
255
|
+
checkInitialised() {
|
256
|
+
const constructor = this.constructor;
|
257
|
+
const moduleName = constructor.moduleName;
|
258
|
+
if (moduleName && isInitialised(this.$root, moduleName)) {
|
259
|
+
throw new InitError(constructor);
|
260
|
+
}
|
261
|
+
}
|
262
|
+
static checkSupport() {
|
198
263
|
if (!isSupported()) {
|
199
264
|
throw new SupportError();
|
200
265
|
}
|
201
266
|
}
|
202
267
|
}
|
203
268
|
|
269
|
+
/**
|
270
|
+
* @typedef ChildClass
|
271
|
+
* @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
|
272
|
+
*/
|
273
|
+
|
274
|
+
/**
|
275
|
+
* @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor
|
276
|
+
*/
|
277
|
+
GOVUKFrontendComponent.elementType = HTMLElement;
|
278
|
+
|
204
279
|
class I18n {
|
205
280
|
constructor(translations = {}, config = {}) {
|
206
281
|
var _config$locale;
|
@@ -408,13 +483,12 @@
|
|
408
483
|
*/
|
409
484
|
class CharacterCount extends GOVUKFrontendComponent {
|
410
485
|
/**
|
411
|
-
* @param {Element | null} $
|
486
|
+
* @param {Element | null} $root - HTML element to use for character count
|
412
487
|
* @param {CharacterCountConfig} [config] - Character count config
|
413
488
|
*/
|
414
|
-
constructor($
|
489
|
+
constructor($root, config = {}) {
|
415
490
|
var _ref, _this$config$maxwords;
|
416
|
-
super();
|
417
|
-
this.$module = void 0;
|
491
|
+
super($root);
|
418
492
|
this.$textarea = void 0;
|
419
493
|
this.$visibleCountMessage = void 0;
|
420
494
|
this.$screenReaderCountMessage = void 0;
|
@@ -424,23 +498,16 @@
|
|
424
498
|
this.config = void 0;
|
425
499
|
this.i18n = void 0;
|
426
500
|
this.maxLength = void 0;
|
427
|
-
|
428
|
-
throw new ElementError({
|
429
|
-
componentName: 'Character count',
|
430
|
-
element: $module,
|
431
|
-
identifier: 'Root element (`$module`)'
|
432
|
-
});
|
433
|
-
}
|
434
|
-
const $textarea = $module.querySelector('.govuk-js-character-count');
|
501
|
+
const $textarea = this.$root.querySelector('.govuk-js-character-count');
|
435
502
|
if (!($textarea instanceof HTMLTextAreaElement || $textarea instanceof HTMLInputElement)) {
|
436
503
|
throw new ElementError({
|
437
|
-
|
504
|
+
component: CharacterCount,
|
438
505
|
element: $textarea,
|
439
506
|
expectedType: 'HTMLTextareaElement or HTMLInputElement',
|
440
507
|
identifier: 'Form field (`.govuk-js-character-count`)'
|
441
508
|
});
|
442
509
|
}
|
443
|
-
const datasetConfig = normaliseDataset(CharacterCount,
|
510
|
+
const datasetConfig = normaliseDataset(CharacterCount, this.$root.dataset);
|
444
511
|
let configOverrides = {};
|
445
512
|
if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
|
446
513
|
configOverrides = {
|
@@ -451,19 +518,18 @@
|
|
451
518
|
this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
|
452
519
|
const errors = validateConfig(CharacterCount.schema, this.config);
|
453
520
|
if (errors[0]) {
|
454
|
-
throw new ConfigError(
|
521
|
+
throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
|
455
522
|
}
|
456
523
|
this.i18n = new I18n(this.config.i18n, {
|
457
|
-
locale: closestAttributeValue(
|
524
|
+
locale: closestAttributeValue(this.$root, 'lang')
|
458
525
|
});
|
459
526
|
this.maxLength = (_ref = (_this$config$maxwords = this.config.maxwords) != null ? _this$config$maxwords : this.config.maxlength) != null ? _ref : Infinity;
|
460
|
-
this.$module = $module;
|
461
527
|
this.$textarea = $textarea;
|
462
528
|
const textareaDescriptionId = `${this.$textarea.id}-info`;
|
463
529
|
const $textareaDescription = document.getElementById(textareaDescriptionId);
|
464
530
|
if (!$textareaDescription) {
|
465
531
|
throw new ElementError({
|
466
|
-
|
532
|
+
component: CharacterCount,
|
467
533
|
element: $textareaDescription,
|
468
534
|
identifier: `Count message (\`id="${textareaDescriptionId}"\`)`
|
469
535
|
});
|