govuk_publishing_components 29.6.0 → 29.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/analytics/explicit-cross-domain-links.js +72 -73
  3. data/app/assets/javascripts/govuk_publishing_components/analytics/page-content.js +11 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/gtm-click-tracking.js +49 -0
  5. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
  6. data/app/assets/javascripts/govuk_publishing_components/components/accordion.js +0 -1
  7. data/app/assets/javascripts/govuk_publishing_components/lib/govspeak/magna-charta.js +20 -6
  8. data/app/assets/javascripts/govuk_publishing_components/modules.js +0 -1
  9. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-measurer.js +37 -0
  10. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +166 -142
  11. data/app/assets/stylesheets/govuk_publishing_components/components/_cards.scss +11 -4
  12. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +1 -1
  13. data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_attachment.scss +1 -1
  14. data/app/controllers/govuk_publishing_components/audit_controller.rb +1 -4
  15. data/app/models/govuk_publishing_components/audit_components.rb +61 -24
  16. data/app/views/govuk_publishing_components/audit/_applications.html.erb +8 -3
  17. data/app/views/govuk_publishing_components/audit/_component_contents.html.erb +82 -0
  18. data/app/views/govuk_publishing_components/audit/_components.html.erb +2 -47
  19. data/app/views/govuk_publishing_components/component_guide/index.html.erb +2 -2
  20. data/app/views/govuk_publishing_components/component_guide/show.html.erb +1 -1
  21. data/app/views/govuk_publishing_components/components/_attachment.html.erb +1 -1
  22. data/app/views/govuk_publishing_components/components/_attachment_link.html.erb +1 -1
  23. data/app/views/govuk_publishing_components/components/_cards.html.erb +13 -11
  24. data/app/views/govuk_publishing_components/components/_summary_list.html.erb +4 -5
  25. data/app/views/govuk_publishing_components/components/docs/contents_list.yml +1 -1
  26. data/app/views/govuk_publishing_components/components/docs/document_list.yml +4 -4
  27. data/app/views/govuk_publishing_components/components/docs/heading.yml +1 -1
  28. data/app/views/govuk_publishing_components/components/docs/image_card.yml +1 -1
  29. data/app/views/govuk_publishing_components/components/docs/meta_tags.yml +4 -4
  30. data/app/views/govuk_publishing_components/components/docs/metadata.yml +1 -1
  31. data/app/views/govuk_publishing_components/components/docs/share_links.yml +1 -1
  32. data/app/views/govuk_publishing_components/components/docs/subscription_links.yml +1 -1
  33. data/app/views/govuk_publishing_components/components/docs/translation_nav.yml +1 -2
  34. data/config/locales/en.yml +0 -2
  35. data/lib/generators/govuk_publishing_components/templates/_component.html.erb +1 -1
  36. data/lib/govuk_publishing_components/app_helpers/brand_helper.rb +1 -1
  37. data/lib/govuk_publishing_components/presenters/{attachment.rb → attachment_helper.rb} +1 -2
  38. data/lib/govuk_publishing_components/version.rb +1 -1
  39. data/lib/govuk_publishing_components.rb +1 -1
  40. metadata +7 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e3f8aaf064237fbd67e4b472e5e57dcafeb3a16ed3228c7b0b45114d7f68257
4
- data.tar.gz: dc5d297b2e7d409277024778eabc48a9faf2a8b68bff7f309426d028b4736f2b
3
+ metadata.gz: d0d30bd1a2b86ab711560f26438e2ba7907402831cc617b752d3e1fb05c2e6cc
4
+ data.tar.gz: e13d38616b71e39a1e3e23df2999eb88f9824a668dc8347a4b914a58e86bf2bb
5
5
  SHA512:
6
- metadata.gz: 38c1773770dc36ecce0857dd4516d8f6aa350542b25d3d0f946d56d664eb5661d3c53da68286b6a5a9c51344c6cd5c927a4206a9d0a2350cd47e737277e7dfb0
7
- data.tar.gz: 9b5f523c0cacffa3f6f12858dfce51f1f770000447e6d29d83d47856d2a22b51e493b64fcd930755a893293566260ade49d9b295bfb31976c946120ca9e06923
6
+ metadata.gz: d91d69b91a14c9f6acaf230a32c9c75385f54cd7c2ff924860f87e3578ea32f58726d16e0de1d5ccbfbeb808da0ceba804adc9827cd5ae2370dae3b6b478090b
7
+ data.tar.gz: 036b79310b2cef0189a2536eab0d70bdf0d293cd079533da198f666c00ee04ddc7f34a22468ad4b853d2875fe1d44ec8ec305157c115ea19e0b2760e21ccd401
@@ -1,89 +1,88 @@
1
- ;(function (global) {
2
- 'use strict'
3
-
4
- var GOVUK = global.GOVUK || {}
5
- GOVUK.Modules = GOVUK.Modules || {}
6
-
7
- GOVUK.Modules.ExplicitCrossDomainLinks = function () {
8
- this.start = function ($module) {
9
- this.element = $module[0]
10
- this.attribute = 'href'
11
- this.attributeValue = this.element.getAttribute(this.attribute)
12
- this.eventType = 'click'
13
- if (!this.attributeValue) {
14
- this.attribute = 'action'
15
- this.attributeValue = this.element.getAttribute(this.attribute)
16
- this.eventType = 'submit'
17
- }
18
-
19
- this.handleEvent = this.handleEvent.bind(this)
20
- this.handleCookiesAccepted = this.handleCookiesAccepted.bind(this)
21
- // Listens for the 'submit' event if the element is a form, and the 'click' event if it is a link
22
- this.element.addEventListener(this.eventType, this.handleEvent)
23
- }
1
+ window.GOVUK = window.GOVUK || {}
2
+ window.GOVUK.Modules = window.GOVUK.Modules || {};
3
+
4
+ (function (Modules) {
5
+ function ExplicitCrossDomainLinks ($module) {
6
+ this.$module = $module
7
+ }
24
8
 
25
- this.handleEvent = function (e) {
26
- // prevent default: we want the link href and/or form action to be decorated before we navigate away
27
- e.preventDefault()
28
- var cookieBannerEngaged = GOVUK.cookie('cookies_preferences_set')
29
- var cookieConsent = GOVUK.getConsentCookie()
30
-
31
- if (cookieBannerEngaged !== 'true') {
32
- // If not engaged, append only ?cookie_consent=not-engaged
33
- this.decorate(this.element, 'cookie_consent=not-engaged', this.attribute)
34
- } else if (cookieConsent && cookieConsent.usage === true) {
35
- this.handleCookiesAccepted()
36
- } else {
37
- this.decorate(this.element, 'cookie_consent=reject', this.attribute)
38
- }
39
-
40
- // remove the event listener to avoid an infinite loop
41
- this.element.removeEventListener(this.eventType, this.handleEvent)
42
-
43
- // if the element is a form, submit it. If it is a link, click it
44
- if (this.eventType === 'submit') {
45
- this.element.submit()
46
- } else {
47
- this.element.click()
48
- }
9
+ ExplicitCrossDomainLinks.prototype.init = function () {
10
+ this.attribute = 'href'
11
+ this.attributeValue = this.$module.getAttribute(this.attribute)
12
+ this.eventType = 'click'
13
+ if (!this.attributeValue) {
14
+ this.attribute = 'action'
15
+ this.attributeValue = this.$module.getAttribute(this.attribute)
16
+ this.eventType = 'submit'
49
17
  }
50
18
 
51
- this.handleCookiesAccepted = function () {
52
- // If the cookie banner was engaged and usage cookie accepted, append ?_ga=clientid if available and cookie_consent=accept
53
- var element = this.element
54
- var attribute = this.attribute
55
- this.decorate(element, 'cookie_consent=accept', attribute)
19
+ this.handleEvent = this.handleEvent.bind(this)
20
+ this.handleCookiesAccepted = this.handleCookiesAccepted.bind(this)
21
+ // Listens for the 'submit' event if the element is a form, and the 'click' event if it is a link
22
+ this.$module.addEventListener(this.eventType, this.handleEvent)
23
+ }
56
24
 
57
- if (!global.ga) {
58
- return
59
- }
25
+ ExplicitCrossDomainLinks.prototype.decorate = function (element, param, attribute) {
26
+ var attributeValue = element.getAttribute(attribute)
60
27
 
61
- global.ga(function () {
62
- var trackers = global.ga.getAll()
28
+ if (!attributeValue) { return }
63
29
 
64
- if (!trackers.length) { return }
30
+ if (attributeValue.indexOf('?') !== -1) {
31
+ attributeValue += '&' + param
32
+ } else {
33
+ attributeValue += '?' + param
34
+ }
65
35
 
66
- var linker = new global.gaplugins.Linker(trackers[0])
67
- var attrValue = element.getAttribute(attribute)
36
+ element.setAttribute(attribute, attributeValue)
37
+ }
68
38
 
69
- element.setAttribute(attribute, linker.decorate(attrValue))
70
- })
39
+ ExplicitCrossDomainLinks.prototype.handleEvent = function (e) {
40
+ // prevent default: we want the link href and/or form action to be decorated before we navigate away
41
+ e.preventDefault()
42
+ var cookieBannerEngaged = window.GOVUK.cookie('cookies_preferences_set')
43
+ var cookieConsent = window.GOVUK.getConsentCookie()
44
+
45
+ if (cookieBannerEngaged !== 'true') {
46
+ // If not engaged, append only ?cookie_consent=not-engaged
47
+ this.decorate(this.$module, 'cookie_consent=not-engaged', this.attribute)
48
+ } else if (cookieConsent && cookieConsent.usage === true) {
49
+ this.handleCookiesAccepted()
50
+ } else {
51
+ this.decorate(this.$module, 'cookie_consent=reject', this.attribute)
71
52
  }
72
53
 
73
- this.decorate = function (element, param, attribute) {
74
- var attributeValue = element.getAttribute(attribute)
54
+ // remove the event listener to avoid an infinite loop
55
+ this.$module.removeEventListener(this.eventType, this.handleEvent)
75
56
 
76
- if (!attributeValue) { return }
57
+ // if the element is a form, submit it. If it is a link, click it
58
+ if (this.eventType === 'submit') {
59
+ this.$module.submit()
60
+ } else {
61
+ this.$module.click()
62
+ }
63
+ }
77
64
 
78
- if (attributeValue.indexOf('?') !== -1) {
79
- attributeValue += '&' + param
80
- } else {
81
- attributeValue += '?' + param
82
- }
65
+ ExplicitCrossDomainLinks.prototype.handleCookiesAccepted = function () {
66
+ // If the cookie banner was engaged and usage cookie accepted, append ?_ga=clientid if available and cookie_consent=accept
67
+ var element = this.$module
68
+ var attribute = this.attribute
69
+ this.decorate(element, 'cookie_consent=accept', attribute)
83
70
 
84
- element.setAttribute(attribute, attributeValue)
71
+ if (!window.ga) {
72
+ return
85
73
  }
74
+
75
+ window.ga(function () {
76
+ var trackers = window.ga.getAll()
77
+
78
+ if (!trackers.length) { return }
79
+
80
+ var linker = new window.gaplugins.Linker(trackers[0])
81
+ var attrValue = element.getAttribute(attribute)
82
+
83
+ element.setAttribute(attribute, linker.decorate(attrValue))
84
+ })
86
85
  }
87
86
 
88
- global.GOVUK = GOVUK
89
- })(window)
87
+ Modules.ExplicitCrossDomainLinks = ExplicitCrossDomainLinks
88
+ })(window.GOVUK.Modules)
@@ -13,6 +13,10 @@
13
13
  return document.querySelectorAll('[data-track-count="accordionSection"]').length
14
14
  case isDocumentCollectionPage():
15
15
  return document.querySelectorAll('.document-collection .group-title').length
16
+ case isNewBrowsePageLevelTwo():
17
+ // if there are no accordion sections on the browse level 2 page
18
+ // then it is a default page with only one section
19
+ return document.querySelectorAll('[data-track-count="accordionSection"]').length || 1
16
20
  case isNewBrowsePage():
17
21
  return document.querySelectorAll('[data-track-count="cardList"]').length
18
22
  case isMainstreamBrowsePage():
@@ -46,6 +50,8 @@
46
50
  return document.querySelectorAll('a[data-track-category="navAccordionLinkClicked"]').length
47
51
  case isDocumentCollectionPage():
48
52
  return document.querySelectorAll('.document-collection .group-document-list li a').length
53
+ case isNewBrowsePageLevelTwo():
54
+ return document.querySelectorAll('[data-track-count="contentLink"]').length
49
55
  case isNewBrowsePage():
50
56
  return document.querySelectorAll('[data-track-count="cardLink"]').length
51
57
  case isMainstreamBrowsePage():
@@ -98,6 +104,11 @@
98
104
  getMetaAttribute(metaNavigationTypeSelector) === 'leaf'
99
105
  }
100
106
 
107
+ function isNewBrowsePageLevelTwo () {
108
+ return getMetaAttribute(metaApplicationSelector) === 'collections' &&
109
+ getMetaAttribute(metaNavigationTypeSelector) === 'browse level 2'
110
+ }
111
+
101
112
  function isNewBrowsePage () {
102
113
  return getMetaAttribute(metaApplicationSelector) === 'collections' &&
103
114
  getMetaAttribute(metaSectionSelector) === 'new_browse_page' &&
@@ -0,0 +1,49 @@
1
+ window.GOVUK = window.GOVUK || {}
2
+ window.GOVUK.Modules = window.GOVUK.Modules || {};
3
+
4
+ (function (Modules) {
5
+ 'use strict'
6
+
7
+ function GtmClickTracking (module) {
8
+ this.module = module
9
+ this.trackingTrigger = 'data-gtm-event-name' // elements with this attribute get tracked
10
+ }
11
+
12
+ GtmClickTracking.prototype.init = function () {
13
+ var trackClicksOn = [this.module]
14
+ if (!this.module.getAttribute(this.trackingTrigger)) {
15
+ trackClicksOn = this.module.querySelectorAll('[' + this.trackingTrigger + ']')
16
+ }
17
+
18
+ for (var i = 0; i < trackClicksOn.length; i++) {
19
+ trackClicksOn[i].addEventListener('click', this.trackClick.bind(this))
20
+ }
21
+ }
22
+
23
+ GtmClickTracking.prototype.trackClick = function (event) {
24
+ if (window.dataLayer) {
25
+ var target = event.currentTarget
26
+ var data = {
27
+ event: 'analytics',
28
+ event_name: target.getAttribute('data-gtm-event-name'),
29
+ // get entire URL apart from domain
30
+ link_url: window.location.href.substring(window.location.origin.length),
31
+ ui: JSON.parse(target.getAttribute('data-gtm-attributes'))
32
+ }
33
+ var ariaExpanded = this.checkExpandedState(target)
34
+ if (ariaExpanded) {
35
+ data.ui.state = ariaExpanded === 'false' ? 'opened' : 'closed'
36
+ }
37
+ window.dataLayer.push(data)
38
+ }
39
+ }
40
+
41
+ GtmClickTracking.prototype.checkExpandedState = function (clicked) {
42
+ var expanded = clicked.querySelector('[aria-expanded]')
43
+ if (expanded) {
44
+ return expanded.getAttribute('aria-expanded')
45
+ }
46
+ }
47
+
48
+ Modules.GtmClickTracking = GtmClickTracking
49
+ })(window.GOVUK.Modules)
@@ -0,0 +1 @@
1
+ //= require ./analytics-ga4/gtm-click-tracking
@@ -131,7 +131,6 @@ window.GOVUK.Modules.GovukAccordion = window.GOVUKFrontend.Accordion;
131
131
 
132
132
  if (window.GOVUK.analytics && window.GOVUK.analytics.trackEvent) {
133
133
  window.GOVUK.analytics.trackEvent('pageElementInteraction', action, options)
134
- console.log('pageElementInteraction', action, options)
135
134
  }
136
135
  }
137
136
 
@@ -51,6 +51,16 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
51
51
  // giving the table a class of mc-stacked
52
52
  this.options.stacked = this.$table.classList.contains('mc-stacked')
53
53
 
54
+ // stacked charts require tables to use the th element in the table header
55
+ // if the th element is not included, then the ENABLED flag is set to false
56
+ // this will stop the chart and toggleLink from being rendered
57
+ if (this.options.stacked) {
58
+ var allTheTHsInTableHead = this.$table.querySelectorAll('thead th')
59
+ if (allTheTHsInTableHead.length === 0) {
60
+ this.ENABLED = false
61
+ }
62
+ }
63
+
54
64
  // set the negative option based on
55
65
  // giving the table a class of mc-negative
56
66
  this.options.negative = this.$table.classList.contains('mc-negative')
@@ -106,12 +116,16 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
106
116
 
107
117
  MagnaCharta.prototype.apply = function () {
108
118
  if (this.ENABLED) {
109
- this.constructChart()
110
- this.addClassesToHeader()
111
- this.applyWidths()
112
- this.insert()
113
- this.$table.classList.add('mc-hidden')
114
- this.applyOutdent()
119
+ try {
120
+ this.constructChart()
121
+ this.addClassesToHeader()
122
+ this.applyWidths()
123
+ this.insert()
124
+ this.$table.classList.add('mc-hidden')
125
+ this.applyOutdent()
126
+ } catch (error) {
127
+ console.error('MagnaCharta error:', error)
128
+ }
115
129
  }
116
130
  }
117
131
 
@@ -35,7 +35,6 @@
35
35
  for (var j = 0, k = moduleNames.length; j < k; j++) {
36
36
  var moduleName = camelCaseAndCapitalise(moduleNames[j])
37
37
  var started = element.getAttribute('data-' + moduleNames[j] + '-module-started')
38
-
39
38
  if (typeof GOVUK.Modules[moduleName] === 'function' && !started) {
40
39
  // Vanilla JavaScript GOV.UK Modules and GOV.UK Frontend Modules
41
40
  if (GOVUK.Modules[moduleName].prototype.init) {
@@ -157,3 +157,40 @@ if (
157
157
  LongTaskObserver.observe({ type: ["longtask"] });
158
158
  } catch (e) {}
159
159
  }
160
+
161
+ // As per RFC 147[1], this adds in monitoring of the type of HTTP protocol that
162
+ // is used when a browser loads a page.
163
+ //
164
+ // The User Timing API (aka window.performance) is used to record the data - to
165
+ // avoid the use of this from breaking the JavaScript for the small number of
166
+ // browsers that don't support it, it's been wrapped in a try/catch block plus a
167
+ // couple of checks to prevent "is not defined" errors.
168
+ //
169
+ // Because the `nextHopProtocol` isn't immediately available - it seems to need
170
+ // a request to be made before it's populated - we need to wait for the
171
+ // `DOMContentReady` event before we can see what the HTTP version is.
172
+ //
173
+ // [1]: https://github.com/alphagov/govuk-rfcs/pull/148
174
+
175
+ var measureHTTPProtocol = function () {
176
+ var getEntriesByType = performance.getEntriesByType('navigation')
177
+
178
+ if (getEntriesByType.length > 0) {
179
+ var httpProtocol = JSON.parse(JSON.stringify(performance.getEntriesByType('navigation')[0].nextHopProtocol))
180
+ LUX.addData("http-protocol", httpProtocol)
181
+ }
182
+ }
183
+
184
+ try {
185
+ if (typeof performance !== 'undefined' && typeof performance.getEntriesByType !== 'undefined') {
186
+ if (document.readyState === 'complete') {
187
+ measureHTTPProtocol()
188
+ } else {
189
+ window.addEventListener('load', function() {
190
+ measureHTTPProtocol()
191
+ })
192
+ }
193
+ }
194
+ } catch (e) {
195
+ console.error('Error in LUX reporting the HTTP protocol (' + window.location + '):', e)
196
+ }