Skip to content

Commit d652496

Browse files
Simon-Tangwardpeet
authored andcommitted
feat(gatsby-plugin-google-analytics): Add pageTransitionDelay… (#15610)
1 parent 44654be commit d652496

File tree

3 files changed

+34
-20
lines changed

3 files changed

+34
-20
lines changed

packages/gatsby-plugin-google-analytics/README.md

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ module.exports = {
2424
respectDNT: true,
2525
// Avoids sending pageview hits from custom paths
2626
exclude: ["/preview/**", "/do-not-track/me/too/"],
27+
// Delays sending pageview hits on route update (in milliseconds)
28+
pageTransitionDelay: 0,
2729
// Enables Google Optimize using your container Id
2830
optimizeId: "YOUR_GOOGLE_OPTIMIZE_TRACKING_ID",
2931
// Enables Google Optimize Experiment ID
@@ -102,6 +104,10 @@ If you enable this optional option, Google Analytics will not be loaded at all f
102104

103105
If you need to exclude any path from the tracking system, you can add it (one or more) to this optional array as glob expressions.
104106

107+
### `pageTransitionDelay`
108+
109+
If your site uses any custom transitions on route update (e.g. [`gatsby-plugin-transition-link`](https://www.gatsbyjs.org/blog/2018-12-04-per-link-gatsby-page-transitions-with-transitionlink/)), then you can delay processing the page view event until the new page is mounted.
110+
105111
### `optimizeId`
106112

107113
If you need to use Google Optimize for A/B testing, you can add this optional Optimize container id to allow Google Optimize to load the correct test parameters for your site.

packages/gatsby-plugin-google-analytics/src/__tests__/gatsby-browser.js

+23-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { onRouteUpdate } from "../gatsby-browser"
22
import { Minimatch } from "minimatch"
33

4-
jest.useFakeTimers()
5-
64
describe(`gatsby-plugin-google-analytics`, () => {
75
describe(`gatsby-browser`, () => {
86
describe(`onRouteUpdate`, () => {
@@ -29,18 +27,22 @@ describe(`gatsby-plugin-google-analytics`, () => {
2927
})
3028

3129
beforeEach(() => {
30+
jest.useFakeTimers()
3231
window.ga = jest.fn()
33-
window.requestAnimationFrame = jest.fn(cb => {
34-
cb()
35-
})
32+
})
33+
34+
afterEach(() => {
35+
jest.resetAllMocks()
3636
})
3737

3838
it(`does not send page view when ga is undefined`, () => {
3939
delete window.ga
4040

4141
onRouteUpdate({})
4242

43-
expect(window.requestAnimationFrame).not.toHaveBeenCalled()
43+
jest.runAllTimers()
44+
45+
expect(setTimeout).not.toHaveBeenCalled()
4446
})
4547

4648
it(`does not send page view when path is excluded`, () => {
@@ -53,23 +55,34 @@ describe(`gatsby-plugin-google-analytics`, () => {
5355
},
5456
})
5557

58+
jest.runAllTimers()
59+
5660
expect(window.ga).not.toHaveBeenCalled()
5761
})
5862

5963
it(`sends page view`, () => {
6064
onRouteUpdate({})
6165

66+
jest.runAllTimers()
67+
6268
expect(window.ga).toHaveBeenCalledTimes(2)
6369
})
6470

65-
it(`uses setTimeout when requestAnimationFrame is undefined`, () => {
66-
delete window.requestAnimationFrame
67-
71+
it(`uses setTimeout with a minimum delay of 32ms`, () => {
6872
onRouteUpdate({})
6973

7074
jest.runAllTimers()
7175

72-
expect(setTimeout).toHaveBeenCalledTimes(1)
76+
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 32)
77+
expect(window.ga).toHaveBeenCalledTimes(2)
78+
})
79+
80+
it(`uses setTimeout with the provided pageTransitionDelay value`, () => {
81+
onRouteUpdate({}, { pageTransitionDelay: 1000 })
82+
83+
jest.runAllTimers()
84+
85+
expect(setTimeout).toHaveBeenCalledWith(expect.any(Function), 1000)
7386
expect(window.ga).toHaveBeenCalledTimes(2)
7487
})
7588
})
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export const onRouteUpdate = ({ location }) => {
1+
export const onRouteUpdate = ({ location }, pluginOptions = {}) => {
22
if (process.env.NODE_ENV !== `production` || typeof ga !== `function`) {
33
return null
44
}
@@ -11,7 +11,7 @@ export const onRouteUpdate = ({ location }) => {
1111
if (pathIsExcluded) return null
1212

1313
// wrap inside a timeout to make sure react-helmet is done with it's changes (https://github.com/gatsbyjs/gatsby/issues/9139)
14-
// reactHelmet is using requestAnimationFrame so we should use it too: https://github.com/nfl/react-helmet/blob/5.2.0/src/HelmetUtils.js#L296-L299
14+
// reactHelmet is using requestAnimationFrame: https://github.com/nfl/react-helmet/blob/5.2.0/src/HelmetUtils.js#L296-L299
1515
const sendPageView = () => {
1616
const pagePath = location
1717
? location.pathname + location.search + location.hash
@@ -20,14 +20,9 @@ export const onRouteUpdate = ({ location }) => {
2020
window.ga(`send`, `pageview`)
2121
}
2222

23-
if (`requestAnimationFrame` in window) {
24-
requestAnimationFrame(() => {
25-
requestAnimationFrame(sendPageView)
26-
})
27-
} else {
28-
// simulate 2 rAF calls
29-
setTimeout(sendPageView, 32)
30-
}
23+
// Minimum delay for reactHelmet's requestAnimationFrame
24+
const delay = Math.max(32, pluginOptions.pageTransitionDelay || 0)
25+
setTimeout(sendPageView, delay)
3126

3227
return null
3328
}

0 commit comments

Comments
 (0)