diff --git a/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx b/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx
index 3f79cc7bdec..028c9df6da6 100644
--- a/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx
+++ b/packages/@ourworldindata/grapher/src/barCharts/DiscreteBarChart.tsx
@@ -75,8 +75,8 @@ import {
} from "../color/ColorConstants"
import { CategoricalBin, ColorScaleBin } from "../color/ColorScaleBin"
import {
- HorizontalColorLegendManager,
HorizontalNumericColorLegend,
+ HorizontalNumericColorLegendProps,
} from "../horizontalColorLegend/HorizontalColorLegends"
import { BaseType, Selection } from "d3"
import { TextWrap } from "@ourworldindata/components"
@@ -514,7 +514,7 @@ export class DiscreteBarChart
<>
{this.renderDefs()}
{this.showColorLegend && (
-
+
)}
{this.showHorizontalAxis && (
<>
@@ -833,6 +833,24 @@ export class DiscreteBarChart
return sortBy(legendBins, (bin) => bin instanceof CategoricalBin)
}
+ @computed
+ private get legendProps(): HorizontalNumericColorLegendProps {
+ return {
+ fontSize: this.fontSize,
+ legendX: this.legendX,
+ legendAlign: this.legendAlign,
+ legendMaxWidth: this.legendMaxWidth,
+ numericLegendData: this.numericLegendData,
+ numericBinSize: this.numericBinSize,
+ numericBinStroke: this.numericBinStroke,
+ equalSizeBins: this.equalSizeBins,
+ legendTitle: this.legendTitle,
+ numericLegendY: this.numericLegendY,
+ legendTextColor: this.legendTextColor,
+ legendTickSize: this.legendTickSize,
+ }
+ }
+
@computed get projectedDataColorInLegend(): string {
// if a single color is in use, use that color in the legend
if (uniqBy(this.series, "color").length === 1)
@@ -840,7 +858,9 @@ export class DiscreteBarChart
return DEFAULT_PROJECTED_DATA_COLOR_IN_LEGEND
}
- @computed get externalLegend(): HorizontalColorLegendManager | undefined {
+ @computed get externalNumericLegend():
+ | HorizontalNumericColorLegendProps
+ | undefined {
if (this.hasColorLegend) {
return {
numericLegendData: this.numericLegendData,
@@ -860,10 +880,10 @@ export class DiscreteBarChart
legendTextColor = "#555"
legendTickSize = 1
- @computed get numericLegend(): HorizontalNumericColorLegend | undefined {
+ @computed get legendHeight(): number {
return this.hasColorScale && this.manager.showLegend
- ? new HorizontalNumericColorLegend({ manager: this })
- : undefined
+ ? HorizontalNumericColorLegend.height(this.legendProps)
+ : 0
}
@computed get numericLegendY(): number {
@@ -876,10 +896,6 @@ export class DiscreteBarChart
: undefined
}
- @computed get legendHeight(): number {
- return this.numericLegend?.height ?? 0
- }
-
// End of color legend props
@computed get series(): DiscreteBarSeries[] {
diff --git a/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts b/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts
index cfd81654f90..669a7558f8f 100644
--- a/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts
+++ b/packages/@ourworldindata/grapher/src/chart/ChartInterface.ts
@@ -7,7 +7,10 @@ import {
} from "@ourworldindata/types"
import { ColorScale } from "../color/ColorScale"
import { HorizontalAxis, VerticalAxis } from "../axis/Axis"
-import { HorizontalColorLegendManager } from "../horizontalColorLegend/HorizontalColorLegends"
+import {
+ HorizontalCategoricalColorLegendProps,
+ HorizontalNumericColorLegendProps,
+} from "../horizontalColorLegend/HorizontalColorLegends"
// The idea of this interface is to try and start reusing more code across our Chart classes and make it easier
// for a dev to work on a chart type they haven't touched before if they've worked with another that implements
// this interface.
@@ -41,9 +44,14 @@ export interface ChartInterface {
/**
* The legend that has been hidden from the chart plot (using `manager.hideLegend`).
- * Used to create a global legend for faceted charts.
+ * Used to create a global categorical legend for faceted charts.
+ */
+ externalCategoricalLegend?: HorizontalCategoricalColorLegendProps
+ /**
+ * The legend that has been hidden from the chart plot (using `manager.hideLegend`).
+ * Used to create a global numeric legend for faceted charts.
*/
- externalLegend?: HorizontalColorLegendManager
+ externalNumericLegend?: HorizontalNumericColorLegendProps
/**
* Which facet strategies the chart type finds reasonable in its current setting, if any.
diff --git a/packages/@ourworldindata/grapher/src/facetChart/FacetChart.tsx b/packages/@ourworldindata/grapher/src/facetChart/FacetChart.tsx
index ef091fb9110..f067c221933 100644
--- a/packages/@ourworldindata/grapher/src/facetChart/FacetChart.tsx
+++ b/packages/@ourworldindata/grapher/src/facetChart/FacetChart.tsx
@@ -55,15 +55,13 @@ import { AxisConfig } from "../axis/AxisConfig"
import { HorizontalAxis, VerticalAxis } from "../axis/Axis"
import {
HorizontalCategoricalColorLegend,
+ HorizontalCategoricalColorLegendProps,
HorizontalColorLegend,
- HorizontalColorLegendManager,
+ HorizontalColorLegendProps,
HorizontalNumericColorLegend,
+ HorizontalNumericColorLegendProps,
} from "../horizontalColorLegend/HorizontalColorLegends"
-import {
- CategoricalBin,
- ColorScaleBin,
- NumericBin,
-} from "../color/ColorScaleBin"
+import { CategoricalBin, ColorScaleBin } from "../color/ColorScaleBin"
import { GRAPHER_DARK_TEXT } from "../color/ColorConstants"
const SHARED_X_AXIS_MIN_FACET_COUNT = 12
@@ -118,7 +116,7 @@ interface AxesInfo {
@observer
export class FacetChart
extends React.Component
- implements ChartInterface, HorizontalColorLegendManager
+ implements ChartInterface
{
transformTable(table: OwidTable): OwidTable {
return table
@@ -589,26 +587,28 @@ export class FacetChart
// legend utils
- @computed private get externalLegends(): HorizontalColorLegendManager[] {
+ @computed
+ private get externalCategoricalLegends(): HorizontalCategoricalColorLegendProps[] {
return excludeUndefined(
this.intermediateChartInstances.map(
- (instance) => instance.externalLegend
+ (instance) => instance.externalCategoricalLegend
)
)
}
- @computed private get isNumericLegend(): boolean {
- return this.externalLegends.some((legend) =>
- legend.numericLegendData?.some((bin) => bin instanceof NumericBin)
+ @computed
+ private get externalNumericLegends(): HorizontalNumericColorLegendProps[] {
+ return excludeUndefined(
+ this.intermediateChartInstances.map(
+ (instance) => instance.externalNumericLegend
+ )
)
}
- @computed private get LegendClass():
- | typeof HorizontalNumericColorLegend
- | typeof HorizontalCategoricalColorLegend {
- return this.isNumericLegend
- ? HorizontalNumericColorLegend
- : HorizontalCategoricalColorLegend
+ @computed private get isNumericLegend(): boolean {
+ return this.externalNumericLegends.some(
+ (legend) => legend.numericLegendData.length > 0
+ )
}
@computed private get showLegend(): boolean {
@@ -641,10 +641,21 @@ export class FacetChart
return false
}
- private getExternalLegendProp<
- Prop extends keyof HorizontalColorLegendManager,
- >(prop: Prop): HorizontalColorLegendManager[Prop] | undefined {
- for (const externalLegend of this.externalLegends) {
+ private getCategoricalExternalLegendProp<
+ Prop extends keyof HorizontalCategoricalColorLegendProps,
+ >(prop: Prop): HorizontalCategoricalColorLegendProps[Prop] | undefined {
+ for (const externalLegend of this.externalCategoricalLegends) {
+ if (externalLegend[prop] !== undefined) {
+ return externalLegend[prop]
+ }
+ }
+ return undefined
+ }
+
+ private getNumericExternalLegendProp<
+ Prop extends keyof HorizontalNumericColorLegendProps,
+ >(prop: Prop): HorizontalNumericColorLegendProps[Prop] | undefined {
+ for (const externalLegend of this.externalNumericLegends) {
if (externalLegend[prop] !== undefined) {
return externalLegend[prop]
}
@@ -667,64 +678,50 @@ export class FacetChart
// legend props
- @computed get legendX(): number {
- return this.bounds.x
- }
-
- @computed get numericLegendY(): number {
- return this.bounds.top
- }
-
- @computed get categoryLegendY(): number {
- return this.bounds.top
- }
-
- @computed get legendMaxWidth(): number {
- return this.bounds.width
- }
-
- @computed get legendAlign(): HorizontalAlign {
- return HorizontalAlign.left
- }
-
- @computed get legendTitle(): string | undefined {
- return this.getExternalLegendProp("legendTitle")
- }
-
- @computed get legendHeight(): number | undefined {
- return this.getExternalLegendProp("legendHeight")
- }
-
- @computed get legendOpacity(): number | undefined {
- return this.getExternalLegendProp("legendOpacity")
- }
-
- @computed get legendTextColor(): Color | undefined {
- return this.getExternalLegendProp("legendTextColor")
- }
-
- @computed get legendTickSize(): number | undefined {
- return this.getExternalLegendProp("legendTickSize")
- }
-
- @computed get categoricalBinStroke(): Color | undefined {
- return this.getExternalLegendProp("categoricalBinStroke")
- }
-
- @computed get numericBinSize(): number | undefined {
- return this.getExternalLegendProp("numericBinSize")
- }
-
- @computed get numericBinStroke(): Color | undefined {
- return this.getExternalLegendProp("numericBinStroke")
+ @computed private get commonLegendProps(): HorizontalColorLegendProps {
+ return {
+ fontSize: this.fontSize,
+ legendX: this.bounds.x,
+ legendMaxWidth: this.bounds.width,
+ legendAlign: HorizontalAlign.left,
+ onLegendMouseOver: this.onLegendMouseOver,
+ onLegendMouseLeave: this.onLegendMouseLeave,
+ }
}
- @computed get numericBinStrokeWidth(): number | undefined {
- return this.getExternalLegendProp("numericBinStrokeWidth")
+ @computed
+ private get numericLegendProps(): HorizontalNumericColorLegendProps {
+ return {
+ ...this.commonLegendProps,
+ numericLegendY: this.bounds.top,
+ legendTitle: this.getNumericExternalLegendProp("legendTitle"),
+ legendTextColor:
+ this.getNumericExternalLegendProp("legendTextColor"),
+ legendTickSize: this.getNumericExternalLegendProp("legendTickSize"),
+ numericBinSize: this.getNumericExternalLegendProp("numericBinSize"),
+ numericBinStroke:
+ this.getNumericExternalLegendProp("numericBinStroke"),
+ numericBinStrokeWidth: this.getNumericExternalLegendProp(
+ "numericBinStrokeWidth"
+ ),
+ equalSizeBins: this.getNumericExternalLegendProp("equalSizeBins"),
+ numericLegendData: this.numericLegendData,
+ }
}
- @computed get equalSizeBins(): boolean | undefined {
- return this.getExternalLegendProp("equalSizeBins")
+ @computed
+ private get categoricalLegendProps(): HorizontalCategoricalColorLegendProps {
+ return {
+ ...this.commonLegendProps,
+ categoryLegendY: this.bounds.top,
+ categoricalBinStroke: this.getCategoricalExternalLegendProp(
+ "categoricalBinStroke"
+ ),
+ hoverColors: this.hoverColors,
+ activeColors: this.activeColors,
+ categoricalLegendData: this.categoricalLegendData,
+ onLegendClick: this.onLegendClick,
+ }
}
@computed get hoverColors(): Color[] | undefined {
@@ -750,13 +747,14 @@ export class FacetChart
@computed get numericLegendData(): ColorScaleBin[] {
if (!this.isNumericLegend || !this.hideFacetLegends) return []
- const allBins: ColorScaleBin[] = this.externalLegends.flatMap(
- (legend) => [
- ...(legend.numericLegendData ?? []),
- ...(legend.categoricalLegendData ?? []),
- ]
- )
- const uniqBins = this.getUniqBins(allBins)
+ const uniqBins = this.getUniqBins([
+ ...this.externalCategoricalLegends.flatMap(
+ (legend) => legend.categoricalLegendData
+ ),
+ ...this.externalNumericLegends.flatMap(
+ (legend) => legend.numericLegendData
+ ),
+ ])
const sortedBins = sortBy(
uniqBins,
(bin) => bin instanceof CategoricalBin
@@ -766,12 +764,9 @@ export class FacetChart
@computed get categoricalLegendData(): CategoricalBin[] {
if (this.isNumericLegend || !this.hideFacetLegends) return []
- const allBins: CategoricalBin[] = this.externalLegends
- .flatMap((legend) => [
- ...(legend.numericLegendData ?? []),
- ...(legend.categoricalLegendData ?? []),
- ])
- .filter((bin) => bin instanceof CategoricalBin) as CategoricalBin[]
+ const allBins = this.externalCategoricalLegends.flatMap(
+ (legend) => legend.categoricalLegendData
+ )
const uniqBins = this.getUniqBins(allBins)
const newBins = uniqBins.map(
// remap index to ensure it's unique (the above procedure can lead to duplicates)
@@ -814,7 +809,9 @@ export class FacetChart
// end of legend props
@computed private get legend(): HorizontalColorLegend {
- return new this.LegendClass({ manager: this })
+ return this.isNumericLegend
+ ? new HorizontalNumericColorLegend(this.numericLegendProps)
+ : new HorizontalCategoricalColorLegend(this.categoricalLegendProps)
}
/**
@@ -858,11 +855,21 @@ export class FacetChart
return { fontSize, shortenedLabel: label }
}
+ private renderLegend(): React.ReactElement {
+ return this.isNumericLegend ? (
+
+ ) : (
+
+ )
+ }
+
render(): React.ReactElement {
- const { facetFontSize, LegendClass, showLegend } = this
+ const { facetFontSize, showLegend } = this
return (
- {showLegend && }
+ {showLegend && this.renderLegend()}
{this.placedSeries.map((facetChart, index: number) => {
const ChartClass =
ChartComponentClassMap.get(this.chartTypeName) ??
diff --git a/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.test.ts b/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.test.ts
index bc38e848369..3a420462480 100755
--- a/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.test.ts
+++ b/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.test.ts
@@ -21,55 +21,53 @@ describe(HorizontalNumericColorLegend, () => {
})
const legend = new HorizontalNumericColorLegend({
- manager: { numericLegendData: [bin] },
+ numericLegendData: [bin],
})
expect(legend.height).toBeGreaterThan(0)
})
it("adds margins between categorical but not numeric bins", () => {
const legend = new HorizontalNumericColorLegend({
- manager: {
- numericLegendData: [
- new CategoricalBin({
- index: 0,
- value: "a",
- label: "a",
- color: "#fff",
- }),
- new CategoricalBin({
- index: 0,
- value: "b",
- label: "b",
- color: "#fff",
- }),
- new NumericBin({
- isFirst: true,
- isOpenLeft: false,
- isOpenRight: false,
- min: 0,
- max: 1,
- displayMin: "0",
- displayMax: "1",
- color: "#fff",
- }),
- new NumericBin({
- isFirst: false,
- isOpenLeft: false,
- isOpenRight: false,
- min: 1,
- max: 2,
- displayMin: "1",
- displayMax: "2",
- color: "#fff",
- }),
- new CategoricalBin({
- index: 0,
- value: "c",
- label: "c",
- color: "#fff",
- }),
- ],
- },
+ numericLegendData: [
+ new CategoricalBin({
+ index: 0,
+ value: "a",
+ label: "a",
+ color: "#fff",
+ }),
+ new CategoricalBin({
+ index: 0,
+ value: "b",
+ label: "b",
+ color: "#fff",
+ }),
+ new NumericBin({
+ isFirst: true,
+ isOpenLeft: false,
+ isOpenRight: false,
+ min: 0,
+ max: 1,
+ displayMin: "0",
+ displayMax: "1",
+ color: "#fff",
+ }),
+ new NumericBin({
+ isFirst: false,
+ isOpenLeft: false,
+ isOpenRight: false,
+ min: 1,
+ max: 2,
+ displayMin: "1",
+ displayMax: "2",
+ color: "#fff",
+ }),
+ new CategoricalBin({
+ index: 0,
+ value: "c",
+ label: "c",
+ color: "#fff",
+ }),
+ ],
})
const margin = legend["itemMargin"]
@@ -100,7 +98,7 @@ describe(HorizontalCategoricalColorLegend, () => {
})
const legend = new HorizontalCategoricalColorLegend({
- manager: { categoricalLegendData: [bin] },
+ categoricalLegendData: [bin],
})
expect(legend.height).toBeGreaterThan(0)
})
diff --git a/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.tsx b/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.tsx
index d53b679238a..b8e42f95783 100644
--- a/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.tsx
+++ b/packages/@ourworldindata/grapher/src/horizontalColorLegend/HorizontalColorLegends.tsx
@@ -65,40 +65,47 @@ interface MarkLine {
marks: CategoricalMark[]
}
-// TODO unify properties across categorical & numeric legend.
-// This would make multiple legends per chart less convenient (only used in Map), but we shouldn't
-// be using multiple anyway – instead the numeric should also handle categorical bins too.
-export interface HorizontalColorLegendManager {
+export interface HorizontalColorLegendProps {
fontSize?: number
legendX?: number
legendAlign?: HorizontalAlign
- legendTitle?: string
- categoryLegendY?: number
- numericLegendY?: number
+
legendWidth?: number
- legendMaxWidth?: number
- legendHeight?: number
legendOpacity?: number
- legendTextColor?: Color
- legendTickSize?: number
- categoricalLegendData?: CategoricalBin[]
- categoricalFocusBracket?: CategoricalBin
- categoricalBinStroke?: Color
- numericLegendData?: ColorScaleBin[]
- numericFocusBracket?: ColorScaleBin
- numericBinSize?: number
- numericBinStroke?: Color
- numericBinStrokeWidth?: number
- equalSizeBins?: boolean
+ legendMaxWidth?: number
+
onLegendMouseLeave?: () => void
onLegendMouseOver?: (d: ColorScaleBin) => void
- onLegendClick?: (d: ColorScaleBin) => void
- activeColors?: string[] // inactive colors are grayed out
+}
+
+export interface HorizontalCategoricalColorLegendProps
+ extends HorizontalColorLegendProps {
+ categoricalLegendData: CategoricalBin[]
+ categoricalBinStroke?: Color
+ categoryLegendY?: number
+
focusColors?: string[] // focused colors are bolded
hoverColors?: string[] // non-hovered colors are muted
+ activeColors?: string[] // inactive colors are grayed out
+
+ onLegendClick?: (d: ColorScaleBin) => void
isStatic?: boolean
}
+export interface HorizontalNumericColorLegendProps
+ extends HorizontalColorLegendProps {
+ numericLegendData: ColorScaleBin[]
+ numericBinSize?: number
+ numericBinStroke?: Color
+ numericBinStrokeWidth?: number
+ equalSizeBins?: boolean
+ legendTitle?: string
+ numericFocusBracket?: ColorScaleBin
+ numericLegendY?: number
+ legendTextColor?: Color
+ legendTickSize?: number
+}
+
const DEFAULT_NUMERIC_BIN_SIZE = 10
const DEFAULT_NUMERIC_BIN_STROKE = "#333"
const DEFAULT_NUMERIC_BIN_STROKE_WIDTH = 0.3
@@ -110,60 +117,82 @@ const FOCUS_BORDER_COLOR = "#111"
const SPACE_BETWEEN_CATEGORICAL_BINS = 7
const MINIMUM_LABEL_DISTANCE = 5
-export abstract class HorizontalColorLegend extends React.Component<{
- manager: HorizontalColorLegendManager
-}> {
- @computed protected get manager(): HorizontalColorLegendManager {
- return this.props.manager
- }
-
+export abstract class HorizontalColorLegend<
+ Props extends HorizontalColorLegendProps = HorizontalColorLegendProps,
+> extends React.Component {
@computed protected get legendX(): number {
- return this.manager.legendX ?? 0
+ return this.props.legendX ?? 0
}
- @computed protected get categoryLegendY(): number {
- return this.manager.categoryLegendY ?? 0
+ @computed protected get legendAlign(): HorizontalAlign {
+ // Assume center alignment if none specified, for backwards-compatibility
+ return this.props.legendAlign ?? HorizontalAlign.center
}
- @computed protected get numericLegendY(): number {
- return this.manager.numericLegendY ?? 0
+ @computed protected get fontSize(): number {
+ return this.props.fontSize ?? BASE_FONT_SIZE
}
@computed protected get legendMaxWidth(): number | undefined {
- return this.manager.legendMaxWidth
+ return this.props.legendMaxWidth
}
- @computed protected get legendHeight(): number {
- return this.manager.legendHeight ?? 200
- }
+ abstract get height(): number
+ abstract get width(): number
+}
- @computed protected get legendAlign(): HorizontalAlign {
- // Assume center alignment if none specified, for backwards-compatibility
- return this.manager.legendAlign ?? HorizontalAlign.center
- }
+@observer
+export class HorizontalNumericColorLegend extends HorizontalColorLegend {
+ base: React.RefObject = React.createRef()
- @computed protected get fontSize(): number {
- return this.manager.fontSize ?? BASE_FONT_SIZE
+ static height(
+ props: Pick<
+ HorizontalNumericColorLegendProps,
+ | "numericLegendData"
+ | "numericBinSize"
+ | "fontSize"
+ | "legendWidth"
+ | "legendMaxWidth"
+ | "legendTitle"
+ | "equalSizeBins"
+ | "legendAlign"
+ | "legendX"
+ >
+ ): number {
+ const legend = new HorizontalNumericColorLegend(props)
+ return legend.height
}
- @computed protected get legendTextColor(): Color {
- return this.manager.legendTextColor ?? DEFAULT_TEXT_COLOR
+ static width(
+ props: Pick<
+ HorizontalNumericColorLegendProps,
+ | "numericLegendData"
+ | "numericBinSize"
+ | "fontSize"
+ | "legendWidth"
+ | "legendMaxWidth"
+ | "legendTitle"
+ | "equalSizeBins"
+ >
+ ): number {
+ const legend = new HorizontalNumericColorLegend(props)
+ return legend.width
}
- @computed protected get legendTickSize(): number {
- return this.manager.legendTickSize ?? DEFAULT_TICK_SIZE
+ @computed private get numericLegendY(): number {
+ return this.props.numericLegendY ?? 0
}
- abstract get height(): number
- abstract get width(): number
-}
+ @computed private get legendTextColor(): Color {
+ return this.props.legendTextColor ?? DEFAULT_TEXT_COLOR
+ }
-@observer
-export class HorizontalNumericColorLegend extends HorizontalColorLegend {
- base: React.RefObject = React.createRef()
+ @computed private get legendTickSize(): number {
+ return this.props.legendTickSize ?? DEFAULT_TICK_SIZE
+ }
@computed private get numericLegendData(): ColorScaleBin[] {
- return this.manager.numericLegendData ?? []
+ return this.props.numericLegendData ?? []
}
@computed private get visibleBins(): ColorScaleBin[] {
@@ -177,17 +206,16 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
}
@computed private get numericBinSize(): number {
- return this.props.manager.numericBinSize ?? DEFAULT_NUMERIC_BIN_SIZE
+ return this.props.numericBinSize ?? DEFAULT_NUMERIC_BIN_SIZE
}
@computed private get numericBinStroke(): Color {
- return this.props.manager.numericBinStroke ?? DEFAULT_NUMERIC_BIN_STROKE
+ return this.props.numericBinStroke ?? DEFAULT_NUMERIC_BIN_STROKE
}
@computed private get numericBinStrokeWidth(): number {
return (
- this.props.manager.numericBinStrokeWidth ??
- DEFAULT_NUMERIC_BIN_STROKE_WIDTH
+ this.props.numericBinStrokeWidth ?? DEFAULT_NUMERIC_BIN_STROKE_WIDTH
)
}
@@ -212,7 +240,7 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
}
@computed private get maxWidth(): number {
- return this.manager.legendMaxWidth ?? this.manager.legendWidth ?? 200
+ return this.props.legendMaxWidth ?? this.props.legendWidth ?? 200
}
private getTickLabelWidth(label: string): number {
@@ -240,8 +268,8 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
@computed private get isAutoWidth(): boolean {
return (
- this.manager.legendWidth === undefined &&
- this.manager.legendMaxWidth !== undefined
+ this.props.legendWidth === undefined &&
+ this.props.legendMaxWidth !== undefined
)
}
@@ -270,7 +298,7 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
shareOfTotal: (bin.max - bin.min) / this.rangeSize,
}))
// Make sure the legend is big enough to avoid overlapping labels (including `raisedMode`)
- if (this.manager.equalSizeBins) {
+ if (this.props.equalSizeBins) {
// Try to keep the minimum close to the size of the "No data" bin,
// so they look visually balanced somewhat.
const minBinWidth = this.fontSize * 3.25
@@ -326,7 +354,6 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
@computed private get positionedBins(): PositionedBin[] {
const {
- manager,
rangeSize,
availableNumericWidth,
visibleBins,
@@ -334,6 +361,7 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
legendTitleWidth,
x,
} = this
+ const { equalSizeBins } = this.props
let xOffset = x + legendTitleWidth
let prevBin: ColorScaleBin | undefined
@@ -344,7 +372,7 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
let marginLeft: number = isFirst ? 0 : this.itemMargin
if (bin instanceof NumericBin) {
- if (manager.equalSizeBins) {
+ if (equalSizeBins) {
width = availableNumericWidth / numericBins.length
} else {
width =
@@ -374,7 +402,7 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
}
@computed private get legendTitle(): TextWrap | undefined {
- const { legendTitle } = this.manager
+ const { legendTitle } = this.props
return legendTitle
? new TextWrap({
text: legendTitle,
@@ -498,8 +526,9 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
}
@action.bound private onMouseMove(ev: MouseEvent | TouchEvent): void {
- const { manager, base, positionedBins } = this
- const { numericFocusBracket } = manager
+ const { base, positionedBins } = this
+ const { numericFocusBracket, onLegendMouseLeave, onLegendMouseOver } =
+ this.props
if (base.current) {
const mouse = getRelativeMouse(base.current, ev)
@@ -512,8 +541,8 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
// If outside legend bounds, trigger onMouseLeave if there is an existing bin in focus.
if (!this.bounds.contains(mouse)) {
- if (numericFocusBracket && manager.onLegendMouseLeave)
- return manager.onLegendMouseLeave()
+ if (numericFocusBracket && onLegendMouseLeave)
+ return onLegendMouseLeave()
return
}
@@ -524,8 +553,8 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
newFocusBracket = bin.bin
})
- if (newFocusBracket && manager.onLegendMouseOver)
- manager.onLegendMouseOver(newFocusBracket)
+ if (newFocusBracket && onLegendMouseOver)
+ onLegendMouseOver(newFocusBracket)
}
}
@@ -546,14 +575,8 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
}
render(): React.ReactElement {
- const {
- manager,
- numericLabels,
- numericBinSize,
- positionedBins,
- height,
- } = this
- const { numericFocusBracket } = manager
+ const { numericLabels, numericBinSize, positionedBins, height } = this
+ const { numericFocusBracket, legendOpacity } = this.props
const stroke = this.numericBinStroke
const strokeWidth = this.numericBinStrokeWidth
@@ -604,7 +627,7 @@ export class HorizontalNumericColorLegend extends HorizontalColorLegend {
? `url(#${bin.patternRef})`
: bin.color
}
- opacity={manager.legendOpacity} // defaults to undefined which removes the prop
+ opacity={legendOpacity} // defaults to undefined which removes the prop
stroke={
isFocus ? FOCUS_BORDER_COLOR : stroke
}
@@ -700,16 +723,47 @@ const NumericBinRect = (props: NumericBinRectProps) => {
}
@observer
-export class HorizontalCategoricalColorLegend extends HorizontalColorLegend {
+export class HorizontalCategoricalColorLegend extends HorizontalColorLegend {
private rectPadding = 5
private markPadding = 5
+ static height(
+ props: Pick<
+ HorizontalCategoricalColorLegendProps,
+ | "categoricalLegendData"
+ | "fontSize"
+ | "legendWidth"
+ | "legendMaxWidth"
+ | "legendAlign"
+ >
+ ): number {
+ const legend = new HorizontalCategoricalColorLegend(props)
+ return legend.height
+ }
+
+ static numLines(
+ props: Pick<
+ HorizontalCategoricalColorLegendProps,
+ | "categoricalLegendData"
+ | "fontSize"
+ | "legendWidth"
+ | "legendMaxWidth"
+ >
+ ): number {
+ const legend = new HorizontalCategoricalColorLegend(props)
+ return legend.numLines
+ }
+
+ @computed private get categoryLegendY(): number {
+ return this.props.categoryLegendY ?? 0
+ }
+
@computed get width(): number {
- return this.manager.legendWidth ?? this.manager.legendMaxWidth ?? 200
+ return this.props.legendWidth ?? this.legendMaxWidth ?? 200
}
@computed private get categoricalLegendData(): CategoricalBin[] {
- return this.manager.categoricalLegendData ?? []
+ return this.props.categoricalLegendData ?? []
}
@computed private get visibleCategoricalBins(): CategoricalBin[] {
@@ -814,8 +868,8 @@ export class HorizontalCategoricalColorLegend extends HorizontalColorLegend {
}
renderLabels(): React.ReactElement {
- const { manager, marks } = this
- const { focusColors, hoverColors = [] } = manager
+ const { marks } = this
+ const { focusColors, hoverColors = [] } = this.props
return (
@@ -847,8 +901,13 @@ export class HorizontalCategoricalColorLegend extends HorizontalColorLegend {
}
renderSwatches(): React.ReactElement {
- const { manager, marks } = this
- const { activeColors, hoverColors = [] } = manager
+ const { marks } = this
+ const {
+ activeColors,
+ hoverColors = [],
+ legendOpacity,
+ categoricalBinStroke,
+ } = this.props
return (
@@ -869,7 +928,7 @@ export class HorizontalCategoricalColorLegend extends HorizontalColorLegend {
const opacity = isNotHovered
? GRAPHER_OPACITY_MUTE
- : manager.legendOpacity
+ : legendOpacity
return (
@@ -891,21 +950,21 @@ export class HorizontalCategoricalColorLegend extends HorizontalColorLegend {
}
renderInteractiveElements(): React.ReactElement {
- const { manager, marks } = this
+ const { marks, props } = this
return (
{marks.map((mark, index) => {
const mouseOver = (): void =>
- manager.onLegendMouseOver
- ? manager.onLegendMouseOver(mark.bin)
+ props.onLegendMouseOver
+ ? props.onLegendMouseOver(mark.bin)
: undefined
const mouseLeave = (): void =>
- manager.onLegendMouseLeave
- ? manager.onLegendMouseLeave()
+ props.onLegendMouseLeave
+ ? props.onLegendMouseLeave()
: undefined
- const click = manager.onLegendClick
- ? (): void => manager.onLegendClick?.(mark.bin)
+ const click = props.onLegendClick
+ ? (): void => props.onLegendClick?.(mark.bin)
: undefined
const cursor = click ? "pointer" : "default"
@@ -948,7 +1007,7 @@ export class HorizontalCategoricalColorLegend extends HorizontalColorLegend {
>
{this.renderSwatches()}
{this.renderLabels()}
- {!this.manager.isStatic && this.renderInteractiveElements()}
+ {!this.props.isStatic && this.renderInteractiveElements()}
)
}
diff --git a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.test.ts b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.test.ts
index bc77d3dee6f..42eef7e07db 100755
--- a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.test.ts
+++ b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.test.ts
@@ -293,16 +293,17 @@ describe("externalLegendBins", () => {
const chart = new LineChart({
manager: { ...baseManager, showLegend: true },
})
- expect(chart["externalLegend"]).toBeUndefined()
+ expect(chart.externalCategoricalLegend).toBeUndefined()
+ expect(chart.externalNumericLegend).toBeUndefined()
})
it("exposes externalLegendBins when legend is hidden", () => {
const chart = new LineChart({
manager: { ...baseManager, showLegend: false },
})
- expect(chart["externalLegend"]?.categoricalLegendData?.length).toEqual(
- 2
- )
+ expect(
+ chart.externalCategoricalLegend?.categoricalLegendData?.length
+ ).toEqual(2)
})
})
diff --git a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx
index dd54175a663..04d4e9202a5 100644
--- a/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx
+++ b/packages/@ourworldindata/grapher/src/lineCharts/LineChart.tsx
@@ -104,8 +104,9 @@ import { MultiColorPolyline } from "../scatterCharts/MultiColorPolyline"
import { CategoricalColorAssigner } from "../color/CategoricalColorAssigner"
import { darkenColorForLine } from "../color/ColorUtils"
import {
- HorizontalColorLegendManager,
+ HorizontalCategoricalColorLegendProps,
HorizontalNumericColorLegend,
+ HorizontalNumericColorLegendProps,
} from "../horizontalColorLegend/HorizontalColorLegends"
import {
AnnotationsMap,
@@ -344,11 +345,7 @@ export class LineChart
bounds?: Bounds
manager: LineChartManager
}>
- implements
- ChartInterface,
- AxisManager,
- ColorScaleManager,
- HorizontalColorLegendManager
+ implements ChartInterface, AxisManager, ColorScaleManager
{
base: React.RefObject = React.createRef()
@@ -498,7 +495,7 @@ export class LineChart
@computed private get boundsWithoutColorLegend(): Bounds {
return this.bounds.padTop(
- this.hasColorLegend ? this.legendHeight + LEGEND_PADDING : 0
+ this.hasColorLegend ? this.colorLegendHeight + LEGEND_PADDING : 0
)
}
@@ -931,7 +928,7 @@ export class LineChart
renderColorLegend(): React.ReactElement | void {
if (this.hasColorLegend)
- return
+ return
}
/**
@@ -1157,10 +1154,28 @@ export class LineChart
return this.manager.backgroundColor ?? GRAPHER_BACKGROUND_DEFAULT
}
- @computed get numericLegend(): HorizontalNumericColorLegend | undefined {
+ @computed get colorLegendHeight(): number {
return this.hasColorScale && this.manager.showLegend
- ? new HorizontalNumericColorLegend({ manager: this })
- : undefined
+ ? HorizontalNumericColorLegend.height(this.colorLegendProps)
+ : 0
+ }
+
+ get colorLegendProps(): HorizontalNumericColorLegendProps {
+ return {
+ fontSize: this.fontSize,
+ legendX: this.legendX,
+ legendAlign: this.legendAlign,
+ legendMaxWidth: this.legendMaxWidth,
+ numericLegendData: this.numericLegendData,
+ numericBinSize: this.numericBinSize,
+ numericBinStroke: this.numericBinStroke,
+ numericBinStrokeWidth: this.numericBinStrokeWidth,
+ equalSizeBins: this.equalSizeBins,
+ legendTitle: this.legendTitle,
+ numericLegendY: this.numericLegendY,
+ legendTextColor: this.legendTextColor,
+ legendTickSize: this.legendTickSize,
+ }
}
@computed get numericLegendY(): number {
@@ -1173,10 +1188,6 @@ export class LineChart
: undefined
}
- @computed get legendHeight(): number {
- return this.numericLegend?.height ?? 0
- }
-
// End of color legend props
@computed private get annotationsMap(): AnnotationsMap | undefined {
@@ -1467,11 +1478,10 @@ export class LineChart
return this.dualAxis.horizontalAxis
}
- @computed get externalLegend(): HorizontalColorLegendManager | undefined {
+ @computed get externalCategoricalLegend():
+ | HorizontalCategoricalColorLegendProps
+ | undefined {
if (!this.manager.showLegend) {
- const numericLegendData = this.hasColorScale
- ? this.numericLegendData
- : []
const categoricalLegendData = this.hasColorScale
? []
: this.series.map(
@@ -1483,6 +1493,20 @@ export class LineChart
color: series.color,
})
)
+ return {
+ categoricalLegendData,
+ }
+ }
+ return undefined
+ }
+
+ @computed get externalNumericLegend():
+ | HorizontalNumericColorLegendProps
+ | undefined {
+ if (!this.manager.showLegend) {
+ const numericLegendData = this.hasColorScale
+ ? this.numericLegendData
+ : []
return {
legendTitle: this.legendTitle,
legendTextColor: this.legendTextColor,
@@ -1492,7 +1516,6 @@ export class LineChart
numericBinStroke: this.numericBinStroke,
numericBinStrokeWidth: this.numericBinStrokeWidth,
numericLegendData,
- categoricalLegendData,
}
}
return undefined
diff --git a/packages/@ourworldindata/grapher/src/mapCharts/MapChart.tsx b/packages/@ourworldindata/grapher/src/mapCharts/MapChart.tsx
index 6a3c29eb8c7..bfbcc27d1e8 100644
--- a/packages/@ourworldindata/grapher/src/mapCharts/MapChart.tsx
+++ b/packages/@ourworldindata/grapher/src/mapCharts/MapChart.tsx
@@ -18,7 +18,6 @@ import { observable, computed, action } from "mobx"
import { observer } from "mobx-react"
import {
HorizontalCategoricalColorLegend,
- HorizontalColorLegendManager,
HorizontalNumericColorLegend,
} from "../horizontalColorLegend/HorizontalColorLegends"
import { MapProjectionGeos } from "./MapProjections"
@@ -163,7 +162,7 @@ const renderFeaturesFor = (
@observer
export class MapChart
extends React.Component
- implements ChartInterface, HorizontalColorLegendManager, ColorScaleManager
+ implements ChartInterface, ColorScaleManager
{
@observable focusEntity?: MapEntity
@observable focusBracket?: MapBracket
@@ -525,50 +524,72 @@ export class MapChart
return this.categoryLegendHeight + this.numericLegendHeight + 10
}
- @computed get numericLegendHeight(): number {
- return this.numericLegend ? this.numericLegend.height : 0
+ @computed private get hasCategoryLegend(): boolean {
+ return this.categoricalLegendData.length > 1
}
- @computed get categoryLegendHeight(): number {
- return this.categoryLegend ? this.categoryLegend.height + 5 : 0
+ @computed private get hasNumericLegend(): boolean {
+ return this.numericLegendData.length > 1
}
- @computed get categoryLegend():
- | HorizontalCategoricalColorLegend
- | undefined {
- return this.categoricalLegendData.length > 1
- ? new HorizontalCategoricalColorLegend({ manager: this })
- : undefined
+ @computed get categoryLegendHeight(): number {
+ return this.hasCategoryLegend
+ ? HorizontalCategoricalColorLegend.height({
+ fontSize: this.fontSize,
+ legendAlign: this.legendAlign,
+ legendMaxWidth: this.legendMaxWidth,
+ categoricalLegendData: this.categoricalLegendData,
+ }) + 5
+ : 0
+ }
+
+ @computed get categoryLegendNumLines(): number {
+ return this.hasCategoryLegend
+ ? HorizontalCategoricalColorLegend.numLines({
+ fontSize: this.fontSize,
+ legendMaxWidth: this.legendMaxWidth,
+ categoricalLegendData: this.categoricalLegendData,
+ })
+ : 0
}
- @computed get numericLegend(): HorizontalNumericColorLegend | undefined {
- return this.numericLegendData.length > 1
- ? new HorizontalNumericColorLegend({ manager: this })
- : undefined
+ @computed get numericLegendHeight(): number {
+ return this.hasNumericLegend
+ ? HorizontalNumericColorLegend.height({
+ fontSize: this.fontSize,
+ legendX: this.legendX,
+ legendAlign: this.legendAlign,
+ legendMaxWidth: this.legendMaxWidth,
+ numericLegendData: this.numericLegendData,
+ equalSizeBins: this.equalSizeBins,
+ })
+ : 0
}
@computed get categoryLegendY(): number {
- const { categoryLegend, bounds, categoryLegendHeight } = this
+ const { hasCategoryLegend, bounds, categoryLegendHeight } = this
- if (categoryLegend) return bounds.bottom - categoryLegendHeight
+ if (hasCategoryLegend) return bounds.bottom - categoryLegendHeight
return 0
}
@computed get legendAlign(): HorizontalAlign {
- if (this.numericLegend) return HorizontalAlign.center
- const { numLines = 0 } = this.categoryLegend ?? {}
- return numLines > 1 ? HorizontalAlign.left : HorizontalAlign.center
+ if (this.hasNumericLegend) return HorizontalAlign.center
+
+ return this.categoryLegendNumLines > 1
+ ? HorizontalAlign.left
+ : HorizontalAlign.center
}
@computed get numericLegendY(): number {
const {
- numericLegend,
+ hasNumericLegend,
numericLegendHeight,
bounds,
categoryLegendHeight,
} = this
- if (numericLegend)
+ if (hasNumericLegend)
return (
bounds.bottom - categoryLegendHeight - numericLegendHeight - 4
)
@@ -588,15 +609,37 @@ export class MapChart
}
renderMapLegend(): React.ReactElement {
- const { numericLegend, categoryLegend } = this
+ const { hasNumericLegend, hasCategoryLegend } = this
return (
<>
- {numericLegend && (
-
+ {hasNumericLegend && (
+
)}
- {categoryLegend && (
-
+ {hasCategoryLegend && (
+
)}
>
)
diff --git a/packages/@ourworldindata/grapher/src/scatterCharts/ScatterPlotChart.tsx b/packages/@ourworldindata/grapher/src/scatterCharts/ScatterPlotChart.tsx
index fc1ee9d3f3e..3b0dba2ad0e 100644
--- a/packages/@ourworldindata/grapher/src/scatterCharts/ScatterPlotChart.tsx
+++ b/packages/@ourworldindata/grapher/src/scatterCharts/ScatterPlotChart.tsx
@@ -844,7 +844,6 @@ export class ScatterPlotChart
legendY={this.legendY}
activeColors={this.activeColors}
focusColors={this.focusColors}
- isStatic={manager.isStatic}
/>
{sizeLegend && (
<>
diff --git a/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx b/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx
index 3d01ce36072..a63195b3998 100644
--- a/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx
+++ b/packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx
@@ -85,7 +85,7 @@ import {
} from "../lineCharts/LineChartHelpers"
import { SelectionArray } from "../selection/SelectionArray"
import { Halo } from "@ourworldindata/components"
-import { HorizontalColorLegendManager } from "../horizontalColorLegend/HorizontalColorLegends"
+import { HorizontalCategoricalColorLegendProps } from "../horizontalColorLegend/HorizontalColorLegends"
import { CategoricalBin } from "../color/ColorScaleBin"
import {
OWID_NON_FOCUSED_GRAY,
@@ -590,7 +590,9 @@ export class SlopeChart
: 0
}
- @computed get externalLegend(): HorizontalColorLegendManager | undefined {
+ @computed get externalCategoricalLegend():
+ | HorizontalCategoricalColorLegendProps
+ | undefined {
if (!this.manager.showLegend) {
const categoricalLegendData = this.series.map(
(series, index) =>
diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx
index 5b40a3086bb..e6824d85d18 100644
--- a/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx
+++ b/packages/@ourworldindata/grapher/src/stackedCharts/AbstractStackedChart.tsx
@@ -38,7 +38,7 @@ import { select } from "d3-selection"
import { ColorSchemes } from "../color/ColorSchemes"
import { SelectionArray } from "../selection/SelectionArray"
import { CategoricalBin } from "../color/ColorScaleBin"
-import { HorizontalColorLegendManager } from "../horizontalColorLegend/HorizontalColorLegends"
+import { HorizontalCategoricalColorLegendProps } from "../horizontalColorLegend/HorizontalColorLegends"
import {
CategoricalColorAssigner,
CategoricalColorMap,
@@ -436,7 +436,9 @@ export class AbstractStackedChart
return this.unstackedSeries
}
- @computed get externalLegend(): HorizontalColorLegendManager | undefined {
+ @computed get externalCategoricalLegend():
+ | HorizontalCategoricalColorLegendProps
+ | undefined {
if (!this.manager.showLegend) {
const categoricalLegendData = this.series
.map(
diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.tsx
index c95f9cc3c72..7de3e4b740c 100644
--- a/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.tsx
+++ b/packages/@ourworldindata/grapher/src/stackedCharts/MarimekkoChart.tsx
@@ -58,10 +58,7 @@ import {
makeTooltipRoundingNotice,
makeTooltipToleranceNotice,
} from "../tooltip/Tooltip"
-import {
- HorizontalCategoricalColorLegend,
- HorizontalColorLegendManager,
-} from "../horizontalColorLegend/HorizontalColorLegends"
+import { HorizontalCategoricalColorLegend } from "../horizontalColorLegend/HorizontalColorLegends"
import { CategoricalBin, ColorScaleBin } from "../color/ColorScaleBin"
import { DualAxis, HorizontalAxis, VerticalAxis } from "../axis/Axis"
import { ColorScale, ColorScaleManager } from "../color/ColorScale"
@@ -262,7 +259,7 @@ export class MarimekkoChart
manager: MarimekkoChartManager
containerElement?: HTMLDivElement
}>
- implements ChartInterface, HorizontalColorLegendManager, ColorScaleManager
+ implements ChartInterface, ColorScaleManager
{
base: React.RefObject = React.createRef()
@@ -551,7 +548,7 @@ export class MarimekkoChart
return this.bounds
.padBottom(this.longestLabelHeight + 2)
.padBottom(labelLinesHeight)
- .padTop(this.legend.height + this.legendPaddingTop)
+ .padTop(this.legendHeight + this.legendPaddingTop)
.padLeft(marginToEnsureWidestEntityLabelFitsEvenIfAtX0)
}
@@ -838,7 +835,7 @@ export class MarimekkoChart
// legend props
@computed get legendPaddingTop(): number {
- return this.legend.height > 0 ? this.baseFontSize : 0
+ return this.legendHeight > 0 ? this.baseFontSize : 0
}
@computed get legendX(): number {
@@ -897,8 +894,13 @@ export class MarimekkoChart
this.focusColorBin = undefined
}
- @computed private get legend(): HorizontalCategoricalColorLegend {
- return new HorizontalCategoricalColorLegend({ manager: this })
+ @computed private get legendHeight(): number {
+ return HorizontalCategoricalColorLegend.height({
+ fontSize: this.fontSize,
+ legendAlign: this.legendAlign,
+ legendWidth: this.legendWidth,
+ categoricalLegendData: this.categoricalLegendData,
+ })
}
@computed private get formatColumn(): CoreColumn {
@@ -1053,7 +1055,18 @@ export class MarimekkoChart
}
detailsMarker={manager.detailsMarkerInSvg}
/>
-
+
{this.renderBars()}
{target && (
{
const chart = new StackedAreaChart({
manager: { ...baseManager, showLegend: true },
})
- expect(chart["externalLegend"]).toBeUndefined()
+ expect(chart.externalCategoricalLegend).toBeUndefined()
})
it("exposes externalLegendBins when legend is hidden", () => {
const chart = new StackedAreaChart({
manager: { ...baseManager, showLegend: false },
})
- expect(chart["externalLegend"]?.categoricalLegendData?.length).toEqual(
- 2
- )
+ expect(
+ chart.externalCategoricalLegend?.categoricalLegendData?.length
+ ).toEqual(2)
})
})
diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/StackedBarChart.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/StackedBarChart.tsx
index 922f0a3d9f6..4fdc2e90b7c 100644
--- a/packages/@ourworldindata/grapher/src/stackedCharts/StackedBarChart.tsx
+++ b/packages/@ourworldindata/grapher/src/stackedCharts/StackedBarChart.tsx
@@ -210,7 +210,7 @@ export class StackedBarChart
@computed protected get paddingForLegendTop(): number {
return this.showHorizontalLegend
- ? this.horizontalColorLegend.height + 8
+ ? this.horizontalColorLegendHeight + 8
: 0
}
@@ -306,13 +306,9 @@ export class StackedBarChart
@computed get sidebarWidth(): number {
if (!this.manager.showLegend) return 0
- const {
- sidebarMinWidth,
- sidebarMaxWidth,
- verticalColorLegend: legendDimensions,
- } = this
+ const { sidebarMinWidth, sidebarMaxWidth, verticalColorLegend } = this
return Math.max(
- Math.min(legendDimensions.width, sidebarMaxWidth),
+ Math.min(verticalColorLegend.width, sidebarMaxWidth),
sidebarMinWidth
)
}
@@ -329,8 +325,13 @@ export class StackedBarChart
}
@computed
- private get horizontalColorLegend(): HorizontalCategoricalColorLegend {
- return new HorizontalCategoricalColorLegend({ manager: this })
+ private get horizontalColorLegendHeight(): number {
+ return HorizontalCategoricalColorLegend.height({
+ fontSize: this.fontSize,
+ legendAlign: this.legendAlign,
+ legendWidth: this.legendWidth,
+ categoricalLegendData: this.categoricalLegendData,
+ })
}
@computed get formatColumn(): CoreColumn {
@@ -480,7 +481,18 @@ export class StackedBarChart
if (!showLegend) return
return showHorizontalLegend ? (
-
+
) : (
{
const chart = new StackedDiscreteBarChart({
manager: { ...baseManager, showLegend: true },
})
- expect(chart["legend"].height).toBeGreaterThan(0)
+ expect(chart["legendHeight"]).toBeGreaterThan(0)
expect(chart["categoricalLegendData"].length).toBeGreaterThan(0)
- expect(chart["externalLegend"]).toBeUndefined()
+ expect(chart["externalCategoricalLegend"]).toBeUndefined()
})
it("exposes externalLegendBins when showLegend is false", () => {
const chart = new StackedDiscreteBarChart({
manager: { ...baseManager, showLegend: false },
})
- expect(chart["legend"].height).toEqual(0)
+ expect(chart["legendHeight"]).toEqual(0)
expect(chart["categoricalLegendData"].length).toEqual(0)
- expect(chart["externalLegend"]?.categoricalLegendData?.length).toEqual(
- 2
- )
+ expect(
+ chart["externalCategoricalLegend"]?.categoricalLegendData?.length
+ ).toEqual(2)
})
})
diff --git a/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx b/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx
index b298d2ae160..d26714dbb60 100644
--- a/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx
+++ b/packages/@ourworldindata/grapher/src/stackedCharts/StackedDiscreteBarChart.tsx
@@ -68,7 +68,7 @@ import { StackedPoint, StackedSeries } from "./StackedConstants"
import { ColorSchemes } from "../color/ColorSchemes"
import {
HorizontalCategoricalColorLegend,
- HorizontalColorLegendManager,
+ HorizontalCategoricalColorLegendProps,
} from "../horizontalColorLegend/HorizontalColorLegends"
import { CategoricalBin, ColorScaleBin } from "../color/ColorScaleBin"
import { isDarkColor } from "../color/ColorUtils"
@@ -131,7 +131,7 @@ export class StackedDiscreteBarChart
manager: StackedDiscreteBarChartManager
containerElement?: HTMLDivElement
}>
- implements ChartInterface, HorizontalColorLegendManager
+ implements ChartInterface
{
base: React.RefObject = React.createRef()
@@ -330,8 +330,8 @@ export class StackedDiscreteBarChart
@computed private get boundsWithoutLegend(): Bounds {
return this.bounds.padTop(
- this.showLegend && this.legend.height > 0
- ? this.legend.height + this.legendPaddingTop
+ this.showLegend && this.legendHeight > 0
+ ? this.legendHeight + this.legendPaddingTop
: 0
)
}
@@ -525,7 +525,9 @@ export class StackedDiscreteBarChart
return this.showLegend ? this.legendBins : []
}
- @computed get externalLegend(): HorizontalColorLegendManager | undefined {
+ @computed get externalCategoricalLegend():
+ | HorizontalCategoricalColorLegendProps
+ | undefined {
if (!this.showLegend) {
return {
categoricalLegendData: this.legendBins,
@@ -546,8 +548,13 @@ export class StackedDiscreteBarChart
this.focusSeriesName = undefined
}
- @computed private get legend(): HorizontalCategoricalColorLegend {
- return new HorizontalCategoricalColorLegend({ manager: this })
+ @computed private get legendHeight(): number {
+ return HorizontalCategoricalColorLegend.height({
+ fontSize: this.fontSize,
+ legendAlign: this.legendAlign,
+ legendWidth: this.legendWidth,
+ categoricalLegendData: this.categoricalLegendData,
+ })
}
@computed private get formatColumn(): CoreColumn {
@@ -724,7 +731,19 @@ export class StackedDiscreteBarChart
renderLegend(): React.ReactElement | void {
if (!this.showLegend) return
- return
+ return (
+
+ )
}
renderStatic(): React.ReactElement {
diff --git a/packages/@ourworldindata/grapher/src/verticalColorLegend/VerticalColorLegend.tsx b/packages/@ourworldindata/grapher/src/verticalColorLegend/VerticalColorLegend.tsx
index 5d33814e6f7..38585abcdcf 100644
--- a/packages/@ourworldindata/grapher/src/verticalColorLegend/VerticalColorLegend.tsx
+++ b/packages/@ourworldindata/grapher/src/verticalColorLegend/VerticalColorLegend.tsx
@@ -24,11 +24,6 @@ export interface VerticalColorLegendProps {
isStatic?: boolean
}
-type VerticalColorLegendPropsMinimal = Pick<
- VerticalColorLegendProps,
- "legendItems" | "maxLegendWidth" | "fontSize" | "legendTitle"
->
-
export interface LegendItem {
label?: string
minText?: string
@@ -49,10 +44,12 @@ export class VerticalColorLegend extends React.Component
+ ): { width: number; height: number } {
const legend = new VerticalColorLegend(props)
return {
width: legend.width,