Files
weave-scope/client/app/scripts/components/sparkline.js
Roland Schilter 8188b7aed2 Append links directly to metrics
The initial idea was to keep it separate since the unattached
links were also to be displayed distinctively from the metrics.
With the new design, unattached links are rendered in the same
list as metrics with attached links.

Therefore, we treat unattached metric links as an empty metric.
2017-08-11 16:45:13 +01:00

154 lines
4.7 KiB
JavaScript

// Forked from: https://github.com/KyleAMathews/react-sparkline at commit a9d7c5203d8f240938b9f2288287aaf0478df013
import React from 'react';
import PropTypes from 'prop-types';
import { min as d3Min, max as d3Max, mean as d3Mean } from 'd3-array';
import { isoParse as parseDate } from 'd3-time-format';
import { line, curveLinear } from 'd3-shape';
import { scaleLinear } from 'd3-scale';
import { formatMetricSvg } from '../utils/string-utils';
import { brightenColor, darkenColor } from '../utils/color-utils';
const HOVER_RADIUS_MULTIPLY = 1.5;
const HOVER_STROKE_MULTIPLY = 5;
export default class Sparkline extends React.Component {
constructor(props, context) {
super(props, context);
this.x = scaleLinear();
this.y = scaleLinear();
this.line = line()
.x(d => this.x(d.date))
.y(d => this.y(d.value));
}
initRanges() {
// adjust scales and leave some room for the circle on the right, upper, and lower edge
const padding = 2 + Math.ceil(this.props.circleRadius * HOVER_RADIUS_MULTIPLY);
this.x.range([2, this.props.width - padding]);
this.y.range([this.props.height - padding, padding]);
this.line.curve(this.props.curve);
}
getGraphData() {
// data is of shape [{date, value}, ...] and is sorted by date (ASC)
let data = this.props.data;
this.initRanges();
// Convert dates into D3 dates
data = data.map(d => ({
date: parseDate(d.date),
value: d.value
}));
// determine date range
let firstDate = this.props.first ? parseDate(this.props.first) : data[0].date;
let lastDate = this.props.last ? parseDate(this.props.last) : data[data.length - 1].date;
// if last prop is after last value, we need to add that difference as
// padding before first value to right-align sparkline
const skip = lastDate - data[data.length - 1].date;
if (skip > 0) {
firstDate -= skip;
lastDate -= skip;
}
this.x.domain([firstDate, lastDate]);
// determine value range
const minValue = this.props.min !== undefined ? this.props.min : d3Min(data, d => d.value);
const maxValue = this.props.max !== undefined
? Math.max(this.props.max, d3Max(data, d => d.value)) : d3Max(data, d => d.value);
this.y.domain([minValue, maxValue]);
const lastValue = data[data.length - 1].value;
const lastX = this.x(lastDate);
const lastY = this.y(lastValue);
const min = formatMetricSvg(d3Min(data, d => d.value), this.props);
const max = formatMetricSvg(d3Max(data, d => d.value), this.props);
const mean = formatMetricSvg(d3Mean(data, d => d.value), this.props);
const title = `Last ${Math.round((lastDate - firstDate) / 1000)} seconds, ` +
`${data.length} samples, min: ${min}, max: ${max}, mean: ${mean}`;
return {title, lastX, lastY, data};
}
getEmptyGraphData() {
this.initRanges();
const first = new Date(0);
const last = new Date(15);
this.x.domain([first, last]);
this.y.domain([0, 1]);
return {
title: '',
lastX: this.x(last),
lastY: this.y(0),
data: [
{date: first, value: 0},
{date: last, value: 0},
],
};
}
render() {
let strokeColor = this.props.strokeColor;
let strokeWidth = this.props.strokeWidth;
let radius = this.props.circleRadius;
let fillOpacity = 0.6;
let circleColor;
let graph = {};
if (!this.props.data || this.props.data.length === 0 || this.props.data[0].date === undefined) {
// no data means just a dead line w/o circle
graph = this.getEmptyGraphData();
strokeColor = brightenColor(strokeColor);
radius = 0;
} else {
graph = this.getGraphData();
if (this.props.hovered) {
strokeColor = this.props.hoverColor;
circleColor = strokeColor;
strokeWidth *= HOVER_STROKE_MULTIPLY;
radius *= HOVER_RADIUS_MULTIPLY;
fillOpacity = 1;
} else {
circleColor = darkenColor(strokeColor);
}
}
return (
<div title={graph.title}>
<svg width={this.props.width} height={this.props.height}>
<path
className="sparkline" fill="none" stroke={strokeColor}
strokeWidth={strokeWidth} d={this.line(graph.data)}
/>
<circle
className="sparkcircle" cx={graph.lastX} cy={graph.lastY} fill={circleColor}
fillOpacity={fillOpacity} stroke="none" r={radius}
/>
</svg>
</div>
);
}
}
Sparkline.propTypes = {
data: PropTypes.arrayOf(PropTypes.object)
};
Sparkline.defaultProps = {
width: 80,
height: 24,
strokeColor: '#7d7da8',
strokeWidth: 0.5,
hoverColor: '#7d7da8',
curve: curveLinear,
circleRadius: 1.75,
hovered: false,
data: [],
};