NodeGraph: Fix empty state to display 'No Data' message (#43633)

* Fix "0" when no nodes present in NodeGraph

* Show no data message in NodeGraph

* Fix tests
This commit is contained in:
Connor Lindsey 2022-01-04 08:45:00 -07:00 committed by GitHub
parent c302552714
commit 2aad6e57c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 48 deletions

View File

@ -14,12 +14,14 @@ jest.mock('react-use/lib/useMeasure', () => {
}); });
describe('NodeGraph', () => { describe('NodeGraph', () => {
it('doesnt fail without any data', async () => { it('shows no data message without any data', async () => {
render(<NodeGraph dataFrames={[]} getLinks={() => []} />); render(<NodeGraph dataFrames={[]} getLinks={() => []} />);
await screen.findByText('No data');
}); });
it('can zoom in and out', async () => { it('can zoom in and out', async () => {
render(<NodeGraph dataFrames={[]} getLinks={() => []} />); render(<NodeGraph dataFrames={[makeNodesDataFrame(2), makeEdgesDataFrame([[0, 1]])]} getLinks={() => []} />);
const zoomIn = await screen.findByTitle(/Zoom in/); const zoomIn = await screen.findByTitle(/Zoom in/);
const zoomOut = await screen.findByTitle(/Zoom out/); const zoomOut = await screen.findByTitle(/Zoom out/);

View File

@ -44,6 +44,15 @@ const getStyles = (theme: GrafanaTheme2) => ({
user-select: none; user-select: none;
`, `,
noDataMsg: css`
height: 100%;
width: 100%;
display: grid;
place-items: center;
font-size: ${theme.typography.h4.fontSize};
color: ${theme.colors.text.secondary};
`,
mainGroup: css` mainGroup: css`
label: mainGroup; label: mainGroup;
will-change: transform; will-change: transform;
@ -67,6 +76,9 @@ const getStyles = (theme: GrafanaTheme2) => ({
padding-bottom: 5px; padding-bottom: 5px;
margin-right: 10px; margin-right: 10px;
`, `,
viewControlsWrapper: css`
margin-left: auto;
`,
alert: css` alert: css`
label: alert; label: alert;
padding: 5px 8px; padding: 5px 8px;
@ -177,42 +189,46 @@ export function NodeGraph({ getLinks, dataFrames, nodeLimit }: Props) {
</div> </div>
) : null} ) : null}
<svg {dataFrames.length ? (
ref={panRef} <svg
viewBox={`${-(width / 2)} ${-(height / 2)} ${width} ${height}`} ref={panRef}
className={cx(styles.svg, isPanning && styles.svgPanning)} viewBox={`${-(width / 2)} ${-(height / 2)} ${width} ${height}`}
> className={cx(styles.svg, isPanning && styles.svgPanning)}
<g
className={styles.mainGroup}
style={{ transform: `scale(${scale}) translate(${Math.floor(position.x)}px, ${Math.floor(position.y)}px)` }}
> >
<EdgeArrowMarker /> <g
{!config.gridLayout && ( className={styles.mainGroup}
<Edges style={{ transform: `scale(${scale}) translate(${Math.floor(position.x)}px, ${Math.floor(position.y)}px)` }}
edges={edges} >
nodeHoveringId={nodeHover} <EdgeArrowMarker />
edgeHoveringId={edgeHover} {!config.gridLayout && (
onClick={onEdgeOpen} <Edges
onMouseEnter={setEdgeHover} edges={edges}
onMouseLeave={clearEdgeHover} nodeHoveringId={nodeHover}
edgeHoveringId={edgeHover}
onClick={onEdgeOpen}
onMouseEnter={setEdgeHover}
onMouseLeave={clearEdgeHover}
/>
)}
<Nodes
nodes={nodes}
onMouseEnter={setNodeHover}
onMouseLeave={clearNodeHover}
onClick={onNodeOpen}
hoveringId={nodeHover || highlightId}
/> />
)}
<Nodes
nodes={nodes}
onMouseEnter={setNodeHover}
onMouseLeave={clearNodeHover}
onClick={onNodeOpen}
hoveringId={nodeHover || highlightId}
/>
<Markers markers={markers || []} onClick={setFocused} /> <Markers markers={markers || []} onClick={setFocused} />
{/*We split the labels from edges so that they are shown on top of everything else*/} {/*We split the labels from edges so that they are shown on top of everything else*/}
{!config.gridLayout && <EdgeLabels edges={edges} nodeHoveringId={nodeHover} edgeHoveringId={edgeHover} />} {!config.gridLayout && <EdgeLabels edges={edges} nodeHoveringId={nodeHover} edgeHoveringId={edgeHover} />}
</g> </g>
</svg> </svg>
) : (
<div className={styles.noDataMsg}>No data</div>
)}
<div className={styles.viewControls}> <div className={styles.viewControls}>
{nodes.length && ( {nodes.length ? (
<div className={styles.legend}> <div className={styles.legend}>
<Legend <Legend
sortable={config.gridLayout} sortable={config.gridLayout}
@ -226,22 +242,24 @@ export function NodeGraph({ getLinks, dataFrames, nodeLimit }: Props) {
}} }}
/> />
</div> </div>
)} ) : null}
<ViewControls<Config> <div className={styles.viewControlsWrapper}>
config={config} <ViewControls<Config>
onConfigChange={(cfg) => { config={config}
if (cfg.gridLayout !== config.gridLayout) { onConfigChange={(cfg) => {
setFocusedNodeId(undefined); if (cfg.gridLayout !== config.gridLayout) {
} setFocusedNodeId(undefined);
setConfig(cfg); }
}} setConfig(cfg);
onMinus={onStepDown} }}
onPlus={onStepUp} onMinus={onStepDown}
scale={scale} onPlus={onStepUp}
disableZoomIn={isMaxZoom} scale={scale}
disableZoomOut={isMinZoom} disableZoomIn={isMaxZoom}
/> disableZoomOut={isMinZoom}
/>
</div>
</div> </div>
{hiddenNodesCount > 0 && ( {hiddenNodesCount > 0 && (