Skip to content

Commit

Permalink
🐛 (explorer) merge grapher configs correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Oct 31, 2024
1 parent dd55a5e commit adf0264
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 73 deletions.
6 changes: 3 additions & 3 deletions baker/siteRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -826,12 +826,12 @@ const getExplorerTitleByUrl = async (
if (url.queryStr) {
explorer.initDecisionMatrix(url.queryParams as ExplorerFullQueryParams)
return (
explorer.grapherConfig.title ??
(explorer.grapherConfig.grapherId
explorer.explorerGrapherConfig.title ??
(explorer.explorerGrapherConfig.grapherId
? (
await getEnrichedChartById(
knex,
explorer.grapherConfig.grapherId
explorer.explorerGrapherConfig.grapherId
)
)?.config?.title
: undefined)
Expand Down
62 changes: 18 additions & 44 deletions explorer/Explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
isInIFrame,
keyBy,
keyMap,
mergeGrapherConfigs,
omitUndefinedValues,
parseIntOrUndefined,
PromiseCache,
Expand Down Expand Up @@ -372,7 +373,7 @@ export class Explorer
reaction(
() => [
this.entityPickerMetric,
this.explorerProgram.grapherConfig.tableSlug,
this.explorerProgram.explorerGrapherConfig.tableSlug,
],
() => this.updateEntityPickerTable()
)
Expand Down Expand Up @@ -473,34 +474,6 @@ export class Explorer
}
}

@action.bound private updateGrapherFromExplorerCommon() {
const grapher = this.grapher
if (!grapher) return
const {
yScaleToggle,
yAxisMin,
facetYDomain,
relatedQuestionText,
relatedQuestionUrl,
mapTargetTime,
} = this.explorerProgram.grapherConfig

grapher.yAxis.canChangeScaleType = yScaleToggle
grapher.yAxis.min = yAxisMin
if (facetYDomain) {
grapher.yAxis.facetDomain = facetYDomain
}
if (relatedQuestionText && relatedQuestionUrl) {
grapher.relatedQuestions = [
{ text: relatedQuestionText, url: relatedQuestionUrl },
]
}
if (mapTargetTime) {
grapher.map.time = mapTargetTime
}
if (!grapher.id) grapher.id = 0
}

@computed private get columnDefsWithoutTableSlugByIdOrSlug(): Record<
number | string,
OwidColumnDef
Expand Down Expand Up @@ -551,12 +524,14 @@ export class Explorer
const grapher = this.grapher
if (!grapher) return

const { grapherId } = this.explorerProgram.grapherConfig
const { grapherId } = this.explorerProgram.explorerGrapherConfig
const grapherConfig = this.grapherConfigs.get(grapherId!) ?? {}

const config: GrapherProgrammaticInterface = {
...grapherConfig,
...this.explorerProgram.grapherConfigOnlyGrapherProps,
...mergeGrapherConfigs(
grapherConfig,
this.explorerProgram.grapherConfig
),
bakedGrapherURL: BAKED_GRAPHER_URL,
dataApiUrl: DATA_API_URL,
hideEntityControls: this.showExplorerControls,
Expand All @@ -570,9 +545,7 @@ export class Explorer

grapher.setAuthoredVersion(config)
grapher.reset()
this.updateGrapherFromExplorerCommon()
grapher.updateFromObject(config)
grapher.slug = undefined
grapher.downloadData()
}

Expand All @@ -588,7 +561,7 @@ export class Explorer
xSlug,
colorSlug,
sizeSlug,
} = this.explorerProgram.grapherConfig
} = this.explorerProgram.explorerGrapherConfig

const yVariableIdsList = yVariableIds
.split(" ")
Expand All @@ -600,8 +573,10 @@ export class Explorer
{}

const config: GrapherProgrammaticInterface = {
...partialGrapherConfig,
...this.explorerProgram.grapherConfigOnlyGrapherProps,
...mergeGrapherConfigs(
partialGrapherConfig,
this.explorerProgram.grapherConfig
),
bakedGrapherURL: BAKED_GRAPHER_URL,
dataApiUrl: DATA_API_URL,
hideEntityControls: this.showExplorerControls,
Expand Down Expand Up @@ -687,7 +662,7 @@ export class Explorer
}

config.dimensions = dimensions
if (config.ySlugs && yVariableIds) config.ySlugs += " " + yVariableIds
if (ySlugs && yVariableIds) config.ySlugs = ySlugs + " " + yVariableIds

const inputTableTransformer = (table: OwidTable) => {
// add transformed (and intermediate) columns to the grapher table
Expand Down Expand Up @@ -733,7 +708,6 @@ export class Explorer

grapher.setAuthoredVersion(config)
grapher.reset()
this.updateGrapherFromExplorerCommon()
grapher.updateFromObject(config)
if (dimensions.length === 0) {
// If dimensions are empty, explicitly set the table to an empty table
Expand All @@ -749,10 +723,10 @@ export class Explorer
@action.bound private updateGrapherFromExplorerUsingColumnSlugs() {
const grapher = this.grapher
if (!grapher) return
const { tableSlug } = this.explorerProgram.grapherConfig
const { tableSlug } = this.explorerProgram.explorerGrapherConfig

const config: GrapherProgrammaticInterface = {
...this.explorerProgram.grapherConfigOnlyGrapherProps,
...this.explorerProgram.grapherConfig,
bakedGrapherURL: BAKED_GRAPHER_URL,
dataApiUrl: DATA_API_URL,
hideEntityControls: this.showExplorerControls,
Expand All @@ -766,7 +740,6 @@ export class Explorer

grapher.setAuthoredVersion(config)
grapher.reset()
this.updateGrapherFromExplorerCommon()
grapher.updateFromObject(config)

// Clear any error messages, they are likely to be related to dataset loading.
Expand Down Expand Up @@ -1099,7 +1072,7 @@ export class Explorer
// so that when we start sorting by entity name we can infer that the column is a string column immediately.
const tableSlugToLoad = this.entityPickerMetric
? this.getTableSlugOfColumnSlug(this.entityPickerMetric)
: this.explorerProgram.grapherConfig.tableSlug
: this.explorerProgram.explorerGrapherConfig.tableSlug

this.entityPickerTableIsLoading = true
void this.futureEntityPickerTable.set(
Expand Down Expand Up @@ -1135,7 +1108,8 @@ export class Explorer

// In most cases, column slugs will be duplicated in the tables, e.g. entityName.
// Prefer the current Grapher table if it contains the column slug.
const grapherTableSlug = this.explorerProgram.grapherConfig.tableSlug
const grapherTableSlug =
this.explorerProgram.explorerGrapherConfig.tableSlug
if (
this.tableSlugHasColumnSlug(
grapherTableSlug,
Expand Down
27 changes: 21 additions & 6 deletions explorer/ExplorerProgram.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ columns\ttable1\ttable2\ttable3
describe("grapherconfig", () => {
it("can return a grapher config", () => {
expect(
new ExplorerProgram("test", `yScaleToggle\ttrue`).grapherConfig
.yScaleToggle
new ExplorerProgram("test", `yScaleToggle\ttrue`)
.explorerGrapherConfig.yScaleToggle
).toEqual(true)
const program = new ExplorerProgram(
"test",
Expand All @@ -118,7 +118,7 @@ columns\ttable1\ttable2\ttable3
\ttrue\tLine`
)
expect(program.currentlySelectedGrapherRow).toEqual(2)
expect(program.grapherConfig.yScaleToggle).toEqual(true)
expect(program.explorerGrapherConfig.yScaleToggle).toEqual(true)
})

it("can convert \\n to a newline", () => {
Expand All @@ -128,7 +128,7 @@ columns\ttable1\ttable2\ttable3
\tsubtitle\tLine Checkbox
\tThis is a\\ntwo-line subtitle\tLine`
)
expect(program.grapherConfig.subtitle).toEqual(
expect(program.explorerGrapherConfig.subtitle).toEqual(
"This is a\ntwo-line subtitle"
)
})
Expand All @@ -142,9 +142,24 @@ graphers
\tyScaleToggle\tLine Checkbox
\ttrue\tLine`
)
expect(program.grapherConfig.hasMapTab).toEqual(true)
expect(program.explorerGrapherConfig.hasMapTab).toEqual(true)
// Only parse white listed grapher props
expect((program.grapherConfig as any).table).toEqual(undefined)
expect((program.explorerGrapherConfig as any).table).toEqual(
undefined
)
})

it("can translate explorer-specific settings to valid config fields", () => {
const program = new ExplorerProgram(
"test",
`hasMapTab\ttrue
table\tfoo
graphers
\tyAxisMin\tLine Checkbox
\t1\tLine`
)
expect(program.explorerGrapherConfig.yAxisMin).toEqual(1)
expect(program.grapherConfig.yAxis?.min).toEqual(1)
})
})

Expand Down
65 changes: 47 additions & 18 deletions explorer/ExplorerProgram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
PromiseCache,
SerializedGridProgram,
trimObject,
omit,
merge,
isEmpty,

Check warning on line 22 in explorer/ExplorerProgram.ts

View workflow job for this annotation

GitHub Actions / eslint

'isEmpty' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 22 in explorer/ExplorerProgram.ts

View workflow job for this annotation

GitHub Actions / eslint

'isEmpty' is defined but never used. Allowed unused vars must match /^_/u
} from "@ourworldindata/utils"
import {
CellDef,
Expand All @@ -39,6 +40,7 @@ import {
import { DecisionMatrix } from "./ExplorerDecisionMatrix.js"
import { ExplorerGrammar } from "./ExplorerGrammar.js"
import { GrapherGrammar } from "./GrapherGrammar.js"
import { defaultGrapherConfig } from "@ourworldindata/grapher"

export const EXPLORER_FILE_SUFFIX = ".explorer.tsv"

Expand Down Expand Up @@ -241,8 +243,8 @@ export class ExplorerProgram extends GridProgram {
// that use Grapher IDs as well as CSV data files to create charts,
// but we plan to drop support for mixed-content explorers in the future
get chartCreationMode(): ExplorerChartCreationMode {
const { decisionMatrix, grapherConfig } = this
const { grapherId } = grapherConfig
const { decisionMatrix, explorerGrapherConfig } = this
const { grapherId } = explorerGrapherConfig
const yVariableIdsColumn = decisionMatrix.table.get(
GrapherGrammar.yVariableIds.keyword
)
Expand Down Expand Up @@ -358,29 +360,56 @@ export class ExplorerProgram extends GridProgram {
return clone
}

get grapherConfig(): ExplorerGrapherInterface {
/**
* Grapher config for the currently selected row including global settings.
* Includes all columns that are part of the GrapherGrammar.
*/
get explorerGrapherConfig(): ExplorerGrapherInterface {
const rootObject = trimAndParseObject(this.tuplesObject, GrapherGrammar)

Object.keys(rootObject).forEach((key) => {
if (!GrapherGrammar[key]) delete rootObject[key]
})
let config = { ...rootObject }

const selectedGrapherRow = this.decisionMatrix.selectedRow
if (selectedGrapherRow && Object.keys(selectedGrapherRow).length) {
return { ...rootObject, ...selectedGrapherRow }
config = { ...config, ...selectedGrapherRow }
}

return rootObject
// remove all keys that are not part of the GrapherGrammar
Object.keys(config).forEach((key) => {
if (!GrapherGrammar[key]) delete config[key]
})

return config
}

get grapherConfigOnlyGrapherProps() {
return omit(this.grapherConfig, [
GrapherGrammar.yVariableIds.keyword,
GrapherGrammar.xVariableId.keyword,
GrapherGrammar.colorVariableId.keyword,
GrapherGrammar.sizeVariableId.keyword,
GrapherGrammar.mapTargetTime.keyword,
])
/**
* Grapher config for the currently selected row, with explorer-specific
* fields translated to valid GrapherInterface fields.
*
* For example, `yAxisMin` is translated to `{yAxis: {min: ... }}`
*/
get grapherConfig(): GrapherInterface {
const partialConfigs: GrapherInterface[] = []
const fields = Object.entries(this.explorerGrapherConfig)
for (const [field, value] of fields) {
const cellDef = GrapherGrammar[field]
partialConfigs.push(cellDef.toGrapherObject(value))
}

const mergedConfig = merge({}, ...partialConfigs)

// assume config is valid against the latest schema
mergedConfig.$schema = defaultGrapherConfig.$schema

// TODO: can be removed once relatedQuestions is refactored
const { relatedQuestionUrl, relatedQuestionText } =
this.explorerGrapherConfig
if (relatedQuestionUrl && relatedQuestionText) {
mergedConfig.relatedQuestions = [
{ url: relatedQuestionUrl, text: relatedQuestionText },
]
}

return mergedConfig
}

/**
Expand Down
Loading

0 comments on commit adf0264

Please sign in to comment.