No preview for overflow health items

This commit is contained in:
Roland Schilter
2017-08-07 16:59:07 +01:00
parent 35ba8ae916
commit 87efac0b1d
6 changed files with 56 additions and 96 deletions

View File

@@ -4,17 +4,18 @@ import Sparkline from '../sparkline';
import { formatMetric } from '../../utils/string-utils';
function NodeDetailsHealthItem(props) {
const labelStyle = { color: props.labelColor };
return (
<div className="node-details-health-item">
{!props.valueEmpty && <div className="node-details-health-item-value">{formatMetric(props.value, props)}</div>}
{!props.valueEmpty && <div className="node-details-health-item-value" style={labelStyle}>{formatMetric(props.value, props)}</div>}
<div className="node-details-health-item-sparkline">
<Sparkline
data={props.samples} max={props.max} format={props.format}
first={props.first} last={props.last} hoverColor={props.metricColor}
hovered={props.samples && props.hovered}
hovered={props.hovered}
/>
</div>
<div className="node-details-health-item-label" style={{ color: props.labelColor }}>
<div className="node-details-health-item-label" style={labelStyle}>
{props.label}
</div>
</div>

View File

@@ -3,6 +3,7 @@ import React from 'react';
import NodeDetailsHealthItem from './node-details-health-item';
import CloudLink from '../cloud-link';
import { getMetricColor } from '../../utils/metric-utils';
import { darkenColor } from '../../utils/color-utils';
import { trackMixpanelEvent } from '../../utils/tracking-utils';
export default class NodeDetailsHealthLinkItem extends React.Component {
@@ -33,6 +34,7 @@ export default class NodeDetailsHealthLinkItem extends React.Component {
render() {
const { id, url, ...props } = this.props;
const metricColor = getMetricColor(id);
const labelColor = this.state.hovered && !props.valueEmpty && darkenColor(metricColor);
return (
<CloudLink
@@ -46,6 +48,7 @@ export default class NodeDetailsHealthLinkItem extends React.Component {
<NodeDetailsHealthItem
{...props}
hovered={this.state.hovered}
labelColor={labelColor}
metricColor={metricColor}
/>
</CloudLink>

View File

@@ -1,16 +0,0 @@
import React from 'react';
import { formatMetric } from '../../utils/string-utils';
function NodeDetailsHealthOverflowItem(props) {
return (
<div className="node-details-health-overflow-item">
<div className="node-details-health-overflow-item-value">
{!props.valueEmpty && formatMetric(props.value, props)}
</div>
<div className="node-details-health-overflow-item-label truncate">{props.label}</div>
</div>
);
}
export default NodeDetailsHealthOverflowItem;

View File

@@ -1,26 +0,0 @@
import React from 'react';
import NodeDetailsHealthOverflowItem from './node-details-health-overflow-item';
export default class NodeDetailsHealthOverflow extends React.Component {
constructor(props, context) {
super(props, context);
this.handleClick = this.handleClick.bind(this);
}
handleClick(ev) {
ev.preventDefault();
this.props.handleClick();
}
render() {
const items = this.props.items.slice(0, 4);
return (
<div className="node-details-health-overflow" onClick={this.handleClick} title="Expand metrics">
{items.map(item => <NodeDetailsHealthOverflowItem key={item.id} {...item} />)}
</div>
);
}
}

View File

@@ -1,7 +1,6 @@
import React from 'react';
import ShowMore from '../show-more';
import NodeDetailsHealthOverflow from './node-details-health-overflow';
import NodeDetailsHealthLinkItem from './node-details-health-link-item';
export default class NodeDetailsHealth extends React.Component {
@@ -25,30 +24,38 @@ export default class NodeDetailsHealth extends React.Component {
topologyId,
} = this.props;
const primeCutoff = metrics.length > 3 && !this.state.expanded ? 2 : metrics.length;
const primeMetrics = metrics.slice(0, primeCutoff);
const overflowMetrics = metrics.slice(primeCutoff);
const showOverflow = overflowMetrics.length > 0 && !this.state.expanded;
const flexWrap = showOverflow || !this.state.expanded ? 'nowrap' : 'wrap';
const justifyContent = showOverflow || !this.state.expanded ? 'space-around' : 'flex-start';
const notShown = overflowMetrics.length;
let primeMetrics = metrics.filter(m => !m.valueEmpty);
let emptyMetrics = metrics.filter(m => m.valueEmpty);
if (primeMetrics.length === 0 && emptyMetrics.length > 0) {
primeMetrics = emptyMetrics;
emptyMetrics = [];
}
const shownWithData = this.state.expanded ? primeMetrics : primeMetrics.slice(0, 3);
const shownEmpty = this.state.expanded ? emptyMetrics : [];
const notShown = metrics.length - shownWithData.length - shownEmpty.length;
return (
<div className="node-details-health" style={{flexWrap, justifyContent}}>
<div className="node-details-health" style={{ justifyContent: 'space-around' }}>
<div className="node-details-health-wrapper">
{primeMetrics.map(item => <NodeDetailsHealthLinkItem
{shownWithData.map(item => <NodeDetailsHealthLinkItem
{...item}
key={item.id}
topologyId={topologyId}
/>)}
</div>
<div className="node-details-health-wrapper">
{shownEmpty.map(item => <NodeDetailsHealthLinkItem
{...item}
key={item.id}
topologyId={topologyId}
/>)}
{showOverflow && <NodeDetailsHealthOverflow
items={overflowMetrics}
handleClick={this.handleClickMore}
/>}
</div>
<ShowMore
handleClick={this.handleClickMore} collection={this.props.metrics}
expanded={this.state.expanded} notShown={notShown} hideNumber />
handleClick={this.handleClickMore} collection={metrics}
expanded={this.state.expanded} notShown={notShown} hideNumber={this.state.expanded}
/>
</div>
);
}

View File

@@ -7,11 +7,11 @@ 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;
const MARGIN = 2;
export default class Sparkline extends React.Component {
constructor(props, context) {
@@ -24,11 +24,15 @@ export default class Sparkline extends React.Component {
.y(d => this.y(d.value));
}
initRanges() {
initRanges(hasCircle) {
// 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]);
let circleSpace = MARGIN;
if (hasCircle) {
circleSpace += Math.ceil(this.props.circleRadius * HOVER_RADIUS_MULTIPLY);
}
this.x.range([MARGIN, this.props.width - circleSpace]);
this.y.range([this.props.height - circleSpace, circleSpace]);
this.line.curve(this.props.curve);
}
@@ -36,7 +40,7 @@ export default class Sparkline extends React.Component {
// data is of shape [{date, value}, ...] and is sorted by date (ASC)
let data = this.props.data;
this.initRanges();
this.initRanges(true);
// Convert dates into D3 dates
data = data.map(d => ({
@@ -75,7 +79,7 @@ export default class Sparkline extends React.Component {
}
getEmptyGraphData() {
this.initRanges();
this.initRanges(false);
const first = new Date(0);
const last = new Date(15);
this.x.domain([first, last]);
@@ -93,43 +97,30 @@ export default class Sparkline extends React.Component {
}
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);
}
}
const dash = 5;
const hasData = this.props.data && this.props.data.length > 0;
const strokeColor = this.props.hovered && hasData
? this.props.hoverColor
: this.props.strokeColor;
const strokeWidth = this.props.strokeWidth * (this.props.hovered ? HOVER_STROKE_MULTIPLY : 1);
const strokeDasharray = hasData || `${dash}, ${dash}`;
const radius = this.props.circleRadius * (this.props.hovered ? HOVER_RADIUS_MULTIPLY : 1);
const fillOpacity = this.props.hovered ? 1 : 0.6;
const circleColor = hasData && this.props.hovered ? strokeColor : strokeColor;
const graph = hasData ? this.getGraphData() : this.getEmptyGraphData();
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)}
strokeWidth={strokeWidth} strokeDasharray={strokeDasharray}
d={this.line(graph.data)}
/>
<circle
{hasData && <circle
className="sparkcircle" cx={graph.lastX} cy={graph.lastY} fill={circleColor}
fillOpacity={fillOpacity} stroke="none" r={radius}
/>
/>}
</svg>
</div>
);