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 }