govuk_publishing_components 35.3.3 → 35.3.4

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 (266) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/component_guide/application.scss +37 -1
  3. data/app/assets/stylesheets/govuk_publishing_components/components/_metadata.scss +10 -6
  4. data/app/assets/stylesheets/govuk_publishing_components/components/_organisation-logo.scss +2 -2
  5. data/app/views/govuk_publishing_components/component_guide/example.html.erb +1 -1
  6. data/app/views/govuk_publishing_components/component_guide/preview.html.erb +1 -1
  7. data/app/views/govuk_publishing_components/component_guide/show.html.erb +1 -1
  8. data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +2 -2
  9. data/app/views/govuk_publishing_components/components/_metadata.html.erb +1 -1
  10. data/app/views/govuk_publishing_components/components/_share_links.html.erb +6 -2
  11. data/app/views/govuk_publishing_components/components/_step_by_step_nav_related.html.erb +1 -1
  12. data/app/views/govuk_publishing_components/components/docs/metadata.yml +1 -3
  13. data/app/views/govuk_publishing_components/components/docs/organisation_logo.yml +5 -5
  14. data/app/views/govuk_publishing_components/components/docs/step_by_step_nav_related.yml +1 -0
  15. data/app/views/layouts/govuk_publishing_components/application.html.erb +1 -0
  16. data/lib/govuk_publishing_components/app_helpers/asset_helper.rb +4 -0
  17. data/lib/govuk_publishing_components/presenters/breadcrumbs_helper.rb +1 -1
  18. data/lib/govuk_publishing_components/version.rb +1 -1
  19. data/node_modules/axe-core/axe.d.ts +77 -6
  20. data/node_modules/axe-core/axe.js +955 -600
  21. data/node_modules/axe-core/axe.min.js +2 -2
  22. data/node_modules/axe-core/locales/_template.json +6 -6
  23. data/node_modules/axe-core/locales/ja.json +91 -11
  24. data/node_modules/axe-core/package.json +5 -5
  25. data/node_modules/axe-core/sri-history.json +4 -0
  26. data/node_modules/govuk-frontend/README.md +4 -5
  27. data/node_modules/govuk-frontend/govuk/all-ie8.scss +8 -0
  28. data/node_modules/govuk-frontend/govuk/all.js +738 -258
  29. data/node_modules/govuk-frontend/govuk/all.js.map +1 -1
  30. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +11 -7
  31. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js.map +1 -1
  32. data/node_modules/govuk-frontend/govuk/common/govuk-frontend-version.js +17 -0
  33. data/node_modules/govuk-frontend/govuk/common/govuk-frontend-version.js.map +1 -0
  34. data/node_modules/govuk-frontend/govuk/common/index.js +16 -4
  35. data/node_modules/govuk-frontend/govuk/common/index.js.map +1 -1
  36. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +20 -10
  37. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js.map +1 -1
  38. data/node_modules/govuk-frontend/govuk/common.js +16 -4
  39. data/node_modules/govuk-frontend/govuk/common.js.map +1 -1
  40. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +3 -3
  41. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +234 -86
  42. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js.map +1 -1
  43. data/node_modules/govuk-frontend/govuk/components/accordion/fixtures.json +32 -13
  44. data/node_modules/govuk-frontend/govuk/components/accordion/macro-options.json +8 -2
  45. data/node_modules/govuk-frontend/govuk/components/accordion/template.njk +1 -0
  46. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +1 -1
  47. data/node_modules/govuk-frontend/govuk/components/back-link/fixtures.json +1 -1
  48. data/node_modules/govuk-frontend/govuk/components/back-link/macro-options.json +1 -1
  49. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +1 -1
  50. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/fixtures.json +12 -12
  51. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/macro-options.json +1 -1
  52. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +8 -6
  53. data/node_modules/govuk-frontend/govuk/components/button/button.js +54 -19
  54. data/node_modules/govuk-frontend/govuk/components/button/button.js.map +1 -1
  55. data/node_modules/govuk-frontend/govuk/components/button/fixtures.json +11 -1
  56. data/node_modules/govuk-frontend/govuk/components/button/macro-options.json +7 -1
  57. data/node_modules/govuk-frontend/govuk/components/button/template.njk +1 -1
  58. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +170 -76
  59. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js.map +1 -1
  60. data/node_modules/govuk-frontend/govuk/components/character-count/fixtures.json +24 -24
  61. data/node_modules/govuk-frontend/govuk/components/character-count/macro-options.json +3 -3
  62. data/node_modules/govuk-frontend/govuk/components/checkboxes/_index.scss +6 -6
  63. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +75 -24
  64. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js.map +1 -1
  65. data/node_modules/govuk-frontend/govuk/components/checkboxes/fixtures.json +47 -47
  66. data/node_modules/govuk-frontend/govuk/components/checkboxes/macro-options.json +1 -1
  67. data/node_modules/govuk-frontend/govuk/components/checkboxes/template.njk +3 -3
  68. data/node_modules/govuk-frontend/govuk/components/cookie-banner/fixtures.json +24 -24
  69. data/node_modules/govuk-frontend/govuk/components/cookie-banner/macro-options.json +1 -1
  70. data/node_modules/govuk-frontend/govuk/components/date-input/fixtures.json +26 -26
  71. data/node_modules/govuk-frontend/govuk/components/date-input/macro-options.json +1 -1
  72. data/node_modules/govuk-frontend/govuk/components/date-input/template.njk +1 -1
  73. data/node_modules/govuk-frontend/govuk/components/details/details.js +39 -13
  74. data/node_modules/govuk-frontend/govuk/components/details/details.js.map +1 -1
  75. data/node_modules/govuk-frontend/govuk/components/details/fixtures.json +1 -1
  76. data/node_modules/govuk-frontend/govuk/components/details/macro-options.json +1 -1
  77. data/node_modules/govuk-frontend/govuk/components/error-message/fixtures.json +10 -10
  78. data/node_modules/govuk-frontend/govuk/components/error-message/macro-options.json +1 -1
  79. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +67 -27
  80. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js.map +1 -1
  81. data/node_modules/govuk-frontend/govuk/components/error-summary/fixtures.json +19 -19
  82. data/node_modules/govuk-frontend/govuk/components/error-summary/macro-options.json +1 -1
  83. data/node_modules/govuk-frontend/govuk/components/fieldset/fixtures.json +19 -19
  84. data/node_modules/govuk-frontend/govuk/components/fieldset/macro-options.json +1 -1
  85. data/node_modules/govuk-frontend/govuk/components/file-upload/_index.scss +6 -1
  86. data/node_modules/govuk-frontend/govuk/components/file-upload/fixtures.json +14 -14
  87. data/node_modules/govuk-frontend/govuk/components/file-upload/macro-options.json +7 -1
  88. data/node_modules/govuk-frontend/govuk/components/file-upload/template.njk +2 -1
  89. data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +0 -7
  90. data/node_modules/govuk-frontend/govuk/components/footer/fixtures.json +24 -24
  91. data/node_modules/govuk-frontend/govuk/components/footer/macro-options.json +1 -1
  92. data/node_modules/govuk-frontend/govuk/components/header/fixtures.json +22 -22
  93. data/node_modules/govuk-frontend/govuk/components/header/header.js +46 -14
  94. data/node_modules/govuk-frontend/govuk/components/header/header.js.map +1 -1
  95. data/node_modules/govuk-frontend/govuk/components/header/macro-options.json +1 -1
  96. data/node_modules/govuk-frontend/govuk/components/header/template.njk +1 -1
  97. data/node_modules/govuk-frontend/govuk/components/hint/fixtures.json +1 -1
  98. data/node_modules/govuk-frontend/govuk/components/hint/macro-options.json +1 -1
  99. data/node_modules/govuk-frontend/govuk/components/input/_index.scss +15 -3
  100. data/node_modules/govuk-frontend/govuk/components/input/fixtures.json +54 -41
  101. data/node_modules/govuk-frontend/govuk/components/input/macro-options.json +7 -1
  102. data/node_modules/govuk-frontend/govuk/components/input/template.njk +2 -1
  103. data/node_modules/govuk-frontend/govuk/components/inset-text/fixtures.json +1 -1
  104. data/node_modules/govuk-frontend/govuk/components/inset-text/macro-options.json +1 -1
  105. data/node_modules/govuk-frontend/govuk/components/label/fixtures.json +6 -6
  106. data/node_modules/govuk-frontend/govuk/components/label/macro-options.json +1 -1
  107. data/node_modules/govuk-frontend/govuk/components/notification-banner/fixtures.json +1 -1
  108. data/node_modules/govuk-frontend/govuk/components/notification-banner/macro-options.json +1 -1
  109. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +49 -22
  110. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js.map +1 -1
  111. data/node_modules/govuk-frontend/govuk/components/pagination/fixtures.json +16 -16
  112. data/node_modules/govuk-frontend/govuk/components/pagination/macro-options.json +1 -1
  113. data/node_modules/govuk-frontend/govuk/components/panel/fixtures.json +10 -10
  114. data/node_modules/govuk-frontend/govuk/components/panel/macro-options.json +1 -1
  115. data/node_modules/govuk-frontend/govuk/components/phase-banner/fixtures.json +1 -1
  116. data/node_modules/govuk-frontend/govuk/components/phase-banner/macro-options.json +1 -1
  117. data/node_modules/govuk-frontend/govuk/components/radios/_index.scss +5 -5
  118. data/node_modules/govuk-frontend/govuk/components/radios/fixtures.json +46 -46
  119. data/node_modules/govuk-frontend/govuk/components/radios/macro-options.json +1 -1
  120. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +65 -22
  121. data/node_modules/govuk-frontend/govuk/components/radios/radios.js.map +1 -1
  122. data/node_modules/govuk-frontend/govuk/components/radios/template.njk +2 -2
  123. data/node_modules/govuk-frontend/govuk/components/select/_index.scss +7 -1
  124. data/node_modules/govuk-frontend/govuk/components/select/fixtures.json +19 -20
  125. data/node_modules/govuk-frontend/govuk/components/select/macro-options.json +7 -1
  126. data/node_modules/govuk-frontend/govuk/components/select/template.njk +5 -2
  127. data/node_modules/govuk-frontend/govuk/components/skip-link/fixtures.json +1 -1
  128. data/node_modules/govuk-frontend/govuk/components/skip-link/macro-options.json +1 -1
  129. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +41 -13
  130. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js.map +1 -1
  131. data/node_modules/govuk-frontend/govuk/components/summary-list/fixtures.json +34 -34
  132. data/node_modules/govuk-frontend/govuk/components/summary-list/macro-options.json +1 -1
  133. data/node_modules/govuk-frontend/govuk/components/table/fixtures.json +21 -21
  134. data/node_modules/govuk-frontend/govuk/components/table/macro-options.json +1 -1
  135. data/node_modules/govuk-frontend/govuk/components/tabs/fixtures.json +15 -15
  136. data/node_modules/govuk-frontend/govuk/components/tabs/macro-options.json +1 -1
  137. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +178 -60
  138. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js.map +1 -1
  139. data/node_modules/govuk-frontend/govuk/components/tabs/template.njk +1 -1
  140. data/node_modules/govuk-frontend/govuk/components/tag/_index.scss +18 -18
  141. data/node_modules/govuk-frontend/govuk/components/tag/fixtures.json +1 -1
  142. data/node_modules/govuk-frontend/govuk/components/tag/macro-options.json +1 -1
  143. data/node_modules/govuk-frontend/govuk/components/textarea/_index.scss +8 -1
  144. data/node_modules/govuk-frontend/govuk/components/textarea/fixtures.json +18 -18
  145. data/node_modules/govuk-frontend/govuk/components/textarea/macro-options.json +7 -1
  146. data/node_modules/govuk-frontend/govuk/components/textarea/template.njk +2 -1
  147. data/node_modules/govuk-frontend/govuk/components/warning-text/fixtures.json +9 -5
  148. data/node_modules/govuk-frontend/govuk/components/warning-text/macro-options.json +3 -3
  149. data/node_modules/govuk-frontend/govuk/components/warning-text/template.njk +1 -1
  150. data/node_modules/govuk-frontend/govuk/core/_all.scss +1 -0
  151. data/node_modules/govuk-frontend/govuk/core/_govuk-frontend-version.scss +5 -0
  152. data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +5 -2
  153. data/node_modules/govuk-frontend/govuk/helpers/_focused.scss +1 -1
  154. data/node_modules/govuk-frontend/govuk/helpers/_font-faces.scss +1 -1
  155. data/node_modules/govuk-frontend/govuk/i18n.js +38 -30
  156. data/node_modules/govuk-frontend/govuk/i18n.js.map +1 -1
  157. data/node_modules/govuk-frontend/govuk/macros/i18n.njk +12 -11
  158. data/node_modules/govuk-frontend/govuk/objects/_width-container.scss +1 -1
  159. data/node_modules/govuk-frontend/govuk/settings/_colours-organisations.scss +4 -0
  160. data/node_modules/govuk-frontend/govuk/settings/_ie8.scss +16 -0
  161. data/node_modules/govuk-frontend/govuk/settings/_links.scss +5 -1
  162. data/node_modules/govuk-frontend/govuk/tools/_ie8.scss +38 -2
  163. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js +2 -1
  164. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js.map +1 -1
  165. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +2 -1
  166. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js.map +1 -1
  167. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js +2 -1
  168. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js.map +1 -1
  169. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js +10 -3
  170. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js.map +1 -1
  171. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js +4 -1
  172. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js.map +1 -1
  173. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +13 -7
  174. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js.map +1 -1
  175. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js +2 -1
  176. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js.map +1 -1
  177. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js +8 -2
  178. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js.map +1 -1
  179. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js +8 -2
  180. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js.map +1 -1
  181. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js +4 -1
  182. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js.map +1 -1
  183. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js +10 -3
  184. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js.map +1 -1
  185. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js +4 -1
  186. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js.map +1 -1
  187. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js +2 -1
  188. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js.map +1 -1
  189. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +3 -2
  190. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js.map +1 -1
  191. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js +2 -1
  192. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js.map +1 -1
  193. data/node_modules/govuk-frontend/govuk-esm/all.mjs +81 -60
  194. data/node_modules/govuk-frontend/govuk-esm/all.mjs.map +1 -0
  195. data/node_modules/govuk-frontend/govuk-esm/common/closest-attribute-value.mjs +12 -8
  196. data/node_modules/govuk-frontend/govuk-esm/common/closest-attribute-value.mjs.map +1 -0
  197. data/node_modules/govuk-frontend/govuk-esm/common/govuk-frontend-version.mjs +9 -0
  198. data/node_modules/govuk-frontend/govuk-esm/common/govuk-frontend-version.mjs.map +1 -0
  199. data/node_modules/govuk-frontend/govuk-esm/common/index.mjs +44 -29
  200. data/node_modules/govuk-frontend/govuk-esm/common/index.mjs.map +1 -0
  201. data/node_modules/govuk-frontend/govuk-esm/common/normalise-dataset.mjs +15 -9
  202. data/node_modules/govuk-frontend/govuk-esm/common/normalise-dataset.mjs.map +1 -0
  203. data/node_modules/govuk-frontend/govuk-esm/common.mjs +3 -5
  204. data/node_modules/govuk-frontend/govuk-esm/common.mjs.map +1 -0
  205. data/node_modules/govuk-frontend/govuk-esm/components/accordion/accordion.mjs +319 -211
  206. data/node_modules/govuk-frontend/govuk-esm/components/accordion/accordion.mjs.map +1 -0
  207. data/node_modules/govuk-frontend/govuk-esm/components/button/button.mjs +47 -30
  208. data/node_modules/govuk-frontend/govuk-esm/components/button/button.mjs.map +1 -0
  209. data/node_modules/govuk-frontend/govuk-esm/components/character-count/character-count.mjs +169 -115
  210. data/node_modules/govuk-frontend/govuk-esm/components/character-count/character-count.mjs.map +1 -0
  211. data/node_modules/govuk-frontend/govuk-esm/components/checkboxes/checkboxes.mjs +88 -51
  212. data/node_modules/govuk-frontend/govuk-esm/components/checkboxes/checkboxes.mjs.map +1 -0
  213. data/node_modules/govuk-frontend/govuk-esm/components/details/details.mjs +64 -54
  214. data/node_modules/govuk-frontend/govuk-esm/components/details/details.mjs.map +1 -0
  215. data/node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs +69 -50
  216. data/node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs.map +1 -0
  217. data/node_modules/govuk-frontend/govuk-esm/components/header/header.mjs +59 -35
  218. data/node_modules/govuk-frontend/govuk-esm/components/header/header.mjs.map +1 -0
  219. data/node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs +36 -25
  220. data/node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs.map +1 -0
  221. data/node_modules/govuk-frontend/govuk-esm/components/radios/radios.mjs +67 -38
  222. data/node_modules/govuk-frontend/govuk-esm/components/radios/radios.mjs.map +1 -0
  223. data/node_modules/govuk-frontend/govuk-esm/components/skip-link/skip-link.mjs +49 -32
  224. data/node_modules/govuk-frontend/govuk-esm/components/skip-link/skip-link.mjs.map +1 -0
  225. data/node_modules/govuk-frontend/govuk-esm/components/tabs/tabs.mjs +263 -163
  226. data/node_modules/govuk-frontend/govuk-esm/components/tabs/tabs.mjs.map +1 -0
  227. data/node_modules/govuk-frontend/govuk-esm/i18n.mjs +68 -57
  228. data/node_modules/govuk-frontend/govuk-esm/i18n.mjs.map +1 -0
  229. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/DOMTokenList.mjs +5 -3
  230. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/DOMTokenList.mjs.map +1 -0
  231. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Date/now.mjs +4 -2
  232. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Date/now.mjs.map +1 -0
  233. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Document.mjs +4 -2
  234. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Document.mjs.map +1 -0
  235. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/classList.mjs +7 -4
  236. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/classList.mjs.map +1 -0
  237. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/closest.mjs +5 -2
  238. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/closest.mjs.map +1 -0
  239. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/dataset.mjs +13 -10
  240. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/dataset.mjs.map +1 -0
  241. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/matches.mjs +4 -2
  242. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/matches.mjs.map +1 -0
  243. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/nextElementSibling.mjs +6 -3
  244. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/nextElementSibling.mjs.map +1 -0
  245. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/previousElementSibling.mjs +6 -3
  246. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/previousElementSibling.mjs.map +1 -0
  247. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element.mjs +5 -2
  248. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element.mjs.map +1 -0
  249. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Event.mjs +7 -4
  250. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Event.mjs.map +1 -0
  251. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Function/prototype/bind.mjs +5 -2
  252. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Function/prototype/bind.mjs.map +1 -0
  253. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Object/defineProperty.mjs +4 -2
  254. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Object/defineProperty.mjs.map +1 -0
  255. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/String/prototype/trim.mjs +5 -3
  256. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/String/prototype/trim.mjs.map +1 -0
  257. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Window.mjs +4 -2
  258. data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Window.mjs.map +1 -0
  259. data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +1 -0
  260. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +2 -1
  261. data/node_modules/govuk-frontend/package.json +3 -2
  262. metadata +43 -6
  263. /data/app/assets/images/govuk_publishing_components/crests/{dit_crest_13px.png → dbt_crest_13px.png} +0 -0
  264. /data/app/assets/images/govuk_publishing_components/crests/{dit_crest_13px_x2.png → dbt_crest_13px_x2.png} +0 -0
  265. /data/app/assets/images/govuk_publishing_components/crests/{dit_crest_18px.png → dbt_crest_18px.png} +0 -0
  266. /data/app/assets/images/govuk_publishing_components/crests/{dit_crest_18px_x2.png → dbt_crest_18px_x2.png} +0 -0
@@ -4,6 +4,13 @@
4
4
  (factory((global.GOVUKFrontend = {})));
5
5
  }(this, (function (exports) { 'use strict';
6
6
 
7
+ /*
8
+ * This variable is automatically overwritten during builds and releases.
9
+ * It doesn't need to be updated manually.
10
+ */
11
+
12
+ var version = '4.6.0';
13
+
7
14
  /**
8
15
  * Common helpers which do not require polyfill.
9
16
  *
@@ -19,8 +26,10 @@
19
26
  * This seems to fail in IE8, requires more investigation.
20
27
  * See: https://github.com/imagitama/nodelist-foreach-polyfill
21
28
  *
22
- * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
23
- * @param {nodeListIterator} callback - Callback function to run for each node
29
+ * @deprecated Will be made private in v5.0
30
+ * @template {Node} ElementType
31
+ * @param {NodeListOf<ElementType>} nodes - NodeList from querySelectorAll()
32
+ * @param {nodeListIterator<ElementType>} callback - Callback function to run for each node
24
33
  * @returns {void}
25
34
  */
26
35
  function nodeListForEach (nodes, callback) {
@@ -37,6 +46,7 @@
37
46
  * without them conflicting with each other.
38
47
  * https://stackoverflow.com/a/8809472
39
48
  *
49
+ * @deprecated Will be made private in v5.0
40
50
  * @returns {string} Unique ID
41
51
  */
42
52
  function generateUniqueID () {
@@ -58,6 +68,7 @@
58
68
  * (e.g. {'i18n.showSection': 'Show section'}) and combines them together, with
59
69
  * greatest priority on the LAST item passed in.
60
70
  *
71
+ * @deprecated Will be made private in v5.0
61
72
  * @returns {Object<string, unknown>} A flattened object of key-value pairs.
62
73
  */
63
74
  function mergeConfigs (/* configObject1, configObject2, ...configObjects */) {
@@ -72,6 +83,7 @@
72
83
  */
73
84
  var flattenObject = function (configObject) {
74
85
  // Prepare an empty return object
86
+ /** @type {Object<string, unknown>} */
75
87
  var flattenedObject = {};
76
88
 
77
89
  /**
@@ -108,6 +120,7 @@
108
120
  };
109
121
 
110
122
  // Start with an empty object as our base
123
+ /** @type {Object<string, unknown>} */
111
124
  var formattedConfigObject = {};
112
125
 
113
126
  // Loop through each of the remaining passed objects and push their keys
@@ -129,6 +142,7 @@
129
142
  * Extracts keys starting with a particular namespace from a flattened config
130
143
  * object, removing the namespace in the process.
131
144
  *
145
+ * @deprecated Will be made private in v5.0
132
146
  * @param {Object<string, unknown>} configObject - The object to extract key-value pairs from.
133
147
  * @param {string} namespace - The namespace to filter keys with.
134
148
  * @returns {Object<string, unknown>} Flattened object with dot-separated key namespace removed
@@ -140,10 +154,14 @@
140
154
  if (!configObject || typeof configObject !== 'object') {
141
155
  throw new Error('Provide a `configObject` of type "object".')
142
156
  }
157
+
143
158
  if (!namespace || typeof namespace !== 'string') {
144
159
  throw new Error('Provide a `namespace` of type "string" to filter the `configObject` by.')
145
160
  }
161
+
162
+ /** @type {Object<string, unknown>} */
146
163
  var newObject = {};
164
+
147
165
  for (var key in configObject) {
148
166
  // Split the key into parts, using . as our namespace separator
149
167
  var keyParts = key.split('.');
@@ -164,14 +182,16 @@
164
182
  }
165
183
 
166
184
  /**
185
+ * @template {Node} ElementType
167
186
  * @callback nodeListIterator
168
- * @param {Element} value - The current node being iterated on
187
+ * @param {ElementType} value - The current node being iterated on
169
188
  * @param {number} index - The current index in the iteration
170
- * @param {NodeListOf<Element>} nodes - NodeList from querySelectorAll()
189
+ * @param {NodeListOf<ElementType>} nodes - NodeList from querySelectorAll()
171
190
  * @returns {void}
172
191
  */
173
192
 
174
- (function(undefined) {
193
+ // @ts-nocheck
194
+ (function (undefined) {
175
195
 
176
196
  // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Object/defineProperty/detect.js
177
197
  var detect = (
@@ -258,7 +278,8 @@
258
278
  })
259
279
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
260
280
 
261
- (function(undefined) {
281
+ // @ts-nocheck
282
+ (function (undefined) {
262
283
 
263
284
  // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Document/detect.js
264
285
  var detect = ("Document" in this);
@@ -285,6 +306,8 @@
285
306
  })
286
307
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
287
308
 
309
+ // @ts-nocheck
310
+
288
311
  (function(undefined) {
289
312
 
290
313
  // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Element/detect.js
@@ -398,6 +421,8 @@
398
421
  })
399
422
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
400
423
 
424
+ // @ts-nocheck
425
+
401
426
  (function(undefined) {
402
427
 
403
428
  // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/13cf7c340974d128d557580b5e2dafcd1b1192d1/polyfills/Element/prototype/dataset/detect.js
@@ -418,10 +443,10 @@
418
443
  var element = this;
419
444
  var attributes = this.attributes;
420
445
  var map = {};
421
-
446
+
422
447
  for (var i = 0; i < attributes.length; i++) {
423
448
  var attribute = attributes[i];
424
-
449
+
425
450
  // This regex has been edited from the original polyfill, to add
426
451
  // support for period (.) separators in data-* attribute names. These
427
452
  // are allowed in the HTML spec, but were not covered by the original
@@ -429,11 +454,11 @@
429
454
  if (attribute && attribute.name && (/^data-\w[.\w-]*$/).test(attribute.name)) {
430
455
  var name = attribute.name;
431
456
  var value = attribute.value;
432
-
457
+
433
458
  var propName = name.substr(5).replace(/-./g, function (prop) {
434
459
  return prop.charAt(1).toUpperCase();
435
460
  });
436
-
461
+
437
462
  // If this browser supports __defineGetter__ and __defineSetter__,
438
463
  // continue using defineProperty. If not (like IE 8 and below), we use
439
464
  // a hacky fallback which at least gives an object in the right format
@@ -457,18 +482,19 @@
457
482
 
458
483
  }
459
484
  }
460
-
485
+
461
486
  return map;
462
487
  }
463
488
  });
464
489
 
465
490
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
466
491
 
467
- (function(undefined) {
492
+ // @ts-nocheck
493
+ (function (undefined) {
468
494
 
469
495
  // Detection from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
470
496
  var detect = ('trim' in String.prototype);
471
-
497
+
472
498
  if (detect) return
473
499
 
474
500
  // Polyfill from https://github.com/mdn/content/blob/cf607d68522cd35ee7670782d3ee3a361eaef2e4/files/en-us/web/javascript/reference/global_objects/string/trim/index.md#polyfill
@@ -491,6 +517,7 @@
491
517
  * Designed to be used to convert config passed via data attributes (which are
492
518
  * always strings) into something sensible.
493
519
  *
520
+ * @deprecated Will be made private in v5.0
494
521
  * @param {string} value - The value to normalise
495
522
  * @returns {string | boolean | number | undefined} Normalised data
496
523
  */
@@ -511,7 +538,7 @@
511
538
 
512
539
  // Empty / whitespace-only strings are considered finite so we need to check
513
540
  // the length of the trimmed string as well
514
- if (trimmedValue.length > 0 && isFinite(trimmedValue)) {
541
+ if (trimmedValue.length > 0 && isFinite(Number(trimmedValue))) {
515
542
  return Number(trimmedValue)
516
543
  }
517
544
 
@@ -523,10 +550,12 @@
523
550
  *
524
551
  * Loop over an object and normalise each value using normaliseData function
525
552
  *
553
+ * @deprecated Will be made private in v5.0
526
554
  * @param {DOMStringMap} dataset - HTML element dataset
527
555
  * @returns {Object<string, unknown>} Normalised dataset
528
556
  */
529
557
  function normaliseDataset (dataset) {
558
+ /** @type {Object<string, unknown>} */
530
559
  var out = {};
531
560
 
532
561
  for (var key in dataset) {
@@ -571,17 +600,17 @@
571
600
  }
572
601
 
573
602
  // If the `count` option is set, determine which plural suffix is needed and
574
- // change the lookupKey to match. We check to see if it's undefined instead of
603
+ // change the lookupKey to match. We check to see if it's numeric instead of
575
604
  // falsy, as this could legitimately be 0.
576
- if (options && typeof options.count !== 'undefined') {
605
+ if (options && typeof options.count === 'number') {
577
606
  // Get the plural suffix
578
607
  lookupKey = lookupKey + '.' + this.getPluralSuffix(lookupKey, options.count);
579
608
  }
580
609
 
581
- if (lookupKey in this.translations) {
582
- // Fetch the translation string for that lookup key
583
- var translationString = this.translations[lookupKey];
610
+ // Fetch the translation string for that lookup key
611
+ var translationString = this.translations[lookupKey];
584
612
 
613
+ if (typeof translationString === 'string') {
585
614
  // Check for ${} placeholders in the translation string
586
615
  if (translationString.match(/%{(.\S+)}/)) {
587
616
  if (!options) {
@@ -608,32 +637,46 @@
608
637
  * @returns {string} The translation string to output, with ${} placeholders replaced
609
638
  */
610
639
  I18n.prototype.replacePlaceholders = function (translationString, options) {
640
+ /** @type {Intl.NumberFormat | undefined} */
611
641
  var formatter;
612
642
 
613
643
  if (this.hasIntlNumberFormatSupport()) {
614
644
  formatter = new Intl.NumberFormat(this.locale);
615
645
  }
616
646
 
617
- return translationString.replace(/%{(.\S+)}/g, function (placeholderWithBraces, placeholderKey) {
618
- if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
619
- var placeholderValue = options[placeholderKey];
647
+ return translationString.replace(
648
+ /%{(.\S+)}/g,
620
649
 
621
- // If a user has passed `false` as the value for the placeholder
622
- // treat it as though the value should not be displayed
623
- if (placeholderValue === false) {
624
- return ''
625
- }
650
+ /**
651
+ * Replace translation string placeholders
652
+ *
653
+ * @param {string} placeholderWithBraces - Placeholder with braces
654
+ * @param {string} placeholderKey - Placeholder key
655
+ * @returns {string} Placeholder value
656
+ */
657
+ function (placeholderWithBraces, placeholderKey) {
658
+ if (Object.prototype.hasOwnProperty.call(options, placeholderKey)) {
659
+ var placeholderValue = options[placeholderKey];
660
+
661
+ // If a user has passed `false` as the value for the placeholder
662
+ // treat it as though the value should not be displayed
663
+ if (placeholderValue === false || (
664
+ typeof placeholderValue !== 'number' &&
665
+ typeof placeholderValue !== 'string')
666
+ ) {
667
+ return ''
668
+ }
626
669
 
627
- // If the placeholder's value is a number, localise the number formatting
628
- if (typeof placeholderValue === 'number' && formatter) {
629
- return formatter.format(placeholderValue)
630
- }
670
+ // If the placeholder's value is a number, localise the number formatting
671
+ if (typeof placeholderValue === 'number') {
672
+ return formatter ? formatter.format(placeholderValue) : placeholderValue.toString()
673
+ }
631
674
 
632
- return placeholderValue
633
- } else {
634
- throw new Error('i18n: no data found to replace ' + placeholderWithBraces + ' placeholder in string')
635
- }
636
- })
675
+ return placeholderValue
676
+ } else {
677
+ throw new Error('i18n: no data found to replace ' + placeholderWithBraces + ' placeholder in string')
678
+ }
679
+ })
637
680
  };
638
681
 
639
682
  /**
@@ -749,7 +792,7 @@
749
792
  * regardless of region. There are exceptions, however, (e.g. Portuguese) so
750
793
  * this searches by both the full and shortened locale codes, just to be sure.
751
794
  *
752
- * @returns {PluralRuleName | undefined} The name of the pluralisation rule to use (a key for one
795
+ * @returns {string | undefined} The name of the pluralisation rule to use (a key for one
753
796
  * of the functions in this.pluralRules)
754
797
  */
755
798
  I18n.prototype.getPluralRulesForLocale = function () {
@@ -800,7 +843,7 @@
800
843
  * Spanish: European Portuguese (pt-PT), Italian (it), Spanish (es)
801
844
  * Welsh: Welsh (cy)
802
845
  *
803
- * @type {Object<PluralRuleName, string[]>}
846
+ * @type {Object<string, string[]>}
804
847
  */
805
848
  I18n.pluralRulesMap = {
806
849
  arabic: ['ar'],
@@ -888,12 +931,6 @@
888
931
  /* eslint-enable jsdoc/require-jsdoc */
889
932
  };
890
933
 
891
- /**
892
- * Supported languages for plural rules
893
- *
894
- * @typedef {'arabic' | 'chinese' | 'french' | 'german' | 'irish' | 'russian' | 'scottish' | 'spanish' | 'welsh'} PluralRuleName
895
- */
896
-
897
934
  /**
898
935
  * Plural rule category mnemonic tags
899
936
  *
@@ -915,7 +952,8 @@
915
952
  * @property {string} [many] - Plural form used for many
916
953
  */
917
954
 
918
- (function(undefined) {
955
+ // @ts-nocheck
956
+ (function (undefined) {
919
957
 
920
958
  // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/master/packages/polyfill-library/polyfills/DOMTokenList/detect.js
921
959
  var detect = (
@@ -1180,6 +1218,8 @@
1180
1218
 
1181
1219
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1182
1220
 
1221
+ // @ts-nocheck
1222
+
1183
1223
  (function(undefined) {
1184
1224
 
1185
1225
  // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/8717a9e04ac7aff99b4980fbedead98036b0929a/packages/polyfill-library/polyfills/Element/prototype/classList/detect.js
@@ -1270,7 +1310,8 @@
1270
1310
 
1271
1311
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1272
1312
 
1273
- (function(undefined) {
1313
+ // @ts-nocheck
1314
+ (function (undefined) {
1274
1315
 
1275
1316
  // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/1f3c09b402f65bf6e393f933a15ba63f1b86ef1f/packages/polyfill-library/polyfills/Element/prototype/matches/detect.js
1276
1317
  var detect = (
@@ -1294,6 +1335,8 @@
1294
1335
 
1295
1336
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1296
1337
 
1338
+ // @ts-nocheck
1339
+
1297
1340
  (function(undefined) {
1298
1341
 
1299
1342
  // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-service/1f3c09b402f65bf6e393f933a15ba63f1b86ef1f/packages/polyfill-library/polyfills/Element/prototype/closest/detect.js
@@ -1317,7 +1360,8 @@
1317
1360
 
1318
1361
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1319
1362
 
1320
- (function(undefined) {
1363
+ // @ts-nocheck
1364
+ (function (undefined) {
1321
1365
 
1322
1366
  // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Window/detect.js
1323
1367
  var detect = ('Window' in this);
@@ -1338,6 +1382,8 @@
1338
1382
  })
1339
1383
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1340
1384
 
1385
+ // @ts-nocheck
1386
+
1341
1387
  (function(undefined) {
1342
1388
 
1343
1389
  // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Event/detect.js
@@ -1587,6 +1633,8 @@
1587
1633
  })
1588
1634
  .call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
1589
1635
 
1636
+ // @ts-nocheck
1637
+
1590
1638
  (function(undefined) {
1591
1639
  // Detection from https://github.com/Financial-Times/polyfill-service/blob/master/packages/polyfill-library/polyfills/Function/prototype/bind/detect.js
1592
1640
  var detect = 'bind' in Function.prototype;
@@ -1775,57 +1823,119 @@
1775
1823
  * attribute, which also provides accessibility.
1776
1824
  *
1777
1825
  * @class
1778
- * @param {HTMLElement} $module - HTML element to use for accordion
1826
+ * @param {Element} $module - HTML element to use for accordion
1779
1827
  * @param {AccordionConfig} [config] - Accordion config
1780
1828
  */
1781
1829
  function Accordion ($module, config) {
1830
+ if (!($module instanceof HTMLElement)) {
1831
+ return this
1832
+ }
1833
+
1834
+ /** @deprecated Will be made private in v5.0 */
1782
1835
  this.$module = $module;
1783
1836
 
1784
1837
  var defaultConfig = {
1785
- i18n: ACCORDION_TRANSLATIONS
1838
+ i18n: ACCORDION_TRANSLATIONS,
1839
+ rememberExpanded: true
1786
1840
  };
1787
1841
 
1842
+ /**
1843
+ * @deprecated Will be made private in v5.0
1844
+ * @type {AccordionConfig}
1845
+ */
1788
1846
  this.config = mergeConfigs(
1789
1847
  defaultConfig,
1790
1848
  config || {},
1791
1849
  normaliseDataset($module.dataset)
1792
1850
  );
1793
1851
 
1852
+ /** @deprecated Will be made private in v5.0 */
1794
1853
  this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'));
1795
1854
 
1855
+ /** @deprecated Will be made private in v5.0 */
1796
1856
  this.controlsClass = 'govuk-accordion__controls';
1857
+
1858
+ /** @deprecated Will be made private in v5.0 */
1797
1859
  this.showAllClass = 'govuk-accordion__show-all';
1860
+
1861
+ /** @deprecated Will be made private in v5.0 */
1798
1862
  this.showAllTextClass = 'govuk-accordion__show-all-text';
1799
1863
 
1864
+ /** @deprecated Will be made private in v5.0 */
1800
1865
  this.sectionClass = 'govuk-accordion__section';
1866
+
1867
+ /** @deprecated Will be made private in v5.0 */
1801
1868
  this.sectionExpandedClass = 'govuk-accordion__section--expanded';
1869
+
1870
+ /** @deprecated Will be made private in v5.0 */
1802
1871
  this.sectionButtonClass = 'govuk-accordion__section-button';
1872
+
1873
+ /** @deprecated Will be made private in v5.0 */
1803
1874
  this.sectionHeaderClass = 'govuk-accordion__section-header';
1875
+
1876
+ /** @deprecated Will be made private in v5.0 */
1804
1877
  this.sectionHeadingClass = 'govuk-accordion__section-heading';
1878
+
1879
+ /** @deprecated Will be made private in v5.0 */
1805
1880
  this.sectionHeadingDividerClass = 'govuk-accordion__section-heading-divider';
1881
+
1882
+ /** @deprecated Will be made private in v5.0 */
1806
1883
  this.sectionHeadingTextClass = 'govuk-accordion__section-heading-text';
1884
+
1885
+ /** @deprecated Will be made private in v5.0 */
1807
1886
  this.sectionHeadingTextFocusClass = 'govuk-accordion__section-heading-text-focus';
1808
1887
 
1888
+ /** @deprecated Will be made private in v5.0 */
1809
1889
  this.sectionShowHideToggleClass = 'govuk-accordion__section-toggle';
1890
+
1891
+ /** @deprecated Will be made private in v5.0 */
1810
1892
  this.sectionShowHideToggleFocusClass = 'govuk-accordion__section-toggle-focus';
1893
+
1894
+ /** @deprecated Will be made private in v5.0 */
1811
1895
  this.sectionShowHideTextClass = 'govuk-accordion__section-toggle-text';
1896
+
1897
+ /** @deprecated Will be made private in v5.0 */
1812
1898
  this.upChevronIconClass = 'govuk-accordion-nav__chevron';
1899
+
1900
+ /** @deprecated Will be made private in v5.0 */
1813
1901
  this.downChevronIconClass = 'govuk-accordion-nav__chevron--down';
1814
1902
 
1903
+ /** @deprecated Will be made private in v5.0 */
1815
1904
  this.sectionSummaryClass = 'govuk-accordion__section-summary';
1905
+
1906
+ /** @deprecated Will be made private in v5.0 */
1816
1907
  this.sectionSummaryFocusClass = 'govuk-accordion__section-summary-focus';
1908
+
1909
+ /** @deprecated Will be made private in v5.0 */
1817
1910
  this.sectionContentClass = 'govuk-accordion__section-content';
1818
1911
 
1819
- this.$sections = this.$module.querySelectorAll('.' + this.sectionClass);
1912
+ var $sections = this.$module.querySelectorAll('.' + this.sectionClass);
1913
+ if (!$sections.length) {
1914
+ return this
1915
+ }
1916
+
1917
+ /** @deprecated Will be made private in v5.0 */
1918
+ this.$sections = $sections;
1919
+
1920
+ /** @deprecated Will be made private in v5.0 */
1820
1921
  this.browserSupportsSessionStorage = helper.checkForSessionStorage();
1922
+
1923
+ /** @deprecated Will be made private in v5.0 */
1924
+ this.$showAllButton = null;
1925
+
1926
+ /** @deprecated Will be made private in v5.0 */
1927
+ this.$showAllIcon = null;
1928
+
1929
+ /** @deprecated Will be made private in v5.0 */
1930
+ this.$showAllText = null;
1821
1931
  }
1822
1932
 
1823
1933
  /**
1824
1934
  * Initialise component
1825
1935
  */
1826
1936
  Accordion.prototype.init = function () {
1827
- // Check for module
1828
- if (!this.$module) {
1937
+ // Check that required elements are present
1938
+ if (!this.$module || !this.$sections) {
1829
1939
  return
1830
1940
  }
1831
1941
 
@@ -1839,6 +1949,8 @@
1839
1949
 
1840
1950
  /**
1841
1951
  * Initialise controls and set attributes
1952
+ *
1953
+ * @deprecated Will be made private in v5.0
1842
1954
  */
1843
1955
  Accordion.prototype.initControls = function () {
1844
1956
  // Create "Show all" button and set attributes
@@ -1874,28 +1986,38 @@
1874
1986
 
1875
1987
  /**
1876
1988
  * Initialise section headers
1989
+ *
1990
+ * @deprecated Will be made private in v5.0
1877
1991
  */
1878
1992
  Accordion.prototype.initSectionHeaders = function () {
1879
- // Loop through section headers
1880
- nodeListForEach(this.$sections, function ($section, i) {
1993
+ var $component = this;
1994
+ var $sections = this.$sections;
1995
+
1996
+ // Loop through sections
1997
+ nodeListForEach($sections, function ($section, i) {
1998
+ var $header = $section.querySelector('.' + $component.sectionHeaderClass);
1999
+ if (!$header) {
2000
+ return
2001
+ }
2002
+
1881
2003
  // Set header attributes
1882
- var $header = $section.querySelector('.' + this.sectionHeaderClass);
1883
- this.constructHeaderMarkup($header, i);
1884
- this.setExpanded(this.isExpanded($section), $section);
2004
+ $component.constructHeaderMarkup($header, i);
2005
+ $component.setExpanded($component.isExpanded($section), $section);
1885
2006
 
1886
2007
  // Handle events
1887
- $header.addEventListener('click', this.onSectionToggle.bind(this, $section));
2008
+ $header.addEventListener('click', $component.onSectionToggle.bind($component, $section));
1888
2009
 
1889
2010
  // See if there is any state stored in sessionStorage and set the sections to
1890
2011
  // open or closed.
1891
- this.setInitialState($section);
1892
- }.bind(this));
2012
+ $component.setInitialState($section);
2013
+ });
1893
2014
  };
1894
2015
 
1895
2016
  /**
1896
2017
  * Construct section header
1897
2018
  *
1898
- * @param {HTMLDivElement} $header - Section header
2019
+ * @deprecated Will be made private in v5.0
2020
+ * @param {Element} $header - Section header
1899
2021
  * @param {number} index - Section index
1900
2022
  */
1901
2023
  Accordion.prototype.constructHeaderMarkup = function ($header, index) {
@@ -1903,10 +2025,14 @@
1903
2025
  var $heading = $header.querySelector('.' + this.sectionHeadingClass);
1904
2026
  var $summary = $header.querySelector('.' + this.sectionSummaryClass);
1905
2027
 
2028
+ if (!$span || !$heading) {
2029
+ return
2030
+ }
2031
+
1906
2032
  // Create a button element that will replace the '.govuk-accordion__section-button' span
1907
2033
  var $button = document.createElement('button');
1908
2034
  $button.setAttribute('type', 'button');
1909
- $button.setAttribute('aria-controls', this.$module.id + '-content-' + (index + 1));
2035
+ $button.setAttribute('aria-controls', this.$module.id + '-content-' + (index + 1).toString());
1910
2036
 
1911
2037
  // Copy all attributes (https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes) from $span to $button
1912
2038
  for (var i = 0; i < $span.attributes.length; i++) {
@@ -1960,7 +2086,7 @@
1960
2086
  $button.appendChild(this.getButtonPunctuationEl());
1961
2087
 
1962
2088
  // If summary content exists add to DOM in correct order
1963
- if (typeof ($summary) !== 'undefined' && $summary !== null) {
2089
+ if ($summary) {
1964
2090
  // Create a new `span` element and copy the summary line content from the original `div` to the
1965
2091
  // new `span`
1966
2092
  // This is because the summary line text is now inside a button element, which can only contain
@@ -1997,10 +2123,19 @@
1997
2123
  /**
1998
2124
  * When a section is opened by the user agent via the 'beforematch' event
1999
2125
  *
2126
+ * @deprecated Will be made private in v5.0
2000
2127
  * @param {Event} event - Generic event
2001
2128
  */
2002
2129
  Accordion.prototype.onBeforeMatch = function (event) {
2003
- var $section = event.target.closest('.' + this.sectionClass);
2130
+ var $fragment = event.target;
2131
+
2132
+ // Handle elements with `.closest()` support only
2133
+ if (!($fragment instanceof Element)) {
2134
+ return
2135
+ }
2136
+
2137
+ // Handle when fragment is inside section
2138
+ var $section = $fragment.closest('.' + this.sectionClass);
2004
2139
  if ($section) {
2005
2140
  this.setExpanded(true, $section);
2006
2141
  }
@@ -2009,7 +2144,8 @@
2009
2144
  /**
2010
2145
  * When section toggled, set and store state
2011
2146
  *
2012
- * @param {HTMLElement} $section - Section element
2147
+ * @deprecated Will be made private in v5.0
2148
+ * @param {Element} $section - Section element
2013
2149
  */
2014
2150
  Accordion.prototype.onSectionToggle = function ($section) {
2015
2151
  var expanded = this.isExpanded($section);
@@ -2021,26 +2157,31 @@
2021
2157
 
2022
2158
  /**
2023
2159
  * When Open/Close All toggled, set and store state
2160
+ *
2161
+ * @deprecated Will be made private in v5.0
2024
2162
  */
2025
2163
  Accordion.prototype.onShowOrHideAllToggle = function () {
2026
- var $module = this;
2164
+ var $component = this;
2027
2165
  var $sections = this.$sections;
2166
+
2028
2167
  var nowExpanded = !this.checkIfAllSectionsOpen();
2029
2168
 
2169
+ // Loop through sections
2030
2170
  nodeListForEach($sections, function ($section) {
2031
- $module.setExpanded(nowExpanded, $section);
2171
+ $component.setExpanded(nowExpanded, $section);
2032
2172
  // Store the state in sessionStorage when a change is triggered
2033
- $module.storeState($section);
2173
+ $component.storeState($section);
2034
2174
  });
2035
2175
 
2036
- $module.updateShowAllButton(nowExpanded);
2176
+ $component.updateShowAllButton(nowExpanded);
2037
2177
  };
2038
2178
 
2039
2179
  /**
2040
2180
  * Set section attributes when opened/closed
2041
2181
  *
2182
+ * @deprecated Will be made private in v5.0
2042
2183
  * @param {boolean} expanded - Section expanded
2043
- * @param {HTMLElement} $section - Section element
2184
+ * @param {Element} $section - Section element
2044
2185
  */
2045
2186
  Accordion.prototype.setExpanded = function (expanded, $section) {
2046
2187
  var $showHideIcon = $section.querySelector('.' + this.upChevronIconClass);
@@ -2048,23 +2189,30 @@
2048
2189
  var $button = $section.querySelector('.' + this.sectionButtonClass);
2049
2190
  var $content = $section.querySelector('.' + this.sectionContentClass);
2050
2191
 
2192
+ if (!$showHideIcon ||
2193
+ !($showHideText instanceof HTMLElement) ||
2194
+ !$button ||
2195
+ !$content) {
2196
+ return
2197
+ }
2198
+
2051
2199
  var newButtonText = expanded
2052
2200
  ? this.i18n.t('hideSection')
2053
2201
  : this.i18n.t('showSection');
2054
2202
 
2055
2203
  $showHideText.innerText = newButtonText;
2056
- $button.setAttribute('aria-expanded', expanded);
2204
+ $button.setAttribute('aria-expanded', expanded.toString());
2057
2205
 
2058
2206
  // Update aria-label combining
2059
2207
  var ariaLabelParts = [];
2060
2208
 
2061
2209
  var $headingText = $section.querySelector('.' + this.sectionHeadingTextClass);
2062
- if ($headingText) {
2210
+ if ($headingText instanceof HTMLElement) {
2063
2211
  ariaLabelParts.push($headingText.innerText.trim());
2064
2212
  }
2065
2213
 
2066
2214
  var $summary = $section.querySelector('.' + this.sectionSummaryClass);
2067
- if ($summary) {
2215
+ if ($summary instanceof HTMLElement) {
2068
2216
  ariaLabelParts.push($summary.innerText.trim());
2069
2217
  }
2070
2218
 
@@ -2099,7 +2247,8 @@
2099
2247
  /**
2100
2248
  * Get state of section
2101
2249
  *
2102
- * @param {HTMLElement} $section - Section element
2250
+ * @deprecated Will be made private in v5.0
2251
+ * @param {Element} $section - Section element
2103
2252
  * @returns {boolean} True if expanded
2104
2253
  */
2105
2254
  Accordion.prototype.isExpanded = function ($section) {
@@ -2109,6 +2258,7 @@
2109
2258
  /**
2110
2259
  * Check if all sections are open
2111
2260
  *
2261
+ * @deprecated Will be made private in v5.0
2112
2262
  * @returns {boolean} True if all sections are open
2113
2263
  */
2114
2264
  Accordion.prototype.checkIfAllSectionsOpen = function () {
@@ -2124,6 +2274,7 @@
2124
2274
  /**
2125
2275
  * Update "Show all sections" button
2126
2276
  *
2277
+ * @deprecated Will be made private in v5.0
2127
2278
  * @param {boolean} expanded - Section expanded
2128
2279
  */
2129
2280
  Accordion.prototype.updateShowAllButton = function (expanded) {
@@ -2131,7 +2282,7 @@
2131
2282
  ? this.i18n.t('hideAllSections')
2132
2283
  : this.i18n.t('showAllSections');
2133
2284
 
2134
- this.$showAllButton.setAttribute('aria-expanded', expanded);
2285
+ this.$showAllButton.setAttribute('aria-expanded', expanded.toString());
2135
2286
  this.$showAllText.innerText = newButtonText;
2136
2287
 
2137
2288
  // Swap icon, toggle class
@@ -2165,10 +2316,11 @@
2165
2316
  /**
2166
2317
  * Set the state of the accordions in sessionStorage
2167
2318
  *
2168
- * @param {HTMLElement} $section - Section element
2319
+ * @deprecated Will be made private in v5.0
2320
+ * @param {Element} $section - Section element
2169
2321
  */
2170
2322
  Accordion.prototype.storeState = function ($section) {
2171
- if (this.browserSupportsSessionStorage) {
2323
+ if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
2172
2324
  // We need a unique way of identifying each content in the Accordion. Since
2173
2325
  // an `#id` should be unique and an `id` is required for `aria-` attributes
2174
2326
  // `id` can be safely used.
@@ -2189,10 +2341,11 @@
2189
2341
  /**
2190
2342
  * Read the state of the accordions from sessionStorage
2191
2343
  *
2192
- * @param {HTMLElement} $section - Section element
2344
+ * @deprecated Will be made private in v5.0
2345
+ * @param {Element} $section - Section element
2193
2346
  */
2194
2347
  Accordion.prototype.setInitialState = function ($section) {
2195
- if (this.browserSupportsSessionStorage) {
2348
+ if (this.browserSupportsSessionStorage && this.config.rememberExpanded) {
2196
2349
  var $button = $section.querySelector('.' + this.sectionButtonClass);
2197
2350
 
2198
2351
  if ($button) {
@@ -2213,7 +2366,8 @@
2213
2366
  * into thematic chunks.
2214
2367
  * See https://github.com/alphagov/govuk-frontend/issues/2327#issuecomment-922957442
2215
2368
  *
2216
- * @returns {HTMLElement} DOM element
2369
+ * @deprecated Will be made private in v5.0
2370
+ * @returns {Element} DOM element
2217
2371
  */
2218
2372
  Accordion.prototype.getButtonPunctuationEl = function () {
2219
2373
  var $punctuationEl = document.createElement('span');
@@ -2227,6 +2381,8 @@
2227
2381
  *
2228
2382
  * @typedef {object} AccordionConfig
2229
2383
  * @property {AccordionTranslations} [i18n = ACCORDION_TRANSLATIONS] - See constant {@link ACCORDION_TRANSLATIONS}
2384
+ * @property {boolean} [rememberExpanded] - Whether the expanded and collapsed
2385
+ * state of each section is remembered and restored when navigating.
2230
2386
  */
2231
2387
 
2232
2388
  /**
@@ -2238,17 +2394,17 @@
2238
2394
  * the visible text shown on screen, and text to help assistive technology users
2239
2395
  * for the buttons toggling each section.
2240
2396
  * @property {string} [hideAllSections] - The text content for the 'Hide all
2241
- * sections' button, used when at least one section is expanded.
2397
+ * sections' button, used when at least one section is expanded.
2242
2398
  * @property {string} [hideSection] - The text content for the 'Hide'
2243
- * button, used when a section is expanded.
2399
+ * button, used when a section is expanded.
2244
2400
  * @property {string} [hideSectionAriaLabel] - The text content appended to the
2245
- * 'Hide' button's accessible name when a section is expanded.
2401
+ * 'Hide' button's accessible name when a section is expanded.
2246
2402
  * @property {string} [showAllSections] - The text content for the 'Show all
2247
- * sections' button, used when all sections are collapsed.
2403
+ * sections' button, used when all sections are collapsed.
2248
2404
  * @property {string} [showSection] - The text content for the 'Show'
2249
- * button, used when a section is collapsed.
2405
+ * button, used when a section is collapsed.
2250
2406
  * @property {string} [showSectionAriaLabel] - The text content appended to the
2251
- * 'Show' button's accessible name when a section is expanded.
2407
+ * 'Show' button's accessible name when a section is expanded.
2252
2408
  */
2253
2409
 
2254
2410
  /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
@@ -2260,20 +2416,28 @@
2260
2416
  * JavaScript enhancements for the Button component
2261
2417
  *
2262
2418
  * @class
2263
- * @param {HTMLElement} $module - HTML element to use for button
2419
+ * @param {Element} $module - HTML element to use for button
2264
2420
  * @param {ButtonConfig} [config] - Button config
2265
2421
  */
2266
2422
  function Button ($module, config) {
2267
- if (!$module) {
2423
+ if (!($module instanceof HTMLElement)) {
2268
2424
  return this
2269
2425
  }
2270
2426
 
2427
+ /** @deprecated Will be made private in v5.0 */
2271
2428
  this.$module = $module;
2429
+
2430
+ /** @deprecated Will be made private in v5.0 */
2272
2431
  this.debounceFormSubmitTimer = null;
2273
2432
 
2274
2433
  var defaultConfig = {
2275
2434
  preventDoubleClick: false
2276
2435
  };
2436
+
2437
+ /**
2438
+ * @deprecated Will be made private in v5.0
2439
+ * @type {ButtonConfig}
2440
+ */
2277
2441
  this.config = mergeConfigs(
2278
2442
  defaultConfig,
2279
2443
  config || {},
@@ -2285,6 +2449,7 @@
2285
2449
  * Initialise component
2286
2450
  */
2287
2451
  Button.prototype.init = function () {
2452
+ // Check that required elements are present
2288
2453
  if (!this.$module) {
2289
2454
  return
2290
2455
  }
@@ -2301,12 +2466,19 @@
2301
2466
  *
2302
2467
  * See https://github.com/alphagov/govuk_elements/pull/272#issuecomment-233028270
2303
2468
  *
2469
+ * @deprecated Will be made private in v5.0
2304
2470
  * @param {KeyboardEvent} event - Keydown event
2305
2471
  */
2306
2472
  Button.prototype.handleKeyDown = function (event) {
2307
2473
  var $target = event.target;
2308
2474
 
2309
- if ($target.getAttribute('role') === 'button' && event.keyCode === KEY_SPACE) {
2475
+ // Handle space bar only
2476
+ if (event.keyCode !== KEY_SPACE) {
2477
+ return
2478
+ }
2479
+
2480
+ // Handle elements with [role="button"] only
2481
+ if ($target instanceof HTMLElement && $target.getAttribute('role') === 'button') {
2310
2482
  event.preventDefault(); // prevent the page from scrolling
2311
2483
  $target.click();
2312
2484
  }
@@ -2319,6 +2491,7 @@
2319
2491
  * stops people accidentally causing multiple form submissions by double
2320
2492
  * clicking buttons.
2321
2493
  *
2494
+ * @deprecated Will be made private in v5.0
2322
2495
  * @param {MouseEvent} event - Mouse click event
2323
2496
  * @returns {undefined | false} Returns undefined, or false when debounced
2324
2497
  */
@@ -2343,26 +2516,27 @@
2343
2516
  * Button config
2344
2517
  *
2345
2518
  * @typedef {object} ButtonConfig
2346
- * @property {boolean} [preventDoubleClick = false] -
2347
- * Prevent accidental double clicks on submit buttons from submitting forms
2348
- * multiple times.
2519
+ * @property {boolean} [preventDoubleClick = false] - Prevent accidental double
2520
+ * clicks on submit buttons from submitting forms multiple times.
2349
2521
  */
2350
2522
 
2351
2523
  /**
2352
2524
  * Returns the value of the given attribute closest to the given element (including itself)
2353
2525
  *
2354
- * @param {HTMLElement} $element - The element to start walking the DOM tree up
2526
+ * @deprecated Will be made private in v5.0
2527
+ * @param {Element} $element - The element to start walking the DOM tree up
2355
2528
  * @param {string} attributeName - The name of the attribute
2356
- * @returns {string | undefined} Attribute value
2529
+ * @returns {string | null} Attribute value
2357
2530
  */
2358
2531
  function closestAttributeValue ($element, attributeName) {
2359
- var closestElementWithAttribute = $element.closest('[' + attributeName + ']');
2360
- if (closestElementWithAttribute) {
2361
- return closestElementWithAttribute.getAttribute(attributeName)
2362
- }
2532
+ var $closestElementWithAttribute = $element.closest('[' + attributeName + ']');
2533
+ return $closestElementWithAttribute
2534
+ ? $closestElementWithAttribute.getAttribute(attributeName)
2535
+ : null
2363
2536
  }
2364
2537
 
2365
- (function(undefined) {
2538
+ // @ts-nocheck
2539
+ (function (undefined) {
2366
2540
 
2367
2541
  // Detection from https://github.com/Financial-Times/polyfill-library/blob/v3.111.0/polyfills/Date/now/detect.js
2368
2542
  var detect = ('Date' in self && 'now' in self.Date && 'getTime' in self.Date.prototype);
@@ -2421,11 +2595,21 @@
2421
2595
  * of the available characters/words has been entered.
2422
2596
  *
2423
2597
  * @class
2424
- * @param {HTMLElement} $module - HTML element to use for character count
2598
+ * @param {Element} $module - HTML element to use for character count
2425
2599
  * @param {CharacterCountConfig} [config] - Character count config
2426
2600
  */
2427
2601
  function CharacterCount ($module, config) {
2428
- if (!$module) {
2602
+ if (!($module instanceof HTMLElement)) {
2603
+ return this
2604
+ }
2605
+
2606
+ var $textarea = $module.querySelector('.govuk-js-character-count');
2607
+ if (
2608
+ !(
2609
+ $textarea instanceof HTMLTextAreaElement ||
2610
+ $textarea instanceof HTMLInputElement
2611
+ )
2612
+ ) {
2429
2613
  return this
2430
2614
  }
2431
2615
 
@@ -2451,6 +2635,10 @@
2451
2635
  };
2452
2636
  }
2453
2637
 
2638
+ /**
2639
+ * @deprecated Will be made private in v5.0
2640
+ * @type {CharacterCountConfig}
2641
+ */
2454
2642
  this.config = mergeConfigs(
2455
2643
  defaultConfig,
2456
2644
  config || {},
@@ -2458,25 +2646,43 @@
2458
2646
  datasetConfig
2459
2647
  );
2460
2648
 
2649
+ /** @deprecated Will be made private in v5.0 */
2461
2650
  this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'), {
2462
2651
  // Read the fallback if necessary rather than have it set in the defaults
2463
2652
  locale: closestAttributeValue($module, 'lang')
2464
2653
  });
2465
2654
 
2655
+ /** @deprecated Will be made private in v5.0 */
2656
+ this.maxLength = Infinity;
2466
2657
  // Determine the limit attribute (characters or words)
2467
- if (this.config.maxwords) {
2658
+ if ('maxwords' in this.config && this.config.maxwords) {
2468
2659
  this.maxLength = this.config.maxwords;
2469
- } else if (this.config.maxlength) {
2660
+ } else if ('maxlength' in this.config && this.config.maxlength) {
2470
2661
  this.maxLength = this.config.maxlength;
2471
2662
  } else {
2472
2663
  return
2473
2664
  }
2474
2665
 
2666
+ /** @deprecated Will be made private in v5.0 */
2475
2667
  this.$module = $module;
2476
- this.$textarea = $module.querySelector('.govuk-js-character-count');
2668
+
2669
+ /** @deprecated Will be made private in v5.0 */
2670
+ this.$textarea = $textarea;
2671
+
2672
+ /** @deprecated Will be made private in v5.0 */
2477
2673
  this.$visibleCountMessage = null;
2674
+
2675
+ /** @deprecated Will be made private in v5.0 */
2478
2676
  this.$screenReaderCountMessage = null;
2677
+
2678
+ /** @deprecated Will be made private in v5.0 */
2479
2679
  this.lastInputTimestamp = null;
2680
+
2681
+ /** @deprecated Will be made private in v5.0 */
2682
+ this.lastInputValue = '';
2683
+
2684
+ /** @deprecated Will be made private in v5.0 */
2685
+ this.valueChecker = null;
2480
2686
  }
2481
2687
 
2482
2688
  /**
@@ -2484,14 +2690,17 @@
2484
2690
  */
2485
2691
  CharacterCount.prototype.init = function () {
2486
2692
  // Check that required elements are present
2487
- if (!this.$textarea) {
2693
+ if (!this.$module || !this.$textarea) {
2488
2694
  return
2489
2695
  }
2490
2696
 
2491
2697
  var $textarea = this.$textarea;
2492
2698
  var $textareaDescription = document.getElementById($textarea.id + '-info');
2699
+ if (!$textareaDescription) {
2700
+ return
2701
+ }
2493
2702
 
2494
- // Inject a decription for the textarea if none is present already
2703
+ // Inject a description for the textarea if none is present already
2495
2704
  // for when the component was rendered with no maxlength, maxwords
2496
2705
  // nor custom textareaDescriptionText
2497
2706
  if ($textareaDescription.innerText.match(/^\s*$/)) {
@@ -2532,11 +2741,11 @@
2532
2741
  // state of the character count is not restored until *after* the
2533
2742
  // DOMContentLoaded event is fired, so we need to manually update it after the
2534
2743
  // pageshow event in browsers that support it.
2535
- if ('onpageshow' in window) {
2536
- window.addEventListener('pageshow', this.updateCountMessage.bind(this));
2537
- } else {
2538
- window.addEventListener('DOMContentLoaded', this.updateCountMessage.bind(this));
2539
- }
2744
+ window.addEventListener(
2745
+ 'onpageshow' in window ? 'pageshow' : 'DOMContentLoaded',
2746
+ this.updateCountMessage.bind(this)
2747
+ );
2748
+
2540
2749
  this.updateCountMessage();
2541
2750
  };
2542
2751
 
@@ -2545,6 +2754,8 @@
2545
2754
  *
2546
2755
  * Set up event listeners on the $textarea so that the count messages update
2547
2756
  * when the user types.
2757
+ *
2758
+ * @deprecated Will be made private in v5.0
2548
2759
  */
2549
2760
  CharacterCount.prototype.bindChangeEvents = function () {
2550
2761
  var $textarea = this.$textarea;
@@ -2560,6 +2771,8 @@
2560
2771
  *
2561
2772
  * Update the visible character counter and keep track of when the last update
2562
2773
  * happened for each keypress
2774
+ *
2775
+ * @deprecated Will be made private in v5.0
2563
2776
  */
2564
2777
  CharacterCount.prototype.handleKeyUp = function () {
2565
2778
  this.updateVisibleCountMessage();
@@ -2578,6 +2791,8 @@
2578
2791
  *
2579
2792
  * This is so that the update triggered by the manual comparison doesn't
2580
2793
  * conflict with debounced KeyboardEvent updates.
2794
+ *
2795
+ * @deprecated Will be made private in v5.0
2581
2796
  */
2582
2797
  CharacterCount.prototype.handleFocus = function () {
2583
2798
  this.valueChecker = setInterval(function () {
@@ -2591,6 +2806,8 @@
2591
2806
  * Handle blur event
2592
2807
  *
2593
2808
  * Stop checking the textarea value once the textarea no longer has focus
2809
+ *
2810
+ * @deprecated Will be made private in v5.0
2594
2811
  */
2595
2812
  CharacterCount.prototype.handleBlur = function () {
2596
2813
  // Cancel value checking on blur
@@ -2599,11 +2816,12 @@
2599
2816
 
2600
2817
  /**
2601
2818
  * Update count message if textarea value has changed
2819
+ *
2820
+ * @deprecated Will be made private in v5.0
2602
2821
  */
2603
2822
  CharacterCount.prototype.updateIfValueChanged = function () {
2604
- if (!this.$textarea.oldValue) this.$textarea.oldValue = '';
2605
- if (this.$textarea.value !== this.$textarea.oldValue) {
2606
- this.$textarea.oldValue = this.$textarea.value;
2823
+ if (this.$textarea.value !== this.lastInputValue) {
2824
+ this.lastInputValue = this.$textarea.value;
2607
2825
  this.updateCountMessage();
2608
2826
  }
2609
2827
  };
@@ -2613,6 +2831,8 @@
2613
2831
  *
2614
2832
  * Helper function to update both the visible and screen reader-specific
2615
2833
  * counters simultaneously (e.g. on init)
2834
+ *
2835
+ * @deprecated Will be made private in v5.0
2616
2836
  */
2617
2837
  CharacterCount.prototype.updateCountMessage = function () {
2618
2838
  this.updateVisibleCountMessage();
@@ -2621,6 +2841,8 @@
2621
2841
 
2622
2842
  /**
2623
2843
  * Update visible count message
2844
+ *
2845
+ * @deprecated Will be made private in v5.0
2624
2846
  */
2625
2847
  CharacterCount.prototype.updateVisibleCountMessage = function () {
2626
2848
  var $textarea = this.$textarea;
@@ -2652,6 +2874,8 @@
2652
2874
 
2653
2875
  /**
2654
2876
  * Update screen reader count message
2877
+ *
2878
+ * @deprecated Will be made private in v5.0
2655
2879
  */
2656
2880
  CharacterCount.prototype.updateScreenReaderCountMessage = function () {
2657
2881
  var $screenReaderCountMessage = this.$screenReaderCountMessage;
@@ -2661,7 +2885,7 @@
2661
2885
  if (this.isOverThreshold()) {
2662
2886
  $screenReaderCountMessage.removeAttribute('aria-hidden');
2663
2887
  } else {
2664
- $screenReaderCountMessage.setAttribute('aria-hidden', true);
2888
+ $screenReaderCountMessage.setAttribute('aria-hidden', 'true');
2665
2889
  }
2666
2890
 
2667
2891
  // Update message
@@ -2672,11 +2896,12 @@
2672
2896
  * Count the number of characters (or words, if `config.maxwords` is set)
2673
2897
  * in the given text
2674
2898
  *
2899
+ * @deprecated Will be made private in v5.0
2675
2900
  * @param {string} text - The text to count the characters of
2676
2901
  * @returns {number} the number of characters (or words) in the text
2677
2902
  */
2678
2903
  CharacterCount.prototype.count = function (text) {
2679
- if (this.config.maxwords) {
2904
+ if ('maxwords' in this.config && this.config.maxwords) {
2680
2905
  var tokens = text.match(/\S+/g) || []; // Matches consecutive non-whitespace chars
2681
2906
  return tokens.length
2682
2907
  } else {
@@ -2687,12 +2912,13 @@
2687
2912
  /**
2688
2913
  * Get count message
2689
2914
  *
2915
+ * @deprecated Will be made private in v5.0
2690
2916
  * @returns {string} Status message
2691
2917
  */
2692
2918
  CharacterCount.prototype.getCountMessage = function () {
2693
2919
  var remainingNumber = this.maxLength - this.count(this.$textarea.value);
2694
2920
 
2695
- var countType = this.config.maxwords ? 'words' : 'characters';
2921
+ var countType = 'maxwords' in this.config && this.config.maxwords ? 'words' : 'characters';
2696
2922
  return this.formatCountMessage(remainingNumber, countType)
2697
2923
  };
2698
2924
 
@@ -2700,6 +2926,7 @@
2700
2926
  * Formats the message shown to users according to what's counted
2701
2927
  * and how many remain
2702
2928
  *
2929
+ * @deprecated Will be made private in v5.0
2703
2930
  * @param {number} remainingNumber - The number of words/characaters remaining
2704
2931
  * @param {string} countType - "words" or "characters"
2705
2932
  * @returns {string} Status message
@@ -2721,6 +2948,7 @@
2721
2948
  * If there is no configured threshold, it is set to 0 and this function will
2722
2949
  * always return true.
2723
2950
  *
2951
+ * @deprecated Will be made private in v5.0
2724
2952
  * @returns {boolean} true if the current count is over the config.threshold
2725
2953
  * (or no threshold is set)
2726
2954
  */
@@ -2752,10 +2980,10 @@
2752
2980
  *
2753
2981
  * @typedef {object} CharacterCountConfigWithMaxLength
2754
2982
  * @property {number} [maxlength] - The maximum number of characters.
2755
- * If maxwords is provided, the maxlength option will be ignored.
2983
+ * If maxwords is provided, the maxlength option will be ignored.
2756
2984
  * @property {number} [threshold = 0] - The percentage value of the limit at
2757
- * which point the count message is displayed. If this attribute is set, the
2758
- * count message will be hidden by default.
2985
+ * which point the count message is displayed. If this attribute is set, the
2986
+ * count message will be hidden by default.
2759
2987
  * @property {CharacterCountTranslations} [i18n = CHARACTER_COUNT_TRANSLATIONS] - See constant {@link CHARACTER_COUNT_TRANSLATIONS}
2760
2988
  */
2761
2989
 
@@ -2764,10 +2992,10 @@
2764
2992
  *
2765
2993
  * @typedef {object} CharacterCountConfigWithMaxWords
2766
2994
  * @property {number} [maxwords] - The maximum number of words. If maxwords is
2767
- * provided, the maxlength option will be ignored.
2995
+ * provided, the maxlength option will be ignored.
2768
2996
  * @property {number} [threshold = 0] - The percentage value of the limit at
2769
- * which point the count message is displayed. If this attribute is set, the
2770
- * count message will be hidden by default.
2997
+ * which point the count message is displayed. If this attribute is set, the
2998
+ * count message will be hidden by default.
2771
2999
  * @property {CharacterCountTranslations} [i18n = CHARACTER_COUNT_TRANSLATIONS] - See constant {@link CHARACTER_COUNT_TRANSLATIONS}
2772
3000
  */
2773
3001
 
@@ -2827,11 +3055,23 @@
2827
3055
  * Checkboxes component
2828
3056
  *
2829
3057
  * @class
2830
- * @param {HTMLElement} $module - HTML element to use for checkboxes
3058
+ * @param {Element} $module - HTML element to use for checkboxes
2831
3059
  */
2832
3060
  function Checkboxes ($module) {
3061
+ if (!($module instanceof HTMLElement)) {
3062
+ return this
3063
+ }
3064
+
3065
+ var $inputs = $module.querySelectorAll('input[type="checkbox"]');
3066
+ if (!$inputs.length) {
3067
+ return this
3068
+ }
3069
+
3070
+ /** @deprecated Will be made private in v5.0 */
2833
3071
  this.$module = $module;
2834
- this.$inputs = $module.querySelectorAll('input[type="checkbox"]');
3072
+
3073
+ /** @deprecated Will be made private in v5.0 */
3074
+ this.$inputs = $inputs;
2835
3075
  }
2836
3076
 
2837
3077
  /**
@@ -2849,6 +3089,11 @@
2849
3089
  * the reveal in sync with the checkbox state.
2850
3090
  */
2851
3091
  Checkboxes.prototype.init = function () {
3092
+ // Check that required elements are present
3093
+ if (!this.$module || !this.$inputs) {
3094
+ return
3095
+ }
3096
+
2852
3097
  var $module = this.$module;
2853
3098
  var $inputs = this.$inputs;
2854
3099
 
@@ -2871,11 +3116,10 @@
2871
3116
  // state of form controls is not restored until *after* the DOMContentLoaded
2872
3117
  // event is fired, so we need to sync after the pageshow event in browsers
2873
3118
  // that support it.
2874
- if ('onpageshow' in window) {
2875
- window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this));
2876
- } else {
2877
- window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.bind(this));
2878
- }
3119
+ window.addEventListener(
3120
+ 'onpageshow' in window ? 'pageshow' : 'DOMContentLoaded',
3121
+ this.syncAllConditionalReveals.bind(this)
3122
+ );
2879
3123
 
2880
3124
  // Although we've set up handlers to sync state on the pageshow or
2881
3125
  // DOMContentLoaded event, init could be called after those events have fired,
@@ -2888,6 +3132,8 @@
2888
3132
 
2889
3133
  /**
2890
3134
  * Sync the conditional reveal states for all checkboxes in this $module.
3135
+ *
3136
+ * @deprecated Will be made private in v5.0
2891
3137
  */
2892
3138
  Checkboxes.prototype.syncAllConditionalReveals = function () {
2893
3139
  nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this));
@@ -2899,15 +3145,20 @@
2899
3145
  * Synchronise the visibility of the conditional reveal, and its accessible
2900
3146
  * state, with the input's checked state.
2901
3147
  *
3148
+ * @deprecated Will be made private in v5.0
2902
3149
  * @param {HTMLInputElement} $input - Checkbox input
2903
3150
  */
2904
3151
  Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
2905
- var $target = document.getElementById($input.getAttribute('aria-controls'));
3152
+ var targetId = $input.getAttribute('aria-controls');
3153
+ if (!targetId) {
3154
+ return
3155
+ }
2906
3156
 
3157
+ var $target = document.getElementById(targetId);
2907
3158
  if ($target && $target.classList.contains('govuk-checkboxes__conditional')) {
2908
3159
  var inputIsChecked = $input.checked;
2909
3160
 
2910
- $input.setAttribute('aria-expanded', inputIsChecked);
3161
+ $input.setAttribute('aria-expanded', inputIsChecked.toString());
2911
3162
  $target.classList.toggle('govuk-checkboxes__conditional--hidden', !inputIsChecked);
2912
3163
  }
2913
3164
  };
@@ -2918,18 +3169,25 @@
2918
3169
  * Find any other checkbox inputs with the same name value, and uncheck them.
2919
3170
  * This is useful for when a “None of these" checkbox is checked.
2920
3171
  *
2921
- * @param {HTMLElement} $input - Checkbox input
3172
+ * @deprecated Will be made private in v5.0
3173
+ * @param {HTMLInputElement} $input - Checkbox input
2922
3174
  */
2923
3175
  Checkboxes.prototype.unCheckAllInputsExcept = function ($input) {
2924
- var allInputsWithSameName = document.querySelectorAll('input[type="checkbox"][name="' + $input.name + '"]');
3176
+ var $component = this;
3177
+
3178
+ /** @type {NodeListOf<HTMLInputElement>} */
3179
+ // @ts-expect-error `NodeListOf<HTMLInputElement>` type expected
3180
+ var allInputsWithSameName = document.querySelectorAll(
3181
+ 'input[type="checkbox"][name="' + $input.name + '"]'
3182
+ );
2925
3183
 
2926
3184
  nodeListForEach(allInputsWithSameName, function ($inputWithSameName) {
2927
3185
  var hasSameFormOwner = ($input.form === $inputWithSameName.form);
2928
3186
  if (hasSameFormOwner && $inputWithSameName !== $input) {
2929
3187
  $inputWithSameName.checked = false;
2930
- this.syncConditionalRevealWithInputState($inputWithSameName);
3188
+ $component.syncConditionalRevealWithInputState($inputWithSameName);
2931
3189
  }
2932
- }.bind(this));
3190
+ });
2933
3191
  };
2934
3192
 
2935
3193
  /**
@@ -2939,9 +3197,14 @@
2939
3197
  * and uncheck them. This helps prevent someone checking both a regular checkbox and a
2940
3198
  * "None of these" checkbox in the same fieldset.
2941
3199
  *
3200
+ * @deprecated Will be made private in v5.0
2942
3201
  * @param {HTMLInputElement} $input - Checkbox input
2943
3202
  */
2944
3203
  Checkboxes.prototype.unCheckExclusiveInputs = function ($input) {
3204
+ var $component = this;
3205
+
3206
+ /** @type {NodeListOf<HTMLInputElement>} */
3207
+ // @ts-expect-error `NodeListOf<HTMLInputElement>` type expected
2945
3208
  var allInputsWithSameNameAndExclusiveBehaviour = document.querySelectorAll(
2946
3209
  'input[data-behaviour="exclusive"][type="checkbox"][name="' + $input.name + '"]'
2947
3210
  );
@@ -2950,9 +3213,9 @@
2950
3213
  var hasSameFormOwner = ($input.form === $exclusiveInput.form);
2951
3214
  if (hasSameFormOwner) {
2952
3215
  $exclusiveInput.checked = false;
2953
- this.syncConditionalRevealWithInputState($exclusiveInput);
3216
+ $component.syncConditionalRevealWithInputState($exclusiveInput);
2954
3217
  }
2955
- }.bind(this));
3218
+ });
2956
3219
  };
2957
3220
 
2958
3221
  /**
@@ -2961,13 +3224,14 @@
2961
3224
  * Handle a click within the $module – if the click occurred on a checkbox, sync
2962
3225
  * the state of any associated conditional reveal with the checkbox state.
2963
3226
  *
3227
+ * @deprecated Will be made private in v5.0
2964
3228
  * @param {MouseEvent} event - Click event
2965
3229
  */
2966
3230
  Checkboxes.prototype.handleClick = function (event) {
2967
3231
  var $clickedInput = event.target;
2968
3232
 
2969
3233
  // Ignore clicks on things that aren't checkbox inputs
2970
- if ($clickedInput.type !== 'checkbox') {
3234
+ if (!($clickedInput instanceof HTMLInputElement) || $clickedInput.type !== 'checkbox') {
2971
3235
  return
2972
3236
  }
2973
3237
 
@@ -3000,32 +3264,45 @@
3000
3264
  * Details component
3001
3265
  *
3002
3266
  * @class
3003
- * @param {HTMLElement} $module - HTML element to use for details
3267
+ * @param {Element} $module - HTML element to use for details
3004
3268
  */
3005
3269
  function Details ($module) {
3270
+ if (!($module instanceof HTMLElement)) {
3271
+ return this
3272
+ }
3273
+
3274
+ /** @deprecated Will be made private in v5.0 */
3006
3275
  this.$module = $module;
3276
+
3277
+ /** @deprecated Will be made private in v5.0 */
3278
+ this.$summary = null;
3279
+
3280
+ /** @deprecated Will be made private in v5.0 */
3281
+ this.$content = null;
3007
3282
  }
3008
3283
 
3009
3284
  /**
3010
3285
  * Initialise component
3011
3286
  */
3012
3287
  Details.prototype.init = function () {
3288
+ // Check that required elements are present
3013
3289
  if (!this.$module) {
3014
3290
  return
3015
3291
  }
3016
3292
 
3017
3293
  // If there is native details support, we want to avoid running code to polyfill native behaviour.
3018
- var hasNativeDetails = typeof this.$module.open === 'boolean';
3294
+ var hasNativeDetails = 'HTMLDetailsElement' in window &&
3295
+ this.$module instanceof HTMLDetailsElement;
3019
3296
 
3020
- if (hasNativeDetails) {
3021
- return
3297
+ if (!hasNativeDetails) {
3298
+ this.polyfillDetails();
3022
3299
  }
3023
-
3024
- this.polyfillDetails();
3025
3300
  };
3026
3301
 
3027
3302
  /**
3028
3303
  * Polyfill component in older browsers
3304
+ *
3305
+ * @deprecated Will be made private in v5.0
3029
3306
  */
3030
3307
  Details.prototype.polyfillDetails = function () {
3031
3308
  var $module = this.$module;
@@ -3076,6 +3353,7 @@
3076
3353
  /**
3077
3354
  * Define a statechange function that updates aria-expanded and style.display
3078
3355
  *
3356
+ * @deprecated Will be made private in v5.0
3079
3357
  * @returns {boolean} Returns true
3080
3358
  */
3081
3359
  Details.prototype.polyfillSetAttributes = function () {
@@ -3095,6 +3373,7 @@
3095
3373
  /**
3096
3374
  * Handle cross-modal click events
3097
3375
  *
3376
+ * @deprecated Will be made private in v5.0
3098
3377
  * @param {polyfillHandleInputsCallback} callback - function
3099
3378
  */
3100
3379
  Details.prototype.polyfillHandleInputs = function (callback) {
@@ -3102,7 +3381,7 @@
3102
3381
  var $target = event.target;
3103
3382
  // When the key gets pressed - check if it is enter or space
3104
3383
  if (event.keyCode === KEY_ENTER || event.keyCode === KEY_SPACE$1) {
3105
- if ($target.nodeName.toLowerCase() === 'summary') {
3384
+ if ($target instanceof HTMLElement && $target.nodeName.toLowerCase() === 'summary') {
3106
3385
  // Prevent space from scrolling the page
3107
3386
  // and enter from submitting a form
3108
3387
  event.preventDefault();
@@ -3121,7 +3400,7 @@
3121
3400
  this.$summary.addEventListener('keyup', function (event) {
3122
3401
  var $target = event.target;
3123
3402
  if (event.keyCode === KEY_SPACE$1) {
3124
- if ($target.nodeName.toLowerCase() === 'summary') {
3403
+ if ($target instanceof HTMLElement && $target.nodeName.toLowerCase() === 'summary') {
3125
3404
  event.preventDefault();
3126
3405
  }
3127
3406
  }
@@ -3144,7 +3423,7 @@
3144
3423
  * Takes focus on initialisation for accessible announcement, unless disabled in configuration.
3145
3424
  *
3146
3425
  * @class
3147
- * @param {HTMLElement} $module - HTML element to use for error summary
3426
+ * @param {Element} $module - HTML element to use for error summary
3148
3427
  * @param {ErrorSummaryConfig} [config] - Error summary config
3149
3428
  */
3150
3429
  function ErrorSummary ($module, config) {
@@ -3155,17 +3434,23 @@
3155
3434
  // To avoid breaking further JavaScript initialisation
3156
3435
  // we need to safeguard against this so things keep
3157
3436
  // working the same now we read the elements data attributes
3158
- if (!$module) {
3437
+ if (!($module instanceof HTMLElement)) {
3159
3438
  // Little safety in case code gets ported as-is
3160
3439
  // into and ES6 class constructor, where the return value matters
3161
3440
  return this
3162
3441
  }
3163
3442
 
3443
+ /** @deprecated Will be made private in v5.0 */
3164
3444
  this.$module = $module;
3165
3445
 
3166
3446
  var defaultConfig = {
3167
3447
  disableAutoFocus: false
3168
3448
  };
3449
+
3450
+ /**
3451
+ * @deprecated Will be made private in v5.0
3452
+ * @type {ErrorSummaryConfig}
3453
+ */
3169
3454
  this.config = mergeConfigs(
3170
3455
  defaultConfig,
3171
3456
  config || {},
@@ -3177,17 +3462,21 @@
3177
3462
  * Initialise component
3178
3463
  */
3179
3464
  ErrorSummary.prototype.init = function () {
3180
- var $module = this.$module;
3181
- if (!$module) {
3465
+ // Check that required elements are present
3466
+ if (!this.$module) {
3182
3467
  return
3183
3468
  }
3184
3469
 
3470
+ var $module = this.$module;
3471
+
3185
3472
  this.setFocus();
3186
3473
  $module.addEventListener('click', this.handleClick.bind(this));
3187
3474
  };
3188
3475
 
3189
3476
  /**
3190
3477
  * Focus the error summary
3478
+ *
3479
+ * @deprecated Will be made private in v5.0
3191
3480
  */
3192
3481
  ErrorSummary.prototype.setFocus = function () {
3193
3482
  var $module = this.$module;
@@ -3210,6 +3499,7 @@
3210
3499
  /**
3211
3500
  * Click event handler
3212
3501
  *
3502
+ * @deprecated Will be made private in v5.0
3213
3503
  * @param {MouseEvent} event - Click event
3214
3504
  */
3215
3505
  ErrorSummary.prototype.handleClick = function (event) {
@@ -3234,16 +3524,21 @@
3234
3524
  * NVDA (as tested in 2018.3.2) - without this only the field type is announced
3235
3525
  * (e.g. "Edit, has autocomplete").
3236
3526
  *
3527
+ * @deprecated Will be made private in v5.0
3237
3528
  * @param {EventTarget} $target - Event target
3238
3529
  * @returns {boolean} True if the target was able to be focussed
3239
3530
  */
3240
3531
  ErrorSummary.prototype.focusTarget = function ($target) {
3241
3532
  // If the element that was clicked was not a link, return early
3242
- if ($target.tagName !== 'A' || $target.href === false) {
3533
+ if (!($target instanceof HTMLAnchorElement)) {
3243
3534
  return false
3244
3535
  }
3245
3536
 
3246
3537
  var inputId = this.getFragmentFromUrl($target.href);
3538
+ if (!inputId) {
3539
+ return false
3540
+ }
3541
+
3247
3542
  var $input = document.getElementById(inputId);
3248
3543
  if (!$input) {
3249
3544
  return false
@@ -3269,12 +3564,13 @@
3269
3564
  * Extract the fragment (everything after the hash) from a URL, but not including
3270
3565
  * the hash.
3271
3566
  *
3567
+ * @deprecated Will be made private in v5.0
3272
3568
  * @param {string} url - URL
3273
- * @returns {string} Fragment from URL, without the hash
3569
+ * @returns {string | undefined} Fragment from URL, without the hash
3274
3570
  */
3275
3571
  ErrorSummary.prototype.getFragmentFromUrl = function (url) {
3276
3572
  if (url.indexOf('#') === -1) {
3277
- return false
3573
+ return undefined
3278
3574
  }
3279
3575
 
3280
3576
  return url.split('#').pop()
@@ -3291,9 +3587,10 @@
3291
3587
  * - The first `<label>` that is associated with the input using for="inputId"
3292
3588
  * - The closest parent `<label>`
3293
3589
  *
3294
- * @param {HTMLElement} $input - The input
3295
- * @returns {HTMLElement} Associated legend or label, or null if no associated
3296
- * legend or label can be found
3590
+ * @deprecated Will be made private in v5.0
3591
+ * @param {Element} $input - The input
3592
+ * @returns {Element | null} Associated legend or label, or null if no associated
3593
+ * legend or label can be found
3297
3594
  */
3298
3595
  ErrorSummary.prototype.getAssociatedLegendOrLabel = function ($input) {
3299
3596
  var $fieldset = $input.closest('fieldset');
@@ -3306,7 +3603,7 @@
3306
3603
 
3307
3604
  // If the input type is radio or checkbox, always use the legend if there
3308
3605
  // is one.
3309
- if ($input.type === 'checkbox' || $input.type === 'radio') {
3606
+ if ($input instanceof HTMLInputElement && ($input.type === 'checkbox' || $input.type === 'radio')) {
3310
3607
  return $candidateLegend
3311
3608
  }
3312
3609
 
@@ -3339,8 +3636,8 @@
3339
3636
  * Error summary config
3340
3637
  *
3341
3638
  * @typedef {object} ErrorSummaryConfig
3342
- * @property {boolean} [disableAutoFocus = false] -
3343
- * If set to `true` the error summary will not be focussed when the page loads.
3639
+ * @property {boolean} [disableAutoFocus = false] - If set to `true` the error
3640
+ * summary will not be focussed when the page loads.
3344
3641
  */
3345
3642
 
3346
3643
  /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
@@ -3349,24 +3646,41 @@
3349
3646
  * Header component
3350
3647
  *
3351
3648
  * @class
3352
- * @param {HTMLElement} $module - HTML element to use for header
3649
+ * @param {Element} $module - HTML element to use for header
3353
3650
  */
3354
3651
  function Header ($module) {
3652
+ if (!($module instanceof HTMLElement)) {
3653
+ return this
3654
+ }
3655
+
3656
+ /** @deprecated Will be made private in v5.0 */
3355
3657
  this.$module = $module;
3356
- this.$menuButton = $module && $module.querySelector('.govuk-js-header-toggle');
3658
+
3659
+ /** @deprecated Will be made private in v5.0 */
3660
+ this.$menuButton = $module.querySelector('.govuk-js-header-toggle');
3661
+
3662
+ /** @deprecated Will be made private in v5.0 */
3357
3663
  this.$menu = this.$menuButton && $module.querySelector(
3358
3664
  '#' + this.$menuButton.getAttribute('aria-controls')
3359
3665
  );
3360
3666
 
3361
- // Save the opened/closed state for the nav in memory so that we can
3362
- // accurately maintain state when the screen is changed from small to
3363
- // big and back to small
3667
+ /**
3668
+ * Save the opened/closed state for the nav in memory so that we can
3669
+ * accurately maintain state when the screen is changed from small to
3670
+ * big and back to small
3671
+ *
3672
+ * @deprecated Will be made private in v5.0
3673
+ */
3364
3674
  this.menuIsOpen = false;
3365
3675
 
3366
- // A global const for storing a matchMedia instance which we'll use to
3367
- // detect when a screen size change happens. We set this later during the
3368
- // init function and rely on it being null if the feature isn't available
3369
- // to initially apply hidden attributes
3676
+ /**
3677
+ * A global const for storing a matchMedia instance which we'll use to
3678
+ * detect when a screen size change happens. We set this later during the
3679
+ * init function and rely on it being null if the feature isn't available
3680
+ * to initially apply hidden attributes
3681
+ *
3682
+ * @deprecated Will be made private in v5.0
3683
+ */
3370
3684
  this.mql = null;
3371
3685
  }
3372
3686
 
@@ -3381,6 +3695,7 @@
3381
3695
  * version of the menu to the user.
3382
3696
  */
3383
3697
  Header.prototype.init = function () {
3698
+ // Check that required elements are present
3384
3699
  if (!this.$module || !this.$menuButton || !this.$menu) {
3385
3700
  return
3386
3701
  }
@@ -3393,8 +3708,9 @@
3393
3708
  this.mql.addEventListener('change', this.syncState.bind(this));
3394
3709
  } else {
3395
3710
  // addListener is a deprecated function, however addEventListener
3396
- // isn't supported by IE or Safari. We therefore add this in as
3711
+ // isn't supported by IE or Safari < 14. We therefore add this in as
3397
3712
  // a fallback for those browsers
3713
+ // @ts-expect-error Property 'addListener' does not exist
3398
3714
  this.mql.addListener(this.syncState.bind(this));
3399
3715
  }
3400
3716
 
@@ -3412,6 +3728,8 @@
3412
3728
  * visual states of the menu and the menu button.
3413
3729
  * Additionally will force the menu to be visible and the menu button to be
3414
3730
  * hidden if the matchMedia is triggered to desktop.
3731
+ *
3732
+ * @deprecated Will be made private in v5.0
3415
3733
  */
3416
3734
  Header.prototype.syncState = function () {
3417
3735
  if (this.mql.matches) {
@@ -3419,7 +3737,7 @@
3419
3737
  this.$menuButton.setAttribute('hidden', '');
3420
3738
  } else {
3421
3739
  this.$menuButton.removeAttribute('hidden');
3422
- this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen);
3740
+ this.$menuButton.setAttribute('aria-expanded', this.menuIsOpen.toString());
3423
3741
 
3424
3742
  if (this.menuIsOpen) {
3425
3743
  this.$menu.removeAttribute('hidden');
@@ -3434,6 +3752,8 @@
3434
3752
  *
3435
3753
  * When the menu button is clicked, change the visibility of the menu and then
3436
3754
  * sync the accessibility state and menu button state
3755
+ *
3756
+ * @deprecated Will be made private in v5.0
3437
3757
  */
3438
3758
  Header.prototype.handleMenuButtonClick = function () {
3439
3759
  this.menuIsOpen = !this.menuIsOpen;
@@ -3444,15 +3764,25 @@
3444
3764
  * Notification Banner component
3445
3765
  *
3446
3766
  * @class
3447
- * @param {HTMLElement} $module - HTML element to use for notification banner
3767
+ * @param {Element} $module - HTML element to use for notification banner
3448
3768
  * @param {NotificationBannerConfig} [config] - Notification banner config
3449
3769
  */
3450
3770
  function NotificationBanner ($module, config) {
3771
+ if (!($module instanceof HTMLElement)) {
3772
+ return this
3773
+ }
3774
+
3775
+ /** @deprecated Will be made private in v5.0 */
3451
3776
  this.$module = $module;
3452
3777
 
3453
3778
  var defaultConfig = {
3454
3779
  disableAutoFocus: false
3455
3780
  };
3781
+
3782
+ /**
3783
+ * @deprecated Will be made private in v5.0
3784
+ * @type {NotificationBannerConfig}
3785
+ */
3456
3786
  this.config = mergeConfigs(
3457
3787
  defaultConfig,
3458
3788
  config || {},
@@ -3464,9 +3794,8 @@
3464
3794
  * Initialise component
3465
3795
  */
3466
3796
  NotificationBanner.prototype.init = function () {
3467
- var $module = this.$module;
3468
- // Check for module
3469
- if (!$module) {
3797
+ // Check that required elements are present
3798
+ if (!this.$module) {
3470
3799
  return
3471
3800
  }
3472
3801
 
@@ -3482,6 +3811,8 @@
3482
3811
  * You can turn off the auto-focus functionality by setting `data-disable-auto-focus="true"` in the
3483
3812
  * component HTML. You might wish to do this based on user research findings, or to avoid a clash
3484
3813
  * with another element which should be focused when the page loads.
3814
+ *
3815
+ * @deprecated Will be made private in v5.0
3485
3816
  */
3486
3817
  NotificationBanner.prototype.setFocus = function () {
3487
3818
  var $module = this.$module;
@@ -3512,11 +3843,10 @@
3512
3843
  * Notification banner config
3513
3844
  *
3514
3845
  * @typedef {object} NotificationBannerConfig
3515
- * @property {boolean} [disableAutoFocus = false] -
3516
- * If set to `true` the notification banner will not be focussed when the page
3517
- * loads. This only applies if the component has a `role` of `alert` – in
3518
- * other cases the component will not be focused on page load, regardless of
3519
- * this option.
3846
+ * @property {boolean} [disableAutoFocus = false] - If set to `true` the
3847
+ * notification banner will not be focussed when the page loads. This only
3848
+ * applies if the component has a `role` of `alert` – in other cases the
3849
+ * component will not be focused on page load, regardless of this option.
3520
3850
  */
3521
3851
 
3522
3852
  /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
@@ -3525,11 +3855,23 @@
3525
3855
  * Radios component
3526
3856
  *
3527
3857
  * @class
3528
- * @param {HTMLElement} $module - HTML element to use for radios
3858
+ * @param {Element} $module - HTML element to use for radios
3529
3859
  */
3530
3860
  function Radios ($module) {
3861
+ if (!($module instanceof HTMLElement)) {
3862
+ return this
3863
+ }
3864
+
3865
+ var $inputs = $module.querySelectorAll('input[type="radio"]');
3866
+ if (!$inputs.length) {
3867
+ return this
3868
+ }
3869
+
3870
+ /** @deprecated Will be made private in v5.0 */
3531
3871
  this.$module = $module;
3532
- this.$inputs = $module.querySelectorAll('input[type="radio"]');
3872
+
3873
+ /** @deprecated Will be made private in v5.0 */
3874
+ this.$inputs = $inputs;
3533
3875
  }
3534
3876
 
3535
3877
  /**
@@ -3547,6 +3889,11 @@
3547
3889
  * the reveal in sync with the radio state.
3548
3890
  */
3549
3891
  Radios.prototype.init = function () {
3892
+ // Check that required elements are present
3893
+ if (!this.$module || !this.$inputs) {
3894
+ return
3895
+ }
3896
+
3550
3897
  var $module = this.$module;
3551
3898
  var $inputs = this.$inputs;
3552
3899
 
@@ -3569,11 +3916,10 @@
3569
3916
  // state of form controls is not restored until *after* the DOMContentLoaded
3570
3917
  // event is fired, so we need to sync after the pageshow event in browsers
3571
3918
  // that support it.
3572
- if ('onpageshow' in window) {
3573
- window.addEventListener('pageshow', this.syncAllConditionalReveals.bind(this));
3574
- } else {
3575
- window.addEventListener('DOMContentLoaded', this.syncAllConditionalReveals.bind(this));
3576
- }
3919
+ window.addEventListener(
3920
+ 'onpageshow' in window ? 'pageshow' : 'DOMContentLoaded',
3921
+ this.syncAllConditionalReveals.bind(this)
3922
+ );
3577
3923
 
3578
3924
  // Although we've set up handlers to sync state on the pageshow or
3579
3925
  // DOMContentLoaded event, init could be called after those events have fired,
@@ -3586,6 +3932,8 @@
3586
3932
 
3587
3933
  /**
3588
3934
  * Sync the conditional reveal states for all radio buttons in this $module.
3935
+ *
3936
+ * @deprecated Will be made private in v5.0
3589
3937
  */
3590
3938
  Radios.prototype.syncAllConditionalReveals = function () {
3591
3939
  nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this));
@@ -3597,15 +3945,20 @@
3597
3945
  * Synchronise the visibility of the conditional reveal, and its accessible
3598
3946
  * state, with the input's checked state.
3599
3947
  *
3948
+ * @deprecated Will be made private in v5.0
3600
3949
  * @param {HTMLInputElement} $input - Radio input
3601
3950
  */
3602
3951
  Radios.prototype.syncConditionalRevealWithInputState = function ($input) {
3603
- var $target = document.getElementById($input.getAttribute('aria-controls'));
3952
+ var targetId = $input.getAttribute('aria-controls');
3953
+ if (!targetId) {
3954
+ return
3955
+ }
3604
3956
 
3957
+ var $target = document.getElementById(targetId);
3605
3958
  if ($target && $target.classList.contains('govuk-radios__conditional')) {
3606
3959
  var inputIsChecked = $input.checked;
3607
3960
 
3608
- $input.setAttribute('aria-expanded', inputIsChecked);
3961
+ $input.setAttribute('aria-expanded', inputIsChecked.toString());
3609
3962
  $target.classList.toggle('govuk-radios__conditional--hidden', !inputIsChecked);
3610
3963
  }
3611
3964
  };
@@ -3618,13 +3971,15 @@
3618
3971
  * with the same name (because checking one radio could have un-checked a radio
3619
3972
  * in another $module)
3620
3973
  *
3974
+ * @deprecated Will be made private in v5.0
3621
3975
  * @param {MouseEvent} event - Click event
3622
3976
  */
3623
3977
  Radios.prototype.handleClick = function (event) {
3978
+ var $component = this;
3624
3979
  var $clickedInput = event.target;
3625
3980
 
3626
3981
  // Ignore clicks on things that aren't radio buttons
3627
- if ($clickedInput.type !== 'radio') {
3982
+ if (!($clickedInput instanceof HTMLInputElement) || $clickedInput.type !== 'radio') {
3628
3983
  return
3629
3984
  }
3630
3985
 
@@ -3632,14 +3987,17 @@
3632
3987
  // aria-controls attributes.
3633
3988
  var $allInputs = document.querySelectorAll('input[type="radio"][aria-controls]');
3634
3989
 
3990
+ var $clickedInputForm = $clickedInput.form;
3991
+ var $clickedInputName = $clickedInput.name;
3992
+
3635
3993
  nodeListForEach($allInputs, function ($input) {
3636
- var hasSameFormOwner = ($input.form === $clickedInput.form);
3637
- var hasSameName = ($input.name === $clickedInput.name);
3994
+ var hasSameFormOwner = $input.form === $clickedInputForm;
3995
+ var hasSameName = $input.name === $clickedInputName;
3638
3996
 
3639
3997
  if (hasSameName && hasSameFormOwner) {
3640
- this.syncConditionalRevealWithInputState($input);
3998
+ $component.syncConditionalRevealWithInputState($input);
3641
3999
  }
3642
- }.bind(this));
4000
+ });
3643
4001
  };
3644
4002
 
3645
4003
  /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
@@ -3648,11 +4006,20 @@
3648
4006
  * Skip link component
3649
4007
  *
3650
4008
  * @class
3651
- * @param {HTMLElement} $module - HTML element to use for skip link
4009
+ * @param {Element} $module - HTML element to use for skip link
3652
4010
  */
3653
4011
  function SkipLink ($module) {
4012
+ if (!($module instanceof HTMLAnchorElement)) {
4013
+ return this
4014
+ }
4015
+
4016
+ /** @deprecated Will be made private in v5.0 */
3654
4017
  this.$module = $module;
4018
+
4019
+ /** @deprecated Will be made private in v5.0 */
3655
4020
  this.$linkedElement = null;
4021
+
4022
+ /** @deprecated Will be made private in v5.0 */
3656
4023
  this.linkedElementListener = false;
3657
4024
  }
3658
4025
 
@@ -3660,30 +4027,31 @@
3660
4027
  * Initialise component
3661
4028
  */
3662
4029
  SkipLink.prototype.init = function () {
3663
- // Check for module
4030
+ // Check that required elements are present
3664
4031
  if (!this.$module) {
3665
4032
  return
3666
4033
  }
3667
4034
 
3668
4035
  // Check for linked element
3669
- this.$linkedElement = this.getLinkedElement();
3670
- if (!this.$linkedElement) {
4036
+ var $linkedElement = this.getLinkedElement();
4037
+ if (!$linkedElement) {
3671
4038
  return
3672
4039
  }
3673
4040
 
4041
+ this.$linkedElement = $linkedElement;
3674
4042
  this.$module.addEventListener('click', this.focusLinkedElement.bind(this));
3675
4043
  };
3676
4044
 
3677
4045
  /**
3678
4046
  * Get linked element
3679
4047
  *
3680
- * @returns {HTMLElement} $linkedElement - DOM element linked to from the skip link
4048
+ * @deprecated Will be made private in v5.0
4049
+ * @returns {HTMLElement | null} $linkedElement - DOM element linked to from the skip link
3681
4050
  */
3682
4051
  SkipLink.prototype.getLinkedElement = function () {
3683
4052
  var linkedElementId = this.getFragmentFromUrl();
3684
-
3685
4053
  if (!linkedElementId) {
3686
- return false
4054
+ return null
3687
4055
  }
3688
4056
 
3689
4057
  return document.getElementById(linkedElementId)
@@ -3693,6 +4061,8 @@
3693
4061
  * Focus the linked element
3694
4062
  *
3695
4063
  * Set tabindex and helper CSS class. Set listener to remove them on blur.
4064
+ *
4065
+ * @deprecated Will be made private in v5.0
3696
4066
  */
3697
4067
  SkipLink.prototype.focusLinkedElement = function () {
3698
4068
  var $linkedElement = this.$linkedElement;
@@ -3708,6 +4078,7 @@
3708
4078
  this.linkedElementListener = true;
3709
4079
  }
3710
4080
  }
4081
+
3711
4082
  $linkedElement.focus();
3712
4083
  };
3713
4084
 
@@ -3716,6 +4087,8 @@
3716
4087
  * focusable until it has received programmatic focus and a screen reader has announced it.
3717
4088
  *
3718
4089
  * Remove the CSS class that removes the native focus styles.
4090
+ *
4091
+ * @deprecated Will be made private in v5.0
3719
4092
  */
3720
4093
  SkipLink.prototype.removeFocusProperties = function () {
3721
4094
  this.$linkedElement.removeAttribute('tabindex');
@@ -3728,17 +4101,20 @@
3728
4101
  * Extract the fragment (everything after the hash symbol) from a URL, but not including
3729
4102
  * the symbol.
3730
4103
  *
3731
- * @returns {string} Fragment from URL, without the hash symbol
4104
+ * @deprecated Will be made private in v5.0
4105
+ * @returns {string | undefined} Fragment from URL, without the hash symbol
3732
4106
  */
3733
4107
  SkipLink.prototype.getFragmentFromUrl = function () {
3734
4108
  // Bail if the anchor link doesn't have a hash
3735
4109
  if (!this.$module.hash) {
3736
- return false
4110
+ return
3737
4111
  }
3738
4112
 
3739
4113
  return this.$module.hash.split('#').pop()
3740
4114
  };
3741
4115
 
4116
+ // @ts-nocheck
4117
+
3742
4118
  (function(undefined) {
3743
4119
 
3744
4120
  // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/master/polyfills/Element/prototype/nextElementSibling/detect.js
@@ -3759,6 +4135,8 @@
3759
4135
 
3760
4136
  }).call('object' === typeof window && window || 'object' === typeof self && self || 'object' === typeof global && global || {});
3761
4137
 
4138
+ // @ts-nocheck
4139
+
3762
4140
  (function(undefined) {
3763
4141
 
3764
4142
  // Detection from https://raw.githubusercontent.com/Financial-Times/polyfill-library/master/polyfills/Element/prototype/previousElementSibling/detect.js
@@ -3785,20 +4163,54 @@
3785
4163
  * Tabs component
3786
4164
  *
3787
4165
  * @class
3788
- * @param {HTMLElement} $module - HTML element to use for tabs
4166
+ * @param {Element} $module - HTML element to use for tabs
3789
4167
  */
3790
4168
  function Tabs ($module) {
4169
+ if (!($module instanceof HTMLElement)) {
4170
+ return this
4171
+ }
4172
+
4173
+ var $tabs = $module.querySelectorAll('a.govuk-tabs__tab');
4174
+ if (!$tabs.length) {
4175
+ return this
4176
+ }
4177
+
4178
+ /** @deprecated Will be made private in v5.0 */
3791
4179
  this.$module = $module;
3792
- this.$tabs = $module.querySelectorAll('.govuk-tabs__tab');
3793
4180
 
4181
+ /** @deprecated Will be made private in v5.0 */
4182
+ this.$tabs = $tabs;
4183
+
4184
+ /** @deprecated Will be made private in v5.0 */
3794
4185
  this.keys = { left: 37, right: 39, up: 38, down: 40 };
4186
+
4187
+ /** @deprecated Will be made private in v5.0 */
3795
4188
  this.jsHiddenClass = 'govuk-tabs__panel--hidden';
4189
+
4190
+ // Save bounded functions to use when removing event listeners during teardown
4191
+
4192
+ /** @deprecated Will be made private in v5.0 */
4193
+ this.boundTabClick = this.onTabClick.bind(this);
4194
+
4195
+ /** @deprecated Will be made private in v5.0 */
4196
+ this.boundTabKeydown = this.onTabKeydown.bind(this);
4197
+
4198
+ /** @deprecated Will be made private in v5.0 */
4199
+ this.boundOnHashChange = this.onHashChange.bind(this);
4200
+
4201
+ /** @deprecated Will be made private in v5.0 */
4202
+ this.changingHash = false;
3796
4203
  }
3797
4204
 
3798
4205
  /**
3799
4206
  * Initialise component
3800
4207
  */
3801
4208
  Tabs.prototype.init = function () {
4209
+ // Check that required elements are present
4210
+ if (!this.$module || !this.$tabs) {
4211
+ return
4212
+ }
4213
+
3802
4214
  if (typeof window.matchMedia === 'function') {
3803
4215
  this.setupResponsiveChecks();
3804
4216
  } else {
@@ -3808,8 +4220,11 @@
3808
4220
 
3809
4221
  /**
3810
4222
  * Setup viewport resize check
4223
+ *
4224
+ * @deprecated Will be made private in v5.0
3811
4225
  */
3812
4226
  Tabs.prototype.setupResponsiveChecks = function () {
4227
+ /** @deprecated Will be made private in v5.0 */
3813
4228
  this.mql = window.matchMedia('(min-width: 40.0625em)');
3814
4229
  this.mql.addListener(this.checkMode.bind(this));
3815
4230
  this.checkMode();
@@ -3817,6 +4232,8 @@
3817
4232
 
3818
4233
  /**
3819
4234
  * Setup or teardown handler for viewport resize check
4235
+ *
4236
+ * @deprecated Will be made private in v5.0
3820
4237
  */
3821
4238
  Tabs.prototype.checkMode = function () {
3822
4239
  if (this.mql.matches) {
@@ -3828,8 +4245,11 @@
3828
4245
 
3829
4246
  /**
3830
4247
  * Setup tab component
4248
+ *
4249
+ * @deprecated Will be made private in v5.0
3831
4250
  */
3832
4251
  Tabs.prototype.setup = function () {
4252
+ var $component = this;
3833
4253
  var $module = this.$module;
3834
4254
  var $tabs = this.$tabs;
3835
4255
  var $tabList = $module.querySelector('.govuk-tabs__list');
@@ -3847,37 +4267,39 @@
3847
4267
 
3848
4268
  nodeListForEach($tabs, function ($tab) {
3849
4269
  // Set HTML attributes
3850
- this.setAttributes($tab);
3851
-
3852
- // Save bounded functions to use when removing event listeners during teardown
3853
- $tab.boundTabClick = this.onTabClick.bind(this);
3854
- $tab.boundTabKeydown = this.onTabKeydown.bind(this);
4270
+ $component.setAttributes($tab);
3855
4271
 
3856
4272
  // Handle events
3857
- $tab.addEventListener('click', $tab.boundTabClick, true);
3858
- $tab.addEventListener('keydown', $tab.boundTabKeydown, true);
4273
+ $tab.addEventListener('click', $component.boundTabClick, true);
4274
+ $tab.addEventListener('keydown', $component.boundTabKeydown, true);
3859
4275
 
3860
4276
  // Remove old active panels
3861
- this.hideTab($tab);
3862
- }.bind(this));
4277
+ $component.hideTab($tab);
4278
+ });
3863
4279
 
3864
4280
  // Show either the active tab according to the URL's hash or the first tab
3865
4281
  var $activeTab = this.getTab(window.location.hash) || this.$tabs[0];
4282
+ if (!$activeTab) {
4283
+ return
4284
+ }
4285
+
3866
4286
  this.showTab($activeTab);
3867
4287
 
3868
4288
  // Handle hashchange events
3869
- $module.boundOnHashChange = this.onHashChange.bind(this);
3870
- window.addEventListener('hashchange', $module.boundOnHashChange, true);
4289
+ window.addEventListener('hashchange', this.boundOnHashChange, true);
3871
4290
  };
3872
4291
 
3873
4292
  /**
3874
4293
  * Teardown tab component
4294
+ *
4295
+ * @deprecated Will be made private in v5.0
3875
4296
  */
3876
4297
  Tabs.prototype.teardown = function () {
4298
+ var $component = this;
3877
4299
  var $module = this.$module;
3878
4300
  var $tabs = this.$tabs;
3879
4301
  var $tabList = $module.querySelector('.govuk-tabs__list');
3880
- var $tabListItems = $module.querySelectorAll('.govuk-tabs__list-item');
4302
+ var $tabListItems = $module.querySelectorAll('a.govuk-tabs__list-item');
3881
4303
 
3882
4304
  if (!$tabs || !$tabList || !$tabListItems) {
3883
4305
  return
@@ -3886,29 +4308,29 @@
3886
4308
  $tabList.removeAttribute('role');
3887
4309
 
3888
4310
  nodeListForEach($tabListItems, function ($item) {
3889
- $item.removeAttribute('role', 'presentation');
4311
+ $item.removeAttribute('role');
3890
4312
  });
3891
4313
 
3892
4314
  nodeListForEach($tabs, function ($tab) {
3893
4315
  // Remove events
3894
- $tab.removeEventListener('click', $tab.boundTabClick, true);
3895
- $tab.removeEventListener('keydown', $tab.boundTabKeydown, true);
4316
+ $tab.removeEventListener('click', $component.boundTabClick, true);
4317
+ $tab.removeEventListener('keydown', $component.boundTabKeydown, true);
3896
4318
 
3897
4319
  // Unset HTML attributes
3898
- this.unsetAttributes($tab);
3899
- }.bind(this));
4320
+ $component.unsetAttributes($tab);
4321
+ });
3900
4322
 
3901
4323
  // Remove hashchange event handler
3902
- window.removeEventListener('hashchange', $module.boundOnHashChange, true);
4324
+ window.removeEventListener('hashchange', this.boundOnHashChange, true);
3903
4325
  };
3904
4326
 
3905
4327
  /**
3906
4328
  * Handle hashchange event
3907
4329
  *
3908
- * @param {HashChangeEvent} event - Hash change event
4330
+ * @deprecated Will be made private in v5.0
3909
4331
  * @returns {void | undefined} Returns void, or undefined when prevented
3910
4332
  */
3911
- Tabs.prototype.onHashChange = function (event) {
4333
+ Tabs.prototype.onHashChange = function () {
3912
4334
  var hash = window.location.hash;
3913
4335
  var $tabWithHash = this.getTab(hash);
3914
4336
  if (!$tabWithHash) {
@@ -3923,6 +4345,9 @@
3923
4345
 
3924
4346
  // Show either the active tab according to the URL's hash or the first tab
3925
4347
  var $previousTab = this.getCurrentTab();
4348
+ if (!$previousTab) {
4349
+ return
4350
+ }
3926
4351
 
3927
4352
  this.hideTab($previousTab);
3928
4353
  this.showTab($tabWithHash);
@@ -3932,6 +4357,7 @@
3932
4357
  /**
3933
4358
  * Hide panel for tab link
3934
4359
  *
4360
+ * @deprecated Will be made private in v5.0
3935
4361
  * @param {HTMLAnchorElement} $tab - Tab link
3936
4362
  */
3937
4363
  Tabs.prototype.hideTab = function ($tab) {
@@ -3942,6 +4368,7 @@
3942
4368
  /**
3943
4369
  * Show panel for tab link
3944
4370
  *
4371
+ * @deprecated Will be made private in v5.0
3945
4372
  * @param {HTMLAnchorElement} $tab - Tab link
3946
4373
  */
3947
4374
  Tabs.prototype.showTab = function ($tab) {
@@ -3952,16 +4379,19 @@
3952
4379
  /**
3953
4380
  * Get tab link by hash
3954
4381
  *
4382
+ * @deprecated Will be made private in v5.0
3955
4383
  * @param {string} hash - Hash fragment including #
3956
4384
  * @returns {HTMLAnchorElement | null} Tab link
3957
4385
  */
3958
4386
  Tabs.prototype.getTab = function (hash) {
3959
- return this.$module.querySelector('.govuk-tabs__tab[href="' + hash + '"]')
4387
+ // @ts-expect-error `HTMLAnchorElement` type expected
4388
+ return this.$module.querySelector('a.govuk-tabs__tab[href="' + hash + '"]')
3960
4389
  };
3961
4390
 
3962
4391
  /**
3963
4392
  * Set tab link and panel attributes
3964
4393
  *
4394
+ * @deprecated Will be made private in v5.0
3965
4395
  * @param {HTMLAnchorElement} $tab - Tab link
3966
4396
  */
3967
4397
  Tabs.prototype.setAttributes = function ($tab) {
@@ -3975,6 +4405,10 @@
3975
4405
 
3976
4406
  // set panel attributes
3977
4407
  var $panel = this.getPanel($tab);
4408
+ if (!$panel) {
4409
+ return
4410
+ }
4411
+
3978
4412
  $panel.setAttribute('role', 'tabpanel');
3979
4413
  $panel.setAttribute('aria-labelledby', $tab.id);
3980
4414
  $panel.classList.add(this.jsHiddenClass);
@@ -3983,6 +4417,7 @@
3983
4417
  /**
3984
4418
  * Unset tab link and panel attributes
3985
4419
  *
4420
+ * @deprecated Will be made private in v5.0
3986
4421
  * @param {HTMLAnchorElement} $tab - Tab link
3987
4422
  */
3988
4423
  Tabs.prototype.unsetAttributes = function ($tab) {
@@ -3995,6 +4430,10 @@
3995
4430
 
3996
4431
  // unset panel attributes
3997
4432
  var $panel = this.getPanel($tab);
4433
+ if (!$panel) {
4434
+ return
4435
+ }
4436
+
3998
4437
  $panel.removeAttribute('role');
3999
4438
  $panel.removeAttribute('aria-labelledby');
4000
4439
  $panel.classList.remove(this.jsHiddenClass);
@@ -4003,20 +4442,23 @@
4003
4442
  /**
4004
4443
  * Handle tab link clicks
4005
4444
  *
4445
+ * @deprecated Will be made private in v5.0
4006
4446
  * @param {MouseEvent} event - Mouse click event
4007
- * @returns {void | false} Returns void, or false within tab link
4447
+ * @returns {void} Returns void
4008
4448
  */
4009
4449
  Tabs.prototype.onTabClick = function (event) {
4010
- if (!event.target.classList.contains('govuk-tabs__tab')) {
4011
- // Allow events on child DOM elements to bubble up to tab parent
4012
- return false
4450
+ var $currentTab = this.getCurrentTab();
4451
+ var $nextTab = event.currentTarget;
4452
+
4453
+ if (!$currentTab || !($nextTab instanceof HTMLAnchorElement)) {
4454
+ return
4013
4455
  }
4456
+
4014
4457
  event.preventDefault();
4015
- var $newTab = event.target;
4016
- var $currentTab = this.getCurrentTab();
4458
+
4017
4459
  this.hideTab($currentTab);
4018
- this.showTab($newTab);
4019
- this.createHistoryEntry($newTab);
4460
+ this.showTab($nextTab);
4461
+ this.createHistoryEntry($nextTab);
4020
4462
  };
4021
4463
 
4022
4464
  /**
@@ -4025,10 +4467,14 @@
4025
4467
  * - Allows back/forward to navigate tabs
4026
4468
  * - Avoids page jump when hash changes
4027
4469
  *
4470
+ * @deprecated Will be made private in v5.0
4028
4471
  * @param {HTMLAnchorElement} $tab - Tab link
4029
4472
  */
4030
4473
  Tabs.prototype.createHistoryEntry = function ($tab) {
4031
4474
  var $panel = this.getPanel($tab);
4475
+ if (!$panel) {
4476
+ return
4477
+ }
4032
4478
 
4033
4479
  // Save and restore the id
4034
4480
  // so the page doesn't jump when a user clicks a tab (which changes the hash)
@@ -4045,6 +4491,7 @@
4045
4491
  * - Press right/down arrow for next tab
4046
4492
  * - Press left/up arrow for previous tab
4047
4493
  *
4494
+ * @deprecated Will be made private in v5.0
4048
4495
  * @param {KeyboardEvent} event - Keydown event
4049
4496
  */
4050
4497
  Tabs.prototype.onTabKeydown = function (event) {
@@ -4064,10 +4511,12 @@
4064
4511
 
4065
4512
  /**
4066
4513
  * Activate next tab
4514
+ *
4515
+ * @deprecated Will be made private in v5.0
4067
4516
  */
4068
4517
  Tabs.prototype.activateNextTab = function () {
4069
4518
  var $currentTab = this.getCurrentTab();
4070
- if (!$currentTab) {
4519
+ if (!$currentTab || !$currentTab.parentElement) {
4071
4520
  return
4072
4521
  }
4073
4522
 
@@ -4076,21 +4525,25 @@
4076
4525
  return
4077
4526
  }
4078
4527
 
4079
- var $nextTab = $nextTabListItem.querySelector('.govuk-tabs__tab');
4080
- if ($nextTab) {
4081
- this.hideTab($currentTab);
4082
- this.showTab($nextTab);
4083
- $nextTab.focus();
4084
- this.createHistoryEntry($nextTab);
4528
+ var $nextTab = $nextTabListItem.querySelector('a.govuk-tabs__tab');
4529
+ if (!$nextTab) {
4530
+ return
4085
4531
  }
4532
+
4533
+ this.hideTab($currentTab);
4534
+ this.showTab($nextTab);
4535
+ $nextTab.focus();
4536
+ this.createHistoryEntry($nextTab);
4086
4537
  };
4087
4538
 
4088
4539
  /**
4089
4540
  * Activate previous tab
4541
+ *
4542
+ * @deprecated Will be made private in v5.0
4090
4543
  */
4091
4544
  Tabs.prototype.activatePreviousTab = function () {
4092
4545
  var $currentTab = this.getCurrentTab();
4093
- if (!$currentTab) {
4546
+ if (!$currentTab || !$currentTab.parentElement) {
4094
4547
  return
4095
4548
  }
4096
4549
 
@@ -4099,75 +4552,98 @@
4099
4552
  return
4100
4553
  }
4101
4554
 
4102
- var $previousTab = $previousTabListItem.querySelector('.govuk-tabs__tab');
4103
- if ($previousTab) {
4104
- this.hideTab($currentTab);
4105
- this.showTab($previousTab);
4106
- $previousTab.focus();
4107
- this.createHistoryEntry($previousTab);
4555
+ var $previousTab = $previousTabListItem.querySelector('a.govuk-tabs__tab');
4556
+ if (!$previousTab) {
4557
+ return
4108
4558
  }
4559
+
4560
+ this.hideTab($currentTab);
4561
+ this.showTab($previousTab);
4562
+ $previousTab.focus();
4563
+ this.createHistoryEntry($previousTab);
4109
4564
  };
4110
4565
 
4111
4566
  /**
4112
4567
  * Get tab panel for tab link
4113
4568
  *
4569
+ * @deprecated Will be made private in v5.0
4114
4570
  * @param {HTMLAnchorElement} $tab - Tab link
4115
- * @returns {HTMLDivElement} Tab panel
4571
+ * @returns {Element | null} Tab panel
4116
4572
  */
4117
4573
  Tabs.prototype.getPanel = function ($tab) {
4118
- var $panel = this.$module.querySelector(this.getHref($tab));
4119
- return $panel
4574
+ return this.$module.querySelector(this.getHref($tab))
4120
4575
  };
4121
4576
 
4122
4577
  /**
4123
4578
  * Show tab panel for tab link
4124
4579
  *
4580
+ * @deprecated Will be made private in v5.0
4125
4581
  * @param {HTMLAnchorElement} $tab - Tab link
4126
4582
  */
4127
4583
  Tabs.prototype.showPanel = function ($tab) {
4128
4584
  var $panel = this.getPanel($tab);
4585
+ if (!$panel) {
4586
+ return
4587
+ }
4588
+
4129
4589
  $panel.classList.remove(this.jsHiddenClass);
4130
4590
  };
4131
4591
 
4132
4592
  /**
4133
4593
  * Hide tab panel for tab link
4134
4594
  *
4595
+ * @deprecated Will be made private in v5.0
4135
4596
  * @param {HTMLAnchorElement} $tab - Tab link
4136
4597
  */
4137
4598
  Tabs.prototype.hidePanel = function ($tab) {
4138
4599
  var $panel = this.getPanel($tab);
4600
+ if (!$panel) {
4601
+ return
4602
+ }
4603
+
4139
4604
  $panel.classList.add(this.jsHiddenClass);
4140
4605
  };
4141
4606
 
4142
4607
  /**
4143
4608
  * Unset 'selected' state for tab link
4144
4609
  *
4610
+ * @deprecated Will be made private in v5.0
4145
4611
  * @param {HTMLAnchorElement} $tab - Tab link
4146
4612
  */
4147
4613
  Tabs.prototype.unhighlightTab = function ($tab) {
4614
+ if (!$tab.parentElement) {
4615
+ return
4616
+ }
4617
+
4148
4618
  $tab.setAttribute('aria-selected', 'false');
4149
- $tab.parentNode.classList.remove('govuk-tabs__list-item--selected');
4619
+ $tab.parentElement.classList.remove('govuk-tabs__list-item--selected');
4150
4620
  $tab.setAttribute('tabindex', '-1');
4151
4621
  };
4152
4622
 
4153
4623
  /**
4154
4624
  * Set 'selected' state for tab link
4155
4625
  *
4626
+ * @deprecated Will be made private in v5.0
4156
4627
  * @param {HTMLAnchorElement} $tab - Tab link
4157
4628
  */
4158
4629
  Tabs.prototype.highlightTab = function ($tab) {
4630
+ if (!$tab.parentElement) {
4631
+ return
4632
+ }
4633
+
4159
4634
  $tab.setAttribute('aria-selected', 'true');
4160
- $tab.parentNode.classList.add('govuk-tabs__list-item--selected');
4635
+ $tab.parentElement.classList.add('govuk-tabs__list-item--selected');
4161
4636
  $tab.setAttribute('tabindex', '0');
4162
4637
  };
4163
4638
 
4164
4639
  /**
4165
4640
  * Get current tab link
4166
4641
  *
4167
- * @returns {HTMLAnchorElement | undefined} Tab link
4642
+ * @deprecated Will be made private in v5.0
4643
+ * @returns {HTMLAnchorElement | null} Tab link
4168
4644
  */
4169
4645
  Tabs.prototype.getCurrentTab = function () {
4170
- return this.$module.querySelector('.govuk-tabs__list-item--selected .govuk-tabs__tab')
4646
+ return this.$module.querySelector('.govuk-tabs__list-item--selected a.govuk-tabs__tab')
4171
4647
  };
4172
4648
 
4173
4649
  /**
@@ -4177,6 +4653,7 @@
4177
4653
  * should be a utility function most prob
4178
4654
  * {@link http://labs.thesedays.com/blog/2010/01/08/getting-the-href-value-with-jquery-in-ie/}
4179
4655
  *
4656
+ * @deprecated Will be made private in v5.0
4180
4657
  * @param {HTMLAnchorElement} $tab - Tab link
4181
4658
  * @returns {string} Hash fragment including #
4182
4659
  */
@@ -4199,7 +4676,7 @@
4199
4676
 
4200
4677
  // Allow the user to initialise GOV.UK Frontend in only certain sections of the page
4201
4678
  // Defaults to the entire document if nothing is set.
4202
- var $scope = typeof config.scope !== 'undefined' ? config.scope : document;
4679
+ var $scope = config.scope instanceof HTMLElement ? config.scope : document;
4203
4680
 
4204
4681
  var $accordions = $scope.querySelectorAll('[data-module="govuk-accordion"]');
4205
4682
  nodeListForEach($accordions, function ($accordion) {
@@ -4250,7 +4727,9 @@
4250
4727
 
4251
4728
  // Find first skip link module to enhance.
4252
4729
  var $skipLink = $scope.querySelector('[data-module="govuk-skip-link"]');
4253
- new SkipLink($skipLink).init();
4730
+ if ($skipLink) {
4731
+ new SkipLink($skipLink).init();
4732
+ }
4254
4733
 
4255
4734
  var $tabs = $scope.querySelectorAll('[data-module="govuk-tabs"]');
4256
4735
  nodeListForEach($tabs, function ($tabs) {
@@ -4262,7 +4741,7 @@
4262
4741
  * Config for all components
4263
4742
  *
4264
4743
  * @typedef {object} Config
4265
- * @property {HTMLElement} [scope=document] - Scope to query for components
4744
+ * @property {Element} [scope=document] - Scope to query for components
4266
4745
  * @property {import('./components/accordion/accordion.mjs').AccordionConfig} [accordion] - Accordion config
4267
4746
  * @property {import('./components/button/button.mjs').ButtonConfig} [button] - Button config
4268
4747
  * @property {import('./components/character-count/character-count.mjs').CharacterCountConfig} [characterCount] - Character Count config
@@ -4271,6 +4750,7 @@
4271
4750
  */
4272
4751
 
4273
4752
  exports.initAll = initAll;
4753
+ exports.version = version;
4274
4754
  exports.Accordion = Accordion;
4275
4755
  exports.Button = Button;
4276
4756
  exports.Details = Details;