diff --git a/client/.eslintrc b/client/.eslintrc
index 9a85980f0..6fae61983 100644
--- a/client/.eslintrc
+++ b/client/.eslintrc
@@ -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,
}
}
diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js
index ff1cd2cb4..4acfaf366 100644
--- a/client/app/scripts/charts/nodes-chart.js
+++ b/client/app/scripts/charts/nodes-chart.js
@@ -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);
diff --git a/client/app/scripts/components/grid-mode-selector.js b/client/app/scripts/components/grid-mode-selector.js
index e27d849ee..c81a76ae1 100644
--- a/client/app/scripts/components/grid-mode-selector.js
+++ b/client/app/scripts/components/grid-mode-selector.js
@@ -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 (
+
+
+ {label}
+
+ );
+}
+
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 (
-
-
- {label}
-
- );
- }
-
render() {
const { gridMode } = this.props;
return (
- {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)}
);
diff --git a/client/app/scripts/components/matched-results.js b/client/app/scripts/components/matched-results.js
index 24c23dbec..9fd0d53c3 100644
--- a/client/app/scripts/components/matched-results.js
+++ b/client/app/scripts/components/matched-results.js
@@ -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 (
-
-
-
- {match.label}:
-
-
-
+function renderMatch(match) {
+ return (
+
+
+
+ {match.label}:
+
+
- );
- }
+
+ );
+}
+
+class MatchedResults extends React.Component {
render() {
const { matches, style } = this.props;
@@ -46,7 +44,7 @@ class MatchedResults extends React.Component {
return (
- {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 &&
{`${moreFieldMatches.size} more matches`}
}
diff --git a/client/app/scripts/components/node-details/node-details-labels.js b/client/app/scripts/components/node-details/node-details-labels.js
index fa3fb0850..a8ae1aac7 100644
--- a/client/app/scripts/components/node-details/node-details-labels.js
+++ b/client/app/scripts/components/node-details/node-details-labels.js
@@ -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 (
+
+ {sortBy(controls, 'rank').map(control => )}
+
+ );
+}
+
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 (
-
- {sortBy(controls, 'rank').map(control => )}
-
- );
- }
-
render() {
const { controls, matches = makeMap() } = this.props;
let rows = this.props.rows;
@@ -49,7 +49,7 @@ export default class NodeDetailsLabels extends React.Component {
return (
- {controls && this.renderControls(controls)}
+ {controls && renderControls(controls)}
{rows.map(field => (
+
Nothing to show. This can have any of these reasons:
+
+ - We haven't received any reports from probes recently.
+ Are the probes properly configured?
+ - There are nodes, but they're currently hidden. Check the view options
+ in the bottom-left if they allow for showing hidden nodes.
+ - Containers view only: you're not running Docker,
+ or you don't have any containers.
+
+
+ );
+}
+
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 (
-
- Nothing to show. This can have any of these reasons:
-
- - We haven't received any reports from probes recently.
- Are the probes properly configured?
- - There are nodes, but they're currently hidden. Check the view options
- in the bottom-left if they allow for showing hidden nodes.
- - Containers view only: you're not running Docker,
- or you don't have any containers.
-
-
- );
- }
-
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} />
- {this.renderEmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)}
+ {renderEmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)}
{gridMode ?
:
diff --git a/client/app/scripts/components/plugins.js b/client/app/scripts/components/plugins.js
index 101f27e0f..93f52567f 100644
--- a/client/app/scripts/components/plugins.js
+++ b/client/app/scripts/components/plugins.js
@@ -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}
Status: ${status}`;
- // Inner span to hold styling so we don't effect the "before:content"
- return (
-
-
- {error && }
- {label || id}
-
-
+function renderPlugin({id, label, description, status}) {
+ const error = status !== 'ok';
+ const className = classNames({ error });
+ const title = `Plugin description: ${description}
Status: ${status}`;
+
+ // Inner span to hold styling so we don't effect the "before:content"
+ return (
+
+
+ {error && }
+ {label || id}
- );
- }
+
+
+ );
+}
+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:
{hasPlugins && this.props.plugins.toIndexedSeq()
- .map(plugin => this.renderPlugin(plugin.toJS()))}
+ .map(plugin => renderPlugin(plugin.toJS()))}
{!hasPlugins &&
n/a}
);
diff --git a/client/app/scripts/components/terminal.js b/client/app/scripts/components/terminal.js
index 4eadf7b6a..7fc4f551d 100644
--- a/client/app/scripts/components/terminal.js
+++ b/client/app/scripts/components/terminal.js
@@ -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');
diff --git a/client/app/scripts/components/topologies.js b/client/app/scripts/components/topologies.js
index 08cb6fec2..df180d319 100644
--- a/client/app/scripts/components/topologies.js
+++ b/client/app/scripts/components/topologies.js
@@ -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 (