govuk_publishing_components 71.0.0 → 71.0.1
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/components/single-page-notification-button.js +128 -57
- data/app/assets/javascripts/govuk_publishing_components/lib/govspeak/table-scroll.js +18 -6
- data/app/assets/stylesheets/govuk_publishing_components/components/govspeak/_tables.scss +4 -0
- data/app/views/govuk_publishing_components/components/docs/govspeak.yml +1 -0
- data/lib/govuk_publishing_components/presenters/machine_readable/has_part_schema.rb +1 -3
- data/lib/govuk_publishing_components/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8e03438d3c963276e86d21ca8e2da336a8cd15220298b2ffbb57cbf53ca86e56
|
|
4
|
+
data.tar.gz: 8e2052d81d95e829bf5239d2cde0080fc21a62ba7aa2097fe707d131db63ae20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5278c358eb404521e67e9bb9fb95e8df365f7ae1a7df0ba88a9f43b57a5281d735206b7265c28cab4f06843ba30b57d43800b516ff701ed794c31c411bc36713
|
|
7
|
+
data.tar.gz: 5aecaf5b41cb5c02b8d85cd194b561e21b8d120adadd4f09dbd1bd6e07fb031d9eecde6d63cf4f3af955f8b0593967ba7b4d4a3d683594b24eb9c41e8161b0e7
|
|
@@ -1,68 +1,139 @@
|
|
|
1
|
-
/* global
|
|
1
|
+
/* global fetch, DOMException, AbortController */
|
|
2
2
|
(function (Modules) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
3
|
+
class SinglePageNotificationButton {
|
|
4
|
+
constructor ($module) {
|
|
5
|
+
/** @type {HTMLDivElement} */
|
|
6
|
+
this.$module = $module
|
|
7
|
+
|
|
8
|
+
/** @type {HTMLInputElement | null} */
|
|
9
|
+
this.basePathInput = this.$module.querySelector('input[name="base_path"]')
|
|
10
|
+
|
|
11
|
+
/** @type {HTMLButtonElement | null} */
|
|
12
|
+
this.button = this.$module.querySelector('.gem-c-button__outline--notification')
|
|
13
|
+
|
|
14
|
+
/** @type {string | null} */
|
|
15
|
+
this.buttonLocation = this.$module.getAttribute('data-button-location')
|
|
16
|
+
|
|
17
|
+
/** @type {string | null} */
|
|
18
|
+
this.customSubscribeText = this.$module.getAttribute('data-button-text-subscribe')
|
|
19
|
+
|
|
20
|
+
/** @type {string | null} */
|
|
21
|
+
this.customUnsubscribeText = this.$module.getAttribute('data-button-text-unsubscribe')
|
|
22
|
+
|
|
23
|
+
/** @type {string} */
|
|
24
|
+
this.buttonVisibleClass = 'gem-c-single-page-notification-button--visible'
|
|
25
|
+
|
|
26
|
+
/** @type {number | null} */
|
|
27
|
+
this.fetchRequestTimeoutId = null
|
|
28
|
+
}
|
|
13
29
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}.bind(this)
|
|
23
|
-
|
|
24
|
-
xhr.onreadystatechange = function () {
|
|
25
|
-
if (xhr.readyState === 4) {
|
|
26
|
-
if (xhr.status === 200) {
|
|
27
|
-
var responseText = xhr.responseText
|
|
28
|
-
// if response text exists and is JSON parse-able, parse the response and update the button html
|
|
29
|
-
if (responseText && this.responseIsJSON(responseText)) {
|
|
30
|
-
var active = JSON.parse(responseText).active
|
|
31
|
-
|
|
32
|
-
var customSubscribeText = this.$module.getAttribute('data-button-text-subscribe')
|
|
33
|
-
var customUnsubscribeText = this.$module.getAttribute('data-button-text-unsubscribe')
|
|
34
|
-
// Only set custom button text if both text items are provided
|
|
35
|
-
var customText = customSubscribeText && customUnsubscribeText
|
|
36
|
-
|
|
37
|
-
// If response returns active, user has subscribed to notifications
|
|
38
|
-
if (active === true) {
|
|
39
|
-
if (customText) {
|
|
40
|
-
this.$module.querySelector('.gem-c-button__outline--notification').textContent = customUnsubscribeText
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
if (customText) {
|
|
44
|
-
this.$module.querySelector('.gem-c-button__outline--notification').textContent = customSubscribeText
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
30
|
+
/**
|
|
31
|
+
* Initialise the single page notification button
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
async init () {
|
|
35
|
+
try {
|
|
36
|
+
if (this.button === null) {
|
|
37
|
+
throw new Error('Button required')
|
|
48
38
|
}
|
|
39
|
+
|
|
40
|
+
if (this.basePathInput === null) {
|
|
41
|
+
throw new Error('Base path required')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const personalisationEndpoint = this.getPersonalisationEndpoint(this.basePathInput.value, this.buttonLocation)
|
|
45
|
+
const personalisationData = await this.getPersonalisationData(personalisationEndpoint, 10000)
|
|
46
|
+
const customButtonText = this.getCustomButtonText(personalisationData.active, this.customSubscribeText, this.customUnsubscribeText)
|
|
47
|
+
|
|
48
|
+
if (customButtonText) {
|
|
49
|
+
this.button.textContent = customButtonText
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(error)
|
|
53
|
+
} finally {
|
|
49
54
|
this.makeVisible(this.$module)
|
|
55
|
+
clearTimeout(this.fetchRequestTimeoutId)
|
|
50
56
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get the personalisation data endpoint
|
|
61
|
+
*
|
|
62
|
+
* @param {string} basePath The base path to use
|
|
63
|
+
* @param {string | null} buttonLocation This attribute is passed through to the personalisation API to ensure the updated button has the same button_location for analytics
|
|
64
|
+
* @returns {string} The personalisation endpoint to use
|
|
65
|
+
*/
|
|
66
|
+
getPersonalisationEndpoint (basePath, buttonLocation) {
|
|
67
|
+
const checkEmailSubscriptionEndpoint = '/api/personalisation/check-email-subscription'
|
|
54
68
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return
|
|
69
|
+
if (buttonLocation) {
|
|
70
|
+
return `${checkEmailSubscriptionEndpoint}?base_path=${basePath}&button_location=${buttonLocation}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return `${checkEmailSubscriptionEndpoint}?base_path=${basePath}`
|
|
60
74
|
}
|
|
61
|
-
return true
|
|
62
|
-
}
|
|
63
75
|
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Fetch data from the personalisation endpoint for checking if an email subscription is active
|
|
78
|
+
*
|
|
79
|
+
* @param {string} personalisationEndpoint The personalisation URL to fetch data from
|
|
80
|
+
* @param {number} timer Set the time in milliseconds when the fetch request should abort
|
|
81
|
+
* @returns {Promise<EmailSubscriptionData>}
|
|
82
|
+
* @throws {DOMException} if the request times out
|
|
83
|
+
* @throws {Error} if the request response is not ok
|
|
84
|
+
*/
|
|
85
|
+
async getPersonalisationData (personalisationEndpoint, timer) {
|
|
86
|
+
const abortController = new AbortController()
|
|
87
|
+
this.fetchRequestTimeoutId = setTimeout(() => abortController.abort(new DOMException('Timeout', 'AbortError')), timer)
|
|
88
|
+
|
|
89
|
+
const response = await fetch(personalisationEndpoint, {
|
|
90
|
+
headers: { Accept: 'application/json' },
|
|
91
|
+
signal: abortController.signal
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
if (!response.ok) {
|
|
95
|
+
throw new Error(`Response status: ${response.status}`)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return await response.json()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Make the single page notification button visible
|
|
103
|
+
*
|
|
104
|
+
* @param {HTMLDivElement} module The element to add the visible class to
|
|
105
|
+
*/
|
|
106
|
+
makeVisible (module) {
|
|
107
|
+
module.classList.add(this.buttonVisibleClass)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get custom button text depending on whether the email subscription is active
|
|
112
|
+
*
|
|
113
|
+
* @param {boolean} activeState If the user has an email subscription to the page
|
|
114
|
+
* @param {string} customSubscribeText The custom subscribe text used when the activeState is false
|
|
115
|
+
* @param {string} customUnsubscribeText The custom subscribe text used when the activeState is true
|
|
116
|
+
* @returns {string | undefined} Returns the custom button text to use or `undefined` if the custom button text cannot be determined
|
|
117
|
+
*/
|
|
118
|
+
getCustomButtonText (activeState, customSubscribeText, customUnsubscribeText) {
|
|
119
|
+
if (typeof activeState !== 'boolean') return
|
|
120
|
+
// Only set custom button text if both text items are provided
|
|
121
|
+
if (!customSubscribeText || !customUnsubscribeText) {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (activeState === true) return customUnsubscribeText
|
|
126
|
+
if (activeState === false) return customSubscribeText
|
|
127
|
+
}
|
|
66
128
|
}
|
|
129
|
+
|
|
67
130
|
Modules.SinglePageNotificationButton = SinglePageNotificationButton
|
|
68
131
|
})(window.GOVUK.Modules)
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @typedef {object} EmailSubscriptionData
|
|
135
|
+
* @property {string} base_path the base_path parameter
|
|
136
|
+
* @property {boolean} active whether the email subscription is active
|
|
137
|
+
*
|
|
138
|
+
* @see {@link https://docs.publishing.service.gov.uk/repos/account-api/api.html#get-apipersonalisationcheck-email-subscription}
|
|
139
|
+
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* global ResizeObserver */
|
|
1
2
|
(function (GOVUK) {
|
|
2
3
|
'use strict'
|
|
3
4
|
|
|
@@ -17,15 +18,26 @@
|
|
|
17
18
|
this.windowResized()
|
|
18
19
|
}, 10)
|
|
19
20
|
|
|
21
|
+
// for all browsers
|
|
20
22
|
window.onresize = () => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
23
|
+
this.detectSizeChange()
|
|
24
|
+
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
// for Firefox with 'zoom text only' (does not trigger onresize)
|
|
27
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
28
|
+
this.detectSizeChange()
|
|
29
|
+
})
|
|
30
|
+
resizeObserver.observe(this.tables[0])
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
TableScroll.prototype.detectSizeChange = function () {
|
|
34
|
+
if (this.windowResizeTimeout) {
|
|
35
|
+
clearTimeout(this.windowResizeTimeout)
|
|
28
36
|
}
|
|
37
|
+
|
|
38
|
+
this.windowResizeTimeout = setTimeout(function () {
|
|
39
|
+
this.windowResized()
|
|
40
|
+
}.bind(this), 300)
|
|
29
41
|
}
|
|
30
42
|
|
|
31
43
|
TableScroll.prototype.windowResized = function () {
|
|
@@ -1128,6 +1128,7 @@ examples:
|
|
|
1128
1128
|
</tbody>
|
|
1129
1129
|
</table>
|
|
1130
1130
|
wide_table:
|
|
1131
|
+
description: Wide tables are enhanced with two features to help users navigate. Left/right indicators are added to show that more content is available to the sides, and a secondary scrollbar is added at the top of the table, that remains at the top of the page as the table is scrolled downwards. This allows users to scroll left/right at any point, which is useful where tables are taller than their browser.
|
|
1131
1132
|
data:
|
|
1132
1133
|
block: |
|
|
1133
1134
|
<table>
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
module GovukPublishingComponents
|
|
2
2
|
module Presenters
|
|
3
3
|
class HasPartSchema
|
|
4
|
-
attr_reader :has_part_url
|
|
5
|
-
|
|
6
4
|
def initialize(has_part_url)
|
|
7
5
|
@has_part_url = has_part_url
|
|
8
6
|
end
|
|
@@ -12,7 +10,7 @@ module GovukPublishingComponents
|
|
|
12
10
|
{
|
|
13
11
|
"@context" => "http://schema.org",
|
|
14
12
|
"@type" => "CreativeWork",
|
|
15
|
-
"sameAs" => has_part_url,
|
|
13
|
+
"sameAs" => @has_part_url,
|
|
16
14
|
}
|
|
17
15
|
end
|
|
18
16
|
end
|