Skip to content

Commit

Permalink
✨ log grapher views to google analytics
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Oct 29, 2024
1 parent e011027 commit 0a1cf71
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 71 deletions.
1 change: 0 additions & 1 deletion explorer/Explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,6 @@ export class Explorer
if (mapTargetTime) {
grapher.map.time = mapTargetTime
}
grapher.slug = this.explorerProgram.slug
if (!grapher.id) grapher.id = 0
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 0 additions & 5 deletions packages/@ourworldindata/grapher/src/controls/ShareMenu.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,3 @@ $zindex-ControlsFooter: 2;
}
}
}

.ShareMenu.disabled a {
pointer-events: none;
opacity: 0.3;
}
129 changes: 68 additions & 61 deletions packages/@ourworldindata/grapher/src/controls/ShareMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface ShareMenuManager {

interface ShareMenuProps {
manager: ShareMenuManager
onDismiss: () => void
onDismiss?: () => void
right?: number
}

Expand Down Expand Up @@ -91,6 +91,11 @@ export class ShareMenu extends React.Component<ShareMenuProps, ShareMenuState> {
}
}

static shouldShow(manager: ShareMenuManager): boolean {
const test = new ShareMenu({ manager })
return test.showShareMenu
}

@computed get manager(): ShareMenuManager {
return this.props.manager
}
Expand All @@ -99,16 +104,16 @@ export class ShareMenu extends React.Component<ShareMenuProps, ShareMenuState> {
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 {
return this.manager.canonicalUrl
}

@action.bound dismiss(): void {
this.props.onDismiss()
this.props.onDismiss?.()
}

@action.bound onClickSomewhere(): void {
Expand Down Expand Up @@ -153,35 +158,31 @@ export class ShareMenu extends React.Component<ShareMenuProps, ShareMenuState> {
}
}

@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 {
return canUseShareApi(this.manager)
}

render(): React.ReactElement {
const {
twitterHref,
facebookHref,
isDisabled,
canUseShareApi,
manager,
} = this
const { twitterHref, facebookHref, canUseShareApi, manager } = this
const { editUrl } = manager

const width = 200
Expand All @@ -193,46 +194,52 @@ export class ShareMenu extends React.Component<ShareMenuProps, ShareMenuState> {

return (
<div
className={"ShareMenu" + (isDisabled ? " disabled" : "")}
className="ShareMenu"
onClick={action(() => (this.dismissable = false))}
style={style}
>
<h2>Share</h2>
<a
target="_blank"
title="Tweet a link"
data-track-note="chart_share_twitter"
href={twitterHref}
rel="noopener"
>
<span className="icon">
<FontAwesomeIcon icon={faXTwitter} />
</span>{" "}
X/Twitter
</a>
<a
target="_blank"
title="Share on Facebook"
data-track-note="chart_share_facebook"
href={facebookHref}
rel="noopener"
>
<span className="icon">
<FontAwesomeIcon icon={faFacebook} />
</span>{" "}
Facebook
</a>
<a
className="embed"
title="Embed this visualization in another HTML document"
data-track-note="chart_share_embed"
onClick={this.onEmbed}
>
<span className="icon">
<FontAwesomeIcon icon={faCode} />
</span>{" "}
Embed
</a>
{twitterHref && (
<a
target="_blank"
title="Tweet a link"
data-track-note="chart_share_twitter"
href={twitterHref}
rel="noopener"
>
<span className="icon">
<FontAwesomeIcon icon={faXTwitter} />
</span>{" "}
X/Twitter
</a>
)}
{facebookHref && (
<a
target="_blank"
title="Share on Facebook"
data-track-note="chart_share_facebook"
href={facebookHref}
rel="noopener"
>
<span className="icon">
<FontAwesomeIcon icon={faFacebook} />
</span>{" "}
Facebook
</a>
)}
{this.canonicalUrl && (
<a
className="embed"
title="Embed this visualization in another HTML document"
data-track-note="chart_share_embed"
onClick={this.onEmbed}
>
<span className="icon">
<FontAwesomeIcon icon={faCode} />
</span>{" "}
Embed
</a>
)}
{canUseShareApi && (
<a
title="Share this visualization with an app on your device"
Expand All @@ -245,7 +252,7 @@ export class ShareMenu extends React.Component<ShareMenuProps, ShareMenuState> {
Share via&hellip;
</a>
)}
{this.state.canWriteToClipboard && (
{this.state.canWriteToClipboard && this.canonicalUrl && (
<a
title="Copy link to clipboard"
data-track-note="chart_share_copylink"
Expand Down
10 changes: 9 additions & 1 deletion packages/@ourworldindata/grapher/src/core/Grapher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,8 @@ export class Grapher
devtools: {
track: "Grapher",
properties: [
["slug", this.slug],
// might be missing for charts within explorers or mdims
["slug", this.slug ?? "missing-slug"],
["chartType", this.type],
["tab", this.tab],
],
Expand Down Expand Up @@ -2342,6 +2343,7 @@ export class Grapher
return this.base.current || undefined
}

private hasLoggedGAViewEvent = false
@observable private hasBeenVisible = false
@observable private uncaughtError?: Error

Expand Down Expand Up @@ -2829,6 +2831,12 @@ export class Grapher
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.hasBeenVisible = true

if (this.slug && !this.hasLoggedGAViewEvent) {
this.hasLoggedGAViewEvent = true
this.analytics.logGrapherView(this.slug)
}

observer.disconnect()
}
})
Expand Down
13 changes: 13 additions & 0 deletions packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -48,6 +49,7 @@ interface GAEvent {
eventContext?: string
eventTarget?: string
grapherPath?: string
grapherView?: string
}

// taken from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/de66435d18fbdb2684947d16b5cd3a77f876324c/types/gtag.js/index.d.ts#L151-L156
Expand All @@ -71,6 +73,14 @@ export class GrapherAnalytics {
private version: string // Ideally the Git hash commit
private isDev: boolean

logGrapherView(slug: string, view?: string): void {
this.logToGA({
event: EventCategory.GrapherView,
grapherPath: `/grapher/${slug}`,
grapherView: view,
})
}

logGrapherViewError(error: Error): void {
this.logToGA({
event: EventCategory.GrapherError,
Expand Down Expand Up @@ -184,6 +194,9 @@ export class GrapherAnalytics {
}

protected logToGA(event: GAEvent): void {
// TODO: remove!! added for testing on staging
console.log("Analytics.logToGA", event)

Check warning on line 198 in packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts

View workflow job for this annotation

GitHub Actions / eslint

Unexpected console statement

Check warning on line 198 in packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts

View workflow job for this annotation

GitHub Actions / eslint

Unexpected console statement

if (DEBUG && this.isDev) {
// eslint-disable-next-line no-console
console.log("Analytics.logToGA", event)
Expand Down
15 changes: 13 additions & 2 deletions site/multiDim/MultiDimDataPageContent.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import {
Grapher,
GrapherAnalytics,
GrapherProgrammaticInterface,
getVariableMetadataRoute,
} from "@ourworldindata/grapher"
Expand Down Expand Up @@ -201,6 +202,8 @@ const useVarDatapageData = (
}
}

const analytics = new GrapherAnalytics()

export const MultiDimDataPageContent = ({
// _datapageData,
configObj,
Expand All @@ -215,6 +218,8 @@ export const MultiDimDataPageContent = ({
}: MultiDimDataPageProps) => {
const grapherFigureRef = useRef<HTMLDivElement>(null)

const slug = window?.location.pathname.split("/").pop()

const config = useMemo(
() => MultiDimDataPageConfig.fromObject(configObj),
[configObj]
Expand Down Expand Up @@ -270,6 +275,13 @@ export const MultiDimDataPageContent = ({
setWindowQueryStr(queryStr ?? "")
}, [queryStr])

useEffect(() => {
if (slug) {
const view = new URLSearchParams(currentSettings).toString()
analytics.logGrapherView(slug, view)
}
}, [slug, currentSettings])

const grapherConfigComputed = useMemo(() => {
const baseConfig: GrapherProgrammaticInterface = {
selectedEntityNames: config.config.defaultSelection ?? [],
Expand All @@ -284,12 +296,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
// TODO: The way manager is set here is just a workaround to make the edit button in the
// share menu work. They should be removed before we publish MDims!
manager: {
canonicalUrl,
},
slug: "DUMMY",
} as GrapherProgrammaticInterface
}, [
varGrapherConfig,
Expand Down

0 comments on commit 0a1cf71

Please sign in to comment.