Skip to content

Commit

Permalink
Search with autocomplete: Add tracking attributes
Browse files Browse the repository at this point in the history
This adds a set of tracking attributes to the `search_with_autocomplete`
component, which will be able to be picked up by the GA4 search tracker
to measure user interaction with the autocomplete component.

In particular, this adds:
- `autocomplete-trigger-input`: the original, possibly partial, input
  from the user that triggered the offered suggestions
- `autocomplete-accepted`: whether the user has accepted a suggestion
  (as opposed to dismissed the autocomplete with Escape or clicking
  outside)
- `autocomplete-suggestions`: a string containing a pipe-separated list
  of the (up to) five top suggestions offered
- `autocomplete-suggestions-count`: the total number of suggestions
  offered
  • Loading branch information
csutter committed Nov 6, 2024
1 parent f47d172 commit 3249a59
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
useful summary for people upgrading their application, not a replication
of the commit log.

## Unreleased
* Add GA4 tracking for search autocomplete ([PR #4371](https://github.com/alphagov/govuk_publishing_components/pull/4371))

## 45.0.0

* **BREAKING** Refactor organisation styles and upgrade to govuk-frontend v5.7.1 ([PR #4321](https://github.com/alphagov/govuk_publishing_components/pull/4321))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,31 @@ window.GOVUK.Modules = window.GOVUK.Modules || {};
url.searchParams.set('q', query)
fetch(url, { headers: { Accept: 'application/json' } })
.then(response => response.json())
.then((data) => { populateResults(data[this.sourceKey]) })
.then((data) => {
const results = data[this.sourceKey]

this.setTrackingAttributes(query, results)
populateResults(results)
})
.catch(() => { populateResults([]) })
}

// Set tracking attributes on the input field. These can be used by the containing form's
// analytics module to track the user's interaction with the autocomplete component.
setTrackingAttributes (query, results) {
const formattedResults = results.slice(0, 5).join('|')

this.$autocompleteInput.dataset.autocompleteTriggerInput = query
this.$autocompleteInput.dataset.autocompleteSuggestions = formattedResults
this.$autocompleteInput.dataset.autocompleteSuggestionsCount = results.length
this.$autocompleteInput.dataset.autocompleteAccepted = false
}

// 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) {
this.$autocompleteInput.dataset.autocompleteAccepted = true

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
Expand Down
65 changes: 65 additions & 0 deletions spec/javascripts/components/search-with-autocomplete-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,4 +215,69 @@ describe('Search with autocomplete component', () => {
expect(formData.get('q')).toEqual('updated value')
expect(submitSpy).toHaveBeenCalled()
})

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

stubSuccessfulFetch([
'my favourite song is red',
'my favourite song is karma',
'my favourite song is death by a thousand cuts'
])
performInput(input, 'my favourite song is', () => {
expect(input.getAttribute('data-autocomplete-trigger-input')).toEqual('my favourite song is')
expect(input.getAttribute('data-autocomplete-suggestions')).toEqual(
'my favourite song is red|' +
'my favourite song is karma|' +
'my favourite song is death by a thousand cuts'
)
expect(input.getAttribute('data-autocomplete-suggestions-count')).toEqual('3')
expect(input.getAttribute('data-autocomplete-accepted')).toEqual('false')
done()
})
})
})

it('limits the number of suggestions included in the data to 5', (done) => {
const input = fixture.querySelector('input')

stubSuccessfulFetch([
'my favourite album is red',
'my favourite album is midnights',
'my favourite album is lover',
'my favourite album is folklore',
'my favourite album is reputation',
'my favourite album is 1989'
])
performInput(input, 'my favourite album is', () => {
expect(input.getAttribute('data-autocomplete-suggestions')).toEqual(
'my favourite album is red|' +
'my favourite album is midnights|' +
'my favourite album is lover|' +
'my favourite album is folklore|' +
'my favourite album is reputation'
)
expect(input.getAttribute('data-autocomplete-suggestions-count')).toEqual('6')
done()
})
})

it('sets autocomplete-accepted when a suggestion is accepted', (done) => {
const form = fixture.querySelector('form')
const input = fixture.querySelector('input')
spyOn(form, 'requestSubmit')

stubSuccessfulFetch([
'my favourite bonus track is message in a bottle',
'my favourite bonus track is is it over now'
])
performInput(input, 'my favourite bonus track is', () => {
const secondOption = fixture.querySelectorAll('.gem-c-search-with-autocomplete__option')[1]
secondOption.click()

expect(input.getAttribute('data-autocomplete-accepted')).toEqual('true')
done()
})
})
})

0 comments on commit 3249a59

Please sign in to comment.