govuk_publishing_components 56.2.1 → 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 +151 -109
- 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 +3 -2
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-width-or-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
|
|
@@ -137,7 +148,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
137
148
|
padding: 0;
|
138
149
|
}
|
139
150
|
|
140
|
-
// Top level navigation links and search link
|
151
|
+
// Top level navigation links and search link, used when JavaScript is not available
|
141
152
|
.gem-c-layout-super-navigation-header__navigation-item-link,
|
142
153
|
.gem-c-layout-super-navigation-header__search-item-link {
|
143
154
|
display: inline-block;
|
@@ -305,10 +316,18 @@ $after-button-padding-left: govuk-spacing(4);
|
|
305
316
|
|
306
317
|
.gem-c-layout-super-navigation-header__navigation-item-link-inner {
|
307
318
|
padding: govuk-spacing(1) $after-link-padding;
|
319
|
+
border-right: 1px solid $button-pipe-colour;
|
320
|
+
}
|
321
|
+
|
322
|
+
.gem-c-layout-super-navigation-header__navigation-item-link--large-navbar {
|
323
|
+
& .gem-c-layout-super-navigation-header__navigation-item-link-inner {
|
324
|
+
padding-left: 26px;
|
325
|
+
padding-right: 26px;
|
326
|
+
}
|
308
327
|
}
|
309
328
|
|
310
329
|
.gem-c-layout-super-navigation-header__navigation-item-link-inner--blue-background {
|
311
|
-
border-right: 1px solid $
|
330
|
+
border-right: 1px solid $button-pipe-colour-blue-background;
|
312
331
|
}
|
313
332
|
|
314
333
|
// Search link and dropdown.
|
@@ -321,8 +340,6 @@ $after-button-padding-left: govuk-spacing(4);
|
|
321
340
|
|
322
341
|
&:link,
|
323
342
|
&:visited {
|
324
|
-
background: $govuk-brand-colour;
|
325
|
-
|
326
343
|
&:hover {
|
327
344
|
&::before {
|
328
345
|
left: 0;
|
@@ -344,10 +361,6 @@ $after-button-padding-left: govuk-spacing(4);
|
|
344
361
|
width: 100%;
|
345
362
|
}
|
346
363
|
|
347
|
-
@include focus-not-focus-visible {
|
348
|
-
background: $govuk-link-colour;
|
349
|
-
}
|
350
|
-
|
351
364
|
@include focus-and-focus-visible {
|
352
365
|
&:hover {
|
353
366
|
background: $govuk-focus-colour;
|
@@ -361,6 +374,11 @@ $after-button-padding-left: govuk-spacing(4);
|
|
361
374
|
}
|
362
375
|
}
|
363
376
|
|
377
|
+
.gem-c-layout-super-navigation-header__search-item-link--large-navbar {
|
378
|
+
padding-left: 24px;
|
379
|
+
padding-right: 24px;
|
380
|
+
}
|
381
|
+
|
364
382
|
.gem-c-layout-super-navigation-header__search-item-link-icon,
|
365
383
|
.gem-c-layout-super-navigation-header__search-toggle-button-link-icon {
|
366
384
|
height: 20px;
|
@@ -382,7 +400,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
382
400
|
box-sizing: border-box;
|
383
401
|
color: govuk-colour("white");
|
384
402
|
cursor: pointer;
|
385
|
-
height: $
|
403
|
+
height: $navbar-height;
|
386
404
|
padding: 0;
|
387
405
|
position: relative;
|
388
406
|
margin: 0;
|
@@ -393,16 +411,18 @@ $after-button-padding-left: govuk-spacing(4);
|
|
393
411
|
@include pseudo-underline($left: $after-button-padding-left, $right: $after-button-padding-right);
|
394
412
|
}
|
395
413
|
|
396
|
-
|
397
|
-
|
414
|
+
@media (hover: hover) and (pointer: fine) {
|
415
|
+
&:hover {
|
416
|
+
color: govuk-colour("mid-grey");
|
398
417
|
|
399
|
-
|
400
|
-
|
401
|
-
|
418
|
+
&::after {
|
419
|
+
background: govuk-colour("mid-grey");
|
420
|
+
}
|
402
421
|
|
403
|
-
|
404
|
-
|
405
|
-
|
422
|
+
.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner {
|
423
|
+
&::before {
|
424
|
+
border-color: govuk-colour("mid-grey");
|
425
|
+
}
|
406
426
|
}
|
407
427
|
}
|
408
428
|
}
|
@@ -438,9 +458,11 @@ $after-button-padding-left: govuk-spacing(4);
|
|
438
458
|
|
439
459
|
box-shadow: none;
|
440
460
|
|
441
|
-
|
442
|
-
|
443
|
-
|
461
|
+
@media (hover: hover) and (pointer: fine) {
|
462
|
+
&:hover {
|
463
|
+
&::after {
|
464
|
+
background-color: govuk-colour("black");
|
465
|
+
}
|
444
466
|
}
|
445
467
|
}
|
446
468
|
|
@@ -458,25 +480,25 @@ $after-button-padding-left: govuk-spacing(4);
|
|
458
480
|
// stylelint-enable max-nesting-depth
|
459
481
|
}
|
460
482
|
|
461
|
-
// Undoes the :focus styles *only* for browsers that support :focus-visible.
|
462
|
-
// See https://www.tpgi.com/focus-visible-and-backwards-compatibility/
|
463
483
|
@include focus-not-focus-visible {
|
464
484
|
background: none;
|
465
485
|
box-shadow: none;
|
466
486
|
color: govuk-colour("white");
|
467
487
|
|
468
488
|
// stylelint-disable max-nesting-depth
|
469
|
-
|
470
|
-
|
471
|
-
|
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");
|
472
493
|
|
473
|
-
|
474
|
-
|
494
|
+
&::before {
|
495
|
+
@include chevron(govuk-colour("mid-grey"), true);
|
496
|
+
}
|
475
497
|
}
|
476
|
-
}
|
477
498
|
|
478
|
-
|
479
|
-
|
499
|
+
&::after {
|
500
|
+
background: govuk-colour("mid-grey");
|
501
|
+
}
|
480
502
|
}
|
481
503
|
}
|
482
504
|
|
@@ -484,10 +506,10 @@ $after-button-padding-left: govuk-spacing(4);
|
|
484
506
|
border-color: $button-pipe-colour;
|
485
507
|
|
486
508
|
&.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner--blue-background {
|
487
|
-
border-color: $
|
509
|
+
border-color: $button-pipe-colour-blue-background;
|
488
510
|
}
|
489
511
|
|
490
|
-
@include govuk-media-query($from:
|
512
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
491
513
|
&::before {
|
492
514
|
@include chevron(govuk-colour("white"), true);
|
493
515
|
}
|
@@ -498,6 +520,8 @@ $after-button-padding-left: govuk-spacing(4);
|
|
498
520
|
}
|
499
521
|
}
|
500
522
|
|
523
|
+
// JS available - targets the "Menu" toggle button
|
524
|
+
// Styles the "Menu" open state
|
501
525
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button.gem-c-layout-super-navigation-header__navigation-top-toggle-button--blue-background,
|
502
526
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button {
|
503
527
|
// Open button modifier
|
@@ -515,7 +539,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
515
539
|
color: govuk-colour("black");
|
516
540
|
border-color: $govuk-focus-colour;
|
517
541
|
|
518
|
-
@include govuk-media-query($from:
|
542
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
519
543
|
&::before {
|
520
544
|
@include chevron(govuk-colour("black"), true);
|
521
545
|
@include prefixed-transform($rotate: 225deg, $translateY: 1px);
|
@@ -535,7 +559,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
535
559
|
color: $govuk-link-colour;
|
536
560
|
border-color: govuk-colour("light-grey");
|
537
561
|
|
538
|
-
@include govuk-media-query($from:
|
562
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
539
563
|
&::before {
|
540
564
|
@include chevron($govuk-link-colour);
|
541
565
|
@include prefixed-transform($rotate: 225deg, $translateY: 1px);
|
@@ -547,135 +571,142 @@ $after-button-padding-left: govuk-spacing(4);
|
|
547
571
|
}
|
548
572
|
}
|
549
573
|
|
574
|
+
// JS available - targets the "Menu" toggle button used on a blue background
|
550
575
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button--blue-background {
|
551
576
|
@include focus-not-focus-visible {
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
577
|
+
@media (hover: hover) and (pointer: fine) {
|
578
|
+
&:hover {
|
579
|
+
&::after {
|
580
|
+
background: govuk-colour("white");
|
581
|
+
}
|
556
582
|
|
557
|
-
|
558
|
-
|
583
|
+
.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner {
|
584
|
+
color: govuk-colour("white");
|
559
585
|
|
560
|
-
|
561
|
-
|
586
|
+
&::before {
|
587
|
+
border-color: govuk-colour("white");
|
588
|
+
}
|
562
589
|
}
|
563
590
|
}
|
564
591
|
}
|
565
592
|
}
|
566
593
|
}
|
567
594
|
|
595
|
+
// JS available - targets the "Menu" toggle button inner text
|
568
596
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-button-inner {
|
569
597
|
display: inline-block;
|
570
598
|
border-right: 1px solid govuk-colour("white");
|
571
599
|
margin: 0;
|
572
600
|
padding: govuk-spacing(1) govuk-spacing(4);
|
573
601
|
|
574
|
-
@include govuk-media-query($from:
|
602
|
+
@include govuk-media-query($from: $chevron-breakpoint) {
|
575
603
|
&::before {
|
576
604
|
@include chevron(govuk-colour("white"));
|
577
605
|
}
|
578
606
|
}
|
579
607
|
}
|
580
608
|
|
581
|
-
//
|
609
|
+
// JS available - targets the search toggle button
|
582
610
|
.gem-c-layout-super-navigation-header__search-toggle-button {
|
583
611
|
background: none;
|
584
612
|
border: 0;
|
585
613
|
color: govuk-colour("white");
|
586
614
|
cursor: pointer;
|
587
|
-
height: $
|
615
|
+
height: $navbar-height;
|
588
616
|
padding: govuk-spacing(3);
|
589
617
|
position: relative;
|
590
|
-
width:
|
618
|
+
width: 51px;
|
619
|
+
@include govuk-font($size: 19, $weight: "bold", $line-height: 20px);
|
591
620
|
|
592
|
-
|
593
|
-
|
621
|
+
&::after {
|
622
|
+
@include pseudo-underline($left: 0, $right: 0);
|
594
623
|
}
|
595
624
|
|
596
|
-
@
|
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
|
+
}
|
597
634
|
|
598
635
|
@include focus-and-focus-visible {
|
599
636
|
@include govuk-focused-text;
|
600
637
|
border-color: $govuk-focus-colour;
|
601
638
|
box-shadow: none;
|
602
639
|
z-index: 11;
|
640
|
+
|
641
|
+
@media (hover: hover) and (pointer: fine) {
|
642
|
+
&:hover {
|
643
|
+
&::after {
|
644
|
+
background: none;
|
645
|
+
}
|
646
|
+
}
|
647
|
+
}
|
603
648
|
}
|
604
649
|
|
605
|
-
|
650
|
+
@include focus-not-focus-visible {
|
606
651
|
background: none;
|
607
|
-
border-color: govuk-colour("white");
|
608
652
|
box-shadow: none;
|
609
653
|
color: govuk-colour("white");
|
610
654
|
}
|
611
655
|
|
612
|
-
|
613
|
-
|
614
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("black");
|
615
|
-
}
|
616
|
-
}
|
617
|
-
|
618
|
-
@include govuk-media-query($from: "desktop") {
|
619
|
-
border: 0;
|
620
|
-
margin: 0;
|
621
|
-
right: 0;
|
622
|
-
|
623
|
-
@include focus-not-focus-visible {
|
624
|
-
border-bottom: 1px solid transparent;
|
625
|
-
border-left: none;
|
626
|
-
position: relative;
|
627
|
-
}
|
628
|
-
|
629
|
-
&:hover {
|
630
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("mid-grey");
|
631
|
-
color: govuk-colour("mid-grey");
|
632
|
-
}
|
633
|
-
|
634
|
-
&.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) {
|
635
658
|
&:hover {
|
636
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("white");
|
637
659
|
color: govuk-colour("white");
|
638
|
-
}
|
639
660
|
|
640
|
-
|
641
|
-
|
642
|
-
border-bottom: $pseudo-underline-height solid $govuk-brand-colour;
|
643
|
-
|
644
|
-
&:hover {
|
645
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("white");
|
661
|
+
&::after {
|
662
|
+
background: govuk-colour("white");
|
646
663
|
}
|
647
664
|
}
|
648
|
-
// stylelint-enable max-nesting-depth
|
649
665
|
}
|
650
666
|
|
651
667
|
&:focus-visible {
|
652
|
-
|
653
|
-
color: govuk-colour("black");
|
668
|
+
color: govuk-colour("black");
|
654
669
|
|
670
|
+
&::after {
|
671
|
+
background: govuk-colour("black");
|
672
|
+
}
|
673
|
+
|
674
|
+
@media (hover: hover) and (pointer: fine) {
|
655
675
|
&:hover {
|
656
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("black");
|
657
676
|
color: govuk-colour("black");
|
677
|
+
|
678
|
+
&::after {
|
679
|
+
background: govuk-colour("black");
|
680
|
+
}
|
658
681
|
}
|
659
682
|
}
|
660
683
|
}
|
661
684
|
|
662
|
-
@include focus-
|
663
|
-
|
664
|
-
|
665
|
-
|
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
|
+
}
|
666
697
|
}
|
667
698
|
}
|
668
699
|
|
669
700
|
// Open button modifier
|
670
701
|
&.gem-c-layout-super-navigation-header__open-button {
|
671
702
|
&.gem-c-layout-super-navigation-header__search-toggle-button--blue-background {
|
672
|
-
|
673
|
-
|
674
|
-
|
703
|
+
@media (hover: hover) and (pointer: fine) {
|
704
|
+
&:hover {
|
705
|
+
color: govuk-colour("white");
|
706
|
+
}
|
675
707
|
}
|
676
708
|
|
677
709
|
&:focus-visible {
|
678
|
-
border-bottom: $pseudo-underline-height solid govuk-colour("black");
|
679
710
|
color: govuk-colour("black");
|
680
711
|
}
|
681
712
|
}
|
@@ -689,17 +720,23 @@ $after-button-padding-left: govuk-spacing(4);
|
|
689
720
|
|
690
721
|
@include focus-not-focus-visible {
|
691
722
|
background: govuk-colour("light-grey");
|
692
|
-
border-bottom-color: govuk-colour("light-grey");
|
693
723
|
color: govuk-colour("light-grey");
|
694
724
|
outline: 1px solid govuk-colour("light-grey"); // overlap the border of the nav menu so it won't appear when menu open
|
695
725
|
|
696
|
-
|
697
|
-
|
726
|
+
// stylelint-disable max-nesting-depth
|
727
|
+
@media (hover: hover) and (pointer: fine) {
|
728
|
+
&:hover {
|
729
|
+
&::after {
|
730
|
+
background: none;
|
731
|
+
}
|
732
|
+
}
|
698
733
|
}
|
734
|
+
// stylelint-enable max-nesting-depth
|
699
735
|
}
|
700
736
|
}
|
701
737
|
}
|
702
738
|
|
739
|
+
// JS available - styles the close icon, used when the search menu is in the open state
|
703
740
|
.gem-c-layout-super-navigation-header__navigation-top-toggle-close-icon {
|
704
741
|
color: $govuk-text-colour;
|
705
742
|
display: none;
|
@@ -721,7 +758,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
721
758
|
padding-bottom: govuk-spacing(7);
|
722
759
|
}
|
723
760
|
|
724
|
-
//
|
761
|
+
// JS available - dropdown menu
|
725
762
|
.gem-c-layout-super-navigation-header__navigation-dropdown-menu {
|
726
763
|
background: govuk-colour("light-grey");
|
727
764
|
border-bottom: 1px govuk-colour("mid-grey") solid;
|
@@ -736,6 +773,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
736
773
|
}
|
737
774
|
}
|
738
775
|
|
776
|
+
// JS available - adds a custom margin to the wrapper for the search items in the search dropdown
|
739
777
|
.gem-c-layout-super-navigation-header__navigation-dropdown-menu--large-navbar {
|
740
778
|
@include govuk-media-query($until: "desktop") {
|
741
779
|
.gem-c-layout-super-navigation-header__search-items {
|
@@ -744,6 +782,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
744
782
|
}
|
745
783
|
}
|
746
784
|
|
785
|
+
// JS available - adds custom padding to the services and information and government activity sections in the menu dropdown
|
747
786
|
@include govuk-media-query($until: "tablet") {
|
748
787
|
// padding to make it the same as the padding for the redesign of the homepage
|
749
788
|
.gem-c-layout-super-navigation-header__navigation-dropdown-menu--large-navbar .gem-c-layout-super-navigation-header__column--services-and-information,
|
@@ -752,7 +791,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
752
791
|
}
|
753
792
|
}
|
754
793
|
|
755
|
-
//
|
794
|
+
// JS available - styles the links in the dropdown menu
|
756
795
|
.gem-c-layout-super-navigation-header__dropdown-list-item {
|
757
796
|
box-sizing: border-box;
|
758
797
|
padding: 0 0 govuk-spacing(3) 0;
|
@@ -764,7 +803,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
764
803
|
}
|
765
804
|
}
|
766
805
|
|
767
|
-
//
|
806
|
+
// JS available - wraps the `dropdown-list-item` navigation menu items
|
768
807
|
.gem-c-layout-super-navigation-header__navigation-second-items {
|
769
808
|
list-style: none;
|
770
809
|
margin: 0;
|
@@ -776,6 +815,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
776
815
|
}
|
777
816
|
}
|
778
817
|
|
818
|
+
// JS available - styling for the "government-activity" group of links
|
779
819
|
.gem-c-layout-super-navigation-header__column--government-activity {
|
780
820
|
position: relative;
|
781
821
|
|
@@ -784,6 +824,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
784
824
|
}
|
785
825
|
}
|
786
826
|
|
827
|
+
// JS available - styling for the "services-and-information" group of links
|
787
828
|
.gem-c-layout-super-navigation-header__navigation-second-items--services-and-information {
|
788
829
|
@include govuk-media-query($until: "desktop") {
|
789
830
|
border-bottom: 1px solid govuk-colour("mid-grey");
|
@@ -833,7 +874,7 @@ $after-button-padding-left: govuk-spacing(4);
|
|
833
874
|
.gem-c-layout-super-navigation-header__search-toggle-button--large-navbar {
|
834
875
|
padding-left: 24px;
|
835
876
|
padding-right: 24px;
|
836
|
-
width:
|
877
|
+
width: 69px;
|
837
878
|
}
|
838
879
|
|
839
880
|
.gem-c-layout-super-navigation-header__navigation-second-item-description {
|
@@ -875,8 +916,8 @@ $after-button-padding-left: govuk-spacing(4);
|
|
875
916
|
// isn't a multiple of 5 :(
|
876
917
|
.gem-c-layout-super-navigation-header__navigation-item-link--large-navbar,
|
877
918
|
.gem-c-layout-super-navigation-header__search-item-link--large-navbar {
|
878
|
-
padding-top:
|
879
|
-
padding-bottom:
|
919
|
+
padding-top: 26px;
|
920
|
+
padding-bottom: 26px;
|
880
921
|
}
|
881
922
|
|
882
923
|
.gem-c-layout-super-navigation-header__logotype-crown--large-navbar {
|
@@ -904,7 +945,8 @@ $after-button-padding-left: govuk-spacing(4);
|
|
904
945
|
.gem-c-layout-super-navigation-header__search-toggle-button--large-navbar {
|
905
946
|
height: $large-navbar-height;
|
906
947
|
// to stop the search icon moving on hover
|
907
|
-
padding-
|
948
|
+
padding-top: 26px;
|
949
|
+
padding-bottom: 26px;
|
908
950
|
}
|
909
951
|
|
910
952
|
.gem-c-layout-super-navigation-header__button-container--large-navbar {
|
@@ -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
|
@@ -2038,7 +2039,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
2038
2039
|
- !ruby/object:Gem::Version
|
2039
2040
|
version: '0'
|
2040
2041
|
requirements: []
|
2041
|
-
rubygems_version: 3.6.
|
2042
|
+
rubygems_version: 3.6.8
|
2042
2043
|
specification_version: 4
|
2043
2044
|
summary: A gem to document components in GOV.UK frontend applications
|
2044
2045
|
test_files: []
|