Skip to content

Commit

Permalink
Restructure and make tests pass
Browse files Browse the repository at this point in the history
- make the code easier to read and more clearly separated into functions
- adds a custom event listener for keyup, so that we can clear the hidden select if the autocomplete is cleared. This doesn't seem to work properly with the standard autocomplete
- update tests to remove jQuery and fix the events so they stop failing intermittently
  • Loading branch information
andysellick committed Dec 3, 2021
1 parent b224821 commit 61d02c7
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
/* eslint-env jquery */
/* global accessibleAutocomplete */
// = require accessible-autocomplete/dist/accessible-autocomplete.min.js

window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {};

(function (Modules) {
function AccessibleAutocomplete ($module) {
this.$module = $module
this.selectElem = this.$module.querySelector('select')
this.selectElement = this.$module.querySelector('select')
}

AccessibleAutocomplete.prototype.init = function () {
var configOptions = {
selectElement: document.getElementById(this.selectElem.getAttribute('id')),
selectElement: this.selectElement,
autoselect: true,
confirmOnBlur: true,
preserveNullOptions: true, // https://github.com/alphagov/accessible-autocomplete#null-options
Expand All @@ -22,43 +23,50 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};

configOptions.onConfirm = this.onConfirm
new accessibleAutocomplete.enhanceSelectElement(configOptions) // eslint-disable-line no-new, new-cap
this.autoCompleteInput = this.$module.querySelector('.autocomplete__input')
if (this.autoCompleteInput) {
this.autoCompleteInput.addEventListener('keyup', this.handleKeyup.bind(this))
}
}

// custom onConfirm function because will likely need future expansion e.g. tracking
// the accessible-autocomplete doesn't update the hidden select if an onConfirm function is supplied
// https://github.com/alphagov/accessible-autocomplete/issues/322
// also it doesn't update either way if the user chooses something then deletes it
AccessibleAutocomplete.prototype.onConfirm = function () {
if (!this.govukModule.autoCompleteInput) {
this.govukModule.autoCompleteInput = this.govukModule.$module.querySelector('.autocomplete__input')
}
var label = this.govukModule.autoCompleteInput.value

if (typeof label !== 'undefined') {
AccessibleAutocomplete.prototype.onConfirm = function (value) {
// onConfirm fires on selecting an option
// also fires when blurring the input, which then provides `value` as undefined
// so we only update the hidden select if there's a value, and handle clearing it separately
if (typeof value !== 'undefined') {
// the accessible-autocomplete doesn't update the hidden select if an onConfirm function is supplied
// https://github.com/alphagov/accessible-autocomplete/issues/322
var options = this.selectElement.querySelectorAll('option')

for (var i = 0; i < options.length; i++) {
var text = options[i].textContent
if (text === label) {
if (text === value) {
this.govukModule.setSelectOption(options[i])
break
}
}
} else {
this.govukModule.clearSelect()
}
}

// seems to be a bug in the accessible autocomplete where clearing the input
// doesn't update the select, so we have to do this
AccessibleAutocomplete.prototype.handleKeyup = function (e) {
var value = this.autoCompleteInput.value

if (value.length === 0) {
this.clearSelect()
}
}

AccessibleAutocomplete.prototype.setSelectOption = function (option) {
var value = option.value
this.selectElem.value = value
this.selectElement.value = value
window.GOVUK.triggerEvent(this.selectElement, 'change')
}

AccessibleAutocomplete.prototype.clearSelect = function () {
var empty = this.selectElem.querySelector('option[value=""]')
if (empty) {
empty.setAttribute('selected', true)
}
this.selectElement.value = ''
window.GOVUK.triggerEvent(this.selectElement, 'change')
}

Modules.AccessibleAutocomplete = AccessibleAutocomplete
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
name: Accessible autocomplete
name: Accessible autocomplete (experimental)
description: An autocomplete component, built to be accessible.
body: |
This component uses the [Accessible Autocomplete](https://github.com/alphagov/accessible-autocomplete) code to create an accessible autocomplete element. The autocomplete is created with the `showAllValues`
option set to `true` and the `confirmOnBlur` option set to `false` (see [Autocomplete examples](https://alphagov.github.io/accessible-autocomplete/examples) here). It also depends upon the
[label component](https://github.com/component-guide/label).
This component uses the [Accessible Autocomplete](https://github.com/alphagov/accessible-autocomplete) code to progressively enhance a hidden select element (see [Autocomplete examples](https://alphagov.github.io/accessible-autocomplete/examples) here). It depends upon the [label component](https://github.com/component-guide/label).
If Javascript is disabled, the component appears as a select box, so the user can still select an option.
accessibility_criteria: |
Expand Down
43 changes: 29 additions & 14 deletions spec/javascripts/components/accessible-autocomplete-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
/* global GOVUK */

describe('An accessible autocomplete component', function () {
'use strict'
var fixture, select

function loadAutocompleteComponent () {
window.setFixtures(html)
var autocomplete = new GOVUK.Modules.AccessibleAutocomplete($('.gem-c-accessible-autocomplete')[0])
fixture = document.createElement('div')
document.body.appendChild(fixture)
fixture.innerHTML = html
select = fixture.querySelector('select')
var autocomplete = new GOVUK.Modules.AccessibleAutocomplete(fixture.querySelector('.gem-c-accessible-autocomplete'))
autocomplete.init()
}

Expand All @@ -26,49 +29,61 @@ describe('An accessible autocomplete component', function () {

setTimeout(function () {
deferred.resolve()
}, 500)
}, 1)

return deferred.promise()
}

afterEach(function () {
fixture.remove()
})

describe('updates the hidden select when', function () {
beforeEach(function (done) {
loadAutocompleteComponent()
var input = fixture.querySelector('.autocomplete__input')
input.value = 'Moose'

// the autocomplete is complex enough that all of these
// events are necessary to simulate user input
$('.autocomplete__input').val('Moose').click().focus().trigger(
$.Event('keypress', { which: 13, key: 13, keyCode: 13 })
).blur()
// need to use triggerEvent as direct e.g. .click() doesn't work headless
window.GOVUK.triggerEvent(input, 'focus')
window.GOVUK.triggerEvent(input, 'keyup')
window.GOVUK.triggerEvent(input, 'click')
window.GOVUK.triggerEvent(input, 'blur')

testAsyncWithDeferredReturnValue().done(function () {
done()
})
})

it('an option is selected', function () {
expect($('select').val()).toEqual('mo')
expect(select.value).toEqual('mo')
})
})

describe('updates the hidden select when', function () {
beforeEach(function (done) {
loadAutocompleteComponent()

$('select').val('de').change()
$('.autocomplete__input').val('Deer')
var input = fixture.querySelector('.autocomplete__input')
input.value = 'Deer'
select.value = 'de'
window.GOVUK.triggerEvent(select, 'change')

$('.autocomplete__input').val('').click().focus().trigger(
$.Event('keypress', { which: 13, key: 13, keyCode: 13 })
).blur()
input.value = ''
window.GOVUK.triggerEvent(input, 'focus')
window.GOVUK.triggerEvent(input, 'keyup')
window.GOVUK.triggerEvent(input, 'click')
window.GOVUK.triggerEvent(input, 'blur')

testAsyncWithDeferredReturnValue().done(function () {
done()
})
})

it('the input is cleared', function () {
expect($('select').val()).toEqual('')
expect(select.value).toEqual('')
})
})
})

0 comments on commit 61d02c7

Please sign in to comment.