govuk_publishing_components 43.4.1 → 43.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (21) hide show
  1. checksums.yaml +4 -4
  2. data/app/views/govuk_publishing_components/components/_radio.html.erb +2 -0
  3. data/app/views/govuk_publishing_components/components/docs/inverse_header.yml +2 -30
  4. data/app/views/govuk_publishing_components/components/docs/radio.yml +15 -0
  5. data/lib/govuk_publishing_components/version.rb +1 -1
  6. data/node_modules/accessible-autocomplete/CHANGELOG.md +8 -0
  7. data/node_modules/accessible-autocomplete/accessibility-criteria.md +2 -1
  8. data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js +1 -1
  9. data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js.map +1 -1
  10. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js +1 -1
  11. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js.map +1 -1
  12. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js +1 -1
  13. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js.map +1 -1
  14. data/node_modules/accessible-autocomplete/examples/ajax-source.html +300 -0
  15. data/node_modules/accessible-autocomplete/examples/form-single.html +4 -2
  16. data/node_modules/accessible-autocomplete/examples/index.html +7 -52
  17. data/node_modules/accessible-autocomplete/examples/suggestions.json +258 -0
  18. data/node_modules/accessible-autocomplete/package.json +15 -15
  19. data/node_modules/accessible-autocomplete/src/autocomplete.js +4 -2
  20. data/node_modules/accessible-autocomplete/test/functional/index.js +21 -5
  21. 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
  }