Skip to content

Commit

Permalink
Adding useGoogleTagManager hook to @next/third-parties (#56106)
Browse files Browse the repository at this point in the history
This PR adds the `useGoogleTagManage` hook to `@next/third-parties` repo.
  • Loading branch information
janicklas-ralph authored Oct 17, 2023
1 parent df1d4a1 commit 5e474a3
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 3 deletions.
1 change: 0 additions & 1 deletion packages/third-parties/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "@next/third-parties",
"version": "13.5.6-canary.3",
"private": true,
"repository": {
"url": "vercel/next.js",
"directory": "packages/third-parties"
Expand Down
68 changes: 68 additions & 0 deletions packages/third-parties/src/google/gtm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client'
// TODO: Evaluate import 'client only'
import React from 'react'
import Script from 'next/script'

declare global {
interface Window {
dataLayer?: Object[]
[key: string]: any
}
}

type GTMParams = {
gtmId: string
dataLayer: string[]
dataLayerName: string
auth: string
preview: string
}

let currDataLayerName: string | undefined = undefined

export function GoogleTagManager(props: GTMParams) {
const { gtmId, dataLayerName = 'dataLayer', auth, preview, dataLayer } = props

if (currDataLayerName === undefined) {
currDataLayerName = dataLayerName
}

const gtmLayer = dataLayerName !== 'dataLayer' ? `$l=${dataLayerName}` : ''
const gtmAuth = auth ? `&gtm_auth=${auth}` : ''
const gtmPreview = preview ? `&gtm_preview=${preview}&gtm_cookies_win=x` : ''

return (
<>
<Script
id="_next-gtm-init"
dangerouslySetInnerHTML={{
__html: `
(function(w,l){
w[l]=w[l]||[];
w[l].push({'gtm.start': new Date().getTime(),event:'gtm.js'});
${dataLayer ? `w[l].push(${JSON.stringify(dataLayer)})` : ''}
})(window,'${dataLayerName}');`,
}}
/>
<Script
id="_next-gtm"
src={`https://www.googletagmanager.com/gtm.js?id=${gtmId}${gtmLayer}${gtmAuth}${gtmPreview}`}
/>
</>
)
}

export const sendGTMEvent = (data: Object) => {
if (currDataLayerName === undefined) {
console.warn(`@next/third-parties: GTM has not been initialized`)
return
}

if (window[currDataLayerName]) {
window[currDataLayerName].push(data)
} else {
console.warn(
`@next/third-parties: GTM dataLayer ${currDataLayerName} does not exist`
)
}
}
5 changes: 3 additions & 2 deletions packages/third-parties/src/google/index.tsx
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as GoogleMapsEmbed } from './GoogleMapsEmbed'
export { default as YouTubeEmbed } from './YouTubeEmbed'
export { default as GoogleMapsEmbed } from './google-maps-embed'
export { default as YouTubeEmbed } from './youtube-embed'
export { GoogleTagManager, sendGTMEvent } from './gtm'
23 changes: 23 additions & 0 deletions test/e2e/app-dir/third-parties/app/gtm/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use client'

import React from 'react'
import { GoogleTagManager, sendGTMEvent } from '@next/third-parties/google'

const Page = () => {
const onClick = () => {
sendGTMEvent({ event: 'buttonClicked', value: 'xyz' })
}

return (
<div class="container">
<GoogleTagManager gtmId="GTM-XYZ" />
<h1>GTM</h1>
<button id="gtm-send" onClick={onClick}>
Click
</button>
<GoogleTagManager gtmId="GTM-XYZ" />
</div>
)
}

export default Page
25 changes: 25 additions & 0 deletions test/e2e/app-dir/third-parties/basic.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createNextDescribe } from 'e2e-utils'
import { waitFor } from 'next-test-utils'

createNextDescribe(
'@next/third-parties basic usage',
Expand Down Expand Up @@ -29,5 +30,29 @@ createNextDescribe(
expect(baseContainer.length).toBe(1)
expect(mapContainer.length).toBe(1)
})

it('renders GTM', async () => {
const browser = await next.browser('/gtm')

await browser.waitForElementByCss('#_next-gtm')
await waitFor(1000)

const gtmInlineScript = await browser.elementsByCss('#_next-gtm-init')
expect(gtmInlineScript.length).toBe(1)

const gtmScript = await browser.elementsByCss(
'[src^="https://www.googletagmanager.com/gtm.js?id=GTM-XYZ"]'
)

expect(gtmScript.length).toBe(1)

const dataLayer = await browser.eval('window.dataLayer')
expect(dataLayer.length).toBe(1)

await browser.elementByCss('#gtm-send').click()

const dataLayer2 = await browser.eval('window.dataLayer')
expect(dataLayer2.length).toBe(2)
})
}
)
25 changes: 25 additions & 0 deletions test/e2e/third-parties/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createNextDescribe } from 'e2e-utils'
import { waitFor } from 'next-test-utils'

createNextDescribe(
'@next/third-parties basic usage',
Expand Down Expand Up @@ -28,5 +29,29 @@ createNextDescribe(
expect(baseContainer.length).toBe(1)
expect(mapContainer.length).toBe(1)
})

it('renders GTM', async () => {
const browser = await next.browser('/gtm')

await browser.waitForElementByCss('#_next-gtm')
await waitFor(1000)

const gtmInlineScript = await browser.elementsByCss('#_next-gtm-init')
expect(gtmInlineScript.length).toBe(1)

const gtmScript = await browser.elementsByCss(
'[src^="https://www.googletagmanager.com/gtm.js?id=GTM-XYZ"]'
)

expect(gtmScript.length).toBe(1)

const dataLayer = await browser.eval('window.dataLayer')
expect(dataLayer.length).toBe(1)

await browser.elementByCss('#gtm-send').click()

const dataLayer2 = await browser.eval('window.dataLayer')
expect(dataLayer2.length).toBe(2)
})
}
)
21 changes: 21 additions & 0 deletions test/e2e/third-parties/pages/gtm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react'
import { GoogleTagManager, sendGTMEvent } from '@next/third-parties/google'

const Page = () => {
const onClick = () => {
sendGTMEvent({ event: 'buttonClicked', value: 'xyz' })
}

return (
<div class="container">
<GoogleTagManager gtmId="GTM-XYZ" />
<h1>GTM</h1>
<button id="gtm-send" onClick={onClick}>
Click
</button>
<GoogleTagManager gtmId="GTM-XYZ" />
</div>
)
}

export default Page

0 comments on commit 5e474a3

Please sign in to comment.