govuk_publishing_components 43.3.0 → 43.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/images/govuk_publishing_components/icon-autocomplete-search-suggestion.svg +4 -0
- data/app/assets/javascripts/govuk_publishing_components/components/search-with-autocomplete.js +123 -0
- data/app/assets/stylesheets/govuk_publishing_components/components/_layout-super-navigation-header.scss +5 -4
- data/app/assets/stylesheets/govuk_publishing_components/components/_phase-banner.scss +0 -2
- data/app/assets/stylesheets/govuk_publishing_components/components/_search-with-autocomplete.scss +200 -0
- data/app/views/govuk_publishing_components/components/_layout_header.html.erb +16 -40
- data/app/views/govuk_publishing_components/components/_search.html.erb +16 -14
- data/app/views/govuk_publishing_components/components/_search_with_autocomplete.html.erb +27 -0
- data/app/views/govuk_publishing_components/components/docs/layout_header.yml +0 -41
- data/app/views/govuk_publishing_components/components/docs/phase_banner.yml +4 -0
- data/app/views/govuk_publishing_components/components/docs/search_with_autocomplete.yml +61 -0
- data/lib/govuk_publishing_components/version.rb +1 -1
- data/node_modules/accessible-autocomplete/CHANGELOG.md +390 -0
- data/node_modules/accessible-autocomplete/CODEOWNERS +2 -0
- data/node_modules/accessible-autocomplete/CONTRIBUTING.md +161 -0
- data/node_modules/accessible-autocomplete/LICENSE.txt +20 -0
- data/node_modules/accessible-autocomplete/Procfile +1 -0
- data/node_modules/accessible-autocomplete/README.md +490 -0
- data/node_modules/accessible-autocomplete/accessibility-criteria.md +42 -0
- data/node_modules/accessible-autocomplete/app.json +15 -0
- data/node_modules/accessible-autocomplete/babel.config.js +29 -0
- data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.css +3 -0
- data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.css.map +1 -0
- data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js +2 -0
- data/node_modules/accessible-autocomplete/dist/accessible-autocomplete.min.js.map +1 -0
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js +2 -0
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.preact.min.js.map +1 -0
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js +2 -0
- data/node_modules/accessible-autocomplete/dist/lib/accessible-autocomplete.react.min.js.map +1 -0
- data/node_modules/accessible-autocomplete/examples/form-single.html +379 -0
- data/node_modules/accessible-autocomplete/examples/form.html +673 -0
- data/node_modules/accessible-autocomplete/examples/index.html +738 -0
- data/node_modules/accessible-autocomplete/examples/preact/index.html +346 -0
- data/node_modules/accessible-autocomplete/examples/react/index.html +347 -0
- data/node_modules/accessible-autocomplete/package.json +93 -0
- data/node_modules/accessible-autocomplete/postcss.config.js +16 -0
- data/node_modules/accessible-autocomplete/preact.js +1 -0
- data/node_modules/accessible-autocomplete/react.js +1 -0
- data/node_modules/accessible-autocomplete/scripts/check-staged.mjs +16 -0
- data/node_modules/accessible-autocomplete/src/autocomplete.css +167 -0
- data/node_modules/accessible-autocomplete/src/autocomplete.js +608 -0
- data/node_modules/accessible-autocomplete/src/dropdown-arrow-down.js +11 -0
- data/node_modules/accessible-autocomplete/src/status.js +125 -0
- data/node_modules/accessible-autocomplete/src/wrapper.js +60 -0
- data/node_modules/accessible-autocomplete/test/functional/dropdown-arrow-down.js +46 -0
- data/node_modules/accessible-autocomplete/test/functional/index.js +793 -0
- data/node_modules/accessible-autocomplete/test/functional/wrapper.js +339 -0
- data/node_modules/accessible-autocomplete/test/integration/index.js +309 -0
- data/node_modules/accessible-autocomplete/test/karma.config.js +46 -0
- data/node_modules/accessible-autocomplete/test/wdio.config.js +123 -0
- data/node_modules/accessible-autocomplete/webpack.config.mjs +244 -0
- metadata +46 -2
@@ -0,0 +1,490 @@
|
|
1
|
+
# Accessible autocomplete
|
2
|
+
|
3
|
+
The accessible autocomplete is a component that helps users choose answers from a list you provide. You can also use it to make the answers you get from users more consistent.
|
4
|
+
|
5
|
+
If you're asking users to provide their country or territory, the [govuk-country-and-territory-autocomplete](https://github.com/alphagov/govuk-country-and-territory-autocomplete/blob/main/README.md) might be more appropriate.
|
6
|
+
|
7
|
+
[![npm version](https://img.shields.io/npm/v/accessible-autocomplete.svg)](http://npm.im/accessible-autocomplete)
|
8
|
+
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
|
9
|
+
[![gzip size](http://img.badgesize.io/https://unpkg.com/accessible-autocomplete/dist/accessible-autocomplete.min.js?compression=gzip)](https://unpkg.com/accessible-autocomplete/dist/accessible-autocomplete.min.js)
|
10
|
+
|
11
|
+
[![Sauce Labs Build Status](https://saucelabs.com/browser-matrix/tvararu-alphagov.svg)](https://saucelabs.com/u/tvararu-alphagov)
|
12
|
+
|
13
|
+
`accessible-autocomplete` is a JavaScript autocomplete built from the ground up to be accessible. The design goals are:
|
14
|
+
|
15
|
+
- **Accessibility**: Following WAI-ARIA best practices and testing with assistive technologies.
|
16
|
+
- **User experience**: Supporting a wide variety of user needs.
|
17
|
+
- **Compatibility**: Working with [recommended browsers](https://www.gov.uk/service-manual/technology/designing-for-different-browsers-and-devices#browsers-to-test-in) and [assistive technologies](https://www.gov.uk/service-manual/technology/testing-with-assistive-technologies#which-assistive-technologies-to-test-with).
|
18
|
+
|
19
|
+
[Try out the examples!](https://alphagov.github.io/accessible-autocomplete/examples/)
|
20
|
+
|
21
|
+
---
|
22
|
+
|
23
|
+
## Support
|
24
|
+
|
25
|
+
The GOV.UK Design System team maintains the accessible autocomplete as a standalone component. However, we’re only able to put in minimal work to support it.
|
26
|
+
|
27
|
+
[Read about our plans to maintain this component](https://github.com/alphagov/accessible-autocomplete/issues/532).
|
28
|
+
|
29
|
+
[Read more about the types of support we can provide](https://github.com/alphagov/accessible-autocomplete/issues/430).
|
30
|
+
|
31
|
+
---
|
32
|
+
|
33
|
+
## Installation / usage
|
34
|
+
|
35
|
+
### Using npm and a module system
|
36
|
+
|
37
|
+
Install it by running:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
npm install --save accessible-autocomplete
|
41
|
+
```
|
42
|
+
|
43
|
+
The `accessibleAutocomplete` function will render an autocomplete `<input>` and its accompanying suggestions and `aria-live` region. Your page should provide a `<label>` and a container element:
|
44
|
+
|
45
|
+
```html
|
46
|
+
<label for="my-autocomplete">Select your country</label>
|
47
|
+
<div id="my-autocomplete-container"></div>
|
48
|
+
```
|
49
|
+
|
50
|
+
Then import it using a module system like Browserify / Webpack / Rollup, and call the `accessibleAutocomplete` function, providing an array of values:
|
51
|
+
|
52
|
+
```js
|
53
|
+
import accessibleAutocomplete from 'accessible-autocomplete'
|
54
|
+
|
55
|
+
const countries = [
|
56
|
+
'France',
|
57
|
+
'Germany',
|
58
|
+
'United Kingdom'
|
59
|
+
]
|
60
|
+
|
61
|
+
accessibleAutocomplete({
|
62
|
+
element: document.querySelector('#my-autocomplete-container'),
|
63
|
+
id: 'my-autocomplete', // To match it to the existing <label>.
|
64
|
+
source: countries
|
65
|
+
})
|
66
|
+
```
|
67
|
+
|
68
|
+
If you want to use it as a replacement for a `<select>` element, read the [Progressive enhancement](#progressive-enhancement) section.
|
69
|
+
|
70
|
+
### As a plain JavaScript module
|
71
|
+
|
72
|
+
You can copy the [dist/accessible-autocomplete.min.js](dist/accessible-autocomplete.min.js) file to your JavaScript folder and import it into the browser:
|
73
|
+
|
74
|
+
```html
|
75
|
+
<script type="text/javascript" src="assets/js/accessible-autocomplete.min.js"></script>
|
76
|
+
```
|
77
|
+
|
78
|
+
### Styling the autocomplete
|
79
|
+
|
80
|
+
A stylesheet is included with the package at [dist/accessible-autocomplete.min.css](dist/accessible-autocomplete.min.css).
|
81
|
+
|
82
|
+
You can copy it to your stylesheets folder and import it into the browser:
|
83
|
+
|
84
|
+
```html
|
85
|
+
<link rel="stylesheet" href="assets/css/accessible-autocomplete.min.css" />
|
86
|
+
```
|
87
|
+
|
88
|
+
You can also import it using Sass:
|
89
|
+
|
90
|
+
```css
|
91
|
+
@import "accessible-autocomplete";
|
92
|
+
```
|
93
|
+
|
94
|
+
> [!NOTE]
|
95
|
+
>
|
96
|
+
> When styling the `.autocomplete__input` element, be aware that the `autoselect` option will render a second `.autocomplete__hint` input element for suggestion text. Both elements should be styled to ensure suggestions exactly align with the typed input text.
|
97
|
+
|
98
|
+
### Using with Preact
|
99
|
+
|
100
|
+
If you already use Preact in your application, you can import a bundle that will use that:
|
101
|
+
|
102
|
+
```js
|
103
|
+
import preact from 'preact'
|
104
|
+
import Autocomplete from 'accessible-autocomplete/preact'
|
105
|
+
|
106
|
+
preact.render(
|
107
|
+
<Autocomplete id='autocomplete' source={suggest} />,
|
108
|
+
document.querySelector('#container')
|
109
|
+
)
|
110
|
+
```
|
111
|
+
|
112
|
+
[Try out the Preact example!](https://alphagov.github.io/accessible-autocomplete/examples/preact/)
|
113
|
+
|
114
|
+
#### Preact versions
|
115
|
+
|
116
|
+
Preact v8.5.3 has been tested to work with the Accessible Autocomplete - although make sure to check out [documented issues](https://github.com/alphagov/accessible-autocomplete/issues).
|
117
|
+
|
118
|
+
Preact 10.19.6 has been incompletely tested with the Accessible Autocomplete. No issues were found in Chrome and Firefox, but our automated tests for picking an option using the keyboard failed in Internet Explorer 11 (an issue we could not replicate when testing manually, though).
|
119
|
+
|
120
|
+
We recommend you carry out thorough testing if you wish to use this or later versions of Preact.
|
121
|
+
|
122
|
+
### Using with React
|
123
|
+
|
124
|
+
If you already use React in your application, you can import a bundle that will use that:
|
125
|
+
|
126
|
+
```js
|
127
|
+
import React from 'react'
|
128
|
+
import ReactDOM from 'react-dom'
|
129
|
+
import Autocomplete from 'accessible-autocomplete/react'
|
130
|
+
|
131
|
+
ReactDOM.render(
|
132
|
+
<Autocomplete id='autocomplete' source={suggest} />,
|
133
|
+
document.querySelector('#container')
|
134
|
+
)
|
135
|
+
```
|
136
|
+
|
137
|
+
[Try out the React example!](https://alphagov.github.io/accessible-autocomplete/examples/react/)
|
138
|
+
|
139
|
+
#### React versions
|
140
|
+
|
141
|
+
React v15.5.4 has been tested to work with the Accessible Autocomplete - although make sure to check
|
142
|
+
out [documented issues](https://github.com/alphagov/accessible-autocomplete/issues).
|
143
|
+
|
144
|
+
React v15.6.2, v16.14.0, v17.0.2, and v18.2.0 have been incompletely tested with the Accessible Autocomplete. No undocumented issues were found (though be aware that [React 18 dropped support for Internet Explorer](https://react.dev/blog/2022/03/08/react-18-upgrade-guide#dropping-support-for-internet-explorer))
|
145
|
+
|
146
|
+
We recommend you carry out thorough testing if you wish to use this or later versions of React.
|
147
|
+
|
148
|
+
## API documentation
|
149
|
+
|
150
|
+
### Required options
|
151
|
+
|
152
|
+
#### `element`
|
153
|
+
|
154
|
+
Type: `HTMLElement`
|
155
|
+
|
156
|
+
The container element in which the autocomplete will be rendered in.
|
157
|
+
|
158
|
+
#### `id`
|
159
|
+
|
160
|
+
Type: `string`
|
161
|
+
|
162
|
+
The `id` to assign to the autocomplete input field, to use with a `<label for=id>`. Not required if using `enhanceSelectElement`.
|
163
|
+
|
164
|
+
#### `source`
|
165
|
+
|
166
|
+
Type: `Array | Function`
|
167
|
+
|
168
|
+
An array of values to search when the user types in the input field, or a function to take what the user types and call a callback function with the results to be displayed.
|
169
|
+
|
170
|
+
An example of an array of values:
|
171
|
+
|
172
|
+
```js
|
173
|
+
const countries = [
|
174
|
+
'France',
|
175
|
+
'Germany',
|
176
|
+
'United Kingdom'
|
177
|
+
]
|
178
|
+
```
|
179
|
+
|
180
|
+
If `source` is a function, the arguments are: `query: string, populateResults: Function`
|
181
|
+
|
182
|
+
Similar to the [`source` argument for typeahead.js](https://github.com/corejavascript/typeahead.js/blob/47d46b40cb834d8285ac9328c4b436e5eccf7197/doc/jquery_typeahead.md#datasets), a backing data source for suggestions. `query` is what gets typed into the input field, which will callback to `populateResults` synchronously with the array of string results to display in the menu.
|
183
|
+
|
184
|
+
An example of a simple suggestion engine:
|
185
|
+
|
186
|
+
```js
|
187
|
+
function suggest (query, populateResults) {
|
188
|
+
const results = [
|
189
|
+
'France',
|
190
|
+
'Germany',
|
191
|
+
'United Kingdom'
|
192
|
+
]
|
193
|
+
const filteredResults = results.filter(result => result.indexOf(query) !== -1)
|
194
|
+
populateResults(filteredResults)
|
195
|
+
}
|
196
|
+
```
|
197
|
+
|
198
|
+
### Other options
|
199
|
+
|
200
|
+
#### `inputClasses` (default: `null`)
|
201
|
+
|
202
|
+
Type: `string | null`
|
203
|
+
|
204
|
+
Adds custom html classes to the generated `input` element.
|
205
|
+
|
206
|
+
If [`autoselect`](#autoselect) is set to `true`, the option [`hintClasses`](#hintClasses) can be configured separately or it will default to the `inputClasses` value.
|
207
|
+
|
208
|
+
#### `hintClasses` (default: `null`)
|
209
|
+
|
210
|
+
Type: `string | null`
|
211
|
+
|
212
|
+
Adds custom html classes to the additional `input` element that appears when what the user typed matches the start of a suggestion.
|
213
|
+
|
214
|
+
If [`autoselect`](#autoselect) is set to `true`, the option [`inputClasses`](#inputClasses) will be used as the default value unless `hintClasses` is set to an empty string `''`.
|
215
|
+
|
216
|
+
#### `menuAttributes` (default: `{}`)
|
217
|
+
|
218
|
+
Type: `Object`
|
219
|
+
|
220
|
+
Sets html attributes and their values on the generated `ul` menu element. Useful for adding `aria-labelledby` and setting to the value of the `id` attribute on your existing label, to provide context to an assistive technology user.
|
221
|
+
|
222
|
+
> [!NOTE]
|
223
|
+
>
|
224
|
+
> To maintain assistive technology support, menu attributes `id`, `role` and `onMouseLeave` cannot be overridden using `menuAttributes`. Setting `className` will append to the component default and [`menuClasses`](#menuClasses) values.
|
225
|
+
|
226
|
+
|
227
|
+
#### `menuClasses` (default: `null`)
|
228
|
+
|
229
|
+
Type: `string | null`
|
230
|
+
|
231
|
+
Adds custom html classes to the generated `ul` menu element.
|
232
|
+
|
233
|
+
#### `autoselect` (default: `false`)
|
234
|
+
|
235
|
+
Type: `Boolean`
|
236
|
+
|
237
|
+
Set to true to highlight the first option when the user types in something and receives results. Pressing enter will select it.
|
238
|
+
|
239
|
+
#### `confirmOnBlur` (default: `true`)
|
240
|
+
|
241
|
+
Type: `Boolean`
|
242
|
+
|
243
|
+
The autocomplete will confirm the currently selected option when the user clicks outside of the component. Set to `false` to disable.
|
244
|
+
|
245
|
+
#### `cssNamespace` (default: `'autocomplete'`)
|
246
|
+
|
247
|
+
Type: `string`
|
248
|
+
|
249
|
+
Use this property to override the [BEM](http://getbem.com/) block name that the JavaScript component will use. You will need to rewrite the CSS class names to use your specified block name.
|
250
|
+
|
251
|
+
#### `defaultValue` (default: `''`)
|
252
|
+
|
253
|
+
Type: `string`
|
254
|
+
|
255
|
+
Specify a string to prefill the autocomplete with.
|
256
|
+
|
257
|
+
#### `displayMenu` (default: `'inline'`)
|
258
|
+
|
259
|
+
Type: `'inline' | 'overlay'`
|
260
|
+
|
261
|
+
You can set this property to specify the way the menu should appear, whether inline or as an overlay.
|
262
|
+
|
263
|
+
#### `minLength` (default: `0`)
|
264
|
+
|
265
|
+
Type: `number`
|
266
|
+
|
267
|
+
The minimum number of characters that should be entered before the autocomplete will attempt to suggest options. When the query length is under this, the aria status region will also provide helpful text to the user informing them they should type in more.
|
268
|
+
|
269
|
+
#### `name` (default: `'input-autocomplete'`)
|
270
|
+
|
271
|
+
Type: `string`
|
272
|
+
|
273
|
+
The `name` for the autocomplete input field, to use with a parent `<form>`.
|
274
|
+
|
275
|
+
#### `onConfirm` (default: `() => {}`)
|
276
|
+
|
277
|
+
Type: `Function`
|
278
|
+
|
279
|
+
Arguments: `confirmed: Object`
|
280
|
+
|
281
|
+
This function will be called when the user confirms an option, with the option they've confirmed.
|
282
|
+
|
283
|
+
#### `placeholder` (default: `''`) :warning: not recommended :warning:
|
284
|
+
|
285
|
+
Type: `string`
|
286
|
+
|
287
|
+
This option will populate the `placeholder` attribute on the input element.
|
288
|
+
|
289
|
+
We think [placeholders have usability issues](http://adamsilver.io/articles/placeholders-are-problematic/) and that there are [better alternatives to input placeholder text](https://design-system.service.gov.uk/components/text-input/#hint-text), so we do not recommend using this option.
|
290
|
+
|
291
|
+
#### `required` (default: `false`)
|
292
|
+
|
293
|
+
Type: `Boolean`
|
294
|
+
|
295
|
+
The input field will be rendered with a `required` attribute, see [W3C `required` attribute definition](https://www.w3.org/TR/html5/forms.html#the-required-attribute).
|
296
|
+
|
297
|
+
#### `showAllValues` (default: `false`)
|
298
|
+
|
299
|
+
Type: `Boolean`
|
300
|
+
|
301
|
+
If this is set to `true`, all values are shown when the user clicks the input. This is similar
|
302
|
+
to a default dropdown, so the autocomplete is rendered with a dropdown arrow to convey
|
303
|
+
this behaviour.
|
304
|
+
|
305
|
+
#### `showNoOptionsFound` (default: `true`)
|
306
|
+
|
307
|
+
Type: `Boolean`
|
308
|
+
|
309
|
+
The autocomplete will display a "No results found" template when there are no results. Set to `false` to disable.
|
310
|
+
|
311
|
+
#### `templates` (default: `undefined`)
|
312
|
+
|
313
|
+
Type:
|
314
|
+
|
315
|
+
```js
|
316
|
+
{
|
317
|
+
inputValue: Function,
|
318
|
+
suggestion: Function
|
319
|
+
}
|
320
|
+
```
|
321
|
+
|
322
|
+
This object defines templates (functions) that are used for displaying parts of the autocomplete.
|
323
|
+
|
324
|
+
`inputValue` is a function that receives one argument, the currently selected suggestion. It returns the string value to be inserted into the input.
|
325
|
+
|
326
|
+
`suggestion` is a function that receives one argument, a suggestion to be displayed. It is used when rendering suggestions, and should return a string, which can contain HTML.
|
327
|
+
|
328
|
+
:warning: **Caution:** because this function allows you to output arbitrary HTML, you should [make sure it's trusted](https://en.wikipedia.org/wiki/Cross-site_scripting), and accessible.
|
329
|
+
|
330
|
+
If your template includes child elements with defined foreground or background colours, check they display correctly in forced colors modes. For example, Windows high contrast mode.
|
331
|
+
|
332
|
+
#### `dropdownArrow` (default: A triangle pointing down)
|
333
|
+
|
334
|
+
Type: `Function`
|
335
|
+
|
336
|
+
A function that gets passed an object with the property `className` (`{ className: '' }`) and should return a string of HTML or a (P)React element. :warning: **Caution:** because this function allows you to output arbitrary HTML, you should [make sure it's trusted](https://en.wikipedia.org/wiki/Cross-site_scripting), and accessible.
|
337
|
+
|
338
|
+
### Internationalization
|
339
|
+
|
340
|
+
#### `tNoResults` (default: `() => 'No results found'`)
|
341
|
+
|
342
|
+
Type: `Function`
|
343
|
+
|
344
|
+
A function that receives no arguments and returns the text used in the dropdown to indicate that there are no results.
|
345
|
+
|
346
|
+
#### `tStatusQueryTooShort` (default: `` (minQueryLength) => `Type in ${minQueryLength} or more characters for results` ``)
|
347
|
+
|
348
|
+
Type: `Function`
|
349
|
+
|
350
|
+
A function that receives one argument that indicates the minimal amount of characters needed for the dropdown to trigger and returns the text used in the accessibility hint to indicate that the query is too short.
|
351
|
+
|
352
|
+
#### `tStatusNoResults` (default: `() => 'No search results'`)
|
353
|
+
|
354
|
+
Type: `Function`
|
355
|
+
|
356
|
+
A function that receives no arguments and returns the text that is used in the accessibility hint to indicate that there are no results.
|
357
|
+
|
358
|
+
#### `tStatusSelectedOption` (default: `` (selectedOption, length, index) => `${selectedOption} ${index + 1} of ${length} is highlighted` ``)
|
359
|
+
|
360
|
+
Type: `Function`
|
361
|
+
|
362
|
+
A function that receives three arguments -
|
363
|
+
the selectedOption, the count of available options, and the (zero-based) index of the selected option -
|
364
|
+
and returns the text used in the accessibility hint to indicate which option is selected.
|
365
|
+
|
366
|
+
#### `tStatusResults`
|
367
|
+
|
368
|
+
Default:
|
369
|
+
|
370
|
+
```js
|
371
|
+
(length, contentSelectedOption) => {
|
372
|
+
const words = {
|
373
|
+
result: (length === 1) ? 'result' : 'results',
|
374
|
+
is: (length === 1) ? 'is' : 'are'
|
375
|
+
}
|
376
|
+
|
377
|
+
return <span>{length} {words.result} {words.is} available. {contentSelectedOption}</span>
|
378
|
+
}
|
379
|
+
```
|
380
|
+
|
381
|
+
Type: `Function`
|
382
|
+
|
383
|
+
A function that receives two arguments - the count of available options and the return value of `tStatusSelectedOption` - and returns the text used in the accessibility hint to indicate which options are available and which is selected.
|
384
|
+
|
385
|
+
#### `tAssistiveHint` (default: `() => 'When autocomplete results are available use up and down arrows to review and enter to select. Touch device users, explore by touch or with swipe gestures.'`)
|
386
|
+
|
387
|
+
Type: `Function`
|
388
|
+
|
389
|
+
A function that receives no arguments and returns the text to be assigned as the aria description of the html `input` element, via the `aria-describedby` attribute.
|
390
|
+
This text is intended as an initial instruction to the assistive tech user. The `aria-describedby` attribute is automatically removed once user input is detected, in order to reduce screen reader verbosity.
|
391
|
+
|
392
|
+
|
393
|
+
## Progressive enhancement
|
394
|
+
|
395
|
+
If your autocomplete is meant to select from a small list of options (a few hundred), we strongly suggest that you render a `<select>` menu on the server, and use progressive enhancement.
|
396
|
+
|
397
|
+
If you have the following HTML:
|
398
|
+
|
399
|
+
```html
|
400
|
+
<select id="location-picker">
|
401
|
+
<option value="fr">France</option>
|
402
|
+
<option value="de">Germany</option>
|
403
|
+
<option value="gb">United Kingdom</option>
|
404
|
+
</select>
|
405
|
+
```
|
406
|
+
|
407
|
+
You can use the `accessibleAutocomplete.enhanceSelectElement` function to enhance it into an autocomplete:
|
408
|
+
|
409
|
+
```js
|
410
|
+
accessibleAutocomplete.enhanceSelectElement({
|
411
|
+
selectElement: document.querySelector('#location-picker')
|
412
|
+
})
|
413
|
+
```
|
414
|
+
|
415
|
+
This will:
|
416
|
+
|
417
|
+
- Place an autocomplete input field after the specified `<select>`
|
418
|
+
- Default the autocomplete `autoselect` to `true`
|
419
|
+
- Default the autocomplete `defaultValue` to the select's `option[selected]`
|
420
|
+
- Default the autocomplete `id` to the `<select>`'s `id`
|
421
|
+
- Default the autocomplete `name` attribute to `''` to prevent it being included in form submissions
|
422
|
+
- Default the autocomplete `source` to use existing `<option>`s from the `<select>`
|
423
|
+
- Hide the `<select>` using inline `display: none`
|
424
|
+
- Set the `<select>`'s `id` to `${id}-select` to decouple from any `<label>`
|
425
|
+
- Upon confirming a value in the autocomplete, update the original `<select>`
|
426
|
+
|
427
|
+
This function takes the same options as `accessibleAutocomplete`, with the only difference being that it uses `selectElement` instead of `element`, which needs to be an instance of `HTMLSelectElement`.
|
428
|
+
|
429
|
+
> **Note**: The `accessibleAutocomplete.enhanceSelectElement` function is fairly light and wraps the public API for `accessibleAutocomplete`. If your use case doesn't fit the above defaults, try [reading the source](src/wrapper.js) and seeing if you can write your own.
|
430
|
+
|
431
|
+
### Null options
|
432
|
+
|
433
|
+
If your `<select>` element has a "null" option - a default option with no value - then you can pass a `defaultValue` option to `enhanceSelectElement` which will replace the label of this option when it is selected.
|
434
|
+
|
435
|
+
With the following HTML:
|
436
|
+
|
437
|
+
```html
|
438
|
+
<select id="location-picker">
|
439
|
+
<option value="">Select a country</option>
|
440
|
+
<option value="fr">France</option>
|
441
|
+
<option value="de">Germany</option>
|
442
|
+
<option value="gb">United Kingdom</option>
|
443
|
+
</select>
|
444
|
+
```
|
445
|
+
|
446
|
+
Then passing a `defaultValue` option of `''` will then leave the autocomplete blank if the null option is selected.
|
447
|
+
|
448
|
+
```js
|
449
|
+
accessibleAutocomplete.enhanceSelectElement({
|
450
|
+
defaultValue: '',
|
451
|
+
selectElement: document.querySelector('#location-picker')
|
452
|
+
})
|
453
|
+
```
|
454
|
+
|
455
|
+
Any null options will also be filtered out of the options used to populate the `source` of the autocomplete element. To preserve options with no value in the autocomplete, then pass a `preserveNullOptions` flag of `true` to `enhanceSelectElement`.
|
456
|
+
|
457
|
+
## Analytics and tracking
|
458
|
+
|
459
|
+
The following events get triggered on the input element during the life cycle of the autocomplete:
|
460
|
+
|
461
|
+
- `onConfirm` - This function will be called when the user confirms an option, with the option they've chosen.
|
462
|
+
|
463
|
+
Example usage:
|
464
|
+
|
465
|
+
```js
|
466
|
+
accessibleAutocomplete({
|
467
|
+
// additional options
|
468
|
+
onConfirm: (val) => {
|
469
|
+
track(val)
|
470
|
+
}
|
471
|
+
})
|
472
|
+
```
|
473
|
+
|
474
|
+
## Why another autocomplete?
|
475
|
+
|
476
|
+
`accessible-autocomplete` was built after studying many existing solutions and prototyping patches to fix user experience or accessibility issues. It draws heavy inspiration from the following (and a lot of others):
|
477
|
+
|
478
|
+
- [ljwatson/design-patterns](http://ljwatson.github.io/design-patterns/autocomplete/index.html): great accessible experience
|
479
|
+
- [corejavascript/corejs-typeahead](https://github.com/corejavascript/typeahead.js): flexible autocomplete/suggestion engine architecture
|
480
|
+
- [JamieAppleseed/selectToAutocomplete](https://github.com/JamieAppleseed/selectToAutocomplete): ease of use
|
481
|
+
|
482
|
+
## Developing locally
|
483
|
+
|
484
|
+
Check out the [CONTRIBUTING](CONTRIBUTING.md) guide for instructions.
|
485
|
+
|
486
|
+
If you want to help and want to get more familiar with the codebase, try starting with the ["good for beginners"](https://github.com/alphagov/accessible-autocomplete/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+for+beginners%22) issues.
|
487
|
+
|
488
|
+
## License
|
489
|
+
|
490
|
+
[MIT](LICENSE.txt).
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Accessibility Acceptance Criteria
|
2
|
+
|
3
|
+
Authors: [Theodor Vararu](https://github.com/tvararu), [Léonie Watson](https://github.com/LJWatson), [Ed Horsford](https://github.com/edwardhorsford).
|
4
|
+
|
5
|
+
## What's this?
|
6
|
+
|
7
|
+
This describes the necessary behaviours that an autocomplete needs to meet to be usable by assistive technologies.
|
8
|
+
|
9
|
+
They are useful if you are evaluating `accessible-autocomplete`, or a different autocomplete solution.
|
10
|
+
|
11
|
+
## User story
|
12
|
+
|
13
|
+
> As an Assistive Technology (AT) user, I want to be able to search using autocomplete, so I can find and choose a matching result easily and accurately.
|
14
|
+
|
15
|
+
## Acceptance criteria
|
16
|
+
|
17
|
+
The field with autocomplete must:
|
18
|
+
|
19
|
+
1. Be focusable with a keyboard
|
20
|
+
1. Indicate when it has keyboard focus
|
21
|
+
1. Inform the user that it is an editable field
|
22
|
+
1. Inform the user if there is a pre-filled value
|
23
|
+
1. Inform the user that autocomplete is available
|
24
|
+
1. Explain how to use autocomplete
|
25
|
+
1. Inform the user that content has been expanded
|
26
|
+
1. Inform the user when there are matches, or if there are no matches
|
27
|
+
1. (Optional) Inform the user how many matches are currently available
|
28
|
+
1. Inform the user as the number of matches changes
|
29
|
+
1. Enable the user to navigate the available matches using touch or keyboard
|
30
|
+
1. Inform the user when a match is selected
|
31
|
+
1. (Optional) Inform the user which number the currently selected match is (1 of 3 for example)
|
32
|
+
1. Inform the user if a match is pre-selected
|
33
|
+
1. Enable the user to confirm the selected match
|
34
|
+
1. Inform the user when a match is confirmed
|
35
|
+
1. Return focus to the editable field when a selected match is confirmed
|
36
|
+
|
37
|
+
Helpful definitions:
|
38
|
+
|
39
|
+
- `navigate`: When the user selects between matches
|
40
|
+
- `selected`: When one of the matches is highlighted, and ready to be confirmed, for example by pressing `enter`
|
41
|
+
- `pre-selected`: When an item is selected on the user's behalf without them navigating to it
|
42
|
+
- `confirmed`: When one of the matches has been confirmed, and will be submitted as a value of the parent form
|
@@ -0,0 +1,29 @@
|
|
1
|
+
/**
|
2
|
+
* Babel config
|
3
|
+
*
|
4
|
+
* @type {import('@babel/core').ConfigFunction}
|
5
|
+
*/
|
6
|
+
module.exports = {
|
7
|
+
presets: [
|
8
|
+
[
|
9
|
+
'@babel/preset-env',
|
10
|
+
{
|
11
|
+
bugfixes: true,
|
12
|
+
corejs: '3.33',
|
13
|
+
loose: true,
|
14
|
+
shippedProposals: true,
|
15
|
+
useBuiltIns: 'usage'
|
16
|
+
}
|
17
|
+
]
|
18
|
+
],
|
19
|
+
|
20
|
+
plugins: [
|
21
|
+
['@babel/plugin-transform-react-jsx', { pragma: 'h' }]
|
22
|
+
],
|
23
|
+
|
24
|
+
env: {
|
25
|
+
test: {
|
26
|
+
plugins: ['istanbul']
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
@@ -0,0 +1,3 @@
|
|
1
|
+
.autocomplete__wrapper{position:relative}.autocomplete__hint,.autocomplete__input{-webkit-appearance:none;appearance:none;border:2px solid #0b0c0c;border-radius:0;box-sizing:border-box;height:2.5rem;line-height:1.25;margin-bottom:0;width:100%}.autocomplete__input{background-color:transparent;position:relative}.autocomplete__hint{color:#505a5f;position:absolute}.autocomplete__input--default{padding:5px}.autocomplete__input--focused{box-shadow:inset 0 0 0 2px;outline:3px solid #fd0;outline-offset:0}.autocomplete__input--show-all-values{cursor:pointer;padding:5px 35px 5px 5px}.autocomplete__dropdown-arrow-down{display:inline-block;height:24px;position:absolute;right:8px;top:10px;width:24px;z-index:-1}.autocomplete__menu{background-color:#fff;border:2px solid #0b0c0c;border-top:0;color:#0b0c0c;margin:0;max-height:342px;overflow-x:hidden;padding:0;width:100%;width:calc(100% - 4px)}.autocomplete__menu--visible{display:block}.autocomplete__menu--hidden{display:none}.autocomplete__menu--overlay{box-shadow:0 2px 6px rgba(0,0,0,.257);left:0;position:absolute;top:100%;z-index:100}.autocomplete__menu--inline{position:relative}.autocomplete__option{border-bottom:1px solid #b1b4b6;border-left-width:0;border-right-width:0;border-top-width:1px;cursor:pointer;display:block;position:relative}.autocomplete__option>*{pointer-events:none}.autocomplete__option:first-of-type{border-top-width:0}.autocomplete__option:last-of-type{border-bottom-width:0}.autocomplete__option--odd{background-color:#f3f2f1}.autocomplete__option--focused,.autocomplete__option:hover{background-color:#1d70b8;border-color:#1d70b8;color:#fff;outline:none}@media (-ms-high-contrast:active),(forced-colors:active){.autocomplete__menu{border-color:FieldText}.autocomplete__option{background-color:Field;color:FieldText}.autocomplete__option--focused,.autocomplete__option:hover{background-color:Highlight;background-color:SelectedItem;border-color:SelectedItem;color:HighlightText;color:SelectedItemText;forced-color-adjust:none;outline-color:SelectedItemText}}.autocomplete__option--no-results{background-color:#f3f2f1;color:#505a5f;cursor:not-allowed}.autocomplete__hint,.autocomplete__input,.autocomplete__option{font-size:1rem;font-weight:400}.autocomplete__hint,.autocomplete__option{padding:5px}@media (min-width:641px){.autocomplete__hint,.autocomplete__input,.autocomplete__option{font-size:1.1875rem;line-height:1.3157894737}}
|
2
|
+
|
3
|
+
/*# sourceMappingURL=accessible-autocomplete.min.css.map*/
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"accessible-autocomplete.min.css","mappings":"AAAA,uBACE,iBACF,CAEA,yCAEE,uBAAgB,CAAhB,eAAgB,CAChB,wBAAyB,CACzB,eAAgB,CAChB,qBAAsB,CAGtB,aAAc,CADd,gBAAiB,CADjB,eAAgB,CAGhB,UACF,CAEA,qBACE,4BAA6B,CAC7B,iBACF,CAEA,oBACE,aAAc,CACd,iBACF,CAEA,8BACE,WACF,CAEA,8BAGE,0BAA2B,CAF3B,sBAAuB,CACvB,gBAEF,CAEA,sCAEE,cAAe,CADf,wBAEF,CAEA,mCAEE,oBAAqB,CAIrB,WAAY,CAHZ,iBAAkB,CAClB,SAAU,CAGV,QAAS,CAFT,UAAW,CAJX,UAOF,CAEA,oBACE,qBAAyB,CACzB,wBAAyB,CACzB,YAAa,CACb,aAAc,CACd,QAAS,CACT,gBAAiB,CACjB,iBAAkB,CAClB,SAAU,CACV,UAAW,CACX,sBACF,CAEA,6BACE,aACF,CAEA,4BACE,YACF,CAEA,6BACE,qCAA+C,CAC/C,MAAO,CACP,iBAAkB,CAClB,QAAS,CACT,WACF,CAEA,4BACE,iBACF,CAEA,sBAEE,+BAAmB,CAAnB,mBAAmB,CAAnB,oBAAmB,CAAnB,oBAAmB,CACnB,cAAe,CACf,aAAc,CACd,iBACF,CAEA,wBACE,mBACF,CAEA,oCACE,kBACF,CAEA,mCACE,qBACF,CAEA,2BACE,wBACF,CAEA,2DAEE,wBAAyB,CACzB,oBAAqB,CACrB,UAAY,CACZ,YACF,CAEA,yDACE,oBACE,sBACF,CAEA,sBACE,sBAAuB,CACvB,eACF,CAEA,2DAGE,0BAA2B,CAK3B,6BAA8B,CAC9B,yBAA0B,CAJ1B,mBAAoB,CAKpB,sBAAuB,CARvB,wBAAyB,CASzB,8BACF,CACF,CAEA,kCACE,wBAAyB,CACzB,aAAc,CACd,kBACF,CAEA,+DAGE,cAAe,CACf,eACF,CAEA,0CAEE,WACF,CAEA,yBACE,+DAGE,mBAAoB,CACpB,wBACF,CACF,C","sources":["webpack:///./autocomplete.css"],"sourcesContent":[".autocomplete__wrapper {\n position: relative;\n}\n\n.autocomplete__hint,\n.autocomplete__input {\n appearance: none;\n border: 2px solid #0b0c0c;\n border-radius: 0; /* Safari 10 on iOS adds implicit border rounding. */\n box-sizing: border-box;\n margin-bottom: 0; /* BUG: Safari 10 on macOS seems to add an implicit margin. */\n line-height: 1.25;\n height: 2.5rem;\n width: 100%;\n}\n\n.autocomplete__input {\n background-color: transparent;\n position: relative;\n}\n\n.autocomplete__hint {\n color: #505a5f;\n position: absolute;\n}\n\n.autocomplete__input--default {\n padding: 5px;\n}\n\n.autocomplete__input--focused {\n outline: 3px solid #fd0;\n outline-offset: 0;\n box-shadow: inset 0 0 0 2px;\n}\n\n.autocomplete__input--show-all-values {\n padding: 5px 35px 5px 5px; /* Space for arrow. Other padding should match .autocomplete__input--default. */\n cursor: pointer;\n}\n\n.autocomplete__dropdown-arrow-down {\n z-index: -1;\n display: inline-block;\n position: absolute;\n right: 8px;\n width: 24px;\n height: 24px;\n top: 10px;\n}\n\n.autocomplete__menu {\n background-color: #ffffff;\n border: 2px solid #0b0c0c;\n border-top: 0;\n color: #0b0c0c;\n margin: 0;\n max-height: 342px;\n overflow-x: hidden;\n padding: 0;\n width: 100%;\n width: calc(100% - 4px);\n}\n\n.autocomplete__menu--visible {\n display: block;\n}\n\n.autocomplete__menu--hidden {\n display: none;\n}\n\n.autocomplete__menu--overlay {\n box-shadow: rgba(0, 0, 0, 0.256863) 0px 2px 6px;\n left: 0;\n position: absolute;\n top: 100%;\n z-index: 100;\n}\n\n.autocomplete__menu--inline {\n position: relative;\n}\n\n.autocomplete__option {\n border-bottom: solid #b1b4b6;\n border-width: 1px 0;\n cursor: pointer;\n display: block;\n position: relative;\n}\n\n.autocomplete__option > * {\n pointer-events: none;\n}\n\n.autocomplete__option:first-of-type {\n border-top-width: 0;\n}\n\n.autocomplete__option:last-of-type {\n border-bottom-width: 0;\n}\n\n.autocomplete__option--odd {\n background-color: #f3f2f1;\n}\n\n.autocomplete__option--focused,\n.autocomplete__option:hover {\n background-color: #1d70b8;\n border-color: #1d70b8;\n color: white;\n outline: none;\n}\n\n@media (-ms-high-contrast: active), (forced-colors: active) {\n .autocomplete__menu {\n border-color: FieldText;\n }\n\n .autocomplete__option {\n background-color: Field;\n color: FieldText;\n }\n\n .autocomplete__option--focused,\n .autocomplete__option:hover {\n forced-color-adjust: none; /* prevent backplate from obscuring text */\n background-color: Highlight;\n border-color: Highlight;\n color: HighlightText;\n\n /* Prefer SelectedItem / SelectedItemText in browsers that support it */\n background-color: SelectedItem;\n border-color: SelectedItem;\n color: SelectedItemText;\n outline-color: SelectedItemText;\n }\n}\n\n.autocomplete__option--no-results {\n background-color: #f3f2f1;\n color: #505a5f;\n cursor: not-allowed;\n}\n\n.autocomplete__hint,\n.autocomplete__input,\n.autocomplete__option {\n font-size: 1rem;\n font-weight: 400;\n}\n\n.autocomplete__hint,\n.autocomplete__option {\n padding: 5px;\n}\n\n@media (min-width: 641px) {\n .autocomplete__hint,\n .autocomplete__input,\n .autocomplete__option {\n font-size: 1.1875rem;\n line-height: 1.3157894737;\n }\n}\n"],"names":[],"sourceRoot":""}
|