diff --git a/client/.eslintignore b/client/.eslintignore index 7ce46b3a1d..5075164602 100644 --- a/client/.eslintignore +++ b/client/.eslintignore @@ -1 +1,2 @@ app/scripts/vendor/term.js +test/ diff --git a/client/.eslintrc b/client/.eslintrc index d7e58c4701..7320a3b9f0 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -7,32 +7,11 @@ }, "rules": { "comma-dangle": 0, - "global-require": 0, - "import/no-extraneous-dependencies": [ - "error", - { - "devDependencies": true, - "optionalDependencies": true, - "peerDependencies": true - } - ], - "import/prefer-default-export": 0, - "jsx-a11y/no-static-element-interactions": 0, "no-param-reassign": 0, - "no-restricted-properties": 0, "object-curly-spacing": 0, "react/jsx-closing-bracket-location": 0, - "react/jsx-filename-extension": [ - 2, - { - "extensions": [ - ".js", - ".jsx" - ] - } - ], "react/prefer-stateless-function": 0, "react/sort-comp": 0, - "react/prop-types": 0, + "react/prop-types": 0 } } diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js index 893d55c5a7..d248c33873 100644 --- a/client/app/scripts/actions/app-actions.js +++ b/client/app/scripts/actions/app-actions.js @@ -39,8 +39,7 @@ export function sortOrderChanged(sortedBy, sortedDesc) { return (dispatch, getState) => { dispatch({ type: ActionTypes.SORT_ORDER_CHANGED, - sortedBy, - sortedDesc + sortedBy, sortedDesc }); updateRoute(getState); }; diff --git a/client/app/scripts/charts/__tests__/node-layout-test.js b/client/app/scripts/charts/__tests__/node-layout-test.js index ec06b61066..72f0467c53 100644 --- a/client/app/scripts/charts/__tests__/node-layout-test.js +++ b/client/app/scripts/charts/__tests__/node-layout-test.js @@ -9,7 +9,7 @@ describe('NodesLayout', () => { const coords = []; nodes .sortBy(node => node.get('id')) - .forEach((node) => { + .forEach(node => { coords.push(node.get('x')); coords.push(node.get('y')); }); diff --git a/client/app/scripts/charts/edge.js b/client/app/scripts/charts/edge.js index ef33a5f1a2..458a8a7c3a 100644 --- a/client/app/scripts/charts/edge.js +++ b/client/app/scripts/charts/edge.js @@ -17,8 +17,7 @@ class Edge extends React.Component { const className = classNames('edge', {highlighted, blurred, focused}); return ( - diff --git a/client/app/scripts/charts/node-container.js b/client/app/scripts/charts/node-container.js index 4faa3ae1dc..92455a8adf 100644 --- a/client/app/scripts/charts/node-container.js +++ b/client/app/scripts/charts/node-container.js @@ -14,13 +14,12 @@ class NodeContainer extends React.Component { const other = omit(this.props, 'dx', 'dy'); return ( - - {(interpolated) => { + + {interpolated => { const transform = `translate(${round(interpolated.x, layoutPrecision)},` + `${round(interpolated.y, layoutPrecision)})`; return ; diff --git a/client/app/scripts/charts/node-networks-overlay.js b/client/app/scripts/charts/node-networks-overlay.js index 520f195658..a125309a45 100644 --- a/client/app/scripts/charts/node-networks-overlay.js +++ b/client/app/scripts/charts/node-networks-overlay.js @@ -26,7 +26,7 @@ function NodeNetworksOverlay({offset, size, stack, networks = makeList()}) { const bars = networks.map((n, i) => ( } - {hasMetric && } + {hasMetric && } {highlighted && hasMetric ? {formattedValue} : } diff --git a/client/app/scripts/charts/node-shape-cloud.js b/client/app/scripts/charts/node-shape-cloud.js index ebf5dd2b77..cf71e326ee 100644 --- a/client/app/scripts/charts/node-shape-cloud.js +++ b/client/app/scripts/charts/node-shape-cloud.js @@ -37,7 +37,8 @@ export default function NodeShapeCloud({highlighted, size, color}) { return ( - {highlighted && } + {highlighted && + } diff --git a/client/app/scripts/charts/node-shape-heptagon.js b/client/app/scripts/charts/node-shape-heptagon.js index c3488ba7f2..40314b1c14 100644 --- a/client/app/scripts/charts/node-shape-heptagon.js +++ b/client/app/scripts/charts/node-shape-heptagon.js @@ -1,8 +1,8 @@ import React from 'react'; import classNames from 'classnames'; import { line, curveCardinalClosed } from 'd3-shape'; -import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils'; -import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles'; +import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils.js'; +import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles.js'; const spline = line() @@ -12,7 +12,7 @@ const spline = line() function polygon(r, sides) { const a = (Math.PI * 2) / sides; const points = []; - for (let i = 0; i < sides; i += 1) { + for (let i = 0; i < sides; i++) { points.push([r * Math.sin(a * i), -r * Math.cos(a * i)]); } return points; @@ -30,20 +30,15 @@ export default function NodeShapeHeptagon({id, highlighted, size, color, metric} const metricStyle = { fill: getMetricColor(metric) }; const className = classNames('shape', { metrics: hasMetric }); const fontSize = size * CANVAS_METRIC_FONT_SIZE; - const halfSize = size * 0.5; return ( - {hasMetric && getClipPathDefinition(clipId, size, height, -halfSize, halfSize - height)} + {hasMetric && getClipPathDefinition(clipId, size, height, -size * 0.5, size * 0.5 - height)} {highlighted && } - {hasMetric && } + {hasMetric && } {highlighted && hasMetric ? {formattedValue} : } diff --git a/client/app/scripts/charts/node-shape-hexagon.js b/client/app/scripts/charts/node-shape-hexagon.js index 7dba51ee2a..36c2a6055e 100644 --- a/client/app/scripts/charts/node-shape-hexagon.js +++ b/client/app/scripts/charts/node-shape-hexagon.js @@ -1,8 +1,8 @@ import React from 'react'; import classNames from 'classnames'; import { line, curveCardinalClosed } from 'd3-shape'; -import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils'; -import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles'; +import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils.js'; +import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles.js'; const spline = line() @@ -48,22 +48,13 @@ export default function NodeShapeHexagon({id, highlighted, size, color, metric}) return ( - {hasMetric && getClipPathDefinition( - clipId, - size * (1 + (hexCurve * 2)), - height, - -(size * hexCurve), - (size - height) * (shadowSize * 2) - )} + {hasMetric && getClipPathDefinition(clipId, + size * (1 + hexCurve * 2), height, -size * hexCurve, (size - height) * shadowSize * 2)} {highlighted && } - {hasMetric && } + {hasMetric && } {highlighted && hasMetric ? {formattedValue} diff --git a/client/app/scripts/charts/node-shape-square.js b/client/app/scripts/charts/node-shape-square.js index f99cfdcebb..d4cd116bc3 100644 --- a/client/app/scripts/charts/node-shape-square.js +++ b/client/app/scripts/charts/node-shape-square.js @@ -1,7 +1,7 @@ import React from 'react'; import classNames from 'classnames'; -import { getMetricValue, getMetricColor, getClipPathDefinition } from '../utils/metric-utils'; -import { CANVAS_METRIC_FONT_SIZE } from '../constants/styles'; +import {getMetricValue, getMetricColor, getClipPathDefinition} from '../utils/metric-utils.js'; +import {CANVAS_METRIC_FONT_SIZE} from '../constants/styles.js'; export default function NodeShapeSquare({ @@ -28,11 +28,8 @@ export default function NodeShapeSquare({ {highlighted && } - {hasMetric && } + {hasMetric && } {highlighted && hasMetric ? {formattedValue} diff --git a/client/app/scripts/charts/node-shape-stack.js b/client/app/scripts/charts/node-shape-stack.js index 1c5cd5f65f..3d691888c9 100644 --- a/client/app/scripts/charts/node-shape-stack.js +++ b/client/app/scripts/charts/node-shape-stack.js @@ -5,8 +5,8 @@ export default function NodeShapeStack(props) { const contrastMode = isContrastMode(); const Shape = props.shape; const [dx, dy] = contrastMode ? [0, 8] : [0, 5]; - const dsx = (props.size + dx) / props.size; - const dsy = (props.size + dy) / props.size; + const dsx = (props.size * 2 + (dx * 2)) / (props.size * 2); + const dsy = (props.size * 2 + (dy * 2)) / (props.size * 2); const hls = [dsx, dsy]; return ( diff --git a/client/app/scripts/charts/node.js b/client/app/scripts/charts/node.js index 1be2a0f3b3..118f5194f7 100644 --- a/client/app/scripts/charts/node.js +++ b/client/app/scripts/charts/node.js @@ -1,4 +1,5 @@ import React from 'react'; +import ReactDOM from 'react-dom'; import { connect } from 'react-redux'; import classnames from 'classnames'; import { Map as makeMap, List as makeList } from 'immutable'; @@ -126,12 +127,9 @@ class Node extends React.Component { svgLabels(label, subLabel, labelClassName, subLabelClassName, labelOffsetY) : - -
@@ -151,11 +149,8 @@ class Node extends React.Component { {...this.props} /> - {showingNetworks && } + {showingNetworks && } ); } @@ -166,7 +161,8 @@ class Node extends React.Component { handleMouseClick(ev) { ev.stopPropagation(); - this.props.clickNode(this.props.id, this.props.label, this.shapeRef.getBoundingClientRect()); + this.props.clickNode(this.props.id, this.props.label, + ReactDOM.findDOMNode(this.shapeRef).getBoundingClientRect()); } handleMouseEnter() { diff --git a/client/app/scripts/charts/nodes-chart-edges.js b/client/app/scripts/charts/nodes-chart-edges.js index 3e0d3ce900..fd764b8199 100644 --- a/client/app/scripts/charts/nodes-chart-edges.js +++ b/client/app/scripts/charts/nodes-chart-edges.js @@ -13,7 +13,7 @@ class NodesChartEdges extends React.Component { return ( - {layoutEdges.toIndexedSeq().map((edge) => { + {layoutEdges.toIndexedSeq().map(edge => { const sourceSelected = selectedNodeId === edge.get('source'); const targetSelected = selectedNodeId === edge.get('target'); const highlighted = highlightedEdgeIds.has(edge.get('id')); @@ -26,8 +26,8 @@ class NodesChartEdges extends React.Component { !(selectedNetworkNodes.contains(edge.get('source')) && selectedNetworkNodes.contains(edge.get('target'))); const blurred = !highlighted && (otherNodesSelected || - (!focused && noMatches) || - (!focused && noSelectedNetworks)); + !focused && noMatches || + !focused && noSelectedNetworks); return ( node.set('blurred', - (selectedNodeId && !node.get('focused')) - || (searchQuery && !searchNodeMatches.has(node.get('id')) && !node.get('highlighted')) - || (selectedNetwork && !(node.get('networks') || makeList()).find(n => n.get('id') === selectedNetwork))); + selectedNodeId && !node.get('focused') + || searchQuery && !searchNodeMatches.has(node.get('id')) + && !node.get('highlighted') + || selectedNetwork + && !(node.get('networks') || makeList()).find(n => n.get('id') === selectedNetwork)); // make sure blurred nodes are in the background - const sortNodes = (node) => { + const sortNodes = node => { if (node.get('id') === mouseOverNodeId) { return 3; } @@ -40,7 +42,7 @@ class NodesChartNodes extends React.Component { }; // TODO: think about pulling this up into the store. - const metric = (node) => { + const metric = node => { const isHighlighted = topCardNode && topCardNode.details && topCardNode.id === node.get('id'); const sourceNode = isHighlighted ? fromJS(topCardNode.details) : node; return sourceNode.get('metrics') && sourceNode.get('metrics') diff --git a/client/app/scripts/charts/nodes-chart.js b/client/app/scripts/charts/nodes-chart.js index 72869150f2..279738494f 100644 --- a/client/app/scripts/charts/nodes-chart.js +++ b/client/app/scripts/charts/nodes-chart.js @@ -52,7 +52,7 @@ function initEdges(nodes) { nodes.forEach((node, nodeId) => { const adjacency = node.get('adjacency'); if (adjacency) { - adjacency.forEach((adjacent) => { + adjacency.forEach(adjacent => { const edge = [nodeId, adjacent]; const edgeId = edge.join(EDGE_ID_SEPARATOR); @@ -115,80 +115,6 @@ 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) { @@ -254,7 +180,7 @@ class NodesChart extends React.Component { assign(state, this.restoreLayout(state)); } if (nextProps.selectedNodeId) { - assign(state, centerSelectedNode(nextProps, state)); + assign(state, this.centerSelectedNode(nextProps, state)); } this.setState(state); @@ -293,8 +219,7 @@ class NodesChart extends React.Component { const layoutPrecision = getLayoutPrecision(nodes.size); return (
- @@ -320,6 +245,78 @@ 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 centerX = (-translate[0] + (state.width + props.margins.left + - DETAILS_PANEL_WIDTH) / 2) / zoomScale; + const centerY = (-translate[1] + (state.height + props.margins.top) / 2) / 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); @@ -329,7 +326,7 @@ class NodesChart extends React.Component { y: node.get('py') })); - const edges = state.edges.map((edge) => { + const edges = state.edges.map(edge => { if (edge.has('ppoints')) { return edge.set('points', edge.get('ppoints')); } diff --git a/client/app/scripts/charts/nodes-grid.js b/client/app/scripts/charts/nodes-grid.js index d3de7f0435..23da67dcaa 100644 --- a/client/app/scripts/charts/nodes-grid.js +++ b/client/app/scripts/charts/nodes-grid.js @@ -17,7 +17,7 @@ const IGNORED_COLUMNS = ['docker_container_ports', 'docker_container_id', 'docke function getColumns(nodes) { const metricColumns = nodes .toList() - .flatMap((n) => { + .flatMap(n => { const metrics = (n.get('metrics') || makeList()) .map(m => makeMap({ id: m.get('id'), label: m.get('label'), dataType: 'number' })); return metrics; @@ -28,7 +28,7 @@ function getColumns(nodes) { const metadataColumns = nodes .toList() - .flatMap((n) => { + .flatMap(n => { const metadata = (n.get('metadata') || makeList()) .map(m => makeMap({ id: m.get('id'), label: m.get('label'), dataType: m.get('dataType') })); return metadata; @@ -40,7 +40,7 @@ function getColumns(nodes) { const relativesColumns = nodes .toList() - .flatMap((n) => { + .flatMap(n => { const metadata = (n.get('parents') || makeList()) .map(m => makeMap({ id: m.get('topologyId'), label: m.get('topologyId') })); return metadata; diff --git a/client/app/scripts/charts/nodes-layout.js b/client/app/scripts/charts/nodes-layout.js index efd3565414..670671777b 100644 --- a/client/app/scripts/charts/nodes-layout.js +++ b/client/app/scripts/charts/nodes-layout.js @@ -55,7 +55,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) { }); // add nodes to the graph if not already there - nodes.forEach((node) => { + nodes.forEach(node => { const gNodeId = graphNodeId(node.get('id')); if (!graph.hasNode(gNodeId)) { graph.setNode(gNodeId, { @@ -66,7 +66,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) { }); // remove nodes that are no longer there or are 0-degree nodes - graph.nodes().forEach((gNodeId) => { + graph.nodes().forEach(gNodeId => { const nodeId = fromGraphNodeId(gNodeId); if (!nodes.has(nodeId) || nodes.get(nodeId).get('degree') === 0) { graph.removeNode(gNodeId); @@ -74,7 +74,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) { }); // add edges to the graph if not already there - edges.forEach((edge) => { + edges.forEach(edge => { const s = graphNodeId(edge.get('source')); const t = graphNodeId(edge.get('target')); if (!graph.hasEdge(s, t)) { @@ -84,7 +84,7 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) { }); // remove edges that are no longer there - graph.edges().forEach((edgeObj) => { + graph.edges().forEach(edgeObj => { const edge = [fromGraphNodeId(edgeObj.v), fromGraphNodeId(edgeObj.w)]; const edgeId = edge.join(EDGE_ID_SEPARATOR); if (!edges.has(edgeId)) { @@ -97,14 +97,14 @@ function runLayoutEngine(graph, imNodes, imEdges, opts) { // apply coordinates to nodes and edges - graph.nodes().forEach((gNodeId) => { + graph.nodes().forEach(gNodeId => { const graphNode = graph.node(gNodeId); const nodeId = fromGraphNodeId(gNodeId); nodes = nodes.setIn([nodeId, 'x'], graphNode.x); nodes = nodes.setIn([nodeId, 'y'], graphNode.y); }); - graph.edges().forEach((graphEdge) => { + graph.edges().forEach(graphEdge => { const graphEdgeMeta = graph.edge(graphEdge); const edge = edges.get(graphEdgeMeta.id); let points = fromJS(graphEdgeMeta.points); @@ -165,7 +165,7 @@ export function doLayoutNewNodesOfExistingRank(layout, nodeCache, opts) { const oldNodes = ImmSet.fromKeys(nodeCache); const newNodes = ImmSet.fromKeys(layout.nodes.filter(n => n.get('degree') > 0)) .subtract(oldNodes); - result.nodes = layout.nodes.map((n) => { + result.nodes = layout.nodes.map(n => { if (newNodes.contains(n.get('id'))) { const nodesSameRank = nodeCache.filter(nn => nn.get('rank') === n.get('rank')); if (nodesSameRank.size > 0) { @@ -178,7 +178,7 @@ export function doLayoutNewNodesOfExistingRank(layout, nodeCache, opts) { return n; }); - result.edges = layout.edges.map((edge) => { + result.edges = layout.edges.map(edge => { if (!edge.has('points')) { return setSimpleEdgePoints(edge, layout.nodes); } @@ -237,23 +237,23 @@ function layoutSingleNodes(layout, opts) { } // default margins - offsetX = offsetX || (margins.left + nodeWidth) / 2; - offsetY = offsetY || (margins.top + nodeHeight) / 2; + offsetX = offsetX || margins.left + nodeWidth / 2; + offsetY = offsetY || margins.top + nodeHeight / 2; const columns = Math.ceil(Math.sqrt(singleNodes.size)); let row = 0; let col = 0; let singleX; let singleY; - nodes = nodes.sortBy(node => node.get('rank')).map((node) => { + nodes = nodes.sortBy(node => node.get('rank')).map(node => { if (singleNodes.has(node.get('id'))) { if (col === columns) { col = 0; - row += 1; + row++; } - singleX = (col * (nodesep + nodeWidth)) + offsetX; - singleY = (row * (ranksep + nodeHeight)) + offsetY; - col += 1; + singleX = col * (nodesep + nodeWidth) + offsetX; + singleY = row * (ranksep + nodeHeight) + offsetY; + col++; return node.merge({ x: singleX, y: singleY @@ -263,8 +263,8 @@ function layoutSingleNodes(layout, opts) { }); // adjust layout dimensions if graph is now bigger - result.width = Math.max(layout.width, singleX + (nodeWidth / 2) + nodesep); - result.height = Math.max(layout.height, singleY + (nodeHeight / 2) + ranksep); + result.width = Math.max(layout.width, singleX + nodeWidth / 2 + nodesep); + result.height = Math.max(layout.height, singleY + nodeHeight / 2 + ranksep); result.nodes = nodes; } @@ -290,12 +290,12 @@ export function shiftLayoutToCenter(layout, opts) { if (layout.width < width) { const xMin = layout.nodes.minBy(n => n.get('x')); const xMax = layout.nodes.maxBy(n => n.get('x')); - offsetX = ((width - (xMin.get('x') + xMax.get('x'))) / 2) + margins.left; + offsetX = (width - (xMin.get('x') + xMax.get('x'))) / 2 + margins.left; } if (layout.height < height) { const yMin = layout.nodes.minBy(n => n.get('y')); const yMax = layout.nodes.maxBy(n => n.get('y')); - offsetY = ((height - (yMin.get('y') + yMax.get('y'))) / 2) + margins.top; + offsetY = (height - (yMin.get('y') + yMax.get('y'))) / 2 + margins.top; } if (offsetX || offsetY) { @@ -412,7 +412,7 @@ function copyLayoutProperties(layout, nodeCache, edgeCache) { const result = Object.assign({}, layout); result.nodes = layout.nodes.map(node => (nodeCache.has(node.get('id')) ? node.merge(nodeCache.get(node.get('id'))) : node)); - result.edges = layout.edges.map((edge) => { + result.edges = layout.edges.map(edge => { if (edgeCache.has(edge.get('id')) && hasSameEndpoints(edgeCache.get(edge.get('id')), result.nodes)) { return edge.merge(edgeCache.get(edge.get('id'))); @@ -453,10 +453,9 @@ export function doLayout(immNodes, immEdges, opts) { const useCache = !options.forceRelayout && cachedLayout && nodeCache && edgeCache; let layout; - layoutRuns += 1; + ++layoutRuns; if (useCache && !hasUnseenNodes(immNodes, nodeCache)) { - layoutRunsTrivial += 1; - log('skip layout, trivial adjustment', layoutRunsTrivial, layoutRuns); + log('skip layout, trivial adjustment', ++layoutRunsTrivial, layoutRuns); layout = cloneLayout(cachedLayout, immNodes, immEdges); // copy old properties, works also if nodes get re-added layout = copyLayoutProperties(layout, nodeCache, edgeCache); diff --git a/client/app/scripts/components/__tests__/node-details-test.js b/client/app/scripts/components/__tests__/node-details-test.js index 66725fb661..461c8b14d2 100644 --- a/client/app/scripts/components/__tests__/node-details-test.js +++ b/client/app/scripts/components/__tests__/node-details-test.js @@ -5,7 +5,7 @@ import { Provider } from 'react-redux'; import configureStore from '../../stores/configureStore'; // need ES5 require to keep automocking off -const NodeDetails = require('../node-details.js').default.WrappedComponent; +const NodeDetails = require('../node-details.js').NodeDetails; describe('NodeDetails', () => { let nodes; @@ -34,8 +34,7 @@ describe('NodeDetails', () => { details = {label: 'Node 1'}; const c = TestUtils.renderIntoDocument( - diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index 696508212d..3b92384024 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -3,13 +3,13 @@ import React from 'react'; import { connect } from 'react-redux'; import Logo from './logo'; -import Footer from './footer'; -import Sidebar from './sidebar'; +import Footer from './footer.js'; +import Sidebar from './sidebar.js'; import HelpPanel from './help-panel'; import Search from './search'; -import Status from './status'; -import Topologies from './topologies'; -import TopologyOptions from './topology-options'; +import Status from './status.js'; +import Topologies from './topologies.js'; +import TopologyOptions from './topology-options.js'; import { getApiDetails, getTopologies } from '../utils/web-api-utils'; import { focusSearch, pinNextMetric, hitBackspace, hitEnter, hitEsc, unpinMetric, selectMetric, toggleHelp, toggleGridMode } from '../actions/app-actions'; @@ -18,8 +18,10 @@ import Nodes from './nodes'; import GridModeSelector from './grid-mode-selector'; import MetricSelector from './metric-selector'; import NetworkSelector from './networks-selector'; -import DebugToolbar, { showingDebugToolbar, toggleDebugToolbar } from './debug-toolbar'; -import { getRouter, getUrlState } from '../utils/router-utils'; +import { getRouter } from '../utils/router-utils'; +import DebugToolbar, { showingDebugToolbar, + toggleDebugToolbar } from './debug-toolbar.js'; +import { getUrlState } from '../utils/router-utils'; import { getActiveTopologyOptions } from '../utils/topology-utils'; const BACKSPACE_KEY_CODE = 8; diff --git a/client/app/scripts/components/debug-toolbar.js b/client/app/scripts/components/debug-toolbar.js index 8da0d3f0de..ff6a8873c3 100644 --- a/client/app/scripts/components/debug-toolbar.js +++ b/client/app/scripts/components/debug-toolbar.js @@ -5,7 +5,9 @@ import { connect } from 'react-redux'; import { sampleSize, sample, random, range, flattenDeep } from 'lodash'; import { fromJS, Set as makeSet } from 'immutable'; import { hsl } from 'd3-color'; + import debug from 'debug'; +const log = debug('scope:debug-panel'); import ActionTypes from '../constants/action-types'; import { receiveNodesDelta } from '../actions/app-actions'; @@ -27,7 +29,7 @@ voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occa proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`; const sampleArray = (collection, n = 4) => sampleSize(collection, random(n)); -const log = debug('scope:debug-panel'); + const shapeTypes = { square: ['Process', 'Processes'], @@ -78,7 +80,7 @@ function label(shape, stacked) { function addAllVariants(dispatch) { - const newNodes = flattenDeep(STACK_VARIANTS.map(stack => (SHAPES.map((s) => { + const newNodes = flattenDeep(STACK_VARIANTS.map(stack => (SHAPES.map(s => { if (!stack) return [deltaAdd(label(s, stack), [], s, stack, 1)]; return NODE_COUNTS.map(n => deltaAdd(label(s, stack), [], s, stack, n)); })))); @@ -237,16 +239,16 @@ class DebugToolbar extends React.Component { const ns = this.props.nodes; const nodeNames = ns.keySeq().toJS(); this.asyncDispatch(receiveNodesDelta({ - add: this.createRandomNodes(7), + add: this._addNodes(7), update: sampleArray(nodeNames).map(n => ({ id: n, adjacency: sampleArray(nodeNames), }), nodeNames.length), - remove: this.randomExistingNode(), + remove: this._removeNode(), })); } - createRandomNodes(n, prefix = 'zing') { + _addNodes(n, prefix = 'zing') { const ns = this.props.nodes; const nodeNames = ns.keySeq().toJS(); const newNodeNames = range(ns.size, ns.size + n).map(i => ( @@ -254,7 +256,7 @@ class DebugToolbar extends React.Component { `${prefix}${i}` )); const allNodes = nodeNames.concat(newNodeNames); - return newNodeNames.map(name => deltaAdd( + return newNodeNames.map((name) => deltaAdd( name, sampleArray(allNodes), sample(SHAPES), @@ -267,13 +269,13 @@ class DebugToolbar extends React.Component { addNodes(n, prefix = 'zing') { setTimeout(() => { this.asyncDispatch(receiveNodesDelta({ - add: this.createRandomNodes(n, prefix) + add: this._addNodes(n, prefix) })); log('added nodes', n); }, 0); } - randomExistingNode() { + _removeNode() { const ns = this.props.nodes; const nodeNames = ns.keySeq().toJS(); return [nodeNames[random(nodeNames.length - 1)]]; @@ -281,7 +283,7 @@ class DebugToolbar extends React.Component { removeNode() { this.asyncDispatch(receiveNodesDelta({ - remove: this.randomExistingNode() + remove: this._removeNode() })); } @@ -291,7 +293,7 @@ class DebugToolbar extends React.Component { return (
- Add nodes + @@ -306,7 +308,7 @@ class DebugToolbar extends React.Component {
- Logging + @@ -315,7 +317,7 @@ class DebugToolbar extends React.Component {
- Colors +
@@ -338,7 +340,7 @@ class DebugToolbar extends React.Component { {LABEL_PREFIXES.map(r => ( {LABEL_PREFIXES.map(c => ( - + ))} ))} @@ -347,19 +349,19 @@ class DebugToolbar extends React.Component { ))}
- State +
- Short-lived nodes +
- Measure React perf for + diff --git a/client/app/scripts/components/details-card.js b/client/app/scripts/components/details-card.js index 703719ce9c..1179248c4e 100644 --- a/client/app/scripts/components/details-card.js +++ b/client/app/scripts/components/details-card.js @@ -3,11 +3,8 @@ import { connect } from 'react-redux'; import NodeDetails from './node-details'; import EmbeddedTerminal from './embedded-terminal'; -import { - DETAILS_PANEL_WIDTH as WIDTH, - DETAILS_PANEL_OFFSET as OFFSET, - DETAILS_PANEL_MARGINS as MARGINS -} from '../constants/styles'; +import { DETAILS_PANEL_WIDTH as WIDTH, DETAILS_PANEL_OFFSET as OFFSET, + DETAILS_PANEL_MARGINS as MARGINS } from '../constants/styles'; class DetailsCard extends React.Component { @@ -33,15 +30,15 @@ class DetailsCard extends React.Component { const scaleY = origin.height / (window.innerHeight - MARGINS.bottom - MARGINS.top) / 2; const scaleX = origin.width / WIDTH / 2; const centerX = window.innerWidth - MARGINS.right - (WIDTH / 2); - const centerY = (panelHeight / 2) + MARGINS.top; - const dx = (origin.left + (origin.width / 2)) - centerX; - const dy = (origin.top + (origin.height / 2)) - centerY; + const centerY = (panelHeight) / 2 + MARGINS.top; + const dx = (origin.left + origin.width / 2) - centerX; + const dy = (origin.top + origin.height / 2) - centerY; transform = `translate(${dx}px, ${dy}px) scale(${scaleX},${scaleY})`; } else { // stack effect: shift top cards to the left, shrink lower cards vertically const shiftX = -1 * this.props.index * OFFSET; const position = this.props.cardCount - this.props.index - 1; // reverse index - const scaleY = (position === 0) ? 1 : (panelHeight - (2 * OFFSET * position)) / panelHeight; + const scaleY = position === 0 ? 1 : (panelHeight - 2 * OFFSET * position) / panelHeight; if (scaleY !== 1) { transform = `translateX(${shiftX}px) scaleY(${scaleY})`; } else { diff --git a/client/app/scripts/components/details.js b/client/app/scripts/components/details.js index 553751062e..87938a0521 100644 --- a/client/app/scripts/components/details.js +++ b/client/app/scripts/components/details.js @@ -9,12 +9,10 @@ class Details extends React.Component { // render all details as cards, later cards go on top return (
- {details.toIndexedSeq().map((obj, index) => ( - - ))} + {details.toIndexedSeq().map((obj, index) => + )}
); } diff --git a/client/app/scripts/components/dev-tools.js b/client/app/scripts/components/dev-tools.js index 2722a23a4f..3012ab99b0 100644 --- a/client/app/scripts/components/dev-tools.js +++ b/client/app/scripts/components/dev-tools.js @@ -4,10 +4,8 @@ import LogMonitor from 'redux-devtools-log-monitor'; import DockMonitor from 'redux-devtools-dock-monitor'; export default createDevTools( - + ); diff --git a/client/app/scripts/components/footer.js b/client/app/scripts/components/footer.js index b1a178704c..fe9a45ed4a 100644 --- a/client/app/scripts/components/footer.js +++ b/client/app/scripts/components/footer.js @@ -2,7 +2,7 @@ import React from 'react'; import { connect } from 'react-redux'; import moment from 'moment'; -import Plugins from './plugins'; +import Plugins from './plugins.js'; import { getUpdateBufferSize } from '../utils/update-buffer-utils'; import { contrastModeUrl, isContrastMode } from '../utils/contrast-utils'; import { clickDownloadGraph, clickForceRelayout, clickPauseUpdate, @@ -48,11 +48,8 @@ class Footer extends React.Component {
- {versionUpdate && + {versionUpdate && Update available: {versionUpdate.version} } Version @@ -70,14 +67,11 @@ class Footer extends React.Component { {pauseLabel !== '' && {pauseLabel}} - - @@ -87,13 +81,11 @@ class Footer extends React.Component { - + - +
@@ -114,11 +106,6 @@ function mapStateToProps(state) { export default connect( mapStateToProps, - { - clickDownloadGraph, - clickForceRelayout, - clickPauseUpdate, - clickResumeUpdate, - toggleHelp - } + { clickDownloadGraph, clickForceRelayout, clickPauseUpdate, + clickResumeUpdate, toggleHelp } )(Footer); diff --git a/client/app/scripts/components/grid-mode-selector.js b/client/app/scripts/components/grid-mode-selector.js index e8d843674e..e27d849eeb 100644 --- a/client/app/scripts/components/grid-mode-selector.js +++ b/client/app/scripts/components/grid-mode-selector.js @@ -4,21 +4,6 @@ import classNames from 'classnames'; import { toggleGridMode } from '../actions/app-actions'; - -const Item = (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) { @@ -36,14 +21,28 @@ 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 (
- {Item('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)} - {Item('fa fa-table', 'Table', gridMode, this.enableGridMode)} + {this.renderItem('fa fa-share-alt', 'Graph', !gridMode, this.disableGridMode)} + {this.renderItem('fa fa-table', 'Table', gridMode, this.enableGridMode)}
); diff --git a/client/app/scripts/components/help-panel.js b/client/app/scripts/components/help-panel.js index aad9b3b7f0..b7147aaa64 100644 --- a/client/app/scripts/components/help-panel.js +++ b/client/app/scripts/components/help-panel.js @@ -85,7 +85,7 @@ function renderSearches(searches) { {searches.map(({term, label}) => (
- + {term}
{label}
diff --git a/client/app/scripts/components/logo.js b/client/app/scripts/components/logo.js index 8e845dbdd9..8b402443d6 100644 --- a/client/app/scripts/components/logo.js +++ b/client/app/scripts/components/logo.js @@ -1,4 +1,3 @@ -/* eslint react/jsx-first-prop-new-line: "off" */ /* eslint max-len: "off" */ import React from 'react'; diff --git a/client/app/scripts/components/matched-results.js b/client/app/scripts/components/matched-results.js index ef1a170f30..48ed8f3fd1 100644 --- a/client/app/scripts/components/matched-results.js +++ b/client/app/scripts/components/matched-results.js @@ -6,22 +6,25 @@ import MatchedText from './matched-text'; const SHOW_ROW_COUNT = 2; const MAX_MATCH_LENGTH = 24; +class MatchedResults extends React.Component { -const Match = match => ( -
-
- - {match.label}: - - -
-
-); + renderMatch(matches, field) { + const match = matches.get(field); + const text = match.text; + + return ( +
+
+ + {match.label}: + + +
+
+ ); + } -class MatchedResults extends React.Component { render() { const { matches, style } = this.props; @@ -41,7 +44,7 @@ class MatchedResults extends React.Component { return (
- {matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => Match(matches.get(fieldId)))} + {matches.keySeq().take(SHOW_ROW_COUNT).map(fieldId => this.renderMatch(matches, fieldId))} {moreFieldMatches &&
{`${moreFieldMatches.size} more matches`}
} diff --git a/client/app/scripts/components/matched-text.js b/client/app/scripts/components/matched-text.js index 7aaa07d491..f444ba7227 100644 --- a/client/app/scripts/components/matched-text.js +++ b/client/app/scripts/components/matched-text.js @@ -84,7 +84,7 @@ class MatchedText extends React.Component { render() { const { match, text, truncate, maxLength } = this.props; - const showFullValue = !truncate || (match && (match.start + match.length) > truncate); + const showFullValue = !truncate || match && match.start + match.length > truncate; const displayText = showFullValue ? text : text.slice(0, truncate); if (!match) { diff --git a/client/app/scripts/components/metric-selector-item.js b/client/app/scripts/components/metric-selector-item.js index 3bce457ec6..9c6e4481c7 100644 --- a/client/app/scripts/components/metric-selector-item.js +++ b/client/app/scripts/components/metric-selector-item.js @@ -45,7 +45,7 @@ class MetricSelectorItem extends React.Component { onMouseOver={this.onMouseOver} onClick={this.onMouseClick}> {metric.get('label')} - {isPinned && } + {isPinned && }
); } diff --git a/client/app/scripts/components/network-selector-item.js b/client/app/scripts/components/network-selector-item.js index 44043402e0..fbd747c263 100644 --- a/client/app/scripts/components/network-selector-item.js +++ b/client/app/scripts/components/network-selector-item.js @@ -50,7 +50,7 @@ class NetworkSelectorItem extends React.Component { onClick={this.onMouseClick} style={style}> {network.get('label')} - {isPinned && } + {isPinned && }
); } diff --git a/client/app/scripts/components/node-details.js b/client/app/scripts/components/node-details.js index b8abffd1b7..4e7f7ee6ab 100644 --- a/client/app/scripts/components/node-details.js +++ b/client/app/scripts/components/node-details.js @@ -20,7 +20,7 @@ function getTruncationText(count) { + ` (${count} extra entries not included). We are working to remove this limitation.`; } -class NodeDetails extends React.Component { +export class NodeDetails extends React.Component { constructor(props, context) { super(props, context); @@ -53,10 +53,8 @@ class NodeDetails extends React.Component { return (
- {showSwitchTopology && + {showSwitchTopology && Show in {this.props.topologyId.replace(/-/g, ' ')} } @@ -170,8 +168,7 @@ class NodeDetails extends React.Component {
{showControls &&
- @@ -187,23 +184,20 @@ class NodeDetails extends React.Component {
} - {details.connections && details.connections.map(connections => ( -
- + {details.connections && details.connections.map(connections =>
+
- ))} + )} - {details.children && details.children.map(children => ( -
+ {details.children && details.children.map(children =>
- ))} + )} - {details.tables && details.tables.length > 0 && details.tables.map((table) => { + {details.tables && details.tables.length > 0 && details.tables.map(table => { if (table.rows.length > 0) { return (
@@ -214,8 +208,7 @@ class NodeDetails extends React.Component { }
-
); diff --git a/client/app/scripts/components/node-details/node-details-controls.js b/client/app/scripts/components/node-details/node-details-controls.js index 9c6d828c7b..87a45df994 100644 --- a/client/app/scripts/components/node-details/node-details-controls.js +++ b/client/app/scripts/components/node-details/node-details-controls.js @@ -21,7 +21,7 @@ export default function NodeDetailsControls({controls, error, nodeId, pending}) {sortBy(controls, 'rank').map(control => )} - {controls && } + {controls && }
); } diff --git a/client/app/scripts/components/node-details/node-details-health-item.js b/client/app/scripts/components/node-details/node-details-health-item.js index 04767a00cd..f418220d6b 100644 --- a/client/app/scripts/components/node-details/node-details-health-item.js +++ b/client/app/scripts/components/node-details/node-details-health-item.js @@ -6,10 +6,9 @@ import { formatMetric } from '../../utils/string-utils'; function NodeDetailsHealthItem(props) { return (
-
{formatMetric(props.value, props)}
+
{formatMetric(props.value, props)}
-
{props.label}
diff --git a/client/app/scripts/components/node-details/node-details-health.js b/client/app/scripts/components/node-details/node-details-health.js index c515562643..18831c2d8b 100644 --- a/client/app/scripts/components/node-details/node-details-health.js +++ b/client/app/scripts/components/node-details/node-details-health.js @@ -33,13 +33,10 @@ export default class NodeDetailsHealth extends React.Component {
{primeMetrics.map(item => )} - {showOverflow && } + {showOverflow && }
-
); diff --git a/client/app/scripts/components/node-details/node-details-info.js b/client/app/scripts/components/node-details/node-details-info.js index 098a5868b3..7c2ce17585 100644 --- a/client/app/scripts/components/node-details/node-details-info.js +++ b/client/app/scripts/components/node-details/node-details-info.js @@ -38,7 +38,7 @@ export default class NodeDetailsInfo extends React.Component { return (
- {rows.map((field) => { + {rows.map(field => { const { value, title } = formatDataType(field); return (
@@ -54,8 +54,7 @@ export default class NodeDetailsInfo extends React.Component {
); })} -
); 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 6823f1330a..b3928a50f4 100644 --- a/client/app/scripts/components/node-details/node-details-labels.js +++ b/client/app/scripts/components/node-details/node-details-labels.js @@ -6,14 +6,6 @@ import MatchedText from '../matched-text'; import NodeDetailsControlButton from './node-details-control-button'; import ShowMore from '../show-more'; - -const Controls = controls => ( -
- {sortBy(controls, 'rank').map(control => )} -
-); - export default class NodeDetailsLabels extends React.Component { constructor(props, context) { @@ -23,6 +15,7 @@ export default class NodeDetailsLabels extends React.Component { limit: this.DEFAULT_LIMIT, }; this.handleLimitClick = this.handleLimitClick.bind(this); + this.renderControls = this.renderControls.bind(this); } handleLimitClick() { @@ -30,6 +23,15 @@ 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; @@ -47,12 +49,10 @@ export default class NodeDetailsLabels extends React.Component { return (
- {controls && Controls(controls)} - {rows.map(field => ( -
-
+ {controls && this.renderControls(controls)} + {rows.map(field => (
+
{field.label}
@@ -60,8 +60,7 @@ export default class NodeDetailsLabels extends React.Component {
))} -
); diff --git a/client/app/scripts/components/node-details/node-details-relatives-link.js b/client/app/scripts/components/node-details/node-details-relatives-link.js index 30d254a604..3505ac7738 100644 --- a/client/app/scripts/components/node-details/node-details-relatives-link.js +++ b/client/app/scripts/components/node-details/node-details-relatives-link.js @@ -1,4 +1,5 @@ import React from 'react'; +import ReactDOM from 'react-dom'; import { connect } from 'react-redux'; import { clickRelative } from '../../actions/app-actions'; @@ -9,29 +10,18 @@ class NodeDetailsRelativesLink extends React.Component { constructor(props, context) { super(props, context); this.handleClick = this.handleClick.bind(this); - this.saveNodeRef = this.saveNodeRef.bind(this); } handleClick(ev) { ev.preventDefault(); - this.props.dispatch(clickRelative( - this.props.id, - this.props.topologyId, - this.props.label, - this.node.getBoundingClientRect() - )); - } - - saveNodeRef(ref) { - this.node = ref; + this.props.dispatch(clickRelative(this.props.id, this.props.topologyId, + this.props.label, ReactDOM.findDOMNode(this).getBoundingClientRect())); } render() { const title = `View in ${this.props.topologyId}: ${this.props.label}`; return ( - + ); diff --git a/client/app/scripts/components/node-details/node-details-relatives.js b/client/app/scripts/components/node-details/node-details-relatives.js index 26cef8a78a..06c3038a5e 100644 --- a/client/app/scripts/components/node-details/node-details-relatives.js +++ b/client/app/scripts/components/node-details/node-details-relatives.js @@ -39,11 +39,8 @@ export default class NodeDetailsRelatives extends React.Component { key={relative.id} match={matches.get(relative.id)} {...relative} />))} - {showLimitAction && - {limitActionText} - } + {showLimitAction && {limitActionText}}
); } diff --git a/client/app/scripts/components/node-details/node-details-table-node-link.js b/client/app/scripts/components/node-details/node-details-table-node-link.js index 3eef941ef7..af66e96093 100644 --- a/client/app/scripts/components/node-details/node-details-table-node-link.js +++ b/client/app/scripts/components/node-details/node-details-table-node-link.js @@ -1,4 +1,5 @@ import React from 'react'; +import ReactDOM from 'react-dom'; import { connect } from 'react-redux'; import { clickRelative } from '../../actions/app-actions'; @@ -8,21 +9,12 @@ class NodeDetailsTableNodeLink extends React.Component { constructor(props, context) { super(props, context); this.handleClick = this.handleClick.bind(this); - this.saveNodeRef = this.saveNodeRef.bind(this); } handleClick(ev) { ev.preventDefault(); - this.props.dispatch(clickRelative( - this.props.nodeId, - this.props.topologyId, - this.props.label, - this.node.getBoundingClientRect() - )); - } - - saveNodeRef(ref) { - this.node = ref; + this.props.dispatch(clickRelative(this.props.nodeId, this.props.topologyId, + this.props.label, ReactDOM.findDOMNode(this).getBoundingClientRect())); } render() { @@ -31,9 +23,8 @@ class NodeDetailsTableNodeLink extends React.Component { if (linkable) { return ( - + {label} ); diff --git a/client/app/scripts/components/node-details/node-details-table-row.js b/client/app/scripts/components/node-details/node-details-table-row.js index a26bee5518..2a9f9e3eb7 100644 --- a/client/app/scripts/components/node-details/node-details-table-row.js +++ b/client/app/scripts/components/node-details/node-details-table-row.js @@ -1,4 +1,5 @@ import React from 'react'; +import ReactDOM from 'react-dom'; import classNames from 'classnames'; import NodeDetailsTableNodeLink from './node-details-table-node-link'; @@ -7,9 +8,9 @@ import { formatDataType } from '../../utils/string-utils'; function getValuesForNode(node) { const values = {}; - ['metrics', 'metadata'].forEach((collection) => { + ['metrics', 'metadata'].forEach(collection => { if (node[collection]) { - node[collection].forEach((field) => { + node[collection].forEach(field => { const result = Object.assign({}, field); result.valueType = collection; values[field.id] = result; @@ -17,7 +18,7 @@ function getValuesForNode(node) { } }); - (node.parents || []).forEach((p) => { + (node.parents || []).forEach(p => { values[p.topologyId] = { id: p.topologyId, label: p.topologyId, @@ -39,9 +40,7 @@ function renderValues(node, columns = [], columnStyles = []) { if (field.valueType === 'metadata') { const {value, title} = formatDataType(field); return ( - {value} @@ -50,9 +49,7 @@ function renderValues(node, columns = [], columnStyles = []) { } if (field.valueType === 'relatives') { return ( - {} @@ -78,15 +75,15 @@ export default class NodeDetailsTableRow extends React.Component { // this.mouseDragOrigin = [0, 0]; - this.saveLabelElementRef = this.saveLabelElementRef.bind(this); + this.storeLabelRef = this.storeLabelRef.bind(this); this.onMouseDown = this.onMouseDown.bind(this); this.onMouseUp = this.onMouseUp.bind(this); this.onMouseEnter = this.onMouseEnter.bind(this); this.onMouseLeave = this.onMouseLeave.bind(this); } - saveLabelElementRef(ref) { - this.labelElement = ref; + storeLabelRef(ref) { + this.labelEl = ref; } onMouseEnter() { @@ -117,7 +114,7 @@ export default class NodeDetailsTableRow extends React.Component { } const { node, onClick } = this.props; - onClick(ev, node, this.labelElement); + onClick(ev, node, ReactDOM.findDOMNode(this.labelEl)); } render() { @@ -135,9 +132,8 @@ export default class NodeDetailsTableRow extends React.Component { onMouseEnter={onMouseEnterRow && this.onMouseEnter} onMouseLeave={onMouseLeaveRow && this.onMouseLeave} className={className}> - + {this.props.renderIdCell(Object.assign(node, {topologyId, nodeId}))} {values} @@ -148,5 +144,5 @@ export default class NodeDetailsTableRow extends React.Component { NodeDetailsTableRow.defaultProps = { - renderIdCell: props => + renderIdCell: (props) => }; diff --git a/client/app/scripts/components/node-details/node-details-table.js b/client/app/scripts/components/node-details/node-details-table.js index a7c820fc10..1b464fe05e 100644 --- a/client/app/scripts/components/node-details/node-details-table.js +++ b/client/app/scripts/components/node-details/node-details-table.js @@ -113,13 +113,13 @@ function getNodeValue(node, header) { function getValueForSortedBy(sortedByHeader) { - return node => maybeToLower(getNodeValue(node, sortedByHeader)); + return (node) => maybeToLower(getNodeValue(node, sortedByHeader)); } function getMetaDataSorters(nodes) { // returns an array of sorters that will take a node - return get(nodes, [0, 'metadata'], []).map((field, index) => (node) => { + return get(nodes, [0, 'metadata'], []).map((field, index) => node => { const nodeMetadataField = node.metadata && node.metadata[index]; if (nodeMetadataField) { if (isNumber(nodeMetadataField)) { @@ -225,7 +225,7 @@ export default class NodeDetailsTable extends React.Component { {headers.map((header, i) => { const headerClasses = ['node-details-table-header', 'truncate']; - const onHeaderClick = (ev) => { + const onHeaderClick = ev => { this.handleHeaderClick(ev, header.id, sortedBy, sortedDesc); }; // sort by first metric by default @@ -243,8 +243,7 @@ export default class NodeDetailsTable extends React.Component { header.label; return ( - {isSortedAsc && } @@ -279,15 +278,14 @@ export default class NodeDetailsTable extends React.Component { const className = classNames('node-details-table-wrapper-wrapper', this.props.className); return ( -
+
{this.renderHeaders(sortedBy, sortedDesc)} - {nodes && nodes.map(node => ( ( - -); - class Nodes extends React.Component { constructor(props, context) { super(props, context); @@ -46,6 +32,22 @@ class Nodes extends React.Component { window.removeEventListener('resize', this.handleResize); } + renderEmptyTopologyError(show) { + return ( + + ); + } + render() { const { topologyEmpty, gridMode, topologiesLoaded, nodesLoaded, topologies, currentTopology } = this.props; @@ -58,11 +60,16 @@ class Nodes extends React.Component { itemType={getNodeType(currentTopology, topologies)} show={topologiesLoaded && !nodesLoaded} /> - {EmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)} + {this.renderEmptyTopologyError(topologiesLoaded && nodesLoaded && topologyEmpty)} {gridMode ? - : - } + : + } ); } diff --git a/client/app/scripts/components/plugins.js b/client/app/scripts/components/plugins.js index 61cdea746f..101f27e0f4 100644 --- a/client/app/scripts/components/plugins.js +++ b/client/app/scripts/components/plugins.js @@ -3,25 +3,24 @@ 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}`; -const Plugin = ({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} + // 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 +28,8 @@ class Plugins extends React.Component { Plugins: - {hasPlugins && this.props.plugins.toIndexedSeq().map(plugin => Plugin(plugin.toJS()))} + {hasPlugins && this.props.plugins.toIndexedSeq() + .map(plugin => this.renderPlugin(plugin.toJS()))} {!hasPlugins && n/a} ); diff --git a/client/app/scripts/components/search.js b/client/app/scripts/components/search.js index 65259cc438..1164baae19 100644 --- a/client/app/scripts/components/search.js +++ b/client/app/scripts/components/search.js @@ -1,4 +1,5 @@ import React from 'react'; +import ReactDOM from 'react-dom'; import { connect } from 'react-redux'; import classnames from 'classnames'; import { debounce } from 'lodash'; @@ -47,7 +48,6 @@ class Search extends React.Component { this.handleBlur = this.handleBlur.bind(this); this.handleChange = this.handleChange.bind(this); this.handleFocus = this.handleFocus.bind(this); - this.saveQueryInputRef = this.saveQueryInputRef.bind(this); this.doSearch = debounce(this.doSearch.bind(this), 200); this.state = { value: '' @@ -81,10 +81,6 @@ class Search extends React.Component { this.props.doSearch(value); } - saveQueryInputRef(ref) { - this.queryInput = ref; - } - componentWillReceiveProps(nextProps) { // when cleared from the outside, reset internal state if (this.props.searchQuery !== nextProps.searchQuery && nextProps.searchQuery === '') { @@ -94,9 +90,9 @@ class Search extends React.Component { componentDidUpdate() { if (this.props.searchFocused) { - this.queryInput.focus(); + ReactDOM.findDOMNode(this.refs.queryInput).focus(); } else if (!this.state.value) { - this.queryInput.blur(); + ReactDOM.findDOMNode(this.refs.queryInput).blur(); } } @@ -125,14 +121,13 @@ class Search extends React.Component {
{showPinnedSearches && pinnedSearches.toIndexedSeq() .map(query => )} - + disabled={disabled} ref="queryInput" />
- + diff --git a/client/app/scripts/components/sparkline.js b/client/app/scripts/components/sparkline.js index 25c409212a..ea23e9dfd1 100644 --- a/client/app/scripts/components/sparkline.js +++ b/client/app/scripts/components/sparkline.js @@ -81,14 +81,10 @@ export default class Sparkline extends React.Component { return (
- - + +
); @@ -97,7 +93,7 @@ export default class Sparkline extends React.Component { } Sparkline.propTypes = { - data: React.PropTypes.arrayOf(React.PropTypes.object).isRequired + data: React.PropTypes.array.isRequired }; Sparkline.defaultProps = { diff --git a/client/app/scripts/components/terminal.js b/client/app/scripts/components/terminal.js index 6cd28052e3..af2ce51da2 100644 --- a/client/app/scripts/components/terminal.js +++ b/client/app/scripts/components/terminal.js @@ -1,15 +1,16 @@ /* eslint no-return-assign: "off", react/jsx-no-bind: "off" */ import debug from 'debug'; import React from 'react'; +import ReactDOM from 'react-dom'; import { connect } from 'react-redux'; import classNames from 'classnames'; import { debounce } from 'lodash'; -import Term from 'xterm'; import { clickCloseTerminal } from '../actions/app-actions'; import { getNeutralColor } from '../utils/color-utils'; import { setDocumentTitle } from '../utils/title-utils'; import { getPipeStatus, basePath, doResizeTty } from '../utils/web-api-utils'; +import Term from 'xterm'; const wsProto = location.protocol === 'https:' ? 'wss' : 'ws'; const wsUrl = `${wsProto}://${location.host}${basePath(location.pathname)}`; @@ -72,7 +73,7 @@ function openNewWindow(url, bcr, minWidth = 200) { }; const windowOptionsString = Object.keys(windowOptions) - .map(k => `${k}=${windowOptions[k]}`) + .map((k) => `${k}=${windowOptions[k]}`) .join(','); window.open(url, '', windowOptionsString); @@ -96,8 +97,6 @@ class Terminal extends React.Component { this.handleCloseClick = this.handleCloseClick.bind(this); this.handlePopoutTerminal = this.handlePopoutTerminal.bind(this); - this.saveInnerFlexRef = this.saveInnerFlexRef.bind(this); - this.saveNodeRef = this.saveNodeRef.bind(this); this.handleResize = this.handleResize.bind(this); this.handleResizeDebounced = debounce(this.handleResize, 500); } @@ -124,7 +123,7 @@ class Terminal extends React.Component { } this.socket = null; const wereConnected = this.state.connected; - if (this.isComponentMounted) { + if (this._isMounted) { // Calling setState on an unmounted component will throw a warning. // `connected` will get set to false by `componentWillUnmount`. this.setState({connected: false}); @@ -177,13 +176,14 @@ class Terminal extends React.Component { } componentDidMount() { - this.isComponentMounted = true; + this._isMounted = true; if (this.props.connect) { this.mountTerminal(); } } mountTerminal() { + const component = this; this.term = new Term({ cols: this.state.cols, rows: this.state.rows, @@ -192,7 +192,8 @@ class Terminal extends React.Component { scrollback: 10000, }); - this.term.open(this.innerFlex); + const innerNode = ReactDOM.findDOMNode(component.innerFlex); + this.term.open(innerNode); this.term.on('data', (data) => { this.scrollToBottom(); if (this.socket) { @@ -216,7 +217,7 @@ class Terminal extends React.Component { } componentWillUnmount() { - this.isComponentMounted = false; + this._isMounted = false; this.setState({connected: false}); log('cwu terminal'); @@ -262,15 +263,16 @@ class Terminal extends React.Component { const paramString = JSON.stringify(this.props); this.props.dispatch(clickCloseTerminal(this.getPipeId())); - const bcr = this.node.getBoundingClientRect(); - const minWidth = (this.state.characterWidth * 80) + (8 * 2); + const bcr = ReactDOM.findDOMNode(this).getBoundingClientRect(); + const minWidth = this.state.characterWidth * 80 + (8 * 2); openNewWindow(`terminal.html#!/state/${paramString}`, bcr, minWidth); } handleResize() { + const innerNode = ReactDOM.findDOMNode(this.innerFlex); // scrollbar === 16px - const width = this.innerFlex.clientWidth - (2 * 8) - 16; - const height = this.innerFlex.clientHeight - (2 * 8); + const width = innerNode.clientWidth - (2 * 8) - 16; + const height = innerNode.clientHeight - (2 * 8); const cols = Math.floor(width / this.state.characterWidth); const rows = Math.floor(height / this.state.characterHeight); @@ -311,8 +313,7 @@ class Terminal extends React.Component { onClick={this.handlePopoutTerminal}> Pop out -
{this.getTitle()} @@ -356,14 +357,6 @@ class Terminal extends React.Component { ); } - saveNodeRef(ref) { - this.node = ref; - } - - saveInnerFlexRef(ref) { - this.innerFlex = ref; - } - render() { const innerFlexStyle = { opacity: this.state.connected ? 1 : 0.8, @@ -377,10 +370,13 @@ class Terminal extends React.Component { }); return ( -
+
{this.isEmbedded() && this.getTerminalHeader()} -
-
+
this.innerFlex = ref} + className={innerClassName} + style={innerFlexStyle} > +
this.inner = ref} />
{this.getTerminalStatusBar()}
diff --git a/client/app/scripts/components/topologies.js b/client/app/scripts/components/topologies.js index df180d3199..8ffcc77aa6 100644 --- a/client/app/scripts/components/topologies.js +++ b/client/app/scripts/components/topologies.js @@ -4,18 +4,6 @@ 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) { @@ -33,15 +21,14 @@ 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 = basicTopologyInfo(subTopology, searchMatchCount); + const title = this.renderTitle(subTopology, searchMatchCount); const className = classnames('topologies-sub-item', { 'topologies-sub-item-active': isActive, 'topologies-sub-item-matched': searchMatchCount }); return ( -
{subTopology.get('name')} @@ -50,6 +37,15 @@ 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')); @@ -59,7 +55,7 @@ class Topologies extends React.Component { 'topologies-item-main-matched': searchMatchCount }); const topologyId = topology.get('id'); - const title = basicTopologyInfo(topology, searchMatchCount); + const title = this.renderTitle(topology, searchMatchCount); return (
diff --git a/client/app/scripts/contrast-main.js b/client/app/scripts/contrast-main.js index f4d01b5ea9..707094e182 100644 --- a/client/app/scripts/contrast-main.js +++ b/client/app/scripts/contrast-main.js @@ -1,11 +1,11 @@ +require('font-awesome-webpack'); +require('../styles/contrast.less'); +require('../images/favicon.ico'); + import 'babel-polyfill'; -import 'font-awesome-webpack'; import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; - -import '../styles/main.less'; -import '../images/favicon.ico'; import configureStore from './stores/configureStore'; const store = configureStore(); diff --git a/client/app/scripts/hoc/metric-feeder.js b/client/app/scripts/hoc/metric-feeder.js index 1180cf9d44..6f09d0b2af 100644 --- a/client/app/scripts/hoc/metric-feeder.js +++ b/client/app/scripts/hoc/metric-feeder.js @@ -149,13 +149,8 @@ export default ComposedComponent => class extends React.Component { .filter(dateFilter); const lastValue = samples.length > 0 ? samples[samples.length - 1].value : null; - const slidingWindow = { - first: movingFirstDate, - last: movingLastDate, - value: lastValue, - samples, - max - }; + const slidingWindow = {first: movingFirstDate, + last: movingLastDate, max, samples, value: lastValue}; return ; } diff --git a/client/app/scripts/main.dev.js b/client/app/scripts/main.dev.js index bba8e65976..96d5353ea3 100644 --- a/client/app/scripts/main.dev.js +++ b/client/app/scripts/main.dev.js @@ -1,17 +1,18 @@ +require('font-awesome-webpack'); +require('../styles/main.less'); +require('../images/favicon.ico'); + import 'babel-polyfill'; -import 'font-awesome-webpack'; import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; -import Immutable from 'immutable'; -import installDevTools from 'immutable-devtools'; - -import '../styles/main.less'; -import '../images/favicon.ico'; import configureStore from './stores/configureStore.dev'; -import DevTools from './components/dev-tools'; +import DevTools from './components/dev-tools'; +import Immutable from 'immutable'; +import installDevTools from 'immutable-devtools'; installDevTools(Immutable); + const store = configureStore(); function renderApp() { diff --git a/client/app/scripts/main.js b/client/app/scripts/main.js index f4d01b5ea9..a7c74057fd 100644 --- a/client/app/scripts/main.js +++ b/client/app/scripts/main.js @@ -1,11 +1,11 @@ +require('font-awesome-webpack'); +require('../styles/main.less'); +require('../images/favicon.ico'); + import 'babel-polyfill'; -import 'font-awesome-webpack'; import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; - -import '../styles/main.less'; -import '../images/favicon.ico'; import configureStore from './stores/configureStore'; const store = configureStore(); diff --git a/client/app/scripts/reducers/root.js b/client/app/scripts/reducers/root.js index 051f7b886b..28a3fb36fa 100644 --- a/client/app/scripts/reducers/root.js +++ b/client/app/scripts/reducers/root.js @@ -95,7 +95,7 @@ function setTopology(state, topologyId) { } function setDefaultTopologyOptions(state, topologyList) { - topologyList.forEach((topology) => { + topologyList.forEach(topology => { let defaultOptions = makeOrderedMap(); if (topology.has('options') && topology.get('options')) { topology.get('options').forEach((option) => { @@ -230,7 +230,7 @@ export function rootReducer(state = initialState, action) { } case ActionTypes.CLICK_PAUSE_UPDATE: { - return state.set('updatePausedAt', new Date()); + return state.set('updatePausedAt', new Date); } case ActionTypes.CLICK_RELATIVE: { @@ -371,13 +371,13 @@ export function rootReducer(state = initialState, action) { case ActionTypes.ENTER_EDGE: { // highlight adjacent nodes - state = state.update('highlightedNodeIds', (highlightedNodeIds) => { + state = state.update('highlightedNodeIds', highlightedNodeIds => { highlightedNodeIds = highlightedNodeIds.clear(); return highlightedNodeIds.union(action.edgeId.split(EDGE_ID_SEPARATOR)); }); // highlight edge - state = state.update('highlightedEdgeIds', (highlightedEdgeIds) => { + state = state.update('highlightedEdgeIds', highlightedEdgeIds => { highlightedEdgeIds = highlightedEdgeIds.clear(); return highlightedEdgeIds.add(action.edgeId); }); @@ -392,18 +392,18 @@ export function rootReducer(state = initialState, action) { state = state.set('mouseOverNodeId', nodeId); // highlight adjacent nodes - state = state.update('highlightedNodeIds', (highlightedNodeIds) => { + state = state.update('highlightedNodeIds', highlightedNodeIds => { highlightedNodeIds = highlightedNodeIds.clear(); highlightedNodeIds = highlightedNodeIds.add(nodeId); return highlightedNodeIds.union(adjacentNodes); }); // highlight edge - state = state.update('highlightedEdgeIds', (highlightedEdgeIds) => { + state = state.update('highlightedEdgeIds', highlightedEdgeIds => { highlightedEdgeIds = highlightedEdgeIds.clear(); if (adjacentNodes.size > 0) { // all neighbour combinations because we dont know which direction exists - highlightedEdgeIds = highlightedEdgeIds.union(adjacentNodes.flatMap(adjacentId => [ + highlightedEdgeIds = highlightedEdgeIds.union(adjacentNodes.flatMap((adjacentId) => [ [adjacentId, nodeId].join(EDGE_ID_SEPARATOR), [nodeId, adjacentId].join(EDGE_ID_SEPARATOR) ])); @@ -491,7 +491,7 @@ export function rootReducer(state = initialState, action) { // disregard if node is not selected anymore if (state.hasIn(['nodeDetails', action.details.id])) { - state = state.updateIn(['nodeDetails', action.details.id], (obj) => { + state = state.updateIn(['nodeDetails', action.details.id], obj => { const result = Object.assign({}, obj); result.notFound = false; result.details = action.details; @@ -554,7 +554,7 @@ export function rootReducer(state = initialState, action) { // TODO move this setting of networks as toplevel node field to backend, // to not rely on field IDs here. should be determined by topology implementer - state = state.update('nodes', nodes => nodes.map((node) => { + state = state.update('nodes', nodes => nodes.map(node => { if (node.has('metadata')) { const networks = node.get('metadata') .find(field => field.get('id') === 'docker_container_networks'); @@ -605,7 +605,7 @@ export function rootReducer(state = initialState, action) { case ActionTypes.RECEIVE_NOT_FOUND: { if (state.hasIn(['nodeDetails', action.nodeId])) { - state = state.updateIn(['nodeDetails', action.nodeId], (obj) => { + state = state.updateIn(['nodeDetails', action.nodeId], obj => { const result = Object.assign({}, obj); result.notFound = true; return result; diff --git a/client/app/scripts/selectors/chartSelectors.js b/client/app/scripts/selectors/chartSelectors.js index 223a4d30a6..3183223722 100644 --- a/client/app/scripts/selectors/chartSelectors.js +++ b/client/app/scripts/selectors/chartSelectors.js @@ -1,5 +1,4 @@ import debug from 'debug'; -import { identity } from 'lodash'; import { createSelector, createSelectorCreator, defaultMemoize } from 'reselect'; import { Map as makeMap, is, Set } from 'immutable'; @@ -45,8 +44,9 @@ function mergeDeepKeyIntersection(mapA, mapB) { // "their results", so use the result of the wrapped selector as the argument to another selector // here to memoize it and get what we want. // -const createDeepEqualSelector = createSelectorCreator(defaultMemoize, is); -const returnPreviousRefIfEqual = selector => createDeepEqualSelector(selector, identity); +const _createDeepEqualSelector = createSelectorCreator(defaultMemoize, is); +const _identity = v => v; +const returnPreviousRefIfEqual = (selector) => _createDeepEqualSelector(selector, _identity); // @@ -60,7 +60,7 @@ const allNodesSelector = state => state.get('nodes'); export const nodesSelector = returnPreviousRefIfEqual( createSelector( allNodesSelector, - allNodes => allNodes.filter(node => !node.get('filtered')) + (allNodes) => allNodes.filter(node => !node.get('filtered')) ) ); @@ -71,7 +71,7 @@ export const adjacentNodesSelector = returnPreviousRefIfEqual(getAdjacentNodes); export const nodeAdjacenciesSelector = returnPreviousRefIfEqual( createSelector( nodesSelector, - nodes => nodes.map(n => makeMap({ + (nodes) => nodes.map(n => makeMap({ id: n.get('id'), adjacency: n.get('adjacency'), })) @@ -81,7 +81,7 @@ export const nodeAdjacenciesSelector = returnPreviousRefIfEqual( export const dataNodesSelector = createSelector( nodesSelector, - nodes => nodes.map((node, id) => makeMap({ + (nodes) => nodes.map((node, id) => makeMap({ id, label: node.get('label'), pseudo: node.get('pseudo'), diff --git a/client/app/scripts/terminal-main.js b/client/app/scripts/terminal-main.js index 171f1b8591..149c801287 100644 --- a/client/app/scripts/terminal-main.js +++ b/client/app/scripts/terminal-main.js @@ -1,10 +1,10 @@ +require('../styles/main.less'); +require('../images/favicon.ico'); + import 'babel-polyfill'; import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; - -import '../styles/main.less'; -import '../images/favicon.ico'; import configureStore from './stores/configureStore'; const store = configureStore(); diff --git a/client/app/scripts/utils/__tests__/array-utils-test.js b/client/app/scripts/utils/__tests__/array-utils-test.js index 49f0eb4bdf..e3aae076d6 100644 --- a/client/app/scripts/utils/__tests__/array-utils-test.js +++ b/client/app/scripts/utils/__tests__/array-utils-test.js @@ -1,4 +1,4 @@ -import { range } from 'lodash'; +import _ from 'lodash'; describe('ArrayUtils', () => { const ArrayUtils = require('../array-utils'); @@ -7,42 +7,34 @@ describe('ArrayUtils', () => { const f = ArrayUtils.uniformSelect; it('it should select the array elements uniformly, including the endpoints', () => { - { - const arr = ['x', 'y']; - expect(f(arr, 3)).toEqual(['x', 'y']); - expect(f(arr, 2)).toEqual(['x', 'y']); - } + expect(f(['x', 'y'], 3)).toEqual(['x', 'y']); + expect(f(['x', 'y'], 2)).toEqual(['x', 'y']); - { - const arr = ['A', 'B', 'C', 'D', 'E']; - expect(f(arr, 6)).toEqual(['A', 'B', 'C', 'D', 'E']); - expect(f(arr, 5)).toEqual(['A', 'B', 'C', 'D', 'E']); - expect(f(arr, 4)).toEqual(['A', 'B', 'D', 'E']); - expect(f(arr, 3)).toEqual(['A', 'C', 'E']); - expect(f(arr, 2)).toEqual(['A', 'E']); - } + expect(f(['A', 'B', 'C', 'D', 'E'], 6)).toEqual(['A', 'B', 'C', 'D', 'E']); + expect(f(['A', 'B', 'C', 'D', 'E'], 5)).toEqual(['A', 'B', 'C', 'D', 'E']); + expect(f(['A', 'B', 'C', 'D', 'E'], 4)).toEqual(['A', 'B', 'D', 'E']); + expect(f(['A', 'B', 'C', 'D', 'E'], 3)).toEqual(['A', 'C', 'E']); + expect(f(['A', 'B', 'C', 'D', 'E'], 2)).toEqual(['A', 'E']); - { - const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; - expect(f(arr, 12)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - expect(f(arr, 11)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); - expect(f(arr, 10)).toEqual([1, 2, 3, 4, 5, 7, 8, 9, 10, 11]); - expect(f(arr, 9)).toEqual([1, 2, 3, 5, 6, 7, 9, 10, 11]); - expect(f(arr, 8)).toEqual([1, 2, 4, 5, 7, 8, 10, 11]); - expect(f(arr, 7)).toEqual([1, 2, 4, 6, 8, 10, 11]); - expect(f(arr, 6)).toEqual([1, 3, 5, 7, 9, 11]); - expect(f(arr, 5)).toEqual([1, 3, 6, 9, 11]); - expect(f(arr, 4)).toEqual([1, 4, 8, 11]); - expect(f(arr, 3)).toEqual([1, 6, 11]); - expect(f(arr, 2)).toEqual([1, 11]); - } + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 12)).toEqual( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + ); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 11)).toEqual( + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 10)).toEqual([1, 2, 3, 4, 5, 7, 8, 9, 10, 11] + ); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 9)).toEqual([1, 2, 3, 5, 6, 7, 9, 10, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 8)).toEqual([1, 2, 4, 5, 7, 8, 10, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 7)).toEqual([1, 2, 4, 6, 8, 10, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 6)).toEqual([1, 3, 5, 7, 9, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 5)).toEqual([1, 3, 6, 9, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 4)).toEqual([1, 4, 8, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 3)).toEqual([1, 6, 11]); + expect(f([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 2)).toEqual([1, 11]); - { - const arr = range(1, 10001); - expect(f(arr, 4)).toEqual([1, 3334, 6667, 10000]); - expect(f(arr, 3)).toEqual([1, 5000, 10000]); - expect(f(arr, 2)).toEqual([1, 10000]); - } + expect(f(_.range(1, 10001), 4)).toEqual([1, 3334, 6667, 10000]); + expect(f(_.range(1, 10001), 3)).toEqual([1, 5000, 10000]); + expect(f(_.range(1, 10001), 2)).toEqual([1, 10000]); }); }); }); diff --git a/client/app/scripts/utils/__tests__/search-utils-test.js b/client/app/scripts/utils/__tests__/search-utils-test.js index 301b431a2c..7fe24cbf96 100644 --- a/client/app/scripts/utils/__tests__/search-utils-test.js +++ b/client/app/scripts/utils/__tests__/search-utils-test.js @@ -154,7 +154,7 @@ describe('SearchUtils', () => { const fun = SearchUtils.makeRegExp; it('should make a regexp from any string', () => { - expect(fun().source).toEqual((new RegExp()).source); + expect(fun().source).toEqual((new RegExp).source); expect(fun('que').source).toEqual((new RegExp('que')).source); // invalid string expect(fun('que[').source).toEqual((new RegExp('que\\[')).source); diff --git a/client/app/scripts/utils/array-utils.js b/client/app/scripts/utils/array-utils.js index c078913984..52cc098e0a 100644 --- a/client/app/scripts/utils/array-utils.js +++ b/client/app/scripts/utils/array-utils.js @@ -1,11 +1,11 @@ -import { range } from 'lodash'; +import _ from 'lodash'; export function uniformSelect(array, size) { if (size > array.length) { return array; } - return range(size).map(index => - array[parseInt(index * (array.length / (size - (1 - 1e-9))), 10)] + return _.range(size).map(index => + array[parseInt(index * array.length / (size - 1 + 1e-9), 10)] ); } diff --git a/client/app/scripts/utils/color-utils.js b/client/app/scripts/utils/color-utils.js index 676d7af04d..c728c1088c 100644 --- a/client/app/scripts/utils/color-utils.js +++ b/client/app/scripts/utils/color-utils.js @@ -17,7 +17,7 @@ const letterRange = endLetterRange - startLetterRange; export function text2degree(text) { const input = text.substr(0, 2).toUpperCase(); let num = 0; - for (let i = 0; i < input.length; i += 1) { + for (let i = 0; i < input.length; i++) { const charCode = Math.max(Math.min(input[i].charCodeAt(), endLetterRange), startLetterRange); num += Math.pow(letterRange, input.length - i - 1) * (charCode - startLetterRange); } diff --git a/client/app/scripts/utils/data-generator-utils.js b/client/app/scripts/utils/data-generator-utils.js index f71b358fad..2f9f31f362 100644 --- a/client/app/scripts/utils/data-generator-utils.js +++ b/client/app/scripts/utils/data-generator-utils.js @@ -1,4 +1,4 @@ -import { zipObject } from 'lodash'; +import _ from 'lodash'; import { scaleLinear } from 'd3-scale'; import { extent } from 'd3-array'; @@ -104,9 +104,10 @@ function mergeMetrics(node) { return node; } return Object.assign({}, node, { - metrics: (metrics[node.shape] || []) + metrics: _(metrics[node.shape]) .map((fn, name) => [name, fn(node)]) .fromPairs() + .value() }); } @@ -120,7 +121,7 @@ function handleAdd(nodes) { function handleUpdated(updatedNodes, prevNodes) { - const modifiedNodesIndex = zipObject((updatedNodes || []).map(n => [n.id, n])); + const modifiedNodesIndex = _.zipObject((updatedNodes || []).map(n => [n.id, n])); return prevNodes.toIndexedSeq().toJS().map(n => ( Object.assign({}, mergeMetrics(n), modifiedNodesIndex[n.id]) )); diff --git a/client/app/scripts/utils/delayed-show.js b/client/app/scripts/utils/delayed-show.js index 5693219de0..fb4df720ef 100644 --- a/client/app/scripts/utils/delayed-show.js +++ b/client/app/scripts/utils/delayed-show.js @@ -1,7 +1,7 @@ import React from 'react'; -export default class DelayedShow extends React.Component { +export class DelayedShow extends React.Component { constructor(props, context) { super(props, context); this.state = { diff --git a/client/app/scripts/utils/file-utils.js b/client/app/scripts/utils/file-utils.js index 002b14f2d9..33af43d55f 100644 --- a/client/app/scripts/utils/file-utils.js +++ b/client/app/scripts/utils/file-utils.js @@ -18,7 +18,7 @@ function setInlineStyles(svg, target, emptySvgDeclarationComputed) { function explicitlySetStyle(element, targetEl) { const cSSStyleDeclarationComputed = getComputedStyle(element); let computedStyleStr = ''; - each(cSSStyleDeclarationComputed, (key) => { + each(cSSStyleDeclarationComputed, key => { const value = cSSStyleDeclarationComputed.getPropertyValue(key); if (value !== emptySvgDeclarationComputed.getPropertyValue(key) && !cssSkipValues[value]) { computedStyleStr += `${key}:${value};`; @@ -55,7 +55,8 @@ function setInlineStyles(svg, target, emptySvgDeclarationComputed) { // hardcode computed css styles inside svg const allElements = traverse(svg); const allTargetElements = traverse(target); - for (let i = allElements.length - 1; i >= 0; i -= 1) { + let i = allElements.length; + while (i--) { explicitlySetStyle(allElements[i], allTargetElements[i]); } @@ -73,11 +74,11 @@ function download(source, name) { if (name) { filename = name; } else if (window.document.title) { - filename = `${window.document.title.replace(/[^a-z0-9]/gi, '-').toLowerCase()}-${(+new Date())}`; + filename = `${window.document.title.replace(/[^a-z0-9]/gi, '-').toLowerCase()}-${(+new Date)}`; } const url = window.URL.createObjectURL(new Blob(source, - { type: 'text/xml' } + {type: 'text\/xml'} )); const a = document.createElement('a'); diff --git a/client/app/scripts/utils/metric-utils.js b/client/app/scripts/utils/metric-utils.js index 5acf724ae9..1d481f0fd7 100644 --- a/client/app/scripts/utils/metric-utils.js +++ b/client/app/scripts/utils/metric-utils.js @@ -1,12 +1,12 @@ import { includes } from 'lodash'; import { scaleLog } from 'd3-scale'; -import React from 'react'; - import { formatMetricSvg } from './string-utils'; import { colors } from './color-utils'; +import React from 'react'; + export function getClipPathDefinition(clipId, size, height, - x = -size * 0.5, y = (size * 0.5) - height) { + x = -size * 0.5, y = size * 0.5 - height) { return ( @@ -44,7 +44,7 @@ export function getMetricValue(metric, size) { let displayedValue = Number(value).toFixed(1); if (displayedValue > 0 && (!max || displayedValue < max)) { const baseline = 0.1; - displayedValue = (valuePercentage * (1 - (baseline * 2))) + baseline; + displayedValue = valuePercentage * (1 - baseline * 2) + baseline; } else if (displayedValue >= m.max && displayedValue > 0) { displayedValue = 1; } diff --git a/client/app/scripts/utils/network-view-utils.js b/client/app/scripts/utils/network-view-utils.js index 343cc41123..8565fc8e6f 100644 --- a/client/app/scripts/utils/network-view-utils.js +++ b/client/app/scripts/utils/network-view-utils.js @@ -2,7 +2,7 @@ import { fromJS, List as makeList } from 'immutable'; export function getNetworkNodes(nodes) { const networks = {}; - nodes.forEach(node => (node.get('networks') || makeList()).forEach((n) => { + nodes.forEach(node => (node.get('networks') || makeList()).forEach(n => { const networkId = n.get('id'); networks[networkId] = (networks[networkId] || []).concat([node.get('id')]); })); diff --git a/client/app/scripts/utils/search-utils.js b/client/app/scripts/utils/search-utils.js index b8f74d0471..a4c192a902 100644 --- a/client/app/scripts/utils/search-utils.js +++ b/client/app/scripts/utils/search-utils.js @@ -132,7 +132,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) { // metadata if (node.get('metadata')) { - node.get('metadata').forEach((field) => { + node.get('metadata').forEach(field => { const keyPath = [nodeId, 'metadata', field.get('id')]; nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'), query, prefix, field.get('label'), field.get('truncate')); @@ -141,7 +141,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) { // parents and relatives if (node.get('parents')) { - node.get('parents').forEach((parent) => { + node.get('parents').forEach(parent => { const keyPath = [nodeId, 'parents', parent.get('id')]; nodeMatches = findNodeMatch(nodeMatches, keyPath, parent.get('label'), query, prefix, parent.get('topologyId')); @@ -153,7 +153,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) { if (tables) { tables.forEach((table) => { if (table.get('rows')) { - table.get('rows').forEach((field) => { + table.get('rows').forEach(field => { const keyPath = [nodeId, 'tables', field.get('id')]; nodeMatches = findNodeMatch(nodeMatches, keyPath, field.get('value'), query, prefix, field.get('label')); @@ -164,7 +164,7 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) { } else if (metric) { const metrics = node.get('metrics'); if (metrics) { - metrics.forEach((field) => { + metrics.forEach(field => { const keyPath = [nodeId, 'metrics', field.get('id')]; nodeMatches = findNodeMatchMetric(nodeMatches, keyPath, field.get('value'), field.get('label'), metric, comp, value); @@ -291,7 +291,7 @@ export function applyPinnedSearches(state) { const pinnedSearches = state.get('pinnedSearches'); if (pinnedSearches.size > 0) { - state.get('pinnedSearches').forEach((query) => { + state.get('pinnedSearches').forEach(query => { const parsed = parseQuery(query); if (parsed) { const nodeMatches = searchTopology(state.get('nodes'), parsed); diff --git a/client/app/scripts/utils/storage-utils.js b/client/app/scripts/utils/storage-utils.js index 4c6d3ee079..1556fa4038 100644 --- a/client/app/scripts/utils/storage-utils.js +++ b/client/app/scripts/utils/storage-utils.js @@ -3,7 +3,7 @@ import debug from 'debug'; const log = debug('scope:storage-utils'); // localStorage detection -const storage = (typeof Storage) !== 'undefined' ? window.localStorage : null; +const storage = typeof(Storage) !== 'undefined' ? window.localStorage : null; export function storageGet(key, defaultValue) { if (storage && storage.getItem(key) !== undefined) { diff --git a/client/app/scripts/utils/topology-utils.js b/client/app/scripts/utils/topology-utils.js index 3ff56e1eee..5a6709bcb8 100644 --- a/client/app/scripts/utils/topology-utils.js +++ b/client/app/scripts/utils/topology-utils.js @@ -19,7 +19,7 @@ export function getDefaultTopology(topologies) { .flatMap(t => makeList([t]).concat(t.get('sub_topologies', makeList()))); return flatTopologies - .sortBy((t) => { + .sortBy(t => { const index = TOPOLOGY_DISPLAY_PRIORITY.indexOf(t.get('id')); return index === -1 ? Infinity : index; }) @@ -53,7 +53,7 @@ export function buildTopologyCacheId(topologyId, topologyOptions) { export function findTopologyById(subTree, topologyId) { let foundTopology; - subTree.forEach((topology) => { + subTree.forEach(topology => { if (endsWith(topology.get('url'), topologyId)) { foundTopology = topology; } @@ -66,7 +66,7 @@ export function findTopologyById(subTree, topologyId) { } export function updateNodeDegrees(nodes, edges) { - return nodes.map((node) => { + return nodes.map(node => { const nodeId = node.get('id'); const degree = edges.count(edge => edge.get('source') === nodeId || edge.get('target') === nodeId); @@ -76,7 +76,7 @@ export function updateNodeDegrees(nodes, edges) { /* set topology.id and parentId for sub-topologies in place */ export function updateTopologyIds(topologies, parentId) { - return topologies.map((topology) => { + return topologies.map(topology => { const result = Object.assign({}, topology); result.id = topology.url.split('/').pop(); if (parentId) { @@ -90,7 +90,7 @@ export function updateTopologyIds(topologies, parentId) { } export function addTopologyFullname(topologies) { - return topologies.map((t) => { + return topologies.map(t => { if (!t.sub_topologies) { return Object.assign({}, t, {fullName: t.name}); } @@ -108,10 +108,10 @@ export function addTopologyFullname(topologies) { export function setTopologyUrlsById(topologyUrlsById, topologies) { let urlMap = topologyUrlsById; if (topologies) { - topologies.forEach((topology) => { + topologies.forEach(topology => { urlMap = urlMap.set(topology.id, topology.url); if (topology.sub_topologies) { - topology.sub_topologies.forEach((subTopology) => { + topology.sub_topologies.forEach(subTopology => { urlMap = urlMap.set(subTopology.id, subTopology.url); }); } diff --git a/client/app/scripts/utils/update-buffer-utils.js b/client/app/scripts/utils/update-buffer-utils.js index cd65da5516..341618364c 100644 --- a/client/app/scripts/utils/update-buffer-utils.js +++ b/client/app/scripts/utils/update-buffer-utils.js @@ -49,7 +49,7 @@ function consolidateBuffer() { size(toUpdate), 'remove', size(toRemove)); // check if an added node in first was updated in second -> add second update - toAdd = map(toAdd, (node) => { + toAdd = map(toAdd, node => { const updateNode = find(second.update, {id: node.id}); if (updateNode) { toUpdate = reject(toUpdate, {id: node.id}); @@ -62,7 +62,7 @@ function consolidateBuffer() { // no action needed, successive updates are fine // check if an added node in first was removed in second -> dont add, dont remove - each(first.add, (node) => { + each(first.add, node => { const removedNode = find(second.remove, {id: node.id}); if (removedNode) { toAdd = reject(toAdd, {id: node.id}); @@ -71,7 +71,7 @@ function consolidateBuffer() { }); // check if an updated node in first was removed in second -> remove - each(first.update, (node) => { + each(first.update, node => { const removedNode = find(second.remove, {id: node.id}); if (removedNode) { toUpdate = reject(toUpdate, {id: node.id}); diff --git a/client/package.json b/client/package.json index dbe360ea4a..77b9b7d2f1 100644 --- a/client/package.json +++ b/client/package.json @@ -8,14 +8,7 @@ "dependencies": { "babel-polyfill": "6.16.0", "classnames": "2.2.5", - "d3-array": "1.0.2", - "d3-color": "1.0.2", - "d3-format": "1.0.2", - "d3-scale": "1.0.4", - "d3-selection": "1.0.3", - "d3-shape": "1.0.4", - "d3-time-format": "2.0.3", - "d3-zoom": "1.1.1", + "d3": "4.4.0", "dagre": "0.7.4", "debug": "2.3.3", "filesize": "3.3.0", @@ -45,7 +38,7 @@ "devDependencies": { "autoprefixer": "6.5.3", "babel-core": "6.18.2", - "babel-eslint": "7.1.1", + "babel-eslint": "5.0.0", "babel-jest": "17.0.2", "babel-loader": "6.2.8", "babel-plugin-lodash": "3.2.10", @@ -53,12 +46,11 @@ "babel-preset-react": "6.16.0", "clean-webpack-plugin": "0.1.14", "css-loader": "0.26.1", - "eslint": "3.11.1", - "eslint-config-airbnb": "13.0.0", - "eslint-loader": "1.6.1", - "eslint-plugin-import": "2.2.0", - "eslint-plugin-jsx-a11y": "2.2.3", - "eslint-plugin-react": "6.8.0", + "eslint": "2.4.0", + "eslint-config-airbnb": "6.1.0", + "eslint-loader": "1.3.0", + "eslint-plugin-jasmine": "1.6.0", + "eslint-plugin-react": "4.2.2", "extract-text-webpack-plugin": "1.0.1", "file-loader": "0.9.0", "html-webpack-plugin": "2.24.1",