Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(plugin-gtag): update gtag plugin to modern SPA recommendations #8143

Merged
merged 14 commits into from
Oct 21, 2022
8 changes: 8 additions & 0 deletions packages/docusaurus-plugin-google-gtag/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
"description": "Global Site Tag (gtag.js) plugin for Docusaurus.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"exports": {
".": "./lib/index.js",
"./utils": {
"type": "./lib/utils.d.ts",
"default": "./lib/utils.js"
}
},
"scripts": {
"build": "tsc --build",
"watch": "tsc --build --watch"
Expand All @@ -21,6 +28,7 @@
"@docusaurus/core": "^3.0.0-alpha.0",
"@docusaurus/types": "^3.0.0-alpha.0",
"@docusaurus/utils-validation": "^3.0.0-alpha.0",
"@types/gtag.js": "^0.0.11",
"tslib": "^2.4.0"
},
"peerDependencies": {
Expand Down
12 changes: 7 additions & 5 deletions packages/docusaurus-plugin-google-gtag/src/gtag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ const clientModule: ClientModule = {
setTimeout(() => {
// Always refer to the variable on window in case it gets overridden
// elsewhere.
window.gtag('event', 'page_view', {
page_title: document.title,
page_location: window.location.href,
page_path: location.pathname + location.search + location.hash,
});
// See https://developers.google.com/analytics/devguides/collection/gtagjs/single-page-applications
window.gtag(
lanegoolsby marked this conversation as resolved.
Show resolved Hide resolved
'set',
'page_path',
location.pathname + location.search + location.hash,
);
slorber marked this conversation as resolved.
Show resolved Hide resolved
window.gtag('event', 'page_view');
});
}
},
Expand Down
8 changes: 2 additions & 6 deletions packages/docusaurus-plugin-google-gtag/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@
interface Window {
gtag: (
command: string,
fields: string,
params: {
page_title?: string;
page_location?: string;
page_path?: string;
},
action: string,
params?: string | Gtag.EventParams,
slorber marked this conversation as resolved.
Show resolved Hide resolved
) => void;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need this TS if we have gtag.js typedefs? Why?

This dependency is supposed to provide all the types we need

Have you tried using my recos like the /// directive?

Looks similar to what someone suggest here:
https://andrew-simpson-ross.medium.com/strongly-typed-google-analytics-v4-with-next-js-aad6c6a5e383

Copy link
Contributor Author

@lanegoolsby lanegoolsby Oct 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure but probably not. I don't understand the intricacies of typedefs and the original purpose of this file enough to say with confidence, but yeah, its probably superfluous.

}
8 changes: 8 additions & 0 deletions packages/docusaurus-plugin-google-gtag/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

export {useGoogleAnalytics} from './utils/useGoogleAnalytics';
Copy link
Collaborator

@slorber slorber Oct 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand why we need this abstraction in the first place.

The previous conversation is not resolved: if you can't explain clearly why we need this, we won't add it 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows developers to send custom events to GA without having to go through the gtag.configure call all over again.

We have added some functionality to our DS sites that does not result in a navigation event (basically it displays or hides some content on the page based off a click event). We need to be able to track those clicks. To do this, we need to send custom events to GA.

With this hook the code needed to send the custom events is much cleaner and easier, we don't have to copy/paste the configure boilerplate in individual React components, its type-safe and does not require ts-ignore statements for the window.gtag() call, nor do we need to create an internal service to reduce the boilerplate. Or, more specifically, I decided instead of writing that service for only our internal use I figured I'd give back to the community.

Also, its best to avoid re-calling configure because every time the configure call is made a new pageview event is generated unless its disabled as documented here. Without disabling the default behavior pageview events will be doubled in count. While easy to disable, its also very easy to forget to disable it (ask me how I know that 😅).

Copy link
Collaborator

@slorber slorber Oct 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what you mean by "configure", I only see this in the doc: gtag("config",...)

This is already handle in the plugin:

          {
            tagName: 'script',
            innerHTML: `
              window.dataLayer = window.dataLayer || [];
              function gtag(){dataLayer.push(arguments);}
              gtag('js', new Date());
              gtag('config', '${trackingID}', { ${
              anonymizeIP ? "'anonymize_ip': true" : ''
            } });`,

There's no reason your code should call this "config" action more than once IMHO, or you'd have to explain to me what you want to achieve exactly by calling configure multiple times. It's already done for you, and you can forget about it.

Just write this in your code:

<button onClick={e => {
	  gtag('event', <action>, {
	  'event_category': <category>,
	  'event_label': <label>,
	  'value': <value>
	});
}}/>

No React hook is needed to do that

And if you wire gtag.js types properly, it should also be typesafe.

Now if you really want to have a React hook, you can create it directly in your own codebase. This might be useful for example if you have a TS union type of "allowed custom events" and you want to provide event name autocompletion or typo prevention on custom event names. But that's outside the scope of this plugin and should be handled on a case-by-case basis

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {useCallback} from 'react';

export function useGoogleAnalytics(): {
sendEvent: (action: string, event: Gtag.EventParams) => void;
} {
const sendEvent = useCallback((action: string, event: Gtag.EventParams) => {
if (window.gtag) {
window.gtag('event', action, event);
}
}, []);

return {sendEvent};
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"rootDir": "src",
"outDir": "lib"
},
"include": ["src/gtag.ts", "src/options.ts", "src/*.d.ts"],
"include": ["src/gtag.ts", "src/options.ts", "src/*.d.ts", "src/utils/*"],
"exclude": ["**/__tests__/**"]
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3524,6 +3524,11 @@
dependencies:
"@types/node" "*"

"@types/gtag.js@^0.0.11":
version "0.0.11"
resolved "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.11.tgz#0c384ea32e4e40043a2dca82db79b5e48d681ad5"
integrity sha512-rUuSDedDjcuUpoc2zf6eX6zRrxqALNgwrmMBfVFopkLH7YGM52C7tt6j9GsYIvaxn+ioVRpOKoHnN1DXzHEqIg==

"@types/hast@^2.0.0":
version "2.3.4"
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc"
Expand Down