govuk_publishing_components 32.1.0 → 33.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/component_guide/accessibility-test.js +0 -1
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-core.js +175 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-ecommerce-tracker.js +4 -4
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-event-tracker.js +5 -13
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-link-tracker.js +80 -309
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-page-views.js +2 -2
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-specialist-link-tracker.js +140 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4/init-ga4.js +3 -0
- data/app/assets/javascripts/govuk_publishing_components/analytics-ga4.js +1 -0
- data/app/assets/javascripts/govuk_publishing_components/components/accordion.js +12 -1
- data/app/assets/javascripts/govuk_publishing_components/components/single-page-notification-button.js +24 -8
- data/app/assets/javascripts/govuk_publishing_components/components/step-by-step-nav.js +22 -1
- data/app/assets/javascripts/govuk_publishing_components/vendor/lux/lux-reporter.js +140 -191
- data/app/assets/stylesheets/govuk_publishing_components/components/_big-number.scss +2 -5
- data/app/assets/stylesheets/govuk_publishing_components/components/_image-card.scss +1 -5
- data/app/assets/stylesheets/govuk_publishing_components/components/_input.scss +3 -5
- data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +10 -30
- data/app/assets/stylesheets/govuk_publishing_components/components/_search.scss +0 -7
- data/app/views/govuk_publishing_components/components/_accordion.html.erb +14 -1
- data/app/views/govuk_publishing_components/components/_error_summary.html.erb +27 -26
- data/app/views/govuk_publishing_components/components/_layout_super_navigation_header.html.erb +2 -2
- data/app/views/govuk_publishing_components/components/_phase_banner.html.erb +1 -1
- data/app/views/govuk_publishing_components/components/_share_links.html.erb +11 -13
- data/app/views/govuk_publishing_components/components/_single_page_notification_button.html.erb +1 -1
- data/app/views/govuk_publishing_components/components/_step_by_step_nav.html.erb +4 -1
- data/app/views/govuk_publishing_components/components/docs/accordion.yml +15 -3
- data/app/views/govuk_publishing_components/components/docs/button.yml +10 -0
- data/app/views/govuk_publishing_components/components/docs/share_links.yml +59 -30
- data/app/views/govuk_publishing_components/components/docs/single_page_notification_button.yml +10 -1
- data/app/views/govuk_publishing_components/components/docs/step_by_step_nav.yml +34 -0
- data/app/views/govuk_publishing_components/components/feedback/_problem_form.html.erb +1 -1
- data/app/views/govuk_publishing_components/components/feedback/_survey_signup_form.html.erb +1 -1
- data/app/views/govuk_publishing_components/components/feedback/_yes_no_banner.html.erb +3 -3
- data/lib/govuk_publishing_components/presenters/button_helper.rb +9 -2
- data/lib/govuk_publishing_components/presenters/single_page_notification_button_helper.rb +25 -1
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/axe-core/axe.js +4559 -4673
- data/node_modules/axe-core/axe.min.js +2 -2
- data/node_modules/axe-core/package.json +2 -2
- data/node_modules/axe-core/sri-history.json +4 -0
- data/node_modules/govuk-frontend/README.md +1 -2
- data/node_modules/govuk-frontend/govuk/all.js +1398 -273
- data/node_modules/govuk-frontend/govuk/common/closest-attribute-value.js +70 -0
- data/node_modules/govuk-frontend/govuk/common/index.js +172 -0
- data/node_modules/govuk-frontend/govuk/common/normalise-dataset.js +373 -0
- data/node_modules/govuk-frontend/govuk/common.js +138 -3
- data/node_modules/govuk-frontend/govuk/components/accordion/accordion.js +753 -25
- data/node_modules/govuk-frontend/govuk/components/accordion/fixtures.json +54 -22
- data/node_modules/govuk-frontend/govuk/components/accordion/macro-options.json +36 -0
- data/node_modules/govuk-frontend/govuk/components/accordion/template.njk +7 -1
- data/node_modules/govuk-frontend/govuk/components/back-link/fixtures.json +12 -12
- data/node_modules/govuk-frontend/govuk/components/breadcrumbs/fixtures.json +22 -22
- data/node_modules/govuk-frontend/govuk/components/button/_index.scss +23 -5
- data/node_modules/govuk-frontend/govuk/components/button/button.js +365 -107
- data/node_modules/govuk-frontend/govuk/components/button/fixtures.json +85 -66
- data/node_modules/govuk-frontend/govuk/components/button/template.njk +1 -1
- data/node_modules/govuk-frontend/govuk/components/character-count/_index.scss +9 -0
- data/node_modules/govuk-frontend/govuk/components/character-count/character-count.js +1033 -121
- data/node_modules/govuk-frontend/govuk/components/character-count/fixtures.json +112 -36
- data/node_modules/govuk-frontend/govuk/components/character-count/macro-options.json +42 -0
- data/node_modules/govuk-frontend/govuk/components/character-count/template.njk +27 -3
- data/node_modules/govuk-frontend/govuk/components/checkboxes/checkboxes.js +30 -2
- data/node_modules/govuk-frontend/govuk/components/checkboxes/fixtures.json +96 -93
- data/node_modules/govuk-frontend/govuk/components/cookie-banner/fixtures.json +46 -46
- data/node_modules/govuk-frontend/govuk/components/date-input/fixtures.json +50 -50
- data/node_modules/govuk-frontend/govuk/components/details/details.js +43 -13
- data/node_modules/govuk-frontend/govuk/components/details/fixtures.json +20 -20
- data/node_modules/govuk-frontend/govuk/components/error-message/fixtures.json +20 -20
- data/node_modules/govuk-frontend/govuk/components/error-summary/error-summary.js +268 -6
- data/node_modules/govuk-frontend/govuk/components/error-summary/fixtures.json +44 -35
- data/node_modules/govuk-frontend/govuk/components/error-summary/template.njk +25 -21
- data/node_modules/govuk-frontend/govuk/components/fieldset/fixtures.json +51 -39
- data/node_modules/govuk-frontend/govuk/components/file-upload/fixtures.json +26 -26
- data/node_modules/govuk-frontend/govuk/components/footer/_index.scss +1 -1
- data/node_modules/govuk-frontend/govuk/components/footer/fixtures.json +46 -46
- data/node_modules/govuk-frontend/govuk/components/footer/macro-options.json +2 -2
- data/node_modules/govuk-frontend/govuk/components/header/fixtures.json +93 -38
- data/node_modules/govuk-frontend/govuk/components/header/header.js +6 -0
- data/node_modules/govuk-frontend/govuk/components/header/macro-options.json +8 -2
- data/node_modules/govuk-frontend/govuk/components/header/template.njk +4 -2
- data/node_modules/govuk-frontend/govuk/components/hint/fixtures.json +12 -12
- data/node_modules/govuk-frontend/govuk/components/input/fixtures.json +80 -80
- data/node_modules/govuk-frontend/govuk/components/inset-text/fixtures.json +12 -12
- data/node_modules/govuk-frontend/govuk/components/label/fixtures.json +34 -34
- data/node_modules/govuk-frontend/govuk/components/notification-banner/fixtures.json +56 -46
- data/node_modules/govuk-frontend/govuk/components/notification-banner/notification-banner.js +252 -2
- data/node_modules/govuk-frontend/govuk/components/notification-banner/template.njk +1 -1
- data/node_modules/govuk-frontend/govuk/components/pagination/_index.scss +10 -7
- data/node_modules/govuk-frontend/govuk/components/pagination/fixtures.json +33 -26
- data/node_modules/govuk-frontend/govuk/components/panel/fixtures.json +18 -18
- data/node_modules/govuk-frontend/govuk/components/phase-banner/fixtures.json +14 -14
- data/node_modules/govuk-frontend/govuk/components/radios/fixtures.json +94 -91
- data/node_modules/govuk-frontend/govuk/components/radios/radios.js +30 -2
- data/node_modules/govuk-frontend/govuk/components/select/fixtures.json +32 -32
- data/node_modules/govuk-frontend/govuk/components/skip-link/fixtures.json +22 -20
- data/node_modules/govuk-frontend/govuk/components/skip-link/skip-link.js +10 -4
- data/node_modules/govuk-frontend/govuk/components/summary-list/fixtures.json +50 -50
- data/node_modules/govuk-frontend/govuk/components/table/_index.scss +1 -1
- data/node_modules/govuk-frontend/govuk/components/table/fixtures.json +40 -40
- data/node_modules/govuk-frontend/govuk/components/tabs/fixtures.json +29 -29
- data/node_modules/govuk-frontend/govuk/components/tabs/tabs.js +28 -0
- data/node_modules/govuk-frontend/govuk/components/tag/fixtures.json +28 -28
- data/node_modules/govuk-frontend/govuk/components/textarea/fixtures.json +34 -34
- data/node_modules/govuk-frontend/govuk/components/warning-text/fixtures.json +14 -14
- data/node_modules/govuk-frontend/govuk/core/_section-break.scss +1 -1
- data/node_modules/govuk-frontend/govuk/helpers/_colour.scss +2 -2
- data/node_modules/govuk-frontend/govuk/helpers/_links.scss +6 -6
- data/node_modules/govuk-frontend/govuk/i18n.js +390 -0
- data/node_modules/govuk-frontend/govuk/macros/i18n.njk +15 -0
- data/node_modules/govuk-frontend/govuk/settings/_all.scss +1 -0
- data/node_modules/govuk-frontend/govuk/settings/_colours-palette.scss +12 -0
- data/node_modules/govuk-frontend/govuk/settings/_compatibility.scss +26 -0
- data/node_modules/govuk-frontend/govuk/settings/_typography-font.scss +23 -0
- data/node_modules/govuk-frontend/govuk/settings/_typography-responsive.scss +12 -0
- data/node_modules/govuk-frontend/govuk/settings/_warnings.scss +53 -0
- data/node_modules/govuk-frontend/govuk/tools/_compatibility.scss +20 -6
- data/node_modules/govuk-frontend/govuk/vendor/polyfills/Date/now.js +21 -0
- data/node_modules/govuk-frontend/govuk/vendor/polyfills/Element/prototype/dataset.js +300 -0
- data/node_modules/govuk-frontend/govuk/vendor/polyfills/String/prototype/trim.js +21 -0
- data/node_modules/govuk-frontend/govuk-esm/all.mjs +50 -27
- data/node_modules/govuk-frontend/govuk-esm/common/closest-attribute-value.mjs +15 -0
- data/node_modules/govuk-frontend/govuk-esm/common/index.mjs +159 -0
- data/node_modules/govuk-frontend/govuk-esm/common/normalise-dataset.mjs +58 -0
- data/node_modules/govuk-frontend/govuk-esm/common.mjs +6 -28
- data/node_modules/govuk-frontend/govuk-esm/components/accordion/accordion.mjs +113 -43
- data/node_modules/govuk-frontend/govuk-esm/components/button/button.mjs +67 -30
- data/node_modules/govuk-frontend/govuk-esm/components/character-count/character-count.mjs +325 -123
- data/node_modules/govuk-frontend/govuk-esm/components/checkboxes/checkboxes.mjs +9 -3
- data/node_modules/govuk-frontend/govuk-esm/components/details/details.mjs +22 -8
- data/node_modules/govuk-frontend/govuk-esm/components/error-summary/error-summary.mjs +48 -6
- data/node_modules/govuk-frontend/govuk-esm/components/header/header.mjs +6 -0
- data/node_modules/govuk-frontend/govuk-esm/components/notification-banner/notification-banner.mjs +32 -2
- data/node_modules/govuk-frontend/govuk-esm/components/radios/radios.mjs +9 -3
- data/node_modules/govuk-frontend/govuk-esm/components/skip-link/skip-link.mjs +10 -4
- data/node_modules/govuk-frontend/govuk-esm/components/tabs/tabs.mjs +8 -2
- data/node_modules/govuk-frontend/govuk-esm/i18n.mjs +380 -0
- data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Date/now.mjs +13 -0
- data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/Element/prototype/dataset.mjs +68 -0
- data/node_modules/govuk-frontend/govuk-esm/vendor/polyfills/String/prototype/trim.mjs +13 -0
- data/node_modules/govuk-frontend/govuk-prototype-kit/init.js +7 -0
- data/node_modules/govuk-frontend/govuk-prototype-kit/init.scss +12 -0
- data/node_modules/govuk-frontend/govuk-prototype-kit.config.json +138 -7
- data/node_modules/govuk-frontend/package.json +1 -1
- metadata +22 -3
@@ -1,8 +1,106 @@
|
|
1
|
+
import '../../vendor/polyfills/Date/now.mjs'
|
1
2
|
import '../../vendor/polyfills/Function/prototype/bind.mjs'
|
2
|
-
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target
|
3
|
+
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normalisation
|
3
4
|
import '../../vendor/polyfills/Element/prototype/classList.mjs'
|
5
|
+
import { extractConfigByNamespace, mergeConfigs } from '../../common/index.mjs'
|
6
|
+
import { I18n } from '../../i18n.mjs'
|
7
|
+
import { normaliseDataset } from '../../common/normalise-dataset.mjs'
|
8
|
+
import { closestAttributeValue } from '../../common/closest-attribute-value.mjs'
|
9
|
+
|
10
|
+
/**
|
11
|
+
* @constant
|
12
|
+
* @type {CharacterCountTranslations}
|
13
|
+
* @see Default value for {@link CharacterCountConfig.i18n}
|
14
|
+
* @default
|
15
|
+
*/
|
16
|
+
var CHARACTER_COUNT_TRANSLATIONS = {
|
17
|
+
// Characters
|
18
|
+
charactersUnderLimit: {
|
19
|
+
one: 'You have %{count} character remaining',
|
20
|
+
other: 'You have %{count} characters remaining'
|
21
|
+
},
|
22
|
+
charactersAtLimit: 'You have 0 characters remaining',
|
23
|
+
charactersOverLimit: {
|
24
|
+
one: 'You have %{count} character too many',
|
25
|
+
other: 'You have %{count} characters too many'
|
26
|
+
},
|
27
|
+
// Words
|
28
|
+
wordsUnderLimit: {
|
29
|
+
one: 'You have %{count} word remaining',
|
30
|
+
other: 'You have %{count} words remaining'
|
31
|
+
},
|
32
|
+
wordsAtLimit: 'You have 0 words remaining',
|
33
|
+
wordsOverLimit: {
|
34
|
+
one: 'You have %{count} word too many',
|
35
|
+
other: 'You have %{count} words too many'
|
36
|
+
},
|
37
|
+
textareaDescription: {
|
38
|
+
other: ''
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* JavaScript enhancements for the CharacterCount component
|
44
|
+
*
|
45
|
+
* Tracks the number of characters or words in the `.govuk-js-character-count`
|
46
|
+
* `<textarea>` inside the element. Displays a message with the remaining number
|
47
|
+
* of characters/words available, or the number of characters/words in excess.
|
48
|
+
*
|
49
|
+
* You can configure the message to only appear after a certain percentage
|
50
|
+
* of the available characters/words has been entered.
|
51
|
+
*
|
52
|
+
* @class
|
53
|
+
* @param {HTMLElement} $module - The element this component controls
|
54
|
+
* @param {CharacterCountConfig} [config] - Character count config
|
55
|
+
*/
|
56
|
+
function CharacterCount ($module, config) {
|
57
|
+
if (!$module) {
|
58
|
+
return this
|
59
|
+
}
|
60
|
+
|
61
|
+
var defaultConfig = {
|
62
|
+
threshold: 0,
|
63
|
+
i18n: CHARACTER_COUNT_TRANSLATIONS
|
64
|
+
}
|
65
|
+
|
66
|
+
// Read config set using dataset ('data-' values)
|
67
|
+
var datasetConfig = normaliseDataset($module.dataset)
|
68
|
+
|
69
|
+
// To ensure data-attributes take complete precedence, even if they change the
|
70
|
+
// type of count, we need to reset the `maxlength` and `maxwords` from the
|
71
|
+
// JavaScript config.
|
72
|
+
//
|
73
|
+
// We can't mutate `config`, though, as it may be shared across multiple
|
74
|
+
// components inside `initAll`.
|
75
|
+
var configOverrides = {}
|
76
|
+
if ('maxwords' in datasetConfig || 'maxlength' in datasetConfig) {
|
77
|
+
configOverrides = {
|
78
|
+
maxlength: false,
|
79
|
+
maxwords: false
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
this.config = mergeConfigs(
|
84
|
+
defaultConfig,
|
85
|
+
config || {},
|
86
|
+
configOverrides,
|
87
|
+
datasetConfig
|
88
|
+
)
|
89
|
+
|
90
|
+
this.i18n = new I18n(extractConfigByNamespace(this.config, 'i18n'), {
|
91
|
+
// Read the fallback if necessary rather than have it set in the defaults
|
92
|
+
locale: closestAttributeValue($module, 'lang')
|
93
|
+
})
|
94
|
+
|
95
|
+
// Determine the limit attribute (characters or words)
|
96
|
+
if (this.config.maxwords) {
|
97
|
+
this.maxLength = this.config.maxwords
|
98
|
+
} else if (this.config.maxlength) {
|
99
|
+
this.maxLength = this.config.maxlength
|
100
|
+
} else {
|
101
|
+
return
|
102
|
+
}
|
4
103
|
|
5
|
-
function CharacterCount ($module) {
|
6
104
|
this.$module = $module
|
7
105
|
this.$textarea = $module.querySelector('.govuk-js-character-count')
|
8
106
|
this.$visibleCountMessage = null
|
@@ -10,26 +108,28 @@ function CharacterCount ($module) {
|
|
10
108
|
this.lastInputTimestamp = null
|
11
109
|
}
|
12
110
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
}
|
17
|
-
|
18
|
-
// Initialize component
|
111
|
+
/**
|
112
|
+
* Initialise component
|
113
|
+
*/
|
19
114
|
CharacterCount.prototype.init = function () {
|
20
115
|
// Check that required elements are present
|
21
116
|
if (!this.$textarea) {
|
22
117
|
return
|
23
118
|
}
|
24
119
|
|
25
|
-
// Check for module
|
26
|
-
var $module = this.$module
|
27
120
|
var $textarea = this.$textarea
|
28
|
-
var $
|
121
|
+
var $textareaDescription = document.getElementById($textarea.id + '-info')
|
122
|
+
|
123
|
+
// Inject a decription for the textarea if none is present already
|
124
|
+
// for when the component was rendered with no maxlength, maxwords
|
125
|
+
// nor custom textareaDescriptionText
|
126
|
+
if ($textareaDescription.innerText.match(/^\s*$/)) {
|
127
|
+
$textareaDescription.innerText = this.i18n.t('textareaDescription', { count: this.maxLength })
|
128
|
+
}
|
29
129
|
|
30
|
-
// Move the
|
130
|
+
// Move the textarea description to be immediately after the textarea
|
31
131
|
// Kept for backwards compatibility
|
32
|
-
$textarea.insertAdjacentElement('afterend', $
|
132
|
+
$textarea.insertAdjacentElement('afterend', $textareaDescription)
|
33
133
|
|
34
134
|
// Create the *screen reader* specific live-updating counter
|
35
135
|
// This doesn't need any styling classes, as it is never visible
|
@@ -37,36 +137,20 @@ CharacterCount.prototype.init = function () {
|
|
37
137
|
$screenReaderCountMessage.className = 'govuk-character-count__sr-status govuk-visually-hidden'
|
38
138
|
$screenReaderCountMessage.setAttribute('aria-live', 'polite')
|
39
139
|
this.$screenReaderCountMessage = $screenReaderCountMessage
|
40
|
-
$
|
140
|
+
$textareaDescription.insertAdjacentElement('afterend', $screenReaderCountMessage)
|
41
141
|
|
42
142
|
// Create our live-updating counter element, copying the classes from the
|
43
|
-
//
|
143
|
+
// textarea description for backwards compatibility as these may have been
|
144
|
+
// configured
|
44
145
|
var $visibleCountMessage = document.createElement('div')
|
45
|
-
$visibleCountMessage.className = $
|
146
|
+
$visibleCountMessage.className = $textareaDescription.className
|
46
147
|
$visibleCountMessage.classList.add('govuk-character-count__status')
|
47
148
|
$visibleCountMessage.setAttribute('aria-hidden', 'true')
|
48
149
|
this.$visibleCountMessage = $visibleCountMessage
|
49
|
-
$
|
150
|
+
$textareaDescription.insertAdjacentElement('afterend', $visibleCountMessage)
|
50
151
|
|
51
|
-
// Hide the
|
52
|
-
$
|
53
|
-
|
54
|
-
// Read options set using dataset ('data-' values)
|
55
|
-
this.options = this.getDataset($module)
|
56
|
-
|
57
|
-
// Determine the limit attribute (characters or words)
|
58
|
-
var countAttribute = this.defaults.characterCountAttribute
|
59
|
-
if (this.options.maxwords) {
|
60
|
-
countAttribute = this.defaults.wordCountAttribute
|
61
|
-
}
|
62
|
-
|
63
|
-
// Save the element limit
|
64
|
-
this.maxLength = $module.getAttribute(countAttribute)
|
65
|
-
|
66
|
-
// Check for limit
|
67
|
-
if (!this.maxLength) {
|
68
|
-
return
|
69
|
-
}
|
152
|
+
// Hide the textarea description
|
153
|
+
$textareaDescription.classList.add('govuk-visually-hidden')
|
70
154
|
|
71
155
|
// Remove hard limit if set
|
72
156
|
$textarea.removeAttribute('maxlength')
|
@@ -74,9 +158,9 @@ CharacterCount.prototype.init = function () {
|
|
74
158
|
this.bindChangeEvents()
|
75
159
|
|
76
160
|
// When the page is restored after navigating 'back' in some browsers the
|
77
|
-
// state of the character count is not restored until *after* the
|
78
|
-
// event is fired, so we need to manually update it after the
|
79
|
-
// in browsers that support it.
|
161
|
+
// state of the character count is not restored until *after* the
|
162
|
+
// DOMContentLoaded event is fired, so we need to manually update it after the
|
163
|
+
// pageshow event in browsers that support it.
|
80
164
|
if ('onpageshow' in window) {
|
81
165
|
window.addEventListener('pageshow', this.updateCountMessage.bind(this))
|
82
166
|
} else {
|
@@ -85,35 +169,12 @@ CharacterCount.prototype.init = function () {
|
|
85
169
|
this.updateCountMessage()
|
86
170
|
}
|
87
171
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
var attribute = attributes[i]
|
95
|
-
var match = attribute.name.match(/^data-(.+)/)
|
96
|
-
if (match) {
|
97
|
-
dataset[match[1]] = attribute.value
|
98
|
-
}
|
99
|
-
}
|
100
|
-
}
|
101
|
-
return dataset
|
102
|
-
}
|
103
|
-
|
104
|
-
// Counts characters or words in text
|
105
|
-
CharacterCount.prototype.count = function (text) {
|
106
|
-
var length
|
107
|
-
if (this.options.maxwords) {
|
108
|
-
var tokens = text.match(/\S+/g) || [] // Matches consecutive non-whitespace chars
|
109
|
-
length = tokens.length
|
110
|
-
} else {
|
111
|
-
length = text.length
|
112
|
-
}
|
113
|
-
return length
|
114
|
-
}
|
115
|
-
|
116
|
-
// Bind input propertychange to the elements and update based on the change
|
172
|
+
/**
|
173
|
+
* Bind change events
|
174
|
+
*
|
175
|
+
* Set up event listeners on the $textarea so that the count messages update
|
176
|
+
* when the user types.
|
177
|
+
*/
|
117
178
|
CharacterCount.prototype.bindChangeEvents = function () {
|
118
179
|
var $textarea = this.$textarea
|
119
180
|
$textarea.addEventListener('keyup', this.handleKeyUp.bind(this))
|
@@ -123,10 +184,52 @@ CharacterCount.prototype.bindChangeEvents = function () {
|
|
123
184
|
$textarea.addEventListener('blur', this.handleBlur.bind(this))
|
124
185
|
}
|
125
186
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
187
|
+
/**
|
188
|
+
* Handle key up event
|
189
|
+
*
|
190
|
+
* Update the visible character counter and keep track of when the last update
|
191
|
+
* happened for each keypress
|
192
|
+
*/
|
193
|
+
CharacterCount.prototype.handleKeyUp = function () {
|
194
|
+
this.updateVisibleCountMessage()
|
195
|
+
this.lastInputTimestamp = Date.now()
|
196
|
+
}
|
197
|
+
|
198
|
+
/**
|
199
|
+
* Handle focus event
|
200
|
+
*
|
201
|
+
* Speech recognition software such as Dragon NaturallySpeaking will modify the
|
202
|
+
* fields by directly changing its `value`. These changes don't trigger events
|
203
|
+
* in JavaScript, so we need to poll to handle when and if they occur.
|
204
|
+
*
|
205
|
+
* Once the keyup event hasn't been detected for at least 1000 ms (1s), check if
|
206
|
+
* the textarea value has changed and update the count message if it has.
|
207
|
+
*
|
208
|
+
* This is so that the update triggered by the manual comparison doesn't
|
209
|
+
* conflict with debounced KeyboardEvent updates.
|
210
|
+
*/
|
211
|
+
CharacterCount.prototype.handleFocus = function () {
|
212
|
+
this.valueChecker = setInterval(function () {
|
213
|
+
if (!this.lastInputTimestamp || (Date.now() - 500) >= this.lastInputTimestamp) {
|
214
|
+
this.updateIfValueChanged()
|
215
|
+
}
|
216
|
+
}.bind(this), 1000)
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Handle blur event
|
221
|
+
*
|
222
|
+
* Stop checking the textarea value once the textarea no longer has focus
|
223
|
+
*/
|
224
|
+
CharacterCount.prototype.handleBlur = function () {
|
225
|
+
// Cancel value checking on blur
|
226
|
+
clearInterval(this.valueChecker)
|
227
|
+
}
|
228
|
+
|
229
|
+
/**
|
230
|
+
* Update count message if textarea value has changed
|
231
|
+
*/
|
232
|
+
CharacterCount.prototype.updateIfValueChanged = function () {
|
130
233
|
if (!this.$textarea.oldValue) this.$textarea.oldValue = ''
|
131
234
|
if (this.$textarea.value !== this.$textarea.oldValue) {
|
132
235
|
this.$textarea.oldValue = this.$textarea.value
|
@@ -134,14 +237,20 @@ CharacterCount.prototype.checkIfValueChanged = function () {
|
|
134
237
|
}
|
135
238
|
}
|
136
239
|
|
137
|
-
|
138
|
-
|
240
|
+
/**
|
241
|
+
* Update count message
|
242
|
+
*
|
243
|
+
* Helper function to update both the visible and screen reader-specific
|
244
|
+
* counters simultaneously (e.g. on init)
|
245
|
+
*/
|
139
246
|
CharacterCount.prototype.updateCountMessage = function () {
|
140
247
|
this.updateVisibleCountMessage()
|
141
248
|
this.updateScreenReaderCountMessage()
|
142
249
|
}
|
143
250
|
|
144
|
-
|
251
|
+
/**
|
252
|
+
* Update visible count message
|
253
|
+
*/
|
145
254
|
CharacterCount.prototype.updateVisibleCountMessage = function () {
|
146
255
|
var $textarea = this.$textarea
|
147
256
|
var $visibleCountMessage = this.$visibleCountMessage
|
@@ -167,10 +276,12 @@ CharacterCount.prototype.updateVisibleCountMessage = function () {
|
|
167
276
|
}
|
168
277
|
|
169
278
|
// Update message
|
170
|
-
$visibleCountMessage.
|
279
|
+
$visibleCountMessage.innerText = this.getCountMessage()
|
171
280
|
}
|
172
281
|
|
173
|
-
|
282
|
+
/**
|
283
|
+
* Update screen reader count message
|
284
|
+
*/
|
174
285
|
CharacterCount.prototype.updateScreenReaderCountMessage = function () {
|
175
286
|
var $screenReaderCountMessage = this.$screenReaderCountMessage
|
176
287
|
|
@@ -183,69 +294,160 @@ CharacterCount.prototype.updateScreenReaderCountMessage = function () {
|
|
183
294
|
}
|
184
295
|
|
185
296
|
// Update message
|
186
|
-
$screenReaderCountMessage.
|
297
|
+
$screenReaderCountMessage.innerText = this.getCountMessage()
|
187
298
|
}
|
188
299
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
300
|
+
/**
|
301
|
+
* Count the number of characters (or words, if `config.maxwords` is set)
|
302
|
+
* in the given text
|
303
|
+
*
|
304
|
+
* @param {string} text - The text to count the characters of
|
305
|
+
* @returns {number} the number of characters (or words) in the text
|
306
|
+
*/
|
307
|
+
CharacterCount.prototype.count = function (text) {
|
308
|
+
if (this.config.maxwords) {
|
309
|
+
var tokens = text.match(/\S+/g) || [] // Matches consecutive non-whitespace chars
|
310
|
+
return tokens.length
|
311
|
+
} else {
|
312
|
+
return text.length
|
313
|
+
}
|
314
|
+
}
|
315
|
+
|
316
|
+
/**
|
317
|
+
* Get count message
|
318
|
+
*
|
319
|
+
* @returns {string} Status message
|
320
|
+
*/
|
321
|
+
CharacterCount.prototype.getCountMessage = function () {
|
322
|
+
var remainingNumber = this.maxLength - this.count(this.$textarea.value)
|
323
|
+
|
324
|
+
var countType = this.config.maxwords ? 'words' : 'characters'
|
325
|
+
return this.formatCountMessage(remainingNumber, countType)
|
326
|
+
}
|
194
327
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
328
|
+
/**
|
329
|
+
* Formats the message shown to users according to what's counted
|
330
|
+
* and how many remain
|
331
|
+
*
|
332
|
+
* @param {number} remainingNumber - The number of words/characaters remaining
|
333
|
+
* @param {string} countType - "words" or "characters"
|
334
|
+
* @returns {string} Status message
|
335
|
+
*/
|
336
|
+
CharacterCount.prototype.formatCountMessage = function (remainingNumber, countType) {
|
337
|
+
if (remainingNumber === 0) {
|
338
|
+
return this.i18n.t(countType + 'AtLimit')
|
200
339
|
}
|
201
|
-
charNoun = charNoun + ((remainingNumber === -1 || remainingNumber === 1) ? '' : 's')
|
202
340
|
|
203
|
-
|
204
|
-
displayNumber = Math.abs(remainingNumber)
|
341
|
+
var translationKeySuffix = remainingNumber < 0 ? 'OverLimit' : 'UnderLimit'
|
205
342
|
|
206
|
-
return
|
343
|
+
return this.i18n.t(countType + translationKeySuffix, { count: Math.abs(remainingNumber) })
|
207
344
|
}
|
208
345
|
|
209
|
-
|
210
|
-
|
211
|
-
|
346
|
+
/**
|
347
|
+
* Check if count is over threshold
|
348
|
+
*
|
349
|
+
* Checks whether the value is over the configured threshold for the input.
|
350
|
+
* If there is no configured threshold, it is set to 0 and this function will
|
351
|
+
* always return true.
|
352
|
+
*
|
353
|
+
* @returns {boolean} true if the current count is over the config.threshold
|
354
|
+
* (or no threshold is set)
|
355
|
+
*/
|
212
356
|
CharacterCount.prototype.isOverThreshold = function () {
|
357
|
+
// No threshold means we're always above threshold so save some computation
|
358
|
+
if (!this.config.threshold) {
|
359
|
+
return true
|
360
|
+
}
|
361
|
+
|
213
362
|
var $textarea = this.$textarea
|
214
|
-
var options = this.options
|
215
363
|
|
216
364
|
// Determine the remaining number of characters/words
|
217
365
|
var currentLength = this.count($textarea.value)
|
218
366
|
var maxLength = this.maxLength
|
219
367
|
|
220
|
-
|
221
|
-
var thresholdPercent = options.threshold ? options.threshold : 0
|
222
|
-
var thresholdValue = maxLength * thresholdPercent / 100
|
368
|
+
var thresholdValue = maxLength * this.config.threshold / 100
|
223
369
|
|
224
370
|
return (thresholdValue <= currentLength)
|
225
371
|
}
|
226
372
|
|
227
|
-
// Update the visible character counter and keep track of when the last update
|
228
|
-
// happened for each keypress
|
229
|
-
CharacterCount.prototype.handleKeyUp = function () {
|
230
|
-
this.updateVisibleCountMessage()
|
231
|
-
this.lastInputTimestamp = Date.now()
|
232
|
-
}
|
233
|
-
|
234
|
-
CharacterCount.prototype.handleFocus = function () {
|
235
|
-
// If the field is focused, and a keyup event hasn't been detected for at
|
236
|
-
// least 1000 ms (1 second), then run the manual change check.
|
237
|
-
// This is so that the update triggered by the manual comparison doesn't
|
238
|
-
// conflict with debounced KeyboardEvent updates.
|
239
|
-
this.valueChecker = setInterval(function () {
|
240
|
-
if (!this.lastInputTimestamp || (Date.now() - 500) >= this.lastInputTimestamp) {
|
241
|
-
this.checkIfValueChanged()
|
242
|
-
}
|
243
|
-
}.bind(this), 1000)
|
244
|
-
}
|
245
|
-
|
246
|
-
CharacterCount.prototype.handleBlur = function () {
|
247
|
-
// Cancel value checking on blur
|
248
|
-
clearInterval(this.valueChecker)
|
249
|
-
}
|
250
|
-
|
251
373
|
export default CharacterCount
|
374
|
+
|
375
|
+
/**
|
376
|
+
* Character count config
|
377
|
+
*
|
378
|
+
* @typedef {CharacterCountConfigWithMaxLength | CharacterCountConfigWithMaxWords} CharacterCountConfig
|
379
|
+
*/
|
380
|
+
|
381
|
+
/**
|
382
|
+
* Character count config (with maximum number of characters)
|
383
|
+
*
|
384
|
+
* @typedef {object} CharacterCountConfigWithMaxLength
|
385
|
+
* @property {number} [maxlength] - The maximum number of characters.
|
386
|
+
* If maxwords is provided, the maxlength option will be ignored.
|
387
|
+
* @property {number} [threshold = 0] - The percentage value of the limit at
|
388
|
+
* which point the count message is displayed. If this attribute is set, the
|
389
|
+
* count message will be hidden by default.
|
390
|
+
* @property {CharacterCountTranslations} [i18n = CHARACTER_COUNT_TRANSLATIONS] - See constant {@link CHARACTER_COUNT_TRANSLATIONS}
|
391
|
+
*/
|
392
|
+
|
393
|
+
/**
|
394
|
+
* Character count config (with maximum number of words)
|
395
|
+
*
|
396
|
+
* @typedef {object} CharacterCountConfigWithMaxWords
|
397
|
+
* @property {number} [maxwords] - The maximum number of words. If maxwords is
|
398
|
+
* provided, the maxlength option will be ignored.
|
399
|
+
* @property {number} [threshold = 0] - The percentage value of the limit at
|
400
|
+
* which point the count message is displayed. If this attribute is set, the
|
401
|
+
* count message will be hidden by default.
|
402
|
+
* @property {CharacterCountTranslations} [i18n = CHARACTER_COUNT_TRANSLATIONS] - See constant {@link CHARACTER_COUNT_TRANSLATIONS}
|
403
|
+
*/
|
404
|
+
|
405
|
+
/**
|
406
|
+
* Character count translations
|
407
|
+
*
|
408
|
+
* @typedef {object} CharacterCountTranslations
|
409
|
+
*
|
410
|
+
* Messages shown to users as they type. It provides feedback on how many words
|
411
|
+
* or characters they have remaining or if they are over the limit. This also
|
412
|
+
* includes a message used as an accessible description for the textarea.
|
413
|
+
* @property {TranslationPluralForms} [charactersUnderLimit] - Message displayed
|
414
|
+
* when the number of characters is under the configured maximum, `maxlength`.
|
415
|
+
* This message is displayed visually and through assistive technologies. The
|
416
|
+
* component will replace the `%{count}` placeholder with the number of
|
417
|
+
* remaining characters. This is a [pluralised list of
|
418
|
+
* messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
|
419
|
+
* @property {string} [charactersAtLimit] - Message displayed when the number of
|
420
|
+
* characters reaches the configured maximum, `maxlength`. This message is
|
421
|
+
* displayed visually and through assistive technologies.
|
422
|
+
* @property {TranslationPluralForms} [charactersOverLimit] - Message displayed
|
423
|
+
* when the number of characters is over the configured maximum, `maxlength`.
|
424
|
+
* This message is displayed visually and through assistive technologies. The
|
425
|
+
* component will replace the `%{count}` placeholder with the number of
|
426
|
+
* remaining characters. This is a [pluralised list of
|
427
|
+
* messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
|
428
|
+
* @property {TranslationPluralForms} [wordsUnderLimit] - Message displayed when
|
429
|
+
* the number of words is under the configured maximum, `maxlength`. This
|
430
|
+
* message is displayed visually and through assistive technologies. The
|
431
|
+
* component will replace the `%{count}` placeholder with the number of
|
432
|
+
* remaining words. This is a [pluralised list of
|
433
|
+
* messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
|
434
|
+
* @property {string} [wordsAtLimit] - Message displayed when the number of
|
435
|
+
* words reaches the configured maximum, `maxlength`. This message is
|
436
|
+
* displayed visually and through assistive technologies.
|
437
|
+
* @property {TranslationPluralForms} [wordsOverLimit] - Message displayed when
|
438
|
+
* the number of words is over the configured maximum, `maxlength`. This
|
439
|
+
* message is displayed visually and through assistive technologies. The
|
440
|
+
* component will replace the `%{count}` placeholder with the number of
|
441
|
+
* remaining words. This is a [pluralised list of
|
442
|
+
* messages](https://frontend.design-system.service.gov.uk/localise-govuk-frontend).
|
443
|
+
* @property {TranslationPluralForms} [textareaDescription] - Message made
|
444
|
+
* available to assistive technologies, if none is already present in the
|
445
|
+
* HTML, to describe that the component accepts only a limited amount of
|
446
|
+
* content. It is visible on the page when JavaScript is unavailable. The
|
447
|
+
* component will replace the `%{count}` placeholder with the value of the
|
448
|
+
* `maxlength` or `maxwords` parameter.
|
449
|
+
*/
|
450
|
+
|
451
|
+
/**
|
452
|
+
* @typedef {import('../../i18n.mjs').TranslationPluralForms} TranslationPluralForms
|
453
|
+
*/
|
@@ -2,8 +2,14 @@ import '../../vendor/polyfills/Function/prototype/bind.mjs'
|
|
2
2
|
// addEventListener, event.target normalization and DOMContentLoaded
|
3
3
|
import '../../vendor/polyfills/Event.mjs'
|
4
4
|
import '../../vendor/polyfills/Element/prototype/classList.mjs'
|
5
|
-
import { nodeListForEach } from '../../common.mjs'
|
5
|
+
import { nodeListForEach } from '../../common/index.mjs'
|
6
6
|
|
7
|
+
/**
|
8
|
+
* Checkboxes component
|
9
|
+
*
|
10
|
+
* @class
|
11
|
+
* @param {HTMLElement} $module - HTML element to use for checkboxes
|
12
|
+
*/
|
7
13
|
function Checkboxes ($module) {
|
8
14
|
this.$module = $module
|
9
15
|
this.$inputs = $module.querySelectorAll('input[type="checkbox"]')
|
@@ -73,7 +79,7 @@ Checkboxes.prototype.syncAllConditionalReveals = function () {
|
|
73
79
|
* Synchronise the visibility of the conditional reveal, and its accessible
|
74
80
|
* state, with the input's checked state.
|
75
81
|
*
|
76
|
-
* @param {HTMLInputElement} $input Checkbox input
|
82
|
+
* @param {HTMLInputElement} $input - Checkbox input
|
77
83
|
*/
|
78
84
|
Checkboxes.prototype.syncConditionalRevealWithInputState = function ($input) {
|
79
85
|
var $target = document.getElementById($input.getAttribute('aria-controls'))
|
@@ -131,7 +137,7 @@ Checkboxes.prototype.unCheckExclusiveInputs = function ($input) {
|
|
131
137
|
* Handle a click within the $module – if the click occurred on a checkbox, sync
|
132
138
|
* the state of any associated conditional reveal with the checkbox state.
|
133
139
|
*
|
134
|
-
* @param {MouseEvent} event Click event
|
140
|
+
* @param {MouseEvent} event - Click event
|
135
141
|
*/
|
136
142
|
Checkboxes.prototype.handleClick = function (event) {
|
137
143
|
var $target = event.target
|
@@ -6,11 +6,17 @@
|
|
6
6
|
*/
|
7
7
|
import '../../vendor/polyfills/Function/prototype/bind.mjs'
|
8
8
|
import '../../vendor/polyfills/Event.mjs' // addEventListener and event.target normaliziation
|
9
|
-
import { generateUniqueID } from '../../common.mjs'
|
9
|
+
import { generateUniqueID } from '../../common/index.mjs'
|
10
10
|
|
11
11
|
var KEY_ENTER = 13
|
12
12
|
var KEY_SPACE = 32
|
13
13
|
|
14
|
+
/**
|
15
|
+
* Details component
|
16
|
+
*
|
17
|
+
* @class
|
18
|
+
* @param {HTMLElement} $module - HTML element to use for details
|
19
|
+
*/
|
14
20
|
function Details ($module) {
|
15
21
|
this.$module = $module
|
16
22
|
}
|
@@ -77,9 +83,10 @@ Details.prototype.polyfillDetails = function () {
|
|
77
83
|
}
|
78
84
|
|
79
85
|
/**
|
80
|
-
* Define a statechange function that updates aria-expanded and style.display
|
81
|
-
*
|
82
|
-
|
86
|
+
* Define a statechange function that updates aria-expanded and style.display
|
87
|
+
*
|
88
|
+
* @returns {boolean} Returns true
|
89
|
+
*/
|
83
90
|
Details.prototype.polyfillSetAttributes = function () {
|
84
91
|
if (this.$module.hasAttribute('open')) {
|
85
92
|
this.$module.removeAttribute('open')
|
@@ -95,10 +102,11 @@ Details.prototype.polyfillSetAttributes = function () {
|
|
95
102
|
}
|
96
103
|
|
97
104
|
/**
|
98
|
-
* Handle cross-modal click events
|
99
|
-
*
|
100
|
-
* @param {
|
101
|
-
|
105
|
+
* Handle cross-modal click events
|
106
|
+
*
|
107
|
+
* @param {object} node - element
|
108
|
+
* @param {polyfillHandleInputsCallback} callback - function
|
109
|
+
*/
|
102
110
|
Details.prototype.polyfillHandleInputs = function (node, callback) {
|
103
111
|
node.addEventListener('keypress', function (event) {
|
104
112
|
var target = event.target
|
@@ -133,3 +141,9 @@ Details.prototype.polyfillHandleInputs = function (node, callback) {
|
|
133
141
|
}
|
134
142
|
|
135
143
|
export default Details
|
144
|
+
|
145
|
+
/**
|
146
|
+
* @callback polyfillHandleInputsCallback
|
147
|
+
* @param {KeyboardEvent} event - Keyboard event
|
148
|
+
* @returns {undefined}
|
149
|
+
*/
|