1
0
mirror of https://github.com/yldio/copilot.git synced 2024-12-01 07:30:07 +02:00

Add primary and secondaryto topology and refactor

This commit is contained in:
JUDIT GRESKOVITS 2017-03-02 16:40:07 +00:00 committed by Sérgio Ramos
parent 62bd05ffb8
commit e71b1e6fde
20 changed files with 577 additions and 425 deletions

View File

@ -0,0 +1,67 @@
const Lengths = {
paddingLeft: 18,
nodeWidth: 180
};
const Sizes = {
buttonSize: {
width: 40,
height: 48
},
contentSize: {
width: Lengths.nodeWidth,
height: 101 // This is the height w/o info comp
},
nodeSize: {
width: Lengths.nodeWidth,
height: 156
},
nodeSizeWithChildren: {
width: Lengths.nodeWidth,
height: 276
}
};
const Points = {
buttonPosition: {
x: Lengths.nodeWidth - Sizes.buttonSize.width,
y: 0
},
contentPosition: {
x: 0,
y: Sizes.buttonSize.height
},
infoPosition: {
x: Lengths.paddingLeft,
y: 11
},
metricsPosition: {
x: Lengths.paddingLeft,
y: 41
},
subtitlePosition: {
x: Lengths.paddingLeft,
y: 23
}
};
const Rects = {
buttonRect: {
...Sizes.buttonSize,
...Points.buttonPosition
},
contentRect: {
...Sizes.contentSize,
...Points.contentPosition
}
};
const Constants = {
...Lengths,
...Sizes,
...Points,
...Rects
};
export default Constants;

View File

@ -1,25 +1,11 @@
import styled from 'styled-components';
import { Baseline } from '../../shared/composers';
import { colors } from '../../shared/constants';
import PropTypes from './prop-types';
import { Baseline } from '../../../shared/composers';
import Constants from '../constants';
import React from 'react';
const StyledLine = styled.line`
stroke: ${colors.base.secondaryActive};
stroke-width: 1.5;
`;
const StyledCircle = styled.circle`
stroke: ${colors.base.secondaryActive};
fill: ${colors.base.secondary};
stroke-width: 1.5;
`;
const StyledArrow = styled.line`
stroke: ${colors.base.white};
stroke-width: 2;
stroke-linecap: round;
`;
import {
GraphLinkLine,
GraphLinkCircle,
GraphLinkArrow
} from './shapes';
const getAngleFromPoints = (source, target) => {
@ -81,8 +67,7 @@ const getPositions = (halfWidth, halfHeight, halfCorner=0) => ([{
const GraphLink = ({
data,
index,
nodeSize
index
}) => {
const {
@ -91,10 +76,11 @@ const GraphLink = ({
} = data;
// actually, this will need to be got dynamically, in case them things are different sizes
// yeah right, now you'll get to do exactly that
const {
width,
height
} = nodeSize;
} = Constants.nodeSize;
const halfWidth = width/2;
const halfHeight = height/2;
@ -109,7 +95,7 @@ const GraphLink = ({
return (
<g>
<StyledLine
<GraphLinkLine
x1={sourcePosition.x}
x2={targetPosition.x}
y1={sourcePosition.y}
@ -121,18 +107,18 @@ const GraphLink = ({
`translate(${targetPosition.x}, ${targetPosition.y}) rotate(${arrowAngle})`
}
>
<StyledCircle
<GraphLinkCircle
cx={0}
cy={0}
r={9}
/>
<StyledArrow
<GraphLinkArrow
x1={-1}
x2={2}
y1={-3}
y2={0}
/>
<StyledArrow
<GraphLinkArrow
x1={-1}
x2={2}
y1={3}
@ -145,8 +131,7 @@ const GraphLink = ({
GraphLink.propTypes = {
data: React.PropTypes.object.isRequired,
index: React.PropTypes.number.isRequired,
nodeSize: PropTypes.Size
index: React.PropTypes.number.isRequired
};
export default Baseline(

View File

@ -0,0 +1,19 @@
import styled from 'styled-components';
import { colors } from '../../../shared/constants';
export const GraphLinkLine = styled.line`
stroke: ${colors.base.secondaryActive};
stroke-width: 1.5;
`;
export const GraphLinkCircle = styled.circle`
stroke: ${colors.base.secondaryActive};
fill: ${colors.base.secondary};
stroke-width: 1.5;
`;
export const GraphLinkArrow = styled.line`
stroke: ${colors.base.white};
stroke-width: 2;
stroke-linecap: round;
`;

View File

@ -1,65 +0,0 @@
import React from 'react';
import { Baseline } from '../../shared/composers';
import { colors } from '../../shared/constants';
import styled from 'styled-components';
const StyledButton = styled.rect`
opacity: 0;
cursor: pointer;
`;
const StyledButtonCircle = styled.circle`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
`;
const GraphNodeButton = ({
connected,
buttonRect,
onButtonClick,
index
}) => {
const buttonCircleRadius = 2;
const buttonCircleSpacing = 2;
const buttonCircleY =
(buttonRect.height - buttonCircleRadius*4 - buttonCircleSpacing*2)/2;
const buttonCircles = [1,2,3].map((item, index) => (
<StyledButtonCircle
cx={buttonRect.width/2}
cy={buttonCircleY + (buttonCircleRadius*2 + buttonCircleSpacing)*index}
key={index}
r={2}
connected={connected}
/>
));
return (
<g transform={`translate(${buttonRect.x}, ${buttonRect.y})`}>
<StyledButton
height={buttonRect.height}
onClick={onButtonClick}
onKeyDown={onButtonClick}
width={buttonRect.width}
role='button'
tabIndex={100 + index}
/>
{buttonCircles}
</g>
);
};
GraphNodeButton.propTypes = {
buttonRect: React.PropTypes.shape({
x: React.PropTypes.number,
y: React.PropTypes.number,
width: React.PropTypes.number,
height: React.PropTypes.number
}).isRequired,
connected: React.PropTypes.bool,
index: React.PropTypes.number.isRequired,
onButtonClick: React.PropTypes.func.isRequired
};
export default Baseline(
GraphNodeButton
);

View File

@ -1,69 +0,0 @@
import React from 'react';
import { Baseline } from '../../shared/composers';
import { colors } from '../../shared/constants';
import DataCentresIcon from './icon-data-centers.svg';
import InstancesIcon from './icon-instances.svg';
import styled from 'styled-components';
const StyledText = styled.text`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
font-size: 12px;
opacity: 0.8;
`;
const StyledInstancesIcon = styled(InstancesIcon)`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
`;
const StyledDataCentresIcon = styled(DataCentresIcon)`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
`;
const GraphNodeInfo = ({
connected,
datacentres,
instances,
healthy,
infoPosition
}) => {
return (
<g transform={`translate(${infoPosition.x}, ${infoPosition.y})`}>
<g transform={'translate(0, 2)'}>
<StyledInstancesIcon connected={connected} />
</g>
<StyledText
x={23}
y={12}
connected={connected}
>
{`${datacentres} inst.`}
</StyledText>
<g transform={'translate(82, 0)'}>
<StyledDataCentresIcon connected={connected} />
</g>
<StyledText
x={96}
y={12}
connected={connected}
>
{`${instances} DCs`}
</StyledText>
</g>
);
};
GraphNodeInfo.propTypes = {
connected: React.PropTypes.bool,
datacentres: React.PropTypes.number,
healthy: React.PropTypes.bool,
infoPosition: React.PropTypes.shape({
x: React.PropTypes.number,
y: React.PropTypes.number
}),
instances: React.PropTypes.number
};
export default Baseline(
GraphNodeInfo
);

View File

@ -1,51 +0,0 @@
import React from 'react';
import { Baseline } from '../../shared/composers';
import { colors } from '../../shared/constants';
import styled from 'styled-components';
const StyledText = styled.text`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
font-size: 12px;
opacity: 0.8;
`;
const GraphNodeMetrics = ({
connected,
metrics,
metricsPosition
}) => {
const metricSpacing = 18;
const metricsText = metrics.map((metric, index) => (
<StyledText
key={index}
x={0}
y={12 + metricSpacing*index}
connected={connected}
>
{`${metric.name}: ${metric.value}`}
</StyledText>
));
return (
<g transform={`translate(${metricsPosition.x}, ${metricsPosition.y})`}>
{metricsText}
</g>
);
};
GraphNodeMetrics.propTypes = {
connected: React.PropTypes.bool,
metrics: React.PropTypes.arrayOf(React.PropTypes.shape({
name: React.PropTypes.string,
stat: React.PropTypes.string
})),
metricsPosition: React.PropTypes.shape({
x: React.PropTypes.number,
y: React.PropTypes.number
})
};
export default Baseline(
GraphNodeMetrics
);

View File

@ -1,188 +0,0 @@
import { Baseline } from '../../shared/composers';
import { colors } from '../../shared/constants';
import PropTypes from './prop-types';
// import HeartIcon from './icon-heart.svg';
import GraphNodeButton from './graph-node-button';
// import GraphNodeInfo from './graph-node-info';
import GraphNodeMetrics from './graph-node-metrics';
import styled from 'styled-components';
import React from 'react';
const StyledRect = styled.rect`
stroke: ${props => props.connected ?
colors.base.secondaryActive : colors.base.grey};
fill: ${props => props.connected ? colors.base.secondary : colors.base.white};
stroke-width: 1.5;
rx: 4;
ry: 4;
`;
const StyledShadowRect = styled.rect`
fill: ${props => props.connected ? colors.base.secondary : colors.base.grey};
opacity: 0.33;
rx: 4;
ry: 4;
`;
const StyledLine = styled.line`
stroke: ${props => props.connected ?
colors.base.secondaryActive : colors.base.grey};
stroke-width: 1.5;
`;
const StyledText = styled.text`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
font-size: 16px;
font-weight: 600;
`;
const HeartCircle = styled.circle`
fill: ${colors.base.green};
`;
const GraphNode = ({
connected,
data,
index,
size,
onDragStart
}) => {
const {
width,
height
} = size;
const halfWidth = width/2;
const halfHeight = height/2;
const lineY = 48;
const lineX = 140;
const buttonRect = {
x: lineX,
y: 0,
width: 40,
height: 48
};
const onButtonClick = (evt) => {
// console.log('Rect clicked!!!');
};
const paddingLeft = 18;
/*const infoPosition = {
x: paddingLeft,
y: 59
};*/
const metricsPosition = {
x: paddingLeft,
y: 89
};
// const titleBbBox = {x:100, y: 30 - halfHeight};
const onStart = (evt) => {
evt.preventDefault();
onDragStart(evt, data.id);
};
const position = connected ? {
x: data.x-halfWidth,
y: data.y-halfHeight
} : {
x: data.x,
y: data.y
};
const nodeRect = connected ? (
<StyledRect
x={0}
y={0}
width={width}
height={height}
onMouseDown={onStart}
onTouchStart={onStart}
connected={connected}
/>
) : (
<StyledRect
x={0}
y={0}
width={width}
height={height}
connected={connected}
/>
);
return (
<g transform={`translate(${position.x}, ${position.y})`}>
<StyledShadowRect
x={0}
y={3}
width={width}
height={height}
connected={connected}
/>
{nodeRect}
<StyledLine
x1={0}
y1={lineY}
x2={width}
y2={lineY}
connected={connected}
/>
<StyledLine
x1={lineX}
y1={0}
x2={lineX}
y2={lineY}
connected={connected}
/>
<StyledText
x={paddingLeft}
y={30}
connected={connected}
>
{data.name}
</StyledText>
<g transform={`translate(${115}, ${15})`}>
<HeartCircle
cx={9}
cy={9}
r={9}
/>
{/*<HeartIcon />*/}
</g>
<GraphNodeButton
buttonRect={buttonRect}
onButtonClick={onButtonClick}
index={index}
connected={connected}
/>
{/*<GraphNodeInfo
datacentres={data.datacentres}
instances={data.instances}
healthy
infoPosition={infoPosition}
connected={connected}
/>*/}
<GraphNodeMetrics
metrics={data.metrics}
metricsPosition={metricsPosition}
connected={connected}
/>
</g>
);
};
GraphNode.propTypes = {
connected: React.PropTypes.bool,
data: React.PropTypes.object.isRequired,
index: React.PropTypes.number.isRequired,
onDragStart: React.PropTypes.func,
size: PropTypes.Size
};
export default Baseline(
GraphNode
);

View File

@ -0,0 +1,63 @@
import React from 'react';
import { Baseline } from '../../../shared/composers';
import Constants from '../constants';
import { GraphLine, GraphButtonRect, GraphButtonCircle } from './shapes';
const GraphNodeButton = ({
connected,
onButtonClick,
index
}) => {
const {
x,
y,
width,
height
} = Constants.buttonRect;
const buttonCircleRadius = 2;
const buttonCircleSpacing = 2;
const buttonCircleY =
(height - buttonCircleRadius*4 - buttonCircleSpacing*2)/2;
const buttonCircles = [1,2,3].map((item, index) => (
<GraphButtonCircle
cx={width/2}
cy={buttonCircleY + (buttonCircleRadius*2 + buttonCircleSpacing)*index}
key={index}
r={2}
connected={connected}
/>
));
return (
<g transform={`translate(${x}, ${y})`}>
<GraphLine
x1={0}
y1={0}
x2={0}
y2={height}
connected={connected}
/>
<GraphButtonRect
height={height}
onClick={onButtonClick}
onKeyDown={onButtonClick}
width={width}
role='button'
tabIndex={100 + index}
/>
{buttonCircles}
</g>
);
};
GraphNodeButton.propTypes = {
connected: React.PropTypes.bool,
index: React.PropTypes.number.isRequired,
onButtonClick: React.PropTypes.func.isRequired
};
export default Baseline(
GraphNodeButton
);

View File

@ -0,0 +1,84 @@
import React from 'react';
import { Baseline } from '../../../shared/composers';
import Constants from '../constants';
import { GraphLine, GraphSubtitle } from './shapes';
import GraphNodeInfo from './info';
import GraphNodeMetrics from './metrics';
const GraphNodeContent = ({
connected,
child=false,
data,
index=0
}) => {
const {
x,
y,
width,
height
} = Constants.contentRect;
const contentY = y + height*index;
const offset = index ? 18 : - 6;
const nodeInfoPos = child ? {
x: Constants.infoPosition.x,
y: Constants.infoPosition.y + offset
} : Constants.infoPosition;
const nodeMetricsPos = child ? {
x: Constants.metricsPosition.x,
y: Constants.metricsPosition.y + offset
} : Constants.metricsPosition;
const nodeSubtitle = child ? (
<GraphSubtitle
{...Constants.subtitlePosition}
connected={connected}
>
{data.name}
</GraphSubtitle>
) : null;
const nodeInfo = !child || index ? (
<GraphNodeInfo
datacentres={data.datacentres}
instances={data.instances}
healthy
connected={connected}
pos={nodeInfoPos}
/>
) : null;
return (
<g transform={`translate(${x}, ${contentY})`}>
<GraphLine
x1={0}
y1={0}
x2={width}
y2={0}
connected={connected}
/>
{nodeSubtitle}
{nodeInfo}
<GraphNodeMetrics
metrics={data.metrics}
connected={connected}
pos={nodeMetricsPos}
/>
</g>
);
};
GraphNodeContent.propTypes = {
child: React.PropTypes.bool,
connected: React.PropTypes.bool,
data: React.PropTypes.object.isRequired,
index: React.PropTypes.number
};
export default Baseline(
GraphNodeContent
);

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,106 @@
import React from 'react';
import { Baseline } from '../../../shared/composers';
import Constants from '../constants';
import GraphNodeTitle from './title';
import GraphNodeButton from './button';
import GraphNodeContent from './content';
import {
GraphNodeRect,
GraphShadowRect
} from './shapes';
const GraphNode = ({
connected,
data,
index,
onDragStart
}) => {
const {
width,
height
} = data.children ?
Constants.nodeSizeWithChildren :
Constants.nodeSize;
const halfWidth = width/2;
const halfHeight = height/2;
let x = 0;
let y = 0;
if(connected) {
x = data.x-halfWidth;
y = data.y-halfHeight;
}
const onButtonClick = (evt) => {
// console.log('Rect clicked!!!');
};
const onStart = (evt) => {
evt.preventDefault();
onDragStart(evt, data.id);
};
const nodeRectEvents = connected ? {
onMouseDown: onStart,
onTouchStart: onStart
} : {};
const nodeContent = data.children ?
data.children.map((d, i) => (
<GraphNodeContent
key={i}
child
connected={connected}
data={d}
index={i}
/>
)) : (
<GraphNodeContent
connected={connected}
data={data}
/>
);
return (
<g transform={`translate(${x}, ${y})`}>
<GraphShadowRect
x={0}
y={3}
width={width}
height={height}
connected={connected}
/>
<GraphNodeRect
x={0}
y={0}
width={width}
height={height}
connected={connected}
{...nodeRectEvents}
/>
<GraphNodeTitle
connected={connected}
data={data}
/>
<GraphNodeButton
connected={connected}
index={index}
onButtonClick={onButtonClick}
/>
{nodeContent}
</g>
);
};
GraphNode.propTypes = {
connected: React.PropTypes.bool,
data: React.PropTypes.object.isRequired,
index: React.PropTypes.number.isRequired,
onDragStart: React.PropTypes.func
};
export default Baseline(
GraphNode
);

View File

@ -0,0 +1,67 @@
import React from 'react';
//import styled from 'styled-components';
import { Baseline } from '../../../shared/composers';
//import { colors } from '../../../shared/constants';
//import DataCentresIcon from './icon-data-centers.svg';
//import InstancesIcon from './icon-instances.svg';
import PropTypes from '../prop-types';
import { GraphText } from './shapes';
/*const StyledInstancesIcon = styled(InstancesIcon)`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
`;
const StyledDataCentresIcon = styled(DataCentresIcon)`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
`;*/
const GraphNodeInfo = ({
connected,
datacentres,
instances,
healthy,
pos
}) => {
const {
x,
y
} = pos;
return (
<g transform={`translate(${x}, ${y})`}>
<g transform={'translate(0, 2)'}>
{/*}<StyledInstancesIcon connected={connected} />*}
</g>
<GraphText
x={23}
y={12}
connected={connected}
>
{`${datacentres} inst.`}
</GraphText>
<g transform={'translate(82, 0)'}>
{/*<StyledDataCentresIcon connected={connected} />*/}
</g>
<GraphText
x={96}
y={12}
connected={connected}
>
{`${instances} DCs`}
</GraphText>
</g>
);
};
GraphNodeInfo.propTypes = {
connected: React.PropTypes.bool,
datacentres: React.PropTypes.number,
healthy: React.PropTypes.bool,
instances: React.PropTypes.number,
pos: PropTypes.Point.isRequired
};
export default Baseline(
GraphNodeInfo
);

View File

@ -0,0 +1,47 @@
import React from 'react';
import { Baseline } from '../../../shared/composers';
import PropTypes from '../prop-types';
import { GraphText } from './shapes';
const GraphNodeMetrics = ({
connected,
metrics,
pos
}) => {
const {
x,
y
} = pos;
const metricSpacing = 18;
const metricsText = metrics.map((metric, index) => (
<GraphText
key={index}
x={0}
y={12 + metricSpacing*index}
connected={connected}
>
{`${metric.name}: ${metric.value}`}
</GraphText>
));
return (
<g transform={`translate(${x}, ${y})`}>
{metricsText}
</g>
);
};
GraphNodeMetrics.propTypes = {
connected: React.PropTypes.bool,
metrics: React.PropTypes.arrayOf(React.PropTypes.shape({
name: React.PropTypes.string.isRequired,
value: React.PropTypes.string.isRequired
})),
pos: PropTypes.Point.isRequired
};
export default Baseline(
GraphNodeMetrics
);

View File

@ -0,0 +1,55 @@
import styled from 'styled-components';
import { colors } from '../../../shared/constants';
export const GraphLine = styled.line`
stroke: ${props => props.connected ?
colors.base.secondaryActive : colors.base.grey};
stroke-width: 1.5;
`;
export const GraphNodeRect = styled.rect`
stroke: ${props => props.connected ?
colors.base.secondaryActive : colors.base.grey};
fill: ${props => props.connected ? colors.base.secondary : colors.base.white};
stroke-width: 1.5;
rx: 4;
ry: 4;
`;
export const GraphShadowRect = styled.rect`
fill: ${props => props.connected ? colors.base.secondary : colors.base.grey};
opacity: 0.33;
rx: 4;
ry: 4;
`;
export const GraphTitle = styled.text`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
font-size: 16px;
font-weight: 600;
`;
export const GraphSubtitle = styled.text`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
font-size: 12px;
font-weight: 600;
`;
export const GraphText = styled.text`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
font-size: 12px;
opacity: 0.8;
`;
export const GraphButtonRect = styled.rect`
opacity: 0;
cursor: pointer;
`;
export const GraphButtonCircle = styled.circle`
fill: ${props => props.connected ? colors.base.white : colors.base.secondary};
`;
export const GraphHealthyCircle = styled.circle`
fill: ${colors.base.green};
`;

View File

@ -0,0 +1,40 @@
import React from 'react';
import { Baseline } from '../../../shared/composers';
import Constants from '../constants';
import { GraphTitle, GraphHealthyCircle } from './shapes';
// import HeartIcon from './icon-heart.svg';
const GraphNodeTitle = ({
connected,
data
}) => {
return (
<g>
<GraphTitle
x={Constants.paddingLeft}
y={30}
connected={connected}
>
{data.name}
</GraphTitle>
<g transform={`translate(${115}, ${15})`}>
<GraphHealthyCircle
cx={9}
cy={9}
r={9}
/>
{/*<HeartIcon />*/}
</g>
</g>
);
};
GraphNodeTitle.propTypes = {
connected: React.PropTypes.bool,
data: React.PropTypes.object.isRequired
};
export default Baseline(
GraphNodeTitle
);

View File

@ -1,13 +1,9 @@
import TopologyGraph from './topology-graph';
import TopologyGraphNode from './graph-node';
import TopologyGraphLink from './graph-link';
import TopologyGraphNodeButton from './graph-node-button';
import TopologyGraphNodeMetrics from './graph-node-metrics';
export {
TopologyGraph,
TopologyGraphNode,
TopologyGraphLink,
TopologyGraphNodeButton,
TopologyGraphNodeMetrics
TopologyGraphLink
};

View File

@ -26,13 +26,7 @@ storiesOf('Topology', module)
}, {
name: 'Network',
stat: '5.9KB/sec'
}],
x: 0,
y: 0
}}
size={{
width: 180,
height: 156
}]
}}
connected={false}
/>

View File

@ -111,19 +111,23 @@ class TopologyGraph extends React.Component {
links
} = this.state;
const node = (nodeId) =>
nodes.reduce((acc, simNode, index) => {
return simNode.id === nodeId ? simNode : acc;
}, {});
const simNode = (nodeId) =>
nodes.reduce((acc, simNode, index) =>
simNode.id === nodeId ? simNode : acc, {});
const nodesData = services.map((service, index) => ({
...service,
...node(service.uuid)
...simNode(service.uuid)
}));
const nodeData = (nodeId) =>
nodesData.reduce((acc, nodeData, index) =>
nodeData.id === nodeId ? nodeData : acc, {});
// TODO links will need to know whether a service has children
// if it does, the height of it will be different
const linksData = links.map((link, index) => ({
source: node(link.source.id),
target: node(link.target.id)
source: nodeData(link.source.id),
target: nodeData(link.target.id)
}));
const onDragStart = (evt, nodeId) => {
@ -207,7 +211,6 @@ class TopologyGraph extends React.Component {
key={index}
data={n}
index={index}
size={nodeSize}
onDragStart={onDragStart}
connected
/>
@ -218,7 +221,6 @@ class TopologyGraph extends React.Component {
key={index}
data={l}
index={index}
nodeSize={nodeSize}
/>
));