govuk_publishing_components 44.1.0 → 44.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/govuk_publishing_components/components/_share-links.scss +32 -0
  3. data/app/views/govuk_publishing_components/components/_share_links.html.erb +4 -1
  4. data/app/views/govuk_publishing_components/components/docs/share_links.yml +36 -0
  5. data/lib/govuk_publishing_components/version.rb +1 -1
  6. data/node_modules/govuk-frontend/dist/govuk/all.bundle.js +336 -225
  7. data/node_modules/govuk-frontend/dist/govuk/all.bundle.js.map +1 -1
  8. data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs +334 -226
  9. data/node_modules/govuk-frontend/dist/govuk/all.bundle.mjs.map +1 -1
  10. data/node_modules/govuk-frontend/dist/govuk/all.mjs +3 -0
  11. data/node_modules/govuk-frontend/dist/govuk/all.mjs.map +1 -1
  12. data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest.svg +1 -0
  13. data/node_modules/govuk-frontend/dist/govuk/common/govuk-frontend-version.mjs +1 -1
  14. data/node_modules/govuk-frontend/dist/govuk/common/index.mjs +21 -1
  15. data/node_modules/govuk-frontend/dist/govuk/common/index.mjs.map +1 -1
  16. data/node_modules/govuk-frontend/dist/govuk/components/_index.scss +1 -0
  17. data/node_modules/govuk-frontend/dist/govuk/components/_index.scss.map +1 -1
  18. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js +92 -26
  19. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.js.map +1 -1
  20. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs +92 -26
  21. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.bundle.mjs.map +1 -1
  22. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs +12 -21
  23. data/node_modules/govuk-frontend/dist/govuk/components/accordion/accordion.mjs.map +1 -1
  24. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js +86 -20
  25. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.js.map +1 -1
  26. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs +86 -20
  27. data/node_modules/govuk-frontend/dist/govuk/components/button/button.bundle.mjs.map +1 -1
  28. data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs +6 -16
  29. data/node_modules/govuk-frontend/dist/govuk/components/button/button.mjs.map +1 -1
  30. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js +89 -23
  31. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.js.map +1 -1
  32. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs +89 -23
  33. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.bundle.mjs.map +1 -1
  34. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs +10 -19
  35. data/node_modules/govuk-frontend/dist/govuk/components/character-count/character-count.mjs.map +1 -1
  36. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js +113 -47
  37. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.js.map +1 -1
  38. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs +113 -47
  39. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.bundle.mjs.map +1 -1
  40. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs +7 -16
  41. data/node_modules/govuk-frontend/dist/govuk/components/checkboxes/checkboxes.mjs.map +1 -1
  42. data/node_modules/govuk-frontend/dist/govuk/components/details/_index.scss +7 -2
  43. data/node_modules/govuk-frontend/dist/govuk/components/details/_index.scss.map +1 -1
  44. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js +86 -20
  45. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.js.map +1 -1
  46. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs +86 -20
  47. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.bundle.mjs.map +1 -1
  48. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs +6 -16
  49. data/node_modules/govuk-frontend/dist/govuk/components/error-summary/error-summary.mjs.map +1 -1
  50. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js +87 -21
  51. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.js.map +1 -1
  52. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs +87 -21
  53. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.bundle.mjs.map +1 -1
  54. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs +7 -16
  55. data/node_modules/govuk-frontend/dist/govuk/components/exit-this-page/exit-this-page.mjs.map +1 -1
  56. data/node_modules/govuk-frontend/dist/govuk/components/footer/_index.scss +8 -10
  57. data/node_modules/govuk-frontend/dist/govuk/components/footer/_index.scss.map +1 -1
  58. data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss +8 -0
  59. data/node_modules/govuk-frontend/dist/govuk/components/header/_index.scss.map +1 -1
  60. data/node_modules/govuk-frontend/dist/govuk/components/header/fixtures.json +12 -0
  61. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js +87 -21
  62. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.js.map +1 -1
  63. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs +87 -21
  64. data/node_modules/govuk-frontend/dist/govuk/components/header/header.bundle.mjs.map +1 -1
  65. data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs +7 -16
  66. data/node_modules/govuk-frontend/dist/govuk/components/header/header.mjs.map +1 -1
  67. data/node_modules/govuk-frontend/dist/govuk/components/header/template-with-full-width-border.html +24 -0
  68. data/node_modules/govuk-frontend/dist/govuk/components/notification-banner/notification-banner.bundle.js +86 -20
  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 +86 -20
  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 -16
  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/password-input.bundle.js +89 -23
  75. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.js.map +1 -1
  76. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs +89 -23
  77. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.bundle.mjs.map +1 -1
  78. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs +9 -18
  79. data/node_modules/govuk-frontend/dist/govuk/components/password-input/password-input.mjs.map +1 -1
  80. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js +113 -47
  81. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.js.map +1 -1
  82. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs +113 -47
  83. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.bundle.mjs.map +1 -1
  84. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs +7 -16
  85. data/node_modules/govuk-frontend/dist/govuk/components/radios/radios.mjs.map +1 -1
  86. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/README.md +15 -0
  87. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_index.scss +168 -0
  88. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_index.scss.map +1 -0
  89. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_service-navigation.scss +4 -0
  90. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/_service-navigation.scss.map +1 -0
  91. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/fixtures.json +464 -0
  92. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/macro-options.json +138 -0
  93. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/macro.njk +3 -0
  94. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js +249 -0
  95. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.js.map +1 -0
  96. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs +241 -0
  97. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.bundle.mjs.map +1 -0
  98. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs +85 -0
  99. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/service-navigation.mjs.map +1 -0
  100. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-default.html +57 -0
  101. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-html-navigation-items.html +49 -0
  102. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-large-navigation.html +153 -0
  103. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-long-service-name.html +20 -0
  104. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-navigation-with-a-current-item.html +58 -0
  105. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-navigation-with-an-active-item.html +58 -0
  106. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-non-link-navigation-items.html +49 -0
  107. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-link.html +20 -0
  108. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-name-and-navigation.html +63 -0
  109. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template-with-service-name.html +18 -0
  110. data/node_modules/govuk-frontend/dist/govuk/components/service-navigation/template.njk +102 -0
  111. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js +93 -26
  112. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.js.map +1 -1
  113. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs +93 -26
  114. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.bundle.mjs.map +1 -1
  115. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs +13 -21
  116. data/node_modules/govuk-frontend/dist/govuk/components/skip-link/skip-link.mjs.map +1 -1
  117. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js +93 -27
  118. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.js.map +1 -1
  119. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs +93 -27
  120. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.bundle.mjs.map +1 -1
  121. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs +13 -22
  122. data/node_modules/govuk-frontend/dist/govuk/components/tabs/tabs.mjs.map +1 -1
  123. data/node_modules/govuk-frontend/dist/govuk/components/warning-text/_index.scss +4 -3
  124. data/node_modules/govuk-frontend/dist/govuk/components/warning-text/_index.scss.map +1 -1
  125. data/node_modules/govuk-frontend/dist/govuk/core/_govuk-frontend-properties.scss +1 -1
  126. data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs +16 -3
  127. data/node_modules/govuk-frontend/dist/govuk/errors/index.mjs.map +1 -1
  128. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend-component.mjs +49 -5
  129. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend-component.mjs.map +1 -1
  130. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css +2 -2
  131. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.css.map +1 -1
  132. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js +1 -1
  133. data/node_modules/govuk-frontend/dist/govuk/govuk-frontend.min.js.map +1 -1
  134. data/node_modules/govuk-frontend/dist/govuk/init.mjs +72 -10
  135. data/node_modules/govuk-frontend/dist/govuk/init.mjs.map +1 -1
  136. data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss +3 -0
  137. data/node_modules/govuk-frontend/dist/govuk/settings/_colours-organisations.scss.map +1 -1
  138. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +5 -1
  139. data/node_modules/govuk-frontend/package.json +8 -8
  140. metadata +29 -4
  141. data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest-2x.png +0 -0
  142. data/node_modules/govuk-frontend/dist/govuk/assets/images/govuk-crest.png +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"skip-link.bundle.mjs","sources":["../../../../src/govuk/common/index.mjs","../../../../src/govuk/errors/index.mjs","../../../../src/govuk/govuk-frontend-component.mjs","../../../../src/govuk/components/skip-link/skip-link.mjs"],"sourcesContent":["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","/**\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 { getFragmentFromUrl, setFocus } from '../../common/index.mjs'\nimport { ElementError } from '../../errors/index.mjs'\nimport { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'\n\n/**\n * Skip link component\n *\n * @preserve\n */\nexport class SkipLink extends GOVUKFrontendComponent {\n /** @private */\n $module\n\n /**\n * @param {Element | null} $module - HTML element to use for skip link\n * @throws {ElementError} when $module is not set or the wrong type\n * @throws {ElementError} when $module.hash does not contain a hash\n * @throws {ElementError} when the linked element is missing or the wrong type\n */\n constructor($module) {\n super()\n\n if (!($module instanceof HTMLAnchorElement)) {\n throw new ElementError({\n componentName: 'Skip link',\n element: $module,\n expectedType: 'HTMLAnchorElement',\n identifier: 'Root element (`$module`)'\n })\n }\n\n this.$module = $module\n\n const hash = this.$module.hash\n const href = this.$module.getAttribute('href') ?? ''\n\n /** @type {URL | undefined} */\n let url\n\n /**\n * Check for valid link URL\n *\n * {@link https://caniuse.com/url}\n * {@link https://url.spec.whatwg.org}\n *\n */\n try {\n url = new window.URL(this.$module.href)\n } catch (error) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) is invalid`\n )\n }\n\n // Return early for external URLs or links to other pages\n if (\n url.origin !== window.location.origin ||\n url.pathname !== window.location.pathname\n ) {\n return\n }\n\n const linkedElementId = getFragmentFromUrl(hash)\n\n // Check link path matching current page\n if (!linkedElementId) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) has no hash fragment`\n )\n }\n\n const $linkedElement = document.getElementById(linkedElementId)\n\n // Check for link target element\n if (!$linkedElement) {\n throw new ElementError({\n componentName: 'Skip link',\n element: $linkedElement,\n identifier: `Target content (\\`id=\"${linkedElementId}\"\\`)`\n })\n }\n\n /**\n * Focus the linked element on click\n *\n * Adds a helper CSS class to hide native focus styles,\n * but removes it on blur to restore native focus styles\n */\n this.$module.addEventListener('click', () =>\n setFocus($linkedElement, {\n onBeforeFocus() {\n $linkedElement.classList.add('govuk-skip-link-focused-element')\n },\n onBlur() {\n $linkedElement.classList.remove('govuk-skip-link-focused-element')\n }\n })\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-skip-link'\n}\n"],"names":["getFragmentFromUrl","url","includes","undefined","split","pop","setFocus","$element","options","_options$onBeforeFocu","isFocusable","getAttribute","setAttribute","onFocus","addEventListener","onBlur","once","_options$onBlur","call","removeAttribute","onBeforeFocus","focus","isSupported","$scope","document","body","classList","contains","GOVUKFrontendError","Error","constructor","args","name","SupportError","supportMessage","HTMLScriptElement","prototype","ElementError","messageOrOptions","message","componentName","identifier","element","expectedType","GOVUKFrontendComponent","checkSupport","SkipLink","$module","_this$$module$getAttr","HTMLAnchorElement","hash","href","window","URL","error","origin","location","pathname","linkedElementId","$linkedElement","getElementById","add","remove","moduleName"],"mappings":"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"}
1
+ {"version":3,"file":"skip-link.bundle.mjs","sources":["../../../../src/govuk/common/index.mjs","../../../../src/govuk/errors/index.mjs","../../../../src/govuk/govuk-frontend-component.mjs","../../../../src/govuk/components/skip-link/skip-link.mjs"],"sourcesContent":["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 { 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 { getFragmentFromUrl, setFocus } from '../../common/index.mjs'\nimport { ElementError } from '../../errors/index.mjs'\nimport { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'\n\n/**\n * Skip link component\n *\n * @preserve\n * @augments GOVUKFrontendComponent<HTMLAnchorElement>\n */\nexport class SkipLink extends GOVUKFrontendComponent {\n static elementType = HTMLAnchorElement\n\n /**\n * @param {Element | null} $root - HTML element to use for skip link\n * @throws {ElementError} when $root is not set or the wrong type\n * @throws {ElementError} when $root.hash does not contain a hash\n * @throws {ElementError} when the linked element is missing or the wrong type\n */\n constructor($root) {\n super($root)\n\n const hash = this.$root.hash\n const href = this.$root.getAttribute('href') ?? ''\n\n /** @type {URL | undefined} */\n let url\n\n /**\n * Check for valid link URL\n *\n * {@link https://caniuse.com/url}\n * {@link https://url.spec.whatwg.org}\n *\n */\n try {\n url = new window.URL(this.$root.href)\n } catch (error) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) is invalid`\n )\n }\n\n // Return early for external URLs or links to other pages\n if (\n url.origin !== window.location.origin ||\n url.pathname !== window.location.pathname\n ) {\n return\n }\n\n const linkedElementId = getFragmentFromUrl(hash)\n\n // Check link path matching current page\n if (!linkedElementId) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) has no hash fragment`\n )\n }\n\n const $linkedElement = document.getElementById(linkedElementId)\n\n // Check for link target element\n if (!$linkedElement) {\n throw new ElementError({\n component: SkipLink,\n element: $linkedElement,\n identifier: `Target content (\\`id=\"${linkedElementId}\"\\`)`\n })\n }\n\n /**\n * Focus the linked element on click\n *\n * Adds a helper CSS class to hide native focus styles,\n * but removes it on blur to restore native focus styles\n */\n this.$root.addEventListener('click', () =>\n setFocus($linkedElement, {\n onBeforeFocus() {\n $linkedElement.classList.add('govuk-skip-link-focused-element')\n },\n onBlur() {\n $linkedElement.classList.remove('govuk-skip-link-focused-element')\n }\n })\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-skip-link'\n}\n"],"names":["getFragmentFromUrl","url","includes","undefined","split","pop","setFocus","$element","options","_options$onBeforeFocu","isFocusable","getAttribute","setAttribute","onFocus","addEventListener","onBlur","once","_options$onBlur","call","removeAttribute","onBeforeFocus","focus","isInitialised","$root","moduleName","HTMLElement","hasAttribute","isSupported","$scope","document","body","classList","contains","formatErrorMessage","Component","message","GOVUKFrontendError","Error","constructor","args","name","SupportError","supportMessage","HTMLScriptElement","prototype","ElementError","messageOrOptions","component","identifier","element","expectedType","InitError","componentOrMessage","GOVUKFrontendComponent","_$root","childConstructor","elementType","checkSupport","checkInitialised","SkipLink","_this$$root$getAttrib","hash","href","window","URL","error","origin","location","pathname","linkedElementId","$linkedElement","getElementById","add","remove","HTMLAnchorElement"],"mappings":"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"}
@@ -6,32 +6,23 @@ import { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs';
6
6
  * Skip link component
7
7
  *
8
8
  * @preserve
9
+ * @augments GOVUKFrontendComponent<HTMLAnchorElement>
9
10
  */
10
11
  class SkipLink extends GOVUKFrontendComponent {
11
12
  /**
12
- * @param {Element | null} $module - HTML element to use for skip link
13
- * @throws {ElementError} when $module is not set or the wrong type
14
- * @throws {ElementError} when $module.hash does not contain a hash
13
+ * @param {Element | null} $root - HTML element to use for skip link
14
+ * @throws {ElementError} when $root is not set or the wrong type
15
+ * @throws {ElementError} when $root.hash does not contain a hash
15
16
  * @throws {ElementError} when the linked element is missing or the wrong type
16
17
  */
17
- constructor($module) {
18
- var _this$$module$getAttr;
19
- super();
20
- this.$module = void 0;
21
- if (!($module instanceof HTMLAnchorElement)) {
22
- throw new ElementError({
23
- componentName: 'Skip link',
24
- element: $module,
25
- expectedType: 'HTMLAnchorElement',
26
- identifier: 'Root element (`$module`)'
27
- });
28
- }
29
- this.$module = $module;
30
- const hash = this.$module.hash;
31
- const href = (_this$$module$getAttr = this.$module.getAttribute('href')) != null ? _this$$module$getAttr : '';
18
+ constructor($root) {
19
+ var _this$$root$getAttrib;
20
+ super($root);
21
+ const hash = this.$root.hash;
22
+ const href = (_this$$root$getAttrib = this.$root.getAttribute('href')) != null ? _this$$root$getAttrib : '';
32
23
  let url;
33
24
  try {
34
- url = new window.URL(this.$module.href);
25
+ url = new window.URL(this.$root.href);
35
26
  } catch (error) {
36
27
  throw new ElementError(`Skip link: Target link (\`href="${href}"\`) is invalid`);
37
28
  }
@@ -45,12 +36,12 @@ class SkipLink extends GOVUKFrontendComponent {
45
36
  const $linkedElement = document.getElementById(linkedElementId);
46
37
  if (!$linkedElement) {
47
38
  throw new ElementError({
48
- componentName: 'Skip link',
39
+ component: SkipLink,
49
40
  element: $linkedElement,
50
41
  identifier: `Target content (\`id="${linkedElementId}"\`)`
51
42
  });
52
43
  }
53
- this.$module.addEventListener('click', () => setFocus($linkedElement, {
44
+ this.$root.addEventListener('click', () => setFocus($linkedElement, {
54
45
  onBeforeFocus() {
55
46
  $linkedElement.classList.add('govuk-skip-link-focused-element');
56
47
  },
@@ -60,6 +51,7 @@ class SkipLink extends GOVUKFrontendComponent {
60
51
  }));
61
52
  }
62
53
  }
54
+ SkipLink.elementType = HTMLAnchorElement;
63
55
  SkipLink.moduleName = 'govuk-skip-link';
64
56
 
65
57
  export { SkipLink };
@@ -1 +1 @@
1
- {"version":3,"file":"skip-link.mjs","sources":["../../../../src/govuk/components/skip-link/skip-link.mjs"],"sourcesContent":["import { getFragmentFromUrl, setFocus } from '../../common/index.mjs'\nimport { ElementError } from '../../errors/index.mjs'\nimport { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'\n\n/**\n * Skip link component\n *\n * @preserve\n */\nexport class SkipLink extends GOVUKFrontendComponent {\n /** @private */\n $module\n\n /**\n * @param {Element | null} $module - HTML element to use for skip link\n * @throws {ElementError} when $module is not set or the wrong type\n * @throws {ElementError} when $module.hash does not contain a hash\n * @throws {ElementError} when the linked element is missing or the wrong type\n */\n constructor($module) {\n super()\n\n if (!($module instanceof HTMLAnchorElement)) {\n throw new ElementError({\n componentName: 'Skip link',\n element: $module,\n expectedType: 'HTMLAnchorElement',\n identifier: 'Root element (`$module`)'\n })\n }\n\n this.$module = $module\n\n const hash = this.$module.hash\n const href = this.$module.getAttribute('href') ?? ''\n\n /** @type {URL | undefined} */\n let url\n\n /**\n * Check for valid link URL\n *\n * {@link https://caniuse.com/url}\n * {@link https://url.spec.whatwg.org}\n *\n */\n try {\n url = new window.URL(this.$module.href)\n } catch (error) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) is invalid`\n )\n }\n\n // Return early for external URLs or links to other pages\n if (\n url.origin !== window.location.origin ||\n url.pathname !== window.location.pathname\n ) {\n return\n }\n\n const linkedElementId = getFragmentFromUrl(hash)\n\n // Check link path matching current page\n if (!linkedElementId) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) has no hash fragment`\n )\n }\n\n const $linkedElement = document.getElementById(linkedElementId)\n\n // Check for link target element\n if (!$linkedElement) {\n throw new ElementError({\n componentName: 'Skip link',\n element: $linkedElement,\n identifier: `Target content (\\`id=\"${linkedElementId}\"\\`)`\n })\n }\n\n /**\n * Focus the linked element on click\n *\n * Adds a helper CSS class to hide native focus styles,\n * but removes it on blur to restore native focus styles\n */\n this.$module.addEventListener('click', () =>\n setFocus($linkedElement, {\n onBeforeFocus() {\n $linkedElement.classList.add('govuk-skip-link-focused-element')\n },\n onBlur() {\n $linkedElement.classList.remove('govuk-skip-link-focused-element')\n }\n })\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-skip-link'\n}\n"],"names":["SkipLink","GOVUKFrontendComponent","constructor","$module","_this$$module$getAttr","HTMLAnchorElement","ElementError","componentName","element","expectedType","identifier","hash","href","getAttribute","url","window","URL","error","origin","location","pathname","linkedElementId","getFragmentFromUrl","$linkedElement","document","getElementById","addEventListener","setFocus","onBeforeFocus","classList","add","onBlur","remove","moduleName"],"mappings":";;;;AAIA;AACA;AACA;AACA;AACA;AACO,MAAMA,QAAQ,SAASC,sBAAsB,CAAC;AAInD;AACF;AACA;AACA;AACA;AACA;EACEC,WAAWA,CAACC,OAAO,EAAE;AAAA,IAAA,IAAAC,qBAAA,CAAA;AACnB,IAAA,KAAK,EAAE,CAAA;AAAA,IAAA,IAAA,CATTD,OAAO,GAAA,KAAA,CAAA,CAAA;AAWL,IAAA,IAAI,EAAEA,OAAO,YAAYE,iBAAiB,CAAC,EAAE;MAC3C,MAAM,IAAIC,YAAY,CAAC;AACrBC,QAAAA,aAAa,EAAE,WAAW;AAC1BC,QAAAA,OAAO,EAAEL,OAAO;AAChBM,QAAAA,YAAY,EAAE,mBAAmB;AACjCC,QAAAA,UAAU,EAAE,0BAAA;AACd,OAAC,CAAC,CAAA;AACJ,KAAA;IAEA,IAAI,CAACP,OAAO,GAAGA,OAAO,CAAA;AAEtB,IAAA,MAAMQ,IAAI,GAAG,IAAI,CAACR,OAAO,CAACQ,IAAI,CAAA;AAC9B,IAAA,MAAMC,IAAI,GAAA,CAAAR,qBAAA,GAAG,IAAI,CAACD,OAAO,CAACU,YAAY,CAAC,MAAM,CAAC,KAAAT,IAAAA,GAAAA,qBAAA,GAAI,EAAE,CAAA;AAGpD,IAAA,IAAIU,GAAG,CAAA;IASP,IAAI;MACFA,GAAG,GAAG,IAAIC,MAAM,CAACC,GAAG,CAAC,IAAI,CAACb,OAAO,CAACS,IAAI,CAAC,CAAA;KACxC,CAAC,OAAOK,KAAK,EAAE;AACd,MAAA,MAAM,IAAIX,YAAY,CACpB,CAAmCM,gCAAAA,EAAAA,IAAI,iBACzC,CAAC,CAAA;AACH,KAAA;AAGA,IAAA,IACEE,GAAG,CAACI,MAAM,KAAKH,MAAM,CAACI,QAAQ,CAACD,MAAM,IACrCJ,GAAG,CAACM,QAAQ,KAAKL,MAAM,CAACI,QAAQ,CAACC,QAAQ,EACzC;AACA,MAAA,OAAA;AACF,KAAA;AAEA,IAAA,MAAMC,eAAe,GAAGC,kBAAkB,CAACX,IAAI,CAAC,CAAA;IAGhD,IAAI,CAACU,eAAe,EAAE;AACpB,MAAA,MAAM,IAAIf,YAAY,CACpB,CAAmCM,gCAAAA,EAAAA,IAAI,2BACzC,CAAC,CAAA;AACH,KAAA;AAEA,IAAA,MAAMW,cAAc,GAAGC,QAAQ,CAACC,cAAc,CAACJ,eAAe,CAAC,CAAA;IAG/D,IAAI,CAACE,cAAc,EAAE;MACnB,MAAM,IAAIjB,YAAY,CAAC;AACrBC,QAAAA,aAAa,EAAE,WAAW;AAC1BC,QAAAA,OAAO,EAAEe,cAAc;QACvBb,UAAU,EAAE,yBAAyBW,eAAe,CAAA,IAAA,CAAA;AACtD,OAAC,CAAC,CAAA;AACJ,KAAA;IAQA,IAAI,CAAClB,OAAO,CAACuB,gBAAgB,CAAC,OAAO,EAAE,MACrCC,QAAQ,CAACJ,cAAc,EAAE;AACvBK,MAAAA,aAAaA,GAAG;AACdL,QAAAA,cAAc,CAACM,SAAS,CAACC,GAAG,CAAC,iCAAiC,CAAC,CAAA;OAChE;AACDC,MAAAA,MAAMA,GAAG;AACPR,QAAAA,cAAc,CAACM,SAAS,CAACG,MAAM,CAAC,iCAAiC,CAAC,CAAA;AACpE,OAAA;AACF,KAAC,CACH,CAAC,CAAA;AACH,GAAA;AAMF,CAAA;AA/FahC,QAAQ,CA8FZiC,UAAU,GAAG,iBAAiB;;;;"}
1
+ {"version":3,"file":"skip-link.mjs","sources":["../../../../src/govuk/components/skip-link/skip-link.mjs"],"sourcesContent":["import { getFragmentFromUrl, setFocus } from '../../common/index.mjs'\nimport { ElementError } from '../../errors/index.mjs'\nimport { GOVUKFrontendComponent } from '../../govuk-frontend-component.mjs'\n\n/**\n * Skip link component\n *\n * @preserve\n * @augments GOVUKFrontendComponent<HTMLAnchorElement>\n */\nexport class SkipLink extends GOVUKFrontendComponent {\n static elementType = HTMLAnchorElement\n\n /**\n * @param {Element | null} $root - HTML element to use for skip link\n * @throws {ElementError} when $root is not set or the wrong type\n * @throws {ElementError} when $root.hash does not contain a hash\n * @throws {ElementError} when the linked element is missing or the wrong type\n */\n constructor($root) {\n super($root)\n\n const hash = this.$root.hash\n const href = this.$root.getAttribute('href') ?? ''\n\n /** @type {URL | undefined} */\n let url\n\n /**\n * Check for valid link URL\n *\n * {@link https://caniuse.com/url}\n * {@link https://url.spec.whatwg.org}\n *\n */\n try {\n url = new window.URL(this.$root.href)\n } catch (error) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) is invalid`\n )\n }\n\n // Return early for external URLs or links to other pages\n if (\n url.origin !== window.location.origin ||\n url.pathname !== window.location.pathname\n ) {\n return\n }\n\n const linkedElementId = getFragmentFromUrl(hash)\n\n // Check link path matching current page\n if (!linkedElementId) {\n throw new ElementError(\n `Skip link: Target link (\\`href=\"${href}\"\\`) has no hash fragment`\n )\n }\n\n const $linkedElement = document.getElementById(linkedElementId)\n\n // Check for link target element\n if (!$linkedElement) {\n throw new ElementError({\n component: SkipLink,\n element: $linkedElement,\n identifier: `Target content (\\`id=\"${linkedElementId}\"\\`)`\n })\n }\n\n /**\n * Focus the linked element on click\n *\n * Adds a helper CSS class to hide native focus styles,\n * but removes it on blur to restore native focus styles\n */\n this.$root.addEventListener('click', () =>\n setFocus($linkedElement, {\n onBeforeFocus() {\n $linkedElement.classList.add('govuk-skip-link-focused-element')\n },\n onBlur() {\n $linkedElement.classList.remove('govuk-skip-link-focused-element')\n }\n })\n )\n }\n\n /**\n * Name for the component used when initialising using data-module attributes.\n */\n static moduleName = 'govuk-skip-link'\n}\n"],"names":["SkipLink","GOVUKFrontendComponent","constructor","$root","_this$$root$getAttrib","hash","href","getAttribute","url","window","URL","error","ElementError","origin","location","pathname","linkedElementId","getFragmentFromUrl","$linkedElement","document","getElementById","component","element","identifier","addEventListener","setFocus","onBeforeFocus","classList","add","onBlur","remove","elementType","HTMLAnchorElement","moduleName"],"mappings":";;;;AAIA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMA,QAAQ,SAASC,sBAAsB,CAAC;AAGnD;AACF;AACA;AACA;AACA;AACA;EACEC,WAAWA,CAACC,KAAK,EAAE;AAAA,IAAA,IAAAC,qBAAA,CAAA;IACjB,KAAK,CAACD,KAAK,CAAC,CAAA;AAEZ,IAAA,MAAME,IAAI,GAAG,IAAI,CAACF,KAAK,CAACE,IAAI,CAAA;AAC5B,IAAA,MAAMC,IAAI,GAAA,CAAAF,qBAAA,GAAG,IAAI,CAACD,KAAK,CAACI,YAAY,CAAC,MAAM,CAAC,KAAAH,IAAAA,GAAAA,qBAAA,GAAI,EAAE,CAAA;AAGlD,IAAA,IAAII,GAAG,CAAA;IASP,IAAI;MACFA,GAAG,GAAG,IAAIC,MAAM,CAACC,GAAG,CAAC,IAAI,CAACP,KAAK,CAACG,IAAI,CAAC,CAAA;KACtC,CAAC,OAAOK,KAAK,EAAE;AACd,MAAA,MAAM,IAAIC,YAAY,CACpB,CAAmCN,gCAAAA,EAAAA,IAAI,iBACzC,CAAC,CAAA;AACH,KAAA;AAGA,IAAA,IACEE,GAAG,CAACK,MAAM,KAAKJ,MAAM,CAACK,QAAQ,CAACD,MAAM,IACrCL,GAAG,CAACO,QAAQ,KAAKN,MAAM,CAACK,QAAQ,CAACC,QAAQ,EACzC;AACA,MAAA,OAAA;AACF,KAAA;AAEA,IAAA,MAAMC,eAAe,GAAGC,kBAAkB,CAACZ,IAAI,CAAC,CAAA;IAGhD,IAAI,CAACW,eAAe,EAAE;AACpB,MAAA,MAAM,IAAIJ,YAAY,CACpB,CAAmCN,gCAAAA,EAAAA,IAAI,2BACzC,CAAC,CAAA;AACH,KAAA;AAEA,IAAA,MAAMY,cAAc,GAAGC,QAAQ,CAACC,cAAc,CAACJ,eAAe,CAAC,CAAA;IAG/D,IAAI,CAACE,cAAc,EAAE;MACnB,MAAM,IAAIN,YAAY,CAAC;AACrBS,QAAAA,SAAS,EAAErB,QAAQ;AACnBsB,QAAAA,OAAO,EAAEJ,cAAc;QACvBK,UAAU,EAAE,yBAAyBP,eAAe,CAAA,IAAA,CAAA;AACtD,OAAC,CAAC,CAAA;AACJ,KAAA;IAQA,IAAI,CAACb,KAAK,CAACqB,gBAAgB,CAAC,OAAO,EAAE,MACnCC,QAAQ,CAACP,cAAc,EAAE;AACvBQ,MAAAA,aAAaA,GAAG;AACdR,QAAAA,cAAc,CAACS,SAAS,CAACC,GAAG,CAAC,iCAAiC,CAAC,CAAA;OAChE;AACDC,MAAAA,MAAMA,GAAG;AACPX,QAAAA,cAAc,CAACS,SAAS,CAACG,MAAM,CAAC,iCAAiC,CAAC,CAAA;AACpE,OAAA;AACF,KAAC,CACH,CAAC,CAAA;AACH,GAAA;AAMF,CAAA;AAnFa9B,QAAQ,CACZ+B,WAAW,GAAGC,iBAAiB,CAAA;AAD3BhC,QAAQ,CAkFZiC,UAAU,GAAG,iBAAiB;;;;"}
@@ -18,12 +18,28 @@
18
18
  value: value || undefined
19
19
  };
20
20
  }
21
+ function isInitialised($root, moduleName) {
22
+ return $root instanceof HTMLElement && $root.hasAttribute(`data-${moduleName}-init`);
23
+ }
24
+
25
+ /**
26
+ * Checks if GOV.UK Frontend is supported on this page
27
+ *
28
+ * Some browsers will load and run our JavaScript but GOV.UK Frontend
29
+ * won't be supported.
30
+ *
31
+ * @param {HTMLElement | null} [$scope] - (internal) `<body>` HTML element checked for browser support
32
+ * @returns {boolean} Whether GOV.UK Frontend is supported on this page
33
+ */
21
34
  function isSupported($scope = document.body) {
22
35
  if (!$scope) {
23
36
  return false;
24
37
  }
25
38
  return $scope.classList.contains('govuk-frontend-supported');
26
39
  }
40
+ function formatErrorMessage(Component, message) {
41
+ return `${Component.moduleName}: ${message}`;
42
+ }
27
43
 
28
44
  /**
29
45
  * Schema for component config
@@ -47,6 +63,10 @@
47
63
  * @property {string[]} required - List of required config fields
48
64
  * @property {string} errorMessage - Error message when required config fields not provided
49
65
  */
66
+ /**
67
+ * @typedef ComponentWithModuleName
68
+ * @property {string} moduleName - Name of the component
69
+ */
50
70
 
51
71
  class GOVUKFrontendError extends Error {
52
72
  constructor(...args) {
@@ -71,30 +91,85 @@
71
91
  let message = typeof messageOrOptions === 'string' ? messageOrOptions : '';
72
92
  if (typeof messageOrOptions === 'object') {
73
93
  const {
74
- componentName,
94
+ component,
75
95
  identifier,
76
96
  element,
77
97
  expectedType
78
98
  } = messageOrOptions;
79
- message = `${componentName}: ${identifier}`;
99
+ message = identifier;
80
100
  message += element ? ` is not of type ${expectedType != null ? expectedType : 'HTMLElement'}` : ' not found';
101
+ message = formatErrorMessage(component, message);
81
102
  }
82
103
  super(message);
83
104
  this.name = 'ElementError';
84
105
  }
85
106
  }
107
+ class InitError extends GOVUKFrontendError {
108
+ constructor(componentOrMessage) {
109
+ const message = typeof componentOrMessage === 'string' ? componentOrMessage : formatErrorMessage(componentOrMessage, `Root element (\`$root\`) already initialised`);
110
+ super(message);
111
+ this.name = 'InitError';
112
+ }
113
+ }
114
+ /**
115
+ * @typedef {import('../common/index.mjs').ComponentWithModuleName} ComponentWithModuleName
116
+ */
86
117
 
87
118
  class GOVUKFrontendComponent {
88
- constructor() {
89
- this.checkSupport();
119
+ /**
120
+ * Returns the root element of the component
121
+ *
122
+ * @protected
123
+ * @returns {RootElementType} - the root element of component
124
+ */
125
+ get $root() {
126
+ return this._$root;
90
127
  }
91
- checkSupport() {
128
+ constructor($root) {
129
+ this._$root = void 0;
130
+ const childConstructor = this.constructor;
131
+ if (typeof childConstructor.moduleName !== 'string') {
132
+ throw new InitError(`\`moduleName\` not defined in component`);
133
+ }
134
+ if (!($root instanceof childConstructor.elementType)) {
135
+ throw new ElementError({
136
+ element: $root,
137
+ component: childConstructor,
138
+ identifier: 'Root element (`$root`)',
139
+ expectedType: childConstructor.elementType.name
140
+ });
141
+ } else {
142
+ this._$root = $root;
143
+ }
144
+ childConstructor.checkSupport();
145
+ this.checkInitialised();
146
+ const moduleName = childConstructor.moduleName;
147
+ this.$root.setAttribute(`data-${moduleName}-init`, '');
148
+ }
149
+ checkInitialised() {
150
+ const constructor = this.constructor;
151
+ const moduleName = constructor.moduleName;
152
+ if (moduleName && isInitialised(this.$root, moduleName)) {
153
+ throw new InitError(constructor);
154
+ }
155
+ }
156
+ static checkSupport() {
92
157
  if (!isSupported()) {
93
158
  throw new SupportError();
94
159
  }
95
160
  }
96
161
  }
97
162
 
163
+ /**
164
+ * @typedef ChildClass
165
+ * @property {string} moduleName - The module name that'll be looked for in the DOM when initialising the component
166
+ */
167
+
168
+ /**
169
+ * @typedef {typeof GOVUKFrontendComponent & ChildClass} ChildClassConstructor
170
+ */
171
+ GOVUKFrontendComponent.elementType = HTMLElement;
172
+
98
173
  /**
99
174
  * Tabs component
100
175
  *
@@ -102,11 +177,10 @@
102
177
  */
103
178
  class Tabs extends GOVUKFrontendComponent {
104
179
  /**
105
- * @param {Element | null} $module - HTML element to use for tabs
180
+ * @param {Element | null} $root - HTML element to use for tabs
106
181
  */
107
- constructor($module) {
108
- super();
109
- this.$module = void 0;
182
+ constructor($root) {
183
+ super($root);
110
184
  this.$tabs = void 0;
111
185
  this.$tabList = void 0;
112
186
  this.$tabListItems = void 0;
@@ -116,36 +190,28 @@
116
190
  this.boundTabKeydown = void 0;
117
191
  this.boundOnHashChange = void 0;
118
192
  this.mql = null;
119
- if (!$module) {
120
- throw new ElementError({
121
- componentName: 'Tabs',
122
- element: $module,
123
- identifier: 'Root element (`$module`)'
124
- });
125
- }
126
- const $tabs = $module.querySelectorAll('a.govuk-tabs__tab');
193
+ const $tabs = this.$root.querySelectorAll('a.govuk-tabs__tab');
127
194
  if (!$tabs.length) {
128
195
  throw new ElementError({
129
- componentName: 'Tabs',
196
+ component: Tabs,
130
197
  identifier: 'Links (`<a class="govuk-tabs__tab">`)'
131
198
  });
132
199
  }
133
- this.$module = $module;
134
200
  this.$tabs = $tabs;
135
201
  this.boundTabClick = this.onTabClick.bind(this);
136
202
  this.boundTabKeydown = this.onTabKeydown.bind(this);
137
203
  this.boundOnHashChange = this.onHashChange.bind(this);
138
- const $tabList = this.$module.querySelector('.govuk-tabs__list');
139
- const $tabListItems = this.$module.querySelectorAll('li.govuk-tabs__list-item');
204
+ const $tabList = this.$root.querySelector('.govuk-tabs__list');
205
+ const $tabListItems = this.$root.querySelectorAll('li.govuk-tabs__list-item');
140
206
  if (!$tabList) {
141
207
  throw new ElementError({
142
- componentName: 'Tabs',
208
+ component: Tabs,
143
209
  identifier: 'List (`<ul class="govuk-tabs__list">`)'
144
210
  });
145
211
  }
146
212
  if (!$tabListItems.length) {
147
213
  throw new ElementError({
148
- componentName: 'Tabs',
214
+ component: Tabs,
149
215
  identifier: 'List items (`<li class="govuk-tabs__list-item">`)'
150
216
  });
151
217
  }
@@ -157,7 +223,7 @@
157
223
  const breakpoint = getBreakpoint('tablet');
158
224
  if (!breakpoint.value) {
159
225
  throw new ElementError({
160
- componentName: 'Tabs',
226
+ component: Tabs,
161
227
  identifier: `CSS custom property (\`${breakpoint.property}\`) on pseudo-class \`:root\``
162
228
  });
163
229
  }
@@ -232,7 +298,7 @@
232
298
  this.showPanel($tab);
233
299
  }
234
300
  getTab(hash) {
235
- return this.$module.querySelector(`a.govuk-tabs__tab[href="${hash}"]`);
301
+ return this.$root.querySelector(`a.govuk-tabs__tab[href="${hash}"]`);
236
302
  }
237
303
  setAttributes($tab) {
238
304
  const panelId = getFragmentFromUrl($tab.href);
@@ -343,7 +409,7 @@
343
409
  if (!panelId) {
344
410
  return null;
345
411
  }
346
- return this.$module.querySelector(`#${panelId}`);
412
+ return this.$root.querySelector(`#${panelId}`);
347
413
  }
348
414
  showPanel($tab) {
349
415
  const $panel = this.getPanel($tab);
@@ -376,7 +442,7 @@
376
442
  $tab.setAttribute('tabindex', '0');
377
443
  }
378
444
  getCurrentTab() {
379
- return this.$module.querySelector('.govuk-tabs__list-item--selected a.govuk-tabs__tab');
445
+ return this.$root.querySelector('.govuk-tabs__list-item--selected a.govuk-tabs__tab');
380
446
  }
381
447
  }
382
448
  Tabs.moduleName = 'govuk-tabs';