Skip to content

Commit

Permalink
Implement reusable 'Display' top panel with a variable set of options.
Browse files Browse the repository at this point in the history
Signed-off-by: Vitaliy Guschin <[email protected]>
  • Loading branch information
Vitaliy Guschin committed Apr 17, 2024
1 parent dba78d7 commit d3bb769
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 19 deletions.
24 changes: 24 additions & 0 deletions src/floatPanels/TopDisplayOptionsPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DisplayPanelOptionEnv } from "../model";

interface TopDisplayOptionsPanelProps {
options: DisplayPanelOptionEnv[];
setShowOptionsPanel: (v: boolean) => void;
}

export default function TopDisplayOptionsPanel({ options, setShowOptionsPanel }: TopDisplayOptionsPanelProps) {
return (
<div className="top-display-options-panel" onMouseLeave={() => setShowOptionsPanel(false)}>
{options.map((opt, index) => (
<div key={index} className="top-display-options-panel-item" onClick={() => opt.onClick()}>
<input
type="checkbox"
checked={opt.checked}
onChange={() => {}}
className="top-display-options-panel-item"
/>
{opt.label}
</div>
))}
</div>
);
}
23 changes: 23 additions & 0 deletions src/floatPanels/TopDisplayPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useState } from 'react';
import TopDisplayOptionsPanel from "./TopDisplayOptionsPanel";
import { DisplayPanelOptionEnv } from "../model";

interface TopDisplayPanelProps {
options: DisplayPanelOptionEnv[];
}

export default function TopDisplayPanel({ options }: TopDisplayPanelProps) {
const [showOptionsPanel, setShowOptionsPanel] = useState(false);

return (
<>
<div
className="top-display-panel"
onClick={() => setShowOptionsPanel(!showOptionsPanel)}
>
<div>Display</div>
</div>
{showOptionsPanel && <TopDisplayOptionsPanel options={options} setShowOptionsPanel={setShowOptionsPanel}/>}
</>
);
}
25 changes: 23 additions & 2 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type Edge = {
export enum NodeType {
Cluster = "cluster",
Interface = "interface",
LCInterface = "loopConIfNT",
Client = "client",
Forwarder = "forwarder",
Manager = "manager",
Expand All @@ -34,19 +35,20 @@ export enum NodeType {
export enum EdgeType {
InterfaceConnection = "interfaceConnection",
InterfaceCrossConnection = "interfaceCrossConnection",
InterfaceLoopedConnection = "interfaceLoopedConnection",
ServiceRequest = "serviceRequest",
RegistryRequest = "registryRequest"
}

export const AllowedNodeTypes = {
Dataplane: [NodeType.Cluster, NodeType.Interface, NodeType.Forwarder, NodeType.Client, NodeType.Endpoint],
Dataplane: [NodeType.Cluster, NodeType.Interface, NodeType.LCInterface, NodeType.Forwarder, NodeType.Client, NodeType.Endpoint],
NetworkServices: [NodeType.Client, NodeType.Service],
NetworkServiceRequests: [NodeType.Forwarder, NodeType.Client, NodeType.Endpoint, NodeType.Manager],
RegistryRequests: [NodeType.Endpoint, NodeType.Forwarder, NodeType.Manager, NodeType.Registry],
}

export const AllowedEdgeTypes = {
Dataplane: [EdgeType.InterfaceConnection, EdgeType.InterfaceCrossConnection],
Dataplane: [EdgeType.InterfaceConnection, EdgeType.InterfaceCrossConnection, EdgeType.InterfaceLoopedConnection],
NetworkServices: [EdgeType.ServiceRequest],
NetworkServiceRequests: [EdgeType.ServiceRequest],
RegistryRequests: [EdgeType.RegistryRequest],
Expand Down Expand Up @@ -80,3 +82,22 @@ export enum LineStyle {
Dashed = "dashed",
Dotted = "dotted"
}

export enum Page {
Dataplane = "dataplane"
}

export enum Option {
ShowLoopedConnections = "showLoopedConnections"
}

export type DisplayPanelOption = {
page: Page,
option: Option
}

export type DisplayPanelOptionEnv = {
label: string,
onClick: () => void,
checked: boolean,
}
60 changes: 54 additions & 6 deletions src/pages/Dataplane.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import * as React from "react";
import { useDispatch, useSelector } from 'react-redux';
import { switchDisplayPanelOption } from '../store/actions';
import { AppDispatch, RootState } from '../store/store';
import { Stylesheet } from "cytoscape";
import cloneDeep from 'lodash/cloneDeep';
import CytoscapeCanvas from "./CytoscapeCanvas";
import TopDisplayPanel from "../floatPanels/TopDisplayPanel";
import {
NodeType,
InterfaceSize,
Expand All @@ -13,7 +17,10 @@ import {
AllowedNodeTypes,
AllowedEdgeTypes,
Node,
Edge
Edge,
Page,
Option,
DisplayPanelOptionEnv
} from "../model";

interface DataplaneProps {
Expand Down Expand Up @@ -51,6 +58,20 @@ function getDataplaneStylesheet() {
"text-border-opacity": 1
}
},
{
selector: `node[type = '${NodeType.LCInterface}']`,
style: {
width: InterfaceSize,
height: InterfaceSize,
"font-size": "8px",
'text-background-color': Color.Gray,
'text-background-opacity': 1,
"text-background-shape": "roundrectangle",
'text-border-color': Color.Gray,
"text-border-width": "0.2em",
"text-border-opacity": 1
}
},
{
selector: `node[type = '${NodeType.Interface}'][label]`,
style: {
Expand Down Expand Up @@ -95,24 +116,51 @@ function getDataplaneStylesheet() {
"line-color": (edge: { data: (arg0: string) => boolean }) =>
edge.data("healthy") === false ? Color.Red : Color.Green
}
},
{
selector: `edge[type = '${EdgeType.InterfaceLoopedConnection}']`,
style: {
"line-color": Color.Gray
}
}
];
}

export default function Dataplane({ nodes, edges }: DataplaneProps) {
const dispatch = useDispatch<AppDispatch>();
const showLoopedConnections = useSelector((state: RootState) => state.app.pages.dataplane.topDisplayPanelOptions.showLoopedConnections);

const displayPanelOptions: DisplayPanelOptionEnv[] = [
{
label: 'Looped Connections',
onClick: () => dispatch(switchDisplayPanelOption({
page: Page.Dataplane,
option: Option.ShowLoopedConnections
})),
checked: showLoopedConnections,
},
];

const [stylesheet] = React.useState<Stylesheet[]>(getDataplaneStylesheet() as Stylesheet[]);
const dataplaneNodes = nodes
.filter(n => AllowedNodeTypes.Dataplane.includes(n.data.type))
.filter(n => showLoopedConnections ? n : n.data.type !== NodeType.LCInterface)
.map(n => ({ ...n, data: cloneDeep(n.data) }));
const dataplaneEdges = edges
.filter(e => AllowedEdgeTypes.Dataplane.includes(e.data.type))
.filter(e => showLoopedConnections ? e : e.data.type !== EdgeType.InterfaceLoopedConnection)
.map(e => ({ ...e, data: cloneDeep(e.data) }));

return (
<CytoscapeCanvas
nodes={dataplaneNodes}
edges={dataplaneEdges}
stylesheet={stylesheet}
/>
<>
<CytoscapeCanvas
nodes={dataplaneNodes}
edges={dataplaneEdges}
stylesheet={stylesheet}
/>
<TopDisplayPanel
options={displayPanelOptions}
/>
</>
);
}
14 changes: 12 additions & 2 deletions src/store/actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { SET_NODES, SET_EDGES, SET_SELECTED_MENU_ITEM } from './types';
import { Node, Edge } from "../model";
import {
SET_NODES,
SET_EDGES,
SET_SELECTED_MENU_ITEM,
SWITCH_DISPLAY_PANEL_OPTION
} from './types';
import { Node, Edge, DisplayPanelOption } from "../model";

export const setNodes = (nodes: Node[]) => ({
type: SET_NODES,
Expand All @@ -15,3 +20,8 @@ export const setSelectedMenuItem = (selectedMenuItem: number) => ({
type: SET_SELECTED_MENU_ITEM,
payload: selectedMenuItem,
});

export const switchDisplayPanelOption = (displayPanelOption: DisplayPanelOption) => ({
type: SWITCH_DISPLAY_PANEL_OPTION,
payload: displayPanelOption,
});
37 changes: 34 additions & 3 deletions src/store/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { SET_NODES, SET_EDGES, SET_SELECTED_MENU_ITEM } from './types';
import { Node, Edge } from "../model";
import {
SET_NODES,
SET_EDGES,
SET_SELECTED_MENU_ITEM,
SWITCH_DISPLAY_PANEL_OPTION
} from './types';
import { Node, Edge, Page, Option } from "../model";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const initialState: { nodes: Node[]; edges: Edge[]; app: any } = {
nodes: [],
edges: [],
app: {
selectedMenuItem: 2,
pages: {
[Page.Dataplane]: {
topDisplayPanelOptions: {
[Option.ShowLoopedConnections]: false
}
}
}
}
};

Expand All @@ -18,7 +30,26 @@ const rootReducer = (state = initialState, action: any) => {
case SET_EDGES:
return { ...state, edges: action.payload };
case SET_SELECTED_MENU_ITEM:
return { ...state, app: { selectedMenuItem: action.payload} };
return { ...state, app: { ...state.app, selectedMenuItem: action.payload} };
case SWITCH_DISPLAY_PANEL_OPTION:
const page = action.payload.page;

Check failure on line 35 in src/store/reducer.ts

View workflow job for this annotation

GitHub Actions / react-lint

Unexpected lexical declaration in case block
const option = action.payload.option;

Check failure on line 36 in src/store/reducer.ts

View workflow job for this annotation

GitHub Actions / react-lint

Unexpected lexical declaration in case block
return {
...state,
app: {
...state.app,
pages: {
...state.app.pages,
[page]: {
...state.app.pages[page],
topDisplayPanelOptions: {
...state.app.pages[page].topDisplayPanelOptions,
[option]: !state.app.pages[page].topDisplayPanelOptions[option]
}
}
}
}
};
default:
return state;
}
Expand Down
1 change: 1 addition & 0 deletions src/store/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const SET_NODES = 'SET_NODES';
export const SET_EDGES = 'SET_EDGES';
export const SET_SELECTED_MENU_ITEM ='SET_SELECTED_MENU_ITEM';
export const SWITCH_DISPLAY_PANEL_OPTION ='SWITCH_DISPLAY_PANEL_OPTION';
44 changes: 38 additions & 6 deletions src/styles.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
:root {
--theme-color: #202020;
--theme-text-color: white;
--floating-panel-background-color: #D9D9D9;
}

body {
margin: 0;
padding: 0;
font-family: 'Roboto';
}

.header {
background-color: #202020;
background-color: var(--theme-color);
height: 39px;
display: flex;
justify-content: center;
Expand All @@ -19,12 +25,12 @@ body {
}

.header-title {
color: white;
color: var(--theme-text-color);
font-size: 16px;
}

.footer {
background-color: #202020;
background-color: var(--theme-color);
height: 16px;
display: flex;
justify-content: center;
Expand All @@ -33,25 +39,51 @@ body {
}

.footer-text {
color: white;
color: var(--theme-text-color);
font-size: 9px;
}

.left-panel {
background-color: #202020;
background-color: var(--theme-color);
width: 200px;
height: calc(100vh - 55px);
padding-left: 10px;
}

.top-display-panel {
position: fixed;
left: 220px;
top: 49px;
font-size: 14px;
background-color: var(--floating-panel-background-color);
padding: 5px;
border-radius: 5px;
user-select: none;
cursor: pointer;
}

.top-display-options-panel {
position: fixed;
left: 220px;
top: 79px;
padding: 10px;
background-color: var(--floating-panel-background-color);
user-select: none;
}

.top-display-options-panel-item {
cursor: pointer;
margin-bottom: 5px;
}

.menu {
list-style: none;
padding: 0;
margin: 0;
}

.menu-item {
color: white;
color: var(--theme-text-color);
font-size: 16px;
cursor: pointer;
padding: 5px 0;
Expand Down

0 comments on commit d3bb769

Please sign in to comment.