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) make charts inherit indicator-level settings / TAS-567 #3793

Merged
merged 39 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
cbba53e
🎉 (grapher) make charts inherit indicator-level settings
sophiamersmann Jul 19, 2024
e417f30
🔨 adapt this PR to string based UUIDs v7
danyx23 Aug 2, 2024
52e02bb
🐝 make migration faster
sophiamersmann Aug 6, 2024
0e7e409
🔨 (grapher) remove DEFAULT_GRAPHER_CONFIG_SCHEMA
sophiamersmann Aug 7, 2024
939209f
💄 add a comment for const value of $schema
sophiamersmann Aug 8, 2024
d0fb7f6
💄 add a comment for required keys of the schema
sophiamersmann Aug 8, 2024
493e728
🐝 wip
sophiamersmann Aug 8, 2024
f465c6d
🐝 wip clean up
sophiamersmann Aug 9, 2024
1c124e1
✅ add more chart inheritance tests
sophiamersmann Aug 9, 2024
55226a9
🚧 wip barebones inheritance tab
sophiamersmann Aug 9, 2024
9f58049
✨ make inheritance opt-in
sophiamersmann Aug 13, 2024
584c751
✅ fix tests
sophiamersmann Aug 13, 2024
1b26014
✨ use a flag to enable/disable inheritance for charts
sophiamersmann Aug 14, 2024
43dd003
🔨 rename view to charts_x_parents
sophiamersmann Aug 14, 2024
dffa0ea
fix rebase
sophiamersmann Aug 15, 2024
aac6479
cleanup
sophiamersmann Aug 15, 2024
619c457
remove topicIds from grapher
sophiamersmann Aug 15, 2024
bcdb059
reset entity selection to the parent value
sophiamersmann Aug 15, 2024
e388432
populate auto fields with parent values
sophiamersmann Aug 15, 2024
5e15850
fix error messages in admin
sophiamersmann Aug 15, 2024
c86ec2b
minor improvements
sophiamersmann Aug 16, 2024
e4ce135
fix
sophiamersmann Aug 16, 2024
74013e3
Set fields to default value on blur
sophiamersmann Aug 19, 2024
ca8f90a
Fix
sophiamersmann Aug 20, 2024
86ca04c
Fix tests
sophiamersmann Aug 21, 2024
798f6fb
Clean up
sophiamersmann Aug 21, 2024
01247f8
fix title issue
sophiamersmann Sep 2, 2024
3dc4762
make inheritance opt-out
sophiamersmann Sep 2, 2024
6eb8170
improve readability of migration
sophiamersmann Sep 2, 2024
f616ca1
use serializeChartConfig
sophiamersmann Sep 3, 2024
91a7fe1
🎉 (admin) add indicator chart editor
sophiamersmann Aug 20, 2024
af587c2
✨ (admin) disallow disabling inheritance
sophiamersmann Sep 3, 2024
73ac420
✨ small improvements
sophiamersmann Sep 3, 2024
aed8179
allow disabling inheritance from the admin
sophiamersmann Sep 4, 2024
c37bcf8
Fix
sophiamersmann Sep 4, 2024
d629147
Add inheritance column to charts list
sophiamersmann Sep 4, 2024
9f9852b
Add db tests for grapherConfigAdmin
sophiamersmann Sep 4, 2024
d3d9b8a
🐛 new charts should inherit by default
sophiamersmann Sep 5, 2024
c6f3fbd
✅ update tests
sophiamersmann Sep 5, 2024
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
148 changes: 148 additions & 0 deletions adminSiteClient/AbstractChartEditor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import {
isEqual,
omit,
GrapherInterface,
diffGrapherConfigs,
mergeGrapherConfigs,
} from "@ourworldindata/utils"
import { action, computed, observable, when } from "mobx"
import { EditorFeatures } from "./EditorFeatures.js"
import { Admin } from "./Admin.js"
import { defaultGrapherConfig, Grapher } from "@ourworldindata/grapher"

export type EditorTab =
| "basic"
| "data"
| "text"
| "customize"
| "map"
| "scatter"
| "marimekko"
| "revisions"
| "refs"
| "export"
| "inheritance"
| "debug"

export interface AbstractChartEditorManager {
admin: Admin
patchConfig: GrapherInterface
parentConfig?: GrapherInterface
isInheritanceEnabled?: boolean
}

export abstract class AbstractChartEditor<
Manager extends AbstractChartEditorManager = AbstractChartEditorManager,
> {
manager: Manager

@observable.ref grapher = new Grapher()
@observable.ref currentRequest: Promise<any> | undefined // Whether the current chart state is saved or not
@observable.ref tab: EditorTab = "basic"
@observable.ref errorMessage?: { title: string; content: string }
@observable.ref previewMode: "mobile" | "desktop"
@observable.ref showStaticPreview = false
@observable.ref savedPatchConfig: GrapherInterface = {}

// parent config derived from the current chart config
@observable.ref parentConfig: GrapherInterface | undefined = undefined
// if inheritance is enabled, the parent config is applied to grapher
@observable.ref isInheritanceEnabled: boolean | undefined = undefined

constructor(props: { manager: Manager }) {
this.manager = props.manager
this.previewMode =
localStorage.getItem("editorPreviewMode") === "mobile"
? "mobile"
: "desktop"

when(
() => this.manager.parentConfig !== undefined,
() => (this.parentConfig = this.manager.parentConfig)
)

when(
() => this.manager.isInheritanceEnabled !== undefined,
() =>
(this.isInheritanceEnabled = this.manager.isInheritanceEnabled)
)

when(
() => this.grapher.hasData && this.grapher.isReady,
() => (this.savedPatchConfig = this.patchConfig)
)
}

/** original grapher config used to init the grapher instance */
@computed get originalGrapherConfig(): GrapherInterface {
const { patchConfig, parentConfig, isInheritanceEnabled } = this.manager
if (!isInheritanceEnabled) return patchConfig
return mergeGrapherConfigs(parentConfig ?? {}, patchConfig)
}

/** live-updating full config */
@computed get fullConfig(): GrapherInterface {
return mergeGrapherConfigs(defaultGrapherConfig, this.grapher.object)
}

/** parent config currently applied to grapher */
@computed get activeParentConfig(): GrapherInterface | undefined {
return this.isInheritanceEnabled ? this.parentConfig : undefined
}

@computed get activeParentConfigWithDefaults():
| GrapherInterface
| undefined {
if (!this.activeParentConfig) return undefined
return mergeGrapherConfigs(
defaultGrapherConfig,
this.activeParentConfig
)
}

/** patch config of the chart that is written to the db on save */
@computed get patchConfig(): GrapherInterface {
return diffGrapherConfigs(
this.fullConfig,
this.activeParentConfigWithDefaults ?? defaultGrapherConfig
)
}

@computed get isModified(): boolean {
return !isEqual(
omit(this.patchConfig, "version"),
omit(this.savedPatchConfig, "version")
)
}

@computed get features(): EditorFeatures {
return new EditorFeatures(this)
}

@action.bound updateLiveGrapher(config: GrapherInterface): void {
this.grapher.reset()
this.grapher.updateFromObject(config)
this.grapher.updateAuthoredVersion(config)
}

// only works for top-level properties
isPropertyInherited(property: keyof GrapherInterface): boolean {
if (!this.isInheritanceEnabled || !this.activeParentConfigWithDefaults)
return false
return (
!Object.hasOwn(this.patchConfig, property) &&
Object.hasOwn(this.activeParentConfigWithDefaults, property)
)
}

// only works for top-level properties
couldPropertyBeInherited(property: keyof GrapherInterface): boolean {
if (!this.isInheritanceEnabled || !this.activeParentConfig) return false
return Object.hasOwn(this.activeParentConfig, property)
}

abstract get isNewGrapher(): boolean
abstract get availableTabs(): EditorTab[]

abstract saveGrapher(props?: { onError?: () => void }): Promise<void>
}
12 changes: 12 additions & 0 deletions adminSiteClient/AdminApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { BulkGrapherConfigEditorPage } from "./BulkGrapherConfigEditor.js"
import { GdocsIndexPage, GdocsMatchProps } from "./GdocsIndexPage.js"
import { GdocsPreviewPage } from "./GdocsPreviewPage.js"
import { GdocsStoreProvider } from "./GdocsStore.js"
import { IndicatorChartEditorPage } from "./IndicatorChartEditorPage.js"

@observer
class AdminErrorMessage extends React.Component<{ admin: Admin }> {
Expand Down Expand Up @@ -200,6 +201,17 @@ export class AdminApp extends React.Component<{
path="/users"
component={UsersIndexPage}
/>
<Route
exact
path="/variables/:variableId/config"
render={({ match }) => (
<IndicatorChartEditorPage
variableId={parseInt(
match.params.variableId
)}
/>
)}
/>
<Route
exact
path="/variables/:variableId"
Expand Down
Loading