- {title &&
{title}
}
+ {title && (
+
+ {title}{" "}
+
+ {titleAnnotation}
+
+
+ )}
{subtitle && (
{timeNotice && TOOLTIP_ICON.notice}
diff --git a/packages/@ourworldindata/grapher/src/tooltip/TooltipContents.tsx b/packages/@ourworldindata/grapher/src/tooltip/TooltipContents.tsx
index 3078fd31d53..a3a8b7f0f1d 100644
--- a/packages/@ourworldindata/grapher/src/tooltip/TooltipContents.tsx
+++ b/packages/@ourworldindata/grapher/src/tooltip/TooltipContents.tsx
@@ -337,8 +337,12 @@ export function IconCircledS({
)
}
-export function makeTooltipToleranceNotice(targetYear: string): string {
- return `Data not available for ${targetYear}. Showing closest available data point instead`
+export function makeTooltipToleranceNotice(
+ targetYear: string,
+ { plural }: { plural: boolean } = { plural: false }
+): string {
+ const dataPoint = plural ? "data points" : "data point"
+ return `Data not available for ${targetYear}. Showing closest available ${dataPoint} instead`
}
export function makeTooltipRoundingNotice(
diff --git a/packages/@ourworldindata/grapher/src/tooltip/TooltipProps.ts b/packages/@ourworldindata/grapher/src/tooltip/TooltipProps.ts
index e3222a8949b..9fae113da6b 100644
--- a/packages/@ourworldindata/grapher/src/tooltip/TooltipProps.ts
+++ b/packages/@ourworldindata/grapher/src/tooltip/TooltipProps.ts
@@ -28,6 +28,7 @@ export interface TooltipProps {
offsetXDirection?: "left" | "right"
offsetYDirection?: "upward" | "downward"
title?: string | number // header text
+ titleAnnotation?: string // rendered next to the title, but muted
subtitle?: string | number // header deck
subtitleFormat?: "notice" | "unit" // optional postprocessing for subtitle
footer?: { icon: TooltipFooterIcon; text: string }[]
diff --git a/packages/@ourworldindata/types/src/domainTypes/CoreTableTypes.ts b/packages/@ourworldindata/types/src/domainTypes/CoreTableTypes.ts
index 5fa5ba92b00..a44d6d42459 100644
--- a/packages/@ourworldindata/types/src/domainTypes/CoreTableTypes.ts
+++ b/packages/@ourworldindata/types/src/domainTypes/CoreTableTypes.ts
@@ -300,5 +300,6 @@ export interface OwidVariableRow {
entityName: EntityName
time: Time
value: ValueType
+ originalTime: Time
originalValue?: ValueType
}
diff --git a/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts b/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts
index 1d73e6a6470..cb146f77cc3 100644
--- a/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts
+++ b/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts
@@ -580,6 +580,7 @@ export interface GrapherInterface extends SortConfig {
includedEntities?: number[]
selectedEntityNames?: EntityName[]
selectedEntityColors?: { [entityName: string]: string | undefined }
+ focusedSeriesNames?: SeriesName[]
missingDataStrategy?: MissingDataStrategy
hideFacetControl?: boolean
facettingLabelByYVariables?: string
@@ -698,6 +699,7 @@ export const grapherKeysToSerialize = [
"dimensions",
"selectedEntityNames",
"selectedEntityColors",
+ "focusedSeriesNames",
"sortBy",
"sortOrder",
"sortColumnSlug",
diff --git a/packages/@ourworldindata/utils/src/grapherConfigUtils.ts b/packages/@ourworldindata/utils/src/grapherConfigUtils.ts
index 2a8db8e0120..7649db3d09f 100644
--- a/packages/@ourworldindata/utils/src/grapherConfigUtils.ts
+++ b/packages/@ourworldindata/utils/src/grapherConfigUtils.ts
@@ -22,6 +22,27 @@ const KEYS_EXCLUDED_FROM_INHERITANCE = [
"isPublished",
]
+/**
+ * Simple merge function that doesn't do any Grapher-specific checks.
+ *
+ * You usually want to use `mergeGrapherConfigs` instead that implements the
+ * inheritance model correctly. Only use this if you know what you're doing.
+ */
+export function simpleMerge(
+ ...grapherConfigs: GrapherInterface[]
+): GrapherInterface {
+ return mergeWith(
+ {}, // mergeWith mutates the first argument
+ ...grapherConfigs,
+ (_: unknown, childValue: unknown): any => {
+ // don't concat arrays, just use the last one
+ if (Array.isArray(childValue)) {
+ return childValue
+ }
+ }
+ )
+}
+
export function mergeGrapherConfigs(
...grapherConfigs: GrapherInterface[]
): GrapherInterface {
@@ -60,16 +81,7 @@ export function mergeGrapherConfigs(
return omit(config, KEYS_EXCLUDED_FROM_INHERITANCE)
})
- return mergeWith(
- {}, // mergeWith mutates the first argument
- ...cleanedConfigs,
- (_: unknown, childValue: unknown): any => {
- // don't concat arrays, just use the last one
- if (Array.isArray(childValue)) {
- return childValue
- }
- }
- )
+ return simpleMerge(...cleanedConfigs)
}
export function diffGrapherConfigs(
diff --git a/packages/@ourworldindata/utils/src/index.ts b/packages/@ourworldindata/utils/src/index.ts
index 75f99afdd16..1e74033b5d5 100644
--- a/packages/@ourworldindata/utils/src/index.ts
+++ b/packages/@ourworldindata/utils/src/index.ts
@@ -335,6 +335,7 @@ export {
export { isAndroid, isIOS } from "./BrowserUtils.js"
export {
+ simpleMerge,
diffGrapherConfigs,
mergeGrapherConfigs,
} from "./grapherConfigUtils.js"
diff --git a/site/multiembedder/MultiEmbedder.tsx b/site/multiembedder/MultiEmbedder.tsx
index b7b84b59516..3089954f249 100644
--- a/site/multiembedder/MultiEmbedder.tsx
+++ b/site/multiembedder/MultiEmbedder.tsx
@@ -19,6 +19,7 @@ import {
merge,
MultiDimDataPageConfig,
extractMultiDimChoicesFromQueryStr,
+ deserializeJSONFromHTML,
} from "@ourworldindata/utils"
import { action } from "mobx"
import React from "react"
@@ -161,8 +162,10 @@ class MultiEmbedder {
dataApiUrl: DATA_API_URL,
}
+ const html = await fetchText(fullUrl)
+
if (isExplorer) {
- const html = await fetchText(fullUrl)
+ // const html = await fetchText(fullUrl)
const props: ExplorerProps = await buildExplorerProps(
html,
queryStr,
@@ -173,35 +176,36 @@ class MultiEmbedder {
ReactDOM.render(, figure)
} else {
figure.classList.remove(GRAPHER_PREVIEW_CLASS)
- const url = new URL(fullUrl)
- const slug = url.pathname.split("/").pop()
- let configUrl
- if (isMultiDim) {
- const mdimConfigUrl = `${MULTI_DIM_DYNAMIC_CONFIG_URL}/${slug}.json`
- const mdimJsonConfig = await fetch(mdimConfigUrl).then((res) =>
- res.json()
- )
- const mdimConfig =
- MultiDimDataPageConfig.fromObject(mdimJsonConfig)
- const dimensions = extractMultiDimChoicesFromQueryStr(
- url.search,
- mdimConfig
- )
- const view = mdimConfig.findViewByDimensions(dimensions)
- if (!view) {
- throw new Error(
- `No view found for dimensions ${JSON.stringify(
- dimensions
- )}`
- )
- }
- configUrl = `${GRAPHER_DYNAMIC_CONFIG_URL}/by-uuid/${view.fullConfigId}.config.json`
- } else {
- configUrl = `${GRAPHER_DYNAMIC_CONFIG_URL}/${slug}.config.json`
- }
- const grapherPageConfig = await fetch(configUrl).then((res) =>
- res.json()
- )
+ const grapherPageConfig = deserializeJSONFromHTML(html)
+ // const url = new URL(fullUrl)
+ // const slug = url.pathname.split("/").pop()
+ // let configUrl
+ // if (isMultiDim) {
+ // const mdimConfigUrl = `${MULTI_DIM_DYNAMIC_CONFIG_URL}/${slug}.json`
+ // const mdimJsonConfig = await fetch(mdimConfigUrl).then((res) =>
+ // res.json()
+ // )
+ // const mdimConfig =
+ // MultiDimDataPageConfig.fromObject(mdimJsonConfig)
+ // const dimensions = extractMultiDimChoicesFromQueryStr(
+ // url.search,
+ // mdimConfig
+ // )
+ // const view = mdimConfig.findViewByDimensions(dimensions)
+ // if (!view) {
+ // throw new Error(
+ // `No view found for dimensions ${JSON.stringify(
+ // dimensions
+ // )}`
+ // )
+ // }
+ // configUrl = `${GRAPHER_DYNAMIC_CONFIG_URL}/by-uuid/${view.fullConfigId}.config.json`
+ // } else {
+ // configUrl = `${GRAPHER_DYNAMIC_CONFIG_URL}/${slug}.config.json`
+ // }
+ // const grapherPageConfig = await fetch(configUrl).then((res) =>
+ // res.json()
+ // )
const figureConfigAttr = figure.getAttribute(
GRAPHER_EMBEDDED_FIGURE_CONFIG_ATTR