govuk_publishing_components 33.0.0 → 33.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72c594630cfc9f9359ae29eaff51094d512f4060cff0ad23e96319f264b0bae6
4
- data.tar.gz: d074f64d7b59bd3657e0e84c2a44f9de23a8a5db99e71e832b2eab98a617fb8e
3
+ metadata.gz: 4e788fa8e962e9665a5c978857dd72563e92394a2b437020e143242e48eac153
4
+ data.tar.gz: 47ebd5cd998e31cf0899e6a4dfa83949466fa1f9e357edecfb5551ca6c4b7ee3
5
5
  SHA512:
6
- metadata.gz: 77cde1c322a721bc97a30a348e53b84d369c029afa32cd81fc7def30577aea67324f4c60ecc5c6f03e1c77dc3697d6a2ab5339a177c3946276ddcaa26bb72303
7
- data.tar.gz: 7134f8030b4d16a4b4e38dc5ba6aa8d9c08aa0bc2ef319e7710b407685a8cbace2ce9c32c5330883cd93412690687387f5960246667a63a8808cc20949c02de1
6
+ metadata.gz: 55773e69567c5797c93ce788fb226f8d8ca9dcc256b12b5c6dd6caca421c5419d20f98dda3db8a8dc3baa5606915b5a0975ee738ceea752cb174feec9ce2bc5e
7
+ data.tar.gz: '093545166981ef4a4c15dd7864bfe6a3017df0a7c5a61bad7c925e1834fcfaec3e2acc9be675ae184f63213882902e20b2190bc0393ab22327b5a6f16d344593'
@@ -24,7 +24,6 @@
24
24
  })
25
25
 
26
26
  var axeOptions = {
27
- include: [selector],
28
27
  rules: axeRules
29
28
  }
30
29
 
@@ -50,7 +50,7 @@
50
50
  var schema = this.populateEcommerceSchema(searchResultsBlock, false, null)
51
51
 
52
52
  this.clearPreviousEcommerceObject()
53
- window.dataLayer.push(schema)
53
+ GOVUK.analyticsGa4.core.sendData(schema)
54
54
  },
55
55
 
56
56
  handleClick: function (event) {
@@ -62,7 +62,7 @@
62
62
  var schema = this.populateEcommerceSchema(searchResultsBlock, true, searchResult)
63
63
 
64
64
  this.clearPreviousEcommerceObject()
65
- window.dataLayer.push(schema)
65
+ GOVUK.analyticsGa4.core.sendData(schema)
66
66
  }
67
67
  },
68
68
 
@@ -127,7 +127,7 @@
127
127
  },
128
128
 
129
129
  clearPreviousEcommerceObject: function () {
130
- window.dataLayer.push({ search_results: { ecommerce: null } })
130
+ GOVUK.analyticsGa4.core.sendData({ search_results: { ecommerce: null } })
131
131
  },
132
132
 
133
133
  getResultsCount: function (searchResultsBlock) {
@@ -36,6 +36,9 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
36
36
 
37
37
  this.$module.uniqueId = this.$module.getAttribute('data-id') || false
38
38
 
39
+ this.$module.dataModule = this.$module.getAttribute('data-module')
40
+ this.$module.isGa4Enabled = this.$module.dataModule ? this.$module.dataModule.indexOf('ga4-event-tracker') !== -1 : false
41
+
39
42
  if (this.$module.uniqueId) {
40
43
  this.$module.sessionStoreLink = this.$module.sessionStoreLink + '_' + this.$module.uniqueId
41
44
  }
@@ -78,6 +81,12 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
78
81
 
79
82
  this.$module.insertBefore(showAll, steps)
80
83
  this.$module.showOrHideAllButton = this.$module.querySelectorAll('.js-step-controls-button')[0]
84
+
85
+ // if GA4 is enabled, set attributes on 'show all sections' for tracking using ga4-event-tracker
86
+ if (this.$module.isGa4Enabled) {
87
+ var showAllAttributesGa4 = { event_name: 'select_content', type: 'step by step', index: 0, index_total: this.$module.totalSteps }
88
+ this.$module.showOrHideAllButton.setAttribute('data-ga4-event', JSON.stringify(showAllAttributesGa4))
89
+ }
81
90
  }
82
91
 
83
92
  Gemstepnav.prototype.addShowHideToggle = function () {
@@ -169,12 +178,24 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
169
178
  var title = thisel.querySelectorAll('.js-step-title')[0]
170
179
  var contentId = thisel.querySelectorAll('.js-panel')[0].getAttribute('id')
171
180
  var titleText = title.textContent || title.innerText // IE8 fallback
181
+ var ga4Data = ''
182
+
183
+ if (this.$module.isGa4Enabled) {
184
+ var ga4JSON = {
185
+ event_name: 'select_content',
186
+ type: 'step by step',
187
+ text: titleText.trim(),
188
+ index: i + 1,
189
+ index_total: this.$module.totalSteps
190
+ }
191
+ ga4Data = "data-ga4-event='" + JSON.stringify(ga4JSON) + "'" // Construct GA4 data-attributes for ga4-event-tracker.
192
+ }
172
193
 
173
194
  title.outerHTML =
174
195
  '<span class="js-step-title">' +
175
196
  '<button ' +
176
197
  'class="gem-c-step-nav__button gem-c-step-nav__button--title js-step-title-button" ' +
177
- 'aria-expanded="false" aria-controls="' + contentId + '">' +
198
+ 'aria-expanded="false" aria-controls="' + contentId + '" ' + ga4Data + '>' +
178
199
  '<span class="gem-c-step-nav____title-text-focus">' +
179
200
  '<span class="gem-c-step-nav__title-text js-step-title-text">' + titleText + '</span>' +
180
201
  '<span class="govuk-visually-hidden gem-c-step-nav__section-heading-divider">, </span>' +
@@ -38,6 +38,8 @@
38
38
  errorBeaconUrl: getProperty(obj, "errorBeaconUrl", "https://lux.speedcurve.com/error/"),
39
39
  jspagelabel: getProperty(obj, "jspagelabel", undefined),
40
40
  label: getProperty(obj, "label", undefined),
41
+ maxBeaconUrlLength: getProperty(obj, "maxBeaconUrlLength", 8190),
42
+ maxBeaconUTEntries: getProperty(obj, "maxBeaconUTEntries", 20),
41
43
  maxErrors: getProperty(obj, "maxErrors", 5),
42
44
  maxMeasureTime: getProperty(obj, "maxMeasureTime", 60000),
43
45
  measureUntil: getProperty(obj, "measureUntil", "onload"),
@@ -276,6 +278,23 @@
276
278
  return Matching;
277
279
  }());
278
280
 
281
+ /**
282
+ * Fit an array of user timing delimited strings into a URL and return both the entries that fit and
283
+ * the remaining entries that didn't fit.
284
+ */
285
+ function fitUserTimingEntries(utValues, config, url) {
286
+ // Start with the maximum allowed UT entries per beacon
287
+ var beaconUtValues = utValues.slice(0, config.maxBeaconUTEntries);
288
+ var remainingUtValues = utValues.slice(config.maxBeaconUTEntries);
289
+ // Trim UT entries until they fit within the maximum URL length, ensuring at least one UT entry
290
+ // is included.
291
+ while ((url + "&UT=" + beaconUtValues.join(",")).length > config.maxBeaconUrlLength &&
292
+ beaconUtValues.length > 1) {
293
+ remainingUtValues.unshift(beaconUtValues.pop());
294
+ }
295
+ return [beaconUtValues, remainingUtValues];
296
+ }
297
+
279
298
  var LUX = window.LUX || {};
280
299
  var scriptEndTime = scriptStartTime;
281
300
  LUX = (function () {
@@ -288,7 +307,7 @@
288
307
  /// End
289
308
  // -------------------------------------------------------------------------
290
309
 
291
- var SCRIPT_VERSION = "304";
310
+ var SCRIPT_VERSION = "305";
292
311
  var logger = new Logger();
293
312
  var globalConfig = fromObject(LUX);
294
313
  logger.logEvent(LogEvent.EvaluationStart, [SCRIPT_VERSION]);
@@ -382,7 +401,6 @@
382
401
  var gUid = refreshUniqueId(gSyncId); // cookie for this session ("Unique ID")
383
402
  var gCustomerDataTimeout; // setTimeout timer for sending a Customer Data beacon after onload
384
403
  var gMaxMeasureTimeout; // setTimeout timer for sending the beacon after a maximum measurement time
385
- var gMaxQuerystring = 8190; // split the beacon querystring if it gets longer than this
386
404
  if (_sample()) {
387
405
  logger.logEvent(LogEvent.SessionIsSampled, [globalConfig.samplerate]);
388
406
  }
@@ -701,7 +719,7 @@
701
719
  }
702
720
  aUT.push(utParts.join("|"));
703
721
  }
704
- return aUT.join(",");
722
+ return aUT;
705
723
  }
706
724
  // Return a string of Element Timing Metrics formatted for beacon querystring.
707
725
  function elementTimingValues() {
@@ -1406,8 +1424,28 @@
1406
1424
  clearTimeout(gMaxMeasureTimeout);
1407
1425
  }
1408
1426
  }
1427
+ function _getBeaconUrl() {
1428
+ var queryParams = [
1429
+ "v=" + SCRIPT_VERSION,
1430
+ "id=" + getCustomerId(),
1431
+ "sid=" + gSyncId,
1432
+ "uid=" + gUid,
1433
+ "l=" + encodeURIComponent(_getPageLabel()),
1434
+ "HN=" + encodeURIComponent(document.location.hostname),
1435
+ "PN=" + encodeURIComponent(document.location.pathname),
1436
+ ];
1437
+ if (gFlags) {
1438
+ queryParams.push("fl=" + gFlags);
1439
+ }
1440
+ var customerData = customerDataValues();
1441
+ if (customerData) {
1442
+ queryParams.push("CD=" + customerData);
1443
+ }
1444
+ return globalConfig.beaconUrl + "?" + queryParams.join("&");
1445
+ }
1409
1446
  // Beacon back the LUX data.
1410
1447
  function _sendLux() {
1448
+ var _a;
1411
1449
  clearMaxMeasureTimeout();
1412
1450
  var customerid = getCustomerId();
1413
1451
  if (!customerid ||
@@ -1425,9 +1463,7 @@
1425
1463
  // with LUX.markLoadTime()
1426
1464
  _markLoadTime();
1427
1465
  }
1428
- var sUT = userTimingValues(); // User Timing data
1429
1466
  var sET = elementTimingValues(); // Element Timing data
1430
- var sCustomerData = customerDataValues(); // customer data
1431
1467
  var sIx = ""; // Interaction Metrics
1432
1468
  if (!gbIxSent) {
1433
1469
  // It is possible for the IX beacon to be sent BEFORE the "main" window.onload LUX beacon.
@@ -1442,21 +1478,10 @@
1442
1478
  }
1443
1479
  // We want ALL beacons to have ALL the data used for query filters (geo, pagelabel, browser, & customerdata).
1444
1480
  // So we create a base URL that has all the necessary information:
1445
- var baseUrl = globalConfig.beaconUrl +
1446
- "?v=" +
1447
- SCRIPT_VERSION +
1448
- "&id=" +
1449
- customerid +
1450
- "&sid=" +
1451
- gSyncId +
1452
- "&uid=" +
1453
- gUid +
1454
- (sCustomerData ? "&CD=" + sCustomerData : "") +
1455
- "&l=" +
1456
- encodeURIComponent(_getPageLabel());
1481
+ var baseUrl = _getBeaconUrl();
1457
1482
  var is = inlineTagSize("script");
1458
1483
  var ic = inlineTagSize("style");
1459
- var querystring =
1484
+ var metricsQueryString =
1460
1485
  // only send Nav Timing and lux.js metrics on initial pageload (not for SPA page views)
1461
1486
  (gbNavSent ? "" : "&NT=" + getNavTiming()) +
1462
1487
  (gbFirstPV ? "&LJS=" + sLuxjs : "") +
@@ -1497,31 +1522,15 @@
1497
1522
  (sIx ? "&IX=" + sIx : "") +
1498
1523
  (typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
1499
1524
  (sCPU ? "&CPU=" + sCPU : "") +
1500
- (gFlags ? "&fl=" + gFlags : "") +
1501
1525
  (sET ? "&ET=" + sET : "") + // element timing
1502
- "&HN=" +
1503
- encodeURIComponent(document.location.hostname) +
1504
- (DCLS !== false ? "&CLS=" + DCLS : "") +
1505
- "&PN=" +
1506
- encodeURIComponent(document.location.pathname);
1507
- // User Timing marks & measures
1508
- var sUT_remainder = "";
1509
- if (sUT) {
1510
- var curLen = baseUrl.length + querystring.length;
1511
- if (curLen + sUT.length <= gMaxQuerystring) {
1512
- // Add all User Timing
1513
- querystring += "&UT=" + sUT;
1514
- }
1515
- else {
1516
- // Only add a substring of User Timing
1517
- var avail_1 = gMaxQuerystring - curLen; // how much room is left in the querystring
1518
- var iComma = sUT.lastIndexOf(",", avail_1); // as many UT tuples as possible
1519
- querystring += "&UT=" + sUT.substring(0, iComma);
1520
- sUT_remainder = sUT.substring(iComma + 1);
1521
- }
1522
- }
1526
+ (DCLS !== false ? "&CLS=" + DCLS : "");
1527
+ // We add the user timing entries last so that we can split them to reduce the URL size if necessary.
1528
+ var utValues = userTimingValues();
1529
+ var _b = fitUserTimingEntries(utValues, globalConfig, baseUrl + metricsQueryString), beaconUtValues = _b[0], remainingUtValues = _b[1];
1523
1530
  // Send the MAIN LUX beacon.
1524
- var mainBeaconUrl = baseUrl + querystring;
1531
+ var mainBeaconUrl = baseUrl +
1532
+ metricsQueryString +
1533
+ (beaconUtValues.length > 0 ? "&UT=" + beaconUtValues.join(",") : "");
1525
1534
  logger.logEvent(LogEvent.MainBeaconSent, [mainBeaconUrl]);
1526
1535
  _sendBeacon(mainBeaconUrl);
1527
1536
  // Set some states.
@@ -1529,34 +1538,9 @@
1529
1538
  gbNavSent = 1;
1530
1539
  gbIxSent = sIx ? 1 : 0;
1531
1540
  // Send other beacons for JUST User Timing.
1532
- var avail = gMaxQuerystring - baseUrl.length;
1533
- while (sUT_remainder) {
1534
- var sUT_cur = "";
1535
- if (sUT_remainder.length <= avail) {
1536
- // We can fit ALL the remaining UT params.
1537
- sUT_cur = sUT_remainder;
1538
- sUT_remainder = "";
1539
- }
1540
- else {
1541
- // We have to take a subset of the remaining UT params.
1542
- var iComma = sUT_remainder.lastIndexOf(",", avail); // as many UT tuples as possible
1543
- if (-1 === iComma) {
1544
- // Trouble: we have SO LITTLE available space we can not fit the first UT tuple.
1545
- // Try it anyway but find it by searching from the front.
1546
- iComma = sUT_remainder.indexOf(",");
1547
- }
1548
- if (-1 === iComma) {
1549
- // The is only one UT tuple left, but it is bigger than the available space.
1550
- // Take the whole tuple even tho it is too big.
1551
- sUT_cur = sUT_remainder;
1552
- sUT_remainder = "";
1553
- }
1554
- else {
1555
- sUT_cur = sUT_remainder.substring(0, iComma);
1556
- sUT_remainder = sUT_remainder.substring(iComma + 1);
1557
- }
1558
- }
1559
- var utBeaconUrl = baseUrl + "&UT=" + sUT_cur;
1541
+ while (remainingUtValues.length) {
1542
+ _a = fitUserTimingEntries(remainingUtValues, globalConfig, baseUrl), beaconUtValues = _a[0], remainingUtValues = _a[1];
1543
+ var utBeaconUrl = baseUrl + "&UT=" + beaconUtValues.join(",");
1560
1544
  logger.logEvent(LogEvent.UserTimingBeaconSent, [utBeaconUrl]);
1561
1545
  _sendBeacon(utBeaconUrl);
1562
1546
  }
@@ -1574,26 +1558,10 @@
1574
1558
  }
1575
1559
  var sIx = ixValues(); // Interaction Metrics
1576
1560
  if (sIx) {
1577
- var sCustomerData = customerDataValues(); // customer data
1578
- var querystring = "?v=" +
1579
- SCRIPT_VERSION +
1580
- "&id=" +
1581
- customerid +
1582
- "&sid=" +
1583
- gSyncId +
1584
- "&uid=" +
1585
- gUid +
1586
- (sCustomerData ? "&CD=" + sCustomerData : "") +
1587
- "&l=" +
1588
- encodeURIComponent(_getPageLabel()) +
1589
- "&IX=" +
1590
- sIx +
1591
- (gFirstInputDelay ? "&FID=" + gFirstInputDelay : "") +
1592
- "&HN=" +
1593
- encodeURIComponent(document.location.hostname) +
1594
- "&PN=" +
1595
- encodeURIComponent(document.location.pathname);
1596
- var beaconUrl = globalConfig.beaconUrl + querystring;
1561
+ var beaconUrl = _getBeaconUrl() +
1562
+ "&IX=" +
1563
+ sIx +
1564
+ (typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "");
1597
1565
  logger.logEvent(LogEvent.InteractionBeaconSent, [beaconUrl]);
1598
1566
  _sendBeacon(beaconUrl);
1599
1567
  gbIxSent = 1;
@@ -1612,23 +1580,7 @@
1612
1580
  }
1613
1581
  var sCustomerData = customerDataValues(); // customer data
1614
1582
  if (sCustomerData) {
1615
- var querystring = "?v=" +
1616
- SCRIPT_VERSION +
1617
- "&id=" +
1618
- customerid +
1619
- "&sid=" +
1620
- gSyncId +
1621
- "&uid=" +
1622
- gUid +
1623
- "&CD=" +
1624
- sCustomerData +
1625
- "&l=" +
1626
- encodeURIComponent(_getPageLabel()) +
1627
- "&HN=" +
1628
- encodeURIComponent(document.location.hostname) +
1629
- "&PN=" +
1630
- encodeURIComponent(document.location.pathname);
1631
- var beaconUrl = globalConfig.beaconUrl + querystring;
1583
+ var beaconUrl = _getBeaconUrl();
1632
1584
  logger.logEvent(LogEvent.CustomDataBeaconSent, [beaconUrl]);
1633
1585
  _sendBeacon(beaconUrl);
1634
1586
  }
@@ -15,10 +15,13 @@
15
15
  step_number = 0
16
16
 
17
17
  step_nav_helper = GovukPublishingComponents::Presenters::StepByStepNavHelper.new
18
+
19
+ ga4_tracking ||= false
18
20
  %>
19
21
  <% if steps %>
20
22
  <div
21
- data-module="gemstepnav"
23
+ data-module="gemstepnav<% if ga4_tracking %> ga4-event-tracker<% end %>"
24
+ <%= "data-ga4-expandable" if ga4_tracking %>
22
25
  class="gem-c-step-nav js-hidden <% if small %>govuk-!-display-none-print<% end %> <% unless small %>gem-c-step-nav--large<% end %>"
23
26
  <%= "data-remember" if remember_last_step %>
24
27
  <%= "data-id=#{tracking_id}" if tracking_id %>
@@ -72,6 +72,40 @@ examples:
72
72
  ]
73
73
  }
74
74
  ]
75
+ with_google_analytics_4_tracking:
76
+ description: |
77
+ The ga4_tracking boolean allows you to add Google Analytics 4 (GA4) tracking to your component.
78
+ Setting this to true will add the `ga4-event-tracker` module to your component. The JavaScript will then add a `data-ga4-event` attribute and `data-ga4-expandable` attribute to the "Show all steps" button, and each clickable Step heading.
79
+ GA4 will then track these elements when they are expanded or collapsed.
80
+ `data-ga4-event` contains a JSON with event data relevant to our tracking. `data-ga4-expandable` enables the value of `aria-expanded` to be captured.
81
+ See the [ga4-event-tracker](https://github.com/alphagov/govuk_publishing_components/blob/main/docs/analytics-ga4/ga4-event-tracker.md) docs for more information.
82
+ data:
83
+ ga4_tracking: true
84
+ steps: [
85
+ {
86
+ title: "Do something",
87
+ contents: [
88
+ {
89
+ type: 'paragraph',
90
+ text: 'This is a paragraph of text.'
91
+ },
92
+ {
93
+ type: 'paragraph',
94
+ text: 'This is also a paragraph of text that intentionally contains lots of words in order to fill the width of the page successfully to check layout and so forth.'
95
+ }
96
+ ],
97
+ },
98
+ {
99
+ title: "Do something thing else",
100
+ logic: "and",
101
+ contents: [
102
+ {
103
+ type: 'paragraph',
104
+ text: 'Some more text.'
105
+ },
106
+ ]
107
+ },
108
+ ]
75
109
  with_unique_tracking:
76
110
  description: |
77
111
  In order to identify the step by step navigation the component is part of, we need to track a unique ID of the navigation in Google Analytics. This ID should be the same across all pages that are linked from and are part of that navigation. Its value is included in any tracking events, specifically in dimension96. It refers to the ID of the step nav page.
@@ -41,7 +41,7 @@
41
41
  <%= render "govuk_publishing_components/components/button", {
42
42
  text: t("components.feedback.send"),
43
43
  data_attributes: {
44
- ga4: {
44
+ ga4_event: {
45
45
  event_name: "form_submit",
46
46
  type: "feedback",
47
47
  text: "Send",
@@ -28,7 +28,7 @@
28
28
  <%= render "govuk_publishing_components/components/button", {
29
29
  text: t("components.feedback.send_me_survey"),
30
30
  data_attributes: {
31
- ga4: {
31
+ ga4_event: {
32
32
  event_name: "form_submit",
33
33
  type: "feedback",
34
34
  text: "Send me the survey",
@@ -38,7 +38,8 @@ module GovukPublishingComponents
38
38
  @info_text_classes << margin_class
39
39
  end
40
40
  @rel = local_assigns[:rel]
41
- @data_attributes = local_assigns[:data_attributes]
41
+ @data_attributes = local_assigns[:data_attributes]&.symbolize_keys || {}
42
+ @data_attributes[:module] = "govuk-button #{data_attributes[:module]}".strip if link?
42
43
  @margin_bottom = local_assigns[:margin_bottom]
43
44
  @inline_layout = local_assigns[:inline_layout]
44
45
  @target = local_assigns[:target]
@@ -1,3 +1,3 @@
1
1
  module GovukPublishingComponents
2
- VERSION = "33.0.0".freeze
2
+ VERSION = "33.1.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_publishing_components
3
3
  version: !ruby/object:Gem::Version
4
- version: 33.0.0
4
+ version: 33.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-29 00:00:00.000000000 Z
11
+ date: 2022-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: govuk_app_config