Skip to content

Commit

Permalink
✨ (line chart) set y-axis min default to 0
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Oct 31, 2024
1 parent a3b1e38 commit 635477e
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 20 deletions.
8 changes: 4 additions & 4 deletions adminSiteClient/EditorCustomizeTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -577,9 +577,9 @@ export class EditorCustomizeTab<
}
resetButton={{
onClick: () =>
(yAxisConfig.min = undefined),
(yAxisConfig.min = Infinity),
disabled:
yAxisConfig.min === undefined,
yAxisConfig.min === Infinity,
}}
allowDecimal
allowNegative
Expand All @@ -592,9 +592,9 @@ export class EditorCustomizeTab<
}
resetButton={{
onClick: () =>
(yAxisConfig.max = undefined),
(yAxisConfig.max = -Infinity),
disabled:
yAxisConfig.max === undefined,
yAxisConfig.max === -Infinity,
}}
allowDecimal
allowNegative
Expand Down
95 changes: 95 additions & 0 deletions db/migration/1729763649580-SetYAxisMinDefaultToZero.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { defaultGrapherConfig } from "@ourworldindata/grapher"
import { mergeGrapherConfigs } from "@ourworldindata/utils"
import { MigrationInterface, QueryRunner } from "typeorm"

export class SetYAxisMinDefaultToZero1729763649580
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
// charts with inheritance disabled:
// set yAxis.min explicitly to "auto" for line charts
// that used to rely on "auto" being the default.
await queryRunner.query(`
-- sql
UPDATE chart_configs cc
JOIN charts c ON cc.id = c.configId
SET
-- using JSON_MERGE_PATCH instead of JSON_SET in case yAxis doesn't exist
cc.patch = JSON_MERGE_PATCH(cc.patch, '{"yAxis":{"min":"auto"}}')
WHERE
cc.full ->> '$.type' = 'LineChart'
AND c.isInheritanceEnabled IS FALSE
AND cc.patch ->> '$.yAxis.min' IS NULL
`)

// charts with inheritance enabled:
// set yAxis.min explicitly to "auto" for line charts
// that used to rely on "auto" being the default.
// this is the case for charts where neither the patch config
// of the chart itself nor the indicator config have yAxis.min set
await queryRunner.query(`
-- sql
UPDATE chart_configs cc
JOIN charts c ON cc.id = c.configId
-- keep charts without parent indicator
LEFT JOIN charts_x_parents cxp ON cxp.chartId = c.id
LEFT JOIN variables v ON v.id = cxp.variableId
LEFT JOIN chart_configs cc_admin ON cc_admin.id = v.grapherConfigIdAdmin
SET
-- using JSON_MERGE_PATCH instead of JSON_SET in case yAxis doesn't exist
cc.patch = JSON_MERGE_PATCH(cc.patch, '{"yAxis":{"min":"auto"}}')
WHERE
cc.full ->> '$.type' = 'LineChart'
AND c.isInheritanceEnabled IS TRUE
AND cc.patch ->> '$.yAxis.min' IS NULL
AND (
cc_admin.full IS NULL
OR cc_admin.full ->> '$.yAxis.min' IS NULL
)
`)

// recompute all full configs (we need to update all charts
// since the defaultGrapherConfig has changed)
const charts = await queryRunner.query(`
-- sql
SELECT
cc.id AS configId,
cc.patch AS patchConfig,
cc_etl.patch AS etlConfig,
cc_admin.patch AS adminConfig
FROM charts c
JOIN chart_configs cc ON cc.id = c.configId
LEFT JOIN charts_x_parents cxp ON cxp.chartId = c.id
LEFT JOIN variables v ON v.id = cxp.variableId
LEFT JOIN chart_configs cc_etl ON cc_etl.id = v.grapherConfigIdETL
LEFT JOIN chart_configs cc_admin ON cc_admin.id = v.grapherConfigIdAdmin
`)
for (const chart of charts) {
const patchConfig = JSON.parse(chart.patchConfig)
const etlConfig = chart.etlConfig ? JSON.parse(chart.etlConfig) : {}
const adminConfig = chart.adminConfig
? JSON.parse(chart.adminConfig)
: {}

const fullConfig = mergeGrapherConfigs(
defaultGrapherConfig,
etlConfig,
adminConfig,
patchConfig
)

await queryRunner.query(
`
-- sql
UPDATE chart_configs cc
SET cc.full = ?
WHERE cc.id = ?
`,
[JSON.stringify(fullConfig), chart.configId]
)
}
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
public async down(): Promise<void> {}
}
4 changes: 2 additions & 2 deletions explorer/GrapherGrammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import {
EnumCellDef,
Grammar,
IntegerCellDef,
NumericCellDef,
SlugDeclarationCellDef,
SlugsDeclarationCellDef,
StringCellDef,
UrlCellDef,
IndicatorIdsOrEtlPathsCellDef,
IndicatorIdOrEtlPathCellDef,
GrapherCellDef,
NumericOrAutoCellDef,
} from "../gridLang/GridLangConstants.js"

const toTerminalOptions = (keywords: string[]): CellDef[] => {
Expand Down Expand Up @@ -176,7 +176,7 @@ export const GrapherGrammar: Grammar<GrapherCellDef> = {
toGrapherObject: (value) => ({ yAxis: { canChangeScaleType: value } }),
},
yAxisMin: {
...NumericCellDef,
...NumericOrAutoCellDef,
keyword: "yAxisMin",
description: "Set the minimum value for the yAxis",
toGrapherObject: (value) => ({ yAxis: { min: value } }),
Expand Down
13 changes: 13 additions & 0 deletions gridLang/GridLangConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,19 @@ export const NumericCellDef: CellDef = {
parse: (value: any) => parseFloat(value),
}

export const NumericOrAutoCellDef: CellDef = {
keyword: "",
cssClass: "NumericCellDef",
description: "",
regex: /^(-?\d+\.?\d*|auto)$/,
requirementsDescription: `Must be a number or "auto"`,
valuePlaceholder: "98.6",
parse: (value: any) => {
if (value === "auto") return "auto"
return parseFloat(value)
},
}

export const IntegerCellDef: CellDef = {
keyword: "",
cssClass: "IntegerCellDef",
Expand Down
8 changes: 4 additions & 4 deletions packages/@ourworldindata/grapher/src/axis/AxisConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import { AxisConfig } from "../axis/AxisConfig"
import { ScaleType } from "@ourworldindata/types"

it("serializes max to 'auto' if undefined", () => {
const axis = new AxisConfig({ min: 1 })
it("serializes max to 'auto' if -Infinity", () => {
const axis = new AxisConfig({ min: 1, max: -Infinity })
const obj = axis.toObject()
expect(obj.min).toBe(1)
expect(obj.max).toBe("auto")
})

it("serializes min to 'auto' if undefined", () => {
const axis = new AxisConfig({ max: 0 })
it("serializes min to 'auto' if Infinity", () => {
const axis = new AxisConfig({ max: 0, min: Infinity })
const obj = axis.toObject()
expect(obj.max).toBe(0)
expect(obj.min).toBe("auto")
Expand Down
19 changes: 13 additions & 6 deletions packages/@ourworldindata/grapher/src/axis/AxisConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,17 @@ class AxisConfigDefaults implements AxisConfigInterface {
@observable.ref domainValues?: number[] = undefined
}

function parseAutoOrNumberFromJSON(
function parseMinFromJSON(
value: AxisMinMaxValueStr.auto | number | undefined
): number | undefined {
if (value === AxisMinMaxValueStr.auto) return undefined
if (value === AxisMinMaxValueStr.auto) return Infinity
return value
}

function parseMaxFromJSON(
value: AxisMinMaxValueStr.auto | number | undefined
): number | undefined {
if (value === AxisMinMaxValueStr.auto) return -Infinity
return value
}

Expand All @@ -67,8 +74,8 @@ export class AxisConfig
// todo: test/refactor
updateFromObject(props?: AxisConfigInterface): void {
if (props) extend(this, props)
if (props?.min) this.min = parseAutoOrNumberFromJSON(props?.min)
if (props?.max) this.max = parseAutoOrNumberFromJSON(props?.max)
if (props?.min) this.min = parseMinFromJSON(props?.min)
if (props?.max) this.max = parseMaxFromJSON(props?.max)
}

toObject(): AxisConfigInterface {
Expand Down Expand Up @@ -96,8 +103,8 @@ export class AxisConfig

deleteRuntimeAndUnchangedProps(obj, new AxisConfigDefaults())

if (obj.min === undefined) obj.min = AxisMinMaxValueStr.auto
if (obj.max === undefined) obj.max = AxisMinMaxValueStr.auto
if (obj.min === Infinity) obj.min = AxisMinMaxValueStr.auto
if (obj.max === -Infinity) obj.max = AxisMinMaxValueStr.auto

return obj
}
Expand Down
2 changes: 2 additions & 0 deletions packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1344,6 +1344,8 @@ export class LineChart
// horizontal axis to be at the bottom of the chart.
// see https://github.com/owid/owid-grapher/pull/975#issuecomment-890798547
singleValueAxisPointAlign: AxisAlign.start,
// default to 0 if not set
min: 0,
...this.manager.yAxisConfig,
},
this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export const defaultGrapherConfig = {
maxTime: "latest",
yAxis: {
removePointsOutsideDomain: false,
min: "auto",
scaleType: "linear",
max: "auto",
canChangeScaleType: false,
Expand Down Expand Up @@ -66,7 +65,6 @@ export const defaultGrapherConfig = {
},
xAxis: {
removePointsOutsideDomain: false,
min: "auto",
scaleType: "linear",
max: "auto",
canChangeScaleType: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,8 +514,9 @@ $defs:
type: string
description: Axis label
min:
description: Minimum domain value of the axis. Inferred from data if set to "auto".
default: auto
description: |
Minimum domain value of the axis. Inferred from data if set to "auto".
Usually defaults to "auto", but defaults to 0 for line charts on the y-axis.
oneOf:
- type: number
- type: string
Expand Down

0 comments on commit 635477e

Please sign in to comment.