Skip to content

Commit c174b29

Browse files
feat: Add GoogleAnalytics 4 to frontend-platform
PR #472 --------- Co-authored-by: Glib Glugovskiy <[email protected]>
1 parent 299ad79 commit c174b29

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

src/initialize.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import {
5959
import {
6060
configure as configureAnalytics, SegmentAnalyticsService, identifyAnonymousUser, identifyAuthenticatedUser,
6161
} from './analytics';
62+
import { GoogleAnalyticsLoader } from './scripts';
6263
import {
6364
getAuthenticatedHttpClient,
6465
configure as configureAuth,
@@ -157,6 +158,13 @@ export async function runtimeConfig() {
157158
}
158159
}
159160

161+
export function loadExternalScripts(externalScripts, data) {
162+
externalScripts.forEach(ExternalScript => {
163+
const script = new ExternalScript(data);
164+
script.loadScript();
165+
});
166+
}
167+
160168
/**
161169
* The default handler for the initialization lifecycle's `analytics` phase.
162170
*
@@ -221,6 +229,8 @@ function applyOverrideHandlers(overrides) {
221229
* @param {*} [options.analyticsService=SegmentAnalyticsService] The `AnalyticsService`
222230
* implementation to use.
223231
* @param {*} [options.authMiddleware=[]] An array of middleware to apply to http clients in the auth service.
232+
* @param {*} [options.externalScripts=[GoogleAnalyticsLoader]] An array of externalScripts.
233+
* By default added GoogleAnalyticsLoader.
224234
* @param {*} [options.requireAuthenticatedUser=false] If true, turns on automatic login
225235
* redirection for unauthenticated users. Defaults to false, meaning that by default the
226236
* application will allow anonymous/unauthenticated sessions.
@@ -240,6 +250,7 @@ export async function initialize({
240250
analyticsService = SegmentAnalyticsService,
241251
authService = AxiosJwtAuthService,
242252
authMiddleware = [],
253+
externalScripts = [GoogleAnalyticsLoader],
243254
requireAuthenticatedUser: requireUser = false,
244255
hydrateAuthenticatedUser: hydrateUser = false,
245256
messages,
@@ -256,6 +267,10 @@ export async function initialize({
256267
await runtimeConfig();
257268
publish(APP_CONFIG_INITIALIZED);
258269

270+
loadExternalScripts(externalScripts, {
271+
config: getConfig(),
272+
});
273+
259274
// Logging
260275
configureLogging(loggingService, {
261276
config: getConfig(),

src/scripts/GoogleAnalyticsLoader.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* @implements {GoogleAnalyticsLoader}
3+
* @memberof module:GoogleAnalytics
4+
*/
5+
class GoogleAnalyticsLoader {
6+
constructor({ config }) {
7+
this.analyticsId = config.GOOGLE_ANALYTICS_4_ID;
8+
}
9+
10+
loadScript() {
11+
if (!this.analyticsId) {
12+
return;
13+
}
14+
15+
global.googleAnalytics = global.googleAnalytics || [];
16+
const { googleAnalytics } = global;
17+
18+
// If the snippet was invoked do nothing.
19+
if (googleAnalytics.invoked) {
20+
return;
21+
}
22+
23+
// Invoked flag, to make sure the snippet
24+
// is never invoked twice.
25+
googleAnalytics.invoked = true;
26+
27+
googleAnalytics.load = (key, options) => {
28+
const scriptSrc = document.createElement('script');
29+
scriptSrc.type = 'text/javascript';
30+
scriptSrc.async = true;
31+
scriptSrc.src = `https://www.googletagmanager.com/gtag/js?id=${key}`;
32+
33+
const scriptGtag = document.createElement('script');
34+
scriptGtag.innerHTML = `
35+
window.dataLayer = window.dataLayer || [];
36+
function gtag(){dataLayer.push(arguments);}
37+
gtag('js', new Date());
38+
gtag('config', '${key}');
39+
`;
40+
41+
// Insert our scripts next to the first script element.
42+
const first = document.getElementsByTagName('script')[0];
43+
first.parentNode.insertBefore(scriptSrc, first);
44+
first.parentNode.insertBefore(scriptGtag, first);
45+
googleAnalytics._loadOptions = options; // eslint-disable-line no-underscore-dangle
46+
};
47+
48+
// Load GoogleAnalytics with your key.
49+
googleAnalytics.load(this.analyticsId);
50+
}
51+
}
52+
53+
export default GoogleAnalyticsLoader;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { GoogleAnalyticsLoader } from './index';
2+
3+
const googleAnalyticsId = 'test-key';
4+
5+
describe('GoogleAnalytics', () => {
6+
let body;
7+
let gaScriptSrc;
8+
let gaScriptGtag;
9+
let data;
10+
11+
beforeEach(() => {
12+
window.googleAnalytics = [];
13+
});
14+
15+
function loadGoogleAnalytics(scriptData) {
16+
const script = new GoogleAnalyticsLoader(scriptData);
17+
script.loadScript();
18+
}
19+
20+
describe('with valid GOOGLE_ANALYTICS_4_ID', () => {
21+
beforeEach(() => {
22+
document.body.innerHTML = '<script id="stub" />';
23+
data = {
24+
config: {
25+
GOOGLE_ANALYTICS_4_ID: googleAnalyticsId,
26+
},
27+
};
28+
loadGoogleAnalytics(data);
29+
expect(global.googleAnalytics.invoked).toBe(true);
30+
body = document.body.innerHTML;
31+
gaScriptSrc = `https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsId}`;
32+
gaScriptGtag = `gtag('config', '${googleAnalyticsId}');`;
33+
});
34+
35+
it('should initialize google analytics', () => {
36+
expect(body).toMatch(gaScriptSrc);
37+
expect(body).toMatch(gaScriptGtag);
38+
});
39+
40+
it('should not invoke snippet twice', () => {
41+
loadGoogleAnalytics(data);
42+
43+
expect(global.googleAnalytics.invoked).toBe(true);
44+
45+
expect(body).toMatch(gaScriptSrc);
46+
expect(body).toMatch(gaScriptGtag);
47+
48+
let count = (body.match(new RegExp(gaScriptSrc.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
49+
expect(count).toBe(1);
50+
51+
count = (body.match(new RegExp(gaScriptGtag.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')) || []).length;
52+
expect(count).toBe(1);
53+
});
54+
});
55+
56+
describe('with invalid GOOGLE_ANALYTICS_ID', () => {
57+
beforeEach(() => {
58+
document.body.innerHTML = '<script id="stub" />';
59+
data = {
60+
config: {
61+
GOOGLE_ANALYTICS_4_ID: '',
62+
},
63+
};
64+
loadGoogleAnalytics(data);
65+
body = document.body.innerHTML;
66+
gaScriptSrc = 'https://www.googletagmanager.com/gtag/js?id=';
67+
gaScriptGtag = "gtag('config', '');";
68+
expect(global.googleAnalytics.invoked).toBeFalsy();
69+
});
70+
71+
it('should not initialize google analytics', () => {
72+
expect(body).not.toMatch(gaScriptSrc);
73+
expect(body).not.toMatch(gaScriptGtag);
74+
});
75+
});
76+
});

src/scripts/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/* eslint-disable import/prefer-default-export */
2+
export { default as GoogleAnalyticsLoader } from './GoogleAnalyticsLoader';

0 commit comments

Comments
 (0)