-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new custom analytics tracker for search
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
Showing
6 changed files
with
246 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
app/assets/javascripts/govuk_publishing_components/analytics-ga4/ga4-search-tracker.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
136 changes: 136 additions & 0 deletions
136
spec/javascripts/govuk_publishing_components/analytics-ga4/ga4-search-tracker.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
) | ||
}) | ||
}) | ||
}) |