govuk_publishing_components 37.6.0 → 37.7.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-specialist-link-tracker.js +24 -2
- data/app/assets/javascripts/govuk_publishing_components/components/layout-super-navigation-header.js +63 -0
- data/app/assets/javascripts/govuk_publishing_components/lib/trigger-event.js +4 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/_contents-list.scss +1 -1
- data/app/views/govuk_publishing_components/components/_contents_list.html.erb +2 -0
- data/app/views/govuk_publishing_components/components/_govspeak.html.erb +11 -0
- data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +3 -4
- data/app/views/govuk_publishing_components/components/docs/contents_list.yml +1 -0
- data/app/views/govuk_publishing_components/components/docs/govspeak.yml +9 -0
- data/app/views/govuk_publishing_components/components/docs/layout_super_navigation_header.yml +2 -0
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/govuk-single-consent/README.md +157 -0
- data/node_modules/govuk-single-consent/dist/singleconsent.cjs.js +419 -0
- data/node_modules/govuk-single-consent/dist/singleconsent.esm.js +417 -0
- data/node_modules/govuk-single-consent/dist/singleconsent.iife.js +431 -0
- data/node_modules/govuk-single-consent/dist/singleconsent.iife.min.js +1 -0
- data/node_modules/govuk-single-consent/package.json +56 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53bea50d5b775c3e29b495eb9098ceb4d4708f081a7e0d3f6c18069de3f4e82b
|
4
|
+
data.tar.gz: dff2018e3a0320a9bb5f0822ec6145c656d9c4b8afee95a07173fc76f48c337e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 810afe26d85390892172d9424293c9b2937d6c39531470b680428929852ba512d9e2eb738286fdce53fbf1e55b84c7eab0a1119e3f41661771d429366cf83a49
|
7
|
+
data.tar.gz: aabf6aa8ee19cbbe2c86ecc666a86be58218a0bf5c61e5834cbd93741ad83ceb78458c1b3dd6f4dfccb74179743f7bcda67f1e75613c6807641cbe6654fab7a8
|
data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js
CHANGED
@@ -40,11 +40,33 @@ window.GOVUK.analyticsGa4.analyticsModules = window.GOVUK.analyticsGa4.analytics
|
|
40
40
|
return
|
41
41
|
}
|
42
42
|
|
43
|
-
//
|
44
|
-
if (element.closest('[data-ga4-
|
43
|
+
// Don't track this link if it's already being tracked by the ecommerce tracker
|
44
|
+
if (element.closest('[data-ga4-ecommerce-path]')) {
|
45
45
|
return
|
46
46
|
}
|
47
47
|
|
48
|
+
// Code below ensures the tracker plays nicely with the other link tracker
|
49
|
+
var otherLinkTracker = element.closest('[data-ga4-link]')
|
50
|
+
if (otherLinkTracker) {
|
51
|
+
var limitToElementClass = otherLinkTracker.getAttribute('data-ga4-limit-to-element-class')
|
52
|
+
|
53
|
+
if (!limitToElementClass) {
|
54
|
+
// If this link is inside the other link tracker, and the other link tracker IS NOT limiting itself to specific classes,
|
55
|
+
// then stop this tracker from firing, as the other tracker is responsible for this link.
|
56
|
+
return
|
57
|
+
} else {
|
58
|
+
// If this link is inside the other link tracker, but the other link tracker IS limiting itself to specific classes,
|
59
|
+
// then track the link here only if it is not within the specified classes that the other tracker is looking for.
|
60
|
+
var classes = limitToElementClass.split(',')
|
61
|
+
|
62
|
+
for (var i = 0; i < classes.length; i++) {
|
63
|
+
if (element.closest('.' + classes[i].trim())) {
|
64
|
+
return
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
48
70
|
var href = element.getAttribute('href')
|
49
71
|
|
50
72
|
if (!href) {
|
data/app/assets/javascripts/govuk_publishing_components/components/layout-super-navigation-header.js
CHANGED
@@ -93,6 +93,8 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
|
|
93
93
|
this.$module = $module
|
94
94
|
this.$searchToggle = this.$module.querySelector('#super-search-menu-toggle')
|
95
95
|
this.$searchMenu = this.$module.querySelector('#super-search-menu')
|
96
|
+
this.$navToggle = this.$module.querySelector('#super-navigation-menu-toggle')
|
97
|
+
this.$navMenu = this.$module.querySelector('#super-navigation-menu')
|
96
98
|
|
97
99
|
// The menu toggler buttons need three attributes for this to work:
|
98
100
|
// - `aria-controls` contains the id of the menu to be toggled
|
@@ -126,7 +128,68 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
|
|
126
128
|
toggle($target, $targetMenu)
|
127
129
|
}
|
128
130
|
|
131
|
+
SuperNavigationMegaMenu.prototype.handleKeyDown = function (event) {
|
132
|
+
var KEY_TAB = 9
|
133
|
+
var KEY_ESC = 27
|
134
|
+
var $navMenuLinks = this.$navMenu.querySelectorAll('li a')
|
135
|
+
var $firstNavLink = $navMenuLinks[0]
|
136
|
+
var $lastNavLink = $navMenuLinks[$navMenuLinks.length - 1]
|
137
|
+
var $searchMenuLinks = this.$searchMenu.querySelectorAll('li a')
|
138
|
+
var $lastSearchLink = $searchMenuLinks[$searchMenuLinks.length - 1]
|
139
|
+
|
140
|
+
if (event.keyCode === KEY_TAB) {
|
141
|
+
if (!this.$navMenu.hasAttribute('hidden')) {
|
142
|
+
switch (document.activeElement) {
|
143
|
+
case this.$navToggle:
|
144
|
+
if (!event.shiftKey) {
|
145
|
+
event.preventDefault()
|
146
|
+
$firstNavLink.focus()
|
147
|
+
}
|
148
|
+
break
|
149
|
+
case $lastNavLink:
|
150
|
+
if (!event.shiftKey) {
|
151
|
+
event.preventDefault()
|
152
|
+
this.$searchToggle.focus()
|
153
|
+
hide(this.$navToggle, this.$navMenu)
|
154
|
+
}
|
155
|
+
break
|
156
|
+
case $firstNavLink:
|
157
|
+
if (event.shiftKey) {
|
158
|
+
event.preventDefault()
|
159
|
+
this.$navToggle.focus()
|
160
|
+
}
|
161
|
+
break
|
162
|
+
case this.$searchToggle:
|
163
|
+
if (event.shiftKey) {
|
164
|
+
event.preventDefault()
|
165
|
+
$lastNavLink.focus()
|
166
|
+
}
|
167
|
+
break
|
168
|
+
default:
|
169
|
+
break
|
170
|
+
}
|
171
|
+
} else if (!this.$searchMenu.hasAttribute('hidden')) {
|
172
|
+
if (document.activeElement === $lastSearchLink) {
|
173
|
+
if (!event.shiftKey) {
|
174
|
+
hide(this.$searchToggle, this.$searchMenu)
|
175
|
+
}
|
176
|
+
}
|
177
|
+
}
|
178
|
+
} else if (event.keyCode === KEY_ESC) {
|
179
|
+
if (!this.$navMenu.hasAttribute('hidden')) {
|
180
|
+
hide(this.$navToggle, this.$navMenu)
|
181
|
+
this.$navToggle.focus()
|
182
|
+
} else if (!this.$searchMenu.hasAttribute('hidden')) {
|
183
|
+
hide(this.$searchToggle, this.$searchMenu)
|
184
|
+
this.$searchToggle.focus()
|
185
|
+
}
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
129
189
|
SuperNavigationMegaMenu.prototype.init = function () {
|
190
|
+
// Handle key events for tab and escape keys
|
191
|
+
this.$module.addEventListener('keydown', this.handleKeyDown.bind(this))
|
192
|
+
|
130
193
|
for (var j = 0; j < this.$buttons.length; j++) {
|
131
194
|
var $button = this.$buttons[j]
|
132
195
|
$button.addEventListener('click', this.buttonHandler.bind(this), true)
|
@@ -41,6 +41,7 @@
|
|
41
41
|
<% index_link = 1 unless disable_ga4 %>
|
42
42
|
<% contents.each.with_index(1) do |contents_item, position| %>
|
43
43
|
<li class="<%= cl_helper.list_item_classes(contents_item, false) %>" <%= "aria-current=true" if contents_item[:active] %>>
|
44
|
+
<span aria-hidden="true"></span>
|
44
45
|
<% link_text = format_numbers ? cl_helper.wrap_numbers_with_spans(contents_item[:text]) : contents_item[:text]
|
45
46
|
unless disable_ga4
|
46
47
|
ga4_data[:event_name] = cl_helper.get_ga4_event_name(contents_item[:href]) if contents_item[:href]
|
@@ -64,6 +65,7 @@
|
|
64
65
|
<ol class="gem-c-contents-list__nested-list">
|
65
66
|
<% contents_item[:items].each.with_index(1) do |nested_contents_item, nested_position| %>
|
66
67
|
<li class="<%= cl_helper.list_item_classes(nested_contents_item, true) %>" <%= "aria-current=true" if nested_contents_item[:active] %>>
|
68
|
+
<span aria-hidden="true"></span>
|
67
69
|
<%
|
68
70
|
unless disable_ga4
|
69
71
|
ga4_data[:event_name] = cl_helper.get_ga4_event_name(nested_contents_item[:href]) if nested_contents_item[:href]
|
@@ -10,9 +10,20 @@
|
|
10
10
|
classes << "disable-youtube" if disable_youtube_expansions
|
11
11
|
classes << "gem-c-govspeak--inverse" if inverse
|
12
12
|
|
13
|
+
disable_ga4 ||= false
|
14
|
+
|
13
15
|
data_modules = "govspeak"
|
16
|
+
data_modules << " ga4-link-tracker" unless disable_ga4
|
14
17
|
data_attributes = { module: data_modules }
|
15
18
|
|
19
|
+
unless disable_ga4
|
20
|
+
data_attributes.merge!({
|
21
|
+
ga4_track_links_only: "",
|
22
|
+
ga4_limit_to_element_class: "call-to-action, info-notice, help-notice, advisory",
|
23
|
+
ga4_link: { "event_name": "navigation", "type": "callout" }.to_json,
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
16
27
|
%>
|
17
28
|
|
18
29
|
<%= tag.div(class: "gem-c-govspeak govuk-govspeak " + classes.join(" "), data: data_attributes) do %>
|
data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb
CHANGED
@@ -162,7 +162,6 @@
|
|
162
162
|
|
163
163
|
<%
|
164
164
|
link = t("components.layout_super_navigation_header.navigation_link")
|
165
|
-
unique_id = SecureRandom.hex(4)
|
166
165
|
show_menu_text = show_navigation_menu_text
|
167
166
|
hide_menu_text = hide_navigation_menu_text
|
168
167
|
tracking_label = link[:label].downcase.gsub(/\s+/, "")
|
@@ -192,7 +191,7 @@
|
|
192
191
|
|
193
192
|
<%= content_tag(:button, {
|
194
193
|
aria: {
|
195
|
-
controls: "super-navigation-menu
|
194
|
+
controls: "super-navigation-menu",
|
196
195
|
expanded: false,
|
197
196
|
label: show_menu_text,
|
198
197
|
},
|
@@ -213,7 +212,7 @@
|
|
213
212
|
}
|
214
213
|
},
|
215
214
|
hidden: true,
|
216
|
-
id: "super-navigation-menu
|
215
|
+
id: "super-navigation-menu-toggle",
|
217
216
|
type: "button",
|
218
217
|
}) do %>
|
219
218
|
<%= tag.span link[:label], class: top_toggle_button_inner_classes %>
|
@@ -282,7 +281,7 @@
|
|
282
281
|
</div>
|
283
282
|
|
284
283
|
<%= content_tag(:div, {
|
285
|
-
id: "super-navigation-menu
|
284
|
+
id: "super-navigation-menu",
|
286
285
|
hidden: "",
|
287
286
|
class: dropdown_menu_classes,
|
288
287
|
}) do %>
|
@@ -18,6 +18,7 @@ accessibility_criteria: |
|
|
18
18
|
- convey the content structure
|
19
19
|
- indicate the current page when contents span different pages, and not link to itself
|
20
20
|
- include an aria-label to contextualise the list
|
21
|
+
- ensure dashes before each list item are hidden from screen readers
|
21
22
|
|
22
23
|
Links with formatted numbers must separate the number and text with a space for correct screen reader pronunciation. This changes pronunciation from "1 dot Item" to "1 Item".
|
23
24
|
shared_accessibility_criteria:
|
@@ -914,3 +914,12 @@ examples:
|
|
914
914
|
<p>Deforested area. Credit: Blue Ventures-Garth Cripps</p>
|
915
915
|
</figcaption>
|
916
916
|
</figure>
|
917
|
+
without_ga4_tracking:
|
918
|
+
description: |
|
919
|
+
Disables GA4 tracking on the component. Tracking is enabled by default. This adds a data module and data-attributes with JSON data. See the [ga4-link-tracker documentation](https://github.com/alphagov/govuk_publishing_components/blob/main/docs/analytics-ga4/ga4-link-tracker.md) for more information.
|
920
|
+
data:
|
921
|
+
block: |
|
922
|
+
<p>
|
923
|
+
<a href='https://www.gov.uk'>Hello World</a>
|
924
|
+
</p>
|
925
|
+
disable_ga4: true
|
data/app/views/govuk_publishing_components/components/docs/layout_super_navigation_header.yml
CHANGED
@@ -7,6 +7,8 @@ accessibility_criteria: |
|
|
7
7
|
The component must:
|
8
8
|
|
9
9
|
* have a text contrast ratio higher than 4.5:1 against the background colour to meet WCAG AA
|
10
|
+
* follow the expected tabbing border
|
11
|
+
* allow menus to be closed when the escape key is pressed
|
10
12
|
|
11
13
|
Images in the super navigation header must:
|
12
14
|
|
@@ -0,0 +1,157 @@
|
|
1
|
+
# Single Consent client
|
2
|
+
|
3
|
+
The Single Consent client is a small Javascript which can be embedded in a
|
4
|
+
website to enable easily sharing a user's consent or rejection of cookies across
|
5
|
+
different websites.
|
6
|
+
|
7
|
+
See the [Single Consent service README](../README.md).
|
8
|
+
|
9
|
+
## Quick start
|
10
|
+
|
11
|
+
See the [CommonJS example](https://github.com/alphagov/consent-api/blob/main/client/examples/commonJS-usage/index.js).
|
12
|
+
|
13
|
+
Contact data-tools-team@digital.cabinet-office.gov.uk to get the URL of the API endpoint.
|
14
|
+
|
15
|
+
The library provides the following static methods for finding out which types of
|
16
|
+
cookies a user has consented to.
|
17
|
+
|
18
|
+
- `hasConsentedToEssential`
|
19
|
+
- `hasConsentedToUsage`
|
20
|
+
- `hasConsentedToCampaigns`
|
21
|
+
- `hasConsentedToSetting`
|
22
|
+
|
23
|
+
The method `GovSingleConsent.getConsents()` provides the current state of the
|
24
|
+
user's consents to all types of cookies.
|
25
|
+
|
26
|
+
### 1. Install with npm
|
27
|
+
|
28
|
+
In order to set first-party cookies, the client Javascript must be served with
|
29
|
+
your application.
|
30
|
+
|
31
|
+
We recommend installing the Single Consent client using
|
32
|
+
[node package manager (npm)](https://www.npmjs.com/).
|
33
|
+
|
34
|
+
```sh
|
35
|
+
npm i govuk-single-consent
|
36
|
+
```
|
37
|
+
|
38
|
+
https://www.npmjs.com/package/govuk-single-consent
|
39
|
+
|
40
|
+
### 2. Including the Javascript client
|
41
|
+
|
42
|
+
The javascript client can be included in different ways. Choose one below.
|
43
|
+
|
44
|
+
#### CommonJS
|
45
|
+
|
46
|
+
See the [example](https://github.com/alphagov/consent-api/blob/main/client/examples/commonJS-usage/index.js).
|
47
|
+
|
48
|
+
#### Typescript
|
49
|
+
|
50
|
+
See the [example](https://github.com/alphagov/consent-api/blob/main/client/examples/typescript-usage/index.ts).
|
51
|
+
|
52
|
+
#### HTML script tag (IIFE)
|
53
|
+
|
54
|
+
The javascript client makes available the object `window.GovSingleConsent` for
|
55
|
+
interacting with the API. It needs to be loaded on any page that could be an
|
56
|
+
entry point to your web application, that allows modifying cookie consent, or
|
57
|
+
provides a link to another domain with which you want to share cookie consent
|
58
|
+
status. It is probably easiest to add the script to a base template used for all
|
59
|
+
pages.
|
60
|
+
|
61
|
+
On the same pages, you need to load your javascript for interacting with the
|
62
|
+
`window.GovSingleConsent` object.
|
63
|
+
|
64
|
+
See the following examples.
|
65
|
+
|
66
|
+
- [Cookie banner script](https://github.com/alphagov/consent-api/blob/main/client/example/cookie-banner.js)
|
67
|
+
- [Cooke page script](https://github.com/alphagov/consent-api/blob/main/client/example/cookies-page.js)
|
68
|
+
|
69
|
+
It is common practice to add Javascript tags just before the end `</body>` tag,
|
70
|
+
eg:
|
71
|
+
|
72
|
+
```html
|
73
|
+
...
|
74
|
+
|
75
|
+
<script src="{path_to_client_js}/singleconsent.js"></script>
|
76
|
+
<script src="{path_to_cookie_banner_script}.js"></script>
|
77
|
+
<script src="{path_to_cookies_page_script}.js"></script>
|
78
|
+
</body>
|
79
|
+
</html>
|
80
|
+
```
|
81
|
+
|
82
|
+
### 3. Passing the base URL to the constructor
|
83
|
+
|
84
|
+
You can either pass an environment string, or a custom base URL.
|
85
|
+
|
86
|
+
If using an environment string, its value should be either `staging` or `production`.
|
87
|
+
|
88
|
+
e.g
|
89
|
+
|
90
|
+
```javascript
|
91
|
+
const dummyCallback = () => {}
|
92
|
+
|
93
|
+
// With an environemnt string to the staging environment
|
94
|
+
new GovSingleConsent(dummyCallback, 'staging')
|
95
|
+
|
96
|
+
// With an environemnt string to the production environment
|
97
|
+
new GovSingleConsent(dummyCallback, 'production')
|
98
|
+
|
99
|
+
// With a custom base URL
|
100
|
+
new GovSingleConsent(dummyCallback, 'http://some-development-url.com')
|
101
|
+
```
|
102
|
+
|
103
|
+
### 4. Share the user's consent to cookies via the API
|
104
|
+
|
105
|
+
When the user interacts with your cookie banner or cookie settings page to
|
106
|
+
consent to or reject cookies you can update the central database by invoking the
|
107
|
+
following function:
|
108
|
+
|
109
|
+
```typescript
|
110
|
+
exampleCookieConsentStatusObject: Consents = {
|
111
|
+
essential: true,
|
112
|
+
settings: false,
|
113
|
+
usage: true,
|
114
|
+
campaigns: false,
|
115
|
+
}
|
116
|
+
|
117
|
+
singleConsentObject.setConsents(exampleCookieConsentStatusObject)
|
118
|
+
```
|
119
|
+
|
120
|
+
## Content Security Policy
|
121
|
+
|
122
|
+
If your website is served with a
|
123
|
+
[`Content-Security-Policy` HTTP header or `<meta>` element](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), you
|
124
|
+
may need to modify it to allow the client to access the Single Consent service.
|
125
|
+
The value of the header or meta element should contain the following:
|
126
|
+
|
127
|
+
```
|
128
|
+
connect-src 'self' https://consent-api-nw.a.run.app/api/v1/consent [... other site URLs separated by spaces];
|
129
|
+
```
|
130
|
+
|
131
|
+
## What the library does
|
132
|
+
|
133
|
+
The library manages all read/write operations with a cookie that stores the
|
134
|
+
state of a user's consent.
|
135
|
+
|
136
|
+
### Callback
|
137
|
+
|
138
|
+
Websites using the Consent API must provide a callback function. This will be
|
139
|
+
invoked each time the consent has been updated. It will be called with three
|
140
|
+
parameters:
|
141
|
+
|
142
|
+
- `consents` An object describing the new consent state.
|
143
|
+
- `consentsPreferencesSet` Boolean, the cookie banner must be displayed if this
|
144
|
+
value is `false`.
|
145
|
+
- `error` An object describing any error, otherwise `null`. If there is an
|
146
|
+
error, then the `consents` object will say that the consents have been
|
147
|
+
revoked.
|
148
|
+
|
149
|
+
The structure of the consent data object is currently based on the
|
150
|
+
[GOV.UK `cookies_policy` cookie](https://www.gov.uk/help/cookies). If your
|
151
|
+
website cookies do not fall into any of the four categories listed, please
|
152
|
+
contact us.
|
153
|
+
|
154
|
+
## Getting updates
|
155
|
+
|
156
|
+
To be notified when there's a new release, you can watch the
|
157
|
+
[consent-api Github repository](https://github.com/alphagov/consent-api).
|