From 5d5f183684a81f8d4dac251336c8f4b12c3a2970 Mon Sep 17 00:00:00 2001 From: Maxim Chervonny Date: Wed, 14 Jun 2023 20:30:30 +0300 Subject: [PATCH 1/6] add gtmId to config.js --- catalog/app/utils/Config.ts | 1 + catalog/config-schema.json | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/catalog/app/utils/Config.ts b/catalog/app/utils/Config.ts index d962489640f..29a096cb78c 100644 --- a/catalog/app/utils/Config.ts +++ b/catalog/app/utils/Config.ts @@ -22,6 +22,7 @@ export interface ConfigJson { registryUrl: string s3Proxy: string + gtmId?: string intercomAppId?: string mixpanelToken: string sentryDSN?: string diff --git a/catalog/config-schema.json b/catalog/config-schema.json index ce72fa51830..9f26732a491 100644 --- a/catalog/config-schema.json +++ b/catalog/config-schema.json @@ -88,6 +88,10 @@ "build_version": { "type": "string", "description": "Optional" + }, + "gtmId": { + "type": "string", + "description": "ID for Google TagManager/Analytics service" } }, "required": [ From bc683cc816e2848532b4f22c0d4ac64ff1c88eb9 Mon Sep 17 00:00:00 2001 From: Maxim Chervonny Date: Wed, 14 Jun 2023 20:31:02 +0300 Subject: [PATCH 2/6] use simple copy-pasted code as gtm initialization --- catalog/app/app.tsx | 2 ++ catalog/app/utils/gtm.tsx | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 catalog/app/utils/gtm.tsx diff --git a/catalog/app/app.tsx b/catalog/app/app.tsx index b4d07e2f30d..4ea88aa2d50 100644 --- a/catalog/app/app.tsx +++ b/catalog/app/app.tsx @@ -24,6 +24,7 @@ import { ExperimentsProvider } from 'components/Experiments' import * as Intercom from 'components/Intercom' import Placeholder from 'components/Placeholder' import App from 'containers/App' +import GTMLoader from 'utils/gtm' import * as Auth from 'containers/Auth' import * as Errors from 'containers/Errors' import * as Notifications from 'containers/Notifications' @@ -97,6 +98,7 @@ const render = () => { Notifications.Provider, [APIConnector.Provider, { fetch, middleware: [Auth.apiMiddleware] }], [Auth.Provider, { checkOn: LOCATION_CHANGE, storage }], + GTMLoader, [ Intercom.Provider, { diff --git a/catalog/app/utils/gtm.tsx b/catalog/app/utils/gtm.tsx new file mode 100644 index 00000000000..531e7e03170 --- /dev/null +++ b/catalog/app/utils/gtm.tsx @@ -0,0 +1,40 @@ +import * as React from 'react' + +import cfg from 'constants/config' + +function loadScript(src: string) { + const s = window.document.createElement('script') + s.type = 'text/javascript' + s.async = true + s.src = src + + const x = window.document.getElementsByTagName('script')[0] + x.parentNode?.insertBefore(s, x) +} + +function addScript(content: string) { + const s = window.document.createElement('script') + s.type = 'text/javascript' + s.innerText = content + const x = window.document.getElementsByTagName('script')[0] + x.parentNode?.insertBefore(s, x) +} + +interface GTMLoaderProps { + children: React.ReactNode +} + +export default function GTMLoader({ children }: GTMLoaderProps) { + const gtmId = cfg.gtmId + React.useEffect(() => { + if (!gtmId) return + loadScript(`https://www.googletagmanager.com/gtag/js?id=${gtmId}`) + addScript(` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '${gtmId}'); + `) + }, [gtmId]) + return <>{children} +} From 655401d9b413b99340cd17c56e4ab40da620e196 Mon Sep 17 00:00:00 2001 From: Maxim Chervonny Date: Thu, 15 Jun 2023 16:08:03 +0300 Subject: [PATCH 3/6] move adding script to on general function --- catalog/app/utils/gtm.tsx | 46 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/catalog/app/utils/gtm.tsx b/catalog/app/utils/gtm.tsx index 531e7e03170..83f9211eb74 100644 --- a/catalog/app/utils/gtm.tsx +++ b/catalog/app/utils/gtm.tsx @@ -2,20 +2,27 @@ import * as React from 'react' import cfg from 'constants/config' -function loadScript(src: string) { - const s = window.document.createElement('script') - s.type = 'text/javascript' - s.async = true - s.src = src - - const x = window.document.getElementsByTagName('script')[0] - x.parentNode?.insertBefore(s, x) +interface SupportedAttrs { + async?: boolean + innerText: string + src: string } -function addScript(content: string) { +function addScript(props: Pick): void +function addScript(props: Pick): void +function addScript({ async, innerText, src }: Partial) { const s = window.document.createElement('script') s.type = 'text/javascript' - s.innerText = content + + if (innerText) { + s.innerText = innerText + } + if (async) { + s.async = async + } + if (src) { + s.src = src + } const x = window.document.getElementsByTagName('script')[0] x.parentNode?.insertBefore(s, x) } @@ -28,13 +35,18 @@ export default function GTMLoader({ children }: GTMLoaderProps) { const gtmId = cfg.gtmId React.useEffect(() => { if (!gtmId) return - loadScript(`https://www.googletagmanager.com/gtag/js?id=${gtmId}`) - addScript(` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', '${gtmId}'); - `) + addScript({ + async: true, + src: `https://www.googletagmanager.com/gtag/js?id=${gtmId}`, + }) + addScript({ + innerText: ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '${gtmId}'); + `.replace(/\n/g, ' '), + }) }, [gtmId]) return <>{children} } From 068777fb2c043969a8f1b26d433e3a76ac6b89bf Mon Sep 17 00:00:00 2001 From: Maxim Chervonny Date: Thu, 15 Jun 2023 16:14:28 +0300 Subject: [PATCH 4/6] throw when no scripts --- catalog/app/utils/gtm.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/catalog/app/utils/gtm.tsx b/catalog/app/utils/gtm.tsx index 83f9211eb74..f8f0e9b1f03 100644 --- a/catalog/app/utils/gtm.tsx +++ b/catalog/app/utils/gtm.tsx @@ -1,3 +1,4 @@ +import invariant from 'invariant' import * as React from 'react' import cfg from 'constants/config' @@ -24,7 +25,8 @@ function addScript({ async, innerText, src }: Partial) { s.src = src } const x = window.document.getElementsByTagName('script')[0] - x.parentNode?.insertBefore(s, x) + invariant(x?.parentNode, 'No SCRIPT or HEAD element was found, is it the DOM environment?') + x.parentNode.insertBefore(s, x) } interface GTMLoaderProps { From d93136be0f41214b3380ec806123dbac55882c02 Mon Sep 17 00:00:00 2001 From: Maxim Chervonny Date: Thu, 15 Jun 2023 16:19:48 +0300 Subject: [PATCH 5/6] make gtmId a property --- catalog/app/app.tsx | 2 +- catalog/app/utils/gtm.tsx | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/catalog/app/app.tsx b/catalog/app/app.tsx index 4ea88aa2d50..e825a44b1f6 100644 --- a/catalog/app/app.tsx +++ b/catalog/app/app.tsx @@ -98,7 +98,7 @@ const render = () => { Notifications.Provider, [APIConnector.Provider, { fetch, middleware: [Auth.apiMiddleware] }], [Auth.Provider, { checkOn: LOCATION_CHANGE, storage }], - GTMLoader, + [GTMLoader, { gtmId: cfg.gtmId }], [ Intercom.Provider, { diff --git a/catalog/app/utils/gtm.tsx b/catalog/app/utils/gtm.tsx index f8f0e9b1f03..363a42fb1a1 100644 --- a/catalog/app/utils/gtm.tsx +++ b/catalog/app/utils/gtm.tsx @@ -1,8 +1,6 @@ import invariant from 'invariant' import * as React from 'react' -import cfg from 'constants/config' - interface SupportedAttrs { async?: boolean innerText: string @@ -25,16 +23,19 @@ function addScript({ async, innerText, src }: Partial) { s.src = src } const x = window.document.getElementsByTagName('script')[0] - invariant(x?.parentNode, 'No SCRIPT or HEAD element was found, is it the DOM environment?') + invariant( + x?.parentNode, + 'No SCRIPT or HEAD element was found, is it the DOM environment?', + ) x.parentNode.insertBefore(s, x) } interface GTMLoaderProps { children: React.ReactNode + gtmId: string } -export default function GTMLoader({ children }: GTMLoaderProps) { - const gtmId = cfg.gtmId +export default function GTMLoader({ children, gtmId }: GTMLoaderProps) { React.useEffect(() => { if (!gtmId) return addScript({ From 4d3dc6d5c311c58f840448ef066e06ae220790bc Mon Sep 17 00:00:00 2001 From: Maxim Chervonny Date: Thu, 15 Jun 2023 16:53:12 +0300 Subject: [PATCH 6/6] remove script in cleanup function --- catalog/app/utils/gtm.tsx | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/catalog/app/utils/gtm.tsx b/catalog/app/utils/gtm.tsx index 363a42fb1a1..95ff9a9fac4 100644 --- a/catalog/app/utils/gtm.tsx +++ b/catalog/app/utils/gtm.tsx @@ -7,8 +7,8 @@ interface SupportedAttrs { src: string } -function addScript(props: Pick): void -function addScript(props: Pick): void +function addScript(props: Pick): HTMLScriptElement +function addScript(props: Pick): HTMLScriptElement function addScript({ async, innerText, src }: Partial) { const s = window.document.createElement('script') s.type = 'text/javascript' @@ -28,6 +28,15 @@ function addScript({ async, innerText, src }: Partial) { 'No SCRIPT or HEAD element was found, is it the DOM environment?', ) x.parentNode.insertBefore(s, x) + return s +} + +function removeScript(script: HTMLScriptElement) { + invariant( + script.parentNode, + 'No SCRIPT or HEAD element was found, is it the DOM environment?', + ) + script.parentNode.removeChild(script) } interface GTMLoaderProps { @@ -38,11 +47,11 @@ interface GTMLoaderProps { export default function GTMLoader({ children, gtmId }: GTMLoaderProps) { React.useEffect(() => { if (!gtmId) return - addScript({ + const gtmMain = addScript({ async: true, src: `https://www.googletagmanager.com/gtag/js?id=${gtmId}`, }) - addScript({ + const gtmInit = addScript({ innerText: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} @@ -50,6 +59,10 @@ export default function GTMLoader({ children, gtmId }: GTMLoaderProps) { gtag('config', '${gtmId}'); `.replace(/\n/g, ' '), }) + return () => { + removeScript(gtmMain) + removeScript(gtmInit) + } }, [gtmId]) return <>{children} }