govuk_publishing_components 43.4.0 → 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.
Files changed (47) 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 +398 -0
  7. data/node_modules/accessible-autocomplete/CODEOWNERS +2 -0
  8. data/node_modules/accessible-autocomplete/CONTRIBUTING.md +161 -0
  9. data/node_modules/accessible-autocomplete/LICENSE.txt +20 -0
  10. data/node_modules/accessible-autocomplete/Procfile +1 -0
  11. data/node_modules/accessible-autocomplete/README.md +490 -0
  12. data/node_modules/accessible-autocomplete/accessibility-criteria.md +43 -0
  13. data/node_modules/accessible-autocomplete/app.json +15 -0
  14. data/node_modules/accessible-autocomplete/babel.config.js +29 -0
  15. data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.css +3 -0
  16. data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.css.map +1 -0
  17. data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js +2 -0
  18. data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js.map +1 -0
  19. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js +2 -0
  20. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js.map +1 -0
  21. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js +2 -0
  22. data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js.map +1 -0
  23. data/node_modules/accessible-autocomplete/examples/ajax-source.html +300 -0
  24. data/node_modules/accessible-autocomplete/examples/form-single.html +381 -0
  25. data/node_modules/accessible-autocomplete/examples/form.html +673 -0
  26. data/node_modules/accessible-autocomplete/examples/index.html +693 -0
  27. data/node_modules/accessible-autocomplete/examples/preact/index.html +346 -0
  28. data/node_modules/accessible-autocomplete/examples/react/index.html +347 -0
  29. data/node_modules/accessible-autocomplete/examples/suggestions.json +258 -0
  30. data/node_modules/accessible-autocomplete/package.json +93 -0
  31. data/node_modules/accessible-autocomplete/postcss.config.js +16 -0
  32. data/node_modules/accessible-autocomplete/preact.js +1 -0
  33. data/node_modules/accessible-autocomplete/react.js +1 -0
  34. data/node_modules/accessible-autocomplete/scripts/check-staged.mjs +16 -0
  35. data/node_modules/accessible-autocomplete/src/autocomplete.css +167 -0
  36. data/node_modules/accessible-autocomplete/src/autocomplete.js +610 -0
  37. data/node_modules/accessible-autocomplete/src/dropdown-arrow-down.js +11 -0
  38. data/node_modules/accessible-autocomplete/src/status.js +125 -0
  39. data/node_modules/accessible-autocomplete/src/wrapper.js +60 -0
  40. data/node_modules/accessible-autocomplete/test/functional/dropdown-arrow-down.js +46 -0
  41. data/node_modules/accessible-autocomplete/test/functional/index.js +809 -0
  42. data/node_modules/accessible-autocomplete/test/functional/wrapper.js +339 -0
  43. data/node_modules/accessible-autocomplete/test/integration/index.js +309 -0
  44. data/node_modules/accessible-autocomplete/test/karma.config.js +46 -0
  45. data/node_modules/accessible-autocomplete/test/wdio.config.js +123 -0
  46. data/node_modules/accessible-autocomplete/webpack.config.mjs +244 -0
  47. metadata +57 -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>