Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ (grapher) support multiple chart types / TAS-694 #4140

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions adminSiteClient/ChartList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import { observer } from "mobx-react"
import { runInAction, observable } from "mobx"
import { bind } from "decko"
import { AdminAppContext, AdminAppContextType } from "./AdminAppContext.js"
import { ChartTypeName, GrapherInterface } from "@ourworldindata/types"
import {
ChartTypeName,
GrapherInterface,
GrapherTabOption,
} from "@ourworldindata/types"
import { startCase, DbChartTagJoin } from "@ourworldindata/utils"
import { References, getFullReferencesCount } from "./ChartEditor.js"
import { ChartRow } from "./ChartRow.js"
Expand All @@ -14,13 +18,14 @@ export interface ChartListItem {
id: GrapherInterface["id"]
title: GrapherInterface["title"]
slug: GrapherInterface["slug"]
type: GrapherInterface["type"]
internalNotes: GrapherInterface["internalNotes"]
variantName: GrapherInterface["variantName"]
isPublished: GrapherInterface["isPublished"]
tab: GrapherInterface["tab"]
hasChartTab: GrapherInterface["hasChartTab"]
hasMapTab: GrapherInterface["hasMapTab"]

type: ChartTypeName
hasChartTab: boolean
hasMapTab: boolean

lastEditedAt: string
lastEditedBy: string
Expand Down Expand Up @@ -142,13 +147,13 @@ export class ChartList extends React.Component<{
}
}

export function showChartType(chart: ChartListItem) {
export function showChartType(chart: ChartListItem): string {
const chartType = chart.type ?? ChartTypeName.LineChart
const displayType = ChartTypeName[chartType]
? startCase(ChartTypeName[chartType])
: "Unknown"

if (chart.tab === "map") {
if (chart.tab === GrapherTabOption.WorldMap) {
if (chart.hasChartTab) return `Map + ${displayType}`
else return "Map"
} else {
Expand Down
64 changes: 55 additions & 9 deletions adminSiteClient/EditorBasicTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import { observer } from "mobx-react"
import {
ChartTypeName,
EntitySelectionMode,
GrapherTabOption,
StackMode,
} from "@ourworldindata/types"
import {
DimensionSlot,
WorldEntityName,
CONTINENTS_INDICATOR_ID,
POPULATION_INDICATOR_ID_USED_IN_ADMIN,
allChartTypes,
} from "@ourworldindata/grapher"
import {
DimensionProperty,
Expand Down Expand Up @@ -147,7 +149,10 @@ class DimensionSlotView<
() => this.grapher.isReady,
() => {
this.disposers.push(
reaction(() => this.grapher.type, this.updateDefaults),
reaction(
() => this.grapher.availableTabs,
this.updateDefaults
),
reaction(
() => this.grapher.yColumnsFromDimensions.length,
this.updateDefaults
Expand Down Expand Up @@ -355,7 +360,20 @@ export class EditorBasicTab<

@action.bound onChartTypeChange(value: string) {
const { grapher } = this.props.editor
grapher.type = value as ChartTypeName

const newChartType = value as ChartTypeName
const newTabs: ChartTypeName[] = (grapher.availableTabs ?? []).filter(
(tab) => tab !== ChartTypeName.WorldMap
)
newTabs.push(newChartType)
grapher.availableTabs = newTabs

if (
grapher.tab !== GrapherTabOption.Table &&
grapher.tab !== GrapherTabOption.WorldMap
) {
grapher.tab = newChartType as unknown as GrapherTabOption
}

if (grapher.isMarimekko) {
grapher.hideRelativeToggle = false
Expand Down Expand Up @@ -395,9 +413,6 @@ export class EditorBasicTab<
render() {
const { editor } = this.props
const { grapher } = editor
const chartTypes = Object.keys(ChartTypeName).filter(
(chartType) => chartType !== ChartTypeName.WorldMap
)

const isIndicatorChart = isIndicatorChartEditorInstance(editor)

Expand All @@ -407,9 +422,9 @@ export class EditorBasicTab<

<Section name="Type of chart">
<SelectField
value={grapher.type}
value={grapher.firstChartType}
onValue={this.onChartTypeChange}
options={chartTypes.map((key) => ({
options={allChartTypes.map((key) => ({
value: key,
label: startCase(key),
}))}
Expand All @@ -418,12 +433,43 @@ export class EditorBasicTab<
<Toggle
label="Chart tab"
value={grapher.hasChartTab}
onValue={(value) => (grapher.hasChartTab = value)}
onValue={(value) => {
if (value) {
// add line chart tab
grapher.availableTabs =
grapher.availableTabs ?? []
grapher.availableTabs.push(
ChartTypeName.LineChart
)
} else {
// remove all chart tabs
grapher.availableTabs =
grapher.availableTabs?.filter(
(tab) =>
tab === ChartTypeName.WorldMap
)
}
}}
/>
<Toggle
label="Map tab"
value={grapher.hasMapTab}
onValue={(value) => (grapher.hasMapTab = value)}
onValue={(value) => {
if (value) {
// add map tab
grapher.availableTabs = [
ChartTypeName.WorldMap,
...(grapher.availableTabs ?? []),
]
} else {
// remove map tab
grapher.availableTabs =
grapher.availableTabs?.filter(
(tab) =>
tab !== ChartTypeName.WorldMap
)
}
}}
/>
</FieldsRow>
</Section>
Expand Down
4 changes: 2 additions & 2 deletions adminSiteClient/EditorCustomizeTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export class ColorSchemeSelector extends React.Component<{
value={grapher.baseColorScheme}
onChange={this.onChange}
onBlur={this.onBlur}
chartType={this.props.grapher.type}
chartType={this.props.grapher.chartType}
invertedColorScheme={!!grapher.invertColorScheme}
additionalOptions={[
{
Expand Down Expand Up @@ -751,7 +751,7 @@ export class EditorCustomizeTab<
{grapher.chartInstanceExceptMap.colorScale && (
<EditorColorScaleSection
scale={grapher.chartInstanceExceptMap.colorScale}
chartType={grapher.type}
chartType={grapher.chartType}
showLineChartColors={grapher.isLineChart}
features={{
visualScaling: true,
Expand Down
2 changes: 1 addition & 1 deletion adminSiteClient/GrapherConfigGridEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ export class GrapherConfigGridEditor extends React.Component<GrapherConfigGridEd
return (
<EditorColorScaleSection
scale={colorScale}
chartType={grapher.type}
chartType={grapher.chartType}
features={{
visualScaling: true,
legendDescription: false,
Expand Down
17 changes: 11 additions & 6 deletions adminSiteClient/VariableEditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
GrapherTabOption,
GrapherInterface,
OwidVariableRoundingMode,
ChartTypeName,
} from "@ourworldindata/types"
import { Grapher } from "@ourworldindata/grapher"
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons"
Expand Down Expand Up @@ -687,18 +688,22 @@ class VariableEditor extends React.Component<{
@computed private get grapherConfig(): GrapherInterface {
const { variable } = this.props
const grapherConfig = variable.grapherConfig
if (grapherConfig)
if (grapherConfig) {
let { availableTabs = [] } = grapherConfig
if (!availableTabs.includes(ChartTypeName.WorldMap)) {
availableTabs = [ChartTypeName.WorldMap, ...availableTabs]
}
return {
...grapherConfig,
hasMapTab: true,
tab: GrapherTabOption.map,
availableTabs,
tab: GrapherTabOption.WorldMap,
}
else
} else
return {
yAxis: { min: 0 },
map: { columnSlug: this.props.variable.id.toString() },
tab: GrapherTabOption.map,
hasMapTab: true,
tab: GrapherTabOption.WorldMap,
availableTabs: [ChartTypeName.WorldMap],
dimensions: [
{
property: DimensionProperty.y,
Expand Down
35 changes: 21 additions & 14 deletions adminSiteServer/testPageRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ async function propsFromQueryParams(

if (params.type) {
if (params.type === ChartTypeName.WorldMap) {
// TODO: add hasMapTab to chart configs table?
query = query.andWhereRaw(`cc.full->>"$.hasMapTab" = "true"`)
tab = tab || GrapherTabOption.map
tab = tab || GrapherTabOption.WorldMap
} else {
if (params.type === "LineChart") {
query = query.andWhereRaw(
Expand All @@ -165,34 +166,39 @@ async function propsFromQueryParams(
{ type: params.type }
)
}
tab = tab || GrapherTabOption.chart
// TODO
// tab = tab || GrapherTabOption.chart
}
}

if (params.logLinear) {
query = query.andWhereRaw(
`cc.full->>'$.yAxis.canChangeScaleType' = "true" OR cc.full->>'$.xAxis.canChangeScaleType' = "true"`
)
tab = GrapherTabOption.chart
// TODO
// tab = GrapherTabOption.chart
}

if (params.comparisonLines) {
query = query.andWhereRaw(
`cc.full->'$.comparisonLines[0].yEquals' != ''`
)
tab = GrapherTabOption.chart
// TODO
// tab = GrapherTabOption.chart
}

if (params.stackMode) {
query = query.andWhereRaw(`cc.full->'$.stackMode' = :stackMode`, {
stackMode: params.stackMode,
})
tab = GrapherTabOption.chart
// TODO
// tab = GrapherTabOption.chart
}

if (params.relativeToggle) {
query = query.andWhereRaw(`cc.full->>'$.hideRelativeToggle' = "false"`)
tab = GrapherTabOption.chart
// TODO
// tab = GrapherTabOption.chart
}

if (params.categoricalLegend) {
Expand All @@ -202,7 +208,7 @@ async function propsFromQueryParams(
query = query.andWhereRaw(
`json_length(cc.full->'$.map.colorScale.customCategoryColors') > 1`
)
tab = GrapherTabOption.map
tab = GrapherTabOption.WorldMap
}

if (params.mixedTimeTypes) {
Expand Down Expand Up @@ -242,13 +248,14 @@ async function propsFromQueryParams(
query = query.andWhereRaw(`charts.id IN (${params.ids})`)
}

if (tab === GrapherTabOption.map) {
query = query.andWhereRaw(`cc.full->>"$.hasMapTab" = "true"`)
} else if (tab === GrapherTabOption.chart) {
query = query.andWhereRaw(
`COALESCE(cc.full->>"$.hasChartTab", "true") = "true"`
)
}
// TODO
// if (tab === GrapherTabOption.WorldMap) {
// query = query.andWhereRaw(`cc.full->>"$.hasMapTab" = "true"`)
// } else if (tab === GrapherTabOption.chart) {
// query = query.andWhereRaw(
// `COALESCE(cc.full->>"$.hasChartTab", "true") = "true"`
// )
// }

if (datasetIds.length > 0) {
const datasetIds = params.datasetIds
Expand Down
13 changes: 10 additions & 3 deletions baker/countryProfiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import {
CountryProfilePage,
} from "../site/CountryProfilePage.js"
import { SiteBaker } from "./SiteBaker.js"
import { countries, getCountryBySlug, JsonError } from "@ourworldindata/utils"
import {
countries,
getCountryBySlug,
JsonError,
getMainChartTypeFromConfig,
hasChartTabFromConfig,
} from "@ourworldindata/utils"
import { renderToHtmlPage } from "./siteRenderers.js"
import { dataAsDF } from "../db/model/Variable.js"
import pl from "nodejs-polars"
Expand All @@ -37,8 +43,9 @@ function bakeCache<T>(cacheKey: any, retriever: () => T): T {
}

const checkShouldShowIndicator = (grapher: GrapherInterface) =>
(grapher.hasChartTab ?? true) &&
(grapher.type ?? "LineChart") === "LineChart" &&
hasChartTabFromConfig(grapher) &&
// TODO: should this be the intial chart type instead?
getMainChartTypeFromConfig(grapher) === "LineChart" &&
grapher.dimensions?.length === 1

// Find the charts that will be shown on the country profile page (if they have that country)
Expand Down
4 changes: 2 additions & 2 deletions baker/updateChartEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ const obtainAvailableEntitiesForGrapherConfig = async (

// If the grapher has a chart tab, then the available entities there are the "most interesting" ones to us
if (grapher.hasChartTab) {
grapher.tab = GrapherTabOption.chart
grapher.tab = grapher.firstChartType as unknown as GrapherTabOption

// If the grapher allows for changing or multi-selecting entities, then let's index all entities the
// user can choose from. Otherwise, we'll just use the default-selected entities.
Expand All @@ -112,7 +112,7 @@ const obtainAvailableEntitiesForGrapherConfig = async (
return grapher.tableForSelection.availableEntityNames as string[]
else return grapher.selectedEntityNames ?? []
} else if (grapher.hasMapTab) {
grapher.tab = GrapherTabOption.map
grapher.tab = GrapherTabOption.WorldMap
// On a map tab, tableAfterAuthorTimelineAndActiveChartTransform contains all
// mappable entities for which data is available
return grapher.tableAfterAuthorTimelineAndActiveChartTransform
Expand Down
4 changes: 3 additions & 1 deletion db/migration/1661264304751-MigrateSelectedData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { MigrationInterface, QueryRunner } from "typeorm"

import { entityNameById } from "./data/entityNameById.js"

import { ChartTypeName, GrapherInterface } from "@ourworldindata/types"
import { ChartTypeName } from "@ourworldindata/types"

type GrapherInterface = Record<string, any>

/**
* Migrate the legacy `selectedData` and get rid of it.
Expand Down
Loading
Loading