Skip to content

Commit

Permalink
🚧 introduce InteractionArray
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Dec 4, 2024
1 parent d5c3083 commit b26a81d
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 35 deletions.
2 changes: 2 additions & 0 deletions packages/@ourworldindata/grapher/src/chart/ChartManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { TooltipManager } from "../tooltip/TooltipProps"
import { OwidTable, CoreColumn } from "@ourworldindata/core-table"

import { SelectionArray } from "../selection/SelectionArray"
import { InteractionArray } from "../selection/InteractionArray"
import { ColumnSlug, SortConfig, TimeBound } from "@ourworldindata/utils"
import { ColorScaleBin } from "../color/ColorScaleBin"
import { ColorScale } from "../color/ColorScale"
Expand Down Expand Up @@ -63,6 +64,7 @@ export interface ChartManager {
sizeColumnSlug?: ColumnSlug
colorColumnSlug?: ColumnSlug

interactionArray?: InteractionArray
selection?: SelectionArray | EntityName[]
entityType?: string

Expand Down
9 changes: 9 additions & 0 deletions packages/@ourworldindata/grapher/src/core/Grapher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ import {
GRAPHER_TAB_NAMES,
GRAPHER_TAB_QUERY_PARAMS,
GrapherTabOption,
SeriesName,
} from "@ourworldindata/types"
import {
BlankOwidTable,
Expand Down Expand Up @@ -222,6 +223,7 @@ import {
} from "../entitySelector/EntitySelector"
import { SlideInDrawer } from "../slideInDrawer/SlideInDrawer"
import { BodyDiv } from "../bodyDiv/BodyDiv"
import { InteractionArray } from "../selection/InteractionArray"

declare global {
interface Window {
Expand Down Expand Up @@ -433,6 +435,7 @@ export class Grapher

// Initializing arrays with `undefined` ensures that empty arrays get serialised
@observable selectedEntityNames?: EntityName[] = undefined
@observable focusedSeriesNames?: SeriesName[] = undefined
@observable excludedEntities?: number[] = undefined
/** IncludedEntities are usually empty which means use all available entities. When
includedEntities is set it means "only use these entities". excludedEntities
Expand Down Expand Up @@ -565,6 +568,7 @@ export class Grapher
)

obj.selectedEntityNames = this.selection.selectedEntityNames
obj.focusedSeriesNames = this.interactionArray.focusedEntityNames

deleteRuntimeAndUnchangedProps(obj, defaultObject)

Expand Down Expand Up @@ -606,6 +610,9 @@ export class Grapher
if (obj.selectedEntityNames)
this.selection.setSelectedEntities(obj.selectedEntityNames)

if (obj.focusedSeriesNames)
this.interactionArray.setFocusedEntities(obj.focusedSeriesNames)

// JSON doesn't support Infinity, so we use strings instead.
this.minTime = minTimeBoundFromJSONOrNegativeInfinity(obj.minTime)
this.maxTime = maxTimeBoundFromJSONOrPositiveInfinity(obj.maxTime)
Expand Down Expand Up @@ -2537,6 +2544,8 @@ export class Grapher
this.props.table?.availableEntities ?? []
)

interactionArray = new InteractionArray()

@computed get availableEntities(): Entity[] {
return this.tableForSelection.availableEntities
}
Expand Down
9 changes: 7 additions & 2 deletions packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ import {
getColorKey,
getSeriesName,
} from "./LineChartHelpers"
import { InteractionArray } from "../selection/InteractionArray"

const LINE_CHART_CLASS_NAME = "LineChart"

Expand Down Expand Up @@ -187,8 +188,12 @@ export class LineChart
return makeSelectionArray(this.manager.selection)
}

@computed get interactionArray(): InteractionArray {
return this.manager.interactionArray ?? new InteractionArray()
}

@computed private get focusedSeriesNameSet(): Set<SeriesName> {
return this.selectionArray.focusedEntityNameSet
return this.interactionArray.focusedEntityNameSet
}

@computed private get missingDataStrategy(): MissingDataStrategy {
Expand Down Expand Up @@ -540,7 +545,7 @@ export class LineChart
}

@action.bound onLineLegendClick(seriesName: SeriesName): void {
this.selectionArray.toggleFocus(seriesName)
this.interactionArray.toggleFocus(seriesName)
}

// TODO
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ properties:
items:
type:
- string
focusedSeriesNames:
type: array
description: |
The initially focused chart elements (e.g. line or bar).
Is either a list of entity or variable names.
Only works to line and slope charts for now.
items:
type:
- string
baseColorScheme:
type: string
description: |
Expand Down
48 changes: 48 additions & 0 deletions packages/@ourworldindata/grapher/src/selection/InteractionArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { EntityName } from "@ourworldindata/types"
import { action, computed, observable } from "mobx"

export class InteractionArray {
constructor(focusedEntityNames: EntityName[] = []) {
this.focusedEntityNames = focusedEntityNames.slice()
}

@observable focusedEntityNames: EntityName[]

@computed get focusedEntityNameSet(): Set<EntityName> {
return new Set<EntityName>(this.focusedEntityNames)
}

@action.bound focusEntity(entityName: EntityName): this {
if (!this.focusedEntityNameSet.has(entityName))
this.focusedEntityNames.push(entityName)
return this
}

@action.bound unfocusEntity(entityName: EntityName): this {
this.focusedEntityNames = this.focusedEntityNames.filter(
(name) => name !== entityName
)
return this
}

@action.bound toggleFocus(entityName: EntityName): this {
return this.focusedEntityNameSet.has(entityName)
? this.unfocusEntity(entityName)
: this.focusEntity(entityName)
}

@action.bound clear(): void {
this.focusedEntityNames = []
}

@action.bound addToFocusedEntities(entityNames: EntityName[]): this {
this.focusedEntityNames = this.focusedEntityNames.concat(entityNames)
return this
}

// Clears and sets focused entities
@action.bound setFocusedEntities(entityNames: EntityName[]): this {
this.clear()
return this.addToFocusedEntities(entityNames)
}
}
31 changes: 0 additions & 31 deletions packages/@ourworldindata/grapher/src/selection/SelectionArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,11 @@ export class SelectionArray {
) {
this.selectedEntityNames = selectedEntityNames.slice()
this.availableEntities = availableEntities.slice()

this.focusedEntityNames = []
}

@observable selectedEntityNames: EntityName[]
@observable private availableEntities: Entity[]

@observable focusedEntityNames: EntityName[]

@computed get availableEntityNames(): string[] {
return this.availableEntities.map((entity) => entity.entityName)
}
Expand Down Expand Up @@ -107,33 +103,6 @@ export class SelectionArray {
return this
}

@computed get focusedEntityNameSet(): Set<EntityName> {
return new Set<EntityName>(this.focusedEntityNames)
}

@computed get hoveredEntityNameSet(): Set<EntityName> {
return new Set<EntityName>(this.focusedEntityNames)
}

@action.bound focusEntity(entityName: EntityName): this {
if (!this.focusedEntityNameSet.has(entityName))
this.focusedEntityNames.push(entityName)
return this
}

@action.bound unfocusEntity(entityName: EntityName): this {
this.focusedEntityNames = this.focusedEntityNames.filter(
(name) => name !== entityName
)
return this
}

@action.bound toggleFocus(entityName: EntityName): this {
return this.focusedEntityNameSet.has(entityName)
? this.unfocusEntity(entityName)
: this.focusEntity(entityName)
}

// Mainly for testing
@action.bound selectSample(howMany = 1): this {
return this.setSelectedEntities(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import {
} from "../lineCharts/LineChartHelpers"
import { HorizontalColorLegendManager } from "../horizontalColorLegend/HorizontalColorLegends"
import { CategoricalBin } from "../color/ColorScaleBin"
import { InteractionArray } from "../selection/InteractionArray"

type SVGMouseOrTouchEvent =
| React.MouseEvent<SVGGElement>
Expand Down Expand Up @@ -212,8 +213,12 @@ export class SlopeChart
return makeSelectionArray(this.manager.selection)
}

@computed get interactionArray(): InteractionArray {
return this.manager.interactionArray ?? new InteractionArray()
}

@computed private get focusedSeriesNameSet(): Set<SeriesName> {
return this.selectionArray.focusedEntityNameSet
return this.interactionArray.focusedEntityNameSet
}

@computed private get formatColumn() {
Expand Down Expand Up @@ -844,7 +849,7 @@ export class SlopeChart
}

@action.bound onLineLegendClick(seriesName: SeriesName): void {
this.selectionArray.toggleFocus(seriesName)
this.interactionArray.toggleFocus(seriesName)
}

private hoverTimer?: NodeJS.Timeout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ export interface GrapherInterface extends SortConfig {
includedEntities?: number[]
selectedEntityNames?: EntityName[]
selectedEntityColors?: { [entityName: string]: string | undefined }
focusedSeriesNames?: SeriesName[]
missingDataStrategy?: MissingDataStrategy
hideFacetControl?: boolean
facettingLabelByYVariables?: string
Expand Down Expand Up @@ -698,6 +699,7 @@ export const grapherKeysToSerialize = [
"dimensions",
"selectedEntityNames",
"selectedEntityColors",
"focusedSeriesNames",
"sortBy",
"sortOrder",
"sortColumnSlug",
Expand Down

0 comments on commit b26a81d

Please sign in to comment.