From c174b29c06d3b1f640a840a1dde466aca731e370 Mon Sep 17 00:00:00 2001 From: Sagirov Evgeniy <34642612+UvgenGen@users.noreply.github.com> Date: Tue, 18 Apr 2023 22:36:33 +0300 Subject: [PATCH] feat: Add GoogleAnalytics 4 to frontend-platform PR #472 --------- Co-authored-by: Glib Glugovskiy --- src/initialize.js | 15 +++++ src/scripts/GoogleAnalyticsLoader.js | 53 ++++++++++++++++ src/scripts/GoogleAnalyticsLoader.test.js | 76 +++++++++++++++++++++++ src/scripts/index.js | 2 + 4 files changed, 146 insertions(+) create mode 100644 src/scripts/GoogleAnalyticsLoader.js create mode 100644 src/scripts/GoogleAnalyticsLoader.test.js create mode 100644 src/scripts/index.js diff --git a/src/initialize.js b/src/initialize.js index f27fe4d00..7af609398 100644 --- a/src/initialize.js +++ b/src/initialize.js @@ -59,6 +59,7 @@ import { import { configure as configureAnalytics, SegmentAnalyticsService, identifyAnonymousUser, identifyAuthenticatedUser, } from './analytics'; +import { GoogleAnalyticsLoader } from './scripts'; import { getAuthenticatedHttpClient, configure as configureAuth, @@ -157,6 +158,13 @@ export async function runtimeConfig() { } } +export function loadExternalScripts(externalScripts, data) { + externalScripts.forEach(ExternalScript => { + const script = new ExternalScript(data); + script.loadScript(); + }); +} + /** * The default handler for the initialization lifecycle's `analytics` phase. * @@ -221,6 +229,8 @@ function applyOverrideHandlers(overrides) { * @param {*} [options.analyticsService=SegmentAnalyticsService] The `AnalyticsService` * implementation to use. * @param {*} [options.authMiddleware=[]] An array of middleware to apply to http clients in the auth service. + * @param {*} [options.externalScripts=[GoogleAnalyticsLoader]] An array of externalScripts. + * By default added GoogleAnalyticsLoader. * @param {*} [options.requireAuthenticatedUser=false] If true, turns on automatic login * redirection for unauthenticated users. Defaults to false, meaning that by default the * application will allow anonymous/unauthenticated sessions. @@ -240,6 +250,7 @@ export async function initialize({ analyticsService = SegmentAnalyticsService, authService = AxiosJwtAuthService, authMiddleware = [], + externalScripts = [GoogleAnalyticsLoader], requireAuthenticatedUser: requireUser = false, hydrateAuthenticatedUser: hydrateUser = false, messages, @@ -256,6 +267,10 @@ export async function initialize({ await runtimeConfig(); publish(APP_CONFIG_INITIALIZED); + loadExternalScripts(externalScripts, { + config: getConfig(), + }); + // Logging configureLogging(loggingService, { config: getConfig(), diff --git a/src/scripts/GoogleAnalyticsLoader.js b/src/scripts/GoogleAnalyticsLoader.js new file mode 100644 index 000000000..290a7a1fc --- /dev/null +++ b/src/scripts/GoogleAnalyticsLoader.js @@ -0,0 +1,53 @@ +/** + * @implements {GoogleAnalyticsLoader} + * @memberof module:GoogleAnalytics + */ +class GoogleAnalyticsLoader { + constructor({ config }) { + this.analyticsId = config.GOOGLE_ANALYTICS_4_ID; + } + + loadScript() { + if (!this.analyticsId) { + return; + } + + global.googleAnalytics = global.googleAnalytics || []; + const { googleAnalytics } = global; + + // If the snippet was invoked do nothing. + if (googleAnalytics.invoked) { + return; + } + + // Invoked flag, to make sure the snippet + // is never invoked twice. + googleAnalytics.invoked = true; + + googleAnalytics.load = (key, options) => { + const scriptSrc = document.createElement('script'); + scriptSrc.type = 'text/javascript'; + scriptSrc.async = true; + scriptSrc.src = `https://www.googletagmanager.com/gtag/js?id=${key}`; + + const scriptGtag = document.createElement('script'); + scriptGtag.innerHTML = ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '${key}'); + `; + + // Insert our scripts next to the first script element. + const first = document.getElementsByTagName('script')[0]; + first.parentNode.insertBefore(scriptSrc, first); + first.parentNode.insertBefore(scriptGtag, first); + googleAnalytics._loadOptions = options; // eslint-disable-line no-underscore-dangle + }; + + // Load GoogleAnalytics with your key. + googleAnalytics.load(this.analyticsId); + } +} + +export default GoogleAnalyticsLoader; diff --git a/src/scripts/GoogleAnalyticsLoader.test.js b/src/scripts/GoogleAnalyticsLoader.test.js new file mode 100644 index 000000000..cca57edee --- /dev/null +++ b/src/scripts/GoogleAnalyticsLoader.test.js @@ -0,0 +1,76 @@ +import { GoogleAnalyticsLoader } from './index'; + +const googleAnalyticsId = 'test-key'; + +describe('GoogleAnalytics', () => { + let body; + let gaScriptSrc; + let gaScriptGtag; + let data; + + beforeEach(() => { + window.googleAnalytics = []; + }); + + function loadGoogleAnalytics(scriptData) { + const script = new GoogleAnalyticsLoader(scriptData); + script.loadScript(); + } + + describe('with valid GOOGLE_ANALYTICS_4_ID', () => { + beforeEach(() => { + document.body.innerHTML = '