Skip to content

Commit

Permalink
🔨 (slope) refactor & small adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed Dec 3, 2024
1 parent 175f15c commit ce3e37a
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ it("should place the second segment in a new line if preferred", () => {
const textWrapGroup = new TextWrapGroup({
fragments: [
{ text: TEXT },
{ text: "30 million", preferLineBreakOverWrapping: true },
{ text: "30 million", newLine: "avoid-wrap" },
],
maxWidth,
fontSize: FONT_SIZE,
Expand All @@ -65,7 +65,7 @@ it("should place the second segment in the same line if possible", () => {
const textWrapGroup = new TextWrapGroup({
fragments: [
{ text: TEXT },
{ text: "30 million", preferLineBreakOverWrapping: true },
{ text: "30 million", newLine: "avoid-wrap" },
],
maxWidth,
fontSize: FONT_SIZE,
Expand All @@ -82,6 +82,25 @@ it("should place the second segment in the same line if possible", () => {
)
})

it("should place the second segment in the same line if specified", () => {
const maxWidth = 1000
const textWrapGroup = new TextWrapGroup({
fragments: [{ text: TEXT }, { text: "30 million", newLine: "always" }],
maxWidth,
fontSize: FONT_SIZE,
})

// since the max width is large, "30 million" fits into the same line
// as the text of the first fragmemt
expect(textWrapGroup.height).toBeGreaterThan(
new TextWrap({
text: TEXT,
maxWidth,
fontSize: FONT_SIZE,
}).height
)
})

it("should use all available space when one fragment exceeds the given max width", () => {
const maxWidth = 150
const textWrap = new TextWrap({
Expand Down
27 changes: 17 additions & 10 deletions packages/@ourworldindata/components/src/TextWrap/TextWrapGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { Bounds, last, max } from "@ourworldindata/utils"
interface TextWrapFragment {
text: string
fontWeight?: number
preferLineBreakOverWrapping?: boolean
// "always" places the fragment in a new line in all cases
// "avoid-wrap" places the fragment in a new line only if the fragment would wrap otherwise
newLine?: "always" | "avoid-wrap"
}

interface PlacedTextWrap {
Expand Down Expand Up @@ -92,10 +94,18 @@ export class TextWrapGroup {
const { textWrap: lastTextWrap, yOffset: lastYOffset } =
textWraps[i - 1]

let textWrap = this.makeTextWrapForFragment(
fragment,
lastTextWrap.lastLineWidth + whitespaceWidth
)
// x-offset for the new text wrap
const offset = lastTextWrap.lastLineWidth + whitespaceWidth

// place the text wrap in a new line
if (fragment.newLine === "always" || offset > this.maxWidth) {
const textWrap = this.makeTextWrapForFragment(fragment)
const yOffset = lastYOffset + lastTextWrap.height
textWraps.push({ textWrap, yOffset })
continue
}

let textWrap = this.makeTextWrapForFragment(fragment, offset)

let yOffset = lastYOffset
if (textWrap.firstLineOffset === 0) {
Expand All @@ -107,10 +117,7 @@ export class TextWrapGroup {

// some fragments are preferred to break into a new line
// instead of being wrapped
if (
fragment.preferLineBreakOverWrapping &&
textWrap.lineCount > 1
) {
if (fragment.newLine === "avoid-wrap" && textWrap.lineCount > 1) {
textWrap = this.makeTextWrapForFragment(fragment)
yOffset += lastTextWrap.singleLineHeight
}
Expand Down Expand Up @@ -141,7 +148,7 @@ export class TextWrapGroup {
}

@computed get lines(): {
fragments: Omit<TextWrapFragment, "preferLineBreakOverWrapping">[]
fragments: Omit<TextWrapFragment, "newLine">[]
textWrap: TextWrap
yOffset: number
}[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface LineLabelSeries extends ChartSeries {
yValue: number
annotation?: string
formattedValue?: string
placeFormattedValueInNewLine?: boolean
yRange?: [number, number]
}

Expand Down Expand Up @@ -385,7 +386,9 @@ export class LineLegend extends React.Component<LineLegendProps> {
label.formattedValue
? {
text: label.formattedValue,
preferLineBreakOverWrapping: true,
newLine: label.placeFormattedValueInNewLine
? "always"
: "avoid-wrap",
}
: undefined,
])
Expand Down
53 changes: 27 additions & 26 deletions packages/@ourworldindata/grapher/src/slopeCharts/SlopeChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ export class SlopeChart
}

@computed get maxLineLegendWidth(): number {
return this.innerBounds.width / 4
return 0.25 * this.innerBounds.width
}

@computed get lineLegendFontSize(): number {
Expand All @@ -526,15 +526,13 @@ export class SlopeChart
// can't use this.lineLegendSeriesLeft due to a circular dependency
const lineLegendSeries = this.series.map((series) => {
const { seriesName, color, start } = series
const showSeriesName = false
const value = this.formatColumn.formatValueShort(start.value)
const label = showSeriesName ? seriesName : value
const formattedValue = showSeriesName ? value : undefined
const formattedValue = this.formatColumn.formatValueShort(
start.value
)
return {
color,
seriesName,
label,
formattedValue,
label: formattedValue,
yValue: start.value,
}
})
Expand Down Expand Up @@ -579,10 +577,28 @@ export class SlopeChart
this.lineLegendWidthLeft + LINE_LEGEND_PADDING
const lineLegendWidthRight =
this.lineLegendWidthRight + LINE_LEGEND_PADDING
const chartAreaWidth = this.innerBounds.width

// start and end value when the slopes are as wide as possible
const minStartX =
this.innerBounds.x + this.yAxisWidth + lineLegendWidthLeft
const maxEndX = this.innerBounds.right - lineLegendWidthRight

// use all available space if the chart is narrow
if (this.manager.isSemiNarrow) {
return [minStartX, maxEndX]
}

const offset = 0.25
let startX = this.innerBounds.x + offset * chartAreaWidth
let endX = this.innerBounds.right - offset * chartAreaWidth

// make sure the start and end values are within the bounds
startX = Math.max(startX, minStartX)
endX = Math.min(endX, maxEndX)

// pick a reasonable max width based on an ideal aspect ratio
const idealAspectRatio = 0.6
const chartAreaWidth = this.innerBounds.width
const idealAspectRatio = 0.9
const availableWidth =
chartAreaWidth -
this.yAxisWidth -
Expand All @@ -591,23 +607,6 @@ export class SlopeChart
const idealWidth = idealAspectRatio * this.bounds.height
const maxSlopeWidth = Math.min(idealWidth, availableWidth)

let startX: number, endX: number
if (this.manager.isSemiNarrow) {
startX = this.bounds.x + this.yAxisWidth + 4 + lineLegendWidthLeft
endX = this.bounds.x + chartAreaWidth - lineLegendWidthRight
} else {
startX =
this.bounds.x +
Math.max(
0.25 * chartAreaWidth,
this.yAxisWidth + 4 + lineLegendWidthLeft
)
endX =
this.bounds.x +
chartAreaWidth -
Math.max(0.25 * chartAreaWidth, lineLegendWidthRight)
}

const currentSlopeWidth = endX - startX
if (currentSlopeWidth > maxSlopeWidth) {
const padding = currentSlopeWidth - maxSlopeWidth
Expand Down Expand Up @@ -651,6 +650,7 @@ export class SlopeChart
seriesName,
label,
formattedValue,
valueInNewLine: this.useCompactLineLegend,
yValue: start.value,
}
})
Expand All @@ -666,6 +666,7 @@ export class SlopeChart
label: seriesName,
annotation: this.useCompactLineLegend ? undefined : annotation,
formattedValue,
valueInNewLine: this.useCompactLineLegend,
yValue: end.value,
}
})
Expand Down

0 comments on commit ce3e37a

Please sign in to comment.