govuk_publishing_components 51.1.1 → 51.2.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/components/feedback.js +29 -34
  3. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +7 -2
  4. data/app/assets/stylesheets/component_guide/application.scss +1 -1
  5. data/app/assets/stylesheets/govuk_publishing_components/_all_components.scss +1 -0
  6. data/app/assets/stylesheets/govuk_publishing_components/components/helpers/_markdown-typography.scss +2 -2
  7. data/app/models/govuk_publishing_components/audit_comparer.rb +3 -3
  8. data/app/views/govuk_publishing_components/components/_title.html.erb +2 -3
  9. data/app/views/govuk_publishing_components/components/docs/add_another.yml +1 -1
  10. data/app/views/govuk_publishing_components/components/docs/feedback.yml +1 -1
  11. data/lib/govuk_publishing_components/presenters/checkboxes_helper.rb +1 -1
  12. data/lib/govuk_publishing_components/presenters/shared_helper.rb +0 -11
  13. data/lib/govuk_publishing_components/version.rb +1 -1
  14. data/node_modules/govuk-frontend/dist/govuk/all.bundle.js +217 -184
  15. data/node_modules/govuk-frontend/dist/govuk/all.bundle.js.map +1 -1
  16. data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs +216 -184
  17. data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs.map +1 -1
  18. data/node_modules/govuk-frontend/dist/govuk/all.mjs +1 -0
  19. data/node_modules/govuk-frontend/dist/govuk/all.mjs.map +1 -1
  20. data/node_modules/govuk-frontend/dist/govuk/all.scss +6 -0
  21. data/node_modules/govuk-frontend/dist/govuk/all.scss.map +1 -1
  22. data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs +164 -0
  23. data/node_modules/govuk-frontend/dist/govuk/common/configuration.mjs.map +1 -0
  24. data/node_modules/govuk-frontend/dist/govuk/common/govuk-frontend-version.mjs +1 -1
  25. data/node_modules/govuk-frontend/dist/govuk/common/index.mjs +1 -87
  26. data/node_modules/govuk-frontend/dist/govuk/common/index.mjs.map +1 -1
  27. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js +149 -112
  28. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js.map +1 -1
  29. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs +148 -111
  30. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs.map +1 -1
  31. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs +5 -8
  32. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs.map +1 -1
  33. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js +149 -112
  34. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js.map +1 -1
  35. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs +148 -111
  36. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs.map +1 -1
  37. data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs +5 -8
  38. data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs.map +1 -1
  39. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js +174 -140
  40. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js.map +1 -1
  41. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs +173 -139
  42. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs.map +1 -1
  43. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs +17 -16
  44. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs.map +1 -1
  45. data/node_modules/govuk-frontend/dist/govuk/components/character-count/macro-options.json +4 -4
  46. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js +1 -24
  47. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js.map +1 -1
  48. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs +0 -23
  49. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs.map +1 -1
  50. data/node_modules/govuk-frontend/dist/govuk/components/date-input/macro-options.json +2 -2
  51. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js +149 -112
  52. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js.map +1 -1
  53. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs +148 -111
  54. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs.map +1 -1
  55. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs +6 -8
  56. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs.map +1 -1
  57. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js +149 -112
  58. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js.map +1 -1
  59. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs +148 -111
  60. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs.map +1 -1
  61. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs +5 -8
  62. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs.map +1 -1
  63. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js +1 -24
  64. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js.map +1 -1
  65. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs +0 -23
  66. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs.map +1 -1
  67. data/node_modules/govuk-frontend/dist/govuk/components/input/macro-options.json +4 -4
  68. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js +149 -112
  69. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js.map +1 -1
  70. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs +148 -111
  71. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.mjs.map +1 -1
  72. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs +6 -8
  73. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.mjs.map +1 -1
  74. data/node_modules/govuk-frontend/dist/govuk/components/password-input/macro-options.json +1 -1
  75. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js +149 -112
  76. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js.map +1 -1
  77. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs +148 -111
  78. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs.map +1 -1
  79. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs +5 -8
  80. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs.map +1 -1
  81. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js +1 -24
  82. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js.map +1 -1
  83. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs +0 -23
  84. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs.map +1 -1
  85. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js +1 -24
  86. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js.map +1 -1
  87. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs +0 -23
  88. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs.map +1 -1
  89. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js +1 -24
  90. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js.map +1 -1
  91. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs +0 -23
  92. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs.map +1 -1
  93. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js +1 -24
  94. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js.map +1 -1
  95. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs +0 -23
  96. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs.map +1 -1
  97. data/node_modules/govuk-frontend/dist/govuk/components/textarea/macro-options.json +1 -1
  98. data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss +1 -1
  99. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css +1 -1
  100. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css.map +1 -1
  101. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js +1 -1
  102. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js.map +1 -1
  103. data/node_modules/govuk-frontend/dist/govuk/init.mjs +17 -13
  104. data/node_modules/govuk-frontend/dist/govuk/init.mjs.map +1 -1
  105. data/node_modules/govuk-frontend/dist/govuk/settings/_typography-responsive.scss +5 -10
  106. data/node_modules/govuk-frontend/dist/govuk/settings/_typography-responsive.scss.map +1 -1
  107. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +1 -1
  108. data/node_modules/govuk-frontend/package.json +9 -9
  109. metadata +4 -6
  110. data/node_modules/govuk-frontend/dist/govuk/common/normalise-dataset.mjs +0 -18
  111. data/node_modules/govuk-frontend/dist/govuk/common/normalise-dataset.mjs.map +0 -1
  112. data/node_modules/govuk-frontend/dist/govuk/common/normalise-string.mjs +0 -31
  113. data/node_modules/govuk-frontend/dist/govuk/common/normalise-string.mjs.map +0 -1
@@ -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 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
+ {"version":3,"file":"button.bundle.mjs","sources":["../../../../src/govuk/common/index.mjs","../../../../src/govuk/errors/index.mjs","../../../../src/govuk/govuk-frontend-component.mjs","../../../../src/govuk/common/configuration.mjs","../../../../src/govuk/components/button/button.mjs"],"sourcesContent":["/**\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 * 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 * 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 */\nexport function 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/* 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 { 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 { ConfigError } from '../errors/index.mjs'\nimport { GOVUKFrontendComponent } from '../govuk-frontend-component.mjs'\n\nimport { isObject, formatErrorMessage } from './index.mjs'\n\nexport const configOverride = Symbol.for('configOverride')\n\n/**\n * Base Component class\n *\n * Centralises the behaviours shared by our components\n *\n * @virtual\n * @template {ObjectNested} [ConfigurationType={}]\n * @template {Element & { dataset: DOMStringMap }} [RootElementType=HTMLElement]\n * @augments GOVUKFrontendComponent<RootElementType>\n */\nexport class ConfigurableComponent extends GOVUKFrontendComponent {\n /**\n * configOverride\n *\n * Function which defines configuration overrides to prioritize\n * properties from the root element's dataset.\n *\n * It should take a subset of configuration as input and return\n * a new configuration object with properties that should be\n * overridden based on the root element's dataset. A Symbol\n * is used for indexing to prevent conflicts.\n *\n * @internal\n * @virtual\n * @param {ObjectNested} [param] - Configuration object\n * @returns {ObjectNested} return - Configuration object\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n [configOverride](param) {\n return {}\n }\n\n /**\n * Returns the root element of the component\n *\n * @protected\n * @returns {ConfigurationType} - the root element of component\n */\n get config() {\n return this._config\n }\n\n /**\n *\n * @type {ConfigurationType}\n */\n _config\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 * @param {ConfigurationType} [config] - HTML element to use for component\n */\n constructor($root, config) {\n super($root)\n\n const childConstructor =\n /** @type {ChildClassConstructor<ConfigurationType>} */ (this.constructor)\n\n if (typeof childConstructor.defaults === 'undefined') {\n throw new ConfigError(\n formatErrorMessage(\n childConstructor,\n 'Config passed as parameter into constructor but no defaults defined'\n )\n )\n }\n\n const datasetConfig = /** @type {ConfigurationType} */ (\n normaliseDataset(childConstructor, this._$root.dataset)\n )\n\n this._config = /** @type {ConfigurationType} */ (\n mergeConfigs(\n childConstructor.defaults,\n config ?? {},\n this[configOverride](datasetConfig),\n datasetConfig\n )\n )\n }\n}\n\n/**\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 * 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, moduleName: string }} Component - Component class\n * @param {DOMStringMap} dataset - HTML element dataset\n * @returns {ObjectNested} Normalised dataset\n */\nexport function normaliseDataset(Component, dataset) {\n if (typeof Component.schema === 'undefined') {\n throw new ConfigError(\n formatErrorMessage(\n Component,\n 'Config passed as parameter into constructor but no schema defined'\n )\n )\n }\n\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.schema, dataset, field)\n }\n }\n\n return out\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 * 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 * 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 - The schema of a component\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(schema, dataset, namespace) {\n const property = 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 * @internal\n * @typedef {keyof ObjectNested} NestedKey\n * @typedef {{ [key: string]: string | boolean | number | ObjectNested | undefined }} ObjectNested\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 * @template {ObjectNested} [ConfigurationType={}]\n * @typedef ChildClass\n * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component\n * @property {Schema} [schema] - The schema of the component configuration\n * @property {ConfigurationType} [defaults] - The default values of the configuration of the component\n */\n\n/**\n * @template {ObjectNested} [ConfigurationType={}]\n * @typedef {typeof GOVUKFrontendComponent & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>\n */\n","import { ConfigurableComponent } from '../../common/configuration.mjs'\n\nconst DEBOUNCE_TIMEOUT_IN_SECONDS = 1\n\n/**\n * JavaScript enhancements for the Button component\n *\n * @preserve\n * @augments ConfigurableComponent<ButtonConfig>\n */\nexport class Button extends ConfigurableComponent {\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, config)\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/configuration.mjs').Schema} Schema\n */\n"],"names":["isInitialised","$root","moduleName","HTMLElement","hasAttribute","isSupported","$scope","document","body","classList","contains","isArray","option","Array","isObject","formatErrorMessage","Component","message","GOVUKFrontendError","Error","constructor","args","name","SupportError","supportMessage","HTMLScriptElement","prototype","ConfigError","ElementError","messageOrOptions","component","identifier","element","expectedType","InitError","componentOrMessage","GOVUKFrontendComponent","_$root","childConstructor","elementType","checkSupport","checkInitialised","setAttribute","configOverride","Symbol","for","ConfigurableComponent","param","config","_config","defaults","datasetConfig","normaliseDataset","dataset","mergeConfigs","normaliseString","value","property","trimmedValue","trim","output","outputType","type","includes","length","isFinite","Number","schema","out","field","Object","entries","properties","extractConfigByNamespace","configObjects","formattedConfigObject","configObject","key","keys","override","namespace","newObject","current","keyParts","split","index","DEBOUNCE_TIMEOUT_IN_SECONDS","Button","debounceFormSubmitTimer","addEventListener","event","handleKeyDown","debounce","$target","target","getAttribute","preventDefault","click","preventDoubleClick","window","setTimeout","freeze"],"mappings":"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"}
@@ -1,6 +1,4 @@
1
- import { mergeConfigs } from '../../common/index.mjs';
2
- import { normaliseDataset } from '../../common/normalise-dataset.mjs';
3
- import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs';
1
+ import { ConfigurableComponent } from '../../common/configuration.mjs';
4
2
 
5
3
  const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
6
4
 
@@ -8,17 +6,16 @@ const DEBOUNCE_TIMEOUT_IN_SECONDS = 1;
8
6
  * JavaScript enhancements for the Button component
9
7
  *
10
8
  * @preserve
9
+ * @augments ConfigurableComponent<ButtonConfig>
11
10
  */
12
- class Button extends GOVUKFrontendComponent {
11
+ class Button extends ConfigurableComponent {
13
12
  /**
14
13
  * @param {Element | null} $root - HTML element to use for button
15
14
  * @param {ButtonConfig} [config] - Button config
16
15
  */
17
16
  constructor($root, config = {}) {
18
- super($root);
19
- this.config = void 0;
17
+ super($root, config);
20
18
  this.debounceFormSubmitTimer = null;
21
- this.config = mergeConfigs(Button.defaults, config, normaliseDataset(Button, this.$root.dataset));
22
19
  this.$root.addEventListener('keydown', event => this.handleKeyDown(event));
23
20
  this.$root.addEventListener('click', event => this.debounce(event));
24
21
  }
@@ -55,7 +52,7 @@ class Button extends GOVUKFrontendComponent {
55
52
  */
56
53
 
57
54
  /**
58
- * @typedef {import('../../common/index.mjs').Schema} Schema
55
+ * @typedef {import('../../common/configuration.mjs').Schema} Schema
59
56
  */
60
57
  Button.moduleName = 'govuk-button';
61
58
  Button.defaults = Object.freeze({
@@ -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 { 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;;;;"}
1
+ {"version":3,"file":"button.mjs","sources":["../../../../src/govuk/components/button/button.mjs"],"sourcesContent":["import { ConfigurableComponent } from '../../common/configuration.mjs'\n\nconst DEBOUNCE_TIMEOUT_IN_SECONDS = 1\n\n/**\n * JavaScript enhancements for the Button component\n *\n * @preserve\n * @augments ConfigurableComponent<ButtonConfig>\n */\nexport class Button extends ConfigurableComponent {\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, config)\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/configuration.mjs').Schema} Schema\n */\n"],"names":["DEBOUNCE_TIMEOUT_IN_SECONDS","Button","ConfigurableComponent","constructor","$root","config","debounceFormSubmitTimer","addEventListener","event","handleKeyDown","debounce","$target","target","key","HTMLElement","getAttribute","preventDefault","click","preventDoubleClick","window","setTimeout","moduleName","defaults","Object","freeze","schema","properties","type"],"mappings":";;AAEA,MAAMA,2BAA2B,GAAG,CAAC,CAAA;;AAErC;AACA;AACA;AACA;AACA;AACA;AACO,MAAMC,MAAM,SAASC,qBAAqB,CAAC;AAOhD;AACF;AACA;AACA;AACEC,EAAAA,WAAWA,CAACC,KAAK,EAAEC,MAAM,GAAG,EAAE,EAAE;AAC9B,IAAA,KAAK,CAACD,KAAK,EAAEC,MAAM,CAAC,CAAA;IAAA,IAPtBC,CAAAA,uBAAuB,GAAG,IAAI,CAAA;AAS5B,IAAA,IAAI,CAACF,KAAK,CAACG,gBAAgB,CAAC,SAAS,EAAGC,KAAK,IAAK,IAAI,CAACC,aAAa,CAACD,KAAK,CAAC,CAAC,CAAA;AAC5E,IAAA,IAAI,CAACJ,KAAK,CAACG,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,CAACH,MAAM,CAACa,kBAAkB,EAAE;AACnC,MAAA,OAAA;AACF,KAAA;IAGA,IAAI,IAAI,CAACZ,uBAAuB,EAAE;MAChCE,KAAK,CAACQ,cAAc,EAAE,CAAA;AACtB,MAAA,OAAO,KAAK,CAAA;AACd,KAAA;AAEA,IAAA,IAAI,CAACV,uBAAuB,GAAGa,MAAM,CAACC,UAAU,CAAC,MAAM;MACrD,IAAI,CAACd,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;AAnHaC,MAAM,CA+EVoB,UAAU,GAAG,cAAc,CAAA;AA/EvBpB,MAAM,CAwFVqB,QAAQ,GAAGC,MAAM,CAACC,MAAM,CAAC;AAC9BN,EAAAA,kBAAkB,EAAE,KAAA;AACtB,CAAC,CAAC,CAAA;AA1FSjB,MAAM,CAkGVwB,MAAM,GAAGF,MAAM,CAACC,MAAM,CAAC;AAC5BE,EAAAA,UAAU,EAAE;AACVR,IAAAA,kBAAkB,EAAE;AAAES,MAAAA,IAAI,EAAE,SAAA;AAAU,KAAA;AACxC,GAAA;AACF,CAAC,CAAC;;;;"}
@@ -1,7 +1,7 @@
1
1
  (function (global, factory) {
2
2
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
3
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = {}));
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GOVUKFrontend = global.GOVUKFrontend || {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
7
  function closestAttributeValue($element, attributeName) {
@@ -9,76 +9,6 @@
9
9
  return $closestElementWithAttribute ? $closestElementWithAttribute.getAttribute(attributeName) : null;
10
10
  }
11
11
 
12
- function normaliseString(value, property) {
13
- const trimmedValue = value ? value.trim() : '';
14
- let output;
15
- let outputType = property == null ? void 0 : property.type;
16
- if (!outputType) {
17
- if (['true', 'false'].includes(trimmedValue)) {
18
- outputType = 'boolean';
19
- }
20
- if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
21
- outputType = 'number';
22
- }
23
- }
24
- switch (outputType) {
25
- case 'boolean':
26
- output = trimmedValue === 'true';
27
- break;
28
- case 'number':
29
- output = Number(trimmedValue);
30
- break;
31
- default:
32
- output = value;
33
- }
34
- return output;
35
- }
36
-
37
- /**
38
- * @typedef {import('./index.mjs').SchemaProperty} SchemaProperty
39
- */
40
-
41
- function mergeConfigs(...configObjects) {
42
- const formattedConfigObject = {};
43
- for (const configObject of configObjects) {
44
- for (const key of Object.keys(configObject)) {
45
- const option = formattedConfigObject[key];
46
- const override = configObject[key];
47
- if (isObject(option) && isObject(override)) {
48
- formattedConfigObject[key] = mergeConfigs(option, override);
49
- } else {
50
- formattedConfigObject[key] = override;
51
- }
52
- }
53
- }
54
- return formattedConfigObject;
55
- }
56
- function extractConfigByNamespace(Component, dataset, namespace) {
57
- const property = Component.schema.properties[namespace];
58
- if ((property == null ? void 0 : property.type) !== 'object') {
59
- return;
60
- }
61
- const newObject = {
62
- [namespace]: ({})
63
- };
64
- for (const [key, value] of Object.entries(dataset)) {
65
- let current = newObject;
66
- const keyParts = key.split('.');
67
- for (const [index, name] of keyParts.entries()) {
68
- if (typeof current === 'object') {
69
- if (index < keyParts.length - 1) {
70
- if (!isObject(current[name])) {
71
- current[name] = {};
72
- }
73
- current = current[name];
74
- } else if (key !== namespace) {
75
- current[name] = normaliseString(value);
76
- }
77
- }
78
- }
79
- }
80
- return newObject[namespace];
81
- }
82
12
  function isInitialised($root, moduleName) {
83
13
  return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
84
14
  }
@@ -98,26 +28,6 @@
98
28
  }
99
29
  return $scope.classList.contains('govuk-frontend-supported');
100
30
  }
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
31
  function isArray(option) {
122
32
  return Array.isArray(option);
123
33
  }
@@ -127,47 +37,11 @@
127
37
  function formatErrorMessage(Component, message) {
128
38
  return `${Component.moduleName}: ${message}`;
129
39
  }
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
40
  /**
154
41
  * @typedef ComponentWithModuleName
155
42
  * @property {string} moduleName - Name of the component
156
43
  */
157
44
 
158
- function normaliseDataset(Component, dataset) {
159
- const out = {};
160
- for (const [field, property] of Object.entries(Component.schema.properties)) {
161
- if (field in dataset) {
162
- out[field] = normaliseString(dataset[field], property);
163
- }
164
- if ((property == null ? void 0 : property.type) === 'object') {
165
- out[field] = extractConfigByNamespace(Component, dataset, field);
166
- }
167
- }
168
- return out;
169
- }
170
-
171
45
  class GOVUKFrontendError extends Error {
172
46
  constructor(...args) {
173
47
  super(...args);
@@ -276,6 +150,164 @@
276
150
  */
277
151
  GOVUKFrontendComponent.elementType = HTMLElement;
278
152
 
153
+ const configOverride = Symbol.for('configOverride');
154
+ class ConfigurableComponent extends GOVUKFrontendComponent {
155
+ [configOverride](param) {
156
+ return {};
157
+ }
158
+
159
+ /**
160
+ * Returns the root element of the component
161
+ *
162
+ * @protected
163
+ * @returns {ConfigurationType} - the root element of component
164
+ */
165
+ get config() {
166
+ return this._config;
167
+ }
168
+ constructor($root, config) {
169
+ super($root);
170
+ this._config = void 0;
171
+ const childConstructor = this.constructor;
172
+ if (typeof childConstructor.defaults === 'undefined') {
173
+ throw new ConfigError(formatErrorMessage(childConstructor, 'Config passed as parameter into constructor but no defaults defined'));
174
+ }
175
+ const datasetConfig = normaliseDataset(childConstructor, this._$root.dataset);
176
+ this._config = mergeConfigs(childConstructor.defaults, config != null ? config : {}, this[configOverride](datasetConfig), datasetConfig);
177
+ }
178
+ }
179
+ function normaliseString(value, property) {
180
+ const trimmedValue = value ? value.trim() : '';
181
+ let output;
182
+ let outputType = property == null ? void 0 : property.type;
183
+ if (!outputType) {
184
+ if (['true', 'false'].includes(trimmedValue)) {
185
+ outputType = 'boolean';
186
+ }
187
+ if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
188
+ outputType = 'number';
189
+ }
190
+ }
191
+ switch (outputType) {
192
+ case 'boolean':
193
+ output = trimmedValue === 'true';
194
+ break;
195
+ case 'number':
196
+ output = Number(trimmedValue);
197
+ break;
198
+ default:
199
+ output = value;
200
+ }
201
+ return output;
202
+ }
203
+ function normaliseDataset(Component, dataset) {
204
+ if (typeof Component.schema === 'undefined') {
205
+ throw new ConfigError(formatErrorMessage(Component, 'Config passed as parameter into constructor but no schema defined'));
206
+ }
207
+ const out = {};
208
+ for (const [field, property] of Object.entries(Component.schema.properties)) {
209
+ if (field in dataset) {
210
+ out[field] = normaliseString(dataset[field], property);
211
+ }
212
+ if ((property == null ? void 0 : property.type) === 'object') {
213
+ out[field] = extractConfigByNamespace(Component.schema, dataset, field);
214
+ }
215
+ }
216
+ return out;
217
+ }
218
+ function mergeConfigs(...configObjects) {
219
+ const formattedConfigObject = {};
220
+ for (const configObject of configObjects) {
221
+ for (const key of Object.keys(configObject)) {
222
+ const option = formattedConfigObject[key];
223
+ const override = configObject[key];
224
+ if (isObject(option) && isObject(override)) {
225
+ formattedConfigObject[key] = mergeConfigs(option, override);
226
+ } else {
227
+ formattedConfigObject[key] = override;
228
+ }
229
+ }
230
+ }
231
+ return formattedConfigObject;
232
+ }
233
+ function validateConfig(schema, config) {
234
+ const validationErrors = [];
235
+ for (const [name, conditions] of Object.entries(schema)) {
236
+ const errors = [];
237
+ if (Array.isArray(conditions)) {
238
+ for (const {
239
+ required,
240
+ errorMessage
241
+ } of conditions) {
242
+ if (!required.every(key => !!config[key])) {
243
+ errors.push(errorMessage);
244
+ }
245
+ }
246
+ if (name === 'anyOf' && !(conditions.length - errors.length >= 1)) {
247
+ validationErrors.push(...errors);
248
+ }
249
+ }
250
+ }
251
+ return validationErrors;
252
+ }
253
+ function extractConfigByNamespace(schema, dataset, namespace) {
254
+ const property = schema.properties[namespace];
255
+ if ((property == null ? void 0 : property.type) !== 'object') {
256
+ return;
257
+ }
258
+ const newObject = {
259
+ [namespace]: ({})
260
+ };
261
+ for (const [key, value] of Object.entries(dataset)) {
262
+ let current = newObject;
263
+ const keyParts = key.split('.');
264
+ for (const [index, name] of keyParts.entries()) {
265
+ if (typeof current === 'object') {
266
+ if (index < keyParts.length - 1) {
267
+ if (!isObject(current[name])) {
268
+ current[name] = {};
269
+ }
270
+ current = current[name];
271
+ } else if (key !== namespace) {
272
+ current[name] = normaliseString(value);
273
+ }
274
+ }
275
+ }
276
+ }
277
+ return newObject[namespace];
278
+ }
279
+ /**
280
+ * Schema for component config
281
+ *
282
+ * @typedef {object} Schema
283
+ * @property {{ [field: string]: SchemaProperty | undefined }} properties - Schema properties
284
+ * @property {SchemaCondition[]} [anyOf] - List of schema conditions
285
+ */
286
+ /**
287
+ * Schema property for component config
288
+ *
289
+ * @typedef {object} SchemaProperty
290
+ * @property {'string' | 'boolean' | 'number' | 'object'} type - Property type
291
+ */
292
+ /**
293
+ * Schema condition for component config
294
+ *
295
+ * @typedef {object} SchemaCondition
296
+ * @property {string[]} required - List of required config fields
297
+ * @property {string} errorMessage - Error message when required config fields not provided
298
+ */
299
+ /**
300
+ * @template {ObjectNested} [ConfigurationType={}]
301
+ * @typedef ChildClass
302
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
303
+ * @property {Schema} [schema] - The schema of the component configuration
304
+ * @property {ConfigurationType} [defaults] - The default values of the configuration of the component
305
+ */
306
+ /**
307
+ * @template {ObjectNested} [ConfigurationType={}]
308
+ * @typedef {typeof GOVUKFrontendComponent & ChildClass<ConfigurationType>} ChildClassConstructor<ConfigurationType>
309
+ */
310
+
279
311
  class I18n {
280
312
  constructor(translations = {}, config = {}) {
281
313
  var _config$locale;
@@ -480,22 +512,33 @@
480
512
  * of the available characters/words has been entered.
481
513
  *
482
514
  * @preserve
515
+ * @augments ConfigurableComponent<CharacterCountConfig>
483
516
  */
484
- class CharacterCount extends GOVUKFrontendComponent {
517
+ class CharacterCount extends ConfigurableComponent {
518
+ [configOverride](datasetConfig) {
519
+ let configOverrides = {};
520
+ if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
521
+ configOverrides = {
522
+ maxlength: undefined,
523
+ maxwords: undefined
524
+ };
525
+ }
526
+ return configOverrides;
527
+ }
528
+
485
529
  /**
486
530
  * @param {Element | null} $root - HTML element to use for character count
487
531
  * @param {CharacterCountConfig} [config] - Character count config
488
532
  */
489
533
  constructor($root, config = {}) {
490
534
  var _ref, _this$config$maxwords;
491
- super($root);
535
+ super($root, config);
492
536
  this.$textarea = void 0;
493
537
  this.$visibleCountMessage = void 0;
494
538
  this.$screenReaderCountMessage = void 0;
495
539
  this.lastInputTimestamp = null;
496
540
  this.lastInputValue = '';
497
541
  this.valueChecker = null;
498
- this.config = void 0;
499
542
  this.i18n = void 0;
500
543
  this.maxLength = void 0;
501
544
  const $textarea = this.$root.querySelector('.govuk-js-character-count');
@@ -507,15 +550,6 @@
507
550
  identifier: 'Form field (`.govuk-js-character-count`)'
508
551
  });
509
552
  }
510
- const datasetConfig = normaliseDataset(CharacterCount, this.$root.dataset);
511
- let configOverrides = {};
512
- if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
513
- configOverrides = {
514
- maxlength: undefined,
515
- maxwords: undefined
516
- };
517
- }
518
- this.config = mergeConfigs(CharacterCount.defaults, config, configOverrides, datasetConfig);
519
553
  const errors = validateConfig(CharacterCount.schema, this.config);
520
554
  if (errors[0]) {
521
555
  throw new ConfigError(formatErrorMessage(CharacterCount, errors[0]));
@@ -701,7 +735,7 @@
701
735
  */
702
736
 
703
737
  /**
704
- * @typedef {import('../../common/index.mjs').Schema} Schema
738
+ * @typedef {import('../../common/configuration.mjs').Schema} Schema
705
739
  * @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
706
740
  */
707
741
  CharacterCount.moduleName = 'govuk-character-count';