Skip to content

Commit

Permalink
Merge pull request #4372 from alphagov/search-auto-fix-enter
Browse files Browse the repository at this point in the history
Search with autocomplete: Work around Enter edge case
  • Loading branch information
csutter authored Nov 7, 2024
2 parents 671ef33 + f8264b1 commit a2285ef
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

* Add GA4 tracking for search autocomplete ([PR #4371](https://github.com/alphagov/govuk_publishing_components/pull/4371))
* Append no-actions class to rows without actions in Summary Cards ([PR #4368](https://github.com/alphagov/govuk_publishing_components/pull/4368))
* Search with autocomplete: Work around Enter edge case ([PR #4372](https://github.com/alphagov/govuk_publishing_components/pull/4372))

## 45.0.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};

this.sourceUrl = this.$module.getAttribute('data-source-url')
this.sourceKey = this.$module.getAttribute('data-source-key')

this.isSubmitting = false
}

init () {
Expand All @@ -28,7 +30,7 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
confirmOnBlur: false,
showNoOptionsFound: false,
source: this.getResults.bind(this),
onConfirm: this.submitContainingForm.bind(this),
onConfirm: this.onConfirm.bind(this),
templates: {
suggestion: this.constructSuggestionHTMLString.bind(this)
},
Expand All @@ -54,6 +56,19 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
this.$autocompleteInput.setAttribute('type', 'search')
// Remove the original input from the DOM
this.$originalInput.parentNode.removeChild(this.$originalInput)

// The accessible-autocomplete component has an edge case where when the menu is visible, it
// prevents default on the Enter key event, even if the user hasn't put keyboard focus on a
// suggestion. This results in a scenario where the user types something, does _not_ interact
// with the autocomplete menu at all, and then hits Enter to try to submit the form - but it
// isn't submitted.
//
// This manually triggers our form submission logic when the Enter key is pressed as a
// workaround (which will do nothing if the form is already in the process of submitting
// through `onConfirm` because the user has accepted a suggestion).
this.$autocompleteInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') this.submitContainingForm()
})
}

// Callback used by accessible-autocomplete to generate the HTML for each suggestion based on
Expand Down Expand Up @@ -109,16 +124,24 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};

// Callback used by accessible-autocomplete to submit the containing form when a suggestion is
// confirmed by the user (e.g. by pressing Enter or clicking on it)
submitContainingForm (value) {
onConfirm (value) {
// The accessible-autocomplete component calls this callback _before_ it updates its
// internal state, so the value of the input field is not yet updated when this callback is
// called. We need to force the value to be updated before submitting the form, but the rest
// of the state can catch up later.
this.$autocompleteInput.value = value

this.$autocompleteInput.dataset.autocompleteAccepted = true
this.submitContainingForm()
}

if (this.$form) {
// The accessible-autocomplete component calls this callback _before_ it updates its
// internal state, so the value of the input field is not yet updated when this callback is
// called. We need to force the value to be updated before submitting the form, but the rest
// of the state can catch up later.
this.$autocompleteInput.value = value
// Submit the containing form, if one exists and the component is not already in the process of
// submitting
submitContainingForm () {
if (this.isSubmitting) return
this.isSubmitting = true

if (this.$form) {
if (this.$form.requestSubmit) {
this.$form.requestSubmit()
} else {
Expand Down
23 changes: 21 additions & 2 deletions spec/javascripts/components/search-with-autocomplete-spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-env jasmine */
/* global GOVUK, Event, FormData */
/* global GOVUK, Event, FormData, KeyboardEvent */

describe('Search with autocomplete component', () => {
let autocomplete, fixture
Expand Down Expand Up @@ -209,13 +209,32 @@ describe('Search with autocomplete component', () => {
const form = fixture.querySelector('form')
const submitSpy = spyOn(form, 'requestSubmit')

autocomplete.submitContainingForm('updated value')
autocomplete.onConfirm('updated value')

const formData = new FormData(form)
expect(formData.get('q')).toEqual('updated value')
expect(submitSpy).toHaveBeenCalled()
})

it('triggers a requestSubmit if Enter is pressed in the search field to work around library bug', (done) => {
const form = fixture.querySelector('form')
const input = fixture.querySelector('input')
const submitSpy = spyOn(form, 'requestSubmit')

stubSuccessfulFetch(['i am an undesirable result'])
performInput(input, 'i just want to search the old-fashioned way', () => {
const enterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
bubbles: true,
cancelable: true
})
input.dispatchEvent(enterEvent)

expect(submitSpy).toHaveBeenCalled()
done()
})
})

describe('analytics data attributes', () => {
it('sets data attributes on the input when suggestions are returned', (done) => {
const input = fixture.querySelector('input')
Expand Down

0 comments on commit a2285ef

Please sign in to comment.