diff --git a/app/assets/javascripts/govuk_publishing_components/components/accessible-autocomplete.js b/app/assets/javascripts/govuk_publishing_components/components/accessible-autocomplete.js index c3e48e3cda..3ea2f0fe51 100644 --- a/app/assets/javascripts/govuk_publishing_components/components/accessible-autocomplete.js +++ b/app/assets/javascripts/govuk_publishing_components/components/accessible-autocomplete.js @@ -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 @@ -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 diff --git a/app/views/govuk_publishing_components/components/docs/accessible_autocomplete.yml b/app/views/govuk_publishing_components/components/docs/accessible_autocomplete.yml index a5e122057a..118aa5978a 100644 --- a/app/views/govuk_publishing_components/components/docs/accessible_autocomplete.yml +++ b/app/views/govuk_publishing_components/components/docs/accessible_autocomplete.yml @@ -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: | diff --git a/spec/javascripts/components/accessible-autocomplete-spec.js b/spec/javascripts/components/accessible-autocomplete-spec.js index 4f45505d5e..5bb8ee13ad 100644 --- a/spec/javascripts/components/accessible-autocomplete-spec.js +++ b/spec/javascripts/components/accessible-autocomplete-spec.js @@ -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() } @@ -26,20 +29,28 @@ 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() @@ -47,7 +58,7 @@ describe('An accessible autocomplete component', function () { }) it('an option is selected', function () { - expect($('select').val()).toEqual('mo') + expect(select.value).toEqual('mo') }) }) @@ -55,12 +66,16 @@ describe('An accessible autocomplete component', 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() @@ -68,7 +83,7 @@ describe('An accessible autocomplete component', function () { }) it('the input is cleared', function () { - expect($('select').val()).toEqual('') + expect(select.value).toEqual('') }) }) })