Skip to content

Commit

Permalink
Add new custom analytics tracker for search
Browse files Browse the repository at this point in the history
This adds a new `Ga4SearchTracker` analytics tracker which will
eventually replace:
- the usage of the generic `Ga4FormTracker` on the homepage and layout
  super navigation
- the custom search event code on the all content finder ("site search")

For now, this fires exactly the same event with the same data as the
existing use of the form tracker/custom search event code. However, it
allows us to share logic and be consistent in event data between all the
different places we offer a search box across GOV.UK, and will allow us
in a future change to add the ability to track usage of the upcoming
autocomplete functionality as well.
  • Loading branch information
csutter committed Oct 31, 2024
1 parent 92ee587 commit cd910d3
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
## Unreleased

* Chart component accessibility improvements ([PR #4344](https://github.com/alphagov/govuk_publishing_components/pull/4344))
* Add new custom analytics tracker for search ([PR #4354](https://github.com/alphagov/govuk_publishing_components/pull/4354)))

## 44.9.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//= require ./analytics-ga4/ga4-auto-tracker
//= require ./analytics-ga4/ga4-smart-answer-results-tracker
//= require ./analytics-ga4/ga4-scroll-tracker
//= require ./analytics-ga4/ga4-search-tracker
//= require ./analytics-ga4/ga4-video-tracker
//= require ./analytics-ga4/ga4-focus-loss-tracker
//= require ./analytics-ga4/init-ga4
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {};
(function (Modules) {
'use strict'
// Tracks interactions with search forms, including on the all content finder ("site search"),
// homepage, and the layout super navigation header search.
class Ga4SearchTracker {
constructor ($module) {
this.$module = $module
this.$searchInput = this.$module.querySelector('input[type="search"]')

this.type = this.$module.dataset.ga4SearchType
this.url = this.$module.dataset.ga4SearchUrl
this.section = this.$module.dataset.ga4SearchSection
this.indexSection = this.$module.dataset.ga4SearchIndexSection
this.indexSectionCount = this.$module.dataset.ga4SearchIndexSectionCount
}

init () {
if (!this.$searchInput) {
console.warn('Ga4SearchTracker: Module added to element without child search input')
return
}

this.initialKeywords = this.$searchInput.value

if (window.GOVUK.getConsentCookie() && window.GOVUK.getConsentCookie().usage) {
this.startModule()
} else {
window.addEventListener('cookie-consent', () => this.startModule())
}
}

startModule () {
this.$module.addEventListener('submit', event => this.trackSearch(event))
}

trackSearch () {
if (this.skipTracking()) return

const data = {
event_name: 'search',
action: 'search',

type: this.type,
section: this.section,
url: this.url,
index_section: this.indexSection,
index_section_count: this.indexSectionCount,
text: this.searchTerm()
}

window.GOVUK.analyticsGa4.core.applySchemaAndSendData(data, 'event_data')
}

skipTracking () {
// Skip tracking for those events that we do not want to track: where the search term is
// present, but has not changed from its initial value
return this.searchTerm() !== '' && this.searchTerm() === this.initialKeywords
}

searchTerm () {
const { standardiseSearchTerm } = window.GOVUK.analyticsGa4.core.trackFunctions

// `standardiseSearchTerm` returns undefined for empty strings, whereas we actively want an
// empty string as part of search events (undefined would not overwrite the current value in
// the analytics state)
return standardiseSearchTerm(this.$searchInput.value) || ''
}
}

Modules.Ga4SearchTracker = Ga4SearchTracker
})(window.GOVUK.Modules)
4 changes: 4 additions & 0 deletions docs/analytics-ga4/trackers.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ The [print intent tracker](trackers/ga4-print-intent-tracker.md) tracks if the p

The [scroll tracker](trackers/ga4-scroll-tracker.md) tracks how much of a page has been viewed.

## Search tracker

The [search tracker](trackers/ga4-search-tracker.md) tracks user interaction with site search boxes in the header, on the homepage, and on the "all content" finder.

## Smart answer results tracker

The [smart answer results tracker](trackers/ga4-smart-answer-results-tracker.md) has been built specifically to track the Cost of Living smart answer.
Expand Down
31 changes: 31 additions & 0 deletions docs/analytics-ga4/trackers/ga4-search-tracker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Google Analytics 4 search tracker

This module allows us to consistently track usage of search across the several different search
fields from which a user can initiate a search on GOV.UK:
- the layout super navigation header
- the homepage
- the search page itself

It is not used by the legacy UI for finders, which triggers search events on user input (rather than
waiting for form submission) as part of its "live search" functionality.

## How it works
Annotate a `<form>` element containing a search field (`<input type="search">`, for example the
`search` or `search_with_autocomplete` publishing components) with the module and its required data
fields:

```html
<form
data-module="ga4-search-tracker"
data-ga4-search-type="site search"
data-ga4-search-url="/search"
data-ga4-search-section="section"
data-ga4-search-index-section="19"
data-ga4-search-index-section-count="89"
>
```

When the form is submitted, a `search` event with the will be tracked containing:
- the type, URL, section, index section, and index section count fields based on the data attributes
outlined above
- the state (text) of the search field contained within
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/* eslint-env jasmine */

describe('Google Analytics search tracking', () => {
'use strict'

let fixture, form, input, sendSpy, ga4SearchTracker
const GOVUK = window.GOVUK

const html = `
<form
data-module="ga4-search-tracker"
data-ga4-search-type="site search"
data-ga4-search-url="/search"
data-ga4-search-section="section"
data-ga4-search-index-section="19"
data-ga4-search-index-section-count="89"
>
<input type="search" name="keyword" value="initial value">
<button type="submit">Search</button>
</form>
`

beforeAll(() => {
GOVUK.analyticsGa4 = GOVUK.analyticsGa4 || {}
GOVUK.analyticsGa4.vars = GOVUK.analyticsGa4.vars || {}
GOVUK.analyticsGa4.vars.gem_version = 'aVersion'
})

afterAll(() => {
window.dataLayer = []
})

beforeEach(() => {
fixture = document.createElement('div')
document.body.appendChild(fixture)
fixture.innerHTML = html

form = fixture.querySelector('form')
input = form.querySelector('input')

sendSpy = spyOn(GOVUK.analyticsGa4.core, 'applySchemaAndSendData')

ga4SearchTracker = new GOVUK.Modules.Ga4SearchTracker(form)
})

afterEach(() => {
fixture.remove()
})

describe('if the search field is missing', () => {
beforeEach(() => {
GOVUK.setConsentCookie({ usage: true })

form.removeChild(input)
ga4SearchTracker = new GOVUK.Modules.Ga4SearchTracker(form)
ga4SearchTracker.init()
})

it('does not track search events', () => {
GOVUK.triggerEvent(form, 'submit')

expect(sendSpy).not.toHaveBeenCalled()
})
})

describe('when usage tracking is declined', () => {
beforeEach(() => {
GOVUK.setConsentCookie({ usage: false })
ga4SearchTracker.init()
})

it('does not track search events', () => {
GOVUK.triggerEvent(form, 'submit')

expect(sendSpy).not.toHaveBeenCalled()
})
})

describe('when usage tracking is accepted', () => {
beforeEach(() => {
GOVUK.setConsentCookie({ usage: true })
ga4SearchTracker.init()
})

it('tracks search events when the input changes', () => {
input.value = 'new value'
GOVUK.triggerEvent(form, 'submit')

expect(sendSpy).toHaveBeenCalledWith(
{
event_name: 'search',
action: 'search',
type: 'site search',
section: 'section',
url: '/search',
index_section: '19',
index_section_count: '89',
text: 'new value'
},
'event_data'
)
})

it('does not track search events when the input does not change', () => {
GOVUK.triggerEvent(form, 'submit')

expect(sendSpy).not.toHaveBeenCalled()
})
})

describe('when the input is originally empty', () => {
beforeEach(() => {
GOVUK.setConsentCookie({ usage: true })
input.value = ''
ga4SearchTracker.init()
})

it('tracks search events even if the (empty) input is unchanged', () => {
GOVUK.triggerEvent(form, 'submit')

expect(sendSpy).toHaveBeenCalledWith(
{
event_name: 'search',
action: 'search',
type: 'site search',
section: 'section',
url: '/search',
index_section: '19',
index_section_count: '89',
text: ''
},
'event_data'
)
})
})
})

0 comments on commit cd910d3

Please sign in to comment.