mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Flamegraph: Fix wrong positioning of tooltip in dashboards (#71396)
This commit is contained in:
parent
96ff95665f
commit
dd41c7c262
@ -83,7 +83,6 @@ const FlameGraph = ({
|
|||||||
|
|
||||||
const [sizeRef, { width: wrapperWidth }] = useMeasure<HTMLDivElement>();
|
const [sizeRef, { width: wrapperWidth }] = useMeasure<HTMLDivElement>();
|
||||||
const graphRef = useRef<HTMLCanvasElement>(null);
|
const graphRef = useRef<HTMLCanvasElement>(null);
|
||||||
const tooltipRef = useRef<HTMLDivElement>(null);
|
|
||||||
const [tooltipItem, setTooltipItem] = useState<LevelItem>();
|
const [tooltipItem, setTooltipItem] = useState<LevelItem>();
|
||||||
|
|
||||||
const [clickedItemData, setClickedItemData] = useState<ClickedItemData>();
|
const [clickedItemData, setClickedItemData] = useState<ClickedItemData>();
|
||||||
@ -132,10 +131,12 @@ const FlameGraph = ({
|
|||||||
[data, rangeMin, rangeMax, totalTicks, levels]
|
[data, rangeMin, rangeMax, totalTicks, levels]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [mousePosition, setMousePosition] = useState<{ x: number; y: number }>();
|
||||||
const onGraphMouseMove = useCallback(
|
const onGraphMouseMove = useCallback(
|
||||||
(e: ReactMouseEvent<HTMLCanvasElement>) => {
|
(e: ReactMouseEvent<HTMLCanvasElement>) => {
|
||||||
if (tooltipRef.current && clickedItemData === undefined) {
|
if (clickedItemData === undefined) {
|
||||||
setTooltipItem(undefined);
|
setTooltipItem(undefined);
|
||||||
|
setMousePosition(undefined);
|
||||||
const pixelsPerTick = graphRef.current!.clientWidth / totalTicks / (rangeMax - rangeMin);
|
const pixelsPerTick = graphRef.current!.clientWidth / totalTicks / (rangeMax - rangeMin);
|
||||||
const { levelIndex, barIndex } = convertPixelCoordinatesToBarCoordinates(
|
const { levelIndex, barIndex } = convertPixelCoordinatesToBarCoordinates(
|
||||||
{ x: e.nativeEvent.offsetX, y: e.nativeEvent.offsetY },
|
{ x: e.nativeEvent.offsetX, y: e.nativeEvent.offsetY },
|
||||||
@ -146,30 +147,12 @@ const FlameGraph = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (barIndex !== -1 && !isNaN(levelIndex) && !isNaN(barIndex)) {
|
if (barIndex !== -1 && !isNaN(levelIndex) && !isNaN(barIndex)) {
|
||||||
// tooltip has a set number of lines of text so 200 should be good enough (with some buffer) without going
|
setMousePosition({ x: e.clientX, y: e.clientY });
|
||||||
// into measuring rendered sizes
|
|
||||||
if (e.clientY < document.documentElement.clientHeight - 200) {
|
|
||||||
tooltipRef.current.style.top = e.clientY + 'px';
|
|
||||||
tooltipRef.current.style.bottom = 'auto';
|
|
||||||
} else {
|
|
||||||
tooltipRef.current.style.bottom = document.documentElement.clientHeight - e.clientY + 'px';
|
|
||||||
tooltipRef.current.style.top = 'auto';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 400 is max width of the tooltip
|
|
||||||
if (e.clientX < document.documentElement.clientWidth - 400) {
|
|
||||||
tooltipRef.current.style.left = e.clientX + 15 + 'px';
|
|
||||||
tooltipRef.current.style.right = 'auto';
|
|
||||||
} else {
|
|
||||||
tooltipRef.current.style.right = document.documentElement.clientWidth - e.clientX + 15 + 'px';
|
|
||||||
tooltipRef.current.style.left = 'auto';
|
|
||||||
}
|
|
||||||
|
|
||||||
setTooltipItem(levels[levelIndex][barIndex]);
|
setTooltipItem(levels[levelIndex][barIndex]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[rangeMin, rangeMax, totalTicks, clickedItemData, levels]
|
[rangeMin, rangeMax, totalTicks, clickedItemData, levels, setMousePosition]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onGraphMouseLeave = useCallback(() => {
|
const onGraphMouseLeave = useCallback(() => {
|
||||||
@ -224,7 +207,7 @@ const FlameGraph = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<FlameGraphTooltip tooltipRef={tooltipRef} item={tooltipItem} data={data} totalTicks={totalTicks} />
|
<FlameGraphTooltip position={mousePosition} item={tooltipItem} data={data} totalTicks={totalTicks} />
|
||||||
{clickedItemData && (
|
{clickedItemData && (
|
||||||
<FlameGraphContextMenu
|
<FlameGraphContextMenu
|
||||||
itemData={clickedItemData}
|
itemData={clickedItemData}
|
||||||
|
@ -1,47 +1,48 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { LegacyRef } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { useStyles2 } from '@grafana/ui';
|
import { Portal, useStyles2, VizTooltipContainer } from '@grafana/ui';
|
||||||
|
|
||||||
import { FlameGraphDataContainer, LevelItem } from './dataTransform';
|
import { FlameGraphDataContainer, LevelItem } from './dataTransform';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: FlameGraphDataContainer;
|
data: FlameGraphDataContainer;
|
||||||
totalTicks: number;
|
totalTicks: number;
|
||||||
|
position?: { x: number; y: number };
|
||||||
item?: LevelItem;
|
item?: LevelItem;
|
||||||
tooltipRef?: LegacyRef<HTMLDivElement>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const FlameGraphTooltip = ({ data, tooltipRef, item, totalTicks }: Props) => {
|
const FlameGraphTooltip = ({ data, item, totalTicks, position }: Props) => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
let content = null;
|
if (!(item && position)) {
|
||||||
if (item) {
|
return null;
|
||||||
const tooltipData = getTooltipData(data, item, totalTicks);
|
|
||||||
content = (
|
|
||||||
<div className={styles.tooltipContent}>
|
|
||||||
<p>{data.getLabel(item.itemIndexes[0])}</p>
|
|
||||||
<p className={styles.lastParagraph}>
|
|
||||||
{tooltipData.unitTitle}
|
|
||||||
<br />
|
|
||||||
Total: <b>{tooltipData.unitValue}</b> ({tooltipData.percentValue}%)
|
|
||||||
<br />
|
|
||||||
Self: <b>{tooltipData.unitSelf}</b> ({tooltipData.percentSelf}%)
|
|
||||||
<br />
|
|
||||||
Samples: <b>{tooltipData.samples}</b>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if we don't show tooltip we need this div so the ref is consistently attached. Would need some refactor in
|
const tooltipData = getTooltipData(data, item, totalTicks);
|
||||||
// FlameGraph.tsx to make it work without it.
|
const content = (
|
||||||
return (
|
<div className={styles.tooltipContent}>
|
||||||
<div ref={tooltipRef} className={styles.tooltip}>
|
<p>{data.getLabel(item.itemIndexes[0])}</p>
|
||||||
{content}
|
<p className={styles.lastParagraph}>
|
||||||
|
{tooltipData.unitTitle}
|
||||||
|
<br />
|
||||||
|
Total: <b>{tooltipData.unitValue}</b> ({tooltipData.percentValue}%)
|
||||||
|
<br />
|
||||||
|
Self: <b>{tooltipData.unitSelf}</b> ({tooltipData.percentSelf}%)
|
||||||
|
<br />
|
||||||
|
Samples: <b>{tooltipData.samples}</b>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<VizTooltipContainer position={position} offset={{ x: 15, y: 0 }}>
|
||||||
|
{content}
|
||||||
|
</VizTooltipContainer>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type TooltipData = {
|
type TooltipData = {
|
||||||
@ -87,23 +88,9 @@ export const getTooltipData = (data: FlameGraphDataContainer, item: LevelItem, t
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
tooltip: css`
|
|
||||||
title: tooltip;
|
|
||||||
position: fixed;
|
|
||||||
z-index: ${theme.zIndex.tooltip};
|
|
||||||
`,
|
|
||||||
tooltipContent: css`
|
tooltipContent: css`
|
||||||
title: tooltipContent;
|
title: tooltipContent;
|
||||||
background-color: ${theme.components.tooltip.background};
|
|
||||||
border-radius: ${theme.shape.radius.default};
|
|
||||||
border: 1px solid ${theme.components.tooltip.background};
|
|
||||||
box-shadow: ${theme.shadows.z2};
|
|
||||||
color: ${theme.components.tooltip.text};
|
|
||||||
font-size: ${theme.typography.bodySmall.fontSize};
|
font-size: ${theme.typography.bodySmall.fontSize};
|
||||||
padding: ${theme.spacing(0.5, 1)};
|
|
||||||
transition: opacity 0.3s;
|
|
||||||
max-width: 400px;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
`,
|
`,
|
||||||
lastParagraph: css`
|
lastParagraph: css`
|
||||||
title: lastParagraph;
|
title: lastParagraph;
|
||||||
|
Loading…
Reference in New Issue
Block a user