This repository has been archived by the owner on Jul 3, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Topology view component PoC * Remove success status * Fix TS error * update version of package * Fix naming convention, cleanup * Update interface name
- Loading branch information
Showing
8 changed files
with
2,564 additions
and
8 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
src/app/components/POCs/TopologyView/NodeSideBarDetails.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ | ||
import { | ||
DescriptionList, | ||
DescriptionListDescription, | ||
DescriptionListGroup, | ||
DescriptionListTerm, | ||
} from "@patternfly/react-core"; | ||
import React from "react"; | ||
import { NodeModel } from "@patternfly/react-topology"; | ||
|
||
interface NodeInformationProps { | ||
node: NodeModel | undefined; | ||
} | ||
|
||
const NodeInformation = (props: NodeInformationProps): JSX.Element => { | ||
const { node } = props; | ||
|
||
return ( | ||
<DescriptionList | ||
isHorizontal | ||
isCompact | ||
style={{ marginLeft: "2rem", marginTop: "2rem" }} | ||
> | ||
<DescriptionListGroup> | ||
<DescriptionListTerm>{"Name"}</DescriptionListTerm> | ||
<DescriptionListDescription>{node?.label}</DescriptionListDescription> | ||
</DescriptionListGroup> | ||
<DescriptionListGroup> | ||
<DescriptionListTerm>{"Type"}</DescriptionListTerm> | ||
<DescriptionListDescription> | ||
{node?.data.type} | ||
</DescriptionListDescription> | ||
</DescriptionListGroup> | ||
<DescriptionListGroup> | ||
<DescriptionListTerm>{"Owner"}</DescriptionListTerm> | ||
<DescriptionListDescription> | ||
{node?.data.owner} | ||
</DescriptionListDescription> | ||
</DescriptionListGroup> | ||
<DescriptionListGroup> | ||
<DescriptionListTerm>{"Time created"}</DescriptionListTerm> | ||
<DescriptionListDescription> | ||
{node?.data.timeCreated} | ||
</DescriptionListDescription> | ||
</DescriptionListGroup>{" "} | ||
<DescriptionListGroup> | ||
<DescriptionListTerm>{"Time Updated"}</DescriptionListTerm> | ||
<DescriptionListDescription> | ||
{node?.data.timeUpdated} | ||
</DescriptionListDescription> | ||
</DescriptionListGroup> | ||
</DescriptionList> | ||
); | ||
}; | ||
|
||
export default NodeInformation; |
62 changes: 62 additions & 0 deletions
62
src/app/components/POCs/TopologyView/TopologyView.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import React from "react"; | ||
import { ComponentMeta, ComponentStory } from "@storybook/react"; | ||
import TopologyView from "./TopologyView"; | ||
import { | ||
EDGES_12, | ||
EDGES_6, | ||
NODES_12, | ||
NODES_12_Without_Edges, | ||
NODES_6, | ||
} from "./topologyStoriesHelper"; | ||
|
||
export default { | ||
title: "PoCs/Topology View", | ||
component: TopologyView, | ||
} as ComponentMeta<typeof TopologyView>; | ||
|
||
const Template: ComponentStory<typeof TopologyView> = (args) => ( | ||
<TopologyView {...args} /> | ||
); | ||
|
||
export const DagreLayout_6_nodes = Template.bind({}); | ||
DagreLayout_6_nodes.args = { | ||
layout: "Dagre_network-simplex", | ||
nodes: NODES_6, | ||
edges: EDGES_6, | ||
}; | ||
|
||
export const DagreLayout_12_nodes_with_tight_tree_algorithm = Template.bind({}); | ||
DagreLayout_12_nodes_with_tight_tree_algorithm.args = { | ||
layout: "Dagre_tight-tree", | ||
nodes: NODES_12, | ||
edges: EDGES_12, | ||
}; | ||
|
||
export const DagreLayout_12_nodes_with_Network_simplex_algorithm = | ||
Template.bind({}); | ||
DagreLayout_12_nodes_with_Network_simplex_algorithm.args = { | ||
layout: "Dagre_network-simplex", | ||
nodes: NODES_12, | ||
edges: EDGES_12, | ||
}; | ||
|
||
export const BreadthFirstLayout_6_nodes = Template.bind({}); | ||
BreadthFirstLayout_6_nodes.args = { | ||
layout: "BreadthFirst", | ||
nodes: NODES_6, | ||
edges: EDGES_6, | ||
}; | ||
|
||
export const BreadthFirstLayout_12_nodes = Template.bind({}); | ||
BreadthFirstLayout_12_nodes.args = { | ||
layout: "BreadthFirst", | ||
nodes: NODES_12, | ||
edges: EDGES_12, | ||
}; | ||
|
||
export const ColaLayout_Without_Edges = Template.bind({}); | ||
ColaLayout_Without_Edges.args = { | ||
layout: "Cola", | ||
nodes: NODES_12_Without_Edges, | ||
edges: [], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
import React from "react"; | ||
import { RegionsIcon as Icon4 } from "@patternfly/react-icons"; | ||
import { DataSourceIcon as Icon1 } from "@patternfly/react-icons"; | ||
import { DataSinkIcon as Icon2 } from "@patternfly/react-icons"; | ||
import { DataProcessorIcon as Icon3 } from "@patternfly/react-icons"; | ||
import { | ||
action, | ||
BreadthFirstLayout, | ||
ColaLayout, | ||
ConcentricLayout, | ||
createTopologyControlButtons, | ||
DagreLayout, | ||
defaultControlButtonsOptions, | ||
DefaultEdge, | ||
DefaultGroup, | ||
DefaultNode, | ||
Edge, | ||
EdgeTerminalType, | ||
ForceLayout, | ||
GraphComponent, | ||
GridLayout, | ||
ModelKind, | ||
SELECTION_EVENT, | ||
TopologyControlBar, | ||
TopologySideBar, | ||
TopologyView as PFTopologyView, | ||
Visualization, | ||
VisualizationProvider, | ||
VisualizationSurface, | ||
withPanZoom, | ||
withSelection, | ||
WithSelectionProps, | ||
ComponentFactory, | ||
Graph, | ||
Layout, | ||
LayoutFactory, | ||
Model, | ||
Node, | ||
EdgeModel, | ||
NodeModel, | ||
} from "@patternfly/react-topology"; | ||
|
||
import NodeInformation from "./NodeSideBarDetails"; | ||
|
||
export interface TopologyViewProps { | ||
layout: string; | ||
nodes: NodeModel[]; | ||
edges: EdgeModel[]; | ||
truncateLength?: number; | ||
} | ||
|
||
interface CustomNodeProps { | ||
element: Node; | ||
} | ||
interface DataEdgeProps { | ||
element: Edge; | ||
} | ||
|
||
const DataEdge: React.FC<DataEdgeProps> = ({ element, ...rest }) => ( | ||
<DefaultEdge | ||
element={element} | ||
startTerminalType={EdgeTerminalType.cross} | ||
endTerminalType={EdgeTerminalType.directionalAlt} | ||
{...rest} | ||
/> | ||
); | ||
|
||
const CustomNode: React.FC<CustomNodeProps & WithSelectionProps> = ({ | ||
element, | ||
onSelect, | ||
selected, | ||
...rest | ||
}) => { | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
const data = element.getData(); | ||
let Icon; | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
if (data.type == "Source") { | ||
Icon = Icon1; | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
} else if (data.type == "Sink") { | ||
Icon = Icon2; | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | ||
} else if (data.type == "Processor") { | ||
Icon = Icon3; | ||
} else { | ||
Icon = Icon4; | ||
} | ||
|
||
return ( | ||
<DefaultNode | ||
element={element} | ||
showStatusDecorator | ||
{...rest} | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access | ||
truncateLength={data.length} | ||
onSelect={onSelect} | ||
selected={selected} | ||
> | ||
<g transform={`translate(25, 25)`}> | ||
<Icon style={{ color: "#393F44" }} width={25} height={25} /> | ||
</g> | ||
</DefaultNode> | ||
); | ||
}; | ||
|
||
const customLayoutFactory: LayoutFactory = ( | ||
type: string, | ||
graph: Graph | ||
): Layout | undefined => { | ||
switch (type) { | ||
case "Cola": | ||
return new ColaLayout(graph, { | ||
layoutOnDrag: false, | ||
linkDistance: 10, | ||
collideDistance: 30, | ||
maxTicks: 1, | ||
groupDistance: 150, | ||
}); | ||
case "Dagre_network-simplex": | ||
return new DagreLayout(graph, { | ||
rankdir: "LR", | ||
nodesep: 10, | ||
linkDistance: 5, | ||
edgesep: 2, | ||
groupDistance: 150, | ||
ranker: "network-simplex", | ||
}); | ||
case "Dagre_tight-tree": | ||
return new DagreLayout(graph, { | ||
rankdir: "LR", | ||
nodesep: 10, | ||
linkDistance: 5, | ||
edgesep: 2, | ||
groupDistance: 150, | ||
ranker: "tight-tree", | ||
}); | ||
case "BreadthFirst": | ||
return new BreadthFirstLayout(graph, { | ||
nodeDistance: 80, | ||
}); | ||
case "Concentric": | ||
return new ConcentricLayout(graph, { | ||
groupDistance: 150, | ||
}); | ||
case "Grid": | ||
return new GridLayout(graph, { | ||
groupDistance: 150, | ||
nodeDistance: 75, | ||
}); | ||
default: | ||
return new ForceLayout(graph, { | ||
groupDistance: 250, | ||
}); | ||
} | ||
}; | ||
|
||
const customComponentFactory: ComponentFactory = ( | ||
kind: ModelKind, | ||
type: string | ||
): any => { | ||
switch (type) { | ||
case "group": | ||
return DefaultGroup; | ||
case "data-edge": | ||
return DataEdge; | ||
default: | ||
switch (kind) { | ||
case ModelKind.graph: | ||
return withPanZoom()(GraphComponent); | ||
case ModelKind.node: | ||
return withSelection()(CustomNode as React.FC); | ||
case ModelKind.edge: | ||
return DefaultEdge; | ||
default: | ||
return undefined; | ||
} | ||
} | ||
}; | ||
|
||
const TopologyView = (props: TopologyViewProps): JSX.Element => { | ||
const { layout, nodes, edges, truncateLength } = props; | ||
|
||
const [selectedIds, setSelectedIds] = React.useState<string[]>([]); | ||
|
||
const controller = React.useMemo(() => { | ||
nodes.map((node) => { | ||
node.data && Object.assign(node?.data, { length: truncateLength }); | ||
}); | ||
|
||
const model: Model = { | ||
nodes: nodes, | ||
edges: edges, | ||
graph: { | ||
id: "g1", | ||
type: "graph", | ||
layout, | ||
}, | ||
}; | ||
|
||
const newController = new Visualization(); | ||
newController.setFitToScreenOnLayout(true); | ||
newController.registerLayoutFactory(customLayoutFactory); | ||
newController.registerComponentFactory(customComponentFactory); | ||
|
||
newController.addEventListener(SELECTION_EVENT, setSelectedIds); | ||
|
||
newController.fromModel(model, false); | ||
|
||
return newController; | ||
}, [edges, layout, nodes, truncateLength]); | ||
|
||
const node = nodes.find((node) => node.id === selectedIds[0]); | ||
|
||
const topologySideBar = ( | ||
<TopologySideBar | ||
className="topology-example-sidebar" | ||
show={selectedIds.length > 0} | ||
onClose={(): void => setSelectedIds([])} | ||
> | ||
<NodeInformation node={node} /> | ||
</TopologySideBar> | ||
); | ||
|
||
return ( | ||
<PFTopologyView | ||
sideBar={topologySideBar} | ||
controlBar={ | ||
<TopologyControlBar | ||
controlButtons={createTopologyControlButtons({ | ||
...defaultControlButtonsOptions, | ||
zoomInCallback: action(() => { | ||
controller.getGraph().scaleBy(4 / 3); | ||
}), | ||
zoomOutCallback: action(() => { | ||
controller.getGraph().scaleBy(0.75); | ||
}), | ||
fitToScreenCallback: action(() => { | ||
controller.getGraph().fit(80); | ||
}), | ||
resetViewCallback: action(() => { | ||
controller.getGraph().reset(); | ||
controller.getGraph().layout(); | ||
}), | ||
legend: false, | ||
})} | ||
/> | ||
} | ||
> | ||
<VisualizationProvider controller={controller}> | ||
<VisualizationSurface state={{ selectedIds }} /> | ||
</VisualizationProvider> | ||
</PFTopologyView> | ||
); | ||
}; | ||
|
||
export default TopologyView; |
Oops, something went wrong.