govuk_publishing_components 56.2.2 → 56.3.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +1 -1
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-finder-tracker.js +163 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +226 -75
- data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +131 -97
- data/app/views/govuk_publishing_components/components/_table.html.erb +2 -1
- data/app/views/govuk_publishing_components/components/docs/table.yml +23 -0
- data/lib/govuk_publishing_components/app_helpers/table_helper.rb +9 -0
- data/lib/govuk_publishing_components/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67d7750a4af75be256720a227401885c736d2af67113acfa16889fc5dc599a5d
|
4
|
+
data.tar.gz: a312eca2e3699de90c89bf5e256a09bca8eedffeb3e00657738ec21fb8fa3059
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ae3113f4320e8ce93b70ec08a9b9581101d59c3eddff0444ec1ec42b551cf14f593e085fb919c7a70d634e47aeb49ec7f463c50195c40e56fe9a42636c864db
|
7
|
+
data.tar.gz: ebc820fe837ad54b2539fbe6c9baed82438c4ad254b9680d1c7716b2d34c6ffcbcfac49f516b5da883f74084116d72c70621b689af5a8adff314d4a9f63f2e04
|
@@ -364,7 +364,7 @@ window.GOVUK.analyticsGa4 = window.GOVUK.analyticsGa4 || {};
|
|
364
364
|
ecommerceSchema.search_results.ecommerce.items.push({
|
365
365
|
item_id: target.getAttribute('data-ga4-ecommerce-path'),
|
366
366
|
item_content_id: target.getAttribute('data-ga4-ecommerce-content-id') || undefined,
|
367
|
-
item_name: target.textContent,
|
367
|
+
item_name: target.getAttribute('data-ga4-ecommerce-item-name') || target.textContent,
|
368
368
|
item_list_name: listTitle,
|
369
369
|
index: window.GOVUK.analyticsGa4.core.ecommerceHelperFunctions.getIndex(target, startPosition)
|
370
370
|
})
|
@@ -0,0 +1,163 @@
|
|
1
|
+
;(function (global) {
|
2
|
+
'use strict'
|
3
|
+
|
4
|
+
const GOVUK = global.GOVUK || {}
|
5
|
+
GOVUK.analyticsGa4 = GOVUK.analyticsGa4 || {}
|
6
|
+
|
7
|
+
GOVUK.analyticsGa4.Ga4FinderTracker = {
|
8
|
+
// Called when the search results updates. Takes the event target, and a string containing the type of change and element type. Creates the GTM schema and pushes it.
|
9
|
+
// changeEventMetadata is a string referring to the type of form change and the element type that triggered it. For example 'update-filter checkbox'.
|
10
|
+
trackChangeEvent: function (event) {
|
11
|
+
const eventTarget = event.target
|
12
|
+
let changeEventMetadata = eventTarget.closest('[data-ga4-change-category]')
|
13
|
+
|
14
|
+
if (!changeEventMetadata) {
|
15
|
+
return
|
16
|
+
}
|
17
|
+
|
18
|
+
changeEventMetadata = changeEventMetadata.dataset.ga4ChangeCategory
|
19
|
+
changeEventMetadata = changeEventMetadata.split(' ')
|
20
|
+
|
21
|
+
const filterParent = eventTarget.closest('[data-ga4-filter-parent]')
|
22
|
+
const section = eventTarget.closest('[data-ga4-section]')
|
23
|
+
const changeType = changeEventMetadata[0]
|
24
|
+
const elementType = changeEventMetadata[1]
|
25
|
+
|
26
|
+
if ((!changeType || !elementType) && changeType !== 'clear-all-filters') {
|
27
|
+
console.warn('GA4 Finder tracker incorrectly configured for element: ' + eventTarget)
|
28
|
+
return
|
29
|
+
}
|
30
|
+
|
31
|
+
let data = {}
|
32
|
+
|
33
|
+
const filterType = eventTarget.closest('[data-ga4-filter-type]')
|
34
|
+
|
35
|
+
data.type = filterType ? filterType.dataset.ga4FilterType : 'finder'
|
36
|
+
data.event_name = 'select_content'
|
37
|
+
|
38
|
+
const elementInfo = this.getElementInfo(event, elementType)
|
39
|
+
if (!elementInfo) {
|
40
|
+
return
|
41
|
+
}
|
42
|
+
const elementValue = elementInfo.elementValue
|
43
|
+
data.text = elementValue
|
44
|
+
const wasFilterRemoved = elementInfo.wasFilterRemoved
|
45
|
+
data = this.setSchemaBasedOnChangeType(data, elementValue, elementType, wasFilterRemoved, changeType, section, filterParent)
|
46
|
+
|
47
|
+
window.GOVUK.analyticsGa4.core.applySchemaAndSendData(data, 'event_data')
|
48
|
+
},
|
49
|
+
|
50
|
+
// Grabs the value from the eventTarget. Checks if the filter was removed if the eventTarget is unchecked, set back to default, or has its user input removed. Returns the results as an object.
|
51
|
+
getElementInfo: function (event, elementType) {
|
52
|
+
const supportedElements = Object.assign(Object.assign({}, this.defaultSupportedElements), this.extraSupportedElements || {})
|
53
|
+
|
54
|
+
return supportedElements[elementType] ? supportedElements[elementType](event.target, event) : { elementValue: '', wasFilterRemoved: false }
|
55
|
+
},
|
56
|
+
|
57
|
+
// Takes the GTM schema, the event target value, the event target HTML type, whether the filter was removed, the type of filter change it was, and the parent section heading. Populates the GTM object based on these values.
|
58
|
+
setSchemaBasedOnChangeType: function (schema, elementValue, elementType, wasFilterRemoved, changeType, section, filterParent) {
|
59
|
+
switch (changeType) {
|
60
|
+
case 'update-filter': {
|
61
|
+
if (section) {
|
62
|
+
schema.section = section.getAttribute('data-ga4-section')
|
63
|
+
}
|
64
|
+
|
65
|
+
const index = this.getSectionIndex(filterParent)
|
66
|
+
if (wasFilterRemoved) {
|
67
|
+
schema.action = 'remove'
|
68
|
+
schema.text = elementType === 'text' ? undefined : elementValue
|
69
|
+
} else {
|
70
|
+
schema.action = elementType === 'text' ? 'search' : 'select'
|
71
|
+
}
|
72
|
+
schema.index_link = index.index_link || undefined
|
73
|
+
schema.index_section = index.index_section || undefined
|
74
|
+
schema.index_section_count = index.index_section_count || undefined
|
75
|
+
break
|
76
|
+
}
|
77
|
+
case 'update-keyword':
|
78
|
+
schema.event_name = 'search'
|
79
|
+
schema.url = window.location.pathname
|
80
|
+
schema.section = 'Search'
|
81
|
+
schema.action = 'search'
|
82
|
+
schema.text = GOVUK.analyticsGa4.core.trackFunctions.standardiseSearchTerm(schema.text)
|
83
|
+
break
|
84
|
+
|
85
|
+
case 'clear-all-filters':
|
86
|
+
schema.action = 'remove'
|
87
|
+
schema.text = 'Clear all filters'
|
88
|
+
break
|
89
|
+
|
90
|
+
case 'update-sort':
|
91
|
+
schema.action = 'sort'
|
92
|
+
schema.section = 'Sort by'
|
93
|
+
break
|
94
|
+
}
|
95
|
+
return schema
|
96
|
+
},
|
97
|
+
|
98
|
+
// Takes a filter section's div, and grabs the index value from it.
|
99
|
+
getSectionIndex: function (sectionElement) {
|
100
|
+
try {
|
101
|
+
let index = sectionElement.getAttribute('data-ga4-index')
|
102
|
+
index = JSON.parse(index)
|
103
|
+
return index
|
104
|
+
} catch (e) {
|
105
|
+
console.error('GA4 configuration error: ' + e.message, window.location)
|
106
|
+
}
|
107
|
+
},
|
108
|
+
|
109
|
+
defaultSupportedElements: {
|
110
|
+
checkbox: (eventTarget) => {
|
111
|
+
const checkboxId = eventTarget.id
|
112
|
+
|
113
|
+
return {
|
114
|
+
elementValue: document.querySelector("label[for='" + checkboxId + "']").textContent,
|
115
|
+
wasFilterRemoved: !eventTarget.checked
|
116
|
+
}
|
117
|
+
},
|
118
|
+
radio: (eventTarget) => {
|
119
|
+
const radioId = eventTarget.id
|
120
|
+
|
121
|
+
// The "value" we need for a radio is the label text that the user sees beside the checkbox.
|
122
|
+
const elementValue = document.querySelector("label[for='" + radioId + "']").textContent
|
123
|
+
const defaultValue = eventTarget.closest('[data-ga4-section]').querySelector('input[type=radio]:first-of-type')
|
124
|
+
|
125
|
+
return {
|
126
|
+
elementValue: elementValue,
|
127
|
+
wasFilterRemoved: eventTarget.id === defaultValue.id
|
128
|
+
}
|
129
|
+
},
|
130
|
+
select: (eventTarget) => {
|
131
|
+
// The value of a <select> is the value attribute of the selected <option>, which is a hyphenated key. We need to grab the human readable label instead for tracking.
|
132
|
+
const elementValue = eventTarget.querySelector("option[value='" + eventTarget.value + "']").textContent
|
133
|
+
const defaultValue = eventTarget.querySelector('option:first-of-type').textContent
|
134
|
+
|
135
|
+
return {
|
136
|
+
elementValue: elementValue,
|
137
|
+
wasFilterRemoved: elementValue === defaultValue
|
138
|
+
}
|
139
|
+
},
|
140
|
+
text: (eventTarget) => ({
|
141
|
+
elementValue: eventTarget.value,
|
142
|
+
wasFilterRemoved: eventTarget.value === ''
|
143
|
+
}),
|
144
|
+
date: (eventTarget) => {
|
145
|
+
// The GOV.UK Design System date input consists of three grouped but separate fields (day,
|
146
|
+
// month, year). We want to fire a single event when all three fields are filled in to
|
147
|
+
// avoid firing excessive events.
|
148
|
+
const inputs = [...eventTarget.closest('.govuk-date-input').querySelectorAll('input')]
|
149
|
+
const allInputsSet = inputs.every(input => input.value)
|
150
|
+
const noInputsSet = inputs.every(input => !input.value)
|
151
|
+
|
152
|
+
if (!allInputsSet && !noInputsSet) return
|
153
|
+
|
154
|
+
return {
|
155
|
+
elementValue: allInputsSet ? inputs.map(input => input.value).join('/') : '',
|
156
|
+
wasFilterRemoved: noInputsSet
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
global.GOVUK = GOVUK
|
163
|
+
})(window)
|
@@ -9,6 +9,7 @@
|
|
9
9
|
//= require ./analytics-ga4/ga4-link-tracker
|
10
10
|
//= require ./analytics-ga4/ga4-event-tracker
|
11
11
|
//= require ./analytics-ga4/ga4-ecommerce-tracker
|
12
|
+
//= require ./analytics-ga4/ga4-finder-tracker
|
12
13
|
//= require ./analytics-ga4/ga4-form-tracker
|
13
14
|
//= require ./analytics-ga4/ga4-auto-tracker
|
14
15
|
//= require ./analytics-ga4/ga4-smart-answer-results-tracker
|
@@ -22,10 +22,8 @@
|
|
22
22
|
(function () {
|
23
23
|
'use strict';
|
24
24
|
|
25
|
-
function floor(x) {
|
26
|
-
return Math.floor(x);
|
27
|
-
}
|
28
25
|
var max = Math.max;
|
26
|
+
var floor = Math.floor;
|
29
27
|
var round = Math.round;
|
30
28
|
/**
|
31
29
|
* Clamp a number so that it is never less than 0
|
@@ -263,6 +261,7 @@
|
|
263
261
|
// PostBeaconDisabled: 88, // Not used
|
264
262
|
PostBeaconSendFailed: 89,
|
265
263
|
PostBeaconCSPViolation: 90,
|
264
|
+
PostBeaconCollector: 91,
|
266
265
|
};
|
267
266
|
var Logger = /** @class */ (function () {
|
268
267
|
function Logger() {
|
@@ -337,7 +336,7 @@
|
|
337
336
|
return str;
|
338
337
|
}
|
339
338
|
|
340
|
-
var VERSION = "4.
|
339
|
+
var VERSION = "4.1.1";
|
341
340
|
/**
|
342
341
|
* Returns the version of the script as a float to be stored in legacy systems that do not support
|
343
342
|
* string versions.
|
@@ -390,6 +389,7 @@
|
|
390
389
|
this.sendRetries = 0;
|
391
390
|
this.maxMeasureTimeout = 0;
|
392
391
|
this.flags = 0;
|
392
|
+
this.metricCollectors = {};
|
393
393
|
this.onBeforeSendCbs = [];
|
394
394
|
this.startTime = opts.startTime || getZeroTime();
|
395
395
|
this.config = opts.config;
|
@@ -397,7 +397,6 @@
|
|
397
397
|
this.customerId = opts.customerId;
|
398
398
|
this.sessionId = opts.sessionId;
|
399
399
|
this.pageId = opts.pageId;
|
400
|
-
this.metricData = {};
|
401
400
|
this.maxMeasureTimeout = window.setTimeout(function () {
|
402
401
|
_this.logger.logEvent(LogEvent.PostBeaconTimeoutReached);
|
403
402
|
_this.stopRecording();
|
@@ -440,19 +439,12 @@
|
|
440
439
|
this.isRecording = false;
|
441
440
|
this.logger.logEvent(LogEvent.PostBeaconStopRecording);
|
442
441
|
};
|
443
|
-
Beacon.prototype.
|
444
|
-
|
445
|
-
this.logger.logEvent(LogEvent.PostBeaconMetricRejected, [metric]);
|
446
|
-
return;
|
447
|
-
}
|
448
|
-
this.metricData[metric] = data;
|
442
|
+
Beacon.prototype.addCollector = function (metric, collector) {
|
443
|
+
this.metricCollectors[metric] = collector;
|
449
444
|
};
|
450
445
|
Beacon.prototype.addFlag = function (flag) {
|
451
446
|
this.flags = addFlag(this.flags, flag);
|
452
447
|
};
|
453
|
-
Beacon.prototype.hasMetricData = function () {
|
454
|
-
return Object.keys(this.metricData).length > 0;
|
455
|
-
};
|
456
448
|
Beacon.prototype.beaconUrl = function () {
|
457
449
|
return this.config.beaconUrlV2;
|
458
450
|
};
|
@@ -468,7 +460,16 @@
|
|
468
460
|
if (!this.isBeingSampled()) {
|
469
461
|
return;
|
470
462
|
}
|
471
|
-
|
463
|
+
var collectionStart = now();
|
464
|
+
var metricData = {};
|
465
|
+
for (var metric in this.metricCollectors) {
|
466
|
+
var data = this.metricCollectors[metric]();
|
467
|
+
this.logger.logEvent(LogEvent.PostBeaconCollector, [metric, !!data]);
|
468
|
+
if (data) {
|
469
|
+
metricData[metric] = data;
|
470
|
+
}
|
471
|
+
}
|
472
|
+
if (!Object.keys(metricData).length && !this.config.allowEmptyPostBeacon) {
|
472
473
|
// TODO: This is only required while the new beacon is supplementary. Once it's the primary
|
473
474
|
// beacon, we should send it regardless of how much metric data it has.
|
474
475
|
this.logger.logEvent(LogEvent.PostBeaconCancelled);
|
@@ -485,11 +486,12 @@
|
|
485
486
|
customerId: this.customerId,
|
486
487
|
flags: this.flags,
|
487
488
|
measureDuration: msSincePageInit(),
|
489
|
+
collectionDuration: now() - collectionStart,
|
488
490
|
pageId: this.pageId,
|
489
491
|
scriptVersion: VERSION,
|
490
492
|
sessionId: this.sessionId,
|
491
493
|
startTime: this.startTime,
|
492
|
-
},
|
494
|
+
}, metricData);
|
493
495
|
try {
|
494
496
|
if (sendBeacon(beaconUrl, JSON.stringify(payload))) {
|
495
497
|
this.isSent = true;
|
@@ -505,6 +507,14 @@
|
|
505
507
|
};
|
506
508
|
return Beacon;
|
507
509
|
}());
|
510
|
+
var BeaconMetricKey;
|
511
|
+
(function (BeaconMetricKey) {
|
512
|
+
BeaconMetricKey["CLS"] = "cls";
|
513
|
+
BeaconMetricKey["INP"] = "inp";
|
514
|
+
BeaconMetricKey["LCP"] = "lcp";
|
515
|
+
BeaconMetricKey["LoAF"] = "loaf";
|
516
|
+
BeaconMetricKey["NavigationTiming"] = "navigationTiming";
|
517
|
+
})(BeaconMetricKey || (BeaconMetricKey = {}));
|
508
518
|
|
509
519
|
function onPageLoad(callback) {
|
510
520
|
if (document.readyState === "complete") {
|
@@ -631,36 +641,36 @@
|
|
631
641
|
try {
|
632
642
|
if (selector &&
|
633
643
|
(node.nodeType === 9 || selector.length > MAX_SELECTOR_LENGTH || !node.parentNode)) {
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
}
|
644
|
+
// Final selector.
|
645
|
+
return selector;
|
646
|
+
}
|
647
|
+
var el = node;
|
648
|
+
// Our first preference is to use the data-sctrack attribute from anywhere in the tree
|
649
|
+
var trackId = getClosestScTrackAttribute(el);
|
650
|
+
if (trackId) {
|
651
|
+
return trackId;
|
652
|
+
}
|
653
|
+
if (el.id) {
|
654
|
+
// Once we've found an element with ID we return the selector.
|
655
|
+
return "#" + el.id + (selector ? ">" + selector : "");
|
656
|
+
}
|
657
|
+
else if (el) {
|
658
|
+
// Otherwise attempt to get parent elements recursively
|
659
|
+
var name_1 = el.nodeType === 1 ? el.nodeName.toLowerCase() : el.nodeName.toUpperCase();
|
660
|
+
var classes = el.className ? "." + el.className.replace(/\s+/g, ".") : "";
|
661
|
+
// Remove classes until the selector is short enough
|
662
|
+
while ((name_1 + classes).length > MAX_SELECTOR_LENGTH) {
|
663
|
+
classes = classes.split(".").slice(0, -1).join(".");
|
664
|
+
}
|
665
|
+
var currentSelector = name_1 + classes + (selector ? ">" + selector : "");
|
666
|
+
if (el.parentNode) {
|
667
|
+
var selectorWithParent = getNodeSelector(el.parentNode, currentSelector);
|
668
|
+
if (selectorWithParent.length < MAX_SELECTOR_LENGTH) {
|
669
|
+
return selectorWithParent;
|
661
670
|
}
|
662
|
-
return currentSelector;
|
663
671
|
}
|
672
|
+
return currentSelector;
|
673
|
+
}
|
664
674
|
}
|
665
675
|
catch (error) {
|
666
676
|
// Do nothing.
|
@@ -680,13 +690,9 @@
|
|
680
690
|
*/
|
681
691
|
function getTrackingParams() {
|
682
692
|
var trackingParams = {};
|
683
|
-
if (location.search && URLSearchParams) {
|
693
|
+
if (location.search && typeof URLSearchParams === "function") {
|
684
694
|
var p = new URLSearchParams(location.search);
|
685
|
-
for (
|
686
|
-
var _i = 0, KNOWN_TRACKING_PARAMS_1 = KNOWN_TRACKING_PARAMS;
|
687
|
-
_i < KNOWN_TRACKING_PARAMS_1.length;
|
688
|
-
_i++
|
689
|
-
) {
|
695
|
+
for (var _i = 0, KNOWN_TRACKING_PARAMS_1 = KNOWN_TRACKING_PARAMS; _i < KNOWN_TRACKING_PARAMS_1.length; _i++) {
|
690
696
|
var key = KNOWN_TRACKING_PARAMS_1[_i];
|
691
697
|
var value = p.get(key);
|
692
698
|
if (value) {
|
@@ -703,7 +709,7 @@
|
|
703
709
|
var largestEntry;
|
704
710
|
var maximumSessionValue = 0;
|
705
711
|
var MAX_CLS_SOURCES = 50;
|
706
|
-
function processEntry$
|
712
|
+
function processEntry$3(entry) {
|
707
713
|
if (!entry.hadRecentInput) {
|
708
714
|
var firstEntry = sessionEntries[0];
|
709
715
|
var latestEntry = sessionEntries[sessionEntries.length - 1];
|
@@ -736,13 +742,13 @@
|
|
736
742
|
maximumSessionValue = max(maximumSessionValue, sessionValue);
|
737
743
|
}
|
738
744
|
}
|
739
|
-
function reset$
|
745
|
+
function reset$3() {
|
740
746
|
sessionValue = 0;
|
741
747
|
sessionEntries = [];
|
742
748
|
maximumSessionValue = 0;
|
743
749
|
largestEntry = undefined;
|
744
750
|
}
|
745
|
-
function getData$
|
751
|
+
function getData$3() {
|
746
752
|
return {
|
747
753
|
value: maximumSessionValue,
|
748
754
|
startTime: sessionEntries[0] ? processTimeMetric(sessionEntries[0].startTime) : null,
|
@@ -756,12 +762,121 @@
|
|
756
762
|
};
|
757
763
|
}
|
758
764
|
|
765
|
+
/******************************************************************************
|
766
|
+
Copyright (c) Microsoft Corporation.
|
767
|
+
|
768
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
769
|
+
purpose with or without fee is hereby granted.
|
770
|
+
|
771
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
772
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
773
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
774
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
775
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
776
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
777
|
+
PERFORMANCE OF THIS SOFTWARE.
|
778
|
+
***************************************************************************** */
|
779
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
780
|
+
|
781
|
+
|
782
|
+
var __assign = function() {
|
783
|
+
__assign = Object.assign || function __assign(t) {
|
784
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
785
|
+
s = arguments[i];
|
786
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
787
|
+
}
|
788
|
+
return t;
|
789
|
+
};
|
790
|
+
return __assign.apply(this, arguments);
|
791
|
+
};
|
792
|
+
|
793
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
794
|
+
var e = new Error(message);
|
795
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
796
|
+
};
|
797
|
+
|
798
|
+
var MAX_LOAF_ENTRIES = 50;
|
799
|
+
var MAX_LOAF_SCRIPTS = 50;
|
800
|
+
var entries = [];
|
801
|
+
function processEntry$2(entry) {
|
802
|
+
entries.push(entry);
|
803
|
+
}
|
804
|
+
function reset$2() {
|
805
|
+
entries = [];
|
806
|
+
}
|
807
|
+
function getEntries$1() {
|
808
|
+
return entries;
|
809
|
+
}
|
810
|
+
function getData$2() {
|
811
|
+
var summarizedEntries = [];
|
812
|
+
var totalDuration = 0;
|
813
|
+
var totalBlockingDuration = 0;
|
814
|
+
var totalStyleAndLayoutDuration = 0;
|
815
|
+
var totalWorkDuration = 0;
|
816
|
+
entries.forEach(function (entry) {
|
817
|
+
var startTime = entry.startTime, blockingDuration = entry.blockingDuration, duration = entry.duration, renderStart = entry.renderStart, styleAndLayoutStart = entry.styleAndLayoutStart;
|
818
|
+
totalDuration += duration;
|
819
|
+
totalBlockingDuration += blockingDuration;
|
820
|
+
totalStyleAndLayoutDuration += styleAndLayoutStart
|
821
|
+
? clamp(startTime + duration - styleAndLayoutStart)
|
822
|
+
: 0;
|
823
|
+
totalWorkDuration += renderStart ? renderStart - startTime : duration;
|
824
|
+
summarizedEntries.push({
|
825
|
+
startTime: floor(startTime),
|
826
|
+
duration: floor(duration),
|
827
|
+
renderStart: floor(renderStart),
|
828
|
+
styleAndLayoutStart: floor(styleAndLayoutStart),
|
829
|
+
blockingDuration: floor(blockingDuration),
|
830
|
+
});
|
831
|
+
});
|
832
|
+
return {
|
833
|
+
totalBlockingDuration: floor(totalBlockingDuration),
|
834
|
+
totalDuration: floor(totalDuration),
|
835
|
+
totalEntries: entries.length,
|
836
|
+
totalStyleAndLayoutDuration: floor(totalStyleAndLayoutDuration),
|
837
|
+
totalWorkDuration: floor(totalWorkDuration),
|
838
|
+
entries: summarizedEntries.slice(0, MAX_LOAF_ENTRIES),
|
839
|
+
scripts: summarizeLoAFScripts(entries.flatMap(function (entry) { return entry.scripts; })).slice(0, MAX_LOAF_SCRIPTS),
|
840
|
+
};
|
841
|
+
}
|
842
|
+
function summarizeLoAFScripts(scripts) {
|
843
|
+
var summary = {};
|
844
|
+
scripts.forEach(function (script) {
|
845
|
+
var key = script.invoker + ":" + script.sourceURL + ":" + script.sourceFunctionName;
|
846
|
+
if (!summary[key]) {
|
847
|
+
summary[key] = {
|
848
|
+
sourceUrl: script.sourceURL,
|
849
|
+
sourceFunctionName: script.sourceFunctionName,
|
850
|
+
timings: [],
|
851
|
+
totalEntries: 0,
|
852
|
+
totalDuration: 0,
|
853
|
+
totalPauseDuration: 0,
|
854
|
+
totalForcedStyleAndLayoutDuration: 0,
|
855
|
+
invoker: script.invoker,
|
856
|
+
inpPhase: script.inpPhase,
|
857
|
+
};
|
858
|
+
}
|
859
|
+
summary[key].totalEntries++;
|
860
|
+
summary[key].totalDuration += script.duration;
|
861
|
+
summary[key].totalPauseDuration += script.pauseDuration;
|
862
|
+
summary[key].totalForcedStyleAndLayoutDuration += script.forcedStyleAndLayoutDuration;
|
863
|
+
summary[key].timings.push([floor(script.startTime), floor(script.duration)]);
|
864
|
+
});
|
865
|
+
return Object.values(summary).map(function (script) { return (__assign(__assign({}, script), { totalDuration: floor(script.totalDuration), totalPauseDuration: floor(script.totalPauseDuration), totalForcedStyleAndLayoutDuration: floor(script.totalForcedStyleAndLayoutDuration) })); });
|
866
|
+
}
|
867
|
+
|
759
868
|
/**
|
760
869
|
* This implementation is based on the web-vitals implementation, however it is stripped back to the
|
761
870
|
* bare minimum required to measure just the INP value and does not store the actual event entries.
|
762
871
|
*/
|
763
872
|
// The maximum number of interactions to store
|
764
873
|
var MAX_INTERACTIONS = 10;
|
874
|
+
var INPPhase;
|
875
|
+
(function (INPPhase) {
|
876
|
+
INPPhase["InputDelay"] = "ID";
|
877
|
+
INPPhase["ProcessingTime"] = "PT";
|
878
|
+
INPPhase["PresentationDelay"] = "PD";
|
879
|
+
})(INPPhase || (INPPhase = {}));
|
765
880
|
// A list of the slowest interactions
|
766
881
|
var slowestEntries = [];
|
767
882
|
// A map of the slowest interactions by ID
|
@@ -838,21 +953,51 @@
|
|
838
953
|
if (!interaction) {
|
839
954
|
return undefined;
|
840
955
|
}
|
956
|
+
var duration = interaction.duration, startTime = interaction.startTime, processingStart = interaction.processingStart;
|
957
|
+
var inpScripts = getEntries$1()
|
958
|
+
.flatMap(function (entry) { return entry.scripts; })
|
959
|
+
// Only include scripts that started during the interaction
|
960
|
+
.filter(function (script) {
|
961
|
+
return script.startTime + script.duration >= startTime && script.startTime <= startTime + duration;
|
962
|
+
})
|
963
|
+
.map(function (_script) {
|
964
|
+
var script = JSON.parse(JSON.stringify(_script));
|
965
|
+
// Clamp the script duration to the time of the interaction
|
966
|
+
script.duration = script.startTime + script.duration - max(startTime, script.startTime);
|
967
|
+
script.inpPhase = getINPPhase(script, interaction);
|
968
|
+
return script;
|
969
|
+
});
|
970
|
+
var loafScripts = summarizeLoAFScripts(inpScripts);
|
841
971
|
return {
|
842
972
|
value: interaction.duration,
|
843
|
-
startTime: processTimeMetric(
|
973
|
+
startTime: processTimeMetric(startTime),
|
974
|
+
duration: interaction.duration,
|
844
975
|
subParts: {
|
845
|
-
inputDelay: clamp(floor(
|
976
|
+
inputDelay: clamp(floor(processingStart - startTime)),
|
977
|
+
processingStart: processTimeMetric(processingStart),
|
978
|
+
processingEnd: processTimeMetric(interaction.processingEnd),
|
846
979
|
processingTime: clamp(floor(interaction.processingTime)),
|
847
|
-
presentationDelay: clamp(floor(
|
980
|
+
presentationDelay: clamp(floor(startTime + interaction.duration - interaction.processingEnd)),
|
848
981
|
},
|
849
982
|
attribution: {
|
850
983
|
eventType: interaction.name,
|
851
984
|
elementSelector: interaction.selector || null,
|
852
985
|
elementType: ((_a = interaction.target) === null || _a === void 0 ? void 0 : _a.nodeName) || null,
|
986
|
+
loafScripts: loafScripts,
|
853
987
|
},
|
854
988
|
};
|
855
989
|
}
|
990
|
+
function getINPPhase(script, interaction) {
|
991
|
+
var processingStart = interaction.processingStart, processingTime = interaction.processingTime, startTime = interaction.startTime;
|
992
|
+
var inputDelay = processingStart - startTime;
|
993
|
+
if (script.startTime < startTime + inputDelay) {
|
994
|
+
return INPPhase.InputDelay;
|
995
|
+
}
|
996
|
+
else if (script.startTime >= startTime + inputDelay + processingTime) {
|
997
|
+
return INPPhase.PresentationDelay;
|
998
|
+
}
|
999
|
+
return INPPhase.ProcessingTime;
|
1000
|
+
}
|
856
1001
|
function getInteractionCount() {
|
857
1002
|
if ("interactionCount" in performance) {
|
858
1003
|
return performance.interactionCount;
|
@@ -1103,24 +1248,29 @@
|
|
1103
1248
|
observe("longtask", processAndLogEntry);
|
1104
1249
|
observe("element", processAndLogEntry);
|
1105
1250
|
observe("paint", processAndLogEntry);
|
1106
|
-
observe("largest-contentful-paint", function (entry) {
|
1251
|
+
if (observe("largest-contentful-paint", function (entry) {
|
1107
1252
|
// Process the LCP entry for the legacy beacon
|
1108
1253
|
processAndLogEntry(entry);
|
1109
1254
|
// Process the LCP entry for the new beacon
|
1110
1255
|
processEntry(entry);
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1256
|
+
})) {
|
1257
|
+
beacon.addCollector(BeaconMetricKey.LCP, getData);
|
1258
|
+
}
|
1259
|
+
if (observe("layout-shift", function (entry) {
|
1260
|
+
processEntry$3(entry);
|
1261
|
+
logEntry(entry);
|
1262
|
+
})) {
|
1263
|
+
beacon.addCollector(BeaconMetricKey.CLS, getData$3);
|
1264
|
+
}
|
1265
|
+
if (observe("long-animation-frame", function (entry) {
|
1114
1266
|
processEntry$2(entry);
|
1115
|
-
beacon.setMetricData("cls", getData$2());
|
1116
1267
|
logEntry(entry);
|
1117
|
-
})
|
1268
|
+
})) {
|
1269
|
+
beacon.addCollector(BeaconMetricKey.LoAF, getData$2);
|
1270
|
+
}
|
1118
1271
|
var handleINPEntry_1 = function (entry) {
|
1119
1272
|
processEntry$1(entry);
|
1120
|
-
|
1121
|
-
if (data) {
|
1122
|
-
beacon.setMetricData("inp", data);
|
1123
|
-
}
|
1273
|
+
logEntry(entry);
|
1124
1274
|
};
|
1125
1275
|
observe("first-input", function (entry) {
|
1126
1276
|
logEntry(entry);
|
@@ -1134,7 +1284,7 @@
|
|
1134
1284
|
// TODO: Set durationThreshold to 40 once performance.interactionCount is widely supported.
|
1135
1285
|
// Right now we have to count every event to get the total interaction count so that we can
|
1136
1286
|
// estimate a high percentile value for INP.
|
1137
|
-
observe("event", function (entry) {
|
1287
|
+
if (observe("event", function (entry) {
|
1138
1288
|
handleINPEntry_1(entry);
|
1139
1289
|
// It's useful to log the interactionId, but it is not serialised by default. Annoyingly, we
|
1140
1290
|
// need to manually serialize our own object with the keys we want.
|
@@ -1147,7 +1297,9 @@
|
|
1147
1297
|
processingStart: entry.processingStart,
|
1148
1298
|
processingEnd: entry.processingEnd,
|
1149
1299
|
});
|
1150
|
-
}, { durationThreshold: 0 })
|
1300
|
+
}, { durationThreshold: 0 })) {
|
1301
|
+
beacon.addCollector(BeaconMetricKey.INP, getData$1);
|
1302
|
+
}
|
1151
1303
|
}
|
1152
1304
|
catch (e) {
|
1153
1305
|
logger.logEvent(LogEvent.PerformanceObserverError, [e]);
|
@@ -1571,7 +1723,7 @@
|
|
1571
1723
|
if (!("LayoutShift" in self)) {
|
1572
1724
|
return undefined;
|
1573
1725
|
}
|
1574
|
-
var clsData = getData$
|
1726
|
+
var clsData = getData$3();
|
1575
1727
|
return clsData.value.toFixed(6);
|
1576
1728
|
}
|
1577
1729
|
// Return the median value from an array of integers.
|
@@ -1714,8 +1866,9 @@
|
|
1714
1866
|
gSyncId = createSyncId();
|
1715
1867
|
gUid = refreshUniqueId(gSyncId);
|
1716
1868
|
reset();
|
1717
|
-
reset$
|
1869
|
+
reset$3();
|
1718
1870
|
reset$1();
|
1871
|
+
reset$2();
|
1719
1872
|
nErrors = 0;
|
1720
1873
|
gFirstInputDelay = undefined;
|
1721
1874
|
beacon = initPostBeacon();
|
@@ -2121,7 +2274,8 @@
|
|
2121
2274
|
curleft += el.offsetLeft;
|
2122
2275
|
curtop += el.offsetTop;
|
2123
2276
|
el = el.offsetParent;
|
2124
|
-
}
|
2277
|
+
}
|
2278
|
+
catch (e) {
|
2125
2279
|
// If we get an exception, just return the current values.
|
2126
2280
|
return [curleft, curtop];
|
2127
2281
|
}
|
@@ -2199,10 +2353,7 @@
|
|
2199
2353
|
// Store any tracking parameters as custom data
|
2200
2354
|
var trackingParams = getTrackingParams();
|
2201
2355
|
for (var key in trackingParams) {
|
2202
|
-
logger.logEvent(LogEvent.TrackingParamAdded, [
|
2203
|
-
key,
|
2204
|
-
trackingParams[key],
|
2205
|
-
]);
|
2356
|
+
logger.logEvent(LogEvent.TrackingParamAdded, [key, trackingParams[key]]);
|
2206
2357
|
addCustomDataValue("_" + key, trackingParams[key]);
|
2207
2358
|
}
|
2208
2359
|
var sIx = "";
|
@@ -3,15 +3,18 @@
|
|
3
3
|
@import "mixins/prefixed-transform";
|
4
4
|
@import "mixins/grid-helper";
|
5
5
|
|
6
|
+
$pale-blue-colour: #d2e2f1;
|
7
|
+
|
8
|
+
$chevron-breakpoint: 360px;
|
6
9
|
$chevron-indent-spacing: 7px;
|
7
|
-
|
8
|
-
$search-toggle-button-height: $black-bar-height;
|
10
|
+
|
9
11
|
$pseudo-underline-height: 3px;
|
10
|
-
$button-pipe-colour: darken(govuk-colour("mid-grey"), 20%);
|
11
12
|
|
13
|
+
$navbar-height: 50px;
|
12
14
|
$large-navbar-height: 72px;
|
13
15
|
|
14
|
-
$
|
16
|
+
$button-pipe-colour: darken(govuk-colour("mid-grey"), 20%);
|
17
|
+
$button-pipe-colour-blue-background: $pale-blue-colour;
|
15
18
|
|
16
19
|
$after-link-padding: govuk-spacing(4);
|
17
20
|
$after-button-padding-right: govuk-spacing(4);
|
@@ -58,6 +61,9 @@ $after-button-padding-left: govuk-spacing(4);
|
|
58
61
|
}
|
59
62
|
}
|
60
63
|
|
64
|
+
// Using `:focus-visible` means that in supporting browsers the focus state won't
|
65
|
+
// be visible when a user clicks on the element, but the focus state will still be
|
66
|
+
// useful for those who use the keyboard to navigate around the page.
|
61
67
|
@mixin focus-and-focus-visible {
|
62
68
|
&:focus {
|
63
69
|
@content;
|
@@ -68,6 +74,11 @@ $after-button-padding-left: govuk-spacing(4);
|
|
68
74
|
}
|
69
75
|
}
|
70
76
|
|
77
|
+
// For browsers that don't support `:focus-visible`, this defaults to using
|
78
|
+
// `:focus` with a CSS-only fallback strategy.
|
79
|
+
//
|
80
|
+
// Undoes the :focus styles *only* for browsers that support :focus-visible.
|
81
|
+
// See https://www.tpgi.com/focus-visible-and-backwards-compatibility/
|
71
82
|
@mixin focus-not-focus-visible {
|
72
83
|
& {
|
73
84
|
@content;
|
@@ -104,7 +115,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
104
115
|
}
|
105
116
|
|
106
117
|
.gem-c-layout-super-navigation-header__button-container {
|
107
|
-
top: -$
|
118
|
+
top: -$navbar-height;
|
108
119
|
position: absolute;
|
109
120
|
right: 0;
|
110
121
|
|
@@ -316,7 +327,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
316
327
|
}
|
317
328
|
|
318
329
|
.gem-c-layout-super-navigation-header__navigation-item-link-inner--blue-background {
|
319
|
-
border-right: 1px solid $
|
330
|
+
border-right: 1px solid $button-pipe-colour-blue-background;
|
320
331
|
}
|
321
332
|
|
322
333
|
// Search link and dropdown.
|
@@ -389,7 +400,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
389
400
|
box-sizing: border-box;
|
390
401
|
color: govuk-colour("white");
|
391
402
|
cursor: pointer;
|
392
|
-
height: $
|
403
|
+
height: $navbar-height;
|
393
404
|
padding: 0;
|
394
405
|
position: relative;
|
395
406
|
margin: 0;
|
@@ -400,16 +411,18 @@ $after-button-padding-left: govuk-spacing(4);
|
|
400
411
|
@include pseudo-underline($left: $after-button-padding-left, $right: $after-button-padding-right);
|
401
412
|
}
|
402
413
|
|
403
|
-
|
404
|
-
|
414
|
+
@media (hover: hover) and (pointer: fine) {
|
415
|
+
&:hover {
|
416
|
+
color: govuk-colour("mid-grey");
|
405
417
|
|
406
|
-
|
407
|
-
|
408
|
-
|
418
|
+
&::after {
|
419
|
+
background: govuk-colour("mid-grey");
|
420
|
+
}
|
409
421
|
|
410
|
-
|
411
|
-
|
412
|
-
|
422
|
+
.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner {
|
423
|
+
&::before {
|
424
|
+
border-color: govuk-colour("mid-grey");
|
425
|
+
}
|
413
426
|
}
|
414
427
|
}
|
415
428
|
}
|
@@ -445,9 +458,11 @@ $after-button-padding-left: govuk-spacing(4);
|
|
445
458
|
|
446
459
|
box-shadow: none;
|
447
460
|
|
448
|
-
|
449
|
-
|
450
|
-
|
461
|
+
@media (hover: hover) and (pointer: fine) {
|
462
|
+
&:hover {
|
463
|
+
&::after {
|
464
|
+
background-color: govuk-colour("black");
|
465
|
+
}
|
451
466
|
}
|
452
467
|
}
|
453
468
|
|
@@ -465,25 +480,25 @@ $after-button-padding-left: govuk-spacing(4);
|
|
465
480
|
// stylelint-enable max-nesting-depth
|
466
481
|
}
|
467
482
|
|
468
|
-
// Undoes the :focus styles *only* for browsers that support :focus-visible.
|
469
|
-
// See https://www.tpgi.com/focus-visible-and-backwards-compatibility/
|
470
483
|
@include focus-not-focus-visible {
|
471
484
|
background: none;
|
472
485
|
box-shadow: none;
|
473
486
|
color: govuk-colour("white");
|
474
487
|
|
475
488
|
// stylelint-disable max-nesting-depth
|
476
|
-
|
477
|
-
|
478
|
-
|
489
|
+
@media (hover: hover) and (pointer: fine) {
|
490
|
+
&:hover {
|
491
|
+
.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner {
|
492
|
+
color: govuk-colour("mid-grey");
|
479
493
|
|
480
|
-
|
481
|
-
|
494
|
+
&::before {
|
495
|
+
@include chevron(govuk-colour("mid-grey"), true);
|
496
|
+
}
|
482
497
|
}
|
483
|
-
}
|
484
498
|
|
485
|
-
|
486
|
-
|
499
|
+
&::after {
|
500
|
+
background: govuk-colour("mid-grey");
|
501
|
+
}
|
487
502
|
}
|
488
503
|
}
|
489
504
|
|
@@ -491,10 +506,10 @@ $after-button-padding-left: govuk-spacing(4);
|
|
491
506
|
border-color: $button-pipe-colour;
|
492
507
|
|
493
508
|
&.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner--blue-background {
|
494
|
-
border-color: $
|
509
|
+
border-color: $button-pipe-colour-blue-background;
|
495
510
|
}
|
496
511
|
|
497
|
-
@include govuk-media-query($from:
|
512
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
498
513
|
&::before {
|
499
514
|
@include chevron(govuk-colour("white"), true);
|
500
515
|
}
|
@@ -505,6 +520,8 @@ $after-button-padding-left: govuk-spacing(4);
|
|
505
520
|
}
|
506
521
|
}
|
507
522
|
|
523
|
+
// JS available - targets the "Menu" toggle button
|
524
|
+
// Styles the "Menu" open state
|
508
525
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button.gem-c-layout-super-navigation-header__navigation-top-toggle-button--blue-background,
|
509
526
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button {
|
510
527
|
// Open button modifier
|
@@ -522,7 +539,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
522
539
|
color: govuk-colour("black");
|
523
540
|
border-color: $govuk-focus-colour;
|
524
541
|
|
525
|
-
@include govuk-media-query($from:
|
542
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
526
543
|
&::before {
|
527
544
|
@include chevron(govuk-colour("black"), true);
|
528
545
|
@include prefixed-transform($rotate: 225deg, $translateY: 1px);
|
@@ -542,7 +559,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
542
559
|
color: $govuk-link-colour;
|
543
560
|
border-color: govuk-colour("light-grey");
|
544
561
|
|
545
|
-
@include govuk-media-query($from:
|
562
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
546
563
|
&::before {
|
547
564
|
@include chevron($govuk-link-colour);
|
548
565
|
@include prefixed-transform($rotate: 225deg, $translateY: 1px);
|
@@ -554,135 +571,142 @@ $after-button-padding-left: govuk-spacing(4);
|
|
554
571
|
}
|
555
572
|
}
|
556
573
|
|
574
|
+
// JS available - targets the "Menu" toggle button used on a blue background
|
557
575
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button--blue-background {
|
558
576
|
@include focus-not-focus-visible {
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
577
|
+
@media (hover: hover) and (pointer: fine) {
|
578
|
+
&:hover {
|
579
|
+
&::after {
|
580
|
+
background: govuk-colour("white");
|
581
|
+
}
|
563
582
|
|
564
|
-
|
565
|
-
|
583
|
+
.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner {
|
584
|
+
color: govuk-colour("white");
|
566
585
|
|
567
|
-
|
568
|
-
|
586
|
+
&::before {
|
587
|
+
border-color: govuk-colour("white");
|
588
|
+
}
|
569
589
|
}
|
570
590
|
}
|
571
591
|
}
|
572
592
|
}
|
573
593
|
}
|
574
594
|
|
595
|
+
// JS available - targets the "Menu" toggle button inner text
|
575
596
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner {
|
576
597
|
display: inline-block;
|
577
598
|
border-right: 1px solid govuk-colour("white");
|
578
599
|
margin: 0;
|
579
600
|
padding: govuk-spacing(1) govuk-spacing(4);
|
580
601
|
|
581
|
-
@include govuk-media-query($from:
|
602
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
582
603
|
&::before {
|
583
604
|
@include chevron(govuk-colour("white"));
|
584
605
|
}
|
585
606
|
}
|
586
607
|
}
|
587
608
|
|
588
|
-
//
|
609
|
+
// JS available - targets the search toggle button
|
589
610
|
.gem-c-layout-super-navigation-header__search-toggle-button {
|
590
611
|
background: none;
|
591
612
|
border: 0;
|
592
613
|
color: govuk-colour("white");
|
593
614
|
cursor: pointer;
|
594
|
-
height: $
|
615
|
+
height: $navbar-height;
|
595
616
|
padding: govuk-spacing(3);
|
596
617
|
position: relative;
|
597
618
|
width: 51px;
|
619
|
+
@include govuk-font($size: 19, $weight: "bold", $line-height: 20px);
|
598
620
|
|
599
|
-
|
600
|
-
|
621
|
+
&::after {
|
622
|
+
@include pseudo-underline($left: 0, $right: 0);
|
601
623
|
}
|
602
624
|
|
603
|
-
@
|
625
|
+
@media (hover: hover) and (pointer: fine) {
|
626
|
+
&:hover {
|
627
|
+
color: govuk-colour("mid-grey");
|
628
|
+
|
629
|
+
&::after {
|
630
|
+
background: govuk-colour("mid-grey");
|
631
|
+
}
|
632
|
+
}
|
633
|
+
}
|
604
634
|
|
605
635
|
@include focus-and-focus-visible {
|
606
636
|
@include govuk-focused-text;
|
607
637
|
border-color: $govuk-focus-colour;
|
608
638
|
box-shadow: none;
|
609
639
|
z-index: 11;
|
640
|
+
|
641
|
+
@media (hover: hover) and (pointer: fine) {
|
642
|
+
&:hover {
|
643
|
+
&::after {
|
644
|
+
background: none;
|
645
|
+
}
|
646
|
+
}
|
647
|
+
}
|
610
648
|
}
|
611
649
|
|
612
|
-
|
650
|
+
@include focus-not-focus-visible {
|
613
651
|
background: none;
|
614
|
-
border-color: govuk-colour("white");
|
615
652
|
box-shadow: none;
|
616
653
|
color: govuk-colour("white");
|
617
654
|
}
|
618
655
|
|
619
|
-
|
620
|
-
|
621
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("black");
|
622
|
-
}
|
623
|
-
}
|
624
|
-
|
625
|
-
@include govuk-media-query($from: "desktop") {
|
626
|
-
border: 0;
|
627
|
-
margin: 0;
|
628
|
-
right: 0;
|
629
|
-
|
630
|
-
@include focus-not-focus-visible {
|
631
|
-
border-bottom: 1px solid transparent;
|
632
|
-
border-left: none;
|
633
|
-
position: relative;
|
634
|
-
}
|
635
|
-
|
636
|
-
&:hover {
|
637
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("mid-grey");
|
638
|
-
color: govuk-colour("mid-grey");
|
639
|
-
}
|
640
|
-
|
641
|
-
&.gem-c-layout-super-navigation-header__search-toggle-button--blue-background {
|
656
|
+
&.gem-c-layout-super-navigation-header__search-toggle-button--blue-background {
|
657
|
+
@media (hover: hover) and (pointer: fine) {
|
642
658
|
&:hover {
|
643
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("white");
|
644
659
|
color: govuk-colour("white");
|
645
|
-
}
|
646
660
|
|
647
|
-
|
648
|
-
|
649
|
-
border-bottom: $pseudo-underline-height solid $govuk-brand-colour;
|
650
|
-
|
651
|
-
&:hover {
|
652
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("white");
|
661
|
+
&::after {
|
662
|
+
background: govuk-colour("white");
|
653
663
|
}
|
654
664
|
}
|
655
|
-
// stylelint-enable max-nesting-depth
|
656
665
|
}
|
657
666
|
|
658
667
|
&:focus-visible {
|
659
|
-
|
660
|
-
|
668
|
+
color: govuk-colour("black");
|
669
|
+
|
670
|
+
&::after {
|
671
|
+
background: govuk-colour("black");
|
672
|
+
}
|
661
673
|
|
674
|
+
@media (hover: hover) and (pointer: fine) {
|
662
675
|
&:hover {
|
663
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("black");
|
664
676
|
color: govuk-colour("black");
|
677
|
+
|
678
|
+
&::after {
|
679
|
+
background: govuk-colour("black");
|
680
|
+
}
|
665
681
|
}
|
666
682
|
}
|
667
683
|
}
|
668
684
|
|
669
|
-
@include focus-
|
670
|
-
|
671
|
-
|
672
|
-
|
685
|
+
@include focus-not-focus-visible {
|
686
|
+
&::after {
|
687
|
+
background: none;
|
688
|
+
}
|
689
|
+
|
690
|
+
@media (hover: hover) and (pointer: fine) {
|
691
|
+
&:hover {
|
692
|
+
&::after {
|
693
|
+
background: govuk-colour("white");
|
694
|
+
}
|
695
|
+
}
|
696
|
+
}
|
673
697
|
}
|
674
698
|
}
|
675
699
|
|
676
700
|
// Open button modifier
|
677
701
|
&.gem-c-layout-super-navigation-header__open-button {
|
678
702
|
&.gem-c-layout-super-navigation-header__search-toggle-button--blue-background {
|
679
|
-
|
680
|
-
|
681
|
-
|
703
|
+
@media (hover: hover) and (pointer: fine) {
|
704
|
+
&:hover {
|
705
|
+
color: govuk-colour("white");
|
706
|
+
}
|
682
707
|
}
|
683
708
|
|
684
709
|
&:focus-visible {
|
685
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("black");
|
686
710
|
color: govuk-colour("black");
|
687
711
|
}
|
688
712
|
}
|
@@ -696,17 +720,23 @@ $after-button-padding-left: govuk-spacing(4);
|
|
696
720
|
|
697
721
|
@include focus-not-focus-visible {
|
698
722
|
background: govuk-colour("light-grey");
|
699
|
-
border-bottom-color: govuk-colour("light-grey");
|
700
723
|
color: govuk-colour("light-grey");
|
701
724
|
outline: 1px solid govuk-colour("light-grey"); // overlap the border of the nav menu so it won't appear when menu open
|
702
725
|
|
703
|
-
|
704
|
-
|
726
|
+
// stylelint-disable max-nesting-depth
|
727
|
+
@media (hover: hover) and (pointer: fine) {
|
728
|
+
&:hover {
|
729
|
+
&::after {
|
730
|
+
background: none;
|
731
|
+
}
|
732
|
+
}
|
705
733
|
}
|
734
|
+
// stylelint-enable max-nesting-depth
|
706
735
|
}
|
707
736
|
}
|
708
737
|
}
|
709
738
|
|
739
|
+
// JS available - styles the close icon, used when the search menu is in the open state
|
710
740
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-close-icon {
|
711
741
|
color: $govuk-text-colour;
|
712
742
|
display: none;
|
@@ -728,7 +758,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
728
758
|
padding-bottom: govuk-spacing(7);
|
729
759
|
}
|
730
760
|
|
731
|
-
//
|
761
|
+
// JS available - dropdown menu
|
732
762
|
.gem-c-layout-super-navigation-header__navigation-dropdown-menu {
|
733
763
|
background: govuk-colour("light-grey");
|
734
764
|
border-bottom: 1px govuk-colour("mid-grey") solid;
|
@@ -743,6 +773,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
743
773
|
}
|
744
774
|
}
|
745
775
|
|
776
|
+
// JS available - adds a custom margin to the wrapper for the search items in the search dropdown
|
746
777
|
.gem-c-layout-super-navigation-header__navigation-dropdown-menu--large-navbar {
|
747
778
|
@include govuk-media-query($until: "desktop") {
|
748
779
|
.gem-c-layout-super-navigation-header__search-items {
|
@@ -751,6 +782,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
751
782
|
}
|
752
783
|
}
|
753
784
|
|
785
|
+
// JS available - adds custom padding to the services and information and government activity sections in the menu dropdown
|
754
786
|
@include govuk-media-query($until: "tablet") {
|
755
787
|
// padding to make it the same as the padding for the redesign of the homepage
|
756
788
|
.gem-c-layout-super-navigation-header__navigation-dropdown-menu--large-navbar .gem-c-layout-super-navigation-header__column--services-and-information,
|
@@ -759,7 +791,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
759
791
|
}
|
760
792
|
}
|
761
793
|
|
762
|
-
//
|
794
|
+
// JS available - styles the links in the dropdown menu
|
763
795
|
.gem-c-layout-super-navigation-header__dropdown-list-item {
|
764
796
|
box-sizing: border-box;
|
765
797
|
padding: 0 0 govuk-spacing(3) 0;
|
@@ -771,7 +803,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
771
803
|
}
|
772
804
|
}
|
773
805
|
|
774
|
-
//
|
806
|
+
// JS available - wraps the `dropdown-list-item` navigation menu items
|
775
807
|
.gem-c-layout-super-navigation-header__navigation-second-items {
|
776
808
|
list-style: none;
|
777
809
|
margin: 0;
|
@@ -783,6 +815,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
783
815
|
}
|
784
816
|
}
|
785
817
|
|
818
|
+
// JS available - styling for the "government-activity" group of links
|
786
819
|
.gem-c-layout-super-navigation-header__column--government-activity {
|
787
820
|
position: relative;
|
788
821
|
|
@@ -791,6 +824,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
791
824
|
}
|
792
825
|
}
|
793
826
|
|
827
|
+
// JS available - styling for the "services-and-information" group of links
|
794
828
|
.gem-c-layout-super-navigation-header__navigation-second-items--services-and-information {
|
795
829
|
@include govuk-media-query($until: "desktop") {
|
796
830
|
border-bottom: 1px solid govuk-colour("mid-grey");
|
@@ -200,3 +200,26 @@ examples:
|
|
200
200
|
format: numeric
|
201
201
|
- text: £125
|
202
202
|
format: numeric
|
203
|
+
with_custom_width:
|
204
|
+
description: >
|
205
|
+
You can use `width` on a header cell to set the width of a table column if you do not want the
|
206
|
+
width to be inferred by the browser based on the content of its cells.
|
207
|
+
data:
|
208
|
+
head:
|
209
|
+
- text: Phrase
|
210
|
+
- text: Rating
|
211
|
+
width: one-half
|
212
|
+
rows:
|
213
|
+
-
|
214
|
+
- text: Lorem ipsum
|
215
|
+
- text: good
|
216
|
+
-
|
217
|
+
- text: dolor sit amet, consectetur adipiscing elit
|
218
|
+
- text: okay
|
219
|
+
-
|
220
|
+
- text: >
|
221
|
+
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua duis aute irure dolor
|
222
|
+
in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur
|
223
|
+
molestiae non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam
|
224
|
+
quaerat voluptatem!!!
|
225
|
+
- text: bad
|
@@ -20,6 +20,14 @@ module GovukPublishingComponents
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class TableBuilder
|
23
|
+
ALLOWABLE_COLUMN_WIDTHS = %w[
|
24
|
+
three-quarters
|
25
|
+
two-thirds
|
26
|
+
one-half
|
27
|
+
one-third
|
28
|
+
one-quarter
|
29
|
+
].freeze
|
30
|
+
|
23
31
|
include ActionView::Helpers::UrlHelper
|
24
32
|
include ActionView::Helpers::TagHelper
|
25
33
|
|
@@ -53,6 +61,7 @@ module GovukPublishingComponents
|
|
53
61
|
classes = %w[govuk-table__header]
|
54
62
|
classes << "govuk-table__header--#{opt[:format]}" if opt[:format]
|
55
63
|
classes << "govuk-table__header--active" if opt[:sort_direction]
|
64
|
+
classes << "govuk-!-width-#{opt[:width]}" if ALLOWABLE_COLUMN_WIDTHS.include?(opt[:width])
|
56
65
|
link_classes = %w[app-table__sort-link]
|
57
66
|
link_classes << "app-table__sort-link--#{opt[:sort_direction]}" if opt[:sort_direction]
|
58
67
|
str = link_to str, opt[:href], class: link_classes, data: opt[:data_attributes] if opt[:href]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: govuk_publishing_components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 56.
|
4
|
+
version: 56.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GOV.UK Dev
|
@@ -456,6 +456,7 @@ files:
|
|
456
456
|
- app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js
|
457
457
|
- app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-ecommerce-tracker.js
|
458
458
|
- app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js
|
459
|
+
- app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-finder-tracker.js
|
459
460
|
- app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-focus-loss-tracker.js
|
460
461
|
- app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-form-tracker.js
|
461
462
|
- app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js
|