Skip to content

Commit 877f363

Browse files
Merge pull request #226 from alanconway/graph-layout
NO-JIRA: feat: improve graph layout and visuals
2 parents 01bc566 + a70bee7 commit 877f363

File tree

4 files changed

+47
-27
lines changed

4 files changed

+47
-27
lines changed

web/locales/en/plugin__troubleshooting-panel-console-plugin.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"Focus the correlation on the current view.": "Focus the correlation on the current view.",
2020
"From": "From",
2121
"Goals": "Goals",
22-
"Include data from this time range": "Include data from this time range",
2322
"Last": "Last",
2423
"Logging Plugin Disabled": "Logging Plugin Disabled",
2524
"Neighbours": "Neighbours",

web/src/components/Korrel8rPanel.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export default function Korrel8rPanel() {
148148
spaceItems={{ default: 'spaceItemsXs' }}
149149
>
150150
<Tooltip
151+
position="bottom-start"
151152
content={
152153
locationQuery
153154
? isFocused
@@ -175,19 +176,17 @@ export default function Korrel8rPanel() {
175176

176177
{/* Time range drop-down */}
177178
<Flex align={{ default: 'alignRight' }} spaceItems={{ default: 'spaceItemsNone' }}>
178-
<Tooltip content={t('Include data from this time range')}>
179-
<TimeRangeDropdown
180-
className="tp-plugin__compact-control"
181-
period={search.period ?? defaultSearch.period}
182-
onChange={(period: time.Period) => dispatchSearch({ ...search, period })}
183-
/>
184-
</Tooltip>
179+
<TimeRangeDropdown
180+
className="tp-plugin__compact-control"
181+
period={search.period ?? defaultSearch.period}
182+
onChange={(period: time.Period) => dispatchSearch({ ...search, period })}
183+
/>
185184
</Flex>
186185

187186
{/* Right aligned buttons */}
188187
<Flex align={{ default: 'alignRight' }} spaceItems={{ default: 'spaceItemsNone' }}>
189188
{/* Advanced search toggle */}
190-
<Tooltip content={t('Advanced search parameters')}>
189+
<Tooltip content={t('Advanced search parameters')} position="bottom-end">
191190
<ExpandableSectionToggle
192191
contentId={advancedContentID}
193192
toggleId={advancedToggleID}

web/src/components/topology/Korrel8rTopology.tsx

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import { Badge, Title } from '@patternfly/react-core';
22
import {
33
action,
4-
BadgeLocation,
5-
BreadthFirstLayout,
64
ComponentFactory,
75
ContextMenuItem,
86
createTopologyControlButtons,
7+
DagreLayout,
98
defaultControlButtonsOptions,
109
DefaultEdge,
1110
DefaultGroup,
@@ -20,13 +19,16 @@ import {
2019
Node,
2120
NodeShape,
2221
SELECTION_EVENT,
22+
TOP_TO_BOTTOM,
2323
TopologyControlBar,
2424
TopologyView,
2525
Visualization,
2626
VisualizationProvider,
2727
VisualizationSurface,
2828
withContextMenu,
2929
WithContextMenuProps,
30+
withDragNode,
31+
WithDragNodeProps,
3032
withPanZoom,
3133
withSelection,
3234
WithSelectionProps,
@@ -38,6 +40,13 @@ import * as korrel8r from '../../korrel8r/types';
3840
import { getIcon } from '../icons';
3941
import './korrel8rtopology.css';
4042

43+
// DagreLayout with straight edges (no angular bendpoints).
44+
class StraightEdgeDagreLayout extends DagreLayout {
45+
protected updateEdgeBendpoints(): void {
46+
// no-op: skip bendpoints for straight edges between nodes.
47+
}
48+
}
49+
4150
const capitalize = (s: string) => (s ? s[0].toUpperCase() + s.slice(1) : '');
4251

4352
const nodeLabel = (node: korrel8r.Node): string => {
@@ -47,20 +56,13 @@ const nodeLabel = (node: korrel8r.Node): string => {
4756
return capitalize(c.name);
4857
};
4958

50-
const nodeBadge = (node: korrel8r.Node): string => {
51-
const queries = nodeQueries(node);
52-
return `${queries.length > 1 ? `${queries[0]?.count}/` : ''}${node?.count ?? '?'} `;
53-
};
54-
55-
const nodeQueries = (node: korrel8r.Node) => node?.queries ?? [];
56-
5759
interface Korrel8rTopologyNodeProps {
5860
element: Node;
5961
}
6062

6163
const Korrel8rTopologyNode: React.FC<
62-
Korrel8rTopologyNodeProps & WithContextMenuProps & WithSelectionProps
63-
> = ({ element, onSelect, selected, onContextMenu, contextMenuOpen }) => {
64+
Korrel8rTopologyNodeProps & WithContextMenuProps & WithSelectionProps & WithDragNodeProps
65+
> = ({ element, onSelect, selected, onContextMenu, contextMenuOpen, dragNodeRef }) => {
6466
const node = element.getData();
6567
const topologyNode = (
6668
<DefaultNode
@@ -69,10 +71,12 @@ const Korrel8rTopologyNode: React.FC<
6971
selected={selected}
7072
onContextMenu={onContextMenu}
7173
contextMenuOpen={contextMenuOpen}
74+
dragNodeRef={dragNodeRef}
7275
hover={false}
7376
label={nodeLabel(node)}
74-
badge={nodeBadge(node)}
75-
badgeLocation={BadgeLocation.below}
77+
badge={node?.count?.toString() ?? '?'}
78+
badgeClassName="tp-plugin__topology_node_badge"
79+
hideContextMenuKebab={node?.queries?.length === 1}
7680
>
7781
<g transform={`translate(25, 25)`}>{getIcon(node.class)}</g>
7882
</DefaultNode>
@@ -159,7 +163,7 @@ export const Korrel8rTopology: React.FC<{
159163
<Title headingLevel="h4">{node.class.toString()}</Title>
160164
</ContextMenuItem>,
161165
];
162-
nodeQueries(node).forEach((qc) =>
166+
node?.queries?.forEach((qc) =>
163167
menu.push(
164168
<ContextMenuItem
165169
key={qc.query.toString()}
@@ -186,7 +190,7 @@ export const Korrel8rTopology: React.FC<{
186190
case ModelKind.graph:
187191
return withPanZoom()(GraphComponent);
188192
case ModelKind.node:
189-
return withContextMenu(nodeMenu)(withSelection()(Korrel8rTopologyNode));
193+
return withDragNode()(withContextMenu(nodeMenu)(withSelection()(Korrel8rTopologyNode)));
190194
case ModelKind.edge:
191195
return DefaultEdge;
192196
default:
@@ -203,12 +207,19 @@ export const Korrel8rTopology: React.FC<{
203207
graph: {
204208
id: 'korrel8r_graph',
205209
type: 'graph',
206-
layout: 'BreadthFirst',
210+
layout: 'Dagre',
207211
},
208212
};
209213

210214
const controller = new Visualization();
211-
controller.registerLayoutFactory((_, graph: Graph) => new BreadthFirstLayout(graph));
215+
controller.registerLayoutFactory(
216+
(_, graph: Graph) =>
217+
new StraightEdgeDagreLayout(graph, {
218+
rankdir: TOP_TO_BOTTOM,
219+
ranksep: 10,
220+
nodeDistance: 15,
221+
}),
222+
);
212223
controller.fromModel(model, false);
213224
return controller;
214225
}, [nodes, edges]);
@@ -252,7 +263,9 @@ export const Korrel8rTopology: React.FC<{
252263
zoomOutCallback: action(() => {
253264
controller.getGraph().scaleBy(0.75);
254265
}),
255-
fitToScreen: false, // Same thing as resetView
266+
fitToScreenCallback: action(() => {
267+
controller.getGraph().fit(PADDING);
268+
}),
256269
resetViewCallback: action(() => {
257270
controller.getGraph().reset();
258271
controller.getGraph().layout();
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
.tp-plugin__topology_invalid_node {
22
cursor: not-allowed;
33
}
4+
5+
.tp-plugin__topology_node_badge rect {
6+
fill: var(--pf-t--global--color--brand--default);
7+
stroke: var(--pf-t--global--color--brand--default);
8+
}
9+
10+
.tp-plugin__topology_node_badge text {
11+
fill: var(--pf-t--global--text--color--on-brand--default);
12+
}

0 commit comments

Comments
 (0)