Skip to content

Commit

Permalink
✨ (slope) show value in the line legend
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Nov 26, 2024
1 parent a2fbbb5 commit a20188b
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 12 deletions.
85 changes: 76 additions & 9 deletions packages/@ourworldindata/grapher/src/lineLegend/LineLegend.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ export interface LineLabelSeries extends ChartSeries {
label: string
yValue: number
annotation?: string
formattedValue?: string
yRange?: [number, number]
}

interface SizedSeries extends LineLabelSeries {
textWrap: TextWrap
annotationTextWrap?: TextWrap
valueTextWrap?: TextWrap
width: number
height: number
}
Expand Down Expand Up @@ -202,6 +204,41 @@ class LineLabels extends React.Component<{
)
}

@computed private get textValues(): React.ReactElement | void {
const markersWithTextValues = this.markers.filter(
({ series }) => series.valueTextWrap !== undefined
)
if (!markersWithTextValues) return
return (
<g id={makeIdForHumanConsumption("text-values")}>
{markersWithTextValues.map(({ series, labelText }, index) => {
const textColor = darkenColorForText(series.color)
return (
<React.Fragment
key={getSeriesKey(
series,
index,
this.props.uniqueKey
)}
>
{series.valueTextWrap?.render(
labelText.x,
labelText.y + series.textWrap.height,
{
textProps: {
fill: textColor,
opacity: this.textOpacity,
textAnchor: this.anchor,
},
}
)}
</React.Fragment>
)
})}
</g>
)
}

@computed private get connectorLines(): React.ReactElement | void {
if (!this.props.needsConnectorLines) return
return (
Expand Down Expand Up @@ -278,6 +315,7 @@ class LineLabels extends React.Component<{
<>
{this.connectorLines}
{this.textAnnotations}
{this.textValues}
{this.textLabels}
{!this.props.isStatic && this.interactions}
</>
Expand All @@ -299,6 +337,7 @@ export interface LineLegendProps {
connectorLineWidth?: number
fontSize?: number
fontWeight?: number
showValueLabelsInline?: boolean

// used to determine which series should be labelled when there is limited space
seriesSortedByImportance?: EntityName[]
Expand Down Expand Up @@ -349,39 +388,67 @@ export class LineLegend extends React.Component<LineLegendProps> {
return this.props.connectorLineWidth ?? DEFAULT_CONNECTOR_LINE_WIDTH
}

@computed private get showValueLabelsInline(): boolean {
return this.props.showValueLabelsInline ?? true
}

@computed private get hasValueLabels(): boolean {
return this.props.labelSeries.some(
(series) => series.formattedValue !== undefined
)
}

@computed private get hideAnnotations(): boolean {
return this.hasValueLabels && !this.showValueLabelsInline
}

@computed.struct get sizedLabels(): SizedSeries[] {
const { fontSize, fontWeight, maxWidth } = this
const maxTextWidth = maxWidth - this.connectorLineWidth
const maxAnnotationWidth = Math.min(maxTextWidth, 150)

return this.props.labelSeries.map((label) => {
const annotationTextWrap = label.annotation
? new TextWrap({
text: label.annotation,
maxWidth: maxAnnotationWidth,
fontSize: fontSize * 0.9,
lineHeight: 1,
})
: undefined
const textWrap = new TextWrap({
text: label.label,
maxWidth: maxTextWidth,
fontSize,
fontWeight,
lineHeight: 1,
})
const annotationTextWrap =
!this.hideAnnotations && label.annotation
? new TextWrap({
text: label.annotation,
maxWidth: maxAnnotationWidth,
fontSize: fontSize * 0.9,
lineHeight: 1,
})
: undefined
const valueTextWrap = label.formattedValue
? new TextWrap({
text: label.formattedValue,
maxWidth: maxAnnotationWidth,
fontSize: fontSize * 0.9,
lineHeight: 1,
})
: undefined
return {
...label,
textWrap,
annotationTextWrap,
valueTextWrap,
width: Math.max(
textWrap.width,
annotationTextWrap ? annotationTextWrap.width : 0
annotationTextWrap ? annotationTextWrap.width : 0,
valueTextWrap ? valueTextWrap.width : 0
),
height:
textWrap.height +
(annotationTextWrap
? ANNOTATION_PADDING + annotationTextWrap.height
: 0) +
(valueTextWrap
? ANNOTATION_PADDING + valueTextWrap.height
: 0),
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ export class SlopeChart
yAxis: this.yAxis,
maxWidth: this.maxLineLegendWidth,
connectorLineWidth: this.lineLegendConnectorLinesWidth,
showValueLabelsInline: false,
fontSize: this.fontSize,
isStatic: this.manager.isStatic,
})
Expand All @@ -515,6 +516,7 @@ export class SlopeChart
yAxis: this.yAxis,
maxWidth: this.maxLineLegendWidth,
connectorLineWidth: this.lineLegendConnectorLinesWidth,
showValueLabelsInline: false,
fontSize: this.fontSize,
isStatic: this.manager.isStatic,
})
Expand Down Expand Up @@ -592,11 +594,13 @@ export class SlopeChart
@computed get lineLegendSeriesRight(): LineLabelSeries[] {
return this.series.map((series) => {
const { seriesName, color, end, annotation } = series
const formattedValue = this.formatColumn.formatValueLong(end.value)
return {
color,
seriesName,
label: seriesName,
annotation,
formattedValue,
yValue: end.value,
}
})
Expand Down Expand Up @@ -898,8 +902,10 @@ export class SlopeChart
x={this.xRange[1] + LINE_LEGEND_PADDING}
maxWidth={this.maxLineLegendWidth}
lineLegendAnchorX="start"
showValueLabelsInline={false}
connectorLineWidth={this.lineLegendConnectorLinesWidth}
fontSize={this.fontSize}
fontWeight={700}
isStatic={this.manager.isStatic}
focusedSeriesNames={this.focusedSeriesNames}
onMouseLeave={this.onLineLegendMouseLeave}
Expand All @@ -911,6 +917,7 @@ export class SlopeChart
x={this.xRange[0] - LINE_LEGEND_PADDING}
maxWidth={this.maxLineLegendWidth}
lineLegendAnchorX="end"
showValueLabelsInline={false}
connectorLineWidth={this.lineLegendConnectorLinesWidth}
fontSize={this.fontSize}
isStatic={this.manager.isStatic}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ export class StackedAreaChart extends AbstractStackedChart {
})
}

@computed get labelSeries(): LineLabelSeries[] {
@computed get lineLegendSeries(): LineLabelSeries[] {
const { midpoints } = this
return this.series
.map((series, index) => ({
Expand All @@ -318,7 +318,7 @@ export class StackedAreaChart extends AbstractStackedChart {
// only pass props that are required to calculate
// the width to avoid circular dependencies
return LineLegend.incorrectWidth({
labelSeries: this.labelSeries,
labelSeries: this.lineLegendSeries,
maxWidth: this.maxLineLegendWidth,
fontSize: this.fontSize,
})
Expand Down Expand Up @@ -657,7 +657,7 @@ export class StackedAreaChart extends AbstractStackedChart {
if (!this.manager.showLegend) return
return (
<LineLegend
labelSeries={this.labelSeries}
labelSeries={this.lineLegendSeries}
yAxis={this.yAxis}
x={this.lineLegendX}
yRange={this.lineLegendY}
Expand Down

0 comments on commit a20188b

Please sign in to comment.