From df5dcaecf6c6b5d5a10a55c59afe7af638ccf6e4 Mon Sep 17 00:00:00 2001 From: sophiamersmann Date: Tue, 29 Oct 2024 17:41:05 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20log=20grapher=20views=20to=20google?= =?UTF-8?q?=20analytics?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- explorer/Explorer.tsx | 2 +- .../grapher/src/controls/ActionButtons.tsx | 4 +- .../grapher/src/controls/ShareMenu.scss | 5 - .../grapher/src/controls/ShareMenu.tsx | 129 +++++++++--------- .../grapher/src/core/Grapher.tsx | 10 +- .../grapher/src/core/GrapherAnalytics.ts | 10 ++ site/multiDim/MultiDimDataPageContent.tsx | 14 +- 7 files changed, 102 insertions(+), 72 deletions(-) diff --git a/explorer/Explorer.tsx b/explorer/Explorer.tsx index 73cccbedd14..d9106ebad47 100644 --- a/explorer/Explorer.tsx +++ b/explorer/Explorer.tsx @@ -471,7 +471,6 @@ export class Explorer if (mapTargetTime) { grapher.map.time = mapTargetTime } - grapher.slug = this.explorerProgram.slug if (!grapher.id) grapher.id = 0 } @@ -546,6 +545,7 @@ export class Explorer grapher.reset() this.updateGrapherFromExplorerCommon() grapher.updateFromObject(config) + grapher.slug = undefined grapher.downloadData() } diff --git a/packages/@ourworldindata/grapher/src/controls/ActionButtons.tsx b/packages/@ourworldindata/grapher/src/controls/ActionButtons.tsx index 324bbcea3d6..4b81087e96c 100644 --- a/packages/@ourworldindata/grapher/src/controls/ActionButtons.tsx +++ b/packages/@ourworldindata/grapher/src/controls/ActionButtons.tsx @@ -203,7 +203,9 @@ export class ActionButtons extends React.Component<{ } @computed private get hasShareButton(): boolean { - return !this.manager.hideShareButton + return ( + !this.manager.hideShareButton && ShareMenu.shouldShow(this.manager) + ) } @computed private get hasFullScreenButton(): boolean { diff --git a/packages/@ourworldindata/grapher/src/controls/ShareMenu.scss b/packages/@ourworldindata/grapher/src/controls/ShareMenu.scss index 3685935880e..8a4b67ee999 100644 --- a/packages/@ourworldindata/grapher/src/controls/ShareMenu.scss +++ b/packages/@ourworldindata/grapher/src/controls/ShareMenu.scss @@ -56,8 +56,3 @@ $zindex-ControlsFooter: 2; } } } - -.ShareMenu.disabled a { - pointer-events: none; - opacity: 0.3; -} diff --git a/packages/@ourworldindata/grapher/src/controls/ShareMenu.tsx b/packages/@ourworldindata/grapher/src/controls/ShareMenu.tsx index b0cc389fc1b..46cf518d4a2 100644 --- a/packages/@ourworldindata/grapher/src/controls/ShareMenu.tsx +++ b/packages/@ourworldindata/grapher/src/controls/ShareMenu.tsx @@ -21,7 +21,7 @@ export interface ShareMenuManager { interface ShareMenuProps { manager: ShareMenuManager - onDismiss: () => void + onDismiss?: () => void right?: number } @@ -91,6 +91,11 @@ export class ShareMenu extends React.Component { } } + static shouldShow(manager: ShareMenuManager): boolean { + const test = new ShareMenu({ manager }) + return test.showShareMenu + } + @computed get manager(): ShareMenuManager { return this.props.manager } @@ -99,8 +104,8 @@ export class ShareMenu extends React.Component { return this.manager.currentTitle ?? "" } - @computed get isDisabled(): boolean { - return !this.manager.slug + @computed get showShareMenu(): boolean { + return !!this.canonicalUrl || !!this.manager.editUrl } @computed get canonicalUrl(): string | undefined { @@ -108,7 +113,7 @@ export class ShareMenu extends React.Component { } @action.bound dismiss(): void { - this.props.onDismiss() + this.props.onDismiss?.() } @action.bound onClickSomewhere(): void { @@ -153,21 +158,23 @@ export class ShareMenu extends React.Component { } } - @computed get twitterHref(): string { - let href = - "https://twitter.com/intent/tweet/?text=" + - encodeURIComponent(this.title) - if (this.canonicalUrl) - href += "&url=" + encodeURIComponent(this.canonicalUrl) - return href + @computed get twitterHref(): string | undefined { + if (!this.canonicalUrl) return undefined + const queryParams = new URLSearchParams({ + text: this.title, + url: this.canonicalUrl, + }) + return `https://twitter.com/intent/tweet/?${queryParams}` } - @computed get facebookHref(): string { - let href = - "https://www.facebook.com/dialog/share?app_id=1149943818390250&display=page" - if (this.canonicalUrl) - href += "&href=" + encodeURIComponent(this.canonicalUrl) - return href + @computed get facebookHref(): string | undefined { + if (!this.canonicalUrl) return undefined + const queryParams = new URLSearchParams({ + app_id: "1149943818390250", + display: "page", + href: this.canonicalUrl, + }) + return `https://www.facebook.com/dialog/share?${queryParams}` } @computed get canUseShareApi(): boolean { @@ -175,13 +182,7 @@ export class ShareMenu extends React.Component { } render(): React.ReactElement { - const { - twitterHref, - facebookHref, - isDisabled, - canUseShareApi, - manager, - } = this + const { twitterHref, facebookHref, canUseShareApi, manager } = this const { editUrl } = manager const width = 200 @@ -193,46 +194,52 @@ export class ShareMenu extends React.Component { return (
(this.dismissable = false))} style={style} >

Share

- - - - {" "} - X/Twitter - - - - - {" "} - Facebook - - - - - {" "} - Embed - + {twitterHref && ( + + + + {" "} + X/Twitter + + )} + {facebookHref && ( + + + + {" "} + Facebook + + )} + {this.canonicalUrl && ( + + + + {" "} + Embed + + )} {canUseShareApi && ( { Share via… )} - {this.state.canWriteToClipboard && ( + {this.state.canWriteToClipboard && this.canonicalUrl && ( { if (entry.isIntersecting) { this.hasBeenVisible = true + + if (this.slug && !this.hasLoggedGAViewEvent) { + this.analytics.logGrapherView(this.slug) + this.hasLoggedGAViewEvent = true + } + observer.disconnect() } }) diff --git a/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts b/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts index 7daa60a1671..bbaf5234f41 100644 --- a/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts +++ b/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts @@ -12,6 +12,7 @@ export enum EventCategory { CountryProfileSearch = "owid.country_profile_search", Filter = "owid.filter", GlobalEntitySelectorUsage = "owid.global_entity_selector_usage", + GrapherView = "owid.grapher_view", GrapherClick = "owid.grapher_click", GrapherError = "owid.grapher_error", ExplorerCountrySelector = "owid.explorer_country_selector", @@ -48,6 +49,7 @@ interface GAEvent { eventContext?: string eventTarget?: string grapherPath?: string + grapherView?: string // specifies a view in a multi-dim data page } // taken from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/de66435d18fbdb2684947d16b5cd3a77f876324c/types/gtag.js/index.d.ts#L151-L156 @@ -71,6 +73,14 @@ export class GrapherAnalytics { private version: string // Ideally the Git hash commit private isDev: boolean + logGrapherView(slug: string, view?: Record): void { + this.logToGA({ + event: EventCategory.GrapherView, + grapherPath: `/grapher/${slug}`, + grapherView: view ? JSON.stringify(view) : undefined, + }) + } + logGrapherViewError(error: Error): void { this.logToGA({ event: EventCategory.GrapherError, diff --git a/site/multiDim/MultiDimDataPageContent.tsx b/site/multiDim/MultiDimDataPageContent.tsx index 86a1c3a4a51..22dbf776369 100644 --- a/site/multiDim/MultiDimDataPageContent.tsx +++ b/site/multiDim/MultiDimDataPageContent.tsx @@ -1,6 +1,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" import { Grapher, + GrapherAnalytics, GrapherProgrammaticInterface, getVariableMetadataRoute, } from "@ourworldindata/grapher" @@ -201,6 +202,8 @@ const useVarDatapageData = ( } } +const analytics = new GrapherAnalytics() + export const MultiDimDataPageContent = ({ // _datapageData, configObj, @@ -215,6 +218,8 @@ export const MultiDimDataPageContent = ({ }: MultiDimDataPageProps) => { const grapherFigureRef = useRef(null) + const slug = window?.location.pathname.split("/").pop() + const config = useMemo( () => MultiDimDataPageConfig.fromObject(configObj), [configObj] @@ -270,6 +275,10 @@ export const MultiDimDataPageContent = ({ setWindowQueryStr(queryStr ?? "") }, [queryStr]) + useEffect(() => { + if (slug) analytics.logGrapherView(slug, currentSettings) + }, [slug, currentSettings]) + const grapherConfigComputed = useMemo(() => { const baseConfig: GrapherProgrammaticInterface = { selectedEntityNames: config.config.defaultSelection ?? [], @@ -284,12 +293,11 @@ export const MultiDimDataPageContent = ({ dimensions: dimensionsConfig, ...baseConfig, dataApiUrl: DATA_API_URL, - // TODO: The way manager and slug are set here are just workarounds to make the edit button in the - // share menu work. They should be removed before we publish MDims! + // TODO: The way manager is set here is just a workaround to make the edit button in the + // share menu work. This should be removed before we publish MDims! manager: { canonicalUrl, }, - slug: "DUMMY", } as GrapherProgrammaticInterface }, [ varGrapherConfig,