govuk_publishing_components 34.7.0 → 34.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js +3 -10
  3. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-form-tracker.js +113 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +2 -11
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-schemas.js +15 -1
  6. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js +20 -29
  7. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
  8. data/app/models/govuk_publishing_components/component_doc.rb +14 -0
  9. data/app/models/govuk_publishing_components/component_wrapper_helper_options.rb +14 -0
  10. data/app/views/govuk_publishing_components/component_guide/show.html.erb +21 -6
  11. data/app/views/govuk_publishing_components/components/_action_link.html.erb +5 -2
  12. data/app/views/govuk_publishing_components/components/_breadcrumbs.html.erb +6 -2
  13. data/app/views/govuk_publishing_components/components/_details.html.erb +3 -1
  14. data/app/views/govuk_publishing_components/components/docs/action_link.yml +1 -0
  15. data/app/views/govuk_publishing_components/components/docs/breadcrumbs.yml +1 -0
  16. data/app/views/govuk_publishing_components/components/docs/details.yml +17 -0
  17. data/lib/govuk_publishing_components/presenters/component_wrapper_helper.rb +78 -0
  18. data/lib/govuk_publishing_components/presenters/related_navigation_helper.rb +0 -7
  19. data/lib/govuk_publishing_components/version.rb +1 -1
  20. data/lib/govuk_publishing_components.rb +1 -0
  21. data/node_modules/govuk-frontend/govuk/all.js +4029 -3792
  22. data/node_modules/govuk-frontend/govuk/all.js.map +1 -0
  23. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +52 -51
  24. data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js.map +1 -0
  25. data/node_modules/govuk-frontend/govuk/common/index.js +153 -145
  26. data/node_modules/govuk-frontend/govuk/common/index.js.map +1 -0
  27. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +324 -321
  28. data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js.map +1 -0
  29. data/node_modules/govuk-frontend/govuk/common.js +154 -146
  30. data/node_modules/govuk-frontend/govuk/common.js.map +1 -0
  31. data/node_modules/govuk-frontend/govuk/components/_all.scss +1 -1
  32. data/node_modules/govuk-frontend/govuk/components/accordion/_index.scss +23 -4
  33. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +2059 -1654
  34. data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js.map +1 -0
  35. data/node_modules/govuk-frontend/govuk/components/accordion/fixtures.json +11 -11
  36. data/node_modules/govuk-frontend/govuk/components/accordion/template.njk +1 -1
  37. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +19 -19
  38. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +21 -10
  39. data/node_modules/govuk-frontend/govuk/components/button/button.js +927 -917
  40. data/node_modules/govuk-frontend/govuk/components/button/button.js.map +1 -0
  41. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +2050 -2040
  42. data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js.map +1 -0
  43. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +1155 -1147
  44. data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js.map +1 -0
  45. data/node_modules/govuk-frontend/govuk/components/cookie-banner/fixtures.json +23 -23
  46. data/node_modules/govuk-frontend/govuk/components/cookie-banner/template.njk +1 -1
  47. data/node_modules/govuk-frontend/govuk/components/details/details.js +800 -799
  48. data/node_modules/govuk-frontend/govuk/components/details/details.js.map +1 -0
  49. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +1058 -1045
  50. data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js.map +1 -0
  51. data/node_modules/govuk-frontend/govuk/components/header/_index.scss +6 -0
  52. data/node_modules/govuk-frontend/govuk/components/header/header.js +646 -998
  53. data/node_modules/govuk-frontend/govuk/components/header/header.js.map +1 -0
  54. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +760 -752
  55. data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js.map +1 -0
  56. data/node_modules/govuk-frontend/govuk/components/pagination/fixtures.json +61 -0
  57. data/node_modules/govuk-frontend/govuk/components/pagination/template.njk +1 -1
  58. data/node_modules/govuk-frontend/govuk/components/phase-banner/macro-options.json +1 -1
  59. data/node_modules/govuk-frontend/govuk/components/radios/radios.js +1110 -1107
  60. data/node_modules/govuk-frontend/govuk/components/radios/radios.js.map +1 -0
  61. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +1017 -1014
  62. data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js.map +1 -0
  63. data/node_modules/govuk-frontend/govuk/components/summary-list/_index.scss +107 -0
  64. data/node_modules/govuk-frontend/govuk/components/summary-list/fixtures.json +318 -23
  65. data/node_modules/govuk-frontend/govuk/components/summary-list/macro-options.json +110 -0
  66. data/node_modules/govuk-frontend/govuk/components/summary-list/template.njk +72 -28
  67. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +1392 -1264
  68. data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js.map +1 -0
  69. data/node_modules/govuk-frontend/govuk/i18n.js +363 -364
  70. data/node_modules/govuk-frontend/govuk/i18n.js.map +1 -0
  71. data/node_modules/govuk-frontend/govuk/settings/_measurements.scss +5 -5
  72. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js +242 -241
  73. data/node_modules/govuk-frontend/govuk/vendor/polyfills/DOMTokenList.js.map +1 -0
  74. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +13 -12
  75. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js.map +1 -0
  76. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js +17 -16
  77. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Document.js.map +1 -0
  78. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js +547 -546
  79. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList.js.map +1 -0
  80. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js +37 -36
  81. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/closest.js.map +1 -0
  82. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +251 -250
  83. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js.map +1 -0
  84. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js +21 -20
  85. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/matches.js.map +1 -0
  86. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js +198 -197
  87. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/nextElementSibling.js.map +1 -0
  88. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js +198 -197
  89. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/previousElementSibling.js.map +1 -0
  90. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js +106 -105
  91. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element.js.map +1 -0
  92. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js +400 -399
  93. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Event.js.map +1 -0
  94. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js +239 -238
  95. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Function/prototype/bind.js.map +1 -0
  96. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js +72 -71
  97. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Object/defineProperty.js.map +1 -0
  98. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +14 -13
  99. data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js.map +1 -0
  100. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js +17 -16
  101. data/node_modules/govuk-frontend/govuk/vendor/polyfills/Window.js.map +1 -0
  102. data/node_modules/govuk-frontend/govuk-esm/all.mjs +2 -2
  103. data/node_modules/govuk-frontend/govuk-esm/common/index.mjs +17 -10
  104. data/node_modules/govuk-frontend/govuk-esm/common/normalise-dataset.mjs +3 -1
  105. data/node_modules/govuk-frontend/govuk-esm/components/accordion/accordion.mjs +135 -52
  106. data/node_modules/govuk-frontend/govuk-esm/components/button/button.mjs +11 -9
  107. data/node_modules/govuk-frontend/govuk-esm/components/character-count/character-count.mjs +10 -7
  108. data/node_modules/govuk-frontend/govuk-esm/components/checkboxes/checkboxes.mjs +24 -18
  109. data/node_modules/govuk-frontend/govuk-esm/components/details/details.mjs +23 -16
  110. data/node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs +15 -11
  111. data/node_modules/govuk-frontend/govuk-esm/components/header/header.mjs +3 -2
  112. data/node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs +3 -4
  113. data/node_modules/govuk-frontend/govuk-esm/components/radios/radios.mjs +10 -9
  114. data/node_modules/govuk-frontend/govuk-esm/components/skip-link/skip-link.mjs +5 -3
  115. data/node_modules/govuk-frontend/govuk-esm/components/tabs/tabs.mjs +165 -38
  116. data/node_modules/govuk-frontend/govuk-esm/i18n.mjs +9 -11
  117. data/node_modules/govuk-frontend/package.json +1 -1
  118. metadata +37 -2
@@ -1,9 +1,10 @@
1
- import '../../vendor/polyfills/Function/prototype/bind.mjs'
2
- import '../../vendor/polyfills/Event.mjs' // addEventListener
3
- import '../../vendor/polyfills/Element/prototype/closest.mjs'
1
+ /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
4
2
 
5
3
  import { mergeConfigs } from '../../common/index.mjs'
6
4
  import { normaliseDataset } from '../../common/normalise-dataset.mjs'
5
+ import '../../vendor/polyfills/Element/prototype/closest.mjs'
6
+ import '../../vendor/polyfills/Event.mjs' // addEventListener, event.target normalization and DOMContentLoaded
7
+ import '../../vendor/polyfills/Function/prototype/bind.mjs'
7
8
 
8
9
  /**
9
10
  * JavaScript enhancements for the ErrorSummary
@@ -11,8 +12,8 @@ import { normaliseDataset } from '../../common/normalise-dataset.mjs'
11
12
  * Takes focus on initialisation for accessible announcement, unless disabled in configuration.
12
13
  *
13
14
  * @class
14
- * @param {HTMLElement} $module - The element this component controls
15
- * @param {ErrorSummaryConfig} config - Error summary config
15
+ * @param {HTMLElement} $module - HTML element to use for error summary
16
+ * @param {ErrorSummaryConfig} [config] - Error summary config
16
17
  */
17
18
  function ErrorSummary ($module, config) {
18
19
  // Some consuming code may not be passing a module,
@@ -40,6 +41,9 @@ function ErrorSummary ($module, config) {
40
41
  )
41
42
  }
42
43
 
44
+ /**
45
+ * Initialise component
46
+ */
43
47
  ErrorSummary.prototype.init = function () {
44
48
  var $module = this.$module
45
49
  if (!$module) {
@@ -77,8 +81,8 @@ ErrorSummary.prototype.setFocus = function () {
77
81
  * @param {MouseEvent} event - Click event
78
82
  */
79
83
  ErrorSummary.prototype.handleClick = function (event) {
80
- var target = event.target
81
- if (this.focusTarget(target)) {
84
+ var $target = event.target
85
+ if (this.focusTarget($target)) {
82
86
  event.preventDefault()
83
87
  }
84
88
  }
@@ -98,7 +102,7 @@ ErrorSummary.prototype.handleClick = function (event) {
98
102
  * NVDA (as tested in 2018.3.2) - without this only the field type is announced
99
103
  * (e.g. "Edit, has autocomplete").
100
104
  *
101
- * @param {HTMLElement} $target - Event target
105
+ * @param {EventTarget} $target - Event target
102
106
  * @returns {boolean} True if the target was able to be focussed
103
107
  */
104
108
  ErrorSummary.prototype.focusTarget = function ($target) {
@@ -163,10 +167,10 @@ ErrorSummary.prototype.getAssociatedLegendOrLabel = function ($input) {
163
167
  var $fieldset = $input.closest('fieldset')
164
168
 
165
169
  if ($fieldset) {
166
- var legends = $fieldset.getElementsByTagName('legend')
170
+ var $legends = $fieldset.getElementsByTagName('legend')
167
171
 
168
- if (legends.length) {
169
- var $candidateLegend = legends[0]
172
+ if ($legends.length) {
173
+ var $candidateLegend = $legends[0]
170
174
 
171
175
  // If the input type is radio or checkbox, always use the legend if there
172
176
  // is one.
@@ -1,5 +1,6 @@
1
+ /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
2
+
1
3
  import '../../vendor/polyfills/Event.mjs'
2
- import '../../vendor/polyfills/Element/prototype/classList.mjs'
3
4
  import '../../vendor/polyfills/Function/prototype/bind.mjs'
4
5
 
5
6
  /**
@@ -28,7 +29,7 @@ function Header ($module) {
28
29
  }
29
30
 
30
31
  /**
31
- * Initialise header
32
+ * Initialise component
32
33
  *
33
34
  * Check for the presence of the header, menu and menu button – if any are
34
35
  * missing then there's nothing to do so return early.
@@ -1,14 +1,13 @@
1
- import '../../vendor/polyfills/Event.mjs' // addEventListener
2
-
3
1
  import { mergeConfigs } from '../../common/index.mjs'
4
2
  import { normaliseDataset } from '../../common/normalise-dataset.mjs'
3
+ import '../../vendor/polyfills/Event.mjs' // addEventListener, event.target normalization and DOMContentLoaded
5
4
 
6
5
  /**
7
6
  * Notification Banner component
8
7
  *
9
8
  * @class
10
9
  * @param {HTMLElement} $module - HTML element to use for notification banner
11
- * @param {NotificationBannerConfig} config - Notification banner config
10
+ * @param {NotificationBannerConfig} [config] - Notification banner config
12
11
  */
13
12
  function NotificationBanner ($module, config) {
14
13
  this.$module = $module
@@ -24,7 +23,7 @@ function NotificationBanner ($module, config) {
24
23
  }
25
24
 
26
25
  /**
27
- * Initialise the component
26
+ * Initialise component
28
27
  */
29
28
  NotificationBanner.prototype.init = function () {
30
29
  var $module = this.$module
@@ -1,8 +1,9 @@
1
- import '../../vendor/polyfills/Function/prototype/bind.mjs'
2
- // addEventListener, event.target normalization and DOMContentLoaded
3
- import '../../vendor/polyfills/Event.mjs'
4
- import '../../vendor/polyfills/Element/prototype/classList.mjs'
1
+ /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
2
+
5
3
  import { nodeListForEach } from '../../common/index.mjs'
4
+ import '../../vendor/polyfills/Element/prototype/classList.mjs'
5
+ import '../../vendor/polyfills/Event.mjs' // addEventListener, event.target normalization and DOMContentLoaded
6
+ import '../../vendor/polyfills/Function/prototype/bind.mjs'
6
7
 
7
8
  /**
8
9
  * Radios component
@@ -16,7 +17,7 @@ function Radios ($module) {
16
17
  }
17
18
 
18
19
  /**
19
- * Initialise Radios
20
+ * Initialise component
20
21
  *
21
22
  * Radios can be associated with a 'conditionally revealed' content block – for
22
23
  * example, a radio for 'Phone' could reveal an additional form field for the
@@ -34,17 +35,17 @@ Radios.prototype.init = function () {
34
35
  var $inputs = this.$inputs
35
36
 
36
37
  nodeListForEach($inputs, function ($input) {
37
- var target = $input.getAttribute('data-aria-controls')
38
+ var targetId = $input.getAttribute('data-aria-controls')
38
39
 
39
40
  // Skip radios without data-aria-controls attributes, or where the
40
41
  // target element does not exist.
41
- if (!target || !document.getElementById(target)) {
42
+ if (!targetId || !document.getElementById(targetId)) {
42
43
  return
43
44
  }
44
45
 
45
46
  // Promote the data-aria-controls attribute to a aria-controls attribute
46
47
  // so that the relationship is exposed in the AOM
47
- $input.setAttribute('aria-controls', target)
48
+ $input.setAttribute('aria-controls', targetId)
48
49
  $input.removeAttribute('data-aria-controls')
49
50
  })
50
51
 
@@ -68,7 +69,7 @@ Radios.prototype.init = function () {
68
69
  }
69
70
 
70
71
  /**
71
- * Sync the conditional reveal states for all inputs in this $module.
72
+ * Sync the conditional reveal states for all radio buttons in this $module.
72
73
  */
73
74
  Radios.prototype.syncAllConditionalReveals = function () {
74
75
  nodeListForEach(this.$inputs, this.syncConditionalRevealWithInputState.bind(this))
@@ -1,6 +1,8 @@
1
- import '../../vendor/polyfills/Function/prototype/bind.mjs'
1
+ /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
2
+
2
3
  import '../../vendor/polyfills/Element/prototype/classList.mjs'
3
- import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normalization
4
+ import '../../vendor/polyfills/Event.mjs' // addEventListener, event.target normalization and DOMContentLoaded
5
+ import '../../vendor/polyfills/Function/prototype/bind.mjs'
4
6
 
5
7
  /**
6
8
  * Skip link component
@@ -15,7 +17,7 @@ function SkipLink ($module) {
15
17
  }
16
18
 
17
19
  /**
18
- * Initialise the component
20
+ * Initialise component
19
21
  */
20
22
  SkipLink.prototype.init = function () {
21
23
  // Check for module
@@ -1,9 +1,11 @@
1
- import '../../vendor/polyfills/Function/prototype/bind.mjs'
1
+ /* eslint-disable es-x/no-function-prototype-bind -- Polyfill imported */
2
+
3
+ import { nodeListForEach } from '../../common/index.mjs'
2
4
  import '../../vendor/polyfills/Element/prototype/classList.mjs'
3
5
  import '../../vendor/polyfills/Element/prototype/nextElementSibling.mjs'
4
6
  import '../../vendor/polyfills/Element/prototype/previousElementSibling.mjs'
5
- import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normaliziation
6
- import { nodeListForEach } from '../../common/index.mjs'
7
+ import '../../vendor/polyfills/Event.mjs' // addEventListener, event.target normalization and DOMContentLoaded
8
+ import '../../vendor/polyfills/Function/prototype/bind.mjs'
7
9
 
8
10
  /**
9
11
  * Tabs component
@@ -19,6 +21,9 @@ function Tabs ($module) {
19
21
  this.jsHiddenClass = 'govuk-tabs__panel--hidden'
20
22
  }
21
23
 
24
+ /**
25
+ * Initialise component
26
+ */
22
27
  Tabs.prototype.init = function () {
23
28
  if (typeof window.matchMedia === 'function') {
24
29
  this.setupResponsiveChecks()
@@ -27,12 +32,18 @@ Tabs.prototype.init = function () {
27
32
  }
28
33
  }
29
34
 
35
+ /**
36
+ * Setup viewport resize check
37
+ */
30
38
  Tabs.prototype.setupResponsiveChecks = function () {
31
39
  this.mql = window.matchMedia('(min-width: 40.0625em)')
32
40
  this.mql.addListener(this.checkMode.bind(this))
33
41
  this.checkMode()
34
42
  }
35
43
 
44
+ /**
45
+ * Setup or teardown handler for viewport resize check
46
+ */
36
47
  Tabs.prototype.checkMode = function () {
37
48
  if (this.mql.matches) {
38
49
  this.setup()
@@ -41,6 +52,9 @@ Tabs.prototype.checkMode = function () {
41
52
  }
42
53
  }
43
54
 
55
+ /**
56
+ * Setup tab component
57
+ */
44
58
  Tabs.prototype.setup = function () {
45
59
  var $module = this.$module
46
60
  var $tabs = this.$tabs
@@ -82,6 +96,9 @@ Tabs.prototype.setup = function () {
82
96
  window.addEventListener('hashchange', $module.boundOnHashChange, true)
83
97
  }
84
98
 
99
+ /**
100
+ * Teardown tab component
101
+ */
85
102
  Tabs.prototype.teardown = function () {
86
103
  var $module = this.$module
87
104
  var $tabs = this.$tabs
@@ -111,7 +128,13 @@ Tabs.prototype.teardown = function () {
111
128
  window.removeEventListener('hashchange', $module.boundOnHashChange, true)
112
129
  }
113
130
 
114
- Tabs.prototype.onHashChange = function (e) {
131
+ /**
132
+ * Handle hashchange event
133
+ *
134
+ * @param {HashChangeEvent} event - Hash change event
135
+ * @returns {void | undefined} Returns void, or undefined when prevented
136
+ */
137
+ Tabs.prototype.onHashChange = function (event) {
115
138
  var hash = window.location.hash
116
139
  var $tabWithHash = this.getTab(hash)
117
140
  if (!$tabWithHash) {
@@ -132,20 +155,41 @@ Tabs.prototype.onHashChange = function (e) {
132
155
  $tabWithHash.focus()
133
156
  }
134
157
 
158
+ /**
159
+ * Hide panel for tab link
160
+ *
161
+ * @param {HTMLAnchorElement} $tab - Tab link
162
+ */
135
163
  Tabs.prototype.hideTab = function ($tab) {
136
164
  this.unhighlightTab($tab)
137
165
  this.hidePanel($tab)
138
166
  }
139
167
 
168
+ /**
169
+ * Show panel for tab link
170
+ *
171
+ * @param {HTMLAnchorElement} $tab - Tab link
172
+ */
140
173
  Tabs.prototype.showTab = function ($tab) {
141
174
  this.highlightTab($tab)
142
175
  this.showPanel($tab)
143
176
  }
144
177
 
178
+ /**
179
+ * Get tab link by hash
180
+ *
181
+ * @param {string} hash - Hash fragment including #
182
+ * @returns {HTMLAnchorElement | null} Tab link
183
+ */
145
184
  Tabs.prototype.getTab = function (hash) {
146
185
  return this.$module.querySelector('.govuk-tabs__tab[href="' + hash + '"]')
147
186
  }
148
187
 
188
+ /**
189
+ * Set tab link and panel attributes
190
+ *
191
+ * @param {HTMLAnchorElement} $tab - Tab link
192
+ */
149
193
  Tabs.prototype.setAttributes = function ($tab) {
150
194
  // set tab attributes
151
195
  var panelId = this.getHref($tab).slice(1)
@@ -162,6 +206,11 @@ Tabs.prototype.setAttributes = function ($tab) {
162
206
  $panel.classList.add(this.jsHiddenClass)
163
207
  }
164
208
 
209
+ /**
210
+ * Unset tab link and panel attributes
211
+ *
212
+ * @param {HTMLAnchorElement} $tab - Tab link
213
+ */
165
214
  Tabs.prototype.unsetAttributes = function ($tab) {
166
215
  // unset tab attributes
167
216
  $tab.removeAttribute('id')
@@ -177,108 +226,186 @@ Tabs.prototype.unsetAttributes = function ($tab) {
177
226
  $panel.classList.remove(this.jsHiddenClass)
178
227
  }
179
228
 
180
- Tabs.prototype.onTabClick = function (e) {
181
- if (!e.target.classList.contains('govuk-tabs__tab')) {
182
- // Allow events on child DOM elements to bubble up to tab parent
229
+ /**
230
+ * Handle tab link clicks
231
+ *
232
+ * @param {MouseEvent} event - Mouse click event
233
+ * @returns {void | false} Returns void, or false within tab link
234
+ */
235
+ Tabs.prototype.onTabClick = function (event) {
236
+ if (!event.target.classList.contains('govuk-tabs__tab')) {
237
+ // Allow events on child DOM elements to bubble up to tab parent
183
238
  return false
184
239
  }
185
- e.preventDefault()
186
- var $newTab = e.target
240
+ event.preventDefault()
241
+ var $newTab = event.target
187
242
  var $currentTab = this.getCurrentTab()
188
243
  this.hideTab($currentTab)
189
244
  this.showTab($newTab)
190
245
  this.createHistoryEntry($newTab)
191
246
  }
192
247
 
248
+ /**
249
+ * Update browser URL hash fragment for tab
250
+ *
251
+ * - Allows back/forward to navigate tabs
252
+ * - Avoids page jump when hash changes
253
+ *
254
+ * @param {HTMLAnchorElement} $tab - Tab link
255
+ */
193
256
  Tabs.prototype.createHistoryEntry = function ($tab) {
194
257
  var $panel = this.getPanel($tab)
195
258
 
196
259
  // Save and restore the id
197
260
  // so the page doesn't jump when a user clicks a tab (which changes the hash)
198
- var id = $panel.id
261
+ var panelId = $panel.id
199
262
  $panel.id = ''
200
263
  this.changingHash = true
201
264
  window.location.hash = this.getHref($tab).slice(1)
202
- $panel.id = id
265
+ $panel.id = panelId
203
266
  }
204
267
 
205
- Tabs.prototype.onTabKeydown = function (e) {
206
- switch (e.keyCode) {
268
+ /**
269
+ * Handle tab keydown event
270
+ *
271
+ * - Press right/down arrow for next tab
272
+ * - Press left/up arrow for previous tab
273
+ *
274
+ * @param {KeyboardEvent} event - Keydown event
275
+ */
276
+ Tabs.prototype.onTabKeydown = function (event) {
277
+ switch (event.keyCode) {
207
278
  case this.keys.left:
208
279
  case this.keys.up:
209
280
  this.activatePreviousTab()
210
- e.preventDefault()
281
+ event.preventDefault()
211
282
  break
212
283
  case this.keys.right:
213
284
  case this.keys.down:
214
285
  this.activateNextTab()
215
- e.preventDefault()
286
+ event.preventDefault()
216
287
  break
217
288
  }
218
289
  }
219
290
 
291
+ /**
292
+ * Activate next tab
293
+ */
220
294
  Tabs.prototype.activateNextTab = function () {
221
- var currentTab = this.getCurrentTab()
222
- var nextTabListItem = currentTab.parentNode.nextElementSibling
223
- if (nextTabListItem) {
224
- var nextTab = nextTabListItem.querySelector('.govuk-tabs__tab')
295
+ var $currentTab = this.getCurrentTab()
296
+ if (!$currentTab) {
297
+ return
225
298
  }
226
- if (nextTab) {
227
- this.hideTab(currentTab)
228
- this.showTab(nextTab)
229
- nextTab.focus()
230
- this.createHistoryEntry(nextTab)
299
+
300
+ var $nextTabListItem = $currentTab.parentElement.nextElementSibling
301
+ if (!$nextTabListItem) {
302
+ return
303
+ }
304
+
305
+ var $nextTab = $nextTabListItem.querySelector('.govuk-tabs__tab')
306
+ if ($nextTab) {
307
+ this.hideTab($currentTab)
308
+ this.showTab($nextTab)
309
+ $nextTab.focus()
310
+ this.createHistoryEntry($nextTab)
231
311
  }
232
312
  }
233
313
 
314
+ /**
315
+ * Activate previous tab
316
+ */
234
317
  Tabs.prototype.activatePreviousTab = function () {
235
- var currentTab = this.getCurrentTab()
236
- var previousTabListItem = currentTab.parentNode.previousElementSibling
237
- if (previousTabListItem) {
238
- var previousTab = previousTabListItem.querySelector('.govuk-tabs__tab')
318
+ var $currentTab = this.getCurrentTab()
319
+ if (!$currentTab) {
320
+ return
321
+ }
322
+
323
+ var $previousTabListItem = $currentTab.parentElement.previousElementSibling
324
+ if (!$previousTabListItem) {
325
+ return
239
326
  }
240
- if (previousTab) {
241
- this.hideTab(currentTab)
242
- this.showTab(previousTab)
243
- previousTab.focus()
244
- this.createHistoryEntry(previousTab)
327
+
328
+ var $previousTab = $previousTabListItem.querySelector('.govuk-tabs__tab')
329
+ if ($previousTab) {
330
+ this.hideTab($currentTab)
331
+ this.showTab($previousTab)
332
+ $previousTab.focus()
333
+ this.createHistoryEntry($previousTab)
245
334
  }
246
335
  }
247
336
 
337
+ /**
338
+ * Get tab panel for tab link
339
+ *
340
+ * @param {HTMLAnchorElement} $tab - Tab link
341
+ * @returns {HTMLDivElement} Tab panel
342
+ */
248
343
  Tabs.prototype.getPanel = function ($tab) {
249
344
  var $panel = this.$module.querySelector(this.getHref($tab))
250
345
  return $panel
251
346
  }
252
347
 
348
+ /**
349
+ * Show tab panel for tab link
350
+ *
351
+ * @param {HTMLAnchorElement} $tab - Tab link
352
+ */
253
353
  Tabs.prototype.showPanel = function ($tab) {
254
354
  var $panel = this.getPanel($tab)
255
355
  $panel.classList.remove(this.jsHiddenClass)
256
356
  }
257
357
 
258
- Tabs.prototype.hidePanel = function (tab) {
259
- var $panel = this.getPanel(tab)
358
+ /**
359
+ * Hide tab panel for tab link
360
+ *
361
+ * @param {HTMLAnchorElement} $tab - Tab link
362
+ */
363
+ Tabs.prototype.hidePanel = function ($tab) {
364
+ var $panel = this.getPanel($tab)
260
365
  $panel.classList.add(this.jsHiddenClass)
261
366
  }
262
367
 
368
+ /**
369
+ * Unset 'selected' state for tab link
370
+ *
371
+ * @param {HTMLAnchorElement} $tab - Tab link
372
+ */
263
373
  Tabs.prototype.unhighlightTab = function ($tab) {
264
374
  $tab.setAttribute('aria-selected', 'false')
265
375
  $tab.parentNode.classList.remove('govuk-tabs__list-item--selected')
266
376
  $tab.setAttribute('tabindex', '-1')
267
377
  }
268
378
 
379
+ /**
380
+ * Set 'selected' state for tab link
381
+ *
382
+ * @param {HTMLAnchorElement} $tab - Tab link
383
+ */
269
384
  Tabs.prototype.highlightTab = function ($tab) {
270
385
  $tab.setAttribute('aria-selected', 'true')
271
386
  $tab.parentNode.classList.add('govuk-tabs__list-item--selected')
272
387
  $tab.setAttribute('tabindex', '0')
273
388
  }
274
389
 
390
+ /**
391
+ * Get current tab link
392
+ *
393
+ * @returns {HTMLAnchorElement | undefined} Tab link
394
+ */
275
395
  Tabs.prototype.getCurrentTab = function () {
276
396
  return this.$module.querySelector('.govuk-tabs__list-item--selected .govuk-tabs__tab')
277
397
  }
278
398
 
279
- // this is because IE doesn't always return the actual value but a relative full path
280
- // should be a utility function most prob
281
- // http://labs.thesedays.com/blog/2010/01/08/getting-the-href-value-with-jquery-in-ie/
399
+ /**
400
+ * Get link hash fragment for href attribute
401
+ *
402
+ * this is because IE doesn't always return the actual value but a relative full path
403
+ * should be a utility function most prob
404
+ * {@link http://labs.thesedays.com/blog/2010/01/08/getting-the-href-value-with-jquery-in-ie/}
405
+ *
406
+ * @param {HTMLAnchorElement} $tab - Tab link
407
+ * @returns {string} Hash fragment including #
408
+ */
282
409
  Tabs.prototype.getHref = function ($tab) {
283
410
  var href = $tab.getAttribute('href')
284
411
  var hash = href.slice(href.indexOf('#'), href.length)
@@ -4,9 +4,9 @@
4
4
  *
5
5
  * @class
6
6
  * @private
7
- * @param {TranslationsFlattened} translations - Key-value pairs of the translation strings to use.
7
+ * @param {Object<string, unknown>} translations - Key-value pairs of the translation strings to use.
8
8
  * @param {object} [config] - Configuration options for the function.
9
- * @param {string} config.locale - An overriding locale for the PluralRules functionality.
9
+ * @param {string} [config.locale] - An overriding locale for the PluralRules functionality.
10
10
  */
11
11
  export function I18n (translations, config) {
12
12
  // Make list of translations available throughout function
@@ -21,8 +21,10 @@ export function I18n (translations, config) {
21
21
  * returns the appropriate string.
22
22
  *
23
23
  * @param {string} lookupKey - The lookup key of the string to use.
24
- * @param {object} options - Any options passed with the translation string, e.g: for string interpolation.
24
+ * @param {Object<string, unknown>} [options] - Any options passed with the translation string, e.g: for string interpolation.
25
25
  * @returns {string} The appropriate translation string.
26
+ * @throws {Error} Lookup key required
27
+ * @throws {Error} Options required for `${}` placeholders
26
28
  */
27
29
  I18n.prototype.t = function (lookupKey, options) {
28
30
  if (!lookupKey) {
@@ -64,7 +66,7 @@ I18n.prototype.t = function (lookupKey, options) {
64
66
  * with the provided data
65
67
  *
66
68
  * @param {string} translationString - The translation string
67
- * @param {object} options - Any options passed with the translation string, e.g: for string interpolation.
69
+ * @param {Object<string, unknown>} options - Any options passed with the translation string, e.g: for string interpolation.
68
70
  * @returns {string} The translation string to output, with ${} placeholders replaced
69
71
  */
70
72
  I18n.prototype.replacePlaceholders = function (translationString, options) {
@@ -137,6 +139,7 @@ I18n.prototype.hasIntlNumberFormatSupport = function () {
137
139
  * @param {string} lookupKey - The lookup key of the string to use.
138
140
  * @param {number} count - Number used to determine which pluralisation to use.
139
141
  * @returns {PluralRule} The suffix associated with the correct pluralisation for this locale.
142
+ * @throws {Error} Plural form `.other` required when preferred plural form is missing
140
143
  */
141
144
  I18n.prototype.getPluralSuffix = function (lookupKey, count) {
142
145
  // Validate that the number is actually a number.
@@ -290,6 +293,7 @@ I18n.pluralRulesMap = {
290
293
  * @type {Object<string, function(number): PluralRule>}
291
294
  */
292
295
  I18n.pluralRules = {
296
+ /* eslint-disable jsdoc/require-jsdoc */
293
297
  arabic: function (n) {
294
298
  if (n === 0) { return 'zero' }
295
299
  if (n === 1) { return 'one' }
@@ -343,6 +347,7 @@ I18n.pluralRules = {
343
347
  if (n === 6) { return 'many' }
344
348
  return 'other'
345
349
  }
350
+ /* eslint-enable jsdoc/require-jsdoc */
346
351
  }
347
352
 
348
353
  /**
@@ -371,10 +376,3 @@ I18n.pluralRules = {
371
376
  * @property {string} [few] - Plural form used for a few
372
377
  * @property {string} [many] - Plural form used for many
373
378
  */
374
-
375
- /**
376
- * Translated messages (flattened)
377
- *
378
- * @private
379
- * @typedef {Object<string, string> | {}} TranslationsFlattened
380
- */
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "govuk-frontend",
3
3
  "description": "GOV.UK Frontend contains the code you need to start building a user interface for government platforms and services.",
4
- "version": "4.4.1",
4
+ "version": "4.5.0",
5
5
  "main": "govuk/all.js",
6
6
  "module": "govuk-esm/all.mjs",
7
7
  "exports": {