mirror of https://github.com/linkerd/linkerd2.git
164 lines
4.6 KiB
JavaScript
164 lines
4.6 KiB
JavaScript
import _ from 'lodash';
|
|
import { metricToFormatter } from './util/Utils.js';
|
|
import Percentage from './util/Percentage.js';
|
|
import React from 'react';
|
|
import * as d3 from 'd3';
|
|
import './../../css/bar-chart.css';
|
|
|
|
const defaultSvgWidth = 595;
|
|
const defaultSvgHeight = 150;
|
|
const margin = { top: 0, right: 0, bottom: 20, left: 0 };
|
|
const horizontalLabelLimit = 4; // number of bars beyond which to tilt axis labels
|
|
const labelLimit = 40; // beyond this, stop labelling bars entirely
|
|
|
|
export default class LineGraph extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.state = this.getChartDimensions();
|
|
}
|
|
|
|
componentWillMount() {
|
|
this.initializeScales();
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.svg = d3.select("." + this.props.containerClassName)
|
|
.append("svg")
|
|
.attr("class", "bar-chart")
|
|
.attr("width", this.state.svgWidth)
|
|
.attr("height", this.state.svgHeight)
|
|
.append("g")
|
|
.attr("transform", "translate(" + this.state.margin.left + "," + this.state.margin.top + ")");
|
|
|
|
this.tooltip = d3.select("." + this.props.containerClassName + " .bar-chart-tooltip")
|
|
.append("div").attr("class", "tooltip");
|
|
|
|
this.xAxis = this.svg.append("g")
|
|
.attr("class", "x-axis")
|
|
.attr("transform", "translate(0," + this.state.height + ")");
|
|
|
|
this.updateScales();
|
|
this.renderGraph();
|
|
}
|
|
|
|
shouldComponentUpdate(nextProps) {
|
|
if (nextProps.lastUpdated === this.props.lastUpdated) {
|
|
// control whether react re-renders the component
|
|
// only rerender if the input data has changed
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
this.updateScales();
|
|
this.renderGraph();
|
|
}
|
|
|
|
getChartDimensions() {
|
|
let svgWidth = this.props.width || defaultSvgWidth;
|
|
let svgHeight = this.props.height || defaultSvgHeight;
|
|
let tiltLabels = false;
|
|
let hideLabels = false;
|
|
|
|
if (_.size(this.props.data) > horizontalLabelLimit) {
|
|
if (_.size(this.props.data) > labelLimit) {
|
|
// if there are way too many bars, don't label at all
|
|
hideLabels = true;
|
|
} else {
|
|
// if there are many bars, tilt x axis labels
|
|
margin.bottom += 100;
|
|
tiltLabels = true;
|
|
}
|
|
}
|
|
|
|
let width = svgWidth - margin.left - margin.right;
|
|
let height = svgHeight - margin.top - margin.bottom;
|
|
|
|
return {
|
|
svgWidth: svgWidth,
|
|
svgHeight: svgHeight,
|
|
width: width,
|
|
height: height,
|
|
margin: margin,
|
|
tiltLabels: tiltLabels,
|
|
hideLabels: hideLabels
|
|
};
|
|
}
|
|
|
|
chartData() {
|
|
let data = this.props.data;
|
|
_.each(data, d => {
|
|
let p = new Percentage(d.requestRate, d.totalRequests);
|
|
d.shareOfRequests = p.get();
|
|
d.pretty = p.prettyRate();
|
|
});
|
|
return data;
|
|
}
|
|
|
|
updateScales() {
|
|
let data = this.chartData();
|
|
this.xScale.domain(_.map(data, d => d.name));
|
|
this.yScale.domain([0, d3.max(data, d => d.requestRate)]);
|
|
}
|
|
|
|
initializeScales() {
|
|
this.xScale = d3.scaleBand()
|
|
.range([0, this.state.width])
|
|
.padding(0.1);
|
|
this.yScale = d3.scaleLinear()
|
|
.range([this.state.height, 0]);
|
|
}
|
|
|
|
renderGraph() {
|
|
let data = this.chartData();
|
|
let barChart = this.svg.selectAll(".bar")
|
|
.remove()
|
|
.exit()
|
|
.data(data);
|
|
|
|
barChart.enter().append("rect")
|
|
.attr("class", "bar")
|
|
.attr("x", d => this.xScale(d.name))
|
|
.attr("width", () => this.xScale.bandwidth())
|
|
.attr("y", d => this.yScale(d.requestRate))
|
|
.attr("height", d => this.state.height - this.yScale(d.requestRate))
|
|
.on("mousemove", d => {
|
|
this.tooltip
|
|
.style("left", d3.event.pageX - 50 + "px")
|
|
.style("top", d3.event.pageY - 70 + "px")
|
|
.style("display", "inline-block") // show tooltip
|
|
.html(`${d.name}:<br /> ${metricToFormatter["REQUEST_RATE"](d.requestRate)} (${d.pretty} of total)`);
|
|
})
|
|
.on("mouseout", () => this.tooltip.style("display", "none"));
|
|
|
|
this.updateAxes();
|
|
}
|
|
|
|
updateAxes() {
|
|
this.xAxis
|
|
.call(d3.axisBottom(this.xScale)) // add x axis labels
|
|
.selectAll("text")
|
|
.attr("class", "tick-labels")
|
|
.attr("transform", () => this.state.tiltLabels ? "rotate(-65)" : "")
|
|
.style("text-anchor", () => this.state.tiltLabels ? "end" : "")
|
|
.text(d => {
|
|
if (this.state.hideLabels) {
|
|
return;
|
|
}
|
|
|
|
let displayText = d;
|
|
// truncate long label names
|
|
if (_.size(displayText) > 20) {
|
|
displayText = "..." + displayText.substring(displayText.length - 20);
|
|
}
|
|
return displayText;
|
|
});
|
|
}
|
|
|
|
render() {
|
|
return null;
|
|
}
|
|
}
|