All files / owid-grapher/grapher/scatterCharts ScatterPoints.tsx

100% Statements 68/68
84.62% Branches 11/13
100% Functions 2/2
100% Lines 68/68

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 1151x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 175x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 380x 1x 1x 1x 1x 1x 1x 1x 1x 1x 230x 230x 230x 230x 205x 25x 25x                                                                                              
import { PointVector } from "../../clientUtils/PointVector"
import { first, last } from "../../clientUtils/Util"
import { observer } from "mobx-react"
import React from "react"
import { MultiColorPolyline } from "./MultiColorPolyline"
import { ScatterRenderSeries } from "./ScatterPlotChartConstants"
import { Triangle } from "./Triangle"
 
// When there's only a single point in a series (e.g. single year mode)
@observer
export class ScatterPoint extends React.Component<{
    series: ScatterRenderSeries
    isLayerMode?: boolean
    isConnected?: boolean
}> {
    render(): JSX.Element | null {
        const { series, isLayerMode, isConnected } = this.props
        const value = first(series.points)
        if (value === undefined) return null
 
        const color = series.isFocus || !isLayerMode ? value.color : "#e2e2e2"
 
        const isLabelled = series.allLabels.some((label) => !label.isHidden)
        const size =
            !series.isFocus && isConnected ? 1 + value.size / 16 : value.size
        const cx = value.position.x.toFixed(2)
        const cy = value.position.y.toFixed(2)
        const stroke = isLayerMode ? "#bbb" : isLabelled ? "#333" : "#666"
 
        return (
            <g key={series.displayKey} className={series.displayKey}>
                {series.isFocus && (
                    <circle
                        cx={cx}
                        cy={cy}
                        fill="none"
                        stroke={color}
                        r={(size + 3).toFixed(2)}
                    />
                )}
                <circle
                    cx={cx}
                    cy={cy}
                    r={size.toFixed(2)}
                    fill={color}
                    opacity={0.8}
                    stroke={stroke}
                    strokeWidth={0.5}
                />
            </g>
        )
    }
}
 
@observer
export class ScatterLine extends React.Component<{
    series: ScatterRenderSeries
    isLayerMode: boolean
    isConnected: boolean
}> {
    render(): JSX.Element | null {
        const { series, isLayerMode, isConnected } = this.props
 
        if (series.points.length === 1)
            return (
                <ScatterPoint
                    series={series}
                    isLayerMode={isLayerMode}
                    isConnected={isConnected}
                />
            )
 
        const firstValue = first(series.points)
        const lastValue = last(series.points)
        if (firstValue === undefined || lastValue === undefined) return null
 
        let rotation = PointVector.angle(series.offsetVector, PointVector.up)
        if (series.offsetVector.x < 0) rotation = -rotation
 
        const opacity = 0.7
 
        return (
            <g key={series.displayKey} className={series.displayKey}>
                <circle
                    cx={firstValue.position.x.toFixed(2)}
                    cy={firstValue.position.y.toFixed(2)}
                    r={(1 + firstValue.size / 25).toFixed(1)}
                    fill={isLayerMode ? "#e2e2e2" : firstValue.color}
                    stroke="none"
                    opacity={opacity}
                />
                <MultiColorPolyline
                    points={series.points.map((v) => ({
                        x: v.position.x,
                        y: v.position.y,
                        color: isLayerMode ? "#ccc" : v.color,
                    }))}
                    strokeWidth={(0.3 + series.size / 16).toFixed(2)}
                    opacity={opacity}
                />
                <Triangle
                    transform={`rotate(${rotation}, ${lastValue.position.x.toFixed(
                        2
                    )}, ${lastValue.position.y.toFixed(2)})`}
                    cx={lastValue.position.x}
                    cy={lastValue.position.y}
                    r={1.5 + lastValue.size / 16}
                    fill={isLayerMode ? "#e2e2e2" : lastValue.color}
                    opacity={opacity}
                />
            </g>
        )
    }
}