github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/ccl/src/views/clusterviz/containers/map/sparkline.tsx (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Licensed as a CockroachDB Enterprise file under the Cockroach Community
     4  // License (the "License"); you may not use this file except in compliance with
     5  // the License. You may obtain a copy of the License at
     6  //
     7  //     https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt
     8  
     9  import d3 from "d3";
    10  import React from "react";
    11  
    12  import { NanoToMilli } from "src/util/convert";
    13  import { MetricsDataComponentProps } from "src/views/shared/components/metricQuery";
    14  import createChartComponent from "src/views/shared/util/d3-react";
    15  import { BACKGROUND_BLUE, MAIN_BLUE } from "src/views/shared/colors";
    16  
    17  interface SparklineConfig {
    18    width: number;
    19    height: number;
    20    backgroundColor: string;
    21    foregroundColor: string;
    22    formatCurrentValue: (value: number) => string;
    23  }
    24  
    25  interface Datapoint {
    26    timestamp: number;
    27    value: number;
    28  }
    29  
    30  interface SparklineChartProps {
    31    results: Datapoint[];
    32  }
    33  
    34  function sparklineChart(config: SparklineConfig) {
    35    const { width, height, backgroundColor, foregroundColor, formatCurrentValue } = config;
    36    const margin = {
    37      left: 1,
    38      top: 1,
    39      right: 1,
    40      bottom: 1,
    41    };
    42  
    43    const xScale = d3.scale.linear()
    44      .range([margin.left, width - margin.right]);
    45    const yScale = d3.scale.linear()
    46      .range([height - margin.bottom, margin.top]);
    47  
    48    const drawPath = d3.svg.line<Datapoint>()
    49      .x((d: Datapoint) => xScale(d.timestamp))
    50      .y((d: Datapoint) => yScale(d.value));
    51  
    52    return function renderSparkline(sel: d3.Selection<SparklineChartProps>) {
    53      // TODO(couchand): unsingletonize this
    54      const { results } = sel.datum();
    55  
    56      xScale.domain(d3.extent(results, (d: Datapoint) => d.timestamp));
    57      yScale.domain(d3.extent(results, (d: Datapoint) => d.value));
    58  
    59      const bg = sel.selectAll("rect")
    60        .data([null]);
    61  
    62      bg
    63        .enter()
    64        .append("rect")
    65        .attr("width", width)
    66        .attr("height", height)
    67        .attr("fill", backgroundColor)
    68        .attr("fill-opacity", 1)
    69        .attr("stroke", "none");
    70  
    71      const line = sel.selectAll("path")
    72        .data([results]);
    73  
    74      line
    75        .enter()
    76        .append("path")
    77        .attr("fill", "none")
    78        .attr("stroke", foregroundColor);
    79  
    80      line
    81        .attr("d", drawPath);
    82  
    83      const lastDatapoint = results && results.length ? results[results.length - 1].value : 0;
    84  
    85      const text = sel.selectAll("text")
    86        .data([lastDatapoint]);
    87  
    88      text.enter()
    89        .append("text")
    90        .attr("x", width + 13)
    91        .attr("y", height - margin.bottom)
    92        .attr("text-anchor", "left")
    93        .attr("fill", foregroundColor)
    94        .attr("font-family", "Lato-Bold, Lato")
    95        .attr("font-size", 12)
    96        .attr("font-weight", 700);
    97  
    98      text
    99        .text(formatCurrentValue);
   100    };
   101  }
   102  
   103  interface SparklineMetricsDataComponentProps {
   104    formatCurrentValue: (value: number) => string;
   105  }
   106  
   107  export class SparklineMetricsDataComponent extends React.Component<MetricsDataComponentProps & SparklineMetricsDataComponentProps> {
   108    chart: React.ComponentClass<SparklineChartProps>;
   109  
   110    constructor(props: MetricsDataComponentProps & SparklineMetricsDataComponentProps) {
   111      super(props);
   112  
   113      this.chart = createChartComponent(
   114        "g",
   115        sparklineChart({
   116          width: 69,
   117          height: 10,
   118          backgroundColor: BACKGROUND_BLUE,
   119          foregroundColor: MAIN_BLUE,
   120          formatCurrentValue: this.props.formatCurrentValue,
   121        }),
   122      );
   123    }
   124  
   125    render() {
   126      const { data } = this.props;
   127      if (!data || !data.results || !data.results.length) {
   128        return null;
   129      }
   130  
   131      const timestamps: number[] = [];
   132      const resultsByTimestamp: { [timestamp: string]: Datapoint } = {};
   133  
   134      data.results.forEach(({ datapoints }) => {
   135        datapoints
   136          .forEach(({ timestamp_nanos, value }) => {
   137            const timestamp = NanoToMilli(timestamp_nanos.toNumber());
   138  
   139            if (timestamps.indexOf(timestamp) !== -1) {
   140              resultsByTimestamp[timestamp].value += value;
   141            } else {
   142              resultsByTimestamp[timestamp] = { timestamp, value };
   143              timestamps.push(timestamp);
   144            }
   145          });
   146      });
   147  
   148      const results = timestamps.map((timestamp) => resultsByTimestamp[timestamp]);
   149  
   150      // tslint:disable-next-line:variable-name
   151      const Sparkline = this.chart;
   152  
   153      return <Sparkline results={results} />;
   154    }
   155  }