govuk_publishing_components 35.7.0 → 35.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (21) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +1 -1
  3. data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +206 -114
  4. data/app/assets/stylesheets/govuk_publishing_components/components/_document-list.scss +5 -9
  5. data/app/assets/stylesheets/govuk_publishing_components/components/_layout-for-public.scss +4 -0
  6. data/app/views/govuk_publishing_components/audit/show.html.erb +2 -1
  7. data/app/views/govuk_publishing_components/components/_accordion.html.erb +4 -3
  8. data/app/views/govuk_publishing_components/components/_document_list.html.erb +8 -7
  9. data/app/views/govuk_publishing_components/components/_layout_for_public.html.erb +9 -2
  10. data/app/views/govuk_publishing_components/components/_metadata.html.erb +20 -14
  11. data/app/views/govuk_publishing_components/components/_single_page_notification_button.html.erb +21 -9
  12. data/app/views/govuk_publishing_components/components/_step_by_step_nav_related.html.erb +2 -2
  13. data/app/views/govuk_publishing_components/components/docs/document_list.yml +17 -0
  14. data/app/views/govuk_publishing_components/components/docs/layout_for_public.yml +6 -0
  15. data/app/views/govuk_publishing_components/components/docs/notice.yml +1 -1
  16. data/app/views/govuk_publishing_components/components/docs/single_page_notification_button.yml +13 -0
  17. data/lib/govuk_publishing_components/app_helpers/asset_helper.rb +1 -1
  18. data/lib/govuk_publishing_components/presenters/public_layout_helper.rb +5 -0
  19. data/lib/govuk_publishing_components/presenters/single_page_notification_button_helper.rb +9 -1
  20. data/lib/govuk_publishing_components/version.rb +1 -1
  21. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19aaa8a186568f17fc86baced60aab3d35f790e35f819702736f6dc861f64bcb
4
- data.tar.gz: 947c684323444fb20d04063a801f71868193ca46ee7dfba877a26b05c235ae5f
3
+ metadata.gz: 619cac6fc6ecb8078d66be77a50e1fe4d5f320b61a15e574fbd92a892d896d80
4
+ data.tar.gz: d339abcad29fe3e59dafe2157e3ba605c8817563797b30bead89650ebe5d278f
5
5
  SHA512:
6
- metadata.gz: 4c151b005305fa534de28d3769131a41334c3a39b64e89a8f737f6e59747ba03d28d24e62fcdf122fb9b290c5a3d3596842aa973100a208144c83280dcf12fb1
7
- data.tar.gz: 1ef830fa00101728968b9bc2e0d865f923cf00cbd265b33d5e8958f18539938146a1b802e6e6d42a0ee269fc2be3664ba89734d67dbcabef04187a46caa22df8
6
+ metadata.gz: a163d27c8f13d7019869a16ac1f0c58c4c71e88be7c456e1639cb717bdf3f0da4c483ae7336824ed288d2888e5f0e671e1b1120fe910fa75d838aa85da998f98
7
+ data.tar.gz: 87d03d1a7c4f50d4065b501c8f415831f218f28e2dd28ebfe61d87d270162ae595899ba46387d5bed9cc3b9472fbc7d47255fd573354bcfcc8477262aa574427
@@ -82,7 +82,7 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
82
82
  var text = data.text || event.target.textContent
83
83
  data.text = window.GOVUK.analyticsGa4.core.trackFunctions.removeLinesAndExtraSpaces(text)
84
84
  data.text = this.PIIRemover.stripPIIWithOverride(data.text, true, true)
85
- if (!data.text && (element.querySelector('img') || element.querySelector('svg'))) {
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')
@@ -42,7 +42,6 @@
42
42
  var autoMode = getProperty(obj, "auto", true);
43
43
  return {
44
44
  auto: autoMode,
45
- autoWhenHidden: getProperty(obj, "autoWhenHidden", false),
46
45
  beaconUrl: getProperty(obj, "beaconUrl", "https://lux.speedcurve.com/lux/"),
47
46
  conversions: getProperty(obj, "conversions", undefined),
48
47
  customerid: getProperty(obj, "customerid", undefined),
@@ -54,9 +53,12 @@
54
53
  maxErrors: getProperty(obj, "maxErrors", 5),
55
54
  maxMeasureTime: getProperty(obj, "maxMeasureTime", 60000),
56
55
  minMeasureTime: getProperty(obj, "minMeasureTime", 0),
56
+ newBeaconOnPageShow: getProperty(obj, "newBeaconOnPageShow", false),
57
57
  samplerate: getProperty(obj, "samplerate", 100),
58
58
  sendBeaconOnPageHidden: getProperty(obj, "sendBeaconOnPageHidden", autoMode),
59
+ serverTiming: getProperty(obj, "serverTiming", undefined),
59
60
  trackErrors: getProperty(obj, "trackErrors", true),
61
+ trackHiddenPages: getProperty(obj, "trackHiddenPages", false),
60
62
  pagegroups: getProperty(obj, "pagegroups", undefined),
61
63
  };
62
64
  }
@@ -102,9 +104,9 @@
102
104
  function valuesToString(values) {
103
105
  var strings = [];
104
106
  for (var key in values) {
105
- // Convert all values to strings
107
+ // Convert all values to strings
106
108
  var value = "" + values[key];
107
- // Strip out reserved characters (, and | are used as delimiters)
109
+ // Strip out reserved characters (, and | are used as delimiters)
108
110
  key = key.replace(/,/g, "").replace(/\|/g, "");
109
111
  value = value.replace(/,/g, "").replace(/\|/g, "");
110
112
  strings.push(key + "|" + value);
@@ -115,6 +117,13 @@
115
117
  function floor(x) {
116
118
  return Math.floor(x);
117
119
  }
120
+ var max = Math.max;
121
+ /**
122
+ * Clamp a number so that it is never less than 0
123
+ */
124
+ function clamp(x) {
125
+ return max(0, x);
126
+ }
118
127
 
119
128
  function now() {
120
129
  return Date.now ? Date.now() : +new Date();
@@ -147,10 +156,13 @@
147
156
  function getNavigationEntry() {
148
157
  var navEntries = getEntriesByType("navigation");
149
158
  if (navEntries.length) {
150
- var entry_1 = navEntries[0];
151
- entry_1.navigationStart = 0;
152
- if (typeof entry_1.activationStart === "undefined") {
153
- entry_1.activationStart = 0;
159
+ var nativeEntry = navEntries[0];
160
+ var entry_1 = {
161
+ navigationStart: 0,
162
+ activationStart: 0,
163
+ };
164
+ for (var key in nativeEntry) {
165
+ entry_1[key] = nativeEntry[key];
154
166
  }
155
167
  return entry_1;
156
168
  }
@@ -164,7 +176,7 @@
164
176
  if (__ENABLE_POLYFILLS) {
165
177
  for (var key in timing) {
166
178
  if (typeof timing[key] === "number" && key !== "navigationStart") {
167
- entry[key] = Math.max(0, timing[key] - timing.navigationStart);
179
+ entry[key] = floor(timing[key] - timing.navigationStart);
168
180
  }
169
181
  }
170
182
  }
@@ -189,26 +201,39 @@
189
201
  if (document.visibilityState) {
190
202
  return document.visibilityState === "visible";
191
203
  }
192
- // For browsers that don't support document.visibilityState, we assume the page is visible.
204
+ // For browsers that don't support document.visibilityState, we assume the page is visible.
193
205
  return true;
194
206
  }
195
207
  function onVisible(cb) {
196
- if (isVisible()) {
197
- cb();
208
+ afterPrerender(function () {
209
+ if (isVisible()) {
210
+ cb();
211
+ }
212
+ else {
213
+ var onVisibleCallback_1 = function () {
214
+ if (isVisible()) {
215
+ cb();
216
+ removeEventListener("visibilitychange", onVisibleCallback_1);
217
+ }
218
+ };
219
+ addEventListener("visibilitychange", onVisibleCallback_1, true);
220
+ }
221
+ });
222
+ }
223
+ function afterPrerender(cb) {
224
+ if (document.prerendering) {
225
+ document.addEventListener("prerenderingchange", cb, true);
198
226
  }
199
227
  else {
200
- var onVisibleCallback_1 = function () {
201
- if (isVisible()) {
202
- cb();
203
- removeEventListener("visibilitychange", onVisibleCallback_1);
204
- }
205
- };
206
- addEventListener("visibilitychange", onVisibleCallback_1, true);
228
+ cb();
207
229
  }
208
230
  }
209
231
  function wasPrerendered() {
210
232
  return document.prerendering || getNavigationEntry().activationStart > 0;
211
233
  }
234
+ function wasRedirected() {
235
+ return getNavigationEntry().redirectCount > 0 || timing.redirectEnd > 0;
236
+ }
212
237
 
213
238
  var Flags = {
214
239
  InitCalled: 1 << 0,
@@ -222,6 +247,7 @@
222
247
  PageLabelFromGlobalVariable: 1 << 8,
223
248
  PageLabelFromPagegroup: 1 << 9,
224
249
  PageWasPrerendered: 1 << 10,
250
+ PageWasBfCacheRestored: 1 << 11,
225
251
  };
226
252
  function addFlag(flags, flag) {
227
253
  return flags | flag;
@@ -235,8 +261,8 @@
235
261
  }
236
262
 
237
263
  /**
238
- * Get the interaction attribution name for an element
239
- */
264
+ * Get the interaction attribution name for an element
265
+ */
240
266
  function interactionAttributionForElement(el) {
241
267
  // Our first preference is to use the data-sctrack attribute from anywhere in the tree
242
268
  var trackId = getClosestScTrackAttribute(el);
@@ -291,6 +317,7 @@
291
317
  UnloadHandlerTriggered: 10,
292
318
  OnloadHandlerTriggered: 11,
293
319
  MarkLoadTimeCalled: 12,
320
+ SendCancelledPageHidden: 13,
294
321
  // Data collection events
295
322
  SessionIsSampled: 21,
296
323
  SessionIsNotSampled: 22,
@@ -315,39 +342,45 @@
315
342
  PaintTimingNotSupported: 72,
316
343
  };
317
344
  var Logger = /** @class */ (function () {
318
- function Logger() {
319
- this.events = [];
320
- }
321
- Logger.prototype.logEvent = function (event, args) {
322
- if (args === void 0) { args = []; }
323
- this.events.push([now(), event, args]);
324
- };
325
- Logger.prototype.getEvents = function () {
326
- return this.events;
327
- };
328
- return Logger;
345
+ function Logger() {
346
+ this.events = [];
347
+ }
348
+ Logger.prototype.logEvent = function (event, args) {
349
+ if (args === void 0) { args = []; }
350
+ this.events.push([now(), event, args]);
351
+ };
352
+ Logger.prototype.getEvents = function () {
353
+ return this.events;
354
+ };
355
+ return Logger;
329
356
  }());
330
357
  var sessionValue = 0;
331
358
  var sessionEntries = [];
359
+ var maximumSessionValue = 0;
332
360
  function addEntry$2(entry) {
333
361
  if (!entry.hadRecentInput) {
334
362
  var firstEntry = sessionEntries[0];
335
363
  var latestEntry = sessionEntries[sessionEntries.length - 1];
336
364
  if (sessionEntries.length &&
337
365
  (entry.startTime - latestEntry.startTime >= 1000 ||
338
- entry.startTime - firstEntry.startTime >= 5000)) {
339
- reset$1();
366
+ entry.startTime - firstEntry.startTime >= 5000)) {
367
+ sessionValue = entry.value;
368
+ sessionEntries = [entry];
340
369
  }
341
- sessionValue += entry.value;
342
- sessionEntries.push(entry);
370
+ else {
371
+ sessionValue += entry.value;
372
+ sessionEntries.push(entry);
373
+ }
374
+ maximumSessionValue = max(maximumSessionValue, sessionValue);
343
375
  }
344
376
  }
345
377
  function reset$1() {
346
378
  sessionValue = 0;
347
379
  sessionEntries = [];
380
+ maximumSessionValue = 0;
348
381
  }
349
382
  function getCLS() {
350
- return sessionValue;
383
+ return maximumSessionValue;
351
384
  }
352
385
 
353
386
  /**
@@ -372,7 +405,7 @@
372
405
  var duration = entry.duration, startTime = entry.startTime, interactionId = entry.interactionId;
373
406
  var existingEntry = slowestEntriesMap[interactionId];
374
407
  if (existingEntry) {
375
- existingEntry.duration = Math.max(duration, existingEntry.duration);
408
+ existingEntry.duration = max(duration, existingEntry.duration);
376
409
  }
377
410
  else {
378
411
  interactionCountEstimate++;
@@ -405,44 +438,6 @@
405
438
  return interactionCountEstimate;
406
439
  }
407
440
 
408
- /**
409
- * Get the number of milliseconds between navigationStart and the given PerformanceNavigationTiming key
410
- */
411
- function getNavTimingValue(key) {
412
- var navEntry = getNavigationEntry();
413
- var relativeTo = key === "activationStart" ? 0 : navEntry.activationStart;
414
- if (typeof navEntry[key] === "number") {
415
- return Math.max(0, navEntry[key] - relativeTo);
416
- }
417
- return undefined;
418
- }
419
-
420
- /******************************************************************************
421
- Copyright (c) Microsoft Corporation.
422
-
423
- Permission to use, copy, modify, and/or distribute this software for any
424
- purpose with or without fee is hereby granted.
425
-
426
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
427
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
428
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
429
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
430
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
431
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
432
- PERFORMANCE OF THIS SOFTWARE.
433
- ***************************************************************************** */
434
-
435
- var __assign = function() {
436
- __assign = Object.assign || function __assign(t) {
437
- for (var s, i = 1, n = arguments.length; i < n; i++) {
438
- s = arguments[i];
439
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
440
- }
441
- return t;
442
- };
443
- return __assign.apply(this, arguments);
444
- };
445
-
446
441
  var ALL_ENTRIES = [];
447
442
  function observe(type, callback, options) {
448
443
  if (typeof PerformanceObserver === "function" &&
@@ -450,10 +445,10 @@
450
445
  var po = new PerformanceObserver(function (list) {
451
446
  list.getEntries().forEach(function (entry) { return callback(entry); });
452
447
  });
453
- po.observe(__assign({ type: type, buffered: true }, options));
454
- return po;
455
- }
456
- return undefined;
448
+ po.observe(Object.assign({ type: type, buffered: true }, { options: options }));
449
+ return po;
450
+ }
451
+ return undefined;
457
452
  }
458
453
  function getEntries(type) {
459
454
  return ALL_ENTRIES.filter(function (entry) { return entry.entryType === type; });
@@ -465,6 +460,39 @@
465
460
  ALL_ENTRIES.splice(0);
466
461
  }
467
462
 
463
+ /**
464
+ * A server timing metric that has its value set to the duration field
465
+ */
466
+ var TYPE_DURATION = "r";
467
+ /**
468
+ * When a description metric has no value, we consider it to be a boolean and set it to this value.
469
+ */
470
+ var BOOLEAN_TRUE_VALUE = "true";
471
+ function getKeyValuePairs(config, serverTiming) {
472
+ var pairs = {};
473
+ serverTiming.forEach(function (stEntry) {
474
+ var name = stEntry.name;
475
+ var description = stEntry.description;
476
+ if (name in config) {
477
+ var spec = config[name];
478
+ var multiplier = spec[1];
479
+ if (spec[0] === TYPE_DURATION) {
480
+ pairs[name] = stEntry.duration * (multiplier || 1);
481
+ }
482
+ else if (description && multiplier) {
483
+ var numericValue = parseFloat(description);
484
+ if (!isNaN(numericValue)) {
485
+ pairs[name] = numericValue * multiplier;
486
+ }
487
+ }
488
+ else {
489
+ pairs[name] = description || BOOLEAN_TRUE_VALUE;
490
+ }
491
+ }
492
+ });
493
+ return pairs;
494
+ }
495
+
468
496
  function getMatchesFromPatternMap(patternMap, hostname, pathname, firstOnly) {
469
497
  var matches = [];
470
498
  for (var key in patternMap) {
@@ -496,7 +524,7 @@
496
524
  return regex.test(hostname + pathname);
497
525
  }
498
526
  function createRegExpFromPattern(pattern) {
499
- return new RegExp("^" + escapeStringForRegExp(pattern).replaceAll("*", ".*") + "$", "i");
527
+ return new RegExp("^" + escapeStringForRegExp(pattern).replace(/\*/g, ".*") + "$", "i");
500
528
  }
501
529
  function escapeStringForRegExp(str) {
502
530
  // Note: we don't escape * because it's our own special symbol!
@@ -514,7 +542,7 @@
514
542
  // -------------------------------------------------------------------------
515
543
  /// End
516
544
  // -------------------------------------------------------------------------
517
- var SCRIPT_VERSION = "308";
545
+ var SCRIPT_VERSION = "309";
518
546
  var logger = new Logger();
519
547
  var globalConfig = fromObject(LUX);
520
548
  logger.logEvent(LogEvent.EvaluationStart, [SCRIPT_VERSION]);
@@ -585,7 +613,7 @@
585
613
  observe("first-input", function (entry) {
586
614
  var fid = entry.processingStart - entry.startTime;
587
615
  if (!gFirstInputDelay || gFirstInputDelay < fid) {
588
- gFirstInputDelay = fid;
616
+ gFirstInputDelay = floor(fid);
589
617
  }
590
618
  // Allow first-input events to be considered for INP
591
619
  addEntry$1(entry);
@@ -612,7 +640,13 @@
612
640
  var gUid = refreshUniqueId(gSyncId); // cookie for this session ("Unique ID")
613
641
  var gCustomerDataTimeout; // setTimeout timer for sending a Customer Data beacon after onload
614
642
  var gMaxMeasureTimeout; // setTimeout timer for sending the beacon after a maximum measurement time
615
- var navEntry = getNavigationEntry();
643
+ var pageRestoreTime; // ms since navigationStart representing when the page was restored from the bfcache
644
+ /**
645
+ * To measure the way a user experienced a metric, we measure metrics relative to the time the user
646
+ * started viewing the page. On prerendered pages, this is activationStart. On bfcache restores, this
647
+ * is the page restore time. On all other pages this value will be zero.
648
+ */
649
+ var getZeroTime = function () { return pageRestoreTime || getNavigationEntry().activationStart; };
616
650
  if (_sample()) {
617
651
  logger.logEvent(LogEvent.SessionIsSampled, [globalConfig.samplerate]);
618
652
  }
@@ -713,7 +747,7 @@
713
747
  var startMark = _getMark(START_MARK);
714
748
  // For SPA page views, we use our internal mark as a reference point
715
749
  if (startMark && !absolute) {
716
- return sinceNavigationStart - startMark.startTime;
750
+ return floor(sinceNavigationStart - startMark.startTime);
717
751
  }
718
752
  // For "regular" page views, we can use performance.now() if it's available...
719
753
  return sinceNavigationStart;
@@ -796,6 +830,7 @@
796
830
  }
797
831
  // ...Otherwise provide a polyfill
798
832
  if (__ENABLE_POLYFILLS) {
833
+ var navEntry = getNavigationEntry();
799
834
  var startTime = typeof startMarkName === "number" ? startMarkName : 0;
800
835
  var endTime = typeof endMarkName === "number" ? endMarkName : _now();
801
836
  var throwError = function (missingMark) {
@@ -906,7 +941,7 @@
906
941
  hUT[name] = { startTime: startTime };
907
942
  }
908
943
  else {
909
- hUT[name].startTime = Math.max(startTime, hUT[name].startTime);
944
+ hUT[name].startTime = max(startTime, hUT[name].startTime);
910
945
  }
911
946
  });
912
947
  // measures
@@ -939,7 +974,7 @@
939
974
  function elementTimingValues() {
940
975
  var aET = [];
941
976
  var startMark = _getMark(START_MARK);
942
- var tZero = startMark ? startMark.startTime : 0;
977
+ var tZero = startMark ? startMark.startTime : getZeroTime();
943
978
  getEntries("element").forEach(function (entry) {
944
979
  if (entry.identifier && entry.startTime) {
945
980
  logger.logEvent(LogEvent.PerformanceEntryProcessed, [entry]);
@@ -1164,11 +1199,11 @@
1164
1199
  var nThis = ("" + gUid).substr(-2); // number for THIS page - from 00 to 99
1165
1200
  return parseInt(nThis) < globalConfig.samplerate;
1166
1201
  }
1167
- // _init()
1168
- // Use this function in Single Page Apps to reset things.
1169
- // This function should ONLY be called within a SPA!
1170
- // Otherwise, you might clear marks & measures that were set by a shim.
1171
- function _init() {
1202
+ /**
1203
+ * Re-initialize lux.js to start a new "page". This is typically called within a SPA at the
1204
+ * beginning of a page transition, but is also called internally when the BF cache is restored.
1205
+ */
1206
+ function _init(startTime) {
1172
1207
  // Some customers (incorrectly) call LUX.init on the very first page load of a SPA. This would
1173
1208
  // cause some first-page-only data (like paint metrics) to be lost. To prevent this, we silently
1174
1209
  // bail from this function when we detect an unnecessary LUX.init call.
@@ -1176,6 +1211,14 @@
1176
1211
  if (!endMark) {
1177
1212
  return;
1178
1213
  }
1214
+ // Mark the "navigationStart" for this SPA page. A start time can be passed through, for example
1215
+ // to set a page's start time as an event timestamp.
1216
+ if (startTime) {
1217
+ _mark(START_MARK, { startTime: startTime });
1218
+ }
1219
+ else {
1220
+ _mark(START_MARK);
1221
+ }
1179
1222
  logger.logEvent(LogEvent.InitCalled);
1180
1223
  // Clear all interactions from the previous "page".
1181
1224
  _clearIx();
@@ -1197,8 +1240,6 @@
1197
1240
  // Clear flags then set the flag that init was called (ie, this is a SPA).
1198
1241
  gFlags = 0;
1199
1242
  gFlags = addFlag(gFlags, Flags.InitCalled);
1200
- // Mark the "navigationStart" for this SPA page.
1201
- _mark(START_MARK);
1202
1243
  // Reset the maximum measure timeout
1203
1244
  createMaxMeasureTimeout();
1204
1245
  }
@@ -1305,8 +1346,9 @@
1305
1346
  var ns = timing.navigationStart;
1306
1347
  var startMark = _getMark(START_MARK);
1307
1348
  var endMark = _getMark(END_MARK);
1308
- if (startMark && endMark) {
1349
+ if (startMark && endMark && !pageRestoreTime) {
1309
1350
  // This is a SPA page view, so send the SPA marks & measures instead of Nav Timing.
1351
+ // Note: pageRestoreTime indicates this was a bfcache restore, which we don't want to treat as a SPA.
1310
1352
  var start = floor(startMark.startTime); // the start mark is "zero"
1311
1353
  ns += start; // "navigationStart" for a SPA is the real navigationStart plus the start mark
1312
1354
  var end = floor(endMark.startTime) - start; // delta from start mark
@@ -1322,21 +1364,33 @@
1322
1364
  }
1323
1365
  else if (performance.timing) {
1324
1366
  // Return the real Nav Timing metrics because this is the "main" page view (not a SPA)
1367
+ var navEntry_1 = getNavigationEntry();
1325
1368
  var startRender = getStartRender();
1326
1369
  var fcp = getFcp();
1327
1370
  var lcp = getLcp();
1328
1371
  var prefixNTValue = function (key, prefix) {
1329
- var value = getNavTimingValue(key);
1330
- if (typeof value === "undefined") {
1331
- return "";
1372
+ // activationStart is always absolute. Other values are relative to activationStart.
1373
+ var zero = key === "activationStart" ? 0 : getZeroTime();
1374
+ if (typeof navEntry_1[key] === "number") {
1375
+ var value = clamp(floor(navEntry_1[key] - zero));
1376
+ return prefix + value;
1332
1377
  }
1333
- return prefix + value;
1378
+ return "";
1334
1379
  };
1380
+ var loadEventStartStr = prefixNTValue("loadEventStart", "ls");
1381
+ var loadEventEndStr = prefixNTValue("loadEventEnd", "le");
1382
+ if (pageRestoreTime && startMark && endMark) {
1383
+ // For bfcache restores, we set the load time to the time it took for the page to be restored.
1384
+ var loadTime = floor(endMark.startTime - startMark.startTime);
1385
+ loadEventStartStr = "ls" + loadTime;
1386
+ loadEventEndStr = "le" + loadTime;
1387
+ }
1388
+ var redirect = wasRedirected();
1335
1389
  s = [
1336
1390
  ns,
1337
1391
  prefixNTValue("activationStart", "as"),
1338
- prefixNTValue("redirectStart", "rs"),
1339
- prefixNTValue("redirectEnd", "re"),
1392
+ redirect ? prefixNTValue("redirectStart", "rs") : "",
1393
+ redirect ? prefixNTValue("redirectEnd", "re") : "",
1340
1394
  prefixNTValue("fetchStart", "fs"),
1341
1395
  prefixNTValue("domainLookupStart", "ds"),
1342
1396
  prefixNTValue("domainLookupEnd", "de"),
@@ -1350,11 +1404,11 @@
1350
1404
  prefixNTValue("domContentLoadedEventStart", "os"),
1351
1405
  prefixNTValue("domContentLoadedEventEnd", "oe"),
1352
1406
  prefixNTValue("domComplete", "oc"),
1353
- prefixNTValue("loadEventStart", "ls"),
1354
- prefixNTValue("loadEventEnd", "le"),
1355
- typeof startRender !== "undefined" ? "sr" + startRender : "",
1356
- typeof fcp !== "undefined" ? "fc" + fcp : "",
1357
- typeof lcp !== "undefined" ? "lc" + lcp : "",
1407
+ loadEventStartStr,
1408
+ loadEventEndStr,
1409
+ typeof startRender !== "undefined" ? "sr" + clamp(startRender) : "",
1410
+ typeof fcp !== "undefined" ? "fc" + clamp(fcp) : "",
1411
+ typeof lcp !== "undefined" ? "lc" + clamp(lcp) : "",
1358
1412
  ].join("");
1359
1413
  }
1360
1414
  else if (endMark) {
@@ -1378,7 +1432,7 @@
1378
1432
  for (var i = 0; i < paintEntries.length; i++) {
1379
1433
  var entry = paintEntries[i];
1380
1434
  if (entry.name === "first-contentful-paint") {
1381
- return floor(entry.startTime);
1435
+ return floor(entry.startTime - getZeroTime());
1382
1436
  }
1383
1437
  }
1384
1438
  return undefined;
@@ -1389,7 +1443,7 @@
1389
1443
  if (lcpEntries.length) {
1390
1444
  var lastEntry = lcpEntries[lcpEntries.length - 1];
1391
1445
  logger.logEvent(LogEvent.PerformanceEntryProcessed, [lastEntry]);
1392
- return floor(lastEntry.startTime);
1446
+ return floor(lastEntry.startTime - getZeroTime());
1393
1447
  }
1394
1448
  return undefined;
1395
1449
  }
@@ -1402,7 +1456,7 @@
1402
1456
  if (paintEntries.length) {
1403
1457
  // If the Paint Timing API is supported, use the value of the first paint event
1404
1458
  var paintValues = paintEntries.map(function (entry) { return entry.startTime; });
1405
- return floor(Math.min.apply(null, paintValues));
1459
+ return floor(Math.min.apply(null, paintValues) - getZeroTime());
1406
1460
  }
1407
1461
  }
1408
1462
  if (performance.timing && timing.msFirstPaint && __ENABLE_POLYFILLS) {
@@ -1474,17 +1528,17 @@
1474
1528
  }
1475
1529
  function docHeight(doc) {
1476
1530
  var body = doc.body, docelem = doc.documentElement;
1477
- var height = Math.max(body ? body.scrollHeight : 0, body ? body.offsetHeight : 0, docelem ? docelem.clientHeight : 0, docelem ? docelem.scrollHeight : 0, docelem ? docelem.offsetHeight : 0);
1531
+ var height = max(body ? body.scrollHeight : 0, body ? body.offsetHeight : 0, docelem ? docelem.clientHeight : 0, docelem ? docelem.scrollHeight : 0, docelem ? docelem.offsetHeight : 0);
1478
1532
  return height;
1479
1533
  }
1480
1534
  function docWidth(doc) {
1481
1535
  var body = doc.body, docelem = doc.documentElement;
1482
- var width = Math.max(body ? body.scrollWidth : 0, body ? body.offsetWidth : 0, docelem ? docelem.clientWidth : 0, docelem ? docelem.scrollWidth : 0, docelem ? docelem.offsetWidth : 0);
1536
+ var width = max(body ? body.scrollWidth : 0, body ? body.offsetWidth : 0, docelem ? docelem.clientWidth : 0, docelem ? docelem.scrollWidth : 0, docelem ? docelem.offsetWidth : 0);
1483
1537
  return width;
1484
1538
  }
1485
1539
  // Return the main HTML document transfer size (in bytes).
1486
1540
  function docSize() {
1487
- return navEntry.encodedBodySize || 0;
1541
+ return getNavigationEntry().encodedBodySize || 0;
1488
1542
  }
1489
1543
  // Return the connection type based on Network Information API.
1490
1544
  // Note this API is in flux.
@@ -1624,6 +1678,10 @@
1624
1678
  // Beacon back the LUX data.
1625
1679
  function _sendLux() {
1626
1680
  var _a;
1681
+ if (!isVisible() && !globalConfig.trackHiddenPages) {
1682
+ logger.logEvent(LogEvent.SendCancelledPageHidden);
1683
+ return;
1684
+ }
1627
1685
  clearMaxMeasureTimeout();
1628
1686
  var customerid = getCustomerId();
1629
1687
  if (!customerid ||
@@ -1663,6 +1721,15 @@
1663
1721
  if (wasPrerendered()) {
1664
1722
  gFlags = addFlag(gFlags, Flags.PageWasPrerendered);
1665
1723
  }
1724
+ if (globalConfig.serverTiming) {
1725
+ var navEntry = getNavigationEntry();
1726
+ if (navEntry.serverTiming) {
1727
+ var stPairs = getKeyValuePairs(globalConfig.serverTiming, navEntry.serverTiming);
1728
+ for (var name_2 in stPairs) {
1729
+ _addData(name_2, stPairs[name_2]);
1730
+ }
1731
+ }
1732
+ }
1666
1733
  if (LUX.conversions) {
1667
1734
  getMatchesFromPatternMap(LUX.conversions, location.hostname, location.pathname).forEach(function (conversion) {
1668
1735
  LUX.addData(conversion, BOOLEAN_TRUE);
@@ -1757,7 +1824,7 @@
1757
1824
  var sIx = ixValues(); // Interaction Metrics
1758
1825
  var INP = getINP();
1759
1826
  if (sIx) {
1760
- var beaconUrl = _getBeaconUrl(getUpdatedCustomData()) +
1827
+ var beaconUrl = _getBeaconUrl(getUpdatedCustomData()) +
1761
1828
  "&IX=" +
1762
1829
  sIx +
1763
1830
  (typeof gFirstInputDelay !== "undefined" ? "&FID=" + gFirstInputDelay : "") +
@@ -2031,8 +2098,8 @@
2031
2098
  setTimeout(sendBeaconAfterMinimumMeasureTime_1, timeRemaining);
2032
2099
  }
2033
2100
  };
2034
- if (globalConfig.autoWhenHidden) {
2035
- // The autoWhenHidden config forces the beacon to be sent even when the page is not visible.
2101
+ if (globalConfig.trackHiddenPages) {
2102
+ // The trackHiddenPages config forces the beacon to be sent even when the page is not visible.
2036
2103
  sendBeaconAfterMinimumMeasureTime_1();
2037
2104
  }
2038
2105
  else {
@@ -2040,6 +2107,31 @@
2040
2107
  onVisible(sendBeaconAfterMinimumMeasureTime_1);
2041
2108
  }
2042
2109
  }
2110
+ // When newBeaconOnPageShow = true, we initiate a new page view whenever a page is restored from
2111
+ // bfcache. Since we have no "onload" event to hook into after a bfcache restore, we rely on the
2112
+ // unload and maxMeasureTime handlers to send the beacon.
2113
+ if (globalConfig.newBeaconOnPageShow) {
2114
+ window.addEventListener("pageshow", function (event) {
2115
+ if (event.persisted) {
2116
+ // Record the timestamp of the bfcache restore
2117
+ pageRestoreTime = event.timeStamp;
2118
+ // In Chromium, document.visibilityState is still "hidden" when pageshow fires after a bfcache
2119
+ // restore. Wrapping this in a setTimeout ensures the browser has enough time to update the
2120
+ // visibility.
2121
+ // See https://bugs.chromium.org/p/chromium/issues/detail?id=1133363
2122
+ setTimeout(function () {
2123
+ if (gbLuxSent) {
2124
+ // If the beacon was already sent for this page, we start a new page view and mark the
2125
+ // load time as the time it took to restore the page.
2126
+ _init(pageRestoreTime);
2127
+ _markLoadTime();
2128
+ }
2129
+ // Flag the current page as a bfcache restore
2130
+ gFlags = addFlag(gFlags, Flags.PageWasBfCacheRestored);
2131
+ }, 0);
2132
+ }
2133
+ });
2134
+ }
2043
2135
  // Add the unload handlers when sendBeaconOnPageHidden is enabled
2044
2136
  if (globalConfig.sendBeaconOnPageHidden) {
2045
2137
  _addUnloadHandlers();
@@ -8,10 +8,14 @@
8
8
  }
9
9
 
10
10
  .gem-c-document-list__item {
11
- margin-bottom: govuk-spacing(5);
11
+ margin-top: govuk-spacing(5);
12
12
  padding-top: govuk-spacing(2);
13
13
  border-top: 1px solid $govuk-border-colour;
14
14
  list-style: none;
15
+
16
+ &:first-child {
17
+ margin-top: 0;
18
+ }
15
19
  }
16
20
 
17
21
  .gem-c-document-list__item-title {
@@ -86,14 +90,6 @@
86
90
  }
87
91
  }
88
92
 
89
- .gem-c-document-list--bottom-margin {
90
- margin-bottom: govuk-spacing(4);
91
- }
92
-
93
- .gem-c-document-list--top-margin {
94
- margin-top: govuk-spacing(4);
95
- }
96
-
97
93
  .gem-c-document-list__multi-list {
98
94
  width: 100%;
99
95
  margin-right: 25px;
@@ -9,6 +9,10 @@
9
9
  height: govuk-spacing(2);
10
10
  }
11
11
 
12
+ .gem-c-layout-for-public__blue-bar-wrapper--browse {
13
+ background-color: #263135;
14
+ }
15
+
12
16
  .js-enabled .gem-c-layout-for-public__global-banner-wrapper {
13
17
  margin-top: - govuk-spacing(2);
14
18
  min-height: govuk-spacing(2);
@@ -1,6 +1,7 @@
1
- <%
1
+ <%
2
2
  add_gem_component_stylesheet("table")
3
3
  add_gem_component_stylesheet("select")
4
+ add_gem_component_stylesheet("summary-list")
4
5
  %>
5
6
 
6
7
  <% content_for :title, "Component audit" %>
@@ -40,11 +40,12 @@
40
40
  ga4_link_data_attributes ||= nil
41
41
 
42
42
  if ga4_tracking
43
+ heading_text = Nokogiri::HTML(item[:heading][:text]).text
43
44
  item[:data_attributes] ||= {}
44
45
  item[:data_attributes][:ga4_event] = {
45
46
  event_name: "select_content",
46
47
  type: "accordion",
47
- text: item[:heading][:text],
48
+ text: heading_text,
48
49
  index: {
49
50
  index_section: index,
50
51
  index_section_count: items.length,
@@ -56,12 +57,12 @@
56
57
  # tracking attributes
57
58
  ga4_link_data_attributes = {}
58
59
  ga4_link_data_attributes[:module] = "ga4-link-tracker"
59
- ga4_link_data_attributes[:ga4_tracks_links_only] = ""
60
+ ga4_link_data_attributes[:ga4_track_links_only] = ""
60
61
  ga4_link_data_attributes[:ga4_set_indexes] = ""
61
62
  ga4_link_data_attributes[:ga4_link] = {
62
63
  event_name: "navigation",
63
64
  type: "accordion",
64
- section: item[:heading][:text],
65
+ section: heading_text,
65
66
  index: {
66
67
  index_section: index,
67
68
  index_section_count: (items.length),
@@ -1,14 +1,15 @@
1
1
  <%
2
2
  add_gem_component_stylesheet("document-list")
3
3
 
4
+ local_assigns[:margin_bottom] ||= 5
5
+ shared_helper = GovukPublishingComponents::Presenters::SharedHelper.new(local_assigns)
4
6
  items ||= []
5
7
 
6
- classes = "gem-c-document-list"
7
- classes << " gem-c-document-list--top-margin" if local_assigns[:margin_top]
8
- classes << " gem-c-document-list--bottom-margin" if local_assigns[:margin_bottom]
9
- classes << " gem-c-document-list--no-underline" if local_assigns[:remove_underline]
10
- classes << " gem-c-document-list--no-top-border" if local_assigns[:remove_top_border]
11
- classes << " gem-c-document-list--no-top-border-first-child" if local_assigns[:remove_top_border_from_first_child]
8
+ classes = %w[gem-c-document-list]
9
+ classes << "gem-c-document-list--no-underline" if local_assigns[:remove_underline]
10
+ classes << "gem-c-document-list--no-top-border" if local_assigns[:remove_top_border]
11
+ classes << "gem-c-document-list--no-top-border-first-child" if local_assigns[:remove_top_border_from_first_child]
12
+ classes << shared_helper.get_margin_bottom
12
13
 
13
14
  within_multitype_list ||= false
14
15
  within_multitype_list_class = " gem-c-document-list__multi-list" if within_multitype_list
@@ -19,7 +20,7 @@
19
20
  %>
20
21
  <% if items.any? %>
21
22
  <% unless within_multitype_list %>
22
- <ul class="<%= classes %>">
23
+ <ul class="<%= classes.join(" ") %>">
23
24
  <% end %>
24
25
  <% items.each do |item| %>
25
26
  <% highlight_class = " gem-c-document-list__item--highlight" if item[:highlight] %>
@@ -3,9 +3,11 @@
3
3
 
4
4
  emergency_banner ||= nil
5
5
  full_width ||= false
6
+ blue_bar ||= local_assigns.include?(:blue_bar) ? local_assigns[:blue_bar] : !full_width
6
7
  global_bar ||= nil
7
8
  html_lang ||= "en"
8
9
  layout_helper = GovukPublishingComponents::Presenters::PublicLayoutHelper.new(local_assigns)
10
+ blue_bar_background_colour = layout_helper.blue_bar_background_colours.include?(blue_bar_background_colour) ? blue_bar_background_colour : nil
9
11
  logo_link ||= "/"
10
12
  navigation_items ||= []
11
13
  omit_feedback_form ||= false
@@ -44,6 +46,9 @@
44
46
  blue_bar_dedupe = !full_width && global_bar.present?
45
47
  body_css_classes = %w(gem-c-layout-for-public govuk-template__body)
46
48
  body_css_classes << "draft" if draft_watermark
49
+
50
+ blue_bar_wrapper_classes = %w()
51
+ blue_bar_wrapper_classes << "gem-c-layout-for-public__blue-bar-wrapper--#{blue_bar_background_colour}" if blue_bar_background_colour
47
52
  -%>
48
53
  <!DOCTYPE html>
49
54
  <!--[if lt IE 9]><html class="lte-ie8 govuk-template" lang="<%= html_lang %>"><![endif]-->
@@ -117,8 +122,10 @@
117
122
 
118
123
  <%= raw(emergency_banner) %>
119
124
 
120
- <% unless full_width %>
121
- <div class="gem-c-layout-for-public__blue-bar govuk-width-container"></div>
125
+ <% if (blue_bar && !global_bar.present?) || (blue_bar_dedupe) %>
126
+ <%= content_tag(:div, class: blue_bar_wrapper_classes) do %>
127
+ <div class="gem-c-layout-for-public__blue-bar govuk-width-container"></div>
128
+ <% end %>
122
129
  <% end %>
123
130
 
124
131
  <% if global_bar.present? %>
@@ -20,6 +20,11 @@
20
20
  classes << shared_helper.get_margin_bottom if local_assigns[:margin_bottom]
21
21
 
22
22
  ga4_tracking ||= false
23
+ ga4_object = {
24
+ event_name: "navigation",
25
+ type: "content",
26
+ section: "Top"
27
+ }.to_json
23
28
  %>
24
29
  <%= content_tag :div, class: classes, data: { module: "gem-toggle metadata" } do %>
25
30
  <dl class="gem-c-metadata__list" data-module="gem-track-click">
@@ -48,19 +53,15 @@
48
53
  <dd class="gem-c-metadata__definition">
49
54
  <%= last_updated %>
50
55
  <% if local_assigns.include?(:see_updates_link) %>
51
- &#8212; <a href="#full-publication-update-history" class="gem-c-metadata__definition-link govuk-!-display-none-print js-see-all-updates-link"
52
- data-track-category="content-history"
53
- data-track-action="see-all-updates-link-clicked"
54
- data-track-label="history"
55
- <% if ga4_tracking %>
56
- data-module="ga4-link-tracker"
57
- data-ga4-link="<%= {
58
- event_name: "navigation",
59
- type: "content",
60
- section: "Top"
61
- }.to_json %>"
62
- <% end%>
63
- >
56
+ &#8212; <a href="#full-publication-update-history"
57
+ class="gem-c-metadata__definition-link govuk-!-display-none-print js-see-all-updates-link"
58
+ data-track-category="content-history"
59
+ data-track-action="see-all-updates-link-clicked"
60
+ data-track-label="history"
61
+ <% if ga4_tracking %>
62
+ data-module="ga4-link-tracker"
63
+ data-ga4-link="<%= ga4_object %>"
64
+ <% end%>>
64
65
  <%= t("components.metadata.see_all_updates") %>
65
66
  </a>
66
67
  <% end %>
@@ -74,7 +75,12 @@
74
75
  %>
75
76
  <% if definition.any? %>
76
77
  <dt class="gem-c-metadata__term"><%= title %>:</dt>
77
- <dd class="gem-c-metadata__definition">
78
+ <dd class="gem-c-metadata__definition"
79
+ <% if ga4_tracking %>
80
+ data-module="ga4-link-tracker"
81
+ data-ga4-track-links-only
82
+ data-ga4-link="<%= ga4_object %>"
83
+ <% end%>>
78
84
  <%= render 'govuk_publishing_components/components/metadata/sentence', items: definition, toggle_id: "#{index}-#{SecureRandom.hex(4)}" %>
79
85
  </dd>
80
86
  <% end %>
@@ -1,25 +1,37 @@
1
1
  <%
2
2
  add_gem_component_stylesheet("single-page-notification-button")
3
3
 
4
- component_helper = GovukPublishingComponents::Presenters::SinglePageNotificationButtonHelper.new(local_assigns)
4
+ spnb_helper = GovukPublishingComponents::Presenters::SinglePageNotificationButtonHelper.new(local_assigns)
5
5
  shared_helper = GovukPublishingComponents::Presenters::SharedHelper.new(local_assigns)
6
+ component_helper = GovukPublishingComponents::Presenters::ComponentWrapperHelper.new(local_assigns)
6
7
 
7
8
  wrapper_classes = %w(govuk-!-display-none-print)
8
9
  wrapper_classes << shared_helper.get_margin_bottom
10
+
11
+ ga4_data_attributes = ga4_data_attributes ||= nil
12
+ ga4_link_data_attributes = ga4_data_attributes[:ga4_link] if ga4_data_attributes
13
+ ga4_link_data_attributes[:url] = spnb_helper.form_action if ga4_link_data_attributes
14
+
15
+ component_helper.add_class(wrapper_classes.join(" "))
16
+ component_helper.add_data_attribute({ module: "gem-track-click" })
17
+ component_helper.add_data_attribute({ module: ga4_data_attributes[:module] }) if ga4_data_attributes
9
18
  %>
10
19
  <% button_text = capture do %>
11
- <svg class="gem-c-single-page-notification-button__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 459.334 459.334"><path fill="currentColor" d="M177.216 404.514c-.001.12-.009.239-.009.359 0 30.078 24.383 54.461 54.461 54.461s54.461-24.383 54.461-54.461c0-.12-.008-.239-.009-.359H175.216zM403.549 336.438l-49.015-72.002v-89.83c0-60.581-43.144-111.079-100.381-122.459V24.485C254.152 10.963 243.19 0 229.667 0s-24.485 10.963-24.485 24.485v27.663c-57.237 11.381-100.381 61.879-100.381 122.459v89.83l-49.015 72.002a24.76 24.76 0 0 0 20.468 38.693H383.08a24.761 24.761 0 0 0 20.469-38.694z"/></svg><span class="gem-c-single-page-notication-button__text"><%= component_helper.button_text %></span>
20
+ <svg class="gem-c-single-page-notification-button__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" height="18" width="18" viewBox="0 0 459.334 459.334"><path fill="currentColor" d="M177.216 404.514c-.001.12-.009.239-.009.359 0 30.078 24.383 54.461 54.461 54.461s54.461-24.383 54.461-54.461c0-.12-.008-.239-.009-.359H175.216zM403.549 336.438l-49.015-72.002v-89.83c0-60.581-43.144-111.079-100.381-122.459V24.485C254.152 10.963 243.19 0 229.667 0s-24.485 10.963-24.485 24.485v27.663c-57.237 11.381-100.381 61.879-100.381 122.459v89.83l-49.015 72.002a24.76 24.76 0 0 0 20.468 38.693H383.08a24.761 24.761 0 0 0 20.469-38.694z"/></svg><span class="gem-c-single-page-notication-button__text"><%= spnb_helper.button_text %></span>
12
21
  <% end %>
13
- <%= tag.div class: wrapper_classes, data: { module: "gem-track-click"} do %>
14
- <%= tag.form class: component_helper.classes, action: component_helper.form_action, method: "POST", data: component_helper.data do %>
15
- <input type="hidden" name="base_path" value="<%= component_helper.base_path %>">
16
- <% if component_helper.skip_account %>
17
- <input type="hidden" name="<%= component_helper.skip_account_param %>" value="true">
18
- <input type="hidden" name="link" value="<%= component_helper.base_path %>">
22
+ <%= tag.div(**component_helper.all_attributes) do %>
23
+ <%= tag.form class: spnb_helper.classes, action: spnb_helper.form_action, method: "POST", data: spnb_helper.data do %>
24
+ <input type="hidden" name="base_path" value="<%= spnb_helper.base_path %>">
25
+ <% if spnb_helper.skip_the_gov_uk_account? %>
26
+ <input type="hidden" name="<%= spnb_helper.skip_account_param %>" value="true">
27
+ <input type="hidden" name="link" value="<%= spnb_helper.base_path %>">
19
28
  <% end %>
20
29
  <%= content_tag(:button, button_text, {
21
30
  class: "govuk-body-s gem-c-single-page-notification-button__submit",
22
31
  type: "submit",
32
+ data: {
33
+ ga4_link: ga4_link_data_attributes
34
+ }
23
35
  }) %>
24
36
  <% end %>
25
- <% end if component_helper.base_path %>
37
+ <% end if spnb_helper.base_path %>
@@ -26,7 +26,7 @@
26
26
  <% if ga4_tracking
27
27
  ga4_attributes = {
28
28
  event_name: "navigation",
29
- type: "related content",
29
+ type: "part of",
30
30
  index:{
31
31
  "index_link": "1"
32
32
  },
@@ -56,7 +56,7 @@
56
56
  <% if ga4_tracking
57
57
  ga4_attributes = {
58
58
  event_name: "navigation",
59
- type: "related content",
59
+ type: "part of",
60
60
  index:{
61
61
  "index_link": (index + 1).to_s
62
62
  },
@@ -37,6 +37,23 @@ examples:
37
37
  metadata:
38
38
  public_updated_at: 2016-09-05 16:48:27
39
39
  document_type: 'Statutory guidance'
40
+ with_margin:
41
+ description: The component accepts a number for margin bottom from `0` to `9` (`0px` to `60px`) using the [GOV.UK Frontend spacing scale](https://design-system.service.gov.uk/styles/spacing/#the-responsive-spacing-scale). It defaults to having a margin bottom of 5 (25px).
42
+ data:
43
+ margin_bottom: 9
44
+ items:
45
+ - link:
46
+ text: 'Alternative provision'
47
+ path: '/government/publications/alternative-provision'
48
+ metadata:
49
+ public_updated_at: 2016-06-27 10:29:44
50
+ document_type: 'Statutory guidance'
51
+ - link:
52
+ text: 'Behaviour and discipline in schools: guide for governing bodies'
53
+ path: '/government/publications/behaviour-and-discipline-in-schools-guidance-for-governing-bodies'
54
+ metadata:
55
+ public_updated_at: 2015-09-24 16:42:48
56
+ document_type: 'Statutory guidance'
40
57
  without_links:
41
58
  data:
42
59
  items:
@@ -27,6 +27,12 @@ examples:
27
27
  description: This allows the header to be omitted which is currently used when rendering CSV previews from Whitehall
28
28
  data:
29
29
  omit_header: true
30
+ blue_bar_background:
31
+ description: For use when a page has a heading component with a background colour.
32
+ data:
33
+ full_width: true
34
+ blue_bar: true
35
+ blue_bar_background_colour: "no"
30
36
  omit_feedback:
31
37
  description: This allows the feedback form to be omitted
32
38
  data:
@@ -35,7 +35,7 @@ examples:
35
35
  title: 'Advisory Committee on Novel Foods and Processes has a <a href="http://www.food.gov.uk/acnfp">separate website</a>'
36
36
  without_title:
37
37
  data:
38
- description_govspeak: '<p>Scheduled to publish at 8am on 25 April 2019<br/><a href="change-date">Change date</a><br/><a href="stop-scheduled-publishing">Stop scheduled publishing</a></p>'
38
+ description_govspeak: '<p>Scheduled to publish at 8am on 25 April 2019</p><ul><li><a href="change-date">Change date</a></li><li><a href="stop-scheduled-publishing">Stop scheduled publishing</a></li></ul>'
39
39
  with_aria_live:
40
40
  description: Passing the aria live flag to the notice component will read the notice out to users if the notice changes, e.g on form submission the notice may go from hidden to visible.
41
41
  data:
@@ -25,6 +25,19 @@ examples:
25
25
  base_path: '/current-page-path'
26
26
  data_attributes:
27
27
  test_attribute: "testing"
28
+ with_ga4_tracking:
29
+ description: To add GA4 tracking, pass a `ga4_data_attributes` object with the necessary properties to the component. For example:-
30
+ data:
31
+ base_path: '/current-page-path'
32
+ ga4_data_attributes:
33
+ module: "ga4-link-tracker"
34
+ ga4_link:
35
+ event_name: "navigation"
36
+ type: "subscribe"
37
+ index:
38
+ index_link: 1
39
+ index_total: 1
40
+ section: "Top"
28
41
  with_margin_bottom:
29
42
  description: |
30
43
  The component accepts a number for margin bottom from `0` to `9` (`0px` to `60px`) using the [GOV.UK Frontend spacing scale](https://design-system.service.gov.uk/styles/spacing/#the-responsive-spacing-scale). It defaults to having a margin bottom of 15px.
@@ -87,7 +87,7 @@ module GovukPublishingComponents
87
87
  end
88
88
 
89
89
  def viewing_component_guide?
90
- request.path.include?("/component-guide")
90
+ request&.path&.include?("/component-guide")
91
91
  end
92
92
  end
93
93
  end
@@ -1,6 +1,7 @@
1
1
  module GovukPublishingComponents
2
2
  module Presenters
3
3
  class PublicLayoutHelper
4
+ BLUE_BAR_BACKGROUND_COLOURS = %w[browse].freeze
4
5
  FOOTER_NAVIGATION_COLUMNS = [2, 1].freeze
5
6
  FOOTER_TRACK_ACTIONS = %w[topicsLink governmentactivityLink].freeze
6
7
  FOOTER_META = {
@@ -69,6 +70,10 @@ module GovukPublishingComponents
69
70
  FOOTER_TRACK_ACTIONS
70
71
  end
71
72
 
73
+ def blue_bar_background_colours
74
+ BLUE_BAR_BACKGROUND_COLOURS
75
+ end
76
+
72
77
  def generate_data_attribute(link, track_action)
73
78
  {
74
79
  track_category: "footerClicked",
@@ -61,7 +61,11 @@ module GovukPublishingComponents
61
61
  end
62
62
 
63
63
  def form_action
64
- @skip_account ? email_alert_frontend_endpoint_no_account : email_alert_frontend_endpoint_enforce_account
64
+ if skip_the_gov_uk_account?
65
+ email_alert_frontend_endpoint_no_account
66
+ else
67
+ email_alert_frontend_endpoint_enforce_account
68
+ end
65
69
  end
66
70
 
67
71
  def email_alert_frontend_endpoint_enforce_account
@@ -75,6 +79,10 @@ module GovukPublishingComponents
75
79
  def skip_account_param
76
80
  "single_page_subscription"
77
81
  end
82
+
83
+ def skip_the_gov_uk_account?
84
+ @skip_account == "true"
85
+ end
78
86
  end
79
87
  end
80
88
  end
@@ -1,3 +1,3 @@
1
1
  module GovukPublishingComponents
2
- VERSION = "35.7.0".freeze
2
+ VERSION = "35.9.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: 35.7.0
4
+ version: 35.9.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: 2023-06-14 00:00:00.000000000 Z
11
+ date: 2023-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: govuk_app_config
@@ -1455,7 +1455,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1455
1455
  - !ruby/object:Gem::Version
1456
1456
  version: '0'
1457
1457
  requirements: []
1458
- rubygems_version: 3.4.14
1458
+ rubygems_version: 3.4.15
1459
1459
  signing_key:
1460
1460
  specification_version: 4
1461
1461
  summary: A gem to document components in GOV.UK frontend applications