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

🚧 (for testing) fix Multiembedder on staging #4243

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
af40bb8
🎉 (line+slope) add focus state
sophiamersmann Dec 9, 2024
0e460cc
🎉 (admin) edit focused entities/columns
sophiamersmann Dec 9, 2024
aa351c3
✨ (line) make labels of focused entities bold
sophiamersmann Dec 10, 2024
89ad13d
🐛 dismiss focused entities when selection is cleared
sophiamersmann Dec 10, 2024
9cfb913
🔨 rename InteractionArray to FocusArray
sophiamersmann Dec 11, 2024
7d379e1
🐛 ensure series names are shared between line and slope charts
sophiamersmann Dec 12, 2024
f3073fc
🔨 move focus array to its own folder
sophiamersmann Dec 12, 2024
ac77a6d
🧪 add focus array tests
sophiamersmann Dec 12, 2024
2b31e70
🧪 fix Grapher tests
sophiamersmann Dec 12, 2024
5cebec3
✨ ensure the focused series is labelled
sophiamersmann Dec 12, 2024
a099ec5
✨ (line+slope) hide outlines for background series
sophiamersmann Dec 12, 2024
93113f8
🐛 gray out non-focused multi-color lines
sophiamersmann Dec 12, 2024
1cb3c26
🔨 refactor url parsing/serialising
sophiamersmann Dec 12, 2024
346e057
✨ persist focused entities for chart views
sophiamersmann Dec 12, 2024
2534b49
🔨 clean up
sophiamersmann Dec 13, 2024
bc03664
🐛 fix order of render series
sophiamersmann Dec 13, 2024
efd5d15
✨ (admin) improve support for focusing series
sophiamersmann Dec 13, 2024
bb2e1b3
✨ make unfocused lines a little thinner
sophiamersmann Dec 13, 2024
3b45209
🔨 clean up
sophiamersmann Dec 13, 2024
98b1288
✨ (slope) improve svg output for figma
sophiamersmann Dec 12, 2024
2dfcaae
🔨 add empty arrays to the default grapher config
sophiamersmann Dec 12, 2024
55288dc
🚧 (for testing) fix Multiembedder on staging
sophiamersmann Dec 2, 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
22 changes: 21 additions & 1 deletion adminSiteClient/AbstractChartEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
diffGrapherConfigs,
mergeGrapherConfigs,
PostReference,
SeriesName,
difference,
} from "@ourworldindata/utils"
import { action, computed, observable, when } from "mobx"
import { EditorFeatures } from "./EditorFeatures.js"
Expand Down Expand Up @@ -104,7 +106,7 @@ export abstract class AbstractChartEditor<
/** patch config merged with parent config */
@computed get fullConfig(): GrapherInterface {
if (!this.activeParentConfig) return this.liveConfig
return mergeGrapherConfigs(this.activeParentConfig, this.liveConfig)
return mergeGrapherConfigs(this.activeParentConfig, this.patchConfig)
}

/** parent config currently applied to grapher */
Expand Down Expand Up @@ -163,6 +165,24 @@ export abstract class AbstractChartEditor<
return Object.hasOwn(this.activeParentConfig, property)
}

@computed get invalidFocusedSeriesNames(): SeriesName[] {
const { grapher } = this

// if focusing is not supported, then all focused series are invalid
if (!this.features.canHighlightSeries) {
return grapher.focusArray.seriesNames
}

// find invalid focused series
const availableSeriesNames = grapher.chartSeriesNames
const focusedSeriesNames = grapher.focusArray.seriesNames
return difference(focusedSeriesNames, availableSeriesNames)
}

@action.bound removeInvalidFocusedSeriesNames(): void {
this.grapher.focusArray.remove(...this.invalidFocusedSeriesNames)
}

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

Expand Down
11 changes: 3 additions & 8 deletions adminSiteClient/ChartEditorTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,8 @@ export type FieldWithDetailReferences =
| "axisLabelX"
| "axisLabelY"

export interface DimensionErrorMessage {
displayName?: string
}
type ErrorMessageFieldName = FieldWithDetailReferences | "focusedSeriesNames"

export type ErrorMessages = Partial<Record<FieldWithDetailReferences, string>>
export type ErrorMessages = Partial<Record<ErrorMessageFieldName, string>>

export type ErrorMessagesForDimensions = Record<
DimensionProperty,
DimensionErrorMessage[]
>
export type ErrorMessagesForDimensions = Record<DimensionProperty, string[]>
13 changes: 10 additions & 3 deletions adminSiteClient/ChartEditorView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,14 @@ export class ChartEditorView<
}
)

// add an error message if any focused series names are invalid
const { invalidFocusedSeriesNames = [] } = this.editor ?? {}
if (invalidFocusedSeriesNames.length > 0) {
const invalidNames = invalidFocusedSeriesNames.join(", ")
const message = `Invalid focus state. The following entities/indicators are not plotted: ${invalidNames}`
errorMessages.focusedSeriesNames = message
}

return errorMessages
}

Expand All @@ -287,9 +295,8 @@ export class ChartEditorView<

// add error message if details are referenced in the display name
if (hasDetailsInDisplayName) {
errorMessages[slot.property][dimensionIndex] = {
displayName: "Detail syntax is not supported",
}
errorMessages[slot.property][dimensionIndex] =
`Detail syntax is not supported for display names of indicators: ${dimension.display.name}`
}
})
})
Expand Down
5 changes: 2 additions & 3 deletions adminSiteClient/DimensionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { observer } from "mobx-react"
import { ChartDimension } from "@ourworldindata/grapher"
import { OwidColumnDef, OwidVariableRoundingMode } from "@ourworldindata/types"
import { startCase } from "@ourworldindata/utils"
import { DimensionErrorMessage } from "./ChartEditorTypes.js"
import {
Toggle,
BindAutoString,
Expand Down Expand Up @@ -35,7 +34,7 @@ export class DimensionCard<
onChange: (dimension: ChartDimension) => void
onEdit?: () => void
onRemove?: () => void
errorMessage?: DimensionErrorMessage
errorMessage?: string
}> {
@observable.ref isExpanded: boolean = false

Expand Down Expand Up @@ -171,7 +170,7 @@ export class DimensionCard<
store={dimension.display}
auto={column.displayName}
onBlur={this.onChange}
errorMessage={this.props.errorMessage?.displayName}
errorMessage={this.props.errorMessage}
/>
<BindAutoString
label="Unit of measurement"
Expand Down
16 changes: 13 additions & 3 deletions adminSiteClient/EditorBasicTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ class DimensionSlotView<

@observable.ref isSelectingVariables: boolean = false

private get editor() {
return this.props.editor
}

private get grapher() {
return this.props.editor.grapher
}
Expand Down Expand Up @@ -105,7 +109,7 @@ class DimensionSlotView<
this.updateParentConfig()
}

@action.bound private updateDefaults() {
@action.bound private updateDefaultSelection() {
const { grapher } = this.props.editor
const { selection } = grapher
const { availableEntityNames, availableEntityNameSet } = selection
Expand Down Expand Up @@ -151,11 +155,17 @@ class DimensionSlotView<
this.disposers.push(
reaction(
() => this.grapher.validChartTypes,
this.updateDefaults
() => {
this.updateDefaultSelection()
this.editor.removeInvalidFocusedSeriesNames()
}
),
reaction(
() => this.grapher.yColumnsFromDimensions.length,
this.updateDefaults
() => {
this.updateDefaultSelection()
this.editor.removeInvalidFocusedSeriesNames()
}
)
)
}
Expand Down
162 changes: 155 additions & 7 deletions adminSiteClient/EditorDataTab.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import React from "react"
import { moveArrayItemToIndex, omit } from "@ourworldindata/utils"
import {
differenceOfSets,
moveArrayItemToIndex,
omit,
sortBy,
} from "@ourworldindata/utils"
import { computed, action, observable } from "mobx"
import { observer } from "mobx-react"
import cx from "classnames"
import {
EntitySelectionMode,
MissingDataStrategy,
EntityName,
SeriesName,
} from "@ourworldindata/types"
import { Grapher } from "@ourworldindata/grapher"
import { ColorBox, SelectField, Section, FieldsRow } from "./Forms.js"
Expand All @@ -24,14 +31,20 @@ import {
} from "react-beautiful-dnd"
import { AbstractChartEditor } from "./AbstractChartEditor.js"

interface EntityItemProps extends React.HTMLProps<HTMLDivElement> {
interface EntityListItemProps extends React.HTMLProps<HTMLDivElement> {
grapher: Grapher
entityName: EntityName
onRemove?: () => void
}

interface SeriesListItemProps extends React.HTMLProps<HTMLDivElement> {
seriesName: SeriesName
isValid?: boolean
onRemove?: () => void
}

@observer
class EntityItem extends React.Component<EntityItemProps> {
class EntityListItem extends React.Component<EntityListItemProps> {
@observable.ref isChoosingColor: boolean = false

@computed get table() {
Expand Down Expand Up @@ -89,7 +102,36 @@ class EntityItem extends React.Component<EntityItemProps> {
}

@observer
export class KeysSection extends React.Component<{
class SeriesListItem extends React.Component<SeriesListItemProps> {
@action.bound onRemove() {
this.props.onRemove?.()
}

render() {
const { props } = this
const { seriesName, isValid } = props
const rest = omit(props, ["seriesName", "isValid", "onRemove"])

const className = cx("ListItem", "list-group-item", {
invalid: !isValid,
})
const annotation = !isValid ? "(not plotted)" : ""

return (
<div className={className} key={seriesName} {...rest}>
<div>
{seriesName} {annotation}
</div>
<div className="clickable" onClick={this.onRemove}>
<FontAwesomeIcon icon={faTimes} />
</div>
</div>
)
}
}

@observer
export class EntitySelectionSection extends React.Component<{
editor: AbstractChartEditor
}> {
@observable.ref dragKey?: EntityName
Expand All @@ -100,6 +142,12 @@ export class KeysSection extends React.Component<{

@action.bound onAddKey(entityName: EntityName) {
this.editor.grapher.selection.selectEntity(entityName)
this.editor.removeInvalidFocusedSeriesNames()
}

@action.bound onRemoveKey(entityName: EntityName) {
this.editor.grapher.selection.deselectEntity(entityName)
this.editor.removeInvalidFocusedSeriesNames()
}

@action.bound onDragEnd(result: DropResult) {
Expand All @@ -122,6 +170,7 @@ export class KeysSection extends React.Component<{
grapher.selection.setSelectedEntities(
activeParentConfig.selectedEntityNames
)
this.editor.removeInvalidFocusedSeriesNames()
}

render() {
Expand Down Expand Up @@ -183,12 +232,12 @@ export class KeysSection extends React.Component<{
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<EntityItem
<EntityListItem
key={entityName}
grapher={grapher}
entityName={entityName}
onRemove={() =>
selection.deselectEntity(
this.onRemoveKey(
entityName
)
}
Expand Down Expand Up @@ -216,6 +265,102 @@ export class KeysSection extends React.Component<{
}
}

@observer
export class FocusSection extends React.Component<{
editor: AbstractChartEditor
}> {
@computed get editor() {
return this.props.editor
}

@action.bound addToFocusedSeries(seriesName: SeriesName) {
this.editor.grapher.focusArray.add(seriesName)
}

@action.bound removeFromFocusedSeries(seriesName: SeriesName) {
this.editor.grapher.focusArray.remove(seriesName)
}

@action.bound setFocusedSeriesNamesToParentValue() {
const { grapher, activeParentConfig } = this.editor
if (!activeParentConfig || !activeParentConfig.focusedSeriesNames)
return
grapher.focusArray.clearAllAndAdd(
...activeParentConfig.focusedSeriesNames
)
this.editor.removeInvalidFocusedSeriesNames()
}

render() {
const { editor } = this
const { grapher } = editor

const isFocusInherited =
editor.isPropertyInherited("focusedSeriesNames")

const focusedSeriesNameSet = grapher.focusArray.seriesNameSet
const focusedSeriesNames = grapher.focusArray.seriesNames

// series available to highlight are those that are currently plotted
const seriesNameSet = new Set(grapher.chartSeriesNames)
const availableSeriesNameSet = differenceOfSets([
seriesNameSet,
focusedSeriesNameSet,
])

// focusing only makes sense for two or more plotted series
if (focusedSeriesNameSet.size === 0 && availableSeriesNameSet.size < 2)
return null

const availableSeriesNames: SeriesName[] = sortBy(
Array.from(availableSeriesNameSet)
)

const invalidFocusedSeriesNames = differenceOfSets([
focusedSeriesNameSet,
seriesNameSet,
])

return (
<Section name="Data to highlight">
<FieldsRow>
<SelectField
onValue={this.addToFocusedSeries}
value="Select data"
options={["Select data"]
.concat(availableSeriesNames)
.map((key) => ({ value: key }))}
/>
{editor.couldPropertyBeInherited("focusedSeriesNames") && (
<button
className="btn btn-outline-secondary"
type="button"
style={{ maxWidth: "min-content" }}
title="Reset to parent focus"
onClick={this.setFocusedSeriesNamesToParentValue}
disabled={isFocusInherited}
>
<FontAwesomeIcon
icon={isFocusInherited ? faLink : faUnlink}
/>
</button>
)}
</FieldsRow>
{focusedSeriesNames.map((seriesName) => (
<SeriesListItem
key={seriesName}
seriesName={seriesName}
isValid={!invalidFocusedSeriesNames.has(seriesName)}
onRemove={() =>
this.removeFromFocusedSeries(seriesName)
}
/>
))}
</Section>
)
}
}

@observer
class MissingDataSection<
Editor extends AbstractChartEditor,
Expand Down Expand Up @@ -331,7 +476,10 @@ export class EditorDataTab<
</label>
</div>
</Section>
<KeysSection editor={editor} />
<EntitySelectionSection editor={editor} />
{features.canHighlightSeries && (
<FocusSection editor={editor} />
)}
{features.canSpecifyMissingDataStrategy && (
<MissingDataSection editor={this.props.editor} />
)}
Expand Down
1 change: 1 addition & 0 deletions adminSiteClient/EditorExportTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ export class EditorExportTab<
staticFormat: format,
selectedEntityNames:
this.grapher.selection.selectedEntityNames,
focusedSeriesNames: this.grapher.focusedSeriesNames,
isSocialMediaExport,
})
}
Expand Down
Loading
Loading