Skip to content

Commit 4e431f3

Browse files
robmarshallwardpeet
authored andcommitted
feat(gatsby-plugin-google-analytics): add custom event helper (#17612)
1 parent 4db2ff1 commit 4e431f3

File tree

5 files changed

+154
-3
lines changed

5 files changed

+154
-3
lines changed

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

+52
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,58 @@ This plugin also supports several optional General fields documented in [Google
150150

151151
These fields can be specified in the plugin's `options` as shown in the [How to use](#how-to-use) section.
152152

153+
## TrackCustomEvent Function
154+
155+
To allow custom events to be tracked, the plugin exposes a function to include in your project.
156+
157+
To use it, import the package and call the event within your components and business logic.
158+
159+
```jsx
160+
import React
161+
import { trackCustomEvent } from 'gatsby-plugin-google-analytics'
162+
163+
export default () => {
164+
<div>
165+
<button
166+
onClick={e => {
167+
// To stop the page reloading
168+
e.preventDefault()
169+
// Lets track that custom click
170+
trackCustomEvent({
171+
// string - required - The object that was interacted with (e.g.video)
172+
category: "Special Button",
173+
// string - required - Type of interaction (e.g. 'play')
174+
action: "Click",
175+
// string - optional - Useful for categorizing events (e.g. 'Spring Campaign')
176+
label: "Gatsby Plugin Example Campaign",
177+
// number - optional - Numeric value associated with the event. (e.g. A product ID)
178+
value: 43
179+
})
180+
//... Other logic here
181+
}}
182+
>
183+
Tap that!
184+
</button>
185+
</div>
186+
}
187+
```
188+
189+
### All Fields Options
190+
191+
- `category`: string - required
192+
- `action`: string - required
193+
- `label`: string
194+
- `value`: integer
195+
- `nonInteraction`: bool
196+
- `transport`: string
197+
- `hitCallback`: function
198+
199+
For more information see the [Google Analytics](https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#events) documentation.
200+
201+
#### hitCallback
202+
203+
A timeout is included by default incase the Analytics library fails to load. For more information see [Google Analytics - Handling Timeouts](https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits#handling_timeouts)
204+
153205
## Troubleshooting
154206

155207
### No actions are tracked

packages/gatsby-plugin-google-analytics/index.d.ts

+13
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,16 @@ export class OutboundLink extends React.Component<
88
OutboundLinkProps & React.HTMLProps<HTMLAnchorElement>,
99
any
1010
> {}
11+
12+
export interface CustomEventArgs {
13+
category: string
14+
action: string
15+
label?: string
16+
value?: string
17+
nonInteraction: boolean
18+
transport: "beacon" | "xhr" | "image"
19+
hitCallback: Function
20+
callbackTimeout: Number
21+
}
22+
23+
export function trackCustomEvent(args: CustomEventArgs): void

packages/gatsby-plugin-google-analytics/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@
4141
},
4242
"engines": {
4343
"node": ">=8.0.0"
44-
}
44+
},
45+
"types": "./index.d.ts"
4546
}

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

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from "react"
22
import { cleanup, fireEvent, render } from "@testing-library/react"
3-
import { OutboundLink } from "../"
3+
import { trackCustomEvent, OutboundLink } from "../"
44

55
describe(`index.js`, () => {
66
describe(`<OutboundLink />`, () => {
@@ -65,4 +65,40 @@ describe(`index.js`, () => {
6565
expect(props.onClick).toHaveBeenCalled()
6666
})
6767
})
68+
69+
describe(`trackCustomEvent()`, () => {
70+
afterEach(cleanup)
71+
72+
const setup = props => {
73+
const utils = render(
74+
<button
75+
onClick={e => {
76+
e.preventDefault()
77+
trackCustomEvent({
78+
category: `event`,
79+
action: `action`,
80+
label: `label`,
81+
value: `value`,
82+
})
83+
}}
84+
>
85+
tapthis
86+
</button>
87+
)
88+
89+
return Object.assign({}, utils, {
90+
button: utils.getByText(`tapthis`),
91+
})
92+
}
93+
94+
it(`sends tracking event when clicked`, () => {
95+
window.ga = jest.fn()
96+
97+
const { button } = setup()
98+
99+
fireEvent.click(button)
100+
101+
expect(window.ga).toHaveBeenCalled()
102+
})
103+
})
68104
})

packages/gatsby-plugin-google-analytics/src/index.js

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
import React from "react"
22
import PropTypes from "prop-types"
33

4+
const createFunctionWithTimeout = (callback, opt_timeout = 1000) => {
5+
let called = false
6+
const raceCallback = () => {
7+
if (!called) {
8+
called = true
9+
callback()
10+
}
11+
}
12+
setTimeout(raceCallback, opt_timeout)
13+
return raceCallback
14+
}
15+
416
function OutboundLink(props) {
517
return (
618
<a
@@ -53,4 +65,41 @@ OutboundLink.propTypes = {
5365
onClick: PropTypes.func,
5466
}
5567

56-
export { OutboundLink }
68+
/**
69+
* This allows the user to create custom events within their Gatsby projects.
70+
*
71+
* @param {import('gatsby-plugin-google-analytics').CustomEventArgs} args
72+
* @see https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#events
73+
*/
74+
function trackCustomEvent({
75+
category,
76+
action,
77+
label,
78+
value,
79+
nonInteraction = true,
80+
transport,
81+
hitCallback,
82+
callbackTimeout = 1000,
83+
}) {
84+
if (typeof window !== `undefined` && window.ga) {
85+
const trackingEventOptions = {
86+
eventCategory: category,
87+
eventAction: action,
88+
eventLabel: label,
89+
eventValue: value,
90+
nonInteraction: nonInteraction,
91+
transport,
92+
}
93+
94+
if (hitCallback && typeof hitCallback === `function`) {
95+
trackingEventOptions.hitCallback = createFunctionWithTimeout(
96+
hitCallback,
97+
callbackTimeout
98+
)
99+
}
100+
101+
window.ga(`send`, `event`, trackingEventOptions)
102+
}
103+
}
104+
105+
export { OutboundLink, trackCustomEvent }

0 commit comments

Comments
 (0)