NodeGraph: Fix invisible arrow tips in Editor (#86517)

NodeGraphPanel: Namespace marker IDs to fix invisible arrow tips in editor

Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
pull/86562/head
timo 1 year ago committed by GitHub
parent 1d1ebee36b
commit f3fcfad2c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      public/app/plugins/panel/nodeGraph/Edge.tsx
  2. 13
      public/app/plugins/panel/nodeGraph/NodeGraph.tsx
  3. 10
      public/app/plugins/panel/nodeGraph/NodeGraphPanel.tsx

@ -11,13 +11,14 @@ export const defaultEdgeColor = '#999';
interface Props { interface Props {
edge: EdgeDatum; edge: EdgeDatum;
hovering: boolean; hovering: boolean;
svgIdNamespace: string;
onClick: (event: MouseEvent<SVGElement>, link: EdgeDatum) => void; onClick: (event: MouseEvent<SVGElement>, link: EdgeDatum) => void;
onMouseEnter: (id: string) => void; onMouseEnter: (id: string) => void;
onMouseLeave: (id: string) => void; onMouseLeave: (id: string) => void;
} }
export const Edge = memo(function Edge(props: Props) { export const Edge = memo(function Edge(props: Props) {
const { edge, onClick, onMouseEnter, onMouseLeave, hovering } = props; const { edge, onClick, onMouseEnter, onMouseLeave, hovering, svgIdNamespace } = props;
// Not great typing but after we do layout these properties are full objects not just references // Not great typing but after we do layout these properties are full objects not just references
const { source, target, sourceNodeRadius, targetNodeRadius } = edge as { const { source, target, sourceNodeRadius, targetNodeRadius } = edge as {
@ -47,8 +48,8 @@ export const Edge = memo(function Edge(props: Props) {
// in case both are provided // in case both are provided
const highlightedEdgeColor = edge.color || defaultHighlightedEdgeColor; const highlightedEdgeColor = edge.color || defaultHighlightedEdgeColor;
const markerId = `triangle-${edge.id}`; const markerId = `triangle-${svgIdNamespace}-${edge.id}`;
const coloredMarkerId = `triangle-colored-${edge.id}`; const coloredMarkerId = `triangle-colored-${svgIdNamespace}-${edge.id}`;
return ( return (
<> <>

@ -111,8 +111,9 @@ interface Props {
dataFrames: DataFrame[]; dataFrames: DataFrame[];
getLinks: (dataFrame: DataFrame, rowIndex: number) => LinkModel[]; getLinks: (dataFrame: DataFrame, rowIndex: number) => LinkModel[];
nodeLimit?: number; nodeLimit?: number;
panelId?: string;
} }
export function NodeGraph({ getLinks, dataFrames, nodeLimit }: Props) { export function NodeGraph({ getLinks, dataFrames, nodeLimit, panelId }: Props) {
const nodeCountLimit = nodeLimit || defaultNodeCountLimit; const nodeCountLimit = nodeLimit || defaultNodeCountLimit;
const { edges: edgesDataFrames, nodes: nodesDataFrames } = useCategorizeFrames(dataFrames); const { edges: edgesDataFrames, nodes: nodesDataFrames } = useCategorizeFrames(dataFrames);
@ -122,6 +123,13 @@ export function NodeGraph({ getLinks, dataFrames, nodeLimit }: Props) {
const firstNodesDataFrame = nodesDataFrames[0]; const firstNodesDataFrame = nodesDataFrames[0];
const firstEdgesDataFrame = edgesDataFrames[0]; const firstEdgesDataFrame = edgesDataFrames[0];
// Ensure we use unique IDs for the marker tip elements, since IDs are global
// in the entire HTML document. This prevents hidden tips when an earlier
// occurence is hidden (editor is open in front of an existing node graph
// panel) or when the earlier tips have different properties (color, size, or
// shape for example).
const svgIdNamespace = panelId || 'nodegraphpanel';
// TODO we should be able to allow multiple dataframes for both edges and nodes, could be issue with node ids which in // TODO we should be able to allow multiple dataframes for both edges and nodes, could be issue with node ids which in
// that case should be unique or figure a way to link edges and nodes dataframes together. // that case should be unique or figure a way to link edges and nodes dataframes together.
const processed = useMemo( const processed = useMemo(
@ -215,6 +223,7 @@ export function NodeGraph({ getLinks, dataFrames, nodeLimit }: Props) {
onClick={onEdgeOpen} onClick={onEdgeOpen}
onMouseEnter={setEdgeHover} onMouseEnter={setEdgeHover}
onMouseLeave={clearEdgeHover} onMouseLeave={clearEdgeHover}
svgIdNamespace={svgIdNamespace}
/> />
)} )}
<Nodes <Nodes
@ -332,6 +341,7 @@ interface EdgesProps {
edges: EdgeDatum[]; edges: EdgeDatum[];
nodeHoveringId?: string; nodeHoveringId?: string;
edgeHoveringId?: string; edgeHoveringId?: string;
svgIdNamespace: string;
onClick: (event: MouseEvent<SVGElement>, link: EdgeDatum) => void; onClick: (event: MouseEvent<SVGElement>, link: EdgeDatum) => void;
onMouseEnter: (id: string) => void; onMouseEnter: (id: string) => void;
onMouseLeave: (id: string) => void; onMouseLeave: (id: string) => void;
@ -351,6 +361,7 @@ const Edges = memo(function Edges(props: EdgesProps) {
onClick={props.onClick} onClick={props.onClick}
onMouseEnter={props.onMouseEnter} onMouseEnter={props.onMouseEnter}
onMouseLeave={props.onMouseLeave} onMouseLeave={props.onMouseLeave}
svgIdNamespace={props.svgIdNamespace}
/> />
))} ))}
</> </>

@ -1,5 +1,5 @@
import memoizeOne from 'memoize-one'; import memoizeOne from 'memoize-one';
import React from 'react'; import React, { useId } from 'react';
import { PanelProps } from '@grafana/data'; import { PanelProps } from '@grafana/data';
@ -11,6 +11,8 @@ import { getNodeGraphDataFrames } from './utils';
export const NodeGraphPanel = ({ width, height, data, options }: PanelProps<NodeGraphOptions>) => { export const NodeGraphPanel = ({ width, height, data, options }: PanelProps<NodeGraphOptions>) => {
const getLinks = useLinks(data.timeRange); const getLinks = useLinks(data.timeRange);
const panelId = useId();
if (!data || !data.series.length) { if (!data || !data.series.length) {
return ( return (
<div className="panel-empty"> <div className="panel-empty">
@ -22,7 +24,11 @@ export const NodeGraphPanel = ({ width, height, data, options }: PanelProps<Node
const memoizedGetNodeGraphDataFrames = memoizeOne(getNodeGraphDataFrames); const memoizedGetNodeGraphDataFrames = memoizeOne(getNodeGraphDataFrames);
return ( return (
<div style={{ width, height }}> <div style={{ width, height }}>
<NodeGraph dataFrames={memoizedGetNodeGraphDataFrames(data.series, options)} getLinks={getLinks} /> <NodeGraph
dataFrames={memoizedGetNodeGraphDataFrames(data.series, options)}
getLinks={getLinks}
panelId={panelId}
/>
</div> </div>
); );
}; };

Loading…
Cancel
Save