diff --git a/baker/MultiDimBaker.tsx b/baker/MultiDimBaker.tsx index d628f49bc53..03b5c6fc0e8 100644 --- a/baker/MultiDimBaker.tsx +++ b/baker/MultiDimBaker.tsx @@ -23,6 +23,7 @@ import React from "react" import { BAKED_BASE_URL } from "../settings/clientSettings.js" import { getTagToSlugMap } from "./GrapherBakingUtils.js" import { + getGrapherConfigIdsForVariables, getVariableIdsByCatalogPath, getVariableMetadata, } from "../db/model/Variable.js" @@ -38,6 +39,20 @@ import { getMultiDimDataPageBySlug, } from "../db/model/MultiDimDataPage.js" +const getGrapherConfigIdsByVariableIds = async ( + knex: db.KnexReadonlyTransaction, + variableIds: number[] +): Promise> => { + const rows = await getGrapherConfigIdsForVariables(knex, variableIds) + + return Object.fromEntries( + rows.map((row) => [ + row.id, + row.grapherConfigIdAdmin ?? row.grapherConfigIdETL, + ]) + ) +} + const resolveMultiDimDataPageCatalogPathsToIndicatorIds = async ( knex: db.KnexReadonlyTransaction, rawConfig: MultiDimDataPageConfigRaw @@ -206,6 +221,13 @@ export const renderMultiDimDataPageFromConfig = async ( await resolveMultiDimDataPageCatalogPathsToIndicatorIds(knex, rawConfig) const config = MultiDimDataPageConfig.fromObject(preProcessedConfig) + // GET VARIABLE GRAPHER CONFIG UUIDS + const relevantVariableIds = getRelevantVariableIds(preProcessedConfig) + const variableIdToGrapherConfigMap = await getGrapherConfigIdsByVariableIds( + knex, + Array.from(relevantVariableIds) + ) + // FAQs const variableMetaDict = await getRelevantVariableMetadata(preProcessedConfig) @@ -224,6 +246,7 @@ export const renderMultiDimDataPageFromConfig = async ( const props = { configObj: config.config, tagToSlugMap: minimalTagToSlugMap, + variableIdToGrapherConfigMap, faqEntries, primaryTopic, } diff --git a/db/model/Variable.ts b/db/model/Variable.ts index 327759c5ca7..7c24e5f27b3 100644 --- a/db/model/Variable.ts +++ b/db/model/Variable.ts @@ -52,6 +52,25 @@ interface VariableWithGrapherConfigs { etl?: ChartConfigPair } +type VariableWithGrapherConfigIds = Pick< + DbRawVariable, + "id" | "grapherConfigIdAdmin" | "grapherConfigIdETL" +> +export async function getGrapherConfigIdsForVariables( + knex: db.KnexReadonlyTransaction, + variableIds: number[] +): Promise { + const rows: VariableWithGrapherConfigIds[] = await knex(VariablesTableName) + .select([ + "id", + "grapherConfigIdAdmin", + "grapherConfigIdETL", + ] as (keyof DbRawVariable)[]) + .whereIn("id", variableIds) + + return rows +} + export async function getGrapherConfigsForVariable( knex: db.KnexReadonlyTransaction, variableId: number diff --git a/packages/@ourworldindata/types/src/siteTypes/MultiDimDataPage.ts b/packages/@ourworldindata/types/src/siteTypes/MultiDimDataPage.ts index f3636baf233..5ddfb472c50 100644 --- a/packages/@ourworldindata/types/src/siteTypes/MultiDimDataPage.ts +++ b/packages/@ourworldindata/types/src/siteTypes/MultiDimDataPage.ts @@ -28,7 +28,7 @@ export interface Dimension { name: string group?: string description?: string - multi_select?: boolean + // multi_select?: boolean choices: Choice[] } @@ -83,6 +83,7 @@ export interface MultiDimDataPageProps { tagToSlugMap?: Record faqEntries?: FaqEntryKeyedByGdocIdAndFragmentId primaryTopic?: PrimaryTopic | undefined + variableIdToGrapherConfigMap?: Record initialQueryStr?: string canonicalUrl?: string diff --git a/site/multiDim/MultiDimDataPageContent.tsx b/site/multiDim/MultiDimDataPageContent.tsx index f2db3c04f20..4eaba9cfc1e 100644 --- a/site/multiDim/MultiDimDataPageContent.tsx +++ b/site/multiDim/MultiDimDataPageContent.tsx @@ -22,6 +22,7 @@ import { MultiDimDataPageConfig, extractMultiDimChoicesFromQueryStr, multiDimStateToQueryStr, + omit, } from "@ourworldindata/utils" import cx from "classnames" import { DebugProvider } from "../gdocs/DebugContext.js" @@ -101,6 +102,13 @@ const cachedGetVariableMetadata = memoize( ) ) +const cachedGetGrapherConfigByUuid = memoize( + (grapherConfigUuid: string): Promise => + fetch(`/grapher/by-uuid/${grapherConfigUuid}.config.json`).then( + (resp) => resp.json() + ) +) + const useTitleFragments = (config: MultiDimDataPageConfig) => { const title = config.config.title return useMemo( @@ -127,27 +135,62 @@ const useView = ( } const useVarDatapageData = ( - currentView: View | undefined + currentView: View | undefined, + variableIdToGrapherConfigMap: Record | undefined ) => { const [varDatapageData, setVarDatapageData] = useState(null) + const [grapherConfig, setGrapherConfig] = useState( + null + ) + const [grapherConfigIsReady, setGrapherConfigIsReady] = useState(false) useEffect(() => { + setGrapherConfigIsReady(false) + setGrapherConfig(null) setVarDatapageData(null) const yIndicatorOrIndicators = currentView?.indicators?.["y"] const variableId = Array.isArray(yIndicatorOrIndicators) ? yIndicatorOrIndicators[0] : yIndicatorOrIndicators if (!variableId) return - const variableMetadata = cachedGetVariableMetadata(variableId) - variableMetadata - .then((json) => getDatapageDataV2(json, currentView?.config)) - .then(setVarDatapageData) + const datapageDataPromise = cachedGetVariableMetadata(variableId).then( + (json) => getDatapageDataV2(json, currentView?.config) + ) + const grapherConfigUuid = variableIdToGrapherConfigMap?.[variableId] + const grapherConfigPromise = grapherConfigUuid + ? cachedGetGrapherConfigByUuid(grapherConfigUuid) + : null + + Promise.allSettled([datapageDataPromise, grapherConfigPromise]) + .then(([datapageData, grapherConfig]) => { + if (datapageData.status === "rejected") + throw new Error( + `Fetching variable by uuid failed: ${grapherConfigUuid}`, + { cause: datapageData.reason } + ) + + setVarDatapageData(datapageData.value) + setGrapherConfig( + grapherConfig.status === "fulfilled" + ? grapherConfig.value + : null + ) + setGrapherConfigIsReady(true) + }) .catch(console.error) - }, [currentView?.indicators, currentView?.config]) + }, [ + currentView?.indicators, + currentView?.config, + variableIdToGrapherConfigMap, + ]) - return { varDatapageData } + return { + varDatapageData, + varGrapherConfig: grapherConfig, + grapherConfigIsReady, + } } export const MultiDimDataPageContent = ({ @@ -155,6 +198,7 @@ export const MultiDimDataPageContent = ({ configObj, // isPreviewing = false, faqEntries, + variableIdToGrapherConfigMap, primaryTopic, canonicalUrl = "{URL}", // when we bake pages to their proper url this will be set correctly but on preview pages we leave this undefined tagToSlugMap, @@ -179,7 +223,8 @@ export const MultiDimDataPageContent = ({ }) const { currentView, dimensionsConfig } = useView(currentSettings, config) - const { varDatapageData } = useVarDatapageData(currentView) + const { varDatapageData, varGrapherConfig, grapherConfigIsReady } = + useVarDatapageData(currentView, variableIdToGrapherConfigMap) // This is the ACTUAL grapher instance being used, because GrapherFigureView/GrapherWithFallback are doing weird things and are not actually using the grapher instance we pass into it // and therefore we can not access the grapher state (e.g. tab, selection) from the grapher instance we pass into it @@ -207,15 +252,27 @@ export const MultiDimDataPageContent = ({ }, [queryStr]) const grapherConfigComputed = useMemo(() => { + const baseConfig: GrapherProgrammaticInterface = { + isEmbeddedInAnOwidPage: true, + selectedEntityNames: config.config.defaultSelection ?? [], + bounds, + } + + if (!grapherConfigIsReady) return baseConfig return { + ...varGrapherConfig, ...currentView?.config, dimensions: dimensionsConfig, - isEmbeddedInADataPage: true, - selectedEntityNames: config.config.defaultSelection ?? [], - - bounds, + ...baseConfig, } as GrapherProgrammaticInterface - }, [currentView, dimensionsConfig, bounds, config]) + }, [ + varGrapherConfig, + grapherConfigIsReady, + currentView, + dimensionsConfig, + bounds, + config, + ]) const hasTopicTags = !!config.config.topicTags?.length @@ -293,7 +350,9 @@ export const MultiDimDataPageContent = ({ >