govuk_publishing_components 43.3.0 → 43.4.1
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/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,125 @@
|
|
1
|
+
import { createElement, Component } from 'preact' /** @jsx createElement */
|
2
|
+
|
3
|
+
const debounce = function (func, wait, immediate) {
|
4
|
+
let timeout
|
5
|
+
return function () {
|
6
|
+
const context = this
|
7
|
+
const args = arguments
|
8
|
+
const later = function () {
|
9
|
+
timeout = null
|
10
|
+
if (!immediate) func.apply(context, args)
|
11
|
+
}
|
12
|
+
const callNow = immediate && !timeout
|
13
|
+
clearTimeout(timeout)
|
14
|
+
timeout = setTimeout(later, wait)
|
15
|
+
if (callNow) func.apply(context, args)
|
16
|
+
}
|
17
|
+
}
|
18
|
+
const statusDebounceMillis = 1400
|
19
|
+
|
20
|
+
export default class Status extends Component {
|
21
|
+
static defaultProps = {
|
22
|
+
tQueryTooShort: (minQueryLength) => `Type in ${minQueryLength} or more characters for results`,
|
23
|
+
tNoResults: () => 'No search results',
|
24
|
+
tSelectedOption: (selectedOption, length, index) => `${selectedOption} ${index + 1} of ${length} is highlighted`,
|
25
|
+
tResults: (length, contentSelectedOption) => {
|
26
|
+
const words = {
|
27
|
+
result: (length === 1) ? 'result' : 'results',
|
28
|
+
is: (length === 1) ? 'is' : 'are'
|
29
|
+
}
|
30
|
+
|
31
|
+
return `${length} ${words.result} ${words.is} available. ${contentSelectedOption}`
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
state = {
|
36
|
+
bump: false,
|
37
|
+
debounced: false
|
38
|
+
}
|
39
|
+
|
40
|
+
/* eslint-disable react/no-deprecated -- https://github.com/alphagov/accessible-autocomplete/issues/418 */
|
41
|
+
componentWillMount () {
|
42
|
+
const that = this
|
43
|
+
this.debounceStatusUpdate = debounce(function () {
|
44
|
+
if (!that.state.debounced) {
|
45
|
+
const shouldSilence = !that.props.isInFocus || that.props.validChoiceMade
|
46
|
+
that.setState(({ bump }) => ({ bump: !bump, debounced: true, silenced: shouldSilence }))
|
47
|
+
}
|
48
|
+
}, statusDebounceMillis)
|
49
|
+
}
|
50
|
+
|
51
|
+
/* eslint-disable react/no-deprecated -- https://github.com/alphagov/accessible-autocomplete/issues/418 */
|
52
|
+
componentWillReceiveProps ({ queryLength }) {
|
53
|
+
this.setState({ debounced: false })
|
54
|
+
}
|
55
|
+
|
56
|
+
render () {
|
57
|
+
const {
|
58
|
+
id,
|
59
|
+
length,
|
60
|
+
queryLength,
|
61
|
+
minQueryLength,
|
62
|
+
selectedOption,
|
63
|
+
selectedOptionIndex,
|
64
|
+
tQueryTooShort,
|
65
|
+
tNoResults,
|
66
|
+
tSelectedOption,
|
67
|
+
tResults,
|
68
|
+
className
|
69
|
+
} = this.props
|
70
|
+
const { bump, debounced, silenced } = this.state
|
71
|
+
|
72
|
+
const queryTooShort = queryLength < minQueryLength
|
73
|
+
const noResults = length === 0
|
74
|
+
|
75
|
+
const contentSelectedOption = selectedOption
|
76
|
+
? tSelectedOption(selectedOption, length, selectedOptionIndex)
|
77
|
+
: ''
|
78
|
+
|
79
|
+
let content = null
|
80
|
+
if (queryTooShort) {
|
81
|
+
content = tQueryTooShort(minQueryLength)
|
82
|
+
} else if (noResults) {
|
83
|
+
content = tNoResults()
|
84
|
+
} else {
|
85
|
+
content = tResults(length, contentSelectedOption)
|
86
|
+
}
|
87
|
+
|
88
|
+
this.debounceStatusUpdate()
|
89
|
+
|
90
|
+
return (
|
91
|
+
<div
|
92
|
+
className={className}
|
93
|
+
style={{
|
94
|
+
border: '0',
|
95
|
+
clip: 'rect(0 0 0 0)',
|
96
|
+
height: '1px',
|
97
|
+
marginBottom: '-1px',
|
98
|
+
marginRight: '-1px',
|
99
|
+
overflow: 'hidden',
|
100
|
+
padding: '0',
|
101
|
+
position: 'absolute',
|
102
|
+
whiteSpace: 'nowrap',
|
103
|
+
width: '1px'
|
104
|
+
}}
|
105
|
+
>
|
106
|
+
<div
|
107
|
+
id={id + '__status--A'}
|
108
|
+
role='status'
|
109
|
+
aria-atomic='true'
|
110
|
+
aria-live='polite'
|
111
|
+
>
|
112
|
+
{(!silenced && debounced && bump) ? content : ''}
|
113
|
+
</div>
|
114
|
+
<div
|
115
|
+
id={id + '__status--B'}
|
116
|
+
role='status'
|
117
|
+
aria-atomic='true'
|
118
|
+
aria-live='polite'
|
119
|
+
>
|
120
|
+
{(!silenced && debounced && !bump) ? content : ''}
|
121
|
+
</div>
|
122
|
+
</div>
|
123
|
+
)
|
124
|
+
}
|
125
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import { createElement, render } from 'preact' /** @jsx createElement */
|
2
|
+
import Autocomplete from './autocomplete'
|
3
|
+
|
4
|
+
function accessibleAutocomplete (options) {
|
5
|
+
if (!options.element) { throw new Error('element is not defined') }
|
6
|
+
if (!options.id) { throw new Error('id is not defined') }
|
7
|
+
if (!options.source) { throw new Error('source is not defined') }
|
8
|
+
if (Array.isArray(options.source)) {
|
9
|
+
options.source = createSimpleEngine(options.source)
|
10
|
+
}
|
11
|
+
render(<Autocomplete {...options} />, options.element)
|
12
|
+
}
|
13
|
+
|
14
|
+
const createSimpleEngine = (values) => (query, syncResults) => {
|
15
|
+
const matches = values.filter(r => r.toLowerCase().indexOf(query.toLowerCase()) !== -1)
|
16
|
+
syncResults(matches)
|
17
|
+
}
|
18
|
+
|
19
|
+
accessibleAutocomplete.enhanceSelectElement = (configurationOptions) => {
|
20
|
+
if (!configurationOptions.selectElement) { throw new Error('selectElement is not defined') }
|
21
|
+
|
22
|
+
// Set defaults.
|
23
|
+
if (!configurationOptions.source) {
|
24
|
+
const availableOptions = [].filter.call(configurationOptions.selectElement.options, option => (option.value || configurationOptions.preserveNullOptions))
|
25
|
+
configurationOptions.source = availableOptions.map(option => option.textContent || option.innerText)
|
26
|
+
}
|
27
|
+
configurationOptions.onConfirm = configurationOptions.onConfirm || (query => {
|
28
|
+
const requestedOption = [].filter.call(configurationOptions.selectElement.options, option => (option.textContent || option.innerText) === query)[0]
|
29
|
+
if (requestedOption) { requestedOption.selected = true }
|
30
|
+
})
|
31
|
+
|
32
|
+
if (configurationOptions.selectElement.value || configurationOptions.defaultValue === undefined) {
|
33
|
+
const option = configurationOptions.selectElement.options[configurationOptions.selectElement.options.selectedIndex]
|
34
|
+
configurationOptions.defaultValue = option.textContent || option.innerText
|
35
|
+
}
|
36
|
+
|
37
|
+
if (configurationOptions.name === undefined) configurationOptions.name = ''
|
38
|
+
if (configurationOptions.id === undefined) {
|
39
|
+
if (configurationOptions.selectElement.id === undefined) {
|
40
|
+
configurationOptions.id = ''
|
41
|
+
} else {
|
42
|
+
configurationOptions.id = configurationOptions.selectElement.id
|
43
|
+
}
|
44
|
+
}
|
45
|
+
if (configurationOptions.autoselect === undefined) configurationOptions.autoselect = true
|
46
|
+
|
47
|
+
const element = document.createElement('div')
|
48
|
+
|
49
|
+
configurationOptions.selectElement.parentNode.insertBefore(element, configurationOptions.selectElement)
|
50
|
+
|
51
|
+
accessibleAutocomplete({
|
52
|
+
...configurationOptions,
|
53
|
+
element
|
54
|
+
})
|
55
|
+
|
56
|
+
configurationOptions.selectElement.style.display = 'none'
|
57
|
+
configurationOptions.selectElement.id = configurationOptions.selectElement.id + '-select'
|
58
|
+
}
|
59
|
+
|
60
|
+
export default accessibleAutocomplete
|
@@ -0,0 +1,46 @@
|
|
1
|
+
/* global before, beforeEach, after, describe, it */
|
2
|
+
|
3
|
+
import { expect } from 'chai'
|
4
|
+
import { createElement, render } from 'preact' /** @jsx createElement */
|
5
|
+
import DropdownArrowDown from '../../src/dropdown-arrow-down'
|
6
|
+
|
7
|
+
describe('DropdownArrowDown', () => {
|
8
|
+
describe('rendering', () => {
|
9
|
+
let scratch
|
10
|
+
|
11
|
+
before(() => {
|
12
|
+
scratch = document.createElement('div');
|
13
|
+
(document.body || document.documentElement).appendChild(scratch)
|
14
|
+
})
|
15
|
+
|
16
|
+
beforeEach(() => {
|
17
|
+
scratch.innerHTML = ''
|
18
|
+
})
|
19
|
+
|
20
|
+
after(() => {
|
21
|
+
scratch.parentNode.removeChild(scratch)
|
22
|
+
scratch = null
|
23
|
+
})
|
24
|
+
|
25
|
+
describe('basic usage', () => {
|
26
|
+
it('renders an svg', () => {
|
27
|
+
render(<DropdownArrowDown />, scratch)
|
28
|
+
|
29
|
+
expect(scratch.innerHTML).to.contain('svg')
|
30
|
+
})
|
31
|
+
|
32
|
+
it('renders with a given custom class', () => {
|
33
|
+
render(<DropdownArrowDown className='foo' />, scratch)
|
34
|
+
|
35
|
+
expect(scratch.innerHTML).to.contain('class="foo"')
|
36
|
+
})
|
37
|
+
|
38
|
+
// IE issue so the dropdown svg is not focusable (tabindex won't work for this)
|
39
|
+
it('renders an svg where focusable attribute is false', () => {
|
40
|
+
render(<DropdownArrowDown />, scratch)
|
41
|
+
|
42
|
+
expect(scratch.innerHTML).to.contain('focusable="false"')
|
43
|
+
})
|
44
|
+
})
|
45
|
+
})
|
46
|
+
})
|