Home Reference Source

src/components/Chart/ChartEngine.js

import React from 'react';
import Highcharts from 'highcharts/highstock';
import HighchartsBoost from 'highcharts/modules/boost';
import strToColor from '../../utils/strToColor';
import moment from 'moment';

HighchartsBoost(Highcharts);

Highcharts.setOptions({
    lang: {
        months: [
            'Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec',
            'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'
        ],
        shortMonths: ['Sty', 'Lut', 'Mar', 'Kwi', 'Maj', 'Cze', 'Lip', 'Sie', 'Wrz', 'Paź', 'Lis', 'Gru'],
        weekdays: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota']
    },
    global: {
        useUTC: false
    }
});

const metricGeneral = metric => metric[Object.keys(metric)[0]];

const mapData = (data, withDataLabel) => {
    if (withDataLabel) {
        return data.map(({ time, value }) => ({
            x: moment(time).valueOf(),
            y: value
        }));
    } else {
        return data.map(({ time, value }) => [ moment(time).valueOf(), value ]);
    }
};

class ChartEngine extends React.Component {

    constructor(props) {
        super(props);
        this.chartRef = React.createRef();
    }

    componentDidMount() {
        this.createChart();
    }

    componentDidUpdate(prevProps) {
        if (
            this.props.metric1Measurements !== prevProps.metric1Measurements
            || this.props.metric1Measurements !== prevProps.metric2Measurements
        ) {
            this.destroyChart();
            this.createChart();
        }
    }

    componentWillUnmount() {
        this.destroyChart();
    }

    /*addPoints() {
        const { metric1Info, metric1LastUpdate, metric1Measurements, metric1HostsSelected } = this.props;
        metric1HostsSelected
            .map(host => ({
                host,
                points: metric1Measurements[host].slice(-metric1LastUpdate[host])
            }))
            .forEach(({ host, points }) => {
                let series = this.chart.get(`${metric1Info.id}:${host}`);
                points.forEach(({ time, value }) => {
                    series.removePoint(0, false);
                    series.addPoint({
                        x: moment(time).valueOf(),
                        y: value
                    }, false);
                });
            });
        this.chart.redraw(true);
    }*/

    createChart() {
        const {
            metric1, metric1Measurements,
            metric2, metric2Measurements,
            navigatorDisabled, withDataLabel
        } = this.props;
        const metric1General = metricGeneral(metric1);
        let yAxis = [
            {
                id: metric1General.id,
                title: {
                    text: metric1General.id
                },
                labels: {
                    format: '{value}' + metric1General.unit
                },
                opposite: false
            }
        ];
        let series = Object.entries(metric1Measurements).map(([ host, data ]) => ({
            name: `${metric1General.id}, ${host}`,
            id: `${metric1General.id}:${host}`,
            data: mapData(data, withDataLabel),
            color: strToColor(host),
            yAxis: metric1General.id,
            tooltip: {
                valueSuffix: metric1General.unit
            }
        }));
        if (metric2Measurements && Object.keys(metric2Measurements).length > 0) {
            const metric2General = metricGeneral(metric2);
            yAxis.push({
                id: metric2General.id,
                title: {
                    text: metric2General.id
                },
                labels: {
                    format: '{value}' + metric2General.unit
                },
                gridLineWidth: 0,
                opposite: true
            });
            series = series.concat(Object.entries(metric2Measurements || []).map(([ host, data ]) => ({
                name: `${metric2General.id}, ${host}`,
                id: `${metric2General.id}:${host}`,
                data: mapData(data, withDataLabel),
                color: strToColor(host),
                yAxis: metric2General.id,
                tooltip: {
                    valueSuffix: metric2General.unit
                },
                dashStyle: 'Dash'
            })));
        }
        this.chart = Highcharts.stockChart(this.chartRef.current, {
            series,
            plotOptions: {
                series: {
                    dataLabels: {
                        formatter: function() {
                            if (this.point.index !== this.series.data.length - 1) {
                                return null;
                            }
                            const nrFormatted = Highcharts.numberFormat(
                                this.y,
                                this.series.tooltipOptions.valueDecimals
                            );
                            const suffix = this.series.tooltipOptions.valueSuffix;
                            return `
                                <span style="color: ${this.color};">${this.series.name}</span><br />
                                <strong style="color: ${this.color};">${nrFormatted}${suffix}</strong>
                            `;
                        },
                        enabled: withDataLabel
                    }
                }
            },
            chart: {
                backgroundColor: 'transparent'
            },
            tooltip: {
                split: true,
                crosshairs: true,
                valueDecimals: 2
            },
            boost: {
                useGPUTranslations: true
            },
            scrollbar: {
                liveRedraw: false,
                enabled: !navigatorDisabled
            },
            rangeSelector: {
                inputEnabled: false
            },
            navigator: {
                enabled: !navigatorDisabled
            },
            yAxis
        });
        this.chartLoadTimeout = setTimeout(() => this.chart.reflow());
    }

    destroyChart() {
        if (this.chartLoadTimeout) {
            clearTimeout(this.chartLoadTimeout);
        }
        this.chart.destroy();
        this.chart = null;
    }

    render() {
        return <div className={`chart ${this.props.invalidated ? 'indeterminate' : ''}`} ref={this.chartRef}></div>;
    }

}

export default ChartEngine;