govuk_publishing_components 21.62.0 → 21.64.0

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/components/govspeak.js +17 -15
  3. data/app/assets/javascripts/govuk_publishing_components/components/step-by-step-nav.js +402 -340
  4. data/app/assets/javascripts/govuk_publishing_components/dependencies.js +0 -5
  5. data/app/assets/javascripts/govuk_publishing_components/lib.js +1 -0
  6. data/app/assets/javascripts/govuk_publishing_components/lib/govspeak/barchart-enhancement.js +18 -11
  7. data/app/assets/javascripts/govuk_publishing_components/lib/govspeak/magna-charta.js +423 -0
  8. data/app/assets/javascripts/govuk_publishing_components/lib/toggle.js +126 -65
  9. data/app/assets/javascripts/govuk_publishing_components/vendor/polyfills/closest.js +23 -0
  10. data/app/assets/javascripts/govuk_publishing_components/vendor/polyfills/indexOf.js +9 -0
  11. data/app/assets/stylesheets/govuk_publishing_components/components/_breadcrumbs.scss +3 -16
  12. data/app/assets/stylesheets/govuk_publishing_components/components/_panel.scss +7 -1
  13. data/app/views/govuk_publishing_components/component_guide/example.html.erb +4 -1
  14. data/app/views/govuk_publishing_components/component_guide/show.html.erb +3 -1
  15. data/app/views/govuk_publishing_components/components/_breadcrumbs.html.erb +1 -1
  16. data/app/views/govuk_publishing_components/components/_cookie_banner.html.erb +1 -1
  17. data/app/views/govuk_publishing_components/components/_document_list.html.erb +18 -9
  18. data/app/views/govuk_publishing_components/components/_panel.html.erb +13 -11
  19. data/app/views/govuk_publishing_components/components/_radio.html.erb +3 -1
  20. data/app/views/govuk_publishing_components/components/_search.html.erb +6 -2
  21. data/app/views/govuk_publishing_components/components/docs/document_list.yml +18 -0
  22. data/app/views/govuk_publishing_components/components/feedback/_problem_form.html.erb +6 -6
  23. data/app/views/govuk_publishing_components/components/feedback/_survey_signup_form.html.erb +6 -6
  24. data/app/views/govuk_publishing_components/components/feedback/_yes_no_banner.html.erb +8 -10
  25. data/config/locales/en.yml +18 -0
  26. data/lib/govuk_publishing_components/presenters/breadcrumb_selector.rb +5 -0
  27. data/lib/govuk_publishing_components/presenters/content_breadcrumbs_based_on_priority.rb +19 -5
  28. data/lib/govuk_publishing_components/presenters/contextual_navigation.rb +1 -1
  29. data/lib/govuk_publishing_components/version.rb +1 -1
  30. metadata +5 -3
  31. data/app/assets/javascripts/govuk_publishing_components/vendor/magna-charta.min.js +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d338f19f706ae958279f7217d7b10ae3f3939a0e9b850dc481b4a8878249ec53
4
- data.tar.gz: f9a356891d53fc81f49e2f467aefaa233947ebf501d27c1f5dd198574f2de370
3
+ metadata.gz: 19018ea589b383f250f5b7298d06301f5b55e196b475b092e80f707f09bd399b
4
+ data.tar.gz: 1748c5cd55d806fe8e5ef22025ad348a5fe720a045d7879644980224693f8986
5
5
  SHA512:
6
- metadata.gz: 18644ee3c0753253a18d4fd13da292ad4a48e5b78de3be7d2f030a400b5b5b1937bb7433fef7161ca5f29576cf33db0fece60bbc38c0aea1d8c0b08866df6d10
7
- data.tar.gz: 31ffd5af5a5472dc4e294c4b5af84750db0512ead35ecc784bad5b8bbb81f734efdf49d14d59e5e6acc643f62d47b2c016cf1720cdc6d84f6bf74f320b8dae6b
6
+ metadata.gz: 209e7cf684d04b5be7ad3c45ad8a0083c2da82c170877fca927b51b5c422e89f82de285b9c2131f5db65996aac0a28af4a7c680d34ffb827ba382b0682314d10
7
+ data.tar.gz: 17df0e8345e4fb29baaf8c3c61eeb63c68acaebdcc30a3f4cdc07e857b18f1b45368d1178717008b5027c9de1c2fffb2d4c9d84ee20c4db976376937939e4aec
@@ -2,25 +2,27 @@ window.GOVUK = window.GOVUK || {}
2
2
  window.GOVUK.Modules = window.GOVUK.Modules || {};
3
3
 
4
4
  (function (Modules) {
5
- 'use strict'
5
+ function Govspeak () { }
6
6
 
7
- Modules.Govspeak = function () {
8
- this.start = function ($element) {
9
- if (!$element.hasClass('disable-youtube')) {
10
- this.embedYoutube($element)
11
- }
7
+ Govspeak.prototype.start = function ($module) {
8
+ this.$module = $module[0]
12
9
 
13
- this.createBarcharts($element)
10
+ if (this.$module.className.indexOf('disable-youtube') === -1) {
11
+ this.embedYoutube()
14
12
  }
15
13
 
16
- this.embedYoutube = function ($jqElement) {
17
- var enhancement = new window.GOVUK.GovspeakYoutubeLinkEnhancement($jqElement[0])
18
- enhancement.init()
19
- }
14
+ this.createBarcharts()
15
+ }
20
16
 
21
- this.createBarcharts = function ($element) {
22
- var enhancement = new window.GOVUK.GovspeakBarchartEnhancement($element)
23
- enhancement.init()
24
- }
17
+ Govspeak.prototype.embedYoutube = function () {
18
+ var enhancement = new window.GOVUK.GovspeakYoutubeLinkEnhancement(this.$module)
19
+ enhancement.init()
25
20
  }
21
+
22
+ Govspeak.prototype.createBarcharts = function () {
23
+ var enhancement = new window.GOVUK.GovspeakBarchartEnhancement(this.$module)
24
+ enhancement.init()
25
+ }
26
+
27
+ Modules.Govspeak = Govspeak
26
28
  })(window.GOVUK.Modules)
@@ -1,432 +1,494 @@
1
- /* eslint-env jquery */
1
+ //= require govuk/vendor/polyfills/Element/prototype/classList.js
2
+ //= require ../vendor/polyfills/closest.js
3
+ //= require ../vendor/polyfills/indexOf.js
2
4
 
3
5
  window.GOVUK = window.GOVUK || {}
4
6
  window.GOVUK.Modules = window.GOVUK.Modules || {};
5
7
 
6
8
  (function (Modules) {
7
- 'use strict'
8
-
9
- Modules.Gemstepnav = function () {
10
- var actions = {} // stores text for JS appended elements 'show' and 'hide' on steps, and 'show/hide all' button
11
- var rememberShownStep = false
12
- var stepNavSize
13
- var sessionStoreLink = 'govuk-step-nav-active-link'
14
- var activeLinkClass = 'gem-c-step-nav__list-item--active'
15
- var activeStepClass = 'gem-c-step-nav__step--active'
16
- var activeLinkHref = '#content'
17
- var uniqueId
18
-
19
- this.start = function ($element) {
20
- // Indicate that js has worked
21
- $element.addClass('gem-c-step-nav--active')
22
-
23
- // Prevent FOUC, remove class hiding content
24
- $element.removeClass('js-hidden')
25
-
26
- stepNavSize = $element.hasClass('gem-c-step-nav--large') ? 'Big' : 'Small'
27
- rememberShownStep = !!$element.filter('[data-remember]').length && stepNavSize === 'Big'
28
- var $steps = $element.find('.js-step')
29
- var $stepHeaders = $element.find('.js-toggle-panel')
30
- var totalSteps = $element.find('.js-panel').length
31
- var totalLinks = $element.find('.gem-c-step-nav__link').length
32
- var $showOrHideAllButton
33
-
34
- uniqueId = $element.data('id') || false
35
-
36
- if (uniqueId) {
37
- sessionStoreLink = sessionStoreLink + '_' + uniqueId
38
- }
9
+ function Gemstepnav () { }
10
+
11
+ Gemstepnav.prototype.start = function ($module) {
12
+ this.$module = $module[0]
13
+ this.$module.actions = {} // stores text for JS appended elements 'show' and 'hide' on steps, and 'show/hide all' button
14
+ this.$module.rememberShownStep = false
15
+ this.$module.stepNavSize = false
16
+ this.$module.sessionStoreLink = 'govuk-step-nav-active-link'
17
+ this.$module.activeLinkClass = 'gem-c-step-nav__list-item--active'
18
+ this.$module.activeStepClass = 'gem-c-step-nav__step--active'
19
+ this.$module.activeLinkHref = '#content'
20
+ this.$module.uniqueId = false
21
+
22
+ // Indicate that js has worked
23
+ this.$module.classList.add('gem-c-step-nav--active')
24
+
25
+ // Prevent FOUC, remove class hiding content
26
+ this.$module.classList.remove('js-hidden')
27
+
28
+ this.$module.stepNavSize = this.$module.classList.contains('gem-c-step-nav--large') ? 'Big' : 'Small'
29
+ this.$module.rememberShownStep = !!this.$module.hasAttribute('data-remember') && this.$module.stepNavSize === 'Big'
30
+
31
+ this.$module.steps = this.$module.querySelectorAll('.js-step')
32
+ this.$module.stepHeaders = this.$module.querySelectorAll('.js-toggle-panel')
33
+ this.$module.totalSteps = this.$module.querySelectorAll('.js-panel').length
34
+ this.$module.totalLinks = this.$module.querySelectorAll('.gem-c-step-nav__link').length
35
+ this.$module.showOrHideAllButton = false
36
+
37
+ this.$module.uniqueId = this.$module.getAttribute('data-id') || false
38
+
39
+ if (this.$module.uniqueId) {
40
+ this.$module.sessionStoreLink = this.$module.sessionStoreLink + '_' + this.$module.uniqueId
41
+ }
39
42
 
40
- var stepNavTracker = new StepNavTracker(totalSteps, totalLinks, uniqueId)
43
+ var stepNavTracker = new this.StepNavTracker(this.$module.uniqueId, this.$module.totalSteps, this.$module.totalLinks)
41
44
 
42
- getTextForInsertedElements()
43
- addButtonstoSteps()
44
- addShowHideAllButton()
45
- addShowHideToggle()
46
- addAriaControlsAttrForShowHideAllButton()
45
+ this.getTextForInsertedElements()
46
+ this.addButtonstoSteps()
47
+ this.addShowHideAllButton()
48
+ this.addShowHideToggle()
49
+ this.addAriaControlsAttrForShowHideAllButton()
47
50
 
48
- ensureOnlyOneActiveLink()
49
- showPreviouslyOpenedSteps()
51
+ this.ensureOnlyOneActiveLink()
52
+ this.showPreviouslyOpenedSteps()
50
53
 
51
- bindToggleForSteps(stepNavTracker)
52
- bindToggleShowHideAllButton(stepNavTracker)
53
- bindComponentLinkClicks(stepNavTracker)
54
+ this.bindToggleForSteps(stepNavTracker)
55
+ this.bindToggleShowHideAllButton(stepNavTracker)
56
+ this.bindComponentLinkClicks(stepNavTracker)
57
+ }
54
58
 
55
- function getTextForInsertedElements () {
56
- actions.showText = $element.attr('data-show-text')
57
- actions.hideText = $element.attr('data-hide-text')
58
- actions.showAllText = $element.attr('data-show-all-text')
59
- actions.hideAllText = $element.attr('data-hide-all-text')
60
- }
59
+ Gemstepnav.prototype.getTextForInsertedElements = function () {
60
+ this.$module.actions.showText = this.$module.getAttribute('data-show-text')
61
+ this.$module.actions.hideText = this.$module.getAttribute('data-hide-text')
62
+ this.$module.actions.showAllText = this.$module.getAttribute('data-show-all-text')
63
+ this.$module.actions.hideAllText = this.$module.getAttribute('data-hide-all-text')
64
+ }
61
65
 
62
- function addShowHideAllButton () {
63
- $element.prepend('<div class="gem-c-step-nav__controls"><button aria-expanded="false" class="gem-c-step-nav__button gem-c-step-nav__button--controls js-step-controls-button">' + actions.showAllText + '</button></div>')
64
- }
66
+ Gemstepnav.prototype.addShowHideAllButton = function () {
67
+ var showall = document.createElement('div')
68
+ showall.className = 'gem-c-step-nav__controls'
69
+ showall.innerHTML = '<button aria-expanded="false" class="gem-c-step-nav__button gem-c-step-nav__button--controls js-step-controls-button">' + this.$module.actions.showAllText + '</button>'
65
70
 
66
- function addShowHideToggle () {
67
- $stepHeaders.each(function () {
68
- var linkText = actions.showText // eslint-disable-line no-unused-vars
71
+ var steps = this.$module.querySelectorAll('.gem-c-step-nav__steps')[0]
72
+ this.$module.insertBefore(showall, steps)
69
73
 
70
- if (headerIsOpen($(this))) {
71
- linkText = actions.hideText
72
- }
74
+ this.$module.showOrHideAllButton = this.$module.querySelectorAll('.js-step-controls-button')[0]
75
+ }
73
76
 
74
- if (!$(this).find('.js-toggle-link').length) {
75
- $(this).find('.js-step-title-button').append(
76
- '<span class="gem-c-step-nav__toggle-link js-toggle-link" aria-hidden="true" hidden></span>'
77
- )
78
- }
79
- })
80
- }
77
+ Gemstepnav.prototype.addShowHideToggle = function () {
78
+ for (var i = 0; i < this.$module.stepHeaders.length; i++) {
79
+ var thisel = this.$module.stepHeaders[i]
81
80
 
82
- function headerIsOpen ($stepHeader) {
83
- return (typeof $stepHeader.closest('.js-step').data('show') !== 'undefined')
81
+ if (!thisel.querySelectorAll('.js-toggle-link').length) {
82
+ var span = document.createElement('span')
83
+ span.className = 'gem-c-step-nav__toggle-link js-toggle-link'
84
+ span.setAttribute('aria-hidden', true)
85
+ span.setAttribute('hidden', 'hidden')
86
+ thisel.querySelectorAll('.js-step-title-button')[0].appendChild(span)
84
87
  }
88
+ }
89
+ }
85
90
 
86
- function addAriaControlsAttrForShowHideAllButton () {
87
- var ariaControlsValue = $element.find('.js-panel').first().attr('id')
91
+ Gemstepnav.prototype.headerIsOpen = function (stepHeader) {
92
+ return (typeof stepHeader.parentNode.getAttribute('show') !== 'undefined')
93
+ }
88
94
 
89
- $showOrHideAllButton = $element.find('.js-step-controls-button')
90
- $showOrHideAllButton.attr('aria-controls', ariaControlsValue)
91
- }
95
+ Gemstepnav.prototype.addAriaControlsAttrForShowHideAllButton = function () {
96
+ var ariaControlsValue = this.$module.querySelectorAll('.js-panel')[0].getAttribute('id')
97
+ this.$module.showOrHideAllButton.setAttribute('aria-controls', ariaControlsValue)
98
+ }
92
99
 
93
- // called by show all/hide all, sets all steps accordingly
94
- function setAllStepsShownState (isShown) {
95
- var data = []
100
+ // called by show all/hide all, sets all steps accordingly
101
+ Gemstepnav.prototype.setAllStepsShownState = function (isShown) {
102
+ var data = []
96
103
 
97
- $.each($steps, function () {
98
- var stepView = new StepView($(this))
99
- stepView.setIsShown(isShown)
104
+ for (var i = 0; i < this.$module.steps.length; i++) {
105
+ var stepView = new this.StepView(this.$module.steps[i], this.$module.actions.showText, this.$module.actions.hideText)
106
+ stepView.setIsShown(isShown)
100
107
 
101
- if (isShown) {
102
- data.push($(this).attr('id'))
103
- }
104
- })
105
-
106
- if (isShown) {
107
- saveToSessionStorage(uniqueId, JSON.stringify(data))
108
- } else {
109
- removeFromSessionStorage(uniqueId)
110
- }
108
+ if (isShown) {
109
+ data.push(this.$module.steps[i].getAttribute('id'))
111
110
  }
111
+ }
112
112
 
113
- // called on load, determines whether each step should be open or closed
114
- function showPreviouslyOpenedSteps () {
115
- var data = loadFromSessionStorage(uniqueId) || []
113
+ if (isShown) {
114
+ this.saveToSessionStorage(this.$module.uniqueId, JSON.stringify(data))
115
+ } else {
116
+ this.removeFromSessionStorage(this.$module.uniqueId)
117
+ }
118
+ }
116
119
 
117
- $.each($steps, function () {
118
- var id = $(this).attr('id')
119
- var stepView = new StepView($(this))
120
+ // called on load, determines whether each step should be open or closed
121
+ Gemstepnav.prototype.showPreviouslyOpenedSteps = function () {
122
+ var data = this.loadFromSessionStorage(this.$module.uniqueId) || []
120
123
 
121
- // show the step if it has been remembered or if it has the 'data-show' attribute
122
- if ((rememberShownStep && data.indexOf(id) > -1) || typeof $(this).attr('data-show') !== 'undefined') {
123
- stepView.setIsShown(true)
124
- } else {
125
- stepView.setIsShown(false)
126
- }
127
- })
124
+ for (var i = 0; i < this.$module.steps.length; i++) {
125
+ var thisel = this.$module.steps[i]
126
+ var id = thisel.getAttribute('id')
127
+ var stepView = new this.StepView(thisel, this.$module.actions.showText, this.$module.actions.hideText)
128
+ var shouldBeShown = thisel.hasAttribute('data-show')
128
129
 
129
- if (data.length > 0) {
130
- $showOrHideAllButton.attr('aria-expanded', true)
131
- setShowHideAllText()
132
- }
130
+ // show the step if it has been remembered or if it has the 'data-show' attribute
131
+ if ((this.$module.rememberShownStep && data.indexOf(id) > -1) || (shouldBeShown && shouldBeShown !== 'undefined')) {
132
+ stepView.setIsShown(true)
133
+ } else {
134
+ stepView.setIsShown(false)
133
135
  }
136
+ }
134
137
 
135
- function addButtonstoSteps () {
136
- $.each($steps, function () {
137
- var $step = $(this)
138
- var $title = $step.find('.js-step-title')
139
- var contentId = $step.find('.js-panel').first().attr('id')
138
+ if (data.length > 0) {
139
+ this.$module.showOrHideAllButton.setAttribute('aria-expanded', true)
140
+ this.setShowHideAllText()
141
+ }
142
+ }
140
143
 
141
- $title.wrapInner(
142
- '<span class="js-step-title-text"></span>'
143
- )
144
+ Gemstepnav.prototype.addButtonstoSteps = function () {
145
+ for (var i = 0; i < this.$module.steps.length; i++) {
146
+ var thisel = this.$module.steps[i]
147
+ var title = thisel.querySelectorAll('.js-step-title')[0]
148
+ var contentId = thisel.querySelectorAll('.js-panel')[0].getAttribute('id')
149
+ var titleText = title.textContent || title.innerText // IE8 fallback
144
150
 
145
- $title.wrapInner(
146
- '<button ' +
151
+ title.outerHTML =
152
+ '<span class="js-step-title">' +
153
+ '<button ' +
147
154
  'class="gem-c-step-nav__button gem-c-step-nav__button--title js-step-title-button" ' +
148
155
  'aria-expanded="false" aria-controls="' + contentId + '">' +
149
- '</button>'
150
- )
151
- })
152
- }
156
+ '<span class="js-step-title-text">' + titleText + '</span>' +
157
+ '</button>' +
158
+ '</span>'
159
+ }
160
+ }
153
161
 
154
- function bindToggleForSteps (stepNavTracker) {
155
- $element.find('.js-toggle-panel').click(function (event) {
156
- var $step = $(this).closest('.js-step')
162
+ Gemstepnav.prototype.bindToggleForSteps = function (stepNavTracker) {
163
+ var that = this
164
+ var togglePanels = this.$module.querySelectorAll('.js-toggle-panel')
157
165
 
158
- var stepView = new StepView($step)
159
- stepView.toggle()
166
+ for (var i = 0; i < togglePanels.length; i++) {
167
+ togglePanels[i].addEventListener('click', function (event) {
168
+ var stepView = new that.StepView(this.parentNode, that.$module.actions.showText, that.$module.actions.hideText)
169
+ stepView.toggle()
160
170
 
161
- var stepIsOptional = typeof $step.data('optional') !== 'undefined'
162
- var toggleClick = new StepToggleClick(event, stepView, $steps, stepNavTracker, stepIsOptional)
163
- toggleClick.track()
171
+ var stepIsOptional = this.parentNode.hasAttribute('data-optional')
172
+ var toggleClick = new that.StepToggleClick(event, stepView, stepNavTracker, stepIsOptional, that.$module.stepNavSize)
173
+ toggleClick.trackClick()
164
174
 
165
- setShowHideAllText()
166
- rememberStepState($step)
167
- })
168
- }
175
+ that.setShowHideAllText()
176
+ that.rememberStepState(this.parentNode)
177
+ })
178
+ }
179
+ }
169
180
 
170
- // if the step is open, store its id in session store
171
- // if the step is closed, remove its id from session store
172
- function rememberStepState ($step) {
173
- if (rememberShownStep) {
174
- var data = JSON.parse(loadFromSessionStorage(uniqueId)) || []
175
- var thisstep = $step.attr('id')
176
- var shown = $step.hasClass('step-is-shown')
177
-
178
- if (shown) {
179
- data.push(thisstep)
180
- } else {
181
- var i = data.indexOf(thisstep)
182
- if (i > -1) {
183
- data.splice(i, 1)
184
- }
185
- }
186
- saveToSessionStorage(uniqueId, JSON.stringify(data))
181
+ // if the step is open, store its id in session store
182
+ // if the step is closed, remove its id from session store
183
+ Gemstepnav.prototype.rememberStepState = function (step) {
184
+ if (this.$module.rememberShownStep) {
185
+ var data = JSON.parse(this.loadFromSessionStorage(this.$module.uniqueId)) || []
186
+ var thisstep = step.getAttribute('id')
187
+ var shown = step.classList.contains('step-is-shown')
188
+
189
+ if (shown) {
190
+ data.push(thisstep)
191
+ } else {
192
+ var i = data.indexOf(thisstep)
193
+ if (i > -1) {
194
+ data.splice(i, 1)
187
195
  }
188
196
  }
197
+ this.saveToSessionStorage(this.$module.uniqueId, JSON.stringify(data))
198
+ }
199
+ }
189
200
 
190
- // tracking click events on links in step content
191
- function bindComponentLinkClicks (stepNavTracker) {
192
- $element.find('.js-link').click(function (event) {
193
- var linkClick = new componentLinkClick(event, stepNavTracker, $(this).attr('data-position')) // eslint-disable-line no-new, new-cap
194
- linkClick.track()
195
- var thisLinkHref = $(this).attr('href')
196
-
197
- if ($(this).attr('rel') !== 'external') {
198
- saveToSessionStorage(sessionStoreLink, $(this).attr('data-position'))
199
- }
200
-
201
- if (thisLinkHref === activeLinkHref) {
202
- setOnlyThisLinkActive($(this))
203
- setActiveStepClass()
204
- }
205
- })
206
- }
201
+ // tracking click events on links in step content
202
+ Gemstepnav.prototype.bindComponentLinkClicks = function (stepNavTracker) {
203
+ var jsLinks = this.$module.querySelectorAll('.js-link')
204
+ var that = this
207
205
 
208
- function saveToSessionStorage (key, value) {
209
- window.sessionStorage.setItem(key, value)
210
- }
206
+ for (var i = 0; i < jsLinks.length; i++) {
207
+ jsLinks[i].addEventListener('click', function (event) {
208
+ var dataPosition = this.getAttribute('data-position')
209
+ var linkClick = new that.ComponentLinkClick(event, stepNavTracker, dataPosition, that.$module.stepNavSize)
210
+ linkClick.trackClick()
211
211
 
212
- function loadFromSessionStorage (key) {
213
- return window.sessionStorage.getItem(key)
214
- }
212
+ if (this.getAttribute('rel') !== 'external') {
213
+ that.saveToSessionStorage(that.$module.sessionStoreLink, dataPosition)
214
+ }
215
215
 
216
- function removeFromSessionStorage (key) {
217
- window.sessionStorage.removeItem(key)
218
- }
216
+ if (this.getAttribute('href') === that.$module.activeLinkHref) {
217
+ that.setOnlyThisLinkActive(this)
218
+ that.setActiveStepClass()
219
+ }
220
+ })
221
+ }
222
+ }
219
223
 
220
- function setOnlyThisLinkActive (clicked) {
221
- $element.find('.' + activeLinkClass).removeClass(activeLinkClass)
222
- clicked.parent().addClass(activeLinkClass)
223
- }
224
+ Gemstepnav.prototype.saveToSessionStorage = function (key, value) {
225
+ window.sessionStorage.setItem(key, value)
226
+ }
224
227
 
225
- // if a link occurs more than once in a step nav, the backend doesn't know which one to highlight
226
- // so it gives all those links the 'active' attribute and highlights the last step containing that link
227
- // if the user clicked on one of those links previously, it will be in the session store
228
- // this code ensures only that link and its corresponding step have the highlighting
229
- // otherwise it accepts what the backend has already passed to the component
230
- function ensureOnlyOneActiveLink () {
231
- var $activeLinks = $element.find('.js-list-item.' + activeLinkClass)
228
+ Gemstepnav.prototype.loadFromSessionStorage = function (key, value) {
229
+ return window.sessionStorage.getItem(key)
230
+ }
232
231
 
233
- if ($activeLinks.length <= 1) {
234
- return
235
- }
232
+ Gemstepnav.prototype.removeFromSessionStorage = function (key) {
233
+ window.sessionStorage.removeItem(key)
234
+ }
236
235
 
237
- var lastClicked = loadFromSessionStorage(sessionStoreLink) || $element.find('.' + activeLinkClass).first().attr('data-position')
236
+ Gemstepnav.prototype.setOnlyThisLinkActive = function (clicked) {
237
+ var allActiveLinks = this.$module.querySelectorAll('.' + this.$module.activeLinkClass)
238
+ for (var i = 0; i < allActiveLinks.length; i++) {
239
+ allActiveLinks[i].classList.remove(this.$module.activeLinkClass)
240
+ }
241
+ clicked.parentNode.classList.add(this.$module.activeLinkClass)
242
+ }
238
243
 
239
- // it's possible for the saved link position value to not match any of the currently duplicate highlighted links
240
- // so check this otherwise it'll take the highlighting off all of them
241
- if (!$element.find('.js-link[data-position="' + lastClicked + '"]').parent().hasClass(activeLinkClass)) {
242
- lastClicked = $element.find('.' + activeLinkClass).first().find('.js-link').attr('data-position')
243
- }
244
- removeActiveStateFromAllButCurrent($activeLinks, lastClicked)
245
- setActiveStepClass()
246
- }
244
+ // if a link occurs more than once in a step nav, the backend doesn't know which one to highlight
245
+ // so it gives all those links the 'active' attribute and highlights the last step containing that link
246
+ // if the user clicked on one of those links previously, it will be in the session store
247
+ // this code ensures only that link and its corresponding step have the highlighting
248
+ // otherwise it accepts what the backend has already passed to the component
249
+ Gemstepnav.prototype.ensureOnlyOneActiveLink = function () {
250
+ var activeLinks = this.$module.querySelectorAll('.js-list-item.' + this.$module.activeLinkClass)
247
251
 
248
- function removeActiveStateFromAllButCurrent ($activeLinks, current) {
249
- $activeLinks.each(function () {
250
- if ($(this).find('.js-link').attr('data-position').toString() !== current.toString()) {
251
- $(this).removeClass(activeLinkClass)
252
- $(this).find('.visuallyhidden').remove()
253
- }
254
- })
255
- }
252
+ if (activeLinks.length <= 1) {
253
+ return
254
+ }
256
255
 
257
- function setActiveStepClass () {
258
- $element.find('.' + activeStepClass).removeClass(activeStepClass).removeAttr('data-show')
259
- $element.find('.' + activeLinkClass).closest('.gem-c-step-nav__step').addClass(activeStepClass).attr('data-show', '')
260
- }
256
+ var loaded = this.loadFromSessionStorage(this.$module.sessionStoreLink)
257
+ var activeParent = this.$module.querySelectorAll('.' + this.$module.activeLinkClass)[0]
258
+ var activeChild = activeParent.firstChild
259
+ var foundLink = activeChild.getAttribute('data-position')
260
+ var lastClicked = loaded || foundLink // the value saved has priority
261
261
 
262
- function bindToggleShowHideAllButton (stepNavTracker) {
263
- $showOrHideAllButton = $element.find('.js-step-controls-button')
264
- $showOrHideAllButton.on('click', function () {
265
- var shouldshowAll
266
-
267
- if ($showOrHideAllButton.text() === actions.showAllText) {
268
- $showOrHideAllButton.text(actions.hideAllText)
269
- $element.find('.js-toggle-link').html(actions.hideText)
270
- shouldshowAll = true
271
-
272
- stepNavTracker.track('pageElementInteraction', 'stepNavAllShown', {
273
- label: actions.showAllText + ': ' + stepNavSize
274
- })
275
- } else {
276
- $showOrHideAllButton.text(actions.showAllText)
277
- $element.find('.js-toggle-link').html(actions.showText)
278
- shouldshowAll = false
279
-
280
- stepNavTracker.track('pageElementInteraction', 'stepNavAllHidden', {
281
- label: actions.hideAllText + ': ' + stepNavSize
282
- })
283
- }
284
-
285
- setAllStepsShownState(shouldshowAll)
286
- $showOrHideAllButton.attr('aria-expanded', shouldshowAll)
287
- setShowHideAllText()
288
-
289
- return false
290
- })
262
+ // it's possible for the saved link position value to not match any of the currently duplicate highlighted links
263
+ // so check this otherwise it'll take the highlighting off all of them
264
+ var checkLink = this.$module.querySelectorAll('[data-position="' + lastClicked + '"]')[0]
265
+
266
+ if (checkLink) {
267
+ if (!checkLink.parentNode.classList.contains(this.$module.activeLinkClass)) {
268
+ lastClicked = checkLink
291
269
  }
270
+ } else {
271
+ lastClicked = foundLink
272
+ }
273
+
274
+ this.removeActiveStateFromAllButCurrent(activeLinks, lastClicked)
275
+ this.setActiveStepClass()
276
+ }
292
277
 
293
- function setShowHideAllText () {
294
- var shownSteps = $element.find('.step-is-shown').length
295
- // Find out if the number of is-opens == total number of steps
296
- if (shownSteps === totalSteps) {
297
- $showOrHideAllButton.text(actions.hideAllText)
298
- } else {
299
- $showOrHideAllButton.text(actions.showAllText)
278
+ Gemstepnav.prototype.removeActiveStateFromAllButCurrent = function (activeLinks, current) {
279
+ for (var i = 0; i < activeLinks.length; i++) {
280
+ var thisel = activeLinks[i]
281
+ if (thisel.querySelectorAll('.js-link')[0].getAttribute('data-position').toString() !== current.toString()) {
282
+ thisel.classList.remove(this.$module.activeLinkClass)
283
+ var visuallyHidden = thisel.querySelectorAll('.visuallyhidden')
284
+ if (visuallyHidden.length) {
285
+ visuallyHidden[0].parentNode.removeChild(visuallyHidden[0])
300
286
  }
301
287
  }
302
288
  }
289
+ }
303
290
 
304
- function StepView ($stepElement) {
305
- var $titleLink = $stepElement.find('.js-step-title-button')
306
- var $stepContent = $stepElement.find('.js-panel')
291
+ Gemstepnav.prototype.setActiveStepClass = function () {
292
+ // remove the 'active/open' state from all steps
293
+ var allActiveSteps = this.$module.querySelectorAll('.' + this.$module.activeStepClass)
294
+ for (var i = 0; i < allActiveSteps.length; i++) {
295
+ allActiveSteps[i].classList.remove(this.$module.activeStepClass)
296
+ allActiveSteps[i].removeAttribute('data-show')
297
+ }
307
298
 
308
- this.title = $stepElement.find('.js-step-title-text').text().trim()
309
- this.element = $stepElement
299
+ // find the current page link and apply 'active/open' state to parent step
300
+ var activeLink = this.$module.querySelectorAll('.' + this.$module.activeLinkClass)[0]
301
+ if (activeLink) {
302
+ var activeStep = activeLink.closest('.gem-c-step-nav__step')
303
+ activeStep.classList.add(this.$module.activeStepClass)
304
+ activeStep.setAttribute('data-show', '')
305
+ }
306
+ }
310
307
 
311
- this.show = show
312
- this.hide = hide
313
- this.toggle = toggle
314
- this.setIsShown = setIsShown
315
- this.isShown = isShown
316
- this.isHidden = isHidden
317
- this.numberOfContentItems = numberOfContentItems
308
+ Gemstepnav.prototype.bindToggleShowHideAllButton = function (stepNavTracker) {
309
+ var that = this
318
310
 
319
- function show () {
320
- setIsShown(true)
321
- }
311
+ this.$module.showOrHideAllButton.addEventListener('click', function (event) {
312
+ var shouldshowAll
313
+ var showHideTexts = that.$module.querySelectorAll('.js-toggle-link')
314
+ var textContent = this.textContent || this.innerText
322
315
 
323
- function hide () {
324
- setIsShown(false)
325
- }
316
+ if (textContent === that.$module.actions.showAllText) {
317
+ that.$module.showOrHideAllButton.textContent = that.$module.actions.hideAllText
318
+ for (var i = 0; i < showHideTexts.length; i++) {
319
+ showHideTexts[i].innerHTML = that.$module.actions.hideText
320
+ }
321
+ shouldshowAll = true
326
322
 
327
- function toggle () {
328
- setIsShown(isHidden())
329
- }
323
+ stepNavTracker.trackClick('pageElementInteraction', 'stepNavAllShown', {
324
+ label: that.$module.actions.showAllText + ': ' + that.$module.stepNavSize
325
+ })
326
+ } else {
327
+ that.$module.showOrHideAllButton.textContent = that.$module.actions.showAllText
328
+ for (var j = 0; j < showHideTexts.length; j++) {
329
+ showHideTexts[j].innerHTML = that.$module.actions.showText
330
+ }
331
+ shouldshowAll = false
330
332
 
331
- function setIsShown (isShown) {
332
- $stepElement.toggleClass('step-is-shown', isShown)
333
- $stepContent.toggleClass('js-hidden', !isShown)
334
- $titleLink.attr('aria-expanded', isShown)
335
- $stepElement.find('.js-toggle-link').html(isShown ? actions.hideText : actions.showText)
333
+ stepNavTracker.trackClick('pageElementInteraction', 'stepNavAllHidden', {
334
+ label: that.$module.actions.hideAllText + ': ' + that.$module.stepNavSize
335
+ })
336
336
  }
337
337
 
338
- function isShown () {
339
- return $stepElement.hasClass('step-is-shown')
340
- }
338
+ that.setAllStepsShownState(shouldshowAll)
339
+ that.$module.showOrHideAllButton.setAttribute('aria-expanded', shouldshowAll)
340
+ that.setShowHideAllText()
341
341
 
342
- function isHidden () {
343
- return !isShown()
344
- }
342
+ return false
343
+ })
344
+ }
345
345
 
346
- function numberOfContentItems () {
347
- return $stepContent.find('.js-link').length
348
- }
346
+ Gemstepnav.prototype.setShowHideAllText = function () {
347
+ var shownSteps = this.$module.querySelectorAll('.step-is-shown').length
348
+ // Find out if the number of is-opens == total number of steps
349
+ if (shownSteps === this.$module.totalSteps) {
350
+ this.$module.showOrHideAllButton.textContent = this.$module.actions.hideAllText
351
+ } else {
352
+ this.$module.showOrHideAllButton.textContent = this.$module.actions.showAllText
349
353
  }
354
+ }
350
355
 
351
- function StepToggleClick (event, stepView, $steps, stepNavTracker, stepIsOptional) {
352
- this.track = trackClick
353
- var $target = $(event.target)
356
+ Gemstepnav.prototype.StepView = function (stepElement, showText, hideText) {
357
+ this.stepElement = stepElement
358
+ this.stepContent = this.stepElement.querySelectorAll('.js-panel')[0]
359
+ this.titleButton = this.stepElement.querySelectorAll('.js-step-title-button')[0]
360
+ var textElement = this.stepElement.querySelectorAll('.js-step-title-text')[0]
361
+ this.title = textElement.textContent || textElement.innerText
362
+ this.title = this.title.replace(/^\s+|\s+$/g, '') // this is 'trim' but supporting IE8
363
+ this.showText = showText
364
+ this.hideText = hideText
365
+
366
+ this.show = function () {
367
+ this.setIsShown(true)
368
+ }
354
369
 
355
- function trackClick () {
356
- var trackingOptions = { label: trackingLabel(), dimension28: stepView.numberOfContentItems().toString() }
357
- stepNavTracker.track('pageElementInteraction', trackingAction(), trackingOptions)
358
- }
370
+ this.hide = function () {
371
+ this.setIsShown(false)
372
+ }
359
373
 
360
- function trackingLabel () {
361
- return $target.closest('.js-toggle-panel').attr('data-position') + ' - ' + stepView.title + ' - ' + locateClickElement() + ': ' + stepNavSize + isOptional()
362
- }
374
+ this.toggle = function () {
375
+ this.setIsShown(this.isHidden())
376
+ }
363
377
 
364
- // returns index of the clicked step in the overall number of steps
365
- function stepIndex () { // eslint-disable-line no-unused-vars
366
- return $steps.index(stepView.element) + 1
378
+ this.setIsShown = function (isShown) {
379
+ if (isShown) {
380
+ this.stepElement.classList.add('step-is-shown')
381
+ this.stepContent.classList.remove('js-hidden')
382
+ } else {
383
+ this.stepElement.classList.remove('step-is-shown')
384
+ this.stepContent.classList.add('js-hidden')
367
385
  }
368
386
 
369
- function trackingAction () {
370
- return (stepView.isHidden() ? 'stepNavHidden' : 'stepNavShown')
371
- }
387
+ this.titleButton.setAttribute('aria-expanded', isShown)
388
+ var showHideText = this.stepElement.querySelectorAll('.js-toggle-link')[0]
389
+ showHideText.innerHTML = isShown ? this.hideText : this.showText
390
+ }
372
391
 
373
- function locateClickElement () {
374
- if (clickedOnIcon()) {
375
- return iconType() + ' click'
376
- } else if (clickedOnHeading()) {
377
- return 'Heading click'
378
- } else {
379
- return 'Elsewhere click'
380
- }
381
- }
392
+ this.isShown = function () {
393
+ return this.stepElement.classList.contains('step-is-shown')
394
+ }
382
395
 
383
- function clickedOnIcon () {
384
- return $target.hasClass('js-toggle-link')
385
- }
396
+ this.isHidden = function () {
397
+ return !this.isShown()
398
+ }
386
399
 
387
- function clickedOnHeading () {
388
- return $target.hasClass('js-step-title-text')
389
- }
400
+ this.numberOfContentItems = function () {
401
+ return this.stepContent.querySelectorAll('.js-link').length
402
+ }
403
+ }
390
404
 
391
- function iconType () {
392
- return (stepView.isHidden() ? 'Minus' : 'Plus')
393
- }
405
+ Gemstepnav.prototype.StepToggleClick = function (event, stepView, stepNavTracker, stepIsOptional, stepNavSize) {
406
+ this.target = event.target
407
+ this.stepIsOptional = stepIsOptional
408
+ this.stepNavSize = stepNavSize
409
+
410
+ this.trackClick = function () {
411
+ var trackingOptions = { label: this.trackingLabel(), dimension28: stepView.numberOfContentItems().toString() }
412
+ stepNavTracker.trackClick('pageElementInteraction', this.trackingAction(), trackingOptions)
413
+ }
414
+
415
+ this.trackingLabel = function () {
416
+ var clickedNearbyToggle = this.target.closest('.js-step').querySelectorAll('.js-toggle-panel')[0]
417
+ return clickedNearbyToggle.getAttribute('data-position') + ' - ' + stepView.title + ' - ' + this.locateClickElement() + ': ' + this.stepNavSize + this.isOptional()
418
+ }
419
+
420
+ // returns index of the clicked step in the overall number of steps
421
+ this.stepIndex = function () { // eslint-disable-line no-unused-vars
422
+ return this.$module.steps.index(stepView.element) + 1
423
+ }
424
+
425
+ this.trackingAction = function () {
426
+ return (stepView.isHidden() ? 'stepNavHidden' : 'stepNavShown')
427
+ }
394
428
 
395
- function isOptional () {
396
- return (stepIsOptional ? ' ; optional' : '')
429
+ this.locateClickElement = function () {
430
+ if (this.clickedOnIcon()) {
431
+ return this.iconType() + ' click'
432
+ } else if (this.clickedOnHeading()) {
433
+ return 'Heading click'
434
+ } else {
435
+ return 'Elsewhere click'
397
436
  }
398
437
  }
399
438
 
400
- function componentLinkClick (event, stepNavTracker, linkPosition) {
401
- this.track = trackClick
439
+ this.clickedOnIcon = function () {
440
+ return this.target.classList.contains('js-toggle-link')
441
+ }
402
442
 
403
- function trackClick () {
404
- var trackingOptions = { label: $(event.target).attr('href') + ' : ' + stepNavSize }
405
- var dimension28 = $(event.target).closest('.gem-c-step-nav__list').attr('data-length')
443
+ this.clickedOnHeading = function () {
444
+ return this.target.classList.contains('js-step-title-text')
445
+ }
406
446
 
407
- if (dimension28) {
408
- trackingOptions.dimension28 = dimension28
409
- }
447
+ this.iconType = function () {
448
+ return (stepView.isHidden() ? 'Minus' : 'Plus')
449
+ }
450
+
451
+ this.isOptional = function () {
452
+ return (this.stepIsOptional ? ' ; optional' : '')
453
+ }
454
+ }
455
+
456
+ Gemstepnav.prototype.ComponentLinkClick = function (event, stepNavTracker, linkPosition, size) {
457
+ this.size = size
458
+ this.target = event.target
410
459
 
411
- stepNavTracker.track('stepNavLinkClicked', linkPosition, trackingOptions)
460
+ this.trackClick = function () {
461
+ var trackingOptions = { label: this.target.getAttribute('href') + ' : ' + this.size }
462
+ var dimension28 = this.target.closest('.gem-c-step-nav__list').getAttribute('data-length')
463
+
464
+ if (dimension28) {
465
+ trackingOptions.dimension28 = dimension28
412
466
  }
467
+
468
+ stepNavTracker.trackClick('stepNavLinkClicked', linkPosition, trackingOptions)
413
469
  }
470
+ }
414
471
 
415
- // A helper that sends a custom event request to Google Analytics if
416
- // the GOVUK module is setup
417
- function StepNavTracker (totalSteps, totalLinks, uniqueId) {
418
- this.track = function (category, action, options) {
419
- // dimension26 records the total number of expand/collapse steps in this step nav
420
- // dimension27 records the total number of links in this step nav
421
- // dimension28 records the number of links in the step that was shown/hidden (handled in click event)
422
- if (window.GOVUK.analytics && window.GOVUK.analytics.trackEvent) {
423
- options = options || {}
424
- options.dimension26 = options.dimension26 || totalSteps.toString()
425
- options.dimension27 = options.dimension27 || totalLinks.toString()
426
- options.dimension96 = options.dimension96 || uniqueId
427
- window.GOVUK.analytics.trackEvent(category, action, options)
428
- }
472
+ // A helper that sends a custom event request to Google Analytics if
473
+ // the GOVUK module is setup
474
+ Gemstepnav.prototype.StepNavTracker = function (uniqueId, totalSteps, totalLinks) {
475
+ this.totalSteps = totalSteps
476
+ this.totalLinks = totalLinks
477
+ this.uniqueId = uniqueId
478
+
479
+ this.trackClick = function (category, action, options) {
480
+ // dimension26 records the total number of expand/collapse steps in this step nav
481
+ // dimension27 records the total number of links in this step nav
482
+ // dimension28 records the number of links in the step that was shown/hidden (handled in click event)
483
+ if (window.GOVUK.analytics && window.GOVUK.analytics.trackEvent) {
484
+ options = options || {}
485
+ options.dimension26 = options.dimension26 || this.totalSteps.toString()
486
+ options.dimension27 = options.dimension27 || this.totalLinks.toString()
487
+ options.dimension96 = options.dimension96 || this.uniqueId
488
+ window.GOVUK.analytics.trackEvent(category, action, options)
429
489
  }
430
490
  }
431
491
  }
492
+
493
+ Modules.Gemstepnav = Gemstepnav
432
494
  })(window.GOVUK.Modules)