govuk_publishing_components 43.4.1 → 43.5.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/views/govuk_publishing_components/components/_radio.html.erb +2 -0
- data/app/views/govuk_publishing_components/components/docs/inverse_header.yml +2 -30
- data/app/views/govuk_publishing_components/components/docs/radio.yml +15 -0
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/accessible-autocomplete/CHANGELOG.md +8 -0
- data/node_modules/accessible-autocomplete/accessibility-criteria.md +2 -1
- data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js +1 -1
- data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js.map +1 -1
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js +1 -1
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js.map +1 -1
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js +1 -1
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js.map +1 -1
- data/node_modules/accessible-autocomplete/examples/ajax-source.html +300 -0
- data/node_modules/accessible-autocomplete/examples/form-single.html +4 -2
- data/node_modules/accessible-autocomplete/examples/index.html +7 -52
- data/node_modules/accessible-autocomplete/examples/suggestions.json +258 -0
- data/node_modules/accessible-autocomplete/package.json +15 -15
- data/node_modules/accessible-autocomplete/src/autocomplete.js +4 -2
- data/node_modules/accessible-autocomplete/test/functional/index.js +21 -5
- metadata +18 -2
@@ -0,0 +1,300 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
<title>Accessible Autocomplete AJAX source example</title>
|
7
|
+
<style>
|
8
|
+
/* Example page specific styling. */
|
9
|
+
html {
|
10
|
+
color: #111;
|
11
|
+
background: #FFF;
|
12
|
+
font-family: -apple-system, BlinkMacSystemFont, 'avenir next', avenir, 'helvetica neue', helvetica, ubuntu, roboto, noto, 'segoe ui', arial, sans-serif;
|
13
|
+
font-size: 16px;
|
14
|
+
line-height: 1.5;
|
15
|
+
}
|
16
|
+
|
17
|
+
body {
|
18
|
+
padding-left: 1rem;
|
19
|
+
padding-right: 1rem;
|
20
|
+
}
|
21
|
+
|
22
|
+
h1, h2, h3, h4, h5, h6 {
|
23
|
+
line-height: normal;
|
24
|
+
}
|
25
|
+
|
26
|
+
label {
|
27
|
+
display: block;
|
28
|
+
margin-bottom: .5rem;
|
29
|
+
}
|
30
|
+
|
31
|
+
code {
|
32
|
+
padding-left: .5em;
|
33
|
+
padding-right: .5em;
|
34
|
+
background: #EFEFEF;
|
35
|
+
font-weight: normal;
|
36
|
+
font-family: monospace;
|
37
|
+
}
|
38
|
+
|
39
|
+
main {
|
40
|
+
max-width: 40em;
|
41
|
+
margin-left: auto;
|
42
|
+
margin-right: auto;
|
43
|
+
}
|
44
|
+
|
45
|
+
.autocomplete-wrapper {
|
46
|
+
max-width: 20em;
|
47
|
+
margin-bottom: 4rem;
|
48
|
+
}
|
49
|
+
|
50
|
+
.submitted--hidden {
|
51
|
+
display: none;
|
52
|
+
}
|
53
|
+
</style>
|
54
|
+
<link rel="stylesheet" href="../dist/accessible-autocomplete.min.css">
|
55
|
+
</head>
|
56
|
+
<body>
|
57
|
+
<main>
|
58
|
+
<h1>Accessible Autocomplete AJAX source example</h1>
|
59
|
+
|
60
|
+
<div class="submitted submitted--hidden">
|
61
|
+
<p>You submitted:</p>
|
62
|
+
<ul>
|
63
|
+
<li><code>"last-location": <span class="submitted__last-location"></span></code></li>
|
64
|
+
</ul>
|
65
|
+
<hr />
|
66
|
+
</div>
|
67
|
+
|
68
|
+
<form action="form-single.html" method="get">
|
69
|
+
<label for="last-location">What was the last location you visited?</label>
|
70
|
+
<div class="autocomplete-wrapper">
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<button type="submit">Submit your answer</button>
|
74
|
+
</form>
|
75
|
+
</main>
|
76
|
+
|
77
|
+
<script type="text/javascript" src="../dist/accessible-autocomplete.min.js"></script>
|
78
|
+
<script type="text/javascript">
|
79
|
+
// Sending requests to a server means that when the autocomplete has no
|
80
|
+
// result it may not be because there are no results, but because these
|
81
|
+
// results are being fetched, or because an error happened. We can use the
|
82
|
+
// function for internationalising the 'No results found' message to
|
83
|
+
// provide a little more context to users.
|
84
|
+
//
|
85
|
+
// It'll rely on a `status` variable updated by the wrappers of the
|
86
|
+
// function making the request (see thereafter)
|
87
|
+
let status;
|
88
|
+
function tNoResults() {
|
89
|
+
if (status === 'loading') {
|
90
|
+
return 'Loading suggestions...'
|
91
|
+
} else if (status === 'error') {
|
92
|
+
return 'Sorry, an error occurred'
|
93
|
+
}else {
|
94
|
+
return 'No results found'
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
// The aim being to load suggestions from a server, we'll need a function
|
99
|
+
// that does just that. This one uses `fetch`, but you could also use
|
100
|
+
// XMLHttpRequest or whichever library is the most suitable to your
|
101
|
+
// project
|
102
|
+
//
|
103
|
+
// For lack of a actual server able of doing computation our endpoint will
|
104
|
+
// return the whole list of countries and we'll simulate the work of the
|
105
|
+
// server client-side
|
106
|
+
function requestSuggestions(query, fetchArgs = {}) {
|
107
|
+
return fetch('./suggestions.json', fetchArgs)
|
108
|
+
.then((response) => response.json())
|
109
|
+
}
|
110
|
+
|
111
|
+
// We'll wrap that function multiple times, each enhancing the previous
|
112
|
+
// wrapping to handle the the different behaviours necessary to
|
113
|
+
// appropriately coordinate requests to the server and display feedback to
|
114
|
+
// users
|
115
|
+
const makeRequest =
|
116
|
+
// Wrapping everything is the error handling to make sure it catches
|
117
|
+
// errors from any of the other wrappers
|
118
|
+
trackErrors(
|
119
|
+
// Next up is tracking whether we're loading new results
|
120
|
+
trackLoading(
|
121
|
+
// To avoid overloading the server with potentially costly requests
|
122
|
+
// as well as avoid wasting bandwidth while users are typing we'll
|
123
|
+
// only send requests a little bit after they stop typing
|
124
|
+
debounce(
|
125
|
+
// Finally we want to cancel requests that are already sent, so
|
126
|
+
// only the results of the last one update the UI This is the role
|
127
|
+
// of the next two wrappers
|
128
|
+
abortExisting(
|
129
|
+
// That last one is for demo only, to simulate server behaviours
|
130
|
+
// (latency, errors, filtering) on the client
|
131
|
+
simulateServer(
|
132
|
+
requestSuggestions
|
133
|
+
)
|
134
|
+
),
|
135
|
+
250
|
136
|
+
)
|
137
|
+
)
|
138
|
+
);
|
139
|
+
|
140
|
+
// We can then use the function we built and adapt it to the autocomplete
|
141
|
+
// API encapsulating the adjustments specific to rendering the 'No result
|
142
|
+
// found' message
|
143
|
+
function source(query, populateResults) {
|
144
|
+
// Start by clearing the results to ensure a loading message
|
145
|
+
// shows when a the query gets updated after results have loaded
|
146
|
+
populateResults([])
|
147
|
+
|
148
|
+
makeRequest(query)
|
149
|
+
// Only update the results if an actual array of options get returned
|
150
|
+
// allowing for `makeRequest` to avoid making updates to results being
|
151
|
+
// already displayed by resolving to `undefined`, like when we're
|
152
|
+
// aborting requests
|
153
|
+
.then(options => options && populateResults(options))
|
154
|
+
// In case of errors, we need to clear the results so the accessible
|
155
|
+
// autocomplate show its 'No result found'
|
156
|
+
.catch(error => populateResults([]))
|
157
|
+
}
|
158
|
+
|
159
|
+
// And finally we can set up our accessible autocomplete
|
160
|
+
const element = document.querySelector('.autocomplete-wrapper')
|
161
|
+
const id = 'autocomplete-default'
|
162
|
+
accessibleAutocomplete({
|
163
|
+
element: element,
|
164
|
+
id: id,
|
165
|
+
source: source,
|
166
|
+
tNoResults: tNoResults,
|
167
|
+
menuAttributes: {
|
168
|
+
"aria-labelledby": id
|
169
|
+
},
|
170
|
+
inputClasses: "govuk-input"
|
171
|
+
})
|
172
|
+
|
173
|
+
////
|
174
|
+
// INTERNAL DETAILS
|
175
|
+
////
|
176
|
+
|
177
|
+
// Technically, it'd be the server doing the filtering but for lack of
|
178
|
+
// server, we're requesting the whole list and filter client-side.
|
179
|
+
// Similarly, we'll use a specific query to trigger error for demo
|
180
|
+
// purpose, which will be easier than going in the devtools and making the
|
181
|
+
// request error We'll also simulate that the server takes a little time
|
182
|
+
// to respond to make things more visible in the UI
|
183
|
+
const SERVER_LATENCY = 2500;
|
184
|
+
function simulateServer(fn) {
|
185
|
+
return function(query, ...args) {
|
186
|
+
return new Promise(resolve => {
|
187
|
+
setTimeout(() => {
|
188
|
+
const suggestions = fn(query, ...args)
|
189
|
+
.then((response) => {
|
190
|
+
if (query === 'trigger-error') {
|
191
|
+
throw new Error('Custom error')
|
192
|
+
}
|
193
|
+
return response;
|
194
|
+
})
|
195
|
+
.then(countries => {
|
196
|
+
return countries.filter(country => country.toLowerCase().indexOf(query.toLowerCase()) !== -1)
|
197
|
+
})
|
198
|
+
|
199
|
+
resolve(suggestions)
|
200
|
+
}, SERVER_LATENCY)
|
201
|
+
})
|
202
|
+
}
|
203
|
+
}
|
204
|
+
|
205
|
+
// Debouncing limits the number of requests being sent
|
206
|
+
// but does not guarantee the order in which the responses come in
|
207
|
+
// Due to network and server latency, a response to an earlier request
|
208
|
+
// may come back after a response to a later request
|
209
|
+
// This keeps track of the AbortController of the last request sent
|
210
|
+
// so it can be cancelled before sending a new one
|
211
|
+
//
|
212
|
+
// NOTE: If you're using `XMLHttpRequest`s or a custom library,
|
213
|
+
// they'll have a different mechanism for aborting. You can either:
|
214
|
+
// - adapt this function to store whatever object lets you abort in-flight requests
|
215
|
+
// - or adapt your version of `requestSuggestion` to listen to the `signal`
|
216
|
+
// that this function passes to the wrapped function and trigger
|
217
|
+
// whichever API for aborting you have available
|
218
|
+
// See: https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal#events
|
219
|
+
let abortController;
|
220
|
+
function abortExisting(fn) {
|
221
|
+
return function(...args) {
|
222
|
+
if (abortController) {
|
223
|
+
abortController.abort();
|
224
|
+
}
|
225
|
+
|
226
|
+
abortController = new AbortController();
|
227
|
+
|
228
|
+
return fn(...args, {signal: abortController.signal})
|
229
|
+
.then(result => {
|
230
|
+
abortController = null;
|
231
|
+
return result;
|
232
|
+
}, error => {
|
233
|
+
// Aborting requests will lead to `fetch` rejecting with an
|
234
|
+
// `AbortError` In that situation, that's something we expect, so
|
235
|
+
// we don't want to show a message to users
|
236
|
+
if (error.name !== 'AbortError') {
|
237
|
+
abortController = null;
|
238
|
+
throw error;
|
239
|
+
}
|
240
|
+
})
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
// Debounces the given function so it only gets executed after a specific delay
|
245
|
+
function debounce(fn, wait) {
|
246
|
+
let timeout
|
247
|
+
return function (...args) {
|
248
|
+
return new Promise(resolve => {
|
249
|
+
clearTimeout(timeout)
|
250
|
+
|
251
|
+
const later = function () {
|
252
|
+
timeout = null
|
253
|
+
resolve(fn(...args))
|
254
|
+
}
|
255
|
+
timeout = setTimeout(later, wait)
|
256
|
+
})
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
|
261
|
+
// Tracks the loading state so we can adapt the message being displayed to the user
|
262
|
+
function trackLoading(fn) {
|
263
|
+
return function(...args) {
|
264
|
+
status = 'loading';
|
265
|
+
return fn(...args)
|
266
|
+
.then(result => {
|
267
|
+
status = null;
|
268
|
+
return result
|
269
|
+
}, error => {
|
270
|
+
status = null;
|
271
|
+
throw error
|
272
|
+
})
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
// In a similar fashion, we can track errors happening, which will adjust the message
|
277
|
+
function trackErrors(fn) {
|
278
|
+
return function(...args) {
|
279
|
+
return fn(...args)
|
280
|
+
.catch(error => {
|
281
|
+
status = 'error'
|
282
|
+
throw error
|
283
|
+
})
|
284
|
+
}
|
285
|
+
}
|
286
|
+
</script>
|
287
|
+
|
288
|
+
<script>
|
289
|
+
var queryStringParameters = window.location.search
|
290
|
+
var previouslySubmitted = queryStringParameters.length > 0
|
291
|
+
if (previouslySubmitted) {
|
292
|
+
var submittedEl = document.querySelector('.submitted')
|
293
|
+
submittedEl.classList.remove('submitted--hidden')
|
294
|
+
var params = new URLSearchParams(document.location.search.split('?')[1])
|
295
|
+
document.querySelector('.submitted__last-location').innerHTML = params.get('last-location')
|
296
|
+
document.querySelector('.submitted__passport-location').innerHTML = params.get('passport-location')
|
297
|
+
}
|
298
|
+
</script>
|
299
|
+
</body>
|
300
|
+
</html>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<head>
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
-
<title>Accessible Autocomplete form example</title>
|
6
|
+
<title>Accessible Autocomplete single field form example</title>
|
7
7
|
<style>
|
8
8
|
/* Example page specific styling. */
|
9
9
|
html {
|
@@ -55,7 +55,9 @@
|
|
55
55
|
</head>
|
56
56
|
<body>
|
57
57
|
<main>
|
58
|
-
<h1>Accessible Autocomplete form example</h1>
|
58
|
+
<h1>Accessible Autocomplete single field form example</h1>
|
59
|
+
|
60
|
+
<p><a href="ajax-source.html">Another HTML form example, simulating an AJAX request</a></p>
|
59
61
|
|
60
62
|
<div class="submitted submitted--hidden">
|
61
63
|
<p>You submitted:</p>
|
@@ -470,10 +470,7 @@
|
|
470
470
|
accessibleAutocomplete({
|
471
471
|
element: element,
|
472
472
|
id: id,
|
473
|
-
source: countries
|
474
|
-
menuAttributes: {
|
475
|
-
"aria-labelledby": id
|
476
|
-
}
|
473
|
+
source: countries
|
477
474
|
})
|
478
475
|
</script>
|
479
476
|
|
@@ -484,10 +481,7 @@
|
|
484
481
|
element: element,
|
485
482
|
id: id,
|
486
483
|
minLength: 2,
|
487
|
-
source: countries
|
488
|
-
menuAttributes: {
|
489
|
-
"aria-labelledby": id
|
490
|
-
}
|
484
|
+
source: countries
|
491
485
|
})
|
492
486
|
</script>
|
493
487
|
|
@@ -498,10 +492,7 @@
|
|
498
492
|
autoselect: true,
|
499
493
|
element: element,
|
500
494
|
id: id,
|
501
|
-
source: countries
|
502
|
-
menuAttributes: {
|
503
|
-
"aria-labelledby": id
|
504
|
-
}
|
495
|
+
source: countries
|
505
496
|
})
|
506
497
|
</script>
|
507
498
|
|
@@ -512,10 +503,7 @@
|
|
512
503
|
displayMenu: 'overlay',
|
513
504
|
element: element,
|
514
505
|
id: id,
|
515
|
-
source: countries
|
516
|
-
menuAttributes: {
|
517
|
-
"aria-labelledby": id
|
518
|
-
}
|
506
|
+
source: countries
|
519
507
|
})
|
520
508
|
</script>
|
521
509
|
|
@@ -526,10 +514,7 @@
|
|
526
514
|
defaultValue: 'Germany',
|
527
515
|
element: element,
|
528
516
|
id: id,
|
529
|
-
source: countries
|
530
|
-
menuAttributes: {
|
531
|
-
"aria-labelledby": id
|
532
|
-
}
|
517
|
+
source: countries
|
533
518
|
})
|
534
519
|
</script>
|
535
520
|
|
@@ -540,9 +525,6 @@
|
|
540
525
|
element: element,
|
541
526
|
id: id,
|
542
527
|
source: countries,
|
543
|
-
menuAttributes: {
|
544
|
-
"aria-labelledby": id
|
545
|
-
},
|
546
528
|
autoselect: true,
|
547
529
|
inputClasses: 'app-input'
|
548
530
|
})
|
@@ -555,9 +537,6 @@
|
|
555
537
|
element: element,
|
556
538
|
id: id,
|
557
539
|
source: countries,
|
558
|
-
menuAttributes: {
|
559
|
-
"aria-labelledby": id
|
560
|
-
},
|
561
540
|
autoselect: true,
|
562
541
|
hintClasses: 'app-hint'
|
563
542
|
})
|
@@ -570,9 +549,6 @@
|
|
570
549
|
element: element,
|
571
550
|
id: id,
|
572
551
|
source: countries,
|
573
|
-
menuAttributes: {
|
574
|
-
"aria-labelledby": id
|
575
|
-
},
|
576
552
|
autoselect: true,
|
577
553
|
menuClasses: 'custom-menu-class'
|
578
554
|
})
|
@@ -617,9 +593,6 @@
|
|
617
593
|
element: element,
|
618
594
|
id: id,
|
619
595
|
source: customSuggest,
|
620
|
-
menuAttributes: {
|
621
|
-
"aria-labelledby": id
|
622
|
-
},
|
623
596
|
templates: {
|
624
597
|
inputValue: inputValueTemplate,
|
625
598
|
suggestion: suggestionTemplate
|
@@ -635,9 +608,6 @@
|
|
635
608
|
id: id,
|
636
609
|
confirmOnBlur: false,
|
637
610
|
source: countries,
|
638
|
-
menuAttributes: {
|
639
|
-
"aria-labelledby": id
|
640
|
-
}
|
641
611
|
})
|
642
612
|
</script>
|
643
613
|
|
@@ -649,9 +619,6 @@
|
|
649
619
|
id: id,
|
650
620
|
placeholder: 'Search for a country',
|
651
621
|
source: countries,
|
652
|
-
menuAttributes: {
|
653
|
-
"aria-labelledby": id
|
654
|
-
}
|
655
622
|
})
|
656
623
|
</script>
|
657
624
|
|
@@ -662,10 +629,7 @@
|
|
662
629
|
element: element,
|
663
630
|
id: id,
|
664
631
|
showNoOptionsFound: false,
|
665
|
-
source: countries
|
666
|
-
menuAttributes: {
|
667
|
-
"aria-labelledby": id
|
668
|
-
}
|
632
|
+
source: countries
|
669
633
|
})
|
670
634
|
</script>
|
671
635
|
|
@@ -676,10 +640,7 @@
|
|
676
640
|
element: element,
|
677
641
|
id: id,
|
678
642
|
showAllValues: true,
|
679
|
-
source: countries
|
680
|
-
menuAttributes: {
|
681
|
-
"aria-labelledby": id
|
682
|
-
}
|
643
|
+
source: countries
|
683
644
|
})
|
684
645
|
</script>
|
685
646
|
|
@@ -690,9 +651,6 @@
|
|
690
651
|
element: element,
|
691
652
|
id: id,
|
692
653
|
showAllValues: false,
|
693
|
-
menuAttributes: {
|
694
|
-
"aria-labelledby": id
|
695
|
-
},
|
696
654
|
source: countries,
|
697
655
|
tStatusQueryTooShort: function (minQueryLength) {
|
698
656
|
return 'Tippe mindestens ' + minQueryLength + ' Zeichen ein für Resultate.'
|
@@ -725,9 +683,6 @@
|
|
725
683
|
id: id,
|
726
684
|
showAllValues: true,
|
727
685
|
source: countries,
|
728
|
-
menuAttributes: {
|
729
|
-
"aria-labelledby": id
|
730
|
-
},
|
731
686
|
dropdownArrow: function (config) {
|
732
687
|
return '<svg class="' + config.className + '" style="top: 8px;" viewBox="0 0 512 512" ><path d="M256,298.3L256,298.3L256,298.3l174.2-167.2c4.3-4.2,11.4-4.1,15.8,0.2l30.6,29.9c4.4,4.3,4.5,11.3,0.2,15.5L264.1,380.9 c-2.2,2.2-5.2,3.2-8.1,3c-3,0.1-5.9-0.9-8.1-3L35.2,176.7c-4.3-4.2-4.2-11.2,0.2-15.5L66,131.3c4.4-4.3,11.5-4.4,15.8-0.2L256,298.3 z"/></svg>'
|
733
688
|
}
|