From 775d64bb94374d8c684764b932df1e2826ed4982 Mon Sep 17 00:00:00 2001 From: Marcel Gerber Date: Mon, 13 Jan 2025 16:57:01 +0100 Subject: [PATCH] enhance(analytics): analytics tracking for narrative charts --- .../grapher/src/core/Grapher.tsx | 24 ++++++++-- .../grapher/src/core/GrapherAnalytics.ts | 46 ++++++++++++++----- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/packages/@ourworldindata/grapher/src/core/Grapher.tsx b/packages/@ourworldindata/grapher/src/core/Grapher.tsx index 7a0e65d538a..307698ab32a 100644 --- a/packages/@ourworldindata/grapher/src/core/Grapher.tsx +++ b/packages/@ourworldindata/grapher/src/core/Grapher.tsx @@ -514,7 +514,7 @@ export class Grapher chartViewInfo?: Pick< ChartViewInfo, - "parentChartSlug" | "queryParamsForParentChart" + "name" | "parentChartSlug" | "queryParamsForParentChart" > = undefined selection = @@ -3008,7 +3008,10 @@ export class Grapher ref={this.base} className={containerClasses} style={containerStyle} - data-grapher-url={this.canonicalUrl} + data-grapher-url={JSON.stringify({ + grapherUrl: this.canonicalUrl, + chartViewName: this.chartViewInfo?.name, + })} > {this.commandPalette} {this.uncaughtError ? this.renderError() : this.renderReady()} @@ -3102,9 +3105,22 @@ export class Grapher if (entry.isIntersecting) { this.hasBeenVisible = true - if (this.slug && !this.hasLoggedGAViewEvent) { - this.analytics.logGrapherView(this.slug) + if (!this.hasLoggedGAViewEvent) { this.hasLoggedGAViewEvent = true + + if (this.chartViewInfo) { + this.analytics.logGrapherView( + this.chartViewInfo.parentChartSlug, + { + chartViewName: + this.chartViewInfo.name, + } + ) + this.hasLoggedGAViewEvent = true + } else if (this.slug) { + this.analytics.logGrapherView(this.slug) + this.hasLoggedGAViewEvent = true + } } } diff --git a/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts b/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts index 2f81bb6ead7..813c76ffeb3 100644 --- a/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts +++ b/packages/@ourworldindata/grapher/src/core/GrapherAnalytics.ts @@ -51,6 +51,7 @@ interface GAEvent { eventTarget?: string grapherPath?: string grapherView?: string // specifies a view in a multi-dim data page + chartViewName?: string // specifies the name of a chart view / narrative chart explorerPath?: string explorerView?: string } @@ -76,11 +77,15 @@ export class GrapherAnalytics { private version: string // Ideally the Git hash commit private isDev: boolean - logGrapherView(slug: string, view?: Record): void { + logGrapherView( + slug: string, + ctx?: { view?: Record; chartViewName?: string } + ): void { this.logToGA({ event: EventCategory.GrapherView, grapherPath: `/grapher/${slug}`, - grapherView: view ? JSON.stringify(view) : undefined, + grapherView: ctx?.view ? JSON.stringify(ctx.view) : undefined, + chartViewName: ctx?.chartViewName, }) } @@ -126,19 +131,23 @@ export class GrapherAnalytics { logGrapherClick( action: string = "unknown-action", - label?: string, - grapherUrl?: string + ctx: { + label?: string + grapherUrl?: string + chartViewName?: string + } ): void { // GA4 trims metadata fields down to 100 characters, so we want to be concise and only send // the pathname, e.g. `/grapher/life-expectancy` or `/explorers/migration` const grapherUrlObj = - grapherUrl !== undefined ? new URL(grapherUrl) : undefined + ctx.grapherUrl !== undefined ? new URL(ctx.grapherUrl) : undefined this.logToGA({ event: EventCategory.GrapherClick, eventAction: action, - eventTarget: label, + eventTarget: ctx.label, grapherPath: grapherUrlObj?.pathname, + chartViewName: ctx.chartViewName, }) } @@ -184,17 +193,32 @@ export class GrapherAnalytics { ) if (!trackedElement) return - const grapherUrl = trackedElement + const grapherUrlRaw = trackedElement .closest(`[${dataGrapherUrlAttr}]`) ?.getAttribute(dataGrapherUrlAttr) - if (grapherUrl) + if (grapherUrlRaw) { + let grapherUrlObj: + | { + grapherUrl: string + chartViewName: string + } + | undefined + try { + grapherUrlObj = JSON.parse(grapherUrlRaw) + } catch (e) { + console.warn("failed to parse grapherUrl", e) + } + this.logGrapherClick( trackedElement.getAttribute(dataTrackAttr) || undefined, - trackedElement.innerText, - grapherUrl + { + label: trackedElement.innerText, + grapherUrl: grapherUrlObj?.grapherUrl, + chartViewName: grapherUrlObj?.chartViewName, + } ) - else + } else this.logSiteClick( trackedElement.getAttribute(dataTrackAttr) || undefined, trackedElement.innerText