Skip to content

Commit

Permalink
✨ (grapher) improve SVG ouput for manual manipulation
Browse files Browse the repository at this point in the history
  • Loading branch information
sophiamersmann committed May 21, 2024
1 parent 0e85ff4 commit 0c65dde
Show file tree
Hide file tree
Showing 25 changed files with 434 additions and 249 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -643,9 +643,11 @@ export class MarkdownTextWrap extends React.Component<MarkdownTextWrapProps> {
{
textProps,
detailsMarker = "superscript",
id,
}: {
textProps?: React.SVGProps<SVGTextElement>
detailsMarker?: DetailsMarker
id?: string
} = {}
): JSX.Element | null {
const { fontSize, lineHeight } = this
Expand All @@ -670,7 +672,7 @@ export class MarkdownTextWrap extends React.Component<MarkdownTextWrapProps> {
yOffset + lineHeight * fontSize * lineIndex

return (
<g className="markdown-text-wrap">
<g id={id} className="markdown-text-wrap">
<text
x={x.toFixed(1)}
y={yOffset.toFixed(1)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,10 @@ export class TextWrap {
render(
x: number,
y: number,
{ textProps }: { textProps?: React.SVGProps<SVGTextElement> } = {}
{
textProps,
id,
}: { textProps?: React.SVGProps<SVGTextElement>; id?: string } = {}
): JSX.Element | null {
const { props, lines, fontSize, fontWeight, lineHeight } = this

Expand All @@ -258,6 +261,7 @@ export class TextWrap {
y + (containerHeight - (containerHeight - textHeight) / 2)
return (
<text
id={id}
fontSize={fontSize.toFixed(2)}
fontWeight={fontWeight}
x={x.toFixed(1)}
Expand Down
208 changes: 99 additions & 109 deletions packages/@ourworldindata/grapher/src/axis/AxisViews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
VerticalAlign,
dyFromAlign,
textAnchorFromAlign,
makeIdForHumanConsumption,
} from "@ourworldindata/utils"
import { VerticalAxis, HorizontalAxis, DualAxis } from "./Axis"
import classNames from "classnames"
Expand Down Expand Up @@ -36,8 +37,13 @@ export class VerticalAxisGridLines extends React.Component<{
const axis = verticalAxis.clone()
axis.range = bounds.yRange()

console.log(dasharrayFromFontSize(verticalAxis.tickFontSize))

Check warning on line 40 in packages/@ourworldindata/grapher/src/axis/AxisViews.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected console statement

Check warning on line 40 in packages/@ourworldindata/grapher/src/axis/AxisViews.tsx

View workflow job for this annotation

GitHub Actions / eslint

Unexpected console statement

return (
<g className={classNames("AxisGridLines", "horizontalLines")}>
<g
id={makeIdForHumanConsumption("horizontal-grid-lines")}
className={classNames("AxisGridLines", "horizontalLines")}
>
{axis.getTickValues().map((t, i) => {
const color = t.faint
? FAINT_TICK_COLOR
Expand All @@ -47,6 +53,10 @@ export class VerticalAxisGridLines extends React.Component<{

return (
<line
id={makeIdForHumanConsumption(
"grid-line",
t.value.toString()
)}
key={i}
x1={bounds.left.toFixed(2)}
y1={axis.place(t.value)}
Expand Down Expand Up @@ -86,7 +96,10 @@ export class HorizontalAxisGridLines extends React.Component<{
axis.range = bounds.xRange()

return (
<g className={classNames("AxisGridLines", "verticalLines")}>
<g
id={makeIdForHumanConsumption("vertical-grid-lines")}
className={classNames("AxisGridLines", "verticalLines")}
>
{axis.getTickValues().map((t, i) => {
const color = t.faint
? FAINT_TICK_COLOR
Expand All @@ -96,6 +109,10 @@ export class HorizontalAxisGridLines extends React.Component<{

return (
<line
id={makeIdForHumanConsumption(
"grid-line",
t.value.toString()
)}
key={i}
x1={axis.place(t.value)}
y1={bounds.bottom.toFixed(2)}
Expand Down Expand Up @@ -131,6 +148,7 @@ export class HorizontalAxisZeroLine extends React.Component<{

return (
<g
id={makeIdForHumanConsumption("vertical-zero-line")}
className={classNames(
"AxisGridLines",
"verticalLines",
Expand Down Expand Up @@ -213,7 +231,10 @@ export class DualAxisComponent extends React.Component<DualAxisViewProps> {
)

return (
<g className="DualAxisView">
<g
id={makeIdForHumanConsumption("dual-axis")}
className="DualAxisView"
>
{horizontalAxisComponent}
{verticalAxisComponent}
{verticalGridlines}
Expand Down Expand Up @@ -242,52 +263,64 @@ export class VerticalAxisComponent extends React.Component<{
showTickMarks,
} = this.props
const { tickLabels, labelTextWrap } = verticalAxis

const tickMarks = showTickMarks ? (
<VerticalAxisTickMarks
tickMarkYPositions={tickLabels.map((label) =>
verticalAxis.place(label.value)
)}
tickMarkLeftPosition={bounds.left + verticalAxis.width}
color={SOLID_TICK_COLOR}
/>
) : undefined
const tickSize = 5

return (
<g className="VerticalAxis">
<g
id={makeIdForHumanConsumption("vertical-axis")}
className="VerticalAxis"
>
{labelTextWrap &&
labelTextWrap.renderSVG(
-verticalAxis.rangeCenter - labelTextWrap.width / 2,
bounds.left,
{
id: makeIdForHumanConsumption("axis-label"),
textProps: {
transform: "rotate(-90)",
fill: labelColor || GRAPHER_DARK_TEXT,
},
detailsMarker,
}
)}
{tickMarks}
{tickLabels.map((label, i) => {
{tickLabels.map((label) => {
const { y, xAlign, yAlign, formattedValue } = label
const tickLeft = bounds.left + verticalAxis.width
const tickY = verticalAxis.place(label.value)
return (
<text
key={i}
x={(
bounds.left +
verticalAxis.width -
verticalAxis.labelPadding
).toFixed(2)}
y={y}
dy={dyFromAlign(yAlign ?? VerticalAlign.middle)}
textAnchor={textAnchorFromAlign(
xAlign ?? HorizontalAlign.right
<g
id={makeIdForHumanConsumption(
"tick",
formattedValue
)}
fill={tickColor || GRAPHER_DARK_TEXT}
fontSize={verticalAxis.tickFontSize}
key={formattedValue}
>
{formattedValue}
</text>
{showTickMarks && (
<line
x1={tickLeft}
y1={tickY}
x2={tickLeft + tickSize}
y2={tickY}
stroke={SOLID_TICK_COLOR}
/>
)}
<text
x={(
bounds.left +
verticalAxis.width -
verticalAxis.labelPadding
).toFixed(2)}
y={y}
dy={dyFromAlign(yAlign ?? VerticalAlign.middle)}
textAnchor={textAnchorFromAlign(
xAlign ?? HorizontalAlign.right
)}
fill={tickColor || GRAPHER_DARK_TEXT}
fontSize={verticalAxis.tickFontSize}
>
{formattedValue}
</text>
</g>
)
})}
</g>
Expand Down Expand Up @@ -333,6 +366,7 @@ export class HorizontalAxisComponent extends React.Component<{
detailsMarker,
} = this.props
const { tickLabels, labelTextWrap: label, labelOffset, orient } = axis
const tickSize = 5
const horizontalAxisLabelsOnTop = orient === Position.top
const labelYPosition = horizontalAxisLabelsOnTop
? bounds.top
Expand All @@ -342,106 +376,62 @@ export class HorizontalAxisComponent extends React.Component<{
? bounds.top + axis.height - 5
: preferredAxisPosition ?? bounds.bottom

const tickMarks = showTickMarks ? (
<HorizontalAxisTickMarks
tickMarkTopPosition={tickMarksYPosition}
tickMarkXPositions={tickLabels.map((label): number =>
axis.place(label.value)
)}
color={SOLID_TICK_COLOR}
width={tickMarkWidth}
/>
) : undefined

const tickLabelYPlacement = horizontalAxisLabelsOnTop
? bounds.top + labelOffset + 10
: bounds.bottom - labelOffset

return (
<g className="HorizontalAxis">
<g
id={makeIdForHumanConsumption("horizontal-axis")}
className="HorizontalAxis"
>
{label &&
label.renderSVG(
axis.rangeCenter - label.width / 2,
labelYPosition,
{
id: makeIdForHumanConsumption("axis-label"),
textProps: {
fill: labelColor || GRAPHER_DARK_TEXT,
},
detailsMarker,
}
)}
{tickMarks}
{tickLabels.map((label, i) => {
{tickLabels.map((label) => {
const { x, xAlign, formattedValue } = label
return (
<text
key={i}
x={x}
y={tickLabelYPlacement}
fill={tickColor || GRAPHER_DARK_TEXT}
textAnchor={textAnchorFromAlign(
xAlign ?? HorizontalAlign.center
<g
id={makeIdForHumanConsumption(
"tick",
formattedValue
)}
fontSize={axis.tickFontSize}
key={formattedValue}
>
{formattedValue}
</text>
{showTickMarks && (
<line
x1={axis.place(label.value)}
y1={tickMarksYPosition}
x2={axis.place(label.value)}
y2={tickMarksYPosition + tickSize}
stroke={SOLID_TICK_COLOR}
strokeWidth={tickMarkWidth}
/>
)}
<text
x={x}
y={tickLabelYPlacement}
fill={tickColor || GRAPHER_DARK_TEXT}
textAnchor={textAnchorFromAlign(
xAlign ?? HorizontalAlign.center
)}
fontSize={axis.tickFontSize}
>
{formattedValue}
</text>
</g>
)
})}
</g>
)
}
}

export class HorizontalAxisTickMarks extends React.Component<{
tickMarkTopPosition: number
tickMarkXPositions: number[]
color: string
width?: number
}> {
render(): JSX.Element[] {
const { tickMarkTopPosition, tickMarkXPositions, color, width } =
this.props
const tickSize = 5
const tickBottom = tickMarkTopPosition + tickSize
return tickMarkXPositions.map((tickMarkPosition, index) => {
return (
<line
key={index}
x1={tickMarkPosition}
y1={tickMarkTopPosition}
x2={tickMarkPosition}
y2={tickBottom}
stroke={color}
strokeWidth={width}
/>
)
})
}
}

export class VerticalAxisTickMarks extends React.Component<{
tickMarkLeftPosition: number
tickMarkYPositions: number[]
color: string
width?: number
}> {
render(): JSX.Element[] {
const { tickMarkYPositions, tickMarkLeftPosition, color, width } =
this.props
const tickSize = 5
const tickRight = tickMarkLeftPosition + tickSize
return tickMarkYPositions.map((tickMarkPosition, index) => {
return (
<line
key={index}
x1={tickMarkLeftPosition}
y1={tickMarkPosition}
x2={tickRight}
y2={tickMarkPosition}
stroke={color}
strokeWidth={width}
/>
)
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
HorizontalAlign,
AxisAlign,
uniqBy,
makeIdForHumanConsumption,
} from "@ourworldindata/utils"
import { computed } from "mobx"
import { observer } from "mobx-react"
Expand Down Expand Up @@ -424,7 +425,11 @@ export class DiscreteBarChart
: GRAPHER_AXIS_LINE_WIDTH_DEFAULT

return (
<g ref={this.base} className="DiscreteBarChart">
<g
ref={this.base}
id={makeIdForHumanConsumption("discrete-bar-chart")}
className="DiscreteBarChart"
>
{this.hasProjectedData && (
<defs>
{/* passed to the legend as pattern for the
Expand Down Expand Up @@ -481,6 +486,10 @@ export class DiscreteBarChart
const result = (
<g
key={series.seriesName}
id={makeIdForHumanConsumption(
"bar",
series.seriesName
)}
className="bar"
transform={`translate(0, ${yOffset})`}
>
Expand Down

0 comments on commit 0c65dde

Please sign in to comment.