mirror of
https://github.com/weaveworks/scope.git
synced 2026-05-06 17:28:16 +00:00
New design for hover states and overflow handling
This commit is contained in:
@@ -54,6 +54,28 @@ class NodeDetails extends React.Component {
|
||||
resetDocumentTitle();
|
||||
}
|
||||
|
||||
static collectMetrics(details) {
|
||||
const metrics = details.metrics || [];
|
||||
|
||||
// collect by metric id (id => link)
|
||||
const metricLinks = (details.metric_links || [])
|
||||
.reduce((agg, link) => Object.assign(agg, {[link.id]: link}), {});
|
||||
|
||||
const availableMetrics = metrics.reduce(
|
||||
(agg, m) => Object.assign(agg, {[m.id]: true}),
|
||||
{}
|
||||
);
|
||||
|
||||
// append links with no metrics as fake metrics
|
||||
(details.metric_links || []).forEach((link) => {
|
||||
if (availableMetrics[link.id] === undefined) {
|
||||
metrics.push({id: link.id, label: link.label});
|
||||
}
|
||||
});
|
||||
|
||||
return { metrics, metricLinks };
|
||||
}
|
||||
|
||||
renderTools() {
|
||||
const showSwitchTopology = this.props.nodeId !== this.props.selectedNodeId;
|
||||
const topologyTitle = `View ${this.props.label} in ${this.props.topologyId}`;
|
||||
@@ -165,13 +187,7 @@ class NodeDetails extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
// collect by metric id (id => link)
|
||||
const metricLinks = (details.metric_links || [])
|
||||
.reduce((agg, link) => Object.assign(agg, {[link.id]: link}), {});
|
||||
|
||||
// collect links with no corresponding metric
|
||||
const unattachedLinks = Object.assign({}, metricLinks);
|
||||
(details.metrics || []).forEach(metric => delete unattachedLinks[metric.id]);
|
||||
const { metrics, metricLinks } = NodeDetails.collectMetrics(details);
|
||||
|
||||
return (
|
||||
<div className="node-details">
|
||||
@@ -198,13 +214,13 @@ class NodeDetails extends React.Component {
|
||||
</div>}
|
||||
|
||||
<div className="node-details-content">
|
||||
{((details.metrics || []).length + Object.keys(unattachedLinks).length > 0) && <div className="node-details-content-section">
|
||||
{metrics.length > 0 && <div className="node-details-content-section">
|
||||
<div className="node-details-content-section-header">Status</div>
|
||||
<NodeDetailsHealth
|
||||
metrics={details.metrics}
|
||||
metrics={metrics}
|
||||
metricLinks={metricLinks}
|
||||
unattachedLinks={unattachedLinks}
|
||||
topologyId={topologyId}
|
||||
nodeColor={nodeColor}
|
||||
/>
|
||||
</div>}
|
||||
{details.metadata && <div className="node-details-content-section">
|
||||
|
||||
@@ -10,12 +10,11 @@ function NodeDetailsHealthItem(props) {
|
||||
{props.samples && <div className="node-details-health-item-sparkline">
|
||||
<Sparkline
|
||||
data={props.samples} max={props.max} format={props.format}
|
||||
first={props.first} last={props.last} />
|
||||
first={props.first} last={props.last} strokeWidth={props.strokeWidth}
|
||||
strokeColor={props.strokeColor} />
|
||||
</div>}
|
||||
{!props.samples && <div className="node-details-health-item-placeholder"><span className="fa fa-circle-thin" /></div>}
|
||||
<div className="node-details-health-item-label">
|
||||
<div className="node-details-health-item-label" style={{ color: props.labelColor }}>
|
||||
{props.label}
|
||||
{props.icon && <span className={`fa ${props.icon}`} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import React from 'react';
|
||||
|
||||
import { trackMixpanelEvent } from '../../utils/tracking-utils';
|
||||
import { getMetricColor } from '../../utils/metric-utils';
|
||||
import NodeDetailsHealthItem from './node-details-health-item';
|
||||
|
||||
export default class NodeDetailsHealthLinkItem extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
hovered: false
|
||||
};
|
||||
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.buildHref = this.buildHref.bind(this);
|
||||
this.onMouseOver = this.onMouseOver.bind(this);
|
||||
this.onMouseOut = this.onMouseOut.bind(this);
|
||||
}
|
||||
|
||||
onMouseOver() {
|
||||
this.setState({hovered: true});
|
||||
}
|
||||
|
||||
onMouseOut() {
|
||||
this.setState({hovered: false});
|
||||
}
|
||||
|
||||
handleClick(ev, href) {
|
||||
@@ -32,18 +46,29 @@ export default class NodeDetailsHealthLinkItem extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { links, withoutGraph, id, ...props } = this.props;
|
||||
const { links, id, nodeColor, ...props } = this.props;
|
||||
const href = this.buildHref(links[id] && links[id].url);
|
||||
|
||||
if (!href) return <NodeDetailsHealthItem {...props} />;
|
||||
|
||||
const hasData = (props.samples && props.samples.length > 0) || props.value !== undefined;
|
||||
const labelColor = this.state.hovered && !hasData ? nodeColor : undefined;
|
||||
const sparkline = {};
|
||||
if (this.state.hovered) {
|
||||
sparkline.strokeColor = getMetricColor(id);
|
||||
sparkline.strokeWidth = '2px';
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className="node-details-health-link-item"
|
||||
href={href}
|
||||
onClick={e => this.handleClick(e, href)}>
|
||||
{!withoutGraph && <NodeDetailsHealthItem {...props} icon="fa-expand" />}
|
||||
{withoutGraph && <NodeDetailsHealthItem label={props.label} icon="fa-expand" />}
|
||||
onClick={e => this.handleClick(e, href)}
|
||||
onMouseOver={this.onMouseOver}
|
||||
onMouseOut={this.onMouseOut}>
|
||||
<NodeDetailsHealthItem
|
||||
{...props}
|
||||
{...sparkline}
|
||||
labelColor={labelColor} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ function NodeDetailsHealthOverflowItem(props) {
|
||||
return (
|
||||
<div className="node-details-health-overflow-item">
|
||||
<div className="node-details-health-overflow-item-value">
|
||||
{formatMetric(props.value, props)}
|
||||
{props.value !== undefined && formatMetric(props.value, props)}
|
||||
</div>
|
||||
<div className="node-details-health-overflow-item-label truncate">{props.label}</div>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ export default class NodeDetailsHealthOverflow extends React.Component {
|
||||
const items = this.props.items.slice(0, 4);
|
||||
|
||||
return (
|
||||
<div className="node-details-health-overflow" onClick={this.handleClick}>
|
||||
<div className="node-details-health-overflow" onClick={this.handleClick} title="Expand metrics">
|
||||
{items.map(item => <NodeDetailsHealthOverflowItem key={item.id} {...item} />)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -25,8 +25,8 @@ export default class NodeDetailsHealth extends React.Component {
|
||||
const {
|
||||
metrics = makeList(),
|
||||
metricLinks = makeMap(),
|
||||
unattachedLinks = makeMap(),
|
||||
topologyId,
|
||||
nodeColor,
|
||||
} = this.props;
|
||||
|
||||
const primeCutoff = metrics.length > 3 && !this.state.expanded ? 2 : metrics.length;
|
||||
@@ -45,6 +45,7 @@ export default class NodeDetailsHealth extends React.Component {
|
||||
{...item}
|
||||
links={metricLinks}
|
||||
topologyId={topologyId}
|
||||
nodeColor={nodeColor}
|
||||
/>
|
||||
</CloudFeature>)}
|
||||
{showOverflow && <NodeDetailsHealthOverflow
|
||||
@@ -55,17 +56,6 @@ export default class NodeDetailsHealth extends React.Component {
|
||||
<ShowMore
|
||||
handleClick={this.handleClickMore} collection={this.props.metrics}
|
||||
expanded={this.state.expanded} notShown={notShown} hideNumber />
|
||||
|
||||
<div className="node-details-health-wrapper">
|
||||
{Object.keys(unattachedLinks).map(id => <CloudFeature alwaysShow key={id}>
|
||||
<NodeDetailsHealthLinkItem
|
||||
withoutGraph
|
||||
{...unattachedLinks[id]}
|
||||
links={unattachedLinks}
|
||||
topologyId={topologyId}
|
||||
/>
|
||||
</CloudFeature>)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,9 @@ export function getMetricValue(metric) {
|
||||
|
||||
|
||||
export function getMetricColor(metric) {
|
||||
const metricId = metric && metric.get('id');
|
||||
const metricId = typeof metric === 'string'
|
||||
? metric
|
||||
: metric && metric.get('id');
|
||||
if (/mem/.test(metricId)) {
|
||||
return 'steelBlue';
|
||||
} else if (/cpu/.test(metricId)) {
|
||||
|
||||
@@ -926,11 +926,14 @@
|
||||
&-item {
|
||||
padding: 8px 16px;
|
||||
width: 33%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&-label {
|
||||
color: $text-secondary-color;
|
||||
text-transform: uppercase;
|
||||
font-size: 80%;
|
||||
margin-top: auto;
|
||||
|
||||
.fa {
|
||||
margin-left: 0.5em;
|
||||
|
||||
Reference in New Issue
Block a user