From 5ed52c6fc439c57f9d3c19294867e2c7a63b409e Mon Sep 17 00:00:00 2001 From: sophiamersmann Date: Thu, 19 Sep 2024 16:02:57 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(admin)=20allow=20axis=20min/max=20?= =?UTF-8?q?to=20be=20overwritten?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- adminShared/patchHelper.ts | 5 + adminSiteClient/AbstractChartEditor.ts | 14 +- adminSiteClient/EditorCustomizeTab.tsx | 211 ++++++++++-------- adminSiteClient/EditorDataTab.tsx | 10 +- adminSiteClient/EditorTextTab.tsx | 12 +- adminSiteClient/Forms.tsx | 2 +- devTools/schemaProcessor/columns.json | 16 +- .../grapher/src/axis/AxisConfig.ts | 15 +- .../src/schema/defaultGrapherConfig.ts | 4 + .../src/schema/grapher-schema.005.yaml | 22 +- .../types/src/grapherTypes/GrapherTypes.ts | 8 +- packages/@ourworldindata/types/src/index.ts | 1 + 12 files changed, 188 insertions(+), 132 deletions(-) diff --git a/adminShared/patchHelper.ts b/adminShared/patchHelper.ts index 5f4defb63d0..39b3d81498a 100644 --- a/adminShared/patchHelper.ts +++ b/adminShared/patchHelper.ts @@ -8,6 +8,11 @@ import { checkIsStringIndexable, } from "@ourworldindata/utils" +export function getValueRecursive(json: unknown, pointer: string[]): any { + const p = "/" + pointer.join("/") + return jsonpointer.find(json, p) +} + export function setValueRecursiveInplace( json: unknown, pointer: string[], diff --git a/adminSiteClient/AbstractChartEditor.ts b/adminSiteClient/AbstractChartEditor.ts index 0fda11075f3..8c94d8b564a 100644 --- a/adminSiteClient/AbstractChartEditor.ts +++ b/adminSiteClient/AbstractChartEditor.ts @@ -9,6 +9,7 @@ import { action, computed, observable, when } from "mobx" import { EditorFeatures } from "./EditorFeatures.js" import { Admin } from "./Admin.js" import { defaultGrapherConfig, Grapher } from "@ourworldindata/grapher" +import { getValueRecursive } from "../adminShared/patchHelper.js" export type EditorTab = | "basic" @@ -125,20 +126,19 @@ export abstract class AbstractChartEditor< this.grapher.updateAuthoredVersion(config) } - // only works for top-level properties - isPropertyInherited(property: keyof GrapherInterface): boolean { + isPropertyInherited(pointer: string[]): boolean { if (!this.isInheritanceEnabled || !this.activeParentConfigWithDefaults) return false return ( - !Object.hasOwn(this.patchConfig, property) && - Object.hasOwn(this.activeParentConfigWithDefaults, property) + getValueRecursive(this.patchConfig, pointer) === undefined && + getValueRecursive(this.activeParentConfigWithDefaults, pointer) !== + undefined ) } - // only works for top-level properties - couldPropertyBeInherited(property: keyof GrapherInterface): boolean { + couldPropertyBeInherited(pointer: string[]): boolean { if (!this.isInheritanceEnabled || !this.activeParentConfig) return false - return Object.hasOwn(this.activeParentConfig, property) + return getValueRecursive(this.activeParentConfig, pointer) !== undefined } abstract get isNewGrapher(): boolean diff --git a/adminSiteClient/EditorCustomizeTab.tsx b/adminSiteClient/EditorCustomizeTab.tsx index 831fb9f3659..1073ea80baa 100644 --- a/adminSiteClient/EditorCustomizeTab.tsx +++ b/adminSiteClient/EditorCustomizeTab.tsx @@ -6,7 +6,7 @@ import { ColorSchemeName, FacetAxisDomain, FacetStrategy, - GrapherInterface, + TimeBoundValueStr, } from "@ourworldindata/types" import { Grapher } from "@ourworldindata/grapher" import { @@ -29,6 +29,8 @@ import { SortOrder, SortBy, SortConfig, + minTimeBoundFromJSONOrNegativeInfinity, + maxTimeBoundFromJSONOrPositiveInfinity, } from "@ourworldindata/utils" import { faPlus, faMinus } from "@fortawesome/free-solid-svg-icons" import { FontAwesomeIcon } from "@fortawesome/react-fontawesome/index.js" @@ -40,25 +42,46 @@ import { EditorColorScaleSection } from "./EditorColorScaleSection.js" import Select from "react-select" import { AbstractChartEditor } from "./AbstractChartEditor.js" import { ErrorMessages } from "./ChartEditorTypes.js" +import { + getValueRecursive, + setValueRecursiveInplace, +} from "../adminShared/patchHelper.js" @observer -class TimeField< - T extends { [field: string]: any }, - K extends Extract, -> extends React.Component<{ +class BindAutoFloatToParent extends React.Component<{ editor: AbstractChartEditor - field: K - store: T + pointer: string[] label: string - defaultValue: number - defaultTextValue: TimeBoundValue + defaultValue: number | undefined + allowDecimal?: boolean + allowNegative?: boolean + parseConfigValue?: ( + value: string | number | undefined + ) => number | undefined }> { - private setValue(value: number) { - this.props.store[this.props.field] = value as any + private setValue(value: number | undefined) { + setValueRecursiveInplace( + this.props.editor.grapher, + this.props.pointer, + value + ) + } + + @computed get parseConfigValue() { + return this.props.parseConfigValue ?? ((value) => value as number) } @computed get currentValue(): number | undefined { - return this.props.store[this.props.field] + return getValueRecursive(this.props.editor.grapher, this.props.pointer) + } + + @computed get inheritedValue(): number | undefined { + return this.parseConfigValue( + getValueRecursive( + this.props.editor.activeParentConfig, + this.props.pointer + ) + ) } @action.bound onChange(value: number | undefined) { @@ -72,26 +95,18 @@ class TimeField< } render() { - const { editor, label, defaultTextValue, defaultValue } = this.props - - const field = this.props.field as keyof GrapherInterface + const { editor, label, defaultValue } = this.props - const inheritedValue = editor.activeParentConfig?.[field] as number - const autoValue = - inheritedValue === defaultTextValue - ? defaultValue - : inheritedValue ?? defaultValue - - const input = editor.couldPropertyBeInherited(field) ? ( + const input = editor.couldPropertyBeInherited(this.props.pointer) ? ( store[field]} + readFn={(store) => getValueRecursive(store, this.props.pointer)} writeFn={(store, newVal) => - (store[this.props.field] = newVal as any) + setValueRecursiveInplace(store, this.props.pointer, newVal) } - auto={autoValue} - isAuto={editor.isPropertyInherited(field)} - store={this.props.store} + auto={this.inheritedValue} + isAuto={editor.isPropertyInherited(this.props.pointer)} + store={this.props.editor.grapher} onBlur={this.onBlur} tooltipText={{ auto: "Linked to parent value. Unlink by editing", @@ -101,9 +116,10 @@ class TimeField< ) : ( ) @@ -411,45 +427,53 @@ class TimelineSection<
{features.timeDomain && ( - )} - {features.timelineRange && ( - - )} @@ -549,6 +573,7 @@ export class EditorCustomizeTab< const xAxisConfig = this.props.editor.grapher.xAxis const yAxisConfig = this.props.editor.grapher.yAxis + const { editor } = this.props const { features, activeParentConfig } = this.props.editor const { grapher } = this.props.editor @@ -559,35 +584,31 @@ export class EditorCustomizeTab< {features.canCustomizeYAxisScale && ( - - (yAxisConfig.min = value) - } - onBlur={() => { - if (yAxisConfig.min === undefined) { - yAxisConfig.min = - activeParentConfig?.yAxis?.min - } - }} + - - (yAxisConfig.max = value) + parseConfigValue={(value) => + value === "auto" + ? undefined + : (value as number) } - onBlur={() => { - if (yAxisConfig.max === undefined) { - yAxisConfig.max = - activeParentConfig?.yAxis?.max - } - }} + /> + + value === "auto" + ? undefined + : (value as number) + } /> {features.canRemovePointsOutsideAxisDomain && ( @@ -644,35 +665,31 @@ export class EditorCustomizeTab< {features.canCustomizeXAxisScale && ( - - (xAxisConfig.min = value) - } - onBlur={() => { - if (xAxisConfig.min === undefined) { - xAxisConfig.min = - activeParentConfig?.yAxis?.min - } - }} + - - (xAxisConfig.max = value) + parseConfigValue={(value) => + value === "auto" + ? undefined + : (value as number) } - onBlur={() => { - if (xAxisConfig.max === undefined) { - xAxisConfig.max = - activeParentConfig?.yAxis?.max - } - }} + /> + + value === "auto" + ? undefined + : (value as number) + } /> {features.canRemovePointsOutsideAxisDomain && ( diff --git a/adminSiteClient/EditorDataTab.tsx b/adminSiteClient/EditorDataTab.tsx index f36cd92fc3b..b4005e77b5f 100644 --- a/adminSiteClient/EditorDataTab.tsx +++ b/adminSiteClient/EditorDataTab.tsx @@ -130,9 +130,9 @@ export class KeysSection extends React.Component<{ const { selection } = grapher const { unselectedEntityNames, selectedEntityNames } = selection - const isEntitySelectionInherited = editor.isPropertyInherited( - "selectedEntityNames" - ) + const isEntitySelectionInherited = editor.isPropertyInherited([ + "selectedEntityNames", + ]) return (
@@ -144,7 +144,9 @@ export class KeysSection extends React.Component<{ .concat(unselectedEntityNames) .map((key) => ({ value: key }))} /> - {editor.couldPropertyBeInherited("selectedEntityNames") && ( + {editor.couldPropertyBeInherited([ + "selectedEntityNames", + ]) && (