All files / owid-grapher/grapher/tooltip Tooltip.tsx

63.43% Statements 85/134
64.29% Branches 9/14
75% Functions 9/12
63.43% Lines 85/134

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 1111x 1x 1x 1x 1x 1x 1x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x         13x 13x                                                                               13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 4x 4x 13x 13x 13x 13x 13x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x     1x 1x     1x 1x     1x 1x 1x 1x 1x 1x
import * as React from "react"
import { observable, computed, action } from "mobx"
import { observer } from "mobx-react"
import { Bounds } from "../../clientUtils/Bounds"
import { TooltipProps, TooltipManager } from "./TooltipProps"
 
@observer
export class TooltipView extends React.Component<{
    tooltipProvider: TooltipManager
    width: number
    height: number
}> {
    @computed private get rendered() {
        const { bounds } = this
        const tooltipProvider = this.props.tooltipProvider
 
        if (!tooltipProvider.tooltip) return null

        const tooltip = tooltipProvider.tooltip

        const offsetX = tooltip.offsetX ?? 0
        let offsetY = tooltip.offsetY ?? 0
        if (tooltip.offsetYDirection === "upward") {
            offsetY = -offsetY - (bounds?.height ?? 0)
        }

        let x = tooltip.x + offsetX
        let y = tooltip.y + offsetY

        // Ensure tooltip remains inside chart
        if (bounds) {
            if (x + bounds.width > this.props.width)
                x -= bounds.width + 2 * offsetX
            if (y + bounds.height > this.props.height)
                y -= bounds.height + 2 * offsetY
            if (x < 0) x = 0
            if (y < 0) y = 0
        }

        const tooltipStyle: React.CSSProperties = {
            position: "absolute",
            pointerEvents: "none",
            left: `${x}px`,
            top: `${y}px`,
            whiteSpace: "nowrap",
            backgroundColor: "rgba(255,255,255,0.92)",
            boxShadow: "0 2px 2px rgba(0,0,0,.12), 0 0 1px rgba(0,0,0,.35)",
            borderRadius: "2px",
            textAlign: "left",
            fontSize: "0.9em",
        }

        return (
            <div
                ref={this.base}
                className="Tooltip"
                style={{ ...tooltipStyle, ...tooltip.style }}
            >
                {tooltip.children}
            </div>
        )
    }
 
    private base: React.RefObject<HTMLDivElement> = React.createRef()
 
    @observable.struct private bounds?: Bounds
    @action.bound private updateBounds(): void {
        if (this.base.current)
            this.bounds = Bounds.fromElement(this.base.current)
    }
 
    componentDidMount(): void {
        this.updateBounds()
    }
 
    componentDidUpdate(): void {
        this.updateBounds()
    }
 
    render(): JSX.Element | null {
        return this.rendered
    }
}
 
@observer
export class Tooltip extends React.Component<TooltipProps> {
    componentDidMount(): void {
        this.connectTooltipToContainer()
    }
 
    @action.bound private connectTooltipToContainer(): void {
        this.props.tooltipManager.tooltip = this.props
    }
 
    @action.bound private removeToolTipFromContainer(): void {
        this.props.tooltipManager.tooltip = undefined
    }
 
    componentDidUpdate(): void {
        this.connectTooltipToContainer()
    }
 
    componentWillUnmount(): void {
        this.removeToolTipFromContainer()
    }
 
    render(): null {
        return null
    }
}