govuk_publishing_components 35.11.0 → 35.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +2 -48
  3. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-form-tracker.js +5 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +2 -2
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-schemas.js +51 -5
  6. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-scroll-tracker.js +225 -0
  7. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/init-ga4.js +0 -5
  8. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
  9. data/app/views/govuk_publishing_components/components/_attachment.html.erb +3 -1
  10. data/app/views/govuk_publishing_components/components/_tabs.html.erb +30 -14
  11. data/app/views/govuk_publishing_components/components/docs/tabs.yml +25 -2
  12. data/lib/govuk_publishing_components/version.rb +1 -1
  13. data/node_modules/govuk-frontend/govuk/all.js +406 -1
  14. data/node_modules/govuk-frontend/govuk/all.js.map +1 -1
  15. data/node_modules/govuk-frontend/govuk/common/govuk-frontend-version.js +1 -1
  16. data/node_modules/govuk-frontend/govuk/components/_all.scss +2 -1
  17. data/node_modules/govuk-frontend/govuk/components/back-link/_index.scss +8 -0
  18. data/node_modules/govuk-frontend/govuk/components/back-link/fixtures.json +9 -0
  19. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/_index.scss +12 -0
  20. data/node_modules/govuk-frontend/govuk/components/breadcrumbs/fixtures.json +21 -0
  21. data/node_modules/govuk-frontend/govuk/components/button/_index.scss +41 -3
  22. data/node_modules/govuk-frontend/govuk/components/button/fixtures.json +44 -0
  23. data/node_modules/govuk-frontend/govuk/components/checkboxes/macro-options.json +9 -8
  24. data/node_modules/govuk-frontend/govuk/components/exit-this-page/README.md +15 -0
  25. data/node_modules/govuk-frontend/govuk/components/exit-this-page/_exit-this-page.scss +2 -0
  26. data/node_modules/govuk-frontend/govuk/components/exit-this-page/_index.scss +97 -0
  27. data/node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js +2120 -0
  28. data/node_modules/govuk-frontend/govuk/components/exit-this-page/exit-this-page.js.map +1 -0
  29. data/node_modules/govuk-frontend/govuk/components/exit-this-page/fixtures.json +50 -0
  30. data/node_modules/govuk-frontend/govuk/components/exit-this-page/macro-options.json +62 -0
  31. data/node_modules/govuk-frontend/govuk/components/exit-this-page/macro.njk +3 -0
  32. data/node_modules/govuk-frontend/govuk/components/exit-this-page/template.njk +16 -0
  33. data/node_modules/govuk-frontend/govuk/components/radios/macro-options.json +9 -8
  34. data/node_modules/govuk-frontend/govuk/core/_govuk-frontend-version.scss +1 -1
  35. data/node_modules/govuk-frontend/govuk/helpers/_visually-hidden.scss +12 -0
  36. data/node_modules/govuk-frontend/govuk/objects/_template.scss +20 -0
  37. data/node_modules/govuk-frontend/govuk-esm/all.mjs +8 -0
  38. data/node_modules/govuk-frontend/govuk-esm/all.mjs.map +1 -1
  39. data/node_modules/govuk-frontend/govuk-esm/common/govuk-frontend-version.mjs +1 -1
  40. data/node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs +406 -0
  41. data/node_modules/govuk-frontend/govuk-esm/components/exit-this-page/exit-this-page.mjs.map +1 -0
  42. data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +4 -0
  43. data/node_modules/govuk-frontend/package.json +4 -2
  44. metadata +14 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc2a2a38c6182ba05ad0863d7f1b4d47bda4c31726559b42d3e9095fc7d984a7
4
- data.tar.gz: 5bfaa98a5688a6e4d6c7f37a3f1b9099bbdb96e051dd83371da64f67040a723a
3
+ metadata.gz: d923b79189269417363dd2c12781e9ee9644a03bf2c514b21e24d71efe0c07c7
4
+ data.tar.gz: ce1f69cdb8ef0eaf3fcf836e359311e8b38a0f401bb931a3e8bfc27c1b568f56
5
5
  SHA512:
6
- metadata.gz: cee017c867881cb78d2c39357c94a6400d4abf6bd725cd8a2449c44044460ab260d6bd7fab87c6246e4ca8340417e04c1855108ad32329bca72d1e846801aaa1
7
- data.tar.gz: 917e293b5ae5ae0b228f7b66bbae838913213fb8fe4eccf82d92a0d2ae87344b85328e9f2f83296c9de13d22a24c72829e60158c64366dddb8b8d2d4e0332dea
6
+ metadata.gz: bffc7c115facd8c323182b04f089ecae6b5b2a6991ebed42aea17fc748b1a95bcbf1093f75e238291819bb9d821152b7f4ef3f2b95f0a4b2b1b14021ccbb5a14
7
+ data.tar.gz: 03c818b7b4f9731f3075020ad5273256a30c3cdef0862f92892edea1dd6cd19e0e65ea781d8c9bb7fb2c87403c5478503442453c68ff80a2deea73ba820e3721
@@ -30,32 +30,7 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
30
30
  firstScript.parentNode.insertBefore(newScript, firstScript)
31
31
  },
32
32
 
33
- ensureIndexesArePopulated: function (data) {
34
- if (!data.event_data) {
35
- return data
36
- }
37
-
38
- if (!data.event_data.index) {
39
- return data
40
- }
41
-
42
- var indexKeys = ['index_link', 'index_section', 'index_section_count']
43
-
44
- for (var i = 0; i < indexKeys.length; i++) {
45
- var indexKey = indexKeys[i]
46
-
47
- // If the index key isn't in the object, populate it. However if it's set to 0, leave it as 0. 0 is falsy so we have to add this extra check.
48
- if (!data.event_data.index[indexKey] && data.event_data.index[indexKey] !== 0) {
49
- data.event_data.index[indexKey] = undefined
50
- }
51
- }
52
-
53
- return data
54
- },
55
-
56
33
  sendData: function (data) {
57
- data = this.ensureIndexesArePopulated(data)
58
-
59
34
  data.govuk_gem_version = this.getGemVersion()
60
35
  // set this in the console as a debugging aid
61
36
  if (window.GOVUK.analyticsGa4.showDebug) {
@@ -274,29 +249,8 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
274
249
  }
275
250
  },
276
251
 
277
- addAttributesToElements: function (selector, dataAttributes) {
278
- var targetElements = document.querySelectorAll(selector)
279
-
280
- for (var i = 0; i < targetElements.length; i++) {
281
- var element = targetElements[i]
282
-
283
- for (var j = 0; j < dataAttributes.length; j++) {
284
- var key = dataAttributes[j].key
285
- var value = dataAttributes[j].value
286
-
287
- // value must check for undefined only as it would return false on an empty string, the number 0, etc.
288
- if (key && value !== undefined) {
289
- var existingAttributeValue = element.getAttribute(key)
290
-
291
- if (key === 'data-module' && existingAttributeValue) {
292
- // Combines values to prevent replacing any existing data-module values.
293
- element.setAttribute(key, existingAttributeValue + ' ' + value)
294
- } else {
295
- element.setAttribute(key, value)
296
- }
297
- }
298
- }
299
- }
252
+ applyRedactionIfRequired: function (PIIRemover, element, data) {
253
+ return element.closest('[data-ga4-do-not-redact]') ? data : PIIRemover.stripPIIWithOverride(data, true, true)
300
254
  }
301
255
  },
302
256
 
@@ -45,6 +45,11 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
45
45
  var formData = this.getInputValues(formInputs)
46
46
  data.text = data.text || (this.combineGivenAnswers(formData) || 'No answer given')
47
47
 
48
+ if (data.action === 'search') {
49
+ data.text = data.text.toLowerCase()
50
+ data.text = window.GOVUK.analyticsGa4.core.trackFunctions.removeLinesAndExtraSpaces(data.text)
51
+ }
52
+
48
53
  var schemas = new window.GOVUK.analyticsGa4.Schemas()
49
54
  var schema = schemas.mergeProperties(data, 'event_data')
50
55
  window.GOVUK.analyticsGa4.core.sendData(schema)
@@ -81,12 +81,12 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
81
81
 
82
82
  var text = data.text || event.target.textContent
83
83
  data.text = window.GOVUK.analyticsGa4.core.trackFunctions.removeLinesAndExtraSpaces(text)
84
- data.text = this.PIIRemover.stripPIIWithOverride(data.text, true, true)
84
+ data.text = window.GOVUK.analyticsGa4.core.trackFunctions.applyRedactionIfRequired(this.PIIRemover, element, data.text)
85
85
  if (!data.text && (element.querySelector('img') || element.querySelector('svg') || element.tagName === 'IMG' || element.closest('svg'))) {
86
86
  data.text = 'image'
87
87
  }
88
88
  var url = data.url || this.findLink(event.target).getAttribute('href')
89
- data.url = window.GOVUK.analyticsGa4.core.trackFunctions.removeCrossDomainParams(this.PIIRemover.stripPIIWithOverride(url, true, true))
89
+ data.url = window.GOVUK.analyticsGa4.core.trackFunctions.applyRedactionIfRequired(this.PIIRemover, element, window.GOVUK.analyticsGa4.core.trackFunctions.removeCrossDomainParams(url))
90
90
  data.link_domain = window.GOVUK.analyticsGa4.core.trackFunctions.populateLinkDomain(data.url)
91
91
  data.link_path_parts = window.GOVUK.analyticsGa4.core.trackFunctions.populateLinkPathParts(data.url)
92
92
  data.method = window.GOVUK.analyticsGa4.core.trackFunctions.getClickType(event)
@@ -16,7 +16,11 @@
16
16
  type: this.undefined,
17
17
  url: this.undefined,
18
18
  text: this.undefined,
19
- index: this.undefined,
19
+ index: {
20
+ index_link: this.undefined,
21
+ index_section: this.undefined,
22
+ index_section_count: this.undefined
23
+ },
20
24
  index_total: this.undefined,
21
25
  section: this.undefined,
22
26
  action: this.undefined,
@@ -24,7 +28,8 @@
24
28
  method: this.undefined,
25
29
  link_domain: this.undefined,
26
30
  link_path_parts: this.undefined,
27
- tool_name: this.undefined
31
+ tool_name: this.undefined,
32
+ percent_scrolled: this.undefined
28
33
  }
29
34
  }
30
35
  }
@@ -45,19 +50,60 @@
45
50
  }
46
51
  }
47
52
 
48
- // get attributes from the data attribute to send to GA
53
+ // merge data attributes data into the event schema
49
54
  // only allow it if it already exists in the schema
50
55
  Schemas.prototype.mergeProperties = function (data, eventAttribute) {
51
56
  var schema = this.eventSchema()
52
57
  schema.event = eventAttribute
58
+ // exceptions should be inserted without checking for sub parameters
59
+ // exceptions should only come from code, not directly from data attributes
60
+ // e.g. link_path_parts is generated by JS and contains potentially non-unique
61
+ // sub parameter names i.e. '1', '2'
62
+ var exceptions = ['link_path_parts']
63
+
53
64
  for (var property in data) {
54
- if (property in schema.event_data) {
55
- schema.event_data[property] = data[property]
65
+ // some passed data might be undefined, don't want it to overwrite e.g. the index sub parameters
66
+ if (data[property] !== undefined) {
67
+ if (exceptions.indexOf(property) >= 0 || !this.isAnObject(data[property])) {
68
+ schema.event_data = this.addToObject(schema.event_data, property, data[property])
69
+ } else {
70
+ // we check for one level of nesting in the data attributes data
71
+ // this check can be removed once nesting is removed from all data attributes
72
+ for (var subproperty in data[property]) {
73
+ schema.event_data = this.addToObject(schema.event_data, subproperty, data[property][subproperty])
74
+ }
75
+ }
56
76
  }
57
77
  }
58
78
  return schema
59
79
  }
60
80
 
81
+ // might be easier to check if it's not a string or a number?
82
+ Schemas.prototype.isAnObject = function (item) {
83
+ if (typeof item === 'object' && !Array.isArray(item) && item !== null) {
84
+ return true
85
+ }
86
+ }
87
+
88
+ // given an object and a key, insert a value into that object for that key
89
+ // we check for one level of nesting in the object
90
+ Schemas.prototype.addToObject = function (obj, key, value) {
91
+ if (key in obj) {
92
+ obj[key] = value
93
+ return obj
94
+ } else {
95
+ for (var property in obj) {
96
+ if (this.isAnObject(obj[property])) {
97
+ if (key in obj[property]) {
98
+ obj[property][key] = value
99
+ return obj
100
+ }
101
+ }
102
+ }
103
+ }
104
+ return obj
105
+ }
106
+
61
107
  GOVUK.analyticsGa4 = GOVUK.analyticsGa4 || {}
62
108
  GOVUK.analyticsGa4.Schemas = Schemas
63
109
 
@@ -0,0 +1,225 @@
1
+ window.GOVUK = window.GOVUK || {}
2
+ window.GOVUK.Modules = window.GOVUK.Modules || {};
3
+
4
+ (function (Modules) {
5
+ function Ga4ScrollTracker ($module) {
6
+ this.$module = $module
7
+ this.pageHeight = document.querySelector('body').clientHeight
8
+ this.trackedNodes = []
9
+ this.config = {
10
+ allowHeadingsInside: ['main'],
11
+ percentages: [20, 40, 60, 80, 100],
12
+ scrollTimeoutDelay: 20,
13
+ resizeTimeoutDelay: 100,
14
+ pageHeightTimeoutDelay: 500,
15
+ markerAttribute: 'data-ga4-scroll-marker'
16
+ }
17
+ }
18
+
19
+ Ga4ScrollTracker.prototype.init = function () {
20
+ var consentCookie = window.GOVUK.getConsentCookie()
21
+
22
+ if (consentCookie && consentCookie.settings) {
23
+ this.startModule()
24
+ } else {
25
+ this.startModule = this.startModule.bind(this)
26
+ window.addEventListener('cookie-consent', this.startModule)
27
+ }
28
+ }
29
+
30
+ Ga4ScrollTracker.prototype.startModule = function () {
31
+ if (window.GOVUK.analyticsGa4.vars.scrollTrackerStarted) {
32
+ return
33
+ }
34
+
35
+ this.trackType = this.$module.getAttribute('data-ga4-track-type')
36
+ window.GOVUK.analyticsGa4.vars.scrollTrackerStarted = true
37
+
38
+ if (this.trackType === 'headings') {
39
+ this.track = new Ga4ScrollTracker.Heading(this.config)
40
+ } else if (this.trackType === 'markers') {
41
+ this.config.trackMarkers = true
42
+ this.track = new Ga4ScrollTracker.Heading(this.config)
43
+ } else {
44
+ this.track = new Ga4ScrollTracker.Percentage(this.config)
45
+ }
46
+
47
+ this.getWindowDetails()
48
+ // if the URL has a hash we want to prevent tracking on initial page load
49
+ // until the browser jumps down the page, at which point a scroll event
50
+ // will happen and tracking will continue normally
51
+ var windowHash = window.location.hash
52
+ var dontTrackOnLoad = windowHash && document.getElementById(windowHash.substring(1))
53
+ if (!dontTrackOnLoad) {
54
+ this.trackVisibleNodes()
55
+ }
56
+
57
+ if (this.trackedNodes.length) {
58
+ // store event listener functions as variables so they can be removed if needed
59
+ this.scrollEvent = this.onScroll.bind(this)
60
+ window.addEventListener('scroll', this.scrollEvent)
61
+ this.resizeEvent = this.onResize.bind(this)
62
+ window.addEventListener('resize', this.resizeEvent)
63
+
64
+ // check if the page height changes e.g. accordion opened
65
+ this.interval = window.setInterval(function () {
66
+ var pageHeight = document.querySelector('body').clientHeight
67
+ if (pageHeight !== this.pageHeight) {
68
+ this.pageHeight = pageHeight
69
+ this.getWindowDetails()
70
+ this.trackVisibleNodes()
71
+ }
72
+ }.bind(this), this.config.pageHeightTimeoutDelay)
73
+ }
74
+ }
75
+
76
+ Ga4ScrollTracker.prototype.onScroll = function () {
77
+ clearTimeout(this.scrollTimeout)
78
+ this.scrollTimeout = setTimeout(function () {
79
+ this.trackVisibleNodes()
80
+ }.bind(this), this.config.scrollTimeoutDelay)
81
+ }
82
+
83
+ Ga4ScrollTracker.prototype.onResize = function () {
84
+ clearTimeout(this.resizeTimeout)
85
+ this.resizeTimeout = setTimeout(function () {
86
+ this.getWindowDetails()
87
+ this.trackVisibleNodes()
88
+ }.bind(this), this.config.resizeTimeoutDelay)
89
+ }
90
+
91
+ Ga4ScrollTracker.prototype.getWindowDetails = function () {
92
+ this.pageHeight = document.querySelector('body').clientHeight
93
+ this.windowHeight = window.innerHeight
94
+ this.trackedNodes = this.track.getTrackingNodes(this.trackedNodes)
95
+ }
96
+
97
+ Ga4ScrollTracker.prototype.trackVisibleNodes = function () {
98
+ var data = {
99
+ event_name: 'scroll',
100
+ action: 'scroll',
101
+ type: this.config.type
102
+ }
103
+ for (var i = 0; i < this.trackedNodes.length; i++) {
104
+ var node = this.trackedNodes[i]
105
+ if (this.isVisible(node.top, node.bottom) && !node.alreadySeen) {
106
+ node.alreadySeen = true
107
+ // we store whether a heading has been tracked or not on the heading
108
+ // because if headings appear/disappear (e.g. inside an accordion)
109
+ // the order changes, so we can't refer to the previous trackedNodes
110
+ // as we do with percentages
111
+ if (node.element) {
112
+ node.element.setAttribute('data-ga4-scrolltracker-already-seen', true)
113
+ }
114
+
115
+ data.type = node.eventData.type
116
+ // following will be undefined if tracking percentages
117
+ data.text = node.eventData.text
118
+ data.index = node.eventData.index
119
+ // following will be undefined if tracking headings
120
+ data.percent_scrolled = node.eventData.percent_scrolled
121
+
122
+ var schemas = new window.GOVUK.analyticsGa4.Schemas()
123
+ var schema = schemas.mergeProperties(data, 'event_data')
124
+ window.GOVUK.analyticsGa4.core.sendData(schema)
125
+ }
126
+ }
127
+ }
128
+
129
+ Ga4ScrollTracker.prototype.isVisible = function (top, bottom) {
130
+ var scroll = window.scrollY || document.documentElement.scrollTop // IE fallback
131
+ return scroll <= top && (scroll + this.windowHeight) >= bottom
132
+ }
133
+
134
+ Ga4ScrollTracker.Heading = function (config) {
135
+ this.config = config
136
+ }
137
+
138
+ Ga4ScrollTracker.Heading.prototype.getTrackingNodes = function () {
139
+ var headingsDetails = []
140
+ var headingsFound = this.findAllowedHeadings()
141
+ var totalHeadings = headingsFound.length
142
+
143
+ for (var i = 0; i < totalHeadings; i++) {
144
+ var heading = headingsFound[i]
145
+ var type = this.config.trackMarkers ? 'marker' : 'heading'
146
+ // only track headings that are visible i.e. not inside display: none
147
+ if (this.visible(heading)) {
148
+ var pos = heading.getBoundingClientRect()
149
+ headingsDetails.push({
150
+ element: heading,
151
+ alreadySeen: heading.getAttribute('data-ga4-scrolltracker-already-seen'),
152
+ top: pos.top + document.documentElement.scrollTop,
153
+ bottom: pos.bottom + document.documentElement.scrollTop,
154
+ eventData: {
155
+ type: type,
156
+ text: heading.textContent.replace(/\s+/g, ' ').trim(),
157
+ index: {
158
+ index_section: i + 1,
159
+ index_section_count: totalHeadings
160
+ }
161
+ }
162
+ })
163
+ }
164
+ }
165
+ return headingsDetails
166
+ }
167
+
168
+ // check heading is inside allowed elements, generally ignores everything outside of page content
169
+ Ga4ScrollTracker.Heading.prototype.findAllowedHeadings = function () {
170
+ var headingsFound = []
171
+ var headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
172
+ if (this.config.trackMarkers) {
173
+ headings = ['[' + this.config.markerAttribute + ']']
174
+ }
175
+
176
+ // this is a loop that only happens once as we currently only have one
177
+ // allowed element for headings to be in - 'main'
178
+ for (var h = 0; h < this.config.allowHeadingsInside.length; h++) {
179
+ var insideElements = document.querySelectorAll(this.config.allowHeadingsInside[h])
180
+ for (var e = 0; e < insideElements.length; e++) {
181
+ var found = insideElements[e].querySelectorAll(headings)
182
+ for (var f = 0; f < found.length; f++) {
183
+ headingsFound.push(found[f])
184
+ }
185
+ }
186
+ }
187
+ return headingsFound
188
+ }
189
+
190
+ // this is bit more verbose than checking offsetParent !== null but more reliable for IE10+
191
+ Ga4ScrollTracker.Heading.prototype.visible = function (el) {
192
+ return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length)
193
+ }
194
+
195
+ Ga4ScrollTracker.Percentage = function (config) {
196
+ this.config = config
197
+ }
198
+
199
+ Ga4ScrollTracker.Percentage.prototype.getTrackingNodes = function (trackedNodes) {
200
+ var body = document.body
201
+ var html = document.documentElement
202
+ var pageHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)
203
+
204
+ var percentDetails = []
205
+
206
+ for (var i = 0; i < this.config.percentages.length; i++) {
207
+ var percent = this.config.percentages[i]
208
+ // subtract 1 pixel to solve a bug where 100% can't be reached in some cases
209
+ var pos = ((pageHeight / 100) * percent) - 1
210
+ var alreadySeen = false
211
+ if (trackedNodes.length) {
212
+ alreadySeen = trackedNodes[i].alreadySeen
213
+ }
214
+ percentDetails.push({
215
+ alreadySeen: alreadySeen,
216
+ top: pos,
217
+ bottom: pos,
218
+ eventData: { type: 'percent', percent_scrolled: String(percent) }
219
+ })
220
+ }
221
+ return percentDetails
222
+ }
223
+
224
+ Modules.Ga4ScrollTracker = Ga4ScrollTracker
225
+ })(window.GOVUK.Modules)
@@ -8,11 +8,6 @@ var initFunction = function () {
8
8
  window.GOVUK.analyticsGa4.vars.internalDomains = []
9
9
  window.GOVUK.analyticsGa4.vars.internalDomains.push(window.GOVUK.analyticsGa4.core.trackFunctions.getHostname())
10
10
  window.GOVUK.analyticsGa4.core.trackFunctions.appendDomainsWithoutWWW(window.GOVUK.analyticsGa4.vars.internalDomains)
11
- var attachmentLinkData = [
12
- { key: 'data-module', value: 'ga4-link-tracker' },
13
- { key: 'data-ga4-track-links-only', value: '' },
14
- { key: 'data-ga4-link', value: JSON.stringify({ event_name: 'navigation', type: 'attachment' }) }]
15
- window.GOVUK.analyticsGa4.core.trackFunctions.addAttributesToElements('[data-ga4-attachment-link]', attachmentLinkData)
16
11
  window.GOVUK.analyticsGa4.core.load()
17
12
 
18
13
  var analyticsModules = window.GOVUK.analyticsGa4.analyticsModules
@@ -10,4 +10,5 @@
10
10
  //= require ./analytics-ga4/ga4-form-tracker
11
11
  //= require ./analytics-ga4/ga4-auto-tracker
12
12
  //= require ./analytics-ga4/ga4-smart-answer-results-tracker
13
+ //= require ./analytics-ga4/ga4-scroll-tracker
13
14
  //= require ./analytics-ga4/init-ga4
@@ -43,13 +43,15 @@
43
43
  "HTML",
44
44
  class: "gem-c-attachment__attribute",
45
45
  )
46
+ data_attributes[:module] ? data_attributes[:module] << " ga4-link-tracker" : data_attributes[:module] = "ga4-link-tracker"
47
+ data_attributes[:ga4_link] = { "event_name": "navigation", "type": "attachment" }.to_json
46
48
  when "external"
47
49
  attributes << tag.span(
48
50
  attachment.url,
49
51
  class: "gem-c-attachment__attribute",
50
52
  )
51
53
  end
52
-
54
+
53
55
  %>
54
56
  <%= tag.section class: class_names(container_class_names) do %>
55
57
  <%= tag.div class: "gem-c-attachment__thumbnail" do %>
@@ -7,22 +7,30 @@
7
7
  panel_css_classes << "gem-c-tabs__panel--no-border" if panel_border == false
8
8
  panel_css_classes = panel_css_classes.join(" ")
9
9
 
10
+ as_links ||= false
10
11
  ga4_tracking ||= false
11
- data_module = "govuk-tabs"
12
- data_module << " ga4-event-tracker" if ga4_tracking
12
+
13
+ component_helper = GovukPublishingComponents::Presenters::ComponentWrapperHelper.new(local_assigns)
14
+ component_helper.add_class("govuk-tabs gem-c-tabs")
15
+ component_helper.add_data_attribute({ module: "govuk-tabs" }) unless as_links
16
+
17
+ if ga4_tracking
18
+ component_helper.add_data_attribute({ module: "ga4-event-tracker" }) unless as_links
19
+ component_helper.add_data_attribute({ module: "ga4-link-tracker" }) if as_links
20
+ end
13
21
  %>
14
22
  <% if tabs.count > 1 %>
15
- <div class="govuk-tabs gem-c-tabs" data-module="<%= data_module %>">
23
+ <%= tag.div(**component_helper.all_attributes) do %>
16
24
  <h2 class="govuk-tabs__title">
17
25
  <%= t("components.tabs.contents") %>
18
26
  </h2>
19
27
  <ul class="govuk-tabs__list">
20
28
  <% tabs.each_with_index do |tab, index| %>
21
- <li class="govuk-tabs__list-item">
29
+ <li class="govuk-tabs__list-item <%= "govuk-tabs__list-item--selected" if tab[:active] %>">
22
30
  <%
23
31
  tab[:tab_data_attributes] ||= {}
24
32
  if ga4_tracking
25
- tab[:tab_data_attributes][:ga4_event] = {
33
+ ga4_attributes = {
26
34
  event_name: "select_content",
27
35
  type: "tabs",
28
36
  text: tab[:label],
@@ -31,24 +39,32 @@
31
39
  index_section_count: tabs.length,
32
40
  },
33
41
  }
42
+ ga4_attributes[:event_name] = "navigation" if as_links
43
+ tab[:tab_data_attributes][:ga4_link] = ga4_attributes if as_links
44
+ tab[:tab_data_attributes][:ga4_event] = ga4_attributes unless as_links
34
45
  end
46
+
47
+ tab_link = "##{tab[:id]}"
48
+ tab_link = tab[:href] if as_links
35
49
  %>
36
50
  <%= link_to(tab[:label],
37
- "##{tab[:id]}",
51
+ tab_link,
38
52
  class: "govuk-tabs__tab",
39
53
  data: tab[:tab_data_attributes]) %>
40
54
  </li>
41
55
  <% end %>
42
56
  </ul>
43
- <% tabs.each do |tab| %>
44
- <section class="<%= panel_css_classes %>" id="<%= tab[:id] %>">
45
- <% if tab[:title] %>
46
- <h2 class="govuk-heading-l"><%= tab[:title] %></h2>
47
- <% end %>
48
- <%= tab[:content] %>
49
- </section>
57
+ <% unless as_links %>
58
+ <% tabs.each do |tab| %>
59
+ <section class="<%= panel_css_classes %>" id="<%= tab[:id] %>">
60
+ <% if tab[:title] %>
61
+ <h2 class="govuk-heading-l"><%= tab[:title] %></h2>
62
+ <% end %>
63
+ <%= tab[:content] %>
64
+ </section>
65
+ <% end %>
50
66
  <% end %>
51
- </div>
67
+ <% end %>
52
68
  <% end %>
53
69
  <% if tabs.count == 1 %>
54
70
  <section id="<%= tabs[0][:id] %>">
@@ -30,6 +30,17 @@ examples:
30
30
  label: "Second section"
31
31
  content: |
32
32
  <p class="govuk-body-m">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam congue elementum commodo. Vestibulum elit turpis, efficitur quis posuere vitae, commodo vitae augue. Donec ut pharetra ligula. Phasellus ac mauris eu felis bibendum dapibus rutrum sed quam. Pellentesque posuere ante id consequat pretium.</p>
33
+ as_links:
34
+ description: With this option the tabs operate as links, rather than switching between elements within a single page.
35
+ data:
36
+ as_links: true
37
+ tabs:
38
+ - href: "link1"
39
+ label: "Page one"
40
+ active: true
41
+ - href: "link2"
42
+ label: "Page two"
43
+ active: false
33
44
  without_panel_border:
34
45
  data:
35
46
  panel_border: false
@@ -84,8 +95,8 @@ examples:
84
95
  tracking: GTM-123AB
85
96
  content: |
86
97
  <p class="govuk-body-m">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam congue elementum commodo. Vestibulum elit turpis, efficitur quis posuere vitae, commodo vitae augue. Donec ut pharetra ligula. Phasellus ac mauris eu felis bibendum dapibus rutrum sed quam. Pellentesque posuere ante id consequat pretium.</p>
87
- with_ga4_tracking:
88
- description: Enables GA4 tracking. This will add the required data module and data attributes to the tabs. See the [ga4-event-tracker documentation](https://github.com/alphagov/govuk_publishing_components/blob/main/docs/analytics-ga4/ga4-event-tracker.md) for more information.
98
+ with_ga4_tracking_on_tabs:
99
+ description: Enables GA4 tracking by adding the event tracker and required data attributes to the tabs. See the [ga4-event-tracker documentation](https://github.com/alphagov/govuk_publishing_components/blob/main/docs/analytics-ga4/ga4-event-tracker.md) for more information.
89
100
  data:
90
101
  ga4_tracking: true
91
102
  tabs:
@@ -99,3 +110,15 @@ examples:
99
110
  title: "Second section"
100
111
  content: |
101
112
  <p class="govuk-body-m">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam congue elementum commodo. Vestibulum elit turpis, efficitur quis posuere vitae, commodo vitae augue. Donec ut pharetra ligula. Phasellus ac mauris eu felis bibendum dapibus rutrum sed quam. Pellentesque posuere ante id consequat pretium.</p>
113
+ with_ga4_tracking_on_tabs_as_links:
114
+ description: Enables GA4 tracking by adding the link tracker and required data attributes to the tabs. See the [ga4-link-tracker documentation](https://github.com/alphagov/govuk_publishing_components/blob/main/docs/analytics-ga4/ga4-link-tracker.md) for more information.
115
+ data:
116
+ as_links: true
117
+ ga4_tracking: true
118
+ tabs:
119
+ - href: "/page1"
120
+ label: "Link 1"
121
+ active: true
122
+ - href: "/page2"
123
+ label: "Link 2"
124
+ active: false
@@ -1,3 +1,3 @@
1
1
  module GovukPublishingComponents
2
- VERSION = "35.11.0".freeze
2
+ VERSION = "35.12.0".freeze
3
3
  end