mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 02:00:43 +00:00
Extracted 'static' methods outside of classes for the 'class-methods-use-this' lint rule
This commit is contained in:
@@ -33,6 +33,5 @@
|
||||
"jsx-a11y/no-static-element-interactions": 0,
|
||||
"react/jsx-no-target-blank": 0,
|
||||
"react/no-find-dom-node": 0,
|
||||
"class-methods-use-this": 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,80 @@ function updateLayout(width, height, nodes, baseOptions) {
|
||||
}
|
||||
|
||||
|
||||
function centerSelectedNode(props, state) {
|
||||
let stateNodes = state.nodes;
|
||||
let stateEdges = state.edges;
|
||||
if (!stateNodes.has(props.selectedNodeId)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const adjacentNodes = props.adjacentNodes;
|
||||
const adjacentLayoutNodeIds = [];
|
||||
|
||||
adjacentNodes.forEach((adjacentId) => {
|
||||
// filter loopback
|
||||
if (adjacentId !== props.selectedNodeId) {
|
||||
adjacentLayoutNodeIds.push(adjacentId);
|
||||
}
|
||||
});
|
||||
|
||||
// move origin node to center of viewport
|
||||
const zoomScale = state.scale;
|
||||
const translate = [state.panTranslateX, state.panTranslateY];
|
||||
const viewportHalfWidth = ((state.width + props.margins.left) - DETAILS_PANEL_WIDTH) / 2;
|
||||
const viewportHalfHeight = (state.height + props.margins.top) / 2;
|
||||
const centerX = (-translate[0] + viewportHalfWidth) / zoomScale;
|
||||
const centerY = (-translate[1] + viewportHalfHeight) / zoomScale;
|
||||
stateNodes = stateNodes.mergeIn([props.selectedNodeId], {
|
||||
x: centerX,
|
||||
y: centerY
|
||||
});
|
||||
|
||||
// circle layout for adjacent nodes
|
||||
const adjacentCount = adjacentLayoutNodeIds.length;
|
||||
const density = radiusDensity(adjacentCount);
|
||||
const radius = Math.min(state.width, state.height) / density / zoomScale;
|
||||
const offsetAngle = Math.PI / 4;
|
||||
|
||||
stateNodes = stateNodes.map((node, nodeId) => {
|
||||
const index = adjacentLayoutNodeIds.indexOf(nodeId);
|
||||
if (index > -1) {
|
||||
const angle = offsetAngle + ((Math.PI * 2 * index) / adjacentCount);
|
||||
return node.merge({
|
||||
x: centerX + (radius * Math.sin(angle)),
|
||||
y: centerY + (radius * Math.cos(angle))
|
||||
});
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
// fix all edges for circular nodes
|
||||
stateEdges = stateEdges.map((edge) => {
|
||||
if (edge.get('source') === props.selectedNodeId
|
||||
|| edge.get('target') === props.selectedNodeId
|
||||
|| _.includes(adjacentLayoutNodeIds, edge.get('source'))
|
||||
|| _.includes(adjacentLayoutNodeIds, edge.get('target'))) {
|
||||
const source = stateNodes.get(edge.get('source'));
|
||||
const target = stateNodes.get(edge.get('target'));
|
||||
return edge.set('points', fromJS([
|
||||
{x: source.get('x'), y: source.get('y')},
|
||||
{x: target.get('x'), y: target.get('y')}
|
||||
]));
|
||||
}
|
||||
return edge;
|
||||
});
|
||||
|
||||
// auto-scale node size for selected nodes
|
||||
const selectedNodeScale = getNodeScale(adjacentNodes.size, state.width, state.height);
|
||||
|
||||
return {
|
||||
selectedNodeScale,
|
||||
edges: stateEdges,
|
||||
nodes: stateNodes
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class NodesChart extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -180,7 +254,7 @@ class NodesChart extends React.Component {
|
||||
assign(state, this.restoreLayout(state));
|
||||
}
|
||||
if (nextProps.selectedNodeId) {
|
||||
assign(state, this.centerSelectedNode(nextProps, state));
|
||||
assign(state, centerSelectedNode(nextProps, state));
|
||||
}
|
||||
|
||||
this.setState(state);
|
||||
@@ -246,79 +320,6 @@ class NodesChart extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
centerSelectedNode(props, state) {
|
||||
let stateNodes = state.nodes;
|
||||
let stateEdges = state.edges;
|
||||
if (!stateNodes.has(props.selectedNodeId)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const adjacentNodes = props.adjacentNodes;
|
||||
const adjacentLayoutNodeIds = [];
|
||||
|
||||
adjacentNodes.forEach((adjacentId) => {
|
||||
// filter loopback
|
||||
if (adjacentId !== props.selectedNodeId) {
|
||||
adjacentLayoutNodeIds.push(adjacentId);
|
||||
}
|
||||
});
|
||||
|
||||
// move origin node to center of viewport
|
||||
const zoomScale = state.scale;
|
||||
const translate = [state.panTranslateX, state.panTranslateY];
|
||||
const viewportHalfWidth = ((state.width + props.margins.left) - DETAILS_PANEL_WIDTH) / 2;
|
||||
const viewportHalfHeight = (state.height + props.margins.top) / 2;
|
||||
const centerX = (-translate[0] + viewportHalfWidth) / zoomScale;
|
||||
const centerY = (-translate[1] + viewportHalfHeight) / zoomScale;
|
||||
stateNodes = stateNodes.mergeIn([props.selectedNodeId], {
|
||||
x: centerX,
|
||||
y: centerY
|
||||
});
|
||||
|
||||
// circle layout for adjacent nodes
|
||||
const adjacentCount = adjacentLayoutNodeIds.length;
|
||||
const density = radiusDensity(adjacentCount);
|
||||
const radius = Math.min(state.width, state.height) / density / zoomScale;
|
||||
const offsetAngle = Math.PI / 4;
|
||||
|
||||
stateNodes = stateNodes.map((node, nodeId) => {
|
||||
const index = adjacentLayoutNodeIds.indexOf(nodeId);
|
||||
if (index > -1) {
|
||||
const angle = offsetAngle + ((Math.PI * 2 * index) / adjacentCount);
|
||||
return node.merge({
|
||||
x: centerX + (radius * Math.sin(angle)),
|
||||
y: centerY + (radius * Math.cos(angle))
|
||||
});
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
// fix all edges for circular nodes
|
||||
stateEdges = stateEdges.map((edge) => {
|
||||
if (edge.get('source') === props.selectedNodeId
|
||||
|| edge.get('target') === props.selectedNodeId
|
||||
|| includes(adjacentLayoutNodeIds, edge.get('source'))
|
||||
|| includes(adjacentLayoutNodeIds, edge.get('target'))) {
|
||||
const source = stateNodes.get(edge.get('source'));
|
||||
const target = stateNodes.get(edge.get('target'));
|
||||
return edge.set('points', fromJS([
|
||||
{x: source.get('x'), y: source.get('y')},
|
||||
{x: target.get('x'), y: target.get('y')}
|
||||
]));
|
||||
}
|
||||
return edge;
|
||||
});
|
||||
|
||||
// auto-scale node size for selected nodes
|
||||
const selectedNodeScale = getNodeScale(adjacentNodes.size, state.width, state.height);
|
||||
|
||||
return {
|
||||
selectedNodeScale,
|
||||
edges: stateEdges,
|
||||
nodes: stateNodes
|
||||
};
|
||||
}
|
||||
|
||||
restoreLayout(state) {
|
||||
// undo any pan/zooming that might have happened
|
||||
this.setZoom(state);
|
||||
|
||||
@@ -4,6 +4,21 @@ import classNames from 'classnames';
|
||||
|
||||
import { toggleGridMode } from '../actions/app-actions';
|
||||
|
||||
|
||||
function renderItem(icons, label, isSelected, onClick) {
|
||||
const className = classNames('grid-mode-selector-action', {
|
||||
'grid-mode-selector-action-selected': isSelected
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={onClick} >
|
||||
<span className={icons} style={{fontSize: 12}} />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
class GridModeSelector extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -21,28 +36,14 @@ class GridModeSelector extends React.Component {
|
||||
return this.props.toggleGridMode(false);
|
||||
}
|
||||
|
||||
renderItem(icons, label, isSelected, onClick) {
|
||||
const className = classNames('grid-mode-selector-action', {
|
||||
'grid-mode-selector-action-selected': isSelected
|
||||
});
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={onClick} >
|
||||
<span className={icons} style={{fontSize: 12}} />
|
||||
<span>{label}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { gridMode } = this.props;
|
||||
|
||||
return (
|
||||
<div className="grid-mode-selector">
|
||||
<div className="grid-mode-selector-wrapper">
|
||||
{this.renderItem('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)}
|
||||
{this.renderItem('fa fa-table', 'Table', gridMode, this.enableGridMode)}
|
||||
{renderItem('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)}
|
||||
{renderItem('fa fa-table', 'Table', gridMode, this.enableGridMode)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,26 +6,24 @@ import MatchedText from './matched-text';
|
||||
const SHOW_ROW_COUNT = 2;
|
||||
const MAX_MATCH_LENGTH = 24;
|
||||
|
||||
class MatchedResults extends React.Component {
|
||||
|
||||
renderMatch(matches, field) {
|
||||
const match = matches.get(field);
|
||||
const text = match.text;
|
||||
|
||||
return (
|
||||
<div className="matched-results-match" key={match.label}>
|
||||
<div className="matched-results-match-wrapper">
|
||||
<span className="matched-results-match-label">
|
||||
{match.label}:
|
||||
</span>
|
||||
<MatchedText
|
||||
text={text} match={match}
|
||||
maxLength={MAX_MATCH_LENGTH}
|
||||
truncate={match.truncate} />
|
||||
</div>
|
||||
function renderMatch(match) {
|
||||
return (
|
||||
<div className="matched-results-match" key={match.label}>
|
||||
<div className="matched-results-match-wrapper">
|
||||
<span className="matched-results-match-label">
|
||||
{match.label}:
|
||||
</span>
|
||||
<MatchedText
|
||||
text={match.text} match={match}
|
||||
maxLength={MAX_MATCH_LENGTH}
|
||||
truncate={match.truncate} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
class MatchedResults extends React.Component {
|
||||
|
||||
render() {
|
||||
const { matches, style } = this.props;
|
||||
@@ -46,7 +44,7 @@ class MatchedResults extends React.Component {
|
||||
|
||||
return (
|
||||
<div className="matched-results" style={style}>
|
||||
{matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => this.renderMatch(matches, fieldId))}
|
||||
{matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => renderMatch(matches.get(fieldId)))}
|
||||
{moreFieldMatches && <div className="matched-results-more" title={moreFieldMatchesTitle}>
|
||||
{`${moreFieldMatches.size} more matches`}
|
||||
</div>}
|
||||
|
||||
@@ -6,6 +6,16 @@ import MatchedText from '../matched-text';
|
||||
import NodeDetailsControlButton from './node-details-control-button';
|
||||
import ShowMore from '../show-more';
|
||||
|
||||
|
||||
function renderControls(controls) {
|
||||
return (
|
||||
<div className="node-details-labels-controls">
|
||||
{sortBy(controls, 'rank').map(control => <NodeDetailsControlButton
|
||||
nodeId={control.nodeId} control={control} key={control.id} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default class NodeDetailsLabels extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -15,7 +25,6 @@ export default class NodeDetailsLabels extends React.Component {
|
||||
limit: this.DEFAULT_LIMIT,
|
||||
};
|
||||
this.handleLimitClick = this.handleLimitClick.bind(this);
|
||||
this.renderControls = this.renderControls.bind(this);
|
||||
}
|
||||
|
||||
handleLimitClick() {
|
||||
@@ -23,15 +32,6 @@ export default class NodeDetailsLabels extends React.Component {
|
||||
this.setState({limit});
|
||||
}
|
||||
|
||||
renderControls(controls) {
|
||||
return (
|
||||
<div className="node-details-labels-controls">
|
||||
{sortBy(controls, 'rank').map(control => <NodeDetailsControlButton
|
||||
nodeId={control.nodeId} control={control} key={control.id} />)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { controls, matches = makeMap() } = this.props;
|
||||
let rows = this.props.rows;
|
||||
@@ -49,7 +49,7 @@ export default class NodeDetailsLabels extends React.Component {
|
||||
|
||||
return (
|
||||
<div className="node-details-labels">
|
||||
{controls && this.renderControls(controls)}
|
||||
{controls && renderControls(controls)}
|
||||
{rows.map(field => (
|
||||
<div className="node-details-labels-field" key={field.id}>
|
||||
<div
|
||||
|
||||
@@ -13,6 +13,22 @@ const navbarHeight = 194;
|
||||
const marginTop = 0;
|
||||
|
||||
|
||||
function renderEmptyTopologyError(show) {
|
||||
return (
|
||||
<NodesError faIconClass="fa-circle-thin" hidden={!show}>
|
||||
<div className="heading">Nothing to show. This can have any of these reasons:</div>
|
||||
<ul>
|
||||
<li>We haven't received any reports from probes recently.
|
||||
Are the probes properly configured?</li>
|
||||
<li>There are nodes, but they're currently hidden. Check the view options
|
||||
in the bottom-left if they allow for showing hidden nodes.</li>
|
||||
<li>Containers view only: you're not running Docker,
|
||||
or you don't have any containers.</li>
|
||||
</ul>
|
||||
</NodesError>
|
||||
);
|
||||
}
|
||||
|
||||
class Nodes extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
@@ -32,22 +48,6 @@ class Nodes extends React.Component {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
|
||||
renderEmptyTopologyError(show) {
|
||||
return (
|
||||
<NodesError faIconClass="fa-circle-thin" hidden={!show}>
|
||||
<div className="heading">Nothing to show. This can have any of these reasons:</div>
|
||||
<ul>
|
||||
<li>We haven't received any reports from probes recently.
|
||||
Are the probes properly configured?</li>
|
||||
<li>There are nodes, but they're currently hidden. Check the view options
|
||||
in the bottom-left if they allow for showing hidden nodes.</li>
|
||||
<li>Containers view only: you're not running Docker,
|
||||
or you don't have any containers.</li>
|
||||
</ul>
|
||||
</NodesError>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { topologyEmpty, gridMode, topologiesLoaded, nodesLoaded, topologies,
|
||||
currentTopology } = this.props;
|
||||
@@ -60,7 +60,7 @@ class Nodes extends React.Component {
|
||||
itemType={getNodeType(currentTopology, topologies)}
|
||||
show={topologiesLoaded && !nodesLoaded} />
|
||||
</DelayedShow>
|
||||
{this.renderEmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)}
|
||||
{renderEmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)}
|
||||
|
||||
{gridMode ?
|
||||
<NodesGrid {...this.state} nodeSize="24" margins={CANVAS_MARGINS} /> :
|
||||
|
||||
@@ -3,24 +3,25 @@ import { connect } from 'react-redux';
|
||||
import classNames from 'classnames';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
|
||||
class Plugins extends React.Component {
|
||||
renderPlugin({id, label, description, status}) {
|
||||
const error = status !== 'ok';
|
||||
const className = classNames({ error });
|
||||
const title = `Plugin description: ${description}<br />Status: ${status}`;
|
||||
|
||||
// Inner span to hold styling so we don't effect the "before:content"
|
||||
return (
|
||||
<span className="plugins-plugin" key={id}>
|
||||
<span className={className} data-tip={title} data-multiline>
|
||||
{error && <span className="plugins-plugin-icon fa fa-exclamation-circle" />}
|
||||
{label || id}
|
||||
</span>
|
||||
<ReactTooltip class="tooltip" effect="solid" offset={{right: 7}} />
|
||||
function renderPlugin({id, label, description, status}) {
|
||||
const error = status !== 'ok';
|
||||
const className = classNames({ error });
|
||||
const title = `Plugin description: ${description}<br />Status: ${status}`;
|
||||
|
||||
// Inner span to hold styling so we don't effect the "before:content"
|
||||
return (
|
||||
<span className="plugins-plugin" key={id}>
|
||||
<span className={className} data-tip={title} data-multiline>
|
||||
{error && <span className="plugins-plugin-icon fa fa-exclamation-circle" />}
|
||||
{label || id}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
<ReactTooltip class="tooltip" effect="solid" offset={{right: 7}} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
class Plugins extends React.Component {
|
||||
render() {
|
||||
const hasPlugins = this.props.plugins && this.props.plugins.size > 0;
|
||||
return (
|
||||
@@ -29,7 +30,7 @@ class Plugins extends React.Component {
|
||||
Plugins:
|
||||
</span>
|
||||
{hasPlugins && this.props.plugins.toIndexedSeq()
|
||||
.map(plugin => this.renderPlugin(plugin.toJS()))}
|
||||
.map(plugin => renderPlugin(plugin.toJS()))}
|
||||
{!hasPlugins && <span className="plugins-empty">n/a</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -123,7 +123,7 @@ class Terminal extends React.Component {
|
||||
}
|
||||
this.socket = null;
|
||||
const wereConnected = this.state.connected;
|
||||
if (this.isMounted) {
|
||||
if (this.isComponentMounted) {
|
||||
// Calling setState on an unmounted component will throw a warning.
|
||||
// `connected` will get set to false by `componentWillUnmount`.
|
||||
this.setState({connected: false});
|
||||
@@ -176,7 +176,7 @@ class Terminal extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.isMounted = true;
|
||||
this.isComponentMounted = true;
|
||||
if (this.props.connect) {
|
||||
this.mountTerminal();
|
||||
}
|
||||
@@ -217,7 +217,7 @@ class Terminal extends React.Component {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.isMounted = false;
|
||||
this.isComponentMounted = false;
|
||||
this.setState({connected: false});
|
||||
log('cwu terminal');
|
||||
|
||||
|
||||
@@ -4,6 +4,18 @@ import classnames from 'classnames';
|
||||
|
||||
import { clickTopology } from '../actions/app-actions';
|
||||
|
||||
|
||||
function basicTopologyInfo(topology, searchMatchCount) {
|
||||
const info = [
|
||||
`Nodes: ${topology.getIn(['stats', 'node_count'])}`,
|
||||
`Connections: ${topology.getIn(['stats', 'edge_count'])}`
|
||||
];
|
||||
if (searchMatchCount) {
|
||||
info.push(`Search Matches: ${searchMatchCount}`);
|
||||
}
|
||||
return info.join('\n');
|
||||
}
|
||||
|
||||
class Topologies extends React.Component {
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -21,7 +33,7 @@ class Topologies extends React.Component {
|
||||
const topologyId = subTopology.get('id');
|
||||
const searchMatches = this.props.searchNodeMatches.get(subTopology.get('id'));
|
||||
const searchMatchCount = searchMatches ? searchMatches.size : 0;
|
||||
const title = this.renderTitle(subTopology, searchMatchCount);
|
||||
const title = basicTopologyInfo(subTopology, searchMatchCount);
|
||||
const className = classnames('topologies-sub-item', {
|
||||
'topologies-sub-item-active': isActive,
|
||||
'topologies-sub-item-matched': searchMatchCount
|
||||
@@ -38,15 +50,6 @@ class Topologies extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderTitle(topology, searchMatchCount) {
|
||||
let title = `Nodes: ${topology.getIn(['stats', 'node_count'])}\n`
|
||||
+ `Connections: ${topology.getIn(['stats', 'node_count'])}`;
|
||||
if (searchMatchCount) {
|
||||
title = `${title}\nSearch Matches: ${searchMatchCount}`;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
renderTopology(topology) {
|
||||
const isActive = topology === this.props.currentTopology;
|
||||
const searchMatches = this.props.searchNodeMatches.get(topology.get('id'));
|
||||
@@ -56,7 +59,7 @@ class Topologies extends React.Component {
|
||||
'topologies-item-main-matched': searchMatchCount
|
||||
});
|
||||
const topologyId = topology.get('id');
|
||||
const title = this.renderTitle(topology, searchMatchCount);
|
||||
const title = basicTopologyInfo(topology, searchMatchCount);
|
||||
|
||||
return (
|
||||
<div className="topologies-item" key={topologyId}>
|
||||
|
||||
Reference in New Issue
Block a user