diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index 8f77944903..915b2ed8d2 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -7,14 +7,15 @@ import { installAnalyticsScript, sendPageViewEvent, attachExternalLinkTracker, - attachQuestionXsRoutesTracker + attachQuestionXsRoutesTracker, + attachOptionalLinkTracker } from '../javascript/google-tag' import { saveConsentStatus } from '../javascript/utils/cookie-consent' import ajaxMarkdownPreview from '../javascript/ajax-markdown-preview' document .querySelectorAll('[data-module="copy-to-clipboard"]') - .forEach(element => { + .forEach((element) => { copyToClipboard( element, element.querySelector('[data-copy-target]'), @@ -24,7 +25,7 @@ document document .querySelectorAll('[data-module="markdown-editor-toolbar"]') - .forEach(element => { + .forEach((element) => { markdownEditorToolbar( element, JSON.parse(element.getAttribute('data-i18n')), @@ -35,7 +36,7 @@ document document .querySelectorAll('[data-module="ajax-markdown-preview"]') - .forEach(element => { + .forEach((element) => { ajaxMarkdownPreview( element.querySelector('[data-ajax-markdown-target]'), element.querySelector('[data-ajax-markdown-source]'), @@ -50,6 +51,7 @@ if (document.body.dataset.googleAnalyticsEnabled === 'true') { sendPageViewEvent() attachExternalLinkTracker() attachQuestionXsRoutesTracker() + attachOptionalLinkTracker() } initAll() diff --git a/app/frontend/javascript/google-tag/index.js b/app/frontend/javascript/google-tag/index.js index e74b8afee1..46b275324d 100644 --- a/app/frontend/javascript/google-tag/index.js +++ b/app/frontend/javascript/google-tag/index.js @@ -1,7 +1,7 @@ export function installAnalyticsScript (global) { const GTAG_ID = 'GTM-MFJWJNW' if (!window.ga) { - ;(function (w, d, s, l, i) { + (function (w, d, s, l, i) { w[l] = w[l] || [] w[l].push({ 'gtm.start': new Date().getTime(), @@ -68,6 +68,27 @@ export function attachExternalLinkTracker () { }) } +export function attachOptionalLinkTracker () { + const linksToTrack = document.querySelectorAll('a[data-track-link]') + linksToTrack.forEach(function (link) { + link.addEventListener('click', function (event) { + const target = event.target + const external = target.getAttribute('href').startsWith('http') + window.dataLayer.push({ + event: 'event_data', + event_data: { + event_name: 'navigation', + external, + method: 'primary click', + text: target.textContent, + type: 'generic link', + url: target.href + } + }) + }) + }) +} + export function attachQuestionXsRoutesTracker () { const showRoutesPathRegex = /^\/forms\/\d+\/pages\/\d+\/routes$/ const path = window.location.pathname diff --git a/app/frontend/javascript/google-tag/index.test.js b/app/frontend/javascript/google-tag/index.test.js index b86947012e..97f3749714 100644 --- a/app/frontend/javascript/google-tag/index.test.js +++ b/app/frontend/javascript/google-tag/index.test.js @@ -7,7 +7,8 @@ import { sendPageViewEvent, attachExternalLinkTracker, setDefaultConsent, - attachQuestionXsRoutesTracker + attachQuestionXsRoutesTracker, + attachOptionalLinkTracker } from '../google-tag' import { describe, afterEach, it, expect, beforeEach } from 'vitest' @@ -151,7 +152,7 @@ describe('google_tag.mjs', () => { data: 'Some existing data in the dataLayer' } - const preventDefault = event => { + const preventDefault = (event) => { event.preventDefault() } @@ -243,4 +244,116 @@ describe('google_tag.mjs', () => { }) }) }) + + describe('attachOptionalLinkTracker', () => { + const preventDefault = (event) => { + event.preventDefault() + } + beforeEach(() => { + document.body.innerHTML = ` + A link to example.com + A link to example.com + A secure link to example.com + Dpwnload a CSV file + A link to example@example.com + ` + window.dataLayer = [] + + // stop link clicks from navigating, since jsdom can't do navigation + document.querySelector('a').addEventListener('click', preventDefault) + }) + + afterEach(() => { + document.querySelector('a').removeEventListener('click', preventDefault) + window.dataLayer = [] + }) + + it('tracks clicks on external HTTP link with data-track-link attribute', () => { + attachOptionalLinkTracker() + + const externalLink = document.getElementById('externalHTTP') + externalLink.click() + expect(window.dataLayer).toEqual([ + { + event: 'event_data', + event_data: { + event_name: 'navigation', + external: true, + method: 'primary click', + text: 'A link to example.com', + type: 'generic link', + url: 'http://example.com/' + } + } + ]) + }) + + it('tracks clicks on external HTTPS link with data-track-link attribute', () => { + attachOptionalLinkTracker() + + const externalLink = document.getElementById('externalHTTPS') + externalLink.click() + expect(window.dataLayer).toEqual([ + { + event: 'event_data', + event_data: { + event_name: 'navigation', + external: true, + method: 'primary click', + text: 'A secure link to example.com', + type: 'generic link', + url: 'https://example.com/' + } + } + ]) + }) + + it('tracks clicks on internal link with data-track-link attribute', () => { + attachOptionalLinkTracker() + + const internalLink = document.getElementById('internal') + internalLink.click() + expect(window.dataLayer).toEqual([ + { + event: 'event_data', + event_data: { + event_name: 'navigation', + external: false, + method: 'primary click', + text: 'Dpwnload a CSV file', + type: 'generic link', + url: 'http://localhost:3000/a_csv_file.csv' + } + } + ]) + }) + + it('does not track clicks on links without data-track-link attribute', () => { + attachOptionalLinkTracker() + + const link = document.getElementById('noTrack') + link.click() + expect(window.dataLayer).toEqual([]) + }) + + it('tracks clicks on mailto link with data-track-link attribute', () => { + attachOptionalLinkTracker() + + const link = document.getElementById('mailto') + link.click() + expect(window.dataLayer).toEqual([ + { + event: 'event_data', + event_data: { + event_name: 'navigation', + external: false, + method: 'primary click', + text: 'A link to example@example.com', + type: 'generic link', + url: 'mailto:example@example.com' + } + } + ]) + }) + }) }) diff --git a/app/views/forms/welsh_translation/new.html.erb b/app/views/forms/welsh_translation/new.html.erb index 35d2507f6f..c4e72dfd1a 100644 --- a/app/views/forms/welsh_translation/new.html.erb +++ b/app/views/forms/welsh_translation/new.html.erb @@ -14,7 +14,7 @@