diff --git a/public/app/plugins/panel/flamegraph/components/FlameGraph/FlameGraph.tsx b/public/app/plugins/panel/flamegraph/components/FlameGraph/FlameGraph.tsx index 6b4354c5005..092ffeab2a9 100644 --- a/public/app/plugins/panel/flamegraph/components/FlameGraph/FlameGraph.tsx +++ b/public/app/plugins/panel/flamegraph/components/FlameGraph/FlameGraph.tsx @@ -18,7 +18,7 @@ // THIS SOFTWARE. import { css } from '@emotion/css'; import uFuzzy from '@leeoniya/ufuzzy'; -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { useMeasure } from 'react-use'; import { CoreApp, createTheme, DataFrame, FieldType, getDisplayProcessor } from '@grafana/data'; @@ -113,67 +113,62 @@ const FlameGraph = ({ return foundLabels; }, [ufuzzy, search, uniqueLabels]); - const render = useCallback( - (pixelsPerTick: number) => { - if (!levels.length) { - return; + useEffect(() => { + if (!levels.length) { + return; + } + const pixelsPerTick = (wrapperWidth * window.devicePixelRatio) / totalTicks / (rangeMax - rangeMin); + const ctx = graphRef.current?.getContext('2d')!; + const graph = graphRef.current!; + + const height = PIXELS_PER_LEVEL * levels.length; + graph.width = Math.round(wrapperWidth * window.devicePixelRatio); + graph.height = Math.round(height * window.devicePixelRatio); + graph.style.width = `${wrapperWidth}px`; + graph.style.height = `${height}px`; + + ctx.textBaseline = 'middle'; + ctx.font = 12 * window.devicePixelRatio + 'px monospace'; + ctx.strokeStyle = 'white'; + + const processor = getDisplayProcessor({ + field: valueField, + theme: createTheme() /* theme does not matter for us here */, + }); + + for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) { + const level = levels[levelIndex]; + // Get all the dimensions of the rectangles for the level. We do this by level instead of per rectangle, because + // sometimes we collapse multiple bars into single rect. + const dimensions = getRectDimensionsForLevel( + level, + levelIndex, + totalTicks, + rangeMin, + pixelsPerTick, + processor, + getLabelValue + ); + for (const rect of dimensions) { + // Render each rectangle based on the computed dimensions + renderRect(ctx, rect, totalTicks, rangeMin, rangeMax, search, levelIndex, topLevelIndex, foundLabels); } - const ctx = graphRef.current?.getContext('2d')!; - const graph = graphRef.current!; - - const height = PIXELS_PER_LEVEL * levels.length; - graph.width = Math.round(wrapperWidth * window.devicePixelRatio); - graph.height = Math.round(height * window.devicePixelRatio); - graph.style.width = `${wrapperWidth}px`; - graph.style.height = `${height}px`; - - ctx.textBaseline = 'middle'; - ctx.font = 12 * window.devicePixelRatio + 'px monospace'; - ctx.strokeStyle = 'white'; - - const processor = getDisplayProcessor({ - field: valueField, - theme: createTheme() /* theme does not matter for us here */, - }); - - for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) { - const level = levels[levelIndex]; - // Get all the dimensions of the rectangles for the level. We do this by level instead of per rectangle, because - // sometimes we collapse multiple bars into single rect. - const dimensions = getRectDimensionsForLevel( - level, - levelIndex, - totalTicks, - rangeMin, - pixelsPerTick, - processor, - getLabelValue - ); - for (const rect of dimensions) { - // Render each rectangle based on the computed dimensions - renderRect(ctx, rect, totalTicks, rangeMin, rangeMax, search, levelIndex, topLevelIndex, foundLabels); - } - } - }, - [ - levels, - wrapperWidth, - valueField, - totalTicks, - rangeMin, - rangeMax, - search, - topLevelIndex, - foundLabels, - getLabelValue, - ] - ); + } + }, [ + levels, + wrapperWidth, + valueField, + totalTicks, + rangeMin, + rangeMax, + search, + topLevelIndex, + foundLabels, + getLabelValue, + ]); useEffect(() => { if (graphRef.current) { - const pixelsPerTick = (wrapperWidth * window.devicePixelRatio) / totalTicks / (rangeMax - rangeMin); - render(pixelsPerTick); - graphRef.current.onclick = (e) => { setTooltipData(undefined); const pixelsPerTick = graphRef.current!.clientWidth / totalTicks / (rangeMax - rangeMin); @@ -222,7 +217,6 @@ const FlameGraph = ({ }; } }, [ - render, levels, rangeMin, rangeMax, diff --git a/public/app/plugins/panel/flamegraph/components/FlameGraphContainer.tsx b/public/app/plugins/panel/flamegraph/components/FlameGraphContainer.tsx index 0fc5aaef14a..d2ce0f3d3e2 100644 --- a/public/app/plugins/panel/flamegraph/components/FlameGraphContainer.tsx +++ b/public/app/plugins/panel/flamegraph/components/FlameGraphContainer.tsx @@ -2,8 +2,8 @@ import { css } from '@emotion/css'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useMeasure } from 'react-use'; -import { DataFrame, DataFrameView, CoreApp, getEnumDisplayProcessor, createTheme } from '@grafana/data'; -import { useStyles2 } from '@grafana/ui'; +import { DataFrame, DataFrameView, CoreApp, getEnumDisplayProcessor } from '@grafana/data'; +import { useStyles2, useTheme2 } from '@grafana/ui'; import { MIN_WIDTH_TO_SHOW_BOTH_TOPTABLE_AND_FLAMEGRAPH, PIXELS_PER_LEVEL } from '../constants'; @@ -33,6 +33,8 @@ const FlameGraphContainer = (props: Props) => { const labelField = props.data?.fields.find((f) => f.name === 'label'); + const theme = useTheme2(); + // Label can actually be an enum field so depending on that we have to access it through display processor. This is // both a backward compatibility but also to allow using a simple dataFrame without enum config. This would allow // users to use this panel with correct query from data sources that do not return profiles natively. @@ -40,12 +42,12 @@ const FlameGraphContainer = (props: Props) => { (label: string | number) => { const enumConfig = labelField?.config?.type?.enum; if (enumConfig) { - return getEnumDisplayProcessor(createTheme(), enumConfig)(label).text; + return getEnumDisplayProcessor(theme, enumConfig)(label).text; } else { return label.toString(); } }, - [labelField] + [labelField, theme] ); // Transform dataFrame with nested set format to array of levels. Each level contains all the bars for a particular diff --git a/public/app/plugins/panel/flamegraph/components/TopTable/FlameGraphTopTableContainer.tsx b/public/app/plugins/panel/flamegraph/components/TopTable/FlameGraphTopTableContainer.tsx index 7a5eaffda89..08c04d317cb 100644 --- a/public/app/plugins/panel/flamegraph/components/TopTable/FlameGraphTopTableContainer.tsx +++ b/public/app/plugins/panel/flamegraph/components/TopTable/FlameGraphTopTableContainer.tsx @@ -2,8 +2,8 @@ import { css } from '@emotion/css'; import React, { useCallback, useEffect, useState } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { CoreApp, createTheme, DataFrame, Field, FieldType, getDisplayProcessor } from '@grafana/data'; -import { useStyles2 } from '@grafana/ui'; +import { CoreApp, DataFrame, Field, FieldType, getDisplayProcessor } from '@grafana/data'; +import { useStyles2, useTheme2 } from '@grafana/ui'; import { PIXELS_PER_LEVEL } from '../../constants'; import { SampleUnit, SelectedView, TableData, TopTableData } from '../types'; @@ -38,6 +38,7 @@ const FlameGraphTopTableContainer = ({ getLabelValue, }: Props) => { const styles = useStyles2(() => getStyles(selectedView, app)); + const theme = useTheme2(); const [topTable, setTopTable] = useState(); const valueField = data.fields.find((f) => f.name === 'value') ?? data.fields.find((f) => f.type === FieldType.number); @@ -67,26 +68,29 @@ const FlameGraphTopTableContainer = ({ return table; }, [getLabelValue, selfField, valueField, labelsField]); - const getTopTableData = (field: Field, value: number) => { - const processor = getDisplayProcessor({ field, theme: createTheme() /* theme does not matter for us here */ }); - const displayValue = processor(value); - let unitValue = displayValue.text + displayValue.suffix; + const getTopTableData = useCallback( + (field: Field, value: number) => { + const processor = getDisplayProcessor({ field, theme }); + const displayValue = processor(value); + let unitValue = displayValue.text + displayValue.suffix; - switch (field.config.unit) { - case SampleUnit.Bytes: - break; - case SampleUnit.Nanoseconds: - break; - default: - if (!displayValue.suffix) { - // Makes sure we don't show 123undefined or something like that if suffix isn't defined - unitValue = displayValue.text; - } - break; - } + switch (field.config.unit) { + case SampleUnit.Bytes: + break; + case SampleUnit.Nanoseconds: + break; + default: + if (!displayValue.suffix) { + // Makes sure we don't show 123undefined or something like that if suffix isn't defined + unitValue = displayValue.text; + } + break; + } - return unitValue; - }; + return unitValue; + }, + [theme] + ); useEffect(() => { const table = sortLevelsIntoTable(); @@ -104,7 +108,7 @@ const FlameGraphTopTableContainer = ({ } setTopTable(topTable); - }, [data.fields, selfField, sortLevelsIntoTable, valueField]); + }, [data.fields, selfField, sortLevelsIntoTable, valueField, getTopTableData]); return ( <>