- (manager.tab = this.availableTabs[index])
- }
+ labels={this.tabLabels}
+ activeIndex={this.activeTabIndex}
+ setActiveIndex={this.setTab}
/>
)
}
}
+
+function ContentSwitcherTab({
+ tab,
+ showLabel,
+ hasMultipleChartTypes,
+ isLineChartThatTurnedIntoDiscreteBar,
+}: {
+ tab: GrapherTabName
+ showLabel?: boolean
+ hasMultipleChartTypes?: boolean
+ isLineChartThatTurnedIntoDiscreteBar?: boolean
+}): React.ReactElement {
+ return (
+
+
+ {showLabel && (
+
+ {makeTabLabelText(tab, {
+ isLineChartThatTurnedIntoDiscreteBar,
+ hasMultipleChartTypes,
+ })}
+
+ )}
+
+ )
+}
+
+function TabIcon({
+ tab,
+ isLineChartThatTurnedIntoDiscreteBar,
+}: {
+ tab: GrapherTabName
+ isLineChartThatTurnedIntoDiscreteBar?: boolean
+}): React.ReactElement {
+ switch (tab) {
+ case GrapherTabName.Table:
+ return
+ case GrapherTabName.WorldMap:
+ return
+ default:
+ const chartIcon = isLineChartThatTurnedIntoDiscreteBar
+ ? chartIcons[ChartTypeName.DiscreteBar]
+ : chartIcons[tab as unknown as ChartTypeName]
+ return chartIcon
+ }
+}
+
+function makeTabLabelText(
+ tab: GrapherTabName,
+ options: {
+ isLineChartThatTurnedIntoDiscreteBar?: boolean
+ hasMultipleChartTypes?: boolean
+ }
+): string {
+ if (tab === GrapherTabName.Table) return "Table"
+ if (tab === GrapherTabName.WorldMap) return "Map"
+ if (!options.hasMultipleChartTypes) return "Chart"
+
+ switch (tab) {
+ case GrapherTabName.LineChart:
+ return options.isLineChartThatTurnedIntoDiscreteBar ? "Bar" : "Line"
+ case GrapherTabName.SlopeChart:
+ return "Slope"
+
+ // chart type labels are preliminary
+ case GrapherTabName.ScatterPlot:
+ return "Scatter"
+ case GrapherTabName.StackedArea:
+ return "Stacked area"
+ case GrapherTabName.StackedBar:
+ return "Stacked bar"
+ case GrapherTabName.DiscreteBar:
+ return "Bar"
+ case GrapherTabName.StackedDiscreteBar:
+ return "Stacked bar"
+ case GrapherTabName.Marimekko:
+ return "Marimekko"
+ default:
+ return "Chart"
+ }
+}
diff --git a/packages/@ourworldindata/grapher/src/controls/SettingsMenu.tsx b/packages/@ourworldindata/grapher/src/controls/SettingsMenu.tsx
index 53cfcae4414..7613fde4322 100644
--- a/packages/@ourworldindata/grapher/src/controls/SettingsMenu.tsx
+++ b/packages/@ourworldindata/grapher/src/controls/SettingsMenu.tsx
@@ -64,7 +64,7 @@ export interface SettingsMenuManager
hideTableFilterToggle?: boolean
// chart state
- type: ChartTypeName
+ activeChartType?: ChartTypeName
isRelativeMode?: boolean
selection?: SelectionArray | EntityName[]
canChangeAddOrHighlightEntities?: boolean
@@ -101,6 +101,10 @@ export class SettingsMenu extends React.Component<{
return test.showSettingsMenuToggle
}
+ @computed get chartType(): ChartTypeName {
+ return this.manager.activeChartType ?? ChartTypeName.LineChart
+ }
+
@computed get maxWidth(): number {
return this.props.maxWidth ?? DEFAULT_BOUNDS.width
}
@@ -108,7 +112,7 @@ export class SettingsMenu extends React.Component<{
@computed get showYScaleToggle(): boolean | undefined {
if (this.manager.hideYScaleToggle) return false
if (this.manager.isRelativeMode) return false
- if ([StackedArea, StackedBar].includes(this.manager.type)) return false // We currently do not have these charts with log scale
+ if ([StackedArea, StackedBar].includes(this.chartType)) return false // We currently do not have these charts with log scale
return this.manager.yAxis.canChangeScaleType
}
@@ -123,15 +127,15 @@ export class SettingsMenu extends React.Component<{
return (
!this.manager.hideFacetYDomainToggle &&
this.manager.facetStrategy !== FacetStrategy.none &&
- this.manager.type !== StackedDiscreteBar
+ this.chartType !== StackedDiscreteBar
)
}
@computed get showZoomToggle(): boolean {
- const { type, hideZoomToggle } = this.manager
+ const { hideZoomToggle } = this.manager
return (
!hideZoomToggle &&
- type === ScatterPlot &&
+ this.chartType === ScatterPlot &&
this.selectionArray.hasSelection
)
}
@@ -139,16 +143,16 @@ export class SettingsMenu extends React.Component<{
@computed get showNoDataAreaToggle(): boolean {
return (
!this.manager.hideNoDataAreaToggle &&
- this.manager.type === Marimekko &&
+ this.chartType === Marimekko &&
this.manager.xColumnSlug !== undefined
)
}
@computed get showAbsRelToggle(): boolean {
- const { type, canToggleRelativeMode, hasTimeline, xOverrideTime } =
+ const { canToggleRelativeMode, hasTimeline, xOverrideTime } =
this.manager
if (!canToggleRelativeMode) return false
- if (type === ScatterPlot)
+ if (this.chartType === ScatterPlot)
return xOverrideTime === undefined && !!hasTimeline
return [
StackedArea,
@@ -157,7 +161,7 @@ export class SettingsMenu extends React.Component<{
ScatterPlot,
LineChart,
Marimekko,
- ].includes(type)
+ ].includes(this.chartType)
}
@computed get showFacetControl(): boolean {
@@ -166,7 +170,6 @@ export class SettingsMenu extends React.Component<{
availableFacetStrategies,
hideFacetControl,
isOnTableTab,
- type,
} = this.manager
// if there's no choice to be made, don't display a lone button
@@ -181,7 +184,7 @@ export class SettingsMenu extends React.Component<{
StackedBar,
StackedDiscreteBar,
LineChart,
- ].includes(type)
+ ].includes(this.chartType)
const hasProjection = filledDimensions.some(
(dim) => dim.display.isProjection
@@ -259,9 +262,8 @@ export class SettingsMenu extends React.Component<{
return this.props.manager
}
- @computed get chartType(): string {
- const { type } = this.manager
- return type.replace(/([A-Z])/g, " $1")
+ @computed get chartTypeLabel(): string {
+ return this.chartType.replace(/([A-Z])/g, " $1")
}
@computed get selectionArray(): SelectionArray {
@@ -388,10 +390,10 @@ export class SettingsMenu extends React.Component<{
}
@computed get menuContents(): JSX.Element {
- const { manager, chartType } = this
+ const { manager, chartTypeLabel } = this
const { isOnTableTab } = manager
- const menuTitle = `${isOnTableTab ? "Table" : chartType} settings`
+ const menuTitle = `${isOnTableTab ? "Table" : chartTypeLabel} settings`
return (
diff --git a/packages/@ourworldindata/grapher/src/controls/controlsRow/ControlsRow.tsx b/packages/@ourworldindata/grapher/src/controls/controlsRow/ControlsRow.tsx
index 0ae2db8b681..5cc1f330cb5 100644
--- a/packages/@ourworldindata/grapher/src/controls/controlsRow/ControlsRow.tsx
+++ b/packages/@ourworldindata/grapher/src/controls/controlsRow/ControlsRow.tsx
@@ -2,7 +2,7 @@ import React from "react"
import { computed } from "mobx"
import { observer } from "mobx-react"
-import { Bounds, DEFAULT_BOUNDS, GrapherTabOption } from "@ourworldindata/utils"
+import { Bounds, DEFAULT_BOUNDS } from "@ourworldindata/utils"
import { ContentSwitchers, ContentSwitchersManager } from "../ContentSwitchers"
import {
@@ -22,7 +22,6 @@ export interface ControlsRowManager
MapProjectionMenuManager,
SettingsMenuManager {
sidePanelBounds?: Bounds
- availableTabs?: GrapherTabOption[]
showEntitySelectionToggle?: boolean
framePaddingHorizontal?: number
framePaddingVertical?: number
diff --git a/packages/@ourworldindata/grapher/src/controls/settings/AbsRelToggle.tsx b/packages/@ourworldindata/grapher/src/controls/settings/AbsRelToggle.tsx
index 7db655a3e87..fd9e6c17a55 100644
--- a/packages/@ourworldindata/grapher/src/controls/settings/AbsRelToggle.tsx
+++ b/packages/@ourworldindata/grapher/src/controls/settings/AbsRelToggle.tsx
@@ -9,7 +9,7 @@ const { LineChart, ScatterPlot } = ChartTypeName
export interface AbsRelToggleManager {
stackMode?: StackMode
relativeToggleLabel?: string
- type: ChartTypeName
+ activeChartType?: ChartTypeName
}
@observer
@@ -31,10 +31,10 @@ export class AbsRelToggle extends React.Component<{
}
@computed get tooltip(): string {
- const { type } = this.manager
- return type === ScatterPlot
+ const { activeChartType } = this.manager
+ return activeChartType === ScatterPlot
? "Show the percentage change per year over the the selected time range."
- : type === LineChart
+ : activeChartType === LineChart
? "Show proportional changes over time or actual values in their original units."
: "Show values as their share of the total or as actual values in their original units."
}
diff --git a/packages/@ourworldindata/grapher/src/core/Grapher.jsdom.test.ts b/packages/@ourworldindata/grapher/src/core/Grapher.jsdom.test.ts
index c8ad4c18e69..956e6dd4e00 100755
--- a/packages/@ourworldindata/grapher/src/core/Grapher.jsdom.test.ts
+++ b/packages/@ourworldindata/grapher/src/core/Grapher.jsdom.test.ts
@@ -10,6 +10,7 @@ import {
GrapherQueryParams,
LegacyGrapherInterface,
LegacyGrapherQueryParams,
+ GrapherTabName,
} from "@ourworldindata/types"
import {
TimeBoundValue,
@@ -71,7 +72,7 @@ it("can get dimension slots", () => {
const grapher = new Grapher()
expect(grapher.dimensionSlots.length).toBe(2)
- grapher.type = ChartTypeName.ScatterPlot
+ grapher.chartTypes = [ChartTypeName.ScatterPlot]
expect(grapher.dimensionSlots.length).toBe(4)
})
@@ -86,7 +87,7 @@ it("an empty Grapher serializes to an object that includes only the schema", ()
it("a bad chart type does not crash grapher", () => {
const input = {
- type: "fff" as any,
+ chartTypes: ["fff" as any],
}
expect(new Grapher(input).toObject()).toEqual({
...input,
@@ -211,22 +212,22 @@ it("can generate a url with country selection even if there is no entity code",
describe("hasTimeline", () => {
it("charts with timeline", () => {
const grapher = new Grapher(legacyConfig)
- grapher.type = ChartTypeName.LineChart
+ grapher.chartTypes = [ChartTypeName.LineChart]
expect(grapher.hasTimeline).toBeTruthy()
- grapher.type = ChartTypeName.SlopeChart
+ grapher.chartTypes = [ChartTypeName.SlopeChart]
expect(grapher.hasTimeline).toBeTruthy()
- grapher.type = ChartTypeName.StackedArea
+ grapher.chartTypes = [ChartTypeName.StackedArea]
expect(grapher.hasTimeline).toBeTruthy()
- grapher.type = ChartTypeName.StackedBar
+ grapher.chartTypes = [ChartTypeName.StackedBar]
expect(grapher.hasTimeline).toBeTruthy()
- grapher.type = ChartTypeName.DiscreteBar
+ grapher.chartTypes = [ChartTypeName.DiscreteBar]
expect(grapher.hasTimeline).toBeTruthy()
})
it("map tab has timeline even if chart doesn't", () => {
const grapher = new Grapher(legacyConfig)
grapher.hideTimeline = true
- grapher.type = ChartTypeName.LineChart
+ grapher.chartTypes = [ChartTypeName.LineChart]
expect(grapher.hasTimeline).toBeFalsy()
grapher.tab = GrapherTabOption.map
expect(grapher.hasTimeline).toBeTruthy()
@@ -379,7 +380,7 @@ describe("authors can use maxTime", () => {
const table = SynthesizeGDPTable({ timeRange: [2000, 2010] })
const grapher = new Grapher({
table,
- type: ChartTypeName.DiscreteBar,
+ chartTypes: [ChartTypeName.DiscreteBar],
selectedEntityNames: table.availableEntityNames,
maxTime: 2005,
ySlugs: "GDP",
@@ -509,6 +510,73 @@ describe("urls", () => {
grapher.populateFromQueryParams(url.queryParams)
expect(grapher.selection.selectedEntityNames).toEqual(["usa", "canada"])
})
+
+ it("parses tab=table correctly", () => {
+ const grapher = new Grapher()
+ grapher.populateFromQueryParams({ tab: "table" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.Table)
+ })
+
+ it("parses tab=map correctly", () => {
+ const grapher = new Grapher()
+ grapher.populateFromQueryParams({ tab: "map" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.WorldMap)
+ })
+
+ it("parses tab=chart correctly", () => {
+ const grapher = new Grapher({ chartTypes: [ChartTypeName.ScatterPlot] })
+ grapher.populateFromQueryParams({ tab: "chart" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.ScatterPlot)
+ })
+
+ it("parses tab=line and tab=slope correctly", () => {
+ const grapher = new Grapher({
+ chartTypes: [ChartTypeName.LineChart, ChartTypeName.SlopeChart],
+ })
+ grapher.populateFromQueryParams({ tab: "line" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.LineChart)
+ grapher.populateFromQueryParams({ tab: "slope" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.SlopeChart)
+ })
+
+ it("switches to the first chart tab if the given chart isn't available", () => {
+ const grapher = new Grapher({
+ chartTypes: [ChartTypeName.LineChart, ChartTypeName.SlopeChart],
+ })
+ grapher.populateFromQueryParams({ tab: "bar" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.LineChart)
+ })
+
+ it("switches to the map tab if no chart is available", () => {
+ const grapher = new Grapher({ chartTypes: [], hasMapTab: true })
+ grapher.populateFromQueryParams({ tab: "line" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.WorldMap)
+ })
+
+ it("switches to the table tab if it's the only tab available", () => {
+ const grapher = new Grapher({ chartTypes: [] })
+ grapher.populateFromQueryParams({ tab: "line" })
+ expect(grapher.activeTab).toEqual(GrapherTabName.Table)
+ })
+
+ it("adds tab=chart to the URL if there is a single chart tab", () => {
+ const grapher = new Grapher({
+ hasMapTab: true,
+ tab: GrapherTabOption.map,
+ })
+ grapher.setTab(GrapherTabName.LineChart)
+ expect(grapher.changedParams.tab).toEqual("chart")
+ })
+
+ it("adds the chart type name as tab query param if there are multiple chart tabs", () => {
+ const grapher = new Grapher({
+ chartTypes: [ChartTypeName.LineChart, ChartTypeName.SlopeChart],
+ hasMapTab: true,
+ tab: GrapherTabOption.map,
+ })
+ grapher.setTab(GrapherTabName.LineChart)
+ expect(grapher.changedParams.tab).toEqual("line")
+ })
})
describe("time domain tests", () => {
@@ -918,7 +986,7 @@ it("correctly identifies activeColumnSlugs", () => {
`)
const grapher = new Grapher({
table,
- type: ChartTypeName.ScatterPlot,
+ chartTypes: [ChartTypeName.ScatterPlot],
xSlug: "gdp",
ySlugs: "child_mortality",
colorSlug: "continent",
@@ -955,9 +1023,9 @@ it("considers map tolerance before using column tolerance", () => {
const grapher = new Grapher({
table,
- type: ChartTypeName.WorldMap,
ySlugs: "gdp",
tab: GrapherTabOption.map,
+ hasMapTab: true,
map: new MapConfig({ timeTolerance: 1, columnSlug: "gdp", time: 2002 }),
})
@@ -1016,7 +1084,7 @@ describe("tableForSelection", () => {
const grapher = new Grapher({
table,
- type: ChartTypeName.ScatterPlot,
+ chartTypes: [ChartTypeName.ScatterPlot],
excludedEntities: [3],
xSlug: "x",
ySlugs: "y",
@@ -1052,7 +1120,7 @@ it("handles tolerance when there are gaps in ScatterPlot data", () => {
const grapher = new Grapher({
table,
- type: ChartTypeName.ScatterPlot,
+ chartTypes: [ChartTypeName.ScatterPlot],
xSlug: "x",
ySlugs: "y",
minTime: 1999,
diff --git a/packages/@ourworldindata/grapher/src/core/Grapher.stories.tsx b/packages/@ourworldindata/grapher/src/core/Grapher.stories.tsx
index a5d3b43b6bc..84248560305 100644
--- a/packages/@ourworldindata/grapher/src/core/Grapher.stories.tsx
+++ b/packages/@ourworldindata/grapher/src/core/Grapher.stories.tsx
@@ -49,7 +49,7 @@ export const Line = (): React.ReactElement =>
export const SlopeChart = (): React.ReactElement => {
const model = {
- type: ChartTypeName.SlopeChart,
+ chartTypes: [ChartTypeName.SlopeChart],
...basics,
}
return
@@ -57,7 +57,7 @@ export const SlopeChart = (): React.ReactElement => {
export const ScatterPlot = (): React.ReactElement => {
const model = {
- type: ChartTypeName.ScatterPlot,
+ chartTypes: [ChartTypeName.ScatterPlot],
...basics,
}
return
@@ -65,7 +65,7 @@ export const ScatterPlot = (): React.ReactElement => {
export const DiscreteBar = (): React.ReactElement => {
const model = {
- type: ChartTypeName.DiscreteBar,
+ chartTypes: [ChartTypeName.DiscreteBar],
...basics,
}
return
@@ -73,7 +73,7 @@ export const DiscreteBar = (): React.ReactElement => {
export const StackedBar = (): React.ReactElement => {
const model = {
- type: ChartTypeName.StackedBar,
+ chartTypes: [ChartTypeName.StackedBar],
...basics,
}
return
@@ -81,7 +81,7 @@ export const StackedBar = (): React.ReactElement => {
export const StackedArea = (): React.ReactElement => {
const model = {
- type: ChartTypeName.StackedArea,
+ chartTypes: [ChartTypeName.StackedArea],
...basics,
}
return
@@ -97,7 +97,6 @@ export const MapFirst = (): React.ReactElement => {
export const BlankGrapher = (): React.ReactElement => {
const model = {
- type: ChartTypeName.WorldMap,
tab: GrapherTabOption.map,
table: BlankOwidTable(),
hasMapTab: true,
@@ -115,7 +114,7 @@ export const NoMap = (): React.ReactElement => {
export const Faceting = (): React.ReactElement => {
const model = {
- type: ChartTypeName.StackedArea,
+ chartTypes: [ChartTypeName.StackedArea],
facet: FacetStrategy.entity,
...basics,
}
@@ -161,7 +160,7 @@ class PerfGrapher extends React.Component {
diff --git a/packages/@ourworldindata/grapher/src/core/Grapher.tsx b/packages/@ourworldindata/grapher/src/core/Grapher.tsx
index cf38c20e544..7e3822bcd8f 100644
--- a/packages/@ourworldindata/grapher/src/core/Grapher.tsx
+++ b/packages/@ourworldindata/grapher/src/core/Grapher.tsx
@@ -105,6 +105,8 @@ import {
GrapherWindowType,
Color,
GRAPHER_QUERY_PARAM_KEYS,
+ GrapherTabName,
+ GrapherTabQueryParam,
} from "@ourworldindata/types"
import {
BlankOwidTable,
@@ -191,6 +193,9 @@ import { ScatterPlotManager } from "../scatterCharts/ScatterPlotChartConstants"
import {
autoDetectSeriesStrategy,
autoDetectYColumnSlugs,
+ makeChartTypesValid,
+ mapChartTypeNameToQueryParam,
+ mapQueryParamToChartTypeName,
} from "../chart/ChartUtils"
import classnames from "classnames"
import { GrapherAnalytics } from "./GrapherAnalytics"
@@ -301,6 +306,7 @@ export interface GrapherProgrammaticInterface extends GrapherInterface {
hideTableFilterToggle?: boolean
forceHideAnnotationFieldsInTitle?: AnnotationFieldsInTitle
hasTableTab?: boolean
+ hideChartTabs?: boolean
hideShareButton?: boolean
hideExploreTheDataButton?: boolean
hideRelatedQuestion?: boolean
@@ -350,7 +356,7 @@ export class Grapher
SlopeChartManager
{
@observable.ref $schema = defaultGrapherConfig.$schema
- @observable.ref type = ChartTypeName.LineChart
+ @observable.ref chartTypes = [ChartTypeName.LineChart]
@observable.ref id?: number = undefined
@observable.ref version = 1
@observable.ref slug?: string = undefined
@@ -384,9 +390,9 @@ export class Grapher
@observable.ref hideScatterLabels?: boolean = undefined
@observable.ref zoomToSelection?: boolean = undefined
@observable.ref showYearLabels?: boolean = undefined // Always show year in labels for bar charts
- @observable.ref hasChartTab = true
@observable.ref hasMapTab = false
@observable.ref tab = GrapherTabOption.chart
+ @observable.ref chartTab?: ChartTypeName // TODO: remove map from ChartTypeName
@observable.ref isPublished?: boolean = undefined
@observable.ref baseColorScheme?: ColorSchemeName = undefined
@observable.ref invertColorScheme?: boolean = undefined
@@ -605,13 +611,10 @@ export class Grapher
@action.bound populateFromQueryParams(params: GrapherQueryParams): void {
// Set tab if specified
- const tab = params.tab
- if (tab) {
- if (this.availableTabs.includes(tab as any)) {
- this.tab = tab as GrapherTabOption
- } else {
- console.error("Unexpected tab: " + tab)
- }
+ if (params.tab) {
+ const tab = this.mapQueryParamToGrapherTab(params.tab)
+ if (tab) this.setTab(tab)
+ else console.error("Unexpected tab: " + params.tab)
}
// Set overlay if specified
@@ -693,6 +696,29 @@ export class Grapher
) as TimeBounds
}
+ @computed get activeTab(): GrapherTabName {
+ if (this.tab === GrapherTabOption.table) return GrapherTabName.Table
+ if (this.tab === GrapherTabOption.map) return GrapherTabName.WorldMap
+ if (this.chartTab) return this.chartTab as unknown as GrapherTabName
+ return (
+ (this.chartTypes[0] as unknown as GrapherTabName) ??
+ GrapherTabName.LineChart
+ )
+ }
+
+ @computed get activeChartType(): ChartTypeName | undefined {
+ if (!this.isOnChartTab) return undefined
+ return this.activeTab as unknown as ChartTypeName
+ }
+
+ @computed get mainChartType(): ChartTypeName | undefined {
+ return this.chartTypes[0]
+ }
+
+ @computed get hasChartTab(): boolean {
+ return this.chartTypes.length > 0
+ }
+
@computed get isOnChartTab(): boolean {
return this.tab === GrapherTabOption.chart
}
@@ -953,7 +979,7 @@ export class Grapher
properties: [
// might be missing for charts within explorers or mdims
["slug", this.slug ?? "missing-slug"],
- ["chartType", this.type],
+ ["chartTypes", this.chartTypes],
["tab", this.tab],
],
},
@@ -1267,6 +1293,19 @@ export class Grapher
this.disposers.forEach((dispose) => dispose())
}
+ @action.bound setTab(newTab: GrapherTabName): void {
+ if (newTab === GrapherTabName.Table) {
+ this.tab = GrapherTabOption.table
+ this.chartTab = undefined
+ } else if (newTab === GrapherTabName.WorldMap) {
+ this.tab = GrapherTabOption.map
+ this.chartTab = undefined
+ } else {
+ this.tab = GrapherTabOption.chart
+ this.chartTab = newTab as unknown as ChartTypeName
+ }
+ }
+
// todo: can we remove this?
// I believe these states can only occur during editing.
@action.bound private ensureValidConfigWhenEditing(): void {
@@ -1278,8 +1317,8 @@ export class Grapher
)
const disposers = [
autorun(() => {
- if (!this.availableTabs.includes(this.tab))
- runInAction(() => (this.tab = this.availableTabs[0]))
+ if (!this.availableTabs.includes(this.activeTab))
+ runInAction(() => this.setTab(this.availableTabs[0]))
}),
autorun(() => {
const validDimensions = this.validDimensions
@@ -1481,12 +1520,16 @@ export class Grapher
})
}
- @computed get availableTabs(): GrapherTabOption[] {
+ @computed get availableTabs(): GrapherTabName[] {
return [
- this.hasTableTab && GrapherTabOption.table,
- this.hasMapTab && GrapherTabOption.map,
- this.hasChartTab && GrapherTabOption.chart,
- ].filter(identity) as GrapherTabOption[]
+ this.hasTableTab && GrapherTabName.Table,
+ this.hasMapTab && GrapherTabName.WorldMap,
+ ...makeChartTypesValid(this.chartTypes),
+ ].filter(identity) as GrapherTabName[]
+ }
+
+ @computed get hasMultipleChartTypes(): boolean {
+ return this.chartTypes.length > 1
}
@computed get currentSubtitle(): string {
@@ -1868,32 +1911,32 @@ export class Grapher
// Switch to bar chart if a single year is selected. Todo: do we want to do this?
return this.isLineChartThatTurnedIntoDiscreteBar
? ChartTypeName.DiscreteBar
- : this.type
+ : this.mainChartType ?? ChartTypeName.LineChart
}
@computed get isLineChart(): boolean {
- return this.type === ChartTypeName.LineChart
+ return this.mainChartType === ChartTypeName.LineChart
}
@computed get isScatter(): boolean {
- return this.type === ChartTypeName.ScatterPlot
+ return this.mainChartType === ChartTypeName.ScatterPlot
}
@computed get isStackedArea(): boolean {
- return this.type === ChartTypeName.StackedArea
+ return this.mainChartType === ChartTypeName.StackedArea
}
@computed get isSlopeChart(): boolean {
- return this.type === ChartTypeName.SlopeChart
+ return this.mainChartType === ChartTypeName.SlopeChart
}
@computed get isDiscreteBar(): boolean {
- return this.type === ChartTypeName.DiscreteBar
+ return this.mainChartType === ChartTypeName.DiscreteBar
}
@computed get isStackedBar(): boolean {
- return this.type === ChartTypeName.StackedBar
+ return this.mainChartType === ChartTypeName.StackedBar
}
@computed get isMarimekko(): boolean {
- return this.type === ChartTypeName.Marimekko
+ return this.mainChartType === ChartTypeName.Marimekko
}
@computed get isStackedDiscreteBar(): boolean {
- return this.type === ChartTypeName.StackedDiscreteBar
+ return this.mainChartType === ChartTypeName.StackedDiscreteBar
}
@computed get isLineChartThatTurnedIntoDiscreteBar(): boolean {
@@ -2372,7 +2415,7 @@ export class Grapher
}
@action.bound private toggleTabCommand(): void {
- this.tab = next(this.availableTabs, this.tab)
+ this.setTab(next(this.availableTabs, this.activeTab))
}
@action.bound private togglePlayingCommand(): void {
@@ -3140,9 +3183,55 @@ export class Grapher
debounceMode = false
+ private mapQueryParamToGrapherTab(tab: string): GrapherTabName | undefined {
+ const { chartTypes, hasMapTab } = this
+
+ if (tab === GrapherTabQueryParam.Table) {
+ return GrapherTabName.Table
+ }
+ if (tab === GrapherTabQueryParam.WorldMap) {
+ return GrapherTabName.WorldMap
+ }
+
+ const defaultChartType = chartTypes[0]
+ if (tab === GrapherTabQueryParam.Chart) {
+ if (defaultChartType) {
+ return defaultChartType as unknown as GrapherTabName
+ } else if (hasMapTab) {
+ return GrapherTabName.WorldMap
+ } else {
+ return GrapherTabName.Table
+ }
+ }
+
+ const chartTypeName = mapQueryParamToChartTypeName(tab)
+
+ if (!chartTypeName) return undefined
+
+ if (chartTypes.includes(chartTypeName)) {
+ return chartTypeName as unknown as GrapherTabName
+ } else if (defaultChartType) {
+ return defaultChartType as unknown as GrapherTabName
+ } else if (hasMapTab) {
+ return GrapherTabName.WorldMap
+ } else {
+ return GrapherTabName.Table
+ }
+ }
+
+ mapGrapherTabToQueryParam(tab: GrapherTabName): string {
+ if (tab === GrapherTabName.Table) return GrapherTabQueryParam.Table
+ if (tab === GrapherTabName.WorldMap)
+ return GrapherTabQueryParam.WorldMap
+
+ if (!this.hasMultipleChartTypes) return GrapherTabQueryParam.Chart
+
+ return mapChartTypeNameToQueryParam(tab as unknown as ChartTypeName)
+ }
+
@computed.struct get allParams(): GrapherQueryParams {
const params: GrapherQueryParams = {}
- params.tab = this.tab
+ params.tab = this.mapGrapherTabToQueryParam(this.activeTab)
params.xScale = this.xAxis.scaleType
params.yScale = this.yAxis.scaleType
params.stackMode = this.stackMode
@@ -3473,6 +3562,7 @@ export class Grapher
changeInPrefix: false,
}
@observable hasTableTab = true
+ @observable hideChartTabs = false
@observable hideShareButton = false
@observable hideExploreTheDataButton = true
@observable hideRelatedQuestion = false
diff --git a/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts b/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts
index 3fabd87cb2a..2c80e8c26f4 100644
--- a/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts
+++ b/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts
@@ -1,3 +1,4 @@
+import { ChartTypeName } from "@ourworldindata/types"
import type { GrapherProgrammaticInterface } from "./Grapher"
export const GRAPHER_EMBEDDED_FIGURE_ATTR = "data-grapher-src"
@@ -75,7 +76,7 @@ export enum Patterns {
noDataPatternForMapChart = "noDataPatternForMapChart",
}
-export const grapherInterfaceWithHiddenControlsOnly: GrapherProgrammaticInterface =
+export const grapherInterfaceWithHiddenControls: GrapherProgrammaticInterface =
{
hideRelativeToggle: true,
hideTimeline: true,
@@ -93,9 +94,12 @@ export const grapherInterfaceWithHiddenControlsOnly: GrapherProgrammaticInterfac
},
}
-export const grapherInterfaceWithHiddenTabsOnly: GrapherProgrammaticInterface =
- {
- hasChartTab: false,
- hasMapTab: false,
- hasTableTab: false,
- }
+export const grapherInterfaceWithHiddenTabs: GrapherProgrammaticInterface = {
+ hasMapTab: false,
+ hasTableTab: false,
+ hideChartTabs: true,
+}
+
+export const validChartTypeCombinations = [
+ [ChartTypeName.LineChart, ChartTypeName.SlopeChart],
+]
diff --git a/packages/@ourworldindata/grapher/src/core/GrapherWithChartTypes.jsdom.test.tsx b/packages/@ourworldindata/grapher/src/core/GrapherWithChartTypes.jsdom.test.tsx
index b82ca6f925e..467fa2af3c4 100755
--- a/packages/@ourworldindata/grapher/src/core/GrapherWithChartTypes.jsdom.test.tsx
+++ b/packages/@ourworldindata/grapher/src/core/GrapherWithChartTypes.jsdom.test.tsx
@@ -50,7 +50,7 @@ const basicGrapherConfig: GrapherProgrammaticInterface = {
describe("grapher and discrete bar charts", () => {
const grapher = new Grapher({
- type: ChartTypeName.DiscreteBar,
+ chartTypes: [ChartTypeName.DiscreteBar],
...basicGrapherConfig,
})
expect(grapher.chartInstance.series.length).toBeGreaterThan(0)
diff --git a/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.test.ts b/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.test.ts
index 29c8fcf6302..c292f2ad16a 100755
--- a/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.test.ts
+++ b/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.test.ts
@@ -466,7 +466,7 @@ describe(legacyToOwidTableAndDimensions, () => {
it("joins targetTime", () => {
const scatterLegacyGrapherConfig = {
...legacyGrapherConfig,
- type: ChartTypeName.ScatterPlot,
+ chartTypes: [ChartTypeName.ScatterPlot],
}
const { table } = legacyToOwidTableAndDimensions(
diff --git a/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.ts b/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.ts
index dad8561d05c..2141db3909e 100644
--- a/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.ts
+++ b/packages/@ourworldindata/grapher/src/core/LegacyToOwidTable.ts
@@ -198,9 +198,10 @@ export const legacyToOwidTableAndDimensions = (
// We do this by dropping the column. We interpolate before which adds an originalTime
// column which can be used to recover the time.
const targetTime = dimension?.targetYear
+ const mainChartType = grapherConfig.chartTypes?.[0]
if (
- (grapherConfig.type === ChartTypeName.ScatterPlot ||
- grapherConfig.type === ChartTypeName.Marimekko) &&
+ (mainChartType === ChartTypeName.ScatterPlot ||
+ mainChartType === ChartTypeName.Marimekko) &&
isNumber(targetTime)
) {
variableTable = variableTable
diff --git a/packages/@ourworldindata/grapher/src/dataTable/DataTable.jsdom.test.tsx b/packages/@ourworldindata/grapher/src/dataTable/DataTable.jsdom.test.tsx
index 0825a56dece..c52f0c833af 100755
--- a/packages/@ourworldindata/grapher/src/dataTable/DataTable.jsdom.test.tsx
+++ b/packages/@ourworldindata/grapher/src/dataTable/DataTable.jsdom.test.tsx
@@ -70,7 +70,7 @@ describe("when you select a range of years", () => {
let view: ReactWrapper
beforeAll(() => {
const grapher = childMortalityGrapher({
- type: ChartTypeName.LineChart,
+ chartTypes: [ChartTypeName.LineChart],
tab: GrapherTabOption.table,
})
grapher.timelineHandleTimeBounds = [1950, 2019]
diff --git a/packages/@ourworldindata/grapher/src/index.ts b/packages/@ourworldindata/grapher/src/index.ts
index 203bb60db3a..21fb90a2ce4 100644
--- a/packages/@ourworldindata/grapher/src/index.ts
+++ b/packages/@ourworldindata/grapher/src/index.ts
@@ -22,8 +22,8 @@ export {
ThereWasAProblemLoadingThisChart,
WorldEntityName,
Patterns,
- grapherInterfaceWithHiddenControlsOnly,
- grapherInterfaceWithHiddenTabsOnly,
+ grapherInterfaceWithHiddenControls,
+ grapherInterfaceWithHiddenTabs,
CONTINENTS_INDICATOR_ID,
POPULATION_INDICATOR_ID_USED_IN_ADMIN,
} from "./core/GrapherConstants"
diff --git a/packages/@ourworldindata/grapher/src/schema/defaultGrapherConfig.ts b/packages/@ourworldindata/grapher/src/schema/defaultGrapherConfig.ts
index dd0f68334e2..cb53a48545a 100644
--- a/packages/@ourworldindata/grapher/src/schema/defaultGrapherConfig.ts
+++ b/packages/@ourworldindata/grapher/src/schema/defaultGrapherConfig.ts
@@ -4,11 +4,17 @@
import { GrapherInterface } from "@ourworldindata/types"
-export const latestSchemaVersion = "005" as const
-export const outdatedSchemaVersions = ["001", "002", "003", "004"] as const
+export const latestSchemaVersion = "006" as const
+export const outdatedSchemaVersions = [
+ "001",
+ "002",
+ "003",
+ "004",
+ "005",
+] as const
export const defaultGrapherConfig = {
- $schema: "https://files.ourworldindata.org/schemas/grapher-schema.005.json",
+ $schema: "https://files.ourworldindata.org/schemas/grapher-schema.006.json",
map: {
projection: "World",
hideTimeline: false,
@@ -33,7 +39,6 @@ export const defaultGrapherConfig = {
},
tab: "chart",
matchingEntitiesOnly: false,
- hasChartTab: true,
hideLegend: false,
hideLogo: false,
timelineMinTime: "earliest",
@@ -54,7 +59,7 @@ export const defaultGrapherConfig = {
facettingLabelByYVariables: "metric",
addCountryMode: "add-country",
compareEndPointsOnly: false,
- type: "LineChart",
+ types: ["LineChart"],
hasMapTab: false,
stackMode: "absolute",
minTime: "earliest",
diff --git a/packages/@ourworldindata/grapher/src/schema/grapher-schema.005.yaml b/packages/@ourworldindata/grapher/src/schema/grapher-schema.006.yaml
similarity index 97%
rename from packages/@ourworldindata/grapher/src/schema/grapher-schema.005.yaml
rename to packages/@ourworldindata/grapher/src/schema/grapher-schema.006.yaml
index 4465a68f6c0..b6b77c94245 100644
--- a/packages/@ourworldindata/grapher/src/schema/grapher-schema.005.yaml
+++ b/packages/@ourworldindata/grapher/src/schema/grapher-schema.006.yaml
@@ -1,7 +1,7 @@
$schema: "http://json-schema.org/draft-07/schema#"
# if you update the required keys, make sure that the mergeGrapherConfigs and
# diffGrapherConfigs functions both reflect this change
-$id: "https://files.ourworldindata.org/schemas/grapher-schema.005.json"
+$id: "https://files.ourworldindata.org/schemas/grapher-schema.006.json"
required:
- $schema
- dimensions
@@ -14,13 +14,13 @@ properties:
type: string
description: Url of the concrete schema version to use to validate this document
format: uri
- default: "https://files.ourworldindata.org/schemas/grapher-schema.005.json"
+ default: "https://files.ourworldindata.org/schemas/grapher-schema.006.json"
# for now, we only validate configs in our database using this schema.
# since we expect all configs in our database to be valid against the latest schema,
# we restrict the $schema field to a single value, the latest schema version.
# if we ever need to validate configs against multiple schema versions,
# we can remove this constraint.
- const: "https://files.ourworldindata.org/schemas/grapher-schema.005.json"
+ const: "https://files.ourworldindata.org/schemas/grapher-schema.006.json"
id:
type: integer
description: Internal DB id. Useful internally for OWID but not required if just using grapher directly.
@@ -123,10 +123,6 @@ properties:
type: boolean
default: false
description: Exclude entities that do not belong in any color group
- hasChartTab:
- type: boolean
- default: true
- description: Whether to show the (non-map) chart tab
hideLegend:
type: boolean
default: false
@@ -368,19 +364,21 @@ properties:
title:
type: string
description: Big title text of the chart
- type:
- type: string
- description: Which type of chart should be shown (hasMapChart can be used to always also show a map chart)
- default: LineChart
- enum:
- - LineChart
- - ScatterPlot
- - StackedArea
- - DiscreteBar
- - StackedDiscreteBar
- - SlopeChart
- - StackedBar
- - Marimekko
+ types:
+ type: array
+ description: Which chart types should be shown
+ default: ["LineChart"]
+ items:
+ type: string
+ enum:
+ - LineChart
+ - ScatterPlot
+ - StackedArea
+ - DiscreteBar
+ - StackedDiscreteBar
+ - SlopeChart
+ - StackedBar
+ - Marimekko
hasMapTab:
type: boolean
default: false
diff --git a/packages/@ourworldindata/grapher/src/schema/migrations/migrations.ts b/packages/@ourworldindata/grapher/src/schema/migrations/migrations.ts
index 7a4fefa30f0..2a860c0cb9a 100644
--- a/packages/@ourworldindata/grapher/src/schema/migrations/migrations.ts
+++ b/packages/@ourworldindata/grapher/src/schema/migrations/migrations.ts
@@ -15,6 +15,7 @@ import {
getSchemaVersion,
isLatestVersion,
} from "./helpers"
+import { ChartTypeName } from "@ourworldindata/types"
// see https://github.com/owid/owid-grapher/commit/26f2a0d1790c71bdda7e12f284ca552945d2f6ef
const migrateFrom001To002 = (
@@ -60,6 +61,23 @@ const migrateFrom004To005 = (
return config
}
+const migrateFrom005To006 = (
+ config: AnyConfigWithValidSchema
+): AnyConfigWithValidSchema => {
+ const { type = ChartTypeName.LineChart, hasChartTab = true } = config
+
+ // add types field
+ if (!hasChartTab) config.chartTypes = []
+ else if (type !== ChartTypeName.LineChart) config.chartTypes = [type]
+
+ // remove deprecated fields
+ delete config.type
+ delete config.hasChartTab
+
+ config.$schema = createSchemaForVersion("006")
+ return config
+}
+
export const runMigration = (
config: AnyConfigWithValidSchema
): AnyConfigWithValidSchema => {
@@ -70,5 +88,6 @@ export const runMigration = (
.with("002", () => migrateFrom002To003(config))
.with("003", () => migrateFrom003To004(config))
.with("004", () => migrateFrom004To005(config))
+ .with("005", () => migrateFrom005To006(config))
.exhaustive()
}
diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.jsdom.test.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.jsdom.test.tsx
index c4650faccd6..306f5376524 100644
--- a/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.jsdom.test.tsx
+++ b/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.jsdom.test.tsx
@@ -23,7 +23,7 @@ it("can filter years correctly", () => {
// TODO: why is it ySlugs and xSlug here instead of yColumnSlugs and xColumnSlug? Unify when we have config migrations?
const manager = {
- type: ChartTypeName.Marimekko,
+ chartTypes: [ChartTypeName.Marimekko],
table,
selection: table.availableEntityNames,
ySlugs: "percentBelow2USD",
@@ -133,7 +133,7 @@ it("shows no data points at the end", () => {
// TODO: why is it ySlugs and xSlug here instead of yColumnSlugs and xColumnSlug? Unify when we have config migrations?
const manager = {
- type: ChartTypeName.Marimekko,
+ chartTypes: [ChartTypeName.Marimekko],
table,
selection: table.availableEntityNames,
ySlugs: "percentBelow2USD",
@@ -233,7 +233,7 @@ test("interpolation works as expected", () => {
// TODO: why is it ySlugs and xSlug here instead of yColumnSlugs and xColumnSlug? Unify when we have config migrations?
const manager = {
- type: ChartTypeName.Marimekko,
+ chartTypes: [ChartTypeName.Marimekko],
table,
selection: table.availableEntityNames,
ySlugs: "percentBelow2USD",
@@ -344,7 +344,7 @@ it("can deal with y columns with missing values", () => {
// TODO: why is it ySlugs and xSlug here instead of yColumnSlugs and xColumnSlug? Unify when we have config migrations?
const manager = {
- type: ChartTypeName.Marimekko,
+ chartTypes: [ChartTypeName.Marimekko],
table,
selection: table.availableEntityNames,
ySlugs: "percentBelow2USD percentBelow10USD",
diff --git a/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts b/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts
index 618c07588d8..82b060b58b6 100644
--- a/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts
+++ b/packages/@ourworldindata/types/src/grapherTypes/GrapherTypes.ts
@@ -178,6 +178,37 @@ export enum GrapherTabOption {
table = "table",
}
+export enum GrapherTabQueryParam {
+ Chart = "chart",
+ Table = "table",
+ WorldMap = "map",
+
+ // chart types
+ LineChart = "line",
+ ScatterPlot = "scatter",
+ StackedArea = "stacked-area",
+ DiscreteBar = "discrete-bar",
+ StackedDiscreteBar = "stacked-discrete-bar",
+ SlopeChart = "slope",
+ StackedBar = "stacked-bar",
+ Marimekko = "marimekko",
+}
+
+export enum GrapherTabName {
+ Table = "Table",
+ WorldMap = "WorldMap",
+
+ // chart types
+ LineChart = "LineChart",
+ ScatterPlot = "ScatterPlot",
+ StackedArea = "StackedArea",
+ DiscreteBar = "DiscreteBar",
+ StackedDiscreteBar = "StackedDiscreteBar",
+ SlopeChart = "SlopeChart",
+ StackedBar = "StackedBar",
+ Marimekko = "Marimekko",
+}
+
export interface RelatedQuestionsConfig {
text: string
url: string
@@ -528,7 +559,7 @@ export interface MapConfigInterface {
// under the same rendering conditions it ought to remain visually identical
export interface GrapherInterface extends SortConfig {
$schema?: string
- type?: ChartTypeName
+ chartTypes?: ChartTypeName[]
id?: number
version?: number
slug?: string
@@ -556,7 +587,6 @@ export interface GrapherInterface extends SortConfig {
hideTimeline?: boolean
zoomToSelection?: boolean
showYearLabels?: boolean // Always show year in labels for bar charts
- hasChartTab?: boolean
hasMapTab?: boolean
tab?: GrapherTabOption
relatedQuestions?: RelatedQuestionsConfig[]
@@ -647,7 +677,7 @@ export const GRAPHER_QUERY_PARAM_KEYS: (keyof LegacyGrapherQueryParams)[] = [
// Another approach we may want to try is this: https://github.com/mobxjs/serializr
export const grapherKeysToSerialize = [
"$schema",
- "type",
+ "chartTypes",
"id",
"version",
"slug",
@@ -673,7 +703,6 @@ export const grapherKeysToSerialize = [
"hideTimeline",
"zoomToSelection",
"showYearLabels",
- "hasChartTab",
"hasMapTab",
"tab",
"internalNotes",
diff --git a/packages/@ourworldindata/types/src/index.ts b/packages/@ourworldindata/types/src/index.ts
index 5decf69e009..ea93c38d22b 100644
--- a/packages/@ourworldindata/types/src/index.ts
+++ b/packages/@ourworldindata/types/src/index.ts
@@ -76,6 +76,8 @@ export {
colorScaleConfigDefaults,
ChartTypeName,
GrapherTabOption,
+ GrapherTabName,
+ GrapherTabQueryParam,
StackMode,
EntitySelectionMode,
ScatterPointLabelStrategy,
diff --git a/packages/@ourworldindata/utils/src/Util.ts b/packages/@ourworldindata/utils/src/Util.ts
index 5c93748b3fd..ff35f748e57 100644
--- a/packages/@ourworldindata/utils/src/Util.ts
+++ b/packages/@ourworldindata/utils/src/Util.ts
@@ -926,6 +926,9 @@ export const differenceOfSets = (sets: Set[]): Set => {
return diff
}
+export const areSetsEqual = (setA: Set, setB: Set): boolean =>
+ setA.size === setB.size && [...setA].every((value) => setB.has(value))
+
/** Tests whether the first argument is a strict subset of the second. The arguments do not have
to be sets yet, they can be any iterable. Sets will be created by the function internally */
export function isSubsetOf(
@@ -1966,9 +1969,10 @@ export function traverseObjects>(
export function getParentVariableIdFromChartConfig(
config: GrapherInterface // could be a patch config
): number | undefined {
- const { type = ChartTypeName.LineChart, dimensions } = config
+ const { chartTypes, dimensions } = config
- if (type === ChartTypeName.ScatterPlot) return undefined
+ const mainChartType = chartTypes?.[0] ?? ChartTypeName.LineChart
+ if (mainChartType === ChartTypeName.ScatterPlot) return undefined
if (!dimensions) return undefined
const yVariableIds = dimensions
diff --git a/packages/@ourworldindata/utils/src/index.ts b/packages/@ourworldindata/utils/src/index.ts
index eacf00917fa..75f99afdd16 100644
--- a/packages/@ourworldindata/utils/src/index.ts
+++ b/packages/@ourworldindata/utils/src/index.ts
@@ -62,6 +62,7 @@ export {
intersectionOfSets,
unionOfSets,
differenceOfSets,
+ areSetsEqual,
isSubsetOf,
intersection,
sortByUndefinedLast,
diff --git a/site/gdocs/components/Chart.tsx b/site/gdocs/components/Chart.tsx
index dbc57a3f973..c4070b96c38 100644
--- a/site/gdocs/components/Chart.tsx
+++ b/site/gdocs/components/Chart.tsx
@@ -1,8 +1,8 @@
import React, { useRef } from "react"
import { useEmbedChart } from "../../hooks.js"
import {
- grapherInterfaceWithHiddenControlsOnly,
- grapherInterfaceWithHiddenTabsOnly,
+ grapherInterfaceWithHiddenControls,
+ grapherInterfaceWithHiddenTabs,
GrapherProgrammaticInterface,
} from "@ourworldindata/grapher"
import {
@@ -12,6 +12,7 @@ import {
identity,
Url,
merge,
+ excludeUndefined,
} from "@ourworldindata/utils"
import { ChartConfigType } from "@ourworldindata/types"
import { renderSpans, useLinkedChart } from "../utils.js"
@@ -54,17 +55,26 @@ export default function Chart({
if (!isExplorer && isCustomized) {
const controls: ChartControlKeyword[] = d.controls || []
const tabs: ChartTabKeyword[] = d.tabs || []
+
const showAllControls = controls.includes(ChartControlKeyword.all)
const showAllTabs = tabs.includes(ChartTabKeyword.all)
- const listOfPartialGrapherConfigs = [...controls, ...tabs]
- .map(mapKeywordToGrapherConfig)
- .filter(identity) as GrapherProgrammaticInterface[]
+
+ const allControlsHidden = grapherInterfaceWithHiddenControls
+ const allTabsHidden = grapherInterfaceWithHiddenTabs
+
+ const enabledControls = excludeUndefined(
+ controls.map(mapControlKeywordToGrapherConfig)
+ )
+ const enabledTabs = excludeUndefined(
+ tabs.map(mapTabKeywordToGrapherConfig)
+ )
customizedChartConfig = merge(
{},
- !showAllControls ? grapherInterfaceWithHiddenControlsOnly : {},
- !showAllTabs ? grapherInterfaceWithHiddenTabsOnly : {},
- ...listOfPartialGrapherConfigs,
+ !showAllControls ? allControlsHidden : {},
+ !showAllTabs ? allTabsHidden : {},
+ ...enabledControls,
+ ...enabledTabs,
{
hideRelatedQuestion: true,
hideShareButton: true, // always hidden since the original chart would be shared, not the customized one
@@ -134,12 +144,10 @@ export default function Chart({
)
}
-const mapKeywordToGrapherConfig = (
- keyword: ChartControlKeyword | ChartTabKeyword
-): GrapherProgrammaticInterface | null => {
+const mapControlKeywordToGrapherConfig = (
+ keyword: ChartControlKeyword
+): GrapherProgrammaticInterface | undefined => {
switch (keyword) {
- // controls
-
case ChartControlKeyword.relativeToggle:
return { hideRelativeToggle: false }
@@ -173,18 +181,25 @@ const mapKeywordToGrapherConfig = (
case ChartControlKeyword.tableFilterToggle:
return { hideTableFilterToggle: false }
- // tabs
+ default:
+ return undefined
+ }
+}
- case ChartTabKeyword.chart:
- return { hasChartTab: true }
+const mapTabKeywordToGrapherConfig = (
+ keyword: ChartTabKeyword
+): GrapherProgrammaticInterface | undefined => {
+ switch (keyword) {
+ case ChartTabKeyword.table:
+ return { hasTableTab: true }
case ChartTabKeyword.map:
return { hasMapTab: true }
- case ChartTabKeyword.table:
- return { hasTableTab: true }
+ case ChartTabKeyword.chart:
+ return { hideChartTabs: false }
default:
- return null
+ return undefined
}
}