govuk_publishing_components 29.6.0 → 29.9.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 (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
+ }