govuk_publishing_components 37.1.0 → 37.2.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/config/govuk_publishing_components_manifest.js +0 -2
  3. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +6 -0
  4. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-schemas.js +2 -2
  5. data/app/assets/javascripts/govuk_publishing_components/lib/cookie-settings.js +109 -0
  6. data/app/assets/javascripts/govuk_publishing_components/load-analytics.js +83 -60
  7. data/app/assets/stylesheets/component_guide/application.scss +1 -1
  8. data/app/assets/stylesheets/govuk_publishing_components/components/_contents-list.scss +1 -1
  9. data/app/assets/stylesheets/govuk_publishing_components/components/_image-card.scss +3 -3
  10. data/app/assets/stylesheets/govuk_publishing_components/components/_modal-dialogue.scss +1 -1
  11. data/app/assets/stylesheets/govuk_publishing_components/components/_search.scss +2 -2
  12. data/app/assets/stylesheets/govuk_publishing_components/components/_share-links.scss +1 -1
  13. data/app/assets/stylesheets/govuk_publishing_components/components/_step-by-step-nav-related.scss +1 -1
  14. data/app/assets/stylesheets/govuk_publishing_components/components/_step-by-step-nav.scss +10 -10
  15. data/app/assets/stylesheets/govuk_publishing_components/components/_table.scss +1 -1
  16. data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_warning-callout.scss +2 -2
  17. data/app/assets/stylesheets/govuk_publishing_components/components/helpers/_markdown-typography.scss +1 -1
  18. data/app/assets/stylesheets/govuk_publishing_components/components/mixins/_grid-helper.scss +1 -1
  19. data/app/models/govuk_publishing_components/audit_applications.rb +1 -1
  20. data/app/models/govuk_publishing_components/component_docs.rb +1 -1
  21. data/app/views/govuk_publishing_components/components/_details.html.erb +18 -2
  22. data/app/views/govuk_publishing_components/components/_image_card.html.erb +11 -11
  23. data/app/views/govuk_publishing_components/components/_layout_footer.html.erb +5 -0
  24. data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +7 -3
  25. data/app/views/govuk_publishing_components/components/_metadata.html.erb +1 -5
  26. data/app/views/govuk_publishing_components/components/docs/details.yml +21 -0
  27. data/app/views/govuk_publishing_components/components/docs/image_card.yml +18 -11
  28. data/app/views/govuk_publishing_components/components/feedback/_problem_form.html.erb +5 -1
  29. data/app/views/govuk_publishing_components/components/feedback/_survey_signup_form.html.erb +5 -1
  30. data/lib/govuk_publishing_components/presenters/absolute_links_helper.rb +20 -0
  31. data/lib/govuk_publishing_components/version.rb +1 -1
  32. data/lib/govuk_publishing_components.rb +1 -0
  33. data/node_modules/axe-core/axe.js +604 -440
  34. data/node_modules/axe-core/axe.min.js +2 -2
  35. data/node_modules/axe-core/package.json +1 -1
  36. data/node_modules/axe-core/sri-history.json +4 -0
  37. metadata +5 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43da95e575a63b626abef7368fbe99d286682778b3933f9b021768277b3a8d17
4
- data.tar.gz: a2959e0efd156b0c5d5a70b61561725d55a4c67e58f0e60537970439af0088db
3
+ metadata.gz: 638e22aafc5fdc6f80b61babb5bcddf89bc41213e5e49afaf95c23a8ca62d5ea
4
+ data.tar.gz: 3af560c10c9beb1da8db5d930d5bd02b530f5ceeaa65734b5432baf2651a7e9b
5
5
  SHA512:
6
- metadata.gz: 60593190a4219cf35788d97f6f491c9f36ad873772d2b3908bcb21c347fcadcd061e35c25225acaa89199f5cabb6f0769376847521738cc26ff23eb088febce5
7
- data.tar.gz: 22a37d5ff61c2164ef441774aae4f9268a9a46b19e7d5dbc847b7e290af706ce038894cd70a992ffe525a17e61ad205476bdb1d4eedc5e486a9f006a38532183
6
+ metadata.gz: 3b4d262a5afcc7a3b436b9142daa46e994bbbda7bb5e780a8c98d95df894d9ae6d3a8ae46a62fac7daea39d33006311843d231f786f1b2ba5ebcd47c76a41d3d
7
+ data.tar.gz: f7274e4d9d134c750ae933b717b7a6efae8fb126c1c5c290646454ab35b153108d2ccda6971f702abea7e6554f079ce68e395fff789c2ff763d0f8a7df9c350f
@@ -11,5 +11,3 @@
11
11
  //= link govuk_publishing_components/vendor/modernizr.js
12
12
  //= link govuk_publishing_components/vendor/lux/lux-reporter.js
13
13
  //= link govuk_publishing_components/vendor/lux/lux-measurer.js
14
-
15
- //= link_tree ../builds
@@ -34,8 +34,14 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
34
34
  return navigator.userAgent
35
35
  },
36
36
 
37
+ getTimestamp: function () {
38
+ return Date.now().toString()
39
+ },
40
+
37
41
  sendData: function (data) {
38
42
  data.govuk_gem_version = this.getGemVersion()
43
+ data.timestamp = this.getTimestamp()
44
+
39
45
  // set this in the console as a debugging aid
40
46
  if (window.GOVUK.analyticsGa4.showDebug) {
41
47
  if (data.event_data) {
@@ -75,14 +75,14 @@
75
75
  // given an object and a key, insert a value into object[key] if it exists
76
76
  Schemas.prototype.addToObject = function (obj, key, value) {
77
77
  if (key in obj) {
78
- obj[key] = value
78
+ obj[key] = value + '' // ensure is a string
79
79
  return obj
80
80
  } else {
81
81
  // check for one level of nesting in the object
82
82
  for (var property in obj) {
83
83
  if (this.isAnObject(obj[property])) {
84
84
  if (key in obj[property]) {
85
- obj[property][key] = value
85
+ obj[property][key] = value + '' // ensure is a string
86
86
  return obj
87
87
  }
88
88
  }
@@ -0,0 +1,109 @@
1
+ window.GOVUK = window.GOVUK || {}
2
+ window.GOVUK.Modules = window.GOVUK.Modules || {};
3
+
4
+ (function (Modules) {
5
+ function CookieSettings ($module) {
6
+ this.$module = $module
7
+ }
8
+
9
+ CookieSettings.prototype.init = function () {
10
+ this.$module.submitSettingsForm = this.submitSettingsForm.bind(this)
11
+
12
+ document.querySelector('form[data-module=cookie-settings]')
13
+ .addEventListener('submit', this.$module.submitSettingsForm)
14
+
15
+ this.setInitialFormValues()
16
+ }
17
+
18
+ CookieSettings.prototype.setInitialFormValues = function () {
19
+ if (!window.GOVUK.cookie('cookies_policy')) {
20
+ window.GOVUK.setDefaultConsentCookie()
21
+ }
22
+
23
+ var currentConsentCookie = window.GOVUK.cookie('cookies_policy')
24
+ var currentConsentCookieJSON = JSON.parse(currentConsentCookie)
25
+
26
+ // We don't need the essential value as this cannot be changed by the user
27
+ delete currentConsentCookieJSON.essential
28
+
29
+ for (var cookieType in currentConsentCookieJSON) {
30
+ var radioButton
31
+
32
+ if (currentConsentCookieJSON[cookieType]) {
33
+ radioButton = document.querySelector('input[name=cookies-' + cookieType + '][value=on]')
34
+ } else {
35
+ radioButton = document.querySelector('input[name=cookies-' + cookieType + '][value=off]')
36
+ }
37
+
38
+ if (radioButton) {
39
+ radioButton.checked = true
40
+ }
41
+ }
42
+ }
43
+
44
+ CookieSettings.prototype.submitSettingsForm = function (event) {
45
+ event.preventDefault()
46
+
47
+ var formInputs = event.target.getElementsByTagName('input')
48
+ var options = {}
49
+
50
+ for (var i = 0; i < formInputs.length; i++) {
51
+ var input = formInputs[i]
52
+ if (input.checked) {
53
+ var name = input.name.replace('cookies-', '')
54
+ var value = input.value === 'on'
55
+
56
+ options[name] = value
57
+ }
58
+ }
59
+
60
+ window.GOVUK.setConsentCookie(options)
61
+ window.GOVUK.setCookie('cookies_preferences_set', true, { days: 365 })
62
+
63
+ this.fireAnalyticsEvent(options)
64
+
65
+ this.showConfirmationMessage()
66
+
67
+ return false
68
+ }
69
+
70
+ CookieSettings.prototype.fireAnalyticsEvent = function (consent) {
71
+ var eventLabel = ''
72
+
73
+ for (var option in consent) {
74
+ var optionValue = consent[option] ? 'yes' : 'no'
75
+ eventLabel += option + '-' + optionValue + ' '
76
+ }
77
+
78
+ if (window.GOVUK.analytics && window.GOVUK.analytics.trackEvent) {
79
+ window.GOVUK.analytics.trackEvent('cookieSettings', 'Save changes', { label: eventLabel })
80
+ }
81
+ }
82
+
83
+ CookieSettings.prototype.showConfirmationMessage = function () {
84
+ var confirmationMessage = document.querySelector('div[data-cookie-confirmation]')
85
+ // hide the message if already visible so assistive tech is triggered when it appears
86
+ confirmationMessage.style.display = 'none'
87
+ var previousPageLink = document.querySelector('.cookie-settings__prev-page')
88
+ var referrer = CookieSettings.prototype.getReferrerLink()
89
+
90
+ document.body.scrollTop = document.documentElement.scrollTop = 0
91
+
92
+ if (previousPageLink) {
93
+ if (referrer && referrer !== document.location.pathname) {
94
+ previousPageLink.href = referrer
95
+ previousPageLink.style.display = 'inline'
96
+ } else {
97
+ previousPageLink.style.display = 'none'
98
+ }
99
+ }
100
+
101
+ confirmationMessage.style.display = 'block'
102
+ }
103
+
104
+ CookieSettings.prototype.getReferrerLink = function () {
105
+ return document.referrer ? new URL(document.referrer).pathname : false
106
+ }
107
+
108
+ Modules.CookieSettings = CookieSettings
109
+ })(window.GOVUK.Modules)
@@ -3,49 +3,75 @@
3
3
  //= require govuk_publishing_components/analytics/linked-domains
4
4
 
5
5
  window.GOVUK.loadAnalytics = {
6
- productionDomains: [
7
- 'www.gov.uk',
8
- 'www-origin.publishing.service.gov.uk',
9
- 'assets.publishing.service.gov.uk'
10
- ],
11
- stagingDomains: [
12
- 'www.staging.publishing.service.gov.uk',
13
- 'www-origin.staging.publishing.service.gov.uk',
14
- 'assets.staging.publishing.service.gov.uk'
15
- ],
16
- integrationDomains: [
17
- 'www.integration.publishing.service.gov.uk',
18
- 'www-origin.integration.publishing.service.gov.uk',
19
- 'assets.integration.publishing.service.gov.uk'
20
- ],
21
- developmentDomains: [
22
- 'localhost', '127.0.0.1', '0.0.0.0'
23
- ],
24
-
25
- // For Universal Analytics' cross domain tracking. linkedDomains is defined by the require statement at the top of the file.
26
- linkedDomains: window.GOVUK.analytics.linkedDomains,
27
-
28
- ga4EnvironmentVariables: {
29
- // initialiseGA4 is used to enable/disable GA4 on specific environments
30
- production: {
31
- initialiseGA4: true
6
+ domains: [
7
+ {
8
+ // need to have this one at the start, see loadGa4 function
9
+ name: 'development',
10
+ domains: [
11
+ 'localhost',
12
+ '127.0.0.1',
13
+ '0.0.0.0',
14
+ 'dev.gov.uk'
15
+ ],
16
+ initialiseGA4: true,
17
+ id: 'GTM-MG7HG5W',
18
+ auth: 'bRiZ-jiEHtw6hHpGd6dF9w',
19
+ preview: 'env-3',
20
+ gaProperty: 'UA-UNSET',
21
+ gaPropertyCrossDomain: 'UA-UNSET'
22
+ },
23
+ {
24
+ name: 'production',
25
+ domains: [
26
+ 'www.gov.uk',
27
+ 'www-origin.publishing.service.gov.uk',
28
+ 'assets.publishing.service.gov.uk'
29
+ ],
30
+ initialiseGA4: true,
31
+ id: 'GTM-MG7HG5W',
32
+ gaProperty: 'UA-26179049-1',
33
+ gaPropertyCrossDomain: 'UA-145652997-1'
32
34
  },
33
- staging: {
35
+ {
36
+ name: 'staging',
37
+ domains: [
38
+ 'www.staging.publishing.service.gov.uk',
39
+ 'www-origin.staging.publishing.service.gov.uk',
40
+ 'assets.staging.publishing.service.gov.uk'
41
+ ],
34
42
  initialiseGA4: true,
43
+ id: 'GTM-MG7HG5W',
35
44
  auth: 'oJWs562CxSIjZKn_GlB5Bw',
36
- preview: 'env-5'
45
+ preview: 'env-5',
46
+ gaProperty: 'UA-26179049-20',
47
+ gaPropertyCrossDomain: 'UA-145652997-1'
37
48
  },
38
- integration: {
49
+ {
50
+ name: 'integration',
51
+ domains: [
52
+ 'www.integration.publishing.service.gov.uk',
53
+ 'www-origin.integration.publishing.service.gov.uk',
54
+ 'assets.integration.publishing.service.gov.uk'
55
+ ],
39
56
  initialiseGA4: true,
57
+ id: 'GTM-MG7HG5W',
40
58
  auth: 'C7iYdcsOlYgGmiUJjZKrHQ',
41
- preview: 'env-4'
59
+ preview: 'env-4',
60
+ gaProperty: 'UA-26179049-22',
61
+ gaPropertyCrossDomain: 'UA-145652997-1'
42
62
  },
43
- development: {
63
+ {
64
+ name: 'devdocs',
65
+ domains: [
66
+ 'docs.publishing.service.gov.uk'
67
+ ],
44
68
  initialiseGA4: true,
45
- auth: 'bRiZ-jiEHtw6hHpGd6dF9w',
46
- preview: 'env-3'
69
+ id: 'GTM-TNKCK97'
47
70
  }
48
- },
71
+ ],
72
+
73
+ // For Universal Analytics' cross domain tracking. linkedDomains is defined by the require statement at the top of the file.
74
+ linkedDomains: window.GOVUK.analytics.linkedDomains,
49
75
 
50
76
  loadUa: function (currentDomain) {
51
77
  currentDomain = currentDomain || window.location.hostname
@@ -57,17 +83,14 @@ window.GOVUK.loadAnalytics = {
57
83
  window.GOVUK.analyticsVars.gaProperty = 'UA-UNSET'
58
84
  window.GOVUK.analyticsVars.gaPropertyCrossDomain = 'UA-UNSET'
59
85
 
60
- if (this.arrayContains(currentDomain, this.productionDomains)) {
61
- window.GOVUK.analyticsVars.gaProperty = 'UA-26179049-1'
62
- window.GOVUK.analyticsVars.gaPropertyCrossDomain = 'UA-145652997-1'
63
- } else if (this.arrayContains(currentDomain, this.stagingDomains)) {
64
- window.GOVUK.analyticsVars.gaProperty = 'UA-26179049-20'
65
- window.GOVUK.analyticsVars.gaPropertyCrossDomain = 'UA-145652997-1'
66
- } else if (this.arrayContains(currentDomain, this.integrationDomains)) {
67
- window.GOVUK.analyticsVars.gaProperty = 'UA-26179049-22'
68
- window.GOVUK.analyticsVars.gaPropertyCrossDomain = 'UA-145652997-1'
86
+ for (var i = 0; i < this.domains.length; i++) {
87
+ var current = this.domains[i]
88
+ if (this.arrayContains(currentDomain, current.domains)) {
89
+ window.GOVUK.analyticsVars.gaProperty = current.gaProperty
90
+ window.GOVUK.analyticsVars.gaPropertyCrossDomain = current.gaPropertyCrossDomain
91
+ break
92
+ }
69
93
  }
70
-
71
94
  // Load universal analytics
72
95
  if (typeof window.GOVUK.analyticsInit !== 'undefined') {
73
96
  window.GOVUK.analyticsInit()
@@ -76,29 +99,29 @@ window.GOVUK.loadAnalytics = {
76
99
 
77
100
  loadGa4: function (currentDomain) {
78
101
  currentDomain = currentDomain || window.location.hostname
79
- var environment = ''
80
-
81
- // Categorise current environment
82
- if (this.arrayContains(currentDomain, this.productionDomains)) {
83
- environment = 'production'
84
- } else if (this.arrayContains(currentDomain, this.stagingDomains)) {
85
- environment = 'staging'
86
- } else if (this.arrayContains(currentDomain, this.integrationDomains)) {
87
- environment = 'integration'
88
- } else if (this.arrayContains(currentDomain, this.developmentDomains) || currentDomain.indexOf('.dev.gov.uk') !== -1) {
89
- environment = 'development'
102
+ var environment = false
103
+ // lots of dev domains, so simplify the matching process
104
+ if (currentDomain.match(/\/{2}[a-zA-Z0-9.]+dev\.gov\.uk/)) {
105
+ environment = this.domains[0]
106
+ } else {
107
+ for (var i = 0; i < this.domains.length; i++) {
108
+ if (this.arrayContains(currentDomain, this.domains[i].domains)) {
109
+ environment = this.domains[i]
110
+ break
111
+ }
112
+ }
90
113
  }
91
114
 
92
115
  // If we recognise the environment (i.e. the string isn't empty), load in GA4
93
116
  if (environment) {
94
117
  // If analytics-ga4.js exists and our detected environment has 'initialiseGA4' set to true, load GA4.
95
- if (typeof window.GOVUK.analyticsGa4.init !== 'undefined' && this.ga4EnvironmentVariables[environment].initialiseGA4) {
118
+ if (typeof window.GOVUK.analyticsGa4.init !== 'undefined' && environment.initialiseGA4) {
96
119
  window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {}
97
120
  window.GOVUK.analyticsGa4.vars = window.GOVUK.analyticsGa4.vars || {}
98
- window.GOVUK.analyticsGa4.vars.id = 'GTM-MG7HG5W'
99
- window.GOVUK.analyticsGa4.vars.auth = this.ga4EnvironmentVariables[environment].auth
100
- window.GOVUK.analyticsGa4.vars.preview = this.ga4EnvironmentVariables[environment].preview
101
- window.GOVUK.analyticsGa4.vars.environment = environment // Used for testing and debugging
121
+ window.GOVUK.analyticsGa4.vars.id = environment.id
122
+ window.GOVUK.analyticsGa4.vars.auth = environment.auth
123
+ window.GOVUK.analyticsGa4.vars.preview = environment.preview
124
+ window.GOVUK.analyticsGa4.vars.environment = environment.name // Used for testing and debugging
102
125
 
103
126
  window.GOVUK.analyticsGa4.vars.gem_version = 'not found'
104
127
  var gemMeta = document.querySelector('meta[name="govuk:components_gem_version"]')
@@ -165,7 +165,7 @@ $gem-guide-border-width: 1px;
165
165
  h3,
166
166
  h4 {
167
167
  margin-top: 0;
168
- margin-bottom: $govuk-gutter / 2;
168
+ margin-bottom: calc($govuk-gutter / 2);
169
169
  }
170
170
 
171
171
  h3 a {
@@ -40,7 +40,7 @@
40
40
  list-style-type: none;
41
41
 
42
42
  @include govuk-media-query($from: tablet) {
43
- padding-top: govuk-spacing(6) / 4;
43
+ padding-top: calc(govuk-spacing(6) / 4);
44
44
  }
45
45
  }
46
46
 
@@ -186,7 +186,7 @@
186
186
  @include govuk-font($size: false);
187
187
  font-size: 16px;
188
188
  font-size: govuk-px-to-rem(16px);
189
- margin: 0 0 (govuk-spacing(3) / 2);
189
+ margin: 0 0 calc(govuk-spacing(3) / 2);
190
190
  color: govuk-colour("dark-grey", $legacy: "grey-1");
191
191
 
192
192
  @include govuk-media-query($from: tablet) {
@@ -196,7 +196,7 @@
196
196
 
197
197
  .gem-c-image-card__description {
198
198
  @include govuk-font($size: 19);
199
- padding-top: (govuk-spacing(3) / 2);
199
+ padding-top: calc(govuk-spacing(3) / 2);
200
200
  word-wrap: break-word;
201
201
  }
202
202
 
@@ -204,7 +204,7 @@
204
204
  @include govuk-font($size: 19);
205
205
  position: relative;
206
206
  z-index: 2;
207
- padding: (govuk-spacing(3) / 2) 0 0 0;
207
+ padding: calc(govuk-spacing(3) / 2) 0 0 0;
208
208
  margin: 0;
209
209
  list-style: none;
210
210
 
@@ -46,7 +46,7 @@ $govuk-modal-wide-breakpoint: $govuk-page-width + $govuk-modal-margin * 2 + $gov
46
46
  bottom: inherit;
47
47
  left: inherit;
48
48
  width: auto;
49
- max-width: $govuk-page-width * 2 / 3;
49
+ max-width: $govuk-page-width * calc(2 / 3);
50
50
  height: auto;
51
51
  margin: $govuk-modal-margin auto;
52
52
  border: $govuk-border-width-form-element solid $govuk-input-border-colour;
@@ -65,7 +65,7 @@ $large-input-size: 50px;
65
65
  }
66
66
 
67
67
  .gem-c-search__input[type="search"] { // overly specific to prevent some overrides from outside
68
- @include govuk-font($size: 19, $line-height: (28 / 19));
68
+ @include govuk-font($size: 19, $line-height: calc(28 / 19));
69
69
  margin: 0;
70
70
  width: 100%;
71
71
  height: govuk-em(40, 16);
@@ -108,7 +108,7 @@ $large-input-size: 50px;
108
108
 
109
109
  @mixin icon-positioning($container-size) {
110
110
  $icon-dimension: 20px;
111
- $icon-position: ($container-size - $icon-dimension) / 2;
111
+ $icon-position: calc(($container-size - $icon-dimension) / 2);
112
112
 
113
113
  display: block;
114
114
  pointer-events: none;
@@ -18,7 +18,7 @@ $share-button-height: 30px;
18
18
  padding-left: ($share-button-width + govuk-spacing(2));
19
19
  padding-right: govuk-spacing(2);
20
20
  margin-bottom: govuk-spacing(2);
21
- font-size: $share-button-height / 2;
21
+ font-size: calc($share-button-height / 2);
22
22
  }
23
23
 
24
24
  .gem-c-share-links__link {
@@ -32,7 +32,7 @@
32
32
  }
33
33
 
34
34
  .gem-c-step-nav-related__pretitle {
35
- margin-bottom: govuk-spacing(6) / 4;
35
+ margin-bottom: calc(govuk-spacing(6) / 4);
36
36
  }
37
37
  }
38
38
 
@@ -17,12 +17,12 @@ $top-border: solid 1px govuk-colour("mid-grey", $legacy: "grey-3");
17
17
 
18
18
  @mixin step-nav-line-position {
19
19
  left: 0;
20
- margin-left: govuk-em(($number-circle-size / 2) - ($stroke-width / 2), 16);
20
+ margin-left: govuk-em(calc($number-circle-size / 2) - calc($stroke-width / 2), 16);
21
21
  }
22
22
 
23
23
  @mixin step-nav-line-position-large {
24
24
  left: 0;
25
- margin-left: govuk-em(($number-circle-size-large / 2) - ($stroke-width / 2), 16);
25
+ margin-left: govuk-em(calc($number-circle-size-large / 2) - calc($stroke-width / 2), 16);
26
26
  }
27
27
 
28
28
  // custom mixin as govuk-font does undesirable things at different breakpoints
@@ -260,8 +260,8 @@ $top-border: solid 1px govuk-colour("mid-grey", $legacy: "grey-3");
260
260
  z-index: 6;
261
261
  bottom: 0;
262
262
  left: 0;
263
- margin-left: $number-circle-size / 4;
264
- width: $number-circle-size / 2;
263
+ margin-left: calc($number-circle-size / 4);
264
+ width: calc($number-circle-size / 2);
265
265
  height: 0;
266
266
  border-bottom: solid $stroke-width govuk-colour("mid-grey", $legacy: "grey-2");
267
267
  }
@@ -278,8 +278,8 @@ $top-border: solid 1px govuk-colour("mid-grey", $legacy: "grey-3");
278
278
  .gem-c-step-nav--large & {
279
279
  @include govuk-media-query($from: tablet) {
280
280
  &::before {
281
- margin-left: $number-circle-size-large / 4;
282
- width: $number-circle-size-large / 2;
281
+ margin-left: calc($number-circle-size-large / 4);
282
+ width: calc($number-circle-size-large / 2);
283
283
  }
284
284
 
285
285
  &::after {
@@ -508,9 +508,9 @@ $top-border: solid 1px govuk-colour("mid-grey", $legacy: "grey-3");
508
508
  z-index: 5;
509
509
  top: .6em; // position the dot to align with the first row of text in the link
510
510
  left: -(govuk-spacing(6) + govuk-spacing(3));
511
- margin-top: -($stroke-width / 2);
512
- margin-left: ($number-circle-size / 2);
513
- width: $number-circle-size / 2;
511
+ margin-top: - calc($stroke-width / 2);
512
+ margin-left: calc($number-circle-size / 2);
513
+ width: calc($number-circle-size / 2);
514
514
  height: $stroke-width;
515
515
  background: govuk-colour("black");
516
516
  }
@@ -519,7 +519,7 @@ $top-border: solid 1px govuk-colour("mid-grey", $legacy: "grey-3");
519
519
  @include govuk-media-query($from: tablet) {
520
520
  &::before {
521
521
  left: -(govuk-spacing(9));
522
- margin-left: ($number-circle-size-large / 2);
522
+ margin-left: calc($number-circle-size-large / 2);
523
523
  }
524
524
  }
525
525
  }
@@ -8,7 +8,7 @@ $table-header-background-colour: govuk-colour("light-grey", $legacy: "grey-3");
8
8
  $sort-link-active-colour: govuk-colour("white");
9
9
  $sort-link-arrow-size: 14px;
10
10
  $sort-link-arrow-size-small: 8px;
11
- $sort-link-arrow-spacing: $sort-link-arrow-size / 2;
11
+ $sort-link-arrow-spacing: calc($sort-link-arrow-size / 2);
12
12
  $table-row-hover-background-colour: rgba(43, 140, 196, .2);
13
13
  $table-row-even-background-colour: govuk-colour("light-grey", $legacy: "grey-4");
14
14
 
@@ -23,10 +23,10 @@
23
23
  padding-left: $icon-size;
24
24
 
25
25
  // Center the icon around the baseline
26
- padding-top: ($icon-size - $line-height-mobile) / 2;
26
+ padding-top: calc(($icon-size - $line-height-mobile) / 2);
27
27
 
28
28
  @include govuk-media-query($from: tablet) {
29
- padding-top: ($icon-size - $line-height-tablet) / 2;
29
+ padding-top: calc(($icon-size - $line-height-tablet) / 2);
30
30
  }
31
31
 
32
32
  p {
@@ -3,7 +3,7 @@
3
3
 
4
4
  @include govuk-font($size: 16);
5
5
 
6
- $gutter-two-thirds: $govuk-gutter - ($govuk-gutter / 3);
6
+ $gutter-two-thirds: $govuk-gutter - calc($govuk-gutter / 3);
7
7
 
8
8
  ol,
9
9
  ul,
@@ -48,7 +48,7 @@
48
48
  /// }
49
49
  ///
50
50
  @mixin columns($items, $columns, $selector: "*", $flow: row) {
51
- $rows: ceil($items / $columns);
51
+ $rows: ceil(calc($items / $columns));
52
52
 
53
53
  display: -ms-grid;
54
54
  display: grid;
@@ -157,7 +157,7 @@ module GovukPublishingComponents
157
157
  end
158
158
 
159
159
  def find_code_references(file, src, regex)
160
- return clean_file_path(file) if regex.match?(src)
160
+ clean_file_path(file) if regex.match?(src)
161
161
  end
162
162
 
163
163
  def clean_file_path(file)
@@ -31,7 +31,7 @@ module GovukPublishingComponents
31
31
  end
32
32
 
33
33
  def component_in_use(component)
34
- return true if @limit_to.include?(component)
34
+ true if @limit_to.include?(component)
35
35
  end
36
36
 
37
37
  def fetch_component_doc(id)
@@ -2,23 +2,39 @@
2
2
  add_gem_component_stylesheet("details")
3
3
 
4
4
  shared_helper = GovukPublishingComponents::Presenters::SharedHelper.new(local_assigns)
5
-
6
5
  open ||= nil
6
+ disable_ga4 ||= false
7
+ @ga4 ||= OpenStruct.new(index_section: 0) unless disable_ga4
8
+ @ga4[:index_section] += 1 unless disable_ga4
9
+ ga4_attributes ||= {}
10
+
7
11
  margin_bottom ||= 3
8
12
  css_classes = %w(gem-c-details govuk-details)
9
13
  css_classes << shared_helper.get_margin_bottom
10
14
 
11
15
  details_data_attributes = {}
12
16
  details_data_attributes[:module] = 'govuk-details gem-details'
17
+ details_data_attributes[:module] = 'govuk-details gem-details ga4-event-tracker' unless disable_ga4
13
18
 
14
19
  data_attributes ||= {}
15
20
  data_attributes[:details_track_click] = ''
21
+ unless disable_ga4
22
+ ga4_event = {
23
+ event_name: "select_content",
24
+ type: "detail",
25
+ text: title,
26
+ section: title,
27
+ index_section: @ga4[:index_section],
28
+ }
29
+ ga4_event.merge!(ga4_attributes)
30
+ data_attributes[:ga4_event] = ga4_event
31
+ end
16
32
 
17
33
  summary_aria_attributes ||= {}
18
34
  %>
19
35
  <%= tag.details class: css_classes, data: details_data_attributes, open: open do %>
20
36
  <%= tag.summary class: "govuk-details__summary", data: data_attributes, aria: summary_aria_attributes do %>
21
- <span class="govuk-details__summary-text">
37
+ <span class="govuk-details__summary-text" <% unless disable_ga4 %>data-ga4-expandable<% end %>>
22
38
  <%= title %>
23
39
  </span>
24
40
  <% end %>
@@ -7,13 +7,13 @@
7
7
  brand_helper = GovukPublishingComponents::AppHelpers::BrandHelper.new(brand)
8
8
  card_helper = GovukPublishingComponents::Presenters::ImageCardHelper.new(local_assigns, brand_helper)
9
9
  shared_helper = GovukPublishingComponents::Presenters::SharedHelper.new(local_assigns)
10
-
11
- classes = %w[gem-c-image-card]
12
- classes << "govuk-grid-row" if card_helper.two_thirds
13
- classes << "gem-c-image-card--large" if card_helper.large
14
- classes << "gem-c-image-card--two-thirds" if card_helper.two_thirds
15
- classes << "gem-c-image-card--no-image" unless (card_helper.image_src || card_helper.youtube_video_id)
16
- classes << brand_helper.brand_class if brand_helper.brand_class
10
+ component_helper = GovukPublishingComponents::Presenters::ComponentWrapperHelper.new(local_assigns)
11
+ component_helper.add_class("gem-c-image-card")
12
+ component_helper.add_class("govuk-grid-row") if card_helper.two_thirds
13
+ component_helper.add_class("gem-c-image-card--large") if card_helper.large
14
+ component_helper.add_class("gem-c-image-card--two-thirds") if card_helper.two_thirds
15
+ component_helper.add_class("gem-c-image-card--no-image") unless (card_helper.image_src || card_helper.youtube_video_id)
16
+ component_helper.add_class(brand_helper.brand_class) if brand_helper.brand_class
17
17
 
18
18
  text_wrapper_classes = %w[gem-c-image-card__text-wrapper]
19
19
  text_wrapper_classes << "gem-c-image-card__text-wrapper--two-thirds" if card_helper.two_thirds
@@ -34,12 +34,12 @@
34
34
  ]
35
35
  extra_link_classes << brand_helper.color_class
36
36
 
37
- data_modules = %w[]
38
- data_modules << "gem-track-click ga4-link-tracker" if card_helper.is_tracking?
39
- data_modules << "image-card" if card_helper.youtube_video_id
37
+ component_helper.add_data_attribute({ module: "gem-track-click" }) if card_helper.is_tracking?
38
+ component_helper.add_data_attribute({ module: "image-card" }) if card_helper.youtube_video_id
39
+ component_helper.set_lang(card_helper.lang)
40
40
  %>
41
41
  <% if card_helper.href || card_helper.extra_details.any? %>
42
- <%= content_tag(:div, class: classes, "data-module": data_modules, lang: card_helper.lang) do %>
42
+ <%= tag.div(**component_helper.all_attributes) do %>
43
43
  <%= content_tag(:div, class: text_wrapper_classes) do %>
44
44
  <div class="gem-c-image-card__header-and-context-wrapper">
45
45
  <% if card_helper.heading_text %>