mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Flame graph: Add metadata above flame graph (#61921)
* Remove percentTitle * Flame graph metadata * Remove comment * Update test * Update metadata
This commit is contained in:
parent
f19b07c0bc
commit
780f43a33d
@ -21,6 +21,7 @@ jest.mock('react-use', () => ({
|
|||||||
describe('FlameGraph', () => {
|
describe('FlameGraph', () => {
|
||||||
const FlameGraphWithProps = () => {
|
const FlameGraphWithProps = () => {
|
||||||
const [topLevelIndex, setTopLevelIndex] = useState(0);
|
const [topLevelIndex, setTopLevelIndex] = useState(0);
|
||||||
|
const [selectedBarIndex, setSelectedBarIndex] = useState(0);
|
||||||
const [rangeMin, setRangeMin] = useState(0);
|
const [rangeMin, setRangeMin] = useState(0);
|
||||||
const [rangeMax, setRangeMax] = useState(1);
|
const [rangeMax, setRangeMax] = useState(1);
|
||||||
const [search] = useState('');
|
const [search] = useState('');
|
||||||
@ -36,10 +37,12 @@ describe('FlameGraph', () => {
|
|||||||
app={CoreApp.Explore}
|
app={CoreApp.Explore}
|
||||||
levels={levels}
|
levels={levels}
|
||||||
topLevelIndex={topLevelIndex}
|
topLevelIndex={topLevelIndex}
|
||||||
|
selectedBarIndex={selectedBarIndex}
|
||||||
rangeMin={rangeMin}
|
rangeMin={rangeMin}
|
||||||
rangeMax={rangeMax}
|
rangeMax={rangeMax}
|
||||||
search={search}
|
search={search}
|
||||||
setTopLevelIndex={setTopLevelIndex}
|
setTopLevelIndex={setTopLevelIndex}
|
||||||
|
setSelectedBarIndex={setSelectedBarIndex}
|
||||||
setRangeMin={setRangeMin}
|
setRangeMin={setRangeMin}
|
||||||
setRangeMax={setRangeMax}
|
setRangeMax={setRangeMax}
|
||||||
selectedView={selectedView}
|
selectedView={selectedView}
|
||||||
@ -59,4 +62,9 @@ describe('FlameGraph', () => {
|
|||||||
const calls = ctx!.__getDrawCalls();
|
const calls = ctx!.__getDrawCalls();
|
||||||
expect(calls).toMatchSnapshot();
|
expect(calls).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render metadata', async () => {
|
||||||
|
render(<FlameGraphWithProps />);
|
||||||
|
expect(screen.getByText('16.5 Bil (100%) of 16,460,000,000 total samples (Count)')).toBeDefined();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,6 +26,7 @@ import { CoreApp, createTheme, DataFrame, FieldType, getDisplayProcessor } from
|
|||||||
import { PIXELS_PER_LEVEL } from '../../constants';
|
import { PIXELS_PER_LEVEL } from '../../constants';
|
||||||
import { TooltipData, SelectedView } from '../types';
|
import { TooltipData, SelectedView } from '../types';
|
||||||
|
|
||||||
|
import FlameGraphMetadata from './FlameGraphMetadata';
|
||||||
import FlameGraphTooltip, { getTooltipData } from './FlameGraphTooltip';
|
import FlameGraphTooltip, { getTooltipData } from './FlameGraphTooltip';
|
||||||
import { ItemWithStart } from './dataTransform';
|
import { ItemWithStart } from './dataTransform';
|
||||||
import { getBarX, getRectDimensionsForLevel, renderRect } from './rendering';
|
import { getBarX, getRectDimensionsForLevel, renderRect } from './rendering';
|
||||||
@ -36,10 +37,12 @@ type Props = {
|
|||||||
flameGraphHeight?: number;
|
flameGraphHeight?: number;
|
||||||
levels: ItemWithStart[][];
|
levels: ItemWithStart[][];
|
||||||
topLevelIndex: number;
|
topLevelIndex: number;
|
||||||
|
selectedBarIndex: number;
|
||||||
rangeMin: number;
|
rangeMin: number;
|
||||||
rangeMax: number;
|
rangeMax: number;
|
||||||
search: string;
|
search: string;
|
||||||
setTopLevelIndex: (level: number) => void;
|
setTopLevelIndex: (level: number) => void;
|
||||||
|
setSelectedBarIndex: (bar: number) => void;
|
||||||
setRangeMin: (range: number) => void;
|
setRangeMin: (range: number) => void;
|
||||||
setRangeMax: (range: number) => void;
|
setRangeMax: (range: number) => void;
|
||||||
selectedView: SelectedView;
|
selectedView: SelectedView;
|
||||||
@ -52,10 +55,12 @@ const FlameGraph = ({
|
|||||||
flameGraphHeight,
|
flameGraphHeight,
|
||||||
levels,
|
levels,
|
||||||
topLevelIndex,
|
topLevelIndex,
|
||||||
|
selectedBarIndex,
|
||||||
rangeMin,
|
rangeMin,
|
||||||
rangeMax,
|
rangeMax,
|
||||||
search,
|
search,
|
||||||
setTopLevelIndex,
|
setTopLevelIndex,
|
||||||
|
setSelectedBarIndex,
|
||||||
setRangeMin,
|
setRangeMin,
|
||||||
setRangeMax,
|
setRangeMax,
|
||||||
selectedView,
|
selectedView,
|
||||||
@ -140,6 +145,7 @@ const FlameGraph = ({
|
|||||||
|
|
||||||
if (barIndex !== -1 && !isNaN(levelIndex) && !isNaN(barIndex)) {
|
if (barIndex !== -1 && !isNaN(levelIndex) && !isNaN(barIndex)) {
|
||||||
setTopLevelIndex(levelIndex);
|
setTopLevelIndex(levelIndex);
|
||||||
|
setSelectedBarIndex(barIndex);
|
||||||
setRangeMin(levels[levelIndex][barIndex].start / totalTicks);
|
setRangeMin(levels[levelIndex][barIndex].start / totalTicks);
|
||||||
setRangeMax((levels[levelIndex][barIndex].start + levels[levelIndex][barIndex].value) / totalTicks);
|
setRangeMax((levels[levelIndex][barIndex].start + levels[levelIndex][barIndex].value) / totalTicks);
|
||||||
}
|
}
|
||||||
@ -181,10 +187,18 @@ const FlameGraph = ({
|
|||||||
setRangeMax,
|
setRangeMax,
|
||||||
selectedView,
|
selectedView,
|
||||||
valueField,
|
valueField,
|
||||||
|
setSelectedBarIndex,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.graph} ref={sizeRef}>
|
<div className={styles.graph} ref={sizeRef}>
|
||||||
|
<FlameGraphMetadata
|
||||||
|
levels={levels}
|
||||||
|
topLevelIndex={topLevelIndex}
|
||||||
|
selectedBarIndex={selectedBarIndex}
|
||||||
|
valueField={valueField}
|
||||||
|
totalTicks={totalTicks}
|
||||||
|
/>
|
||||||
<canvas ref={graphRef} data-testid="flameGraph" />
|
<canvas ref={graphRef} data-testid="flameGraph" />
|
||||||
<FlameGraphTooltip tooltipRef={tooltipRef} tooltipData={tooltipData!} showTooltip={showTooltip} />
|
<FlameGraphTooltip tooltipRef={tooltipRef} tooltipData={tooltipData!} showTooltip={showTooltip} />
|
||||||
</div>
|
</div>
|
||||||
@ -198,8 +212,8 @@ const getStyles = (selectedView: SelectedView, app: CoreApp, flameGraphHeight: n
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
width: ${selectedView === SelectedView.FlameGraph ? '100%' : '50%'};
|
width: ${selectedView === SelectedView.FlameGraph ? '100%' : '50%'};
|
||||||
${app !== CoreApp.Explore
|
${app !== CoreApp.Explore
|
||||||
? `height: calc(${flameGraphHeight}px - 44px)`
|
? `height: calc(${flameGraphHeight}px - 50px)`
|
||||||
: ''}; // 44px to adjust for header pushing content down
|
: ''}; // 50px to adjust for header pushing content down
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import { ArrayVector, Field, FieldType } from '@grafana/data';
|
||||||
|
|
||||||
|
import { getMetadata } from './FlameGraphMetadata';
|
||||||
|
|
||||||
|
describe('should get metadata correctly', () => {
|
||||||
|
it('for bytes', () => {
|
||||||
|
const metadata = getMetadata(makeField('bytes'), 1_624_078_250, 8_624_078_250);
|
||||||
|
expect(metadata).toEqual({
|
||||||
|
percentValue: 18.83,
|
||||||
|
unitTitle: 'RAM',
|
||||||
|
unitValue: '1.51 GiB',
|
||||||
|
samples: '8,624,078,250',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with default unit', () => {
|
||||||
|
const metadata = getMetadata(makeField('none'), 1_624_078_250, 8_624_078_250);
|
||||||
|
expect(metadata).toEqual({
|
||||||
|
percentValue: 18.83,
|
||||||
|
unitTitle: 'Count',
|
||||||
|
unitValue: '1624078250',
|
||||||
|
samples: '8,624,078,250',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('without unit', () => {
|
||||||
|
const metadata = getMetadata(
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
type: FieldType.number,
|
||||||
|
values: new ArrayVector(),
|
||||||
|
config: {},
|
||||||
|
},
|
||||||
|
1_624_078_250,
|
||||||
|
8_624_078_250
|
||||||
|
);
|
||||||
|
expect(metadata).toEqual({
|
||||||
|
percentValue: 18.83,
|
||||||
|
unitTitle: 'Count',
|
||||||
|
unitValue: '1624078250',
|
||||||
|
samples: '8,624,078,250',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for objects', () => {
|
||||||
|
const metadata = getMetadata(makeField('short'), 1_624_078_250, 8_624_078_250);
|
||||||
|
expect(metadata).toEqual({
|
||||||
|
percentValue: 18.83,
|
||||||
|
unitTitle: 'Count',
|
||||||
|
unitValue: '1.62 Bil',
|
||||||
|
samples: '8,624,078,250',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('for nanoseconds', () => {
|
||||||
|
const metadata = getMetadata(makeField('ns'), 1_624_078_250, 8_624_078_250);
|
||||||
|
expect(metadata).toEqual({
|
||||||
|
percentValue: 18.83,
|
||||||
|
unitTitle: 'Time',
|
||||||
|
unitValue: '1.62 s',
|
||||||
|
samples: '8,624,078,250',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeField(unit: string): Field {
|
||||||
|
return {
|
||||||
|
name: 'test',
|
||||||
|
type: FieldType.number,
|
||||||
|
config: {
|
||||||
|
unit,
|
||||||
|
},
|
||||||
|
values: new ArrayVector(),
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
import { css } from '@emotion/css';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { createTheme, Field, getDisplayProcessor, Vector } from '@grafana/data';
|
||||||
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
|
||||||
|
import { Metadata, SampleUnit } from '../types';
|
||||||
|
|
||||||
|
import { ItemWithStart } from './dataTransform';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
levels: ItemWithStart[][];
|
||||||
|
topLevelIndex: number;
|
||||||
|
selectedBarIndex: number;
|
||||||
|
valueField: Field<number, Vector<number>>;
|
||||||
|
totalTicks: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const FlameGraphMetadata = React.memo(({ levels, topLevelIndex, selectedBarIndex, valueField, totalTicks }: Props) => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
|
if (levels[topLevelIndex] && levels[topLevelIndex][selectedBarIndex]) {
|
||||||
|
const bar = levels[topLevelIndex][selectedBarIndex];
|
||||||
|
const metadata = getMetadata(valueField, bar.value, totalTicks);
|
||||||
|
const metadataText = `${metadata?.unitValue} (${metadata?.percentValue}%) of ${metadata?.samples} total samples (${metadata?.unitTitle})`;
|
||||||
|
return <>{<div className={styles.metadata}>{metadataText}</div>}</>;
|
||||||
|
}
|
||||||
|
return <></>;
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getMetadata = (field: Field, value: number, totalTicks: number): Metadata => {
|
||||||
|
let unitTitle;
|
||||||
|
const processor = getDisplayProcessor({ field, theme: createTheme() /* theme does not matter for us here */ });
|
||||||
|
const displayValue = processor(value);
|
||||||
|
const percentValue = Math.round(10000 * (value / totalTicks)) / 100;
|
||||||
|
let unitValue = displayValue.text + displayValue.suffix;
|
||||||
|
|
||||||
|
switch (field.config.unit) {
|
||||||
|
case SampleUnit.Bytes:
|
||||||
|
unitTitle = 'RAM';
|
||||||
|
break;
|
||||||
|
case SampleUnit.Nanoseconds:
|
||||||
|
unitTitle = 'Time';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unitTitle = 'Count';
|
||||||
|
if (!displayValue.suffix) {
|
||||||
|
// Makes sure we don't show 123undefined or something like that if suffix isn't defined
|
||||||
|
unitValue = displayValue.text;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
percentValue,
|
||||||
|
unitTitle,
|
||||||
|
unitValue,
|
||||||
|
samples: totalTicks.toLocaleString(),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
FlameGraphMetadata.displayName = 'FlameGraphMetadata';
|
||||||
|
|
||||||
|
const getStyles = () => ({
|
||||||
|
metadata: css`
|
||||||
|
margin: 8px 0;
|
||||||
|
text-align: center;
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FlameGraphMetadata;
|
@ -8,7 +8,6 @@ describe('should get tooltip data correctly', () => {
|
|||||||
expect(tooltipData).toEqual({
|
expect(tooltipData).toEqual({
|
||||||
name: 'total',
|
name: 'total',
|
||||||
percentSelf: 0.01,
|
percentSelf: 0.01,
|
||||||
percentTitle: '% of total',
|
|
||||||
percentValue: 100,
|
percentValue: 100,
|
||||||
unitTitle: 'RAM',
|
unitTitle: 'RAM',
|
||||||
unitSelf: '955 KiB',
|
unitSelf: '955 KiB',
|
||||||
@ -17,12 +16,11 @@ describe('should get tooltip data correctly', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with none unit', () => {
|
it('with default unit', () => {
|
||||||
const tooltipData = getTooltipData(makeField('none'), 'total', 8_624_078_250, 978_250, 8_624_078_250);
|
const tooltipData = getTooltipData(makeField('none'), 'total', 8_624_078_250, 978_250, 8_624_078_250);
|
||||||
expect(tooltipData).toEqual({
|
expect(tooltipData).toEqual({
|
||||||
name: 'total',
|
name: 'total',
|
||||||
percentSelf: 0.01,
|
percentSelf: 0.01,
|
||||||
percentTitle: '% of total',
|
|
||||||
percentValue: 100,
|
percentValue: 100,
|
||||||
unitSelf: '978250',
|
unitSelf: '978250',
|
||||||
unitTitle: 'Count',
|
unitTitle: 'Count',
|
||||||
@ -47,7 +45,6 @@ describe('should get tooltip data correctly', () => {
|
|||||||
expect(tooltipData).toEqual({
|
expect(tooltipData).toEqual({
|
||||||
name: 'total',
|
name: 'total',
|
||||||
percentSelf: 0.01,
|
percentSelf: 0.01,
|
||||||
percentTitle: '% of total',
|
|
||||||
percentValue: 100,
|
percentValue: 100,
|
||||||
unitTitle: 'Count',
|
unitTitle: 'Count',
|
||||||
unitSelf: '978250',
|
unitSelf: '978250',
|
||||||
@ -61,7 +58,6 @@ describe('should get tooltip data correctly', () => {
|
|||||||
expect(tooltipData).toEqual({
|
expect(tooltipData).toEqual({
|
||||||
name: 'total',
|
name: 'total',
|
||||||
percentSelf: 0.01,
|
percentSelf: 0.01,
|
||||||
percentTitle: '% of total',
|
|
||||||
percentValue: 100,
|
percentValue: 100,
|
||||||
unitTitle: 'Count',
|
unitTitle: 'Count',
|
||||||
unitSelf: '978 K',
|
unitSelf: '978 K',
|
||||||
@ -75,7 +71,6 @@ describe('should get tooltip data correctly', () => {
|
|||||||
expect(tooltipData).toEqual({
|
expect(tooltipData).toEqual({
|
||||||
name: 'total',
|
name: 'total',
|
||||||
percentSelf: 0.01,
|
percentSelf: 0.01,
|
||||||
percentTitle: '% of total time',
|
|
||||||
percentValue: 100,
|
percentValue: 100,
|
||||||
unitTitle: 'Time',
|
unitTitle: 'Time',
|
||||||
unitSelf: '978 µs',
|
unitSelf: '978 µs',
|
||||||
|
@ -50,7 +50,6 @@ export const getTooltipData = (
|
|||||||
self: number,
|
self: number,
|
||||||
totalTicks: number
|
totalTicks: number
|
||||||
): TooltipData => {
|
): TooltipData => {
|
||||||
let percentTitle;
|
|
||||||
let unitTitle;
|
let unitTitle;
|
||||||
|
|
||||||
const processor = getDisplayProcessor({ field, theme: createTheme() /* theme does not matter for us here */ });
|
const processor = getDisplayProcessor({ field, theme: createTheme() /* theme does not matter for us here */ });
|
||||||
@ -64,15 +63,12 @@ export const getTooltipData = (
|
|||||||
|
|
||||||
switch (field.config.unit) {
|
switch (field.config.unit) {
|
||||||
case SampleUnit.Bytes:
|
case SampleUnit.Bytes:
|
||||||
percentTitle = '% of total';
|
|
||||||
unitTitle = 'RAM';
|
unitTitle = 'RAM';
|
||||||
break;
|
break;
|
||||||
case SampleUnit.Nanoseconds:
|
case SampleUnit.Nanoseconds:
|
||||||
percentTitle = '% of total time';
|
|
||||||
unitTitle = 'Time';
|
unitTitle = 'Time';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
percentTitle = '% of total';
|
|
||||||
unitTitle = 'Count';
|
unitTitle = 'Count';
|
||||||
if (!displayValue.suffix) {
|
if (!displayValue.suffix) {
|
||||||
// Makes sure we don't show 123undefined or something like that if suffix isn't defined
|
// Makes sure we don't show 123undefined or something like that if suffix isn't defined
|
||||||
@ -87,7 +83,6 @@ export const getTooltipData = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
name: label,
|
name: label,
|
||||||
percentTitle,
|
|
||||||
percentValue,
|
percentValue,
|
||||||
percentSelf,
|
percentSelf,
|
||||||
unitTitle,
|
unitTitle,
|
||||||
|
@ -24,6 +24,7 @@ type Props = {
|
|||||||
|
|
||||||
const FlameGraphContainer = (props: Props) => {
|
const FlameGraphContainer = (props: Props) => {
|
||||||
const [topLevelIndex, setTopLevelIndex] = useState(0);
|
const [topLevelIndex, setTopLevelIndex] = useState(0);
|
||||||
|
const [selectedBarIndex, setSelectedBarIndex] = useState(0);
|
||||||
const [rangeMin, setRangeMin] = useState(0);
|
const [rangeMin, setRangeMin] = useState(0);
|
||||||
const [rangeMax, setRangeMax] = useState(1);
|
const [rangeMax, setRangeMax] = useState(1);
|
||||||
const [search, setSearch] = useState('');
|
const [search, setSearch] = useState('');
|
||||||
@ -56,6 +57,7 @@ const FlameGraphContainer = (props: Props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTopLevelIndex(0);
|
setTopLevelIndex(0);
|
||||||
|
setSelectedBarIndex(0);
|
||||||
setRangeMin(0);
|
setRangeMin(0);
|
||||||
setRangeMax(1);
|
setRangeMax(1);
|
||||||
}, [props.data]);
|
}, [props.data]);
|
||||||
@ -65,6 +67,7 @@ const FlameGraphContainer = (props: Props) => {
|
|||||||
<FlameGraphHeader
|
<FlameGraphHeader
|
||||||
app={props.app}
|
app={props.app}
|
||||||
setTopLevelIndex={setTopLevelIndex}
|
setTopLevelIndex={setTopLevelIndex}
|
||||||
|
setSelectedBarIndex={setSelectedBarIndex}
|
||||||
setRangeMin={setRangeMin}
|
setRangeMin={setRangeMin}
|
||||||
setRangeMax={setRangeMax}
|
setRangeMax={setRangeMax}
|
||||||
search={search}
|
search={search}
|
||||||
@ -83,6 +86,7 @@ const FlameGraphContainer = (props: Props) => {
|
|||||||
search={search}
|
search={search}
|
||||||
setSearch={setSearch}
|
setSearch={setSearch}
|
||||||
setTopLevelIndex={setTopLevelIndex}
|
setTopLevelIndex={setTopLevelIndex}
|
||||||
|
setSelectedBarIndex={setSelectedBarIndex}
|
||||||
setRangeMin={setRangeMin}
|
setRangeMin={setRangeMin}
|
||||||
setRangeMax={setRangeMax}
|
setRangeMax={setRangeMax}
|
||||||
/>
|
/>
|
||||||
@ -95,10 +99,12 @@ const FlameGraphContainer = (props: Props) => {
|
|||||||
flameGraphHeight={props.flameGraphHeight}
|
flameGraphHeight={props.flameGraphHeight}
|
||||||
levels={levels}
|
levels={levels}
|
||||||
topLevelIndex={topLevelIndex}
|
topLevelIndex={topLevelIndex}
|
||||||
|
selectedBarIndex={selectedBarIndex}
|
||||||
rangeMin={rangeMin}
|
rangeMin={rangeMin}
|
||||||
rangeMax={rangeMax}
|
rangeMax={rangeMax}
|
||||||
search={search}
|
search={search}
|
||||||
setTopLevelIndex={setTopLevelIndex}
|
setTopLevelIndex={setTopLevelIndex}
|
||||||
|
setSelectedBarIndex={setSelectedBarIndex}
|
||||||
setRangeMin={setRangeMin}
|
setRangeMin={setRangeMin}
|
||||||
setRangeMax={setRangeMax}
|
setRangeMax={setRangeMax}
|
||||||
selectedView={selectedView}
|
selectedView={selectedView}
|
||||||
|
@ -19,6 +19,7 @@ describe('FlameGraphHeader', () => {
|
|||||||
search={search}
|
search={search}
|
||||||
setSearch={setSearch}
|
setSearch={setSearch}
|
||||||
setTopLevelIndex={jest.fn()}
|
setTopLevelIndex={jest.fn()}
|
||||||
|
setSelectedBarIndex={jest.fn()}
|
||||||
setRangeMin={jest.fn()}
|
setRangeMin={jest.fn()}
|
||||||
setRangeMax={jest.fn()}
|
setRangeMax={jest.fn()}
|
||||||
selectedView={selectedView}
|
selectedView={selectedView}
|
||||||
|
@ -12,6 +12,7 @@ type Props = {
|
|||||||
app: CoreApp;
|
app: CoreApp;
|
||||||
search: string;
|
search: string;
|
||||||
setTopLevelIndex: (level: number) => void;
|
setTopLevelIndex: (level: number) => void;
|
||||||
|
setSelectedBarIndex: (bar: number) => void;
|
||||||
setRangeMin: (range: number) => void;
|
setRangeMin: (range: number) => void;
|
||||||
setRangeMax: (range: number) => void;
|
setRangeMax: (range: number) => void;
|
||||||
setSearch: (search: string) => void;
|
setSearch: (search: string) => void;
|
||||||
@ -24,6 +25,7 @@ const FlameGraphHeader = ({
|
|||||||
app,
|
app,
|
||||||
search,
|
search,
|
||||||
setTopLevelIndex,
|
setTopLevelIndex,
|
||||||
|
setSelectedBarIndex,
|
||||||
setRangeMin,
|
setRangeMin,
|
||||||
setRangeMax,
|
setRangeMax,
|
||||||
setSearch,
|
setSearch,
|
||||||
@ -64,6 +66,7 @@ const FlameGraphHeader = ({
|
|||||||
size={'md'}
|
size={'md'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTopLevelIndex(0);
|
setTopLevelIndex(0);
|
||||||
|
setSelectedBarIndex(0);
|
||||||
setRangeMin(0);
|
setRangeMin(0);
|
||||||
setRangeMax(1);
|
setRangeMax(1);
|
||||||
setSearch('');
|
setSearch('');
|
||||||
|
@ -16,6 +16,7 @@ type Props = {
|
|||||||
search: string;
|
search: string;
|
||||||
setSearch: (search: string) => void;
|
setSearch: (search: string) => void;
|
||||||
setTopLevelIndex: (level: number) => void;
|
setTopLevelIndex: (level: number) => void;
|
||||||
|
setSelectedBarIndex: (bar: number) => void;
|
||||||
setRangeMin: (range: number) => void;
|
setRangeMin: (range: number) => void;
|
||||||
setRangeMax: (range: number) => void;
|
setRangeMax: (range: number) => void;
|
||||||
};
|
};
|
||||||
@ -27,6 +28,7 @@ const FlameGraphTopTable = ({
|
|||||||
search,
|
search,
|
||||||
setSearch,
|
setSearch,
|
||||||
setTopLevelIndex,
|
setTopLevelIndex,
|
||||||
|
setSelectedBarIndex,
|
||||||
setRangeMin,
|
setRangeMin,
|
||||||
setRangeMax,
|
setRangeMax,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
@ -84,18 +86,19 @@ const FlameGraphTopTable = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const rowClicked = useCallback(
|
const rowClicked = useCallback(
|
||||||
(row: string) => {
|
(symbol: string) => {
|
||||||
if (search === row) {
|
if (search === symbol) {
|
||||||
setSearch('');
|
setSearch('');
|
||||||
} else {
|
} else {
|
||||||
setSearch(row);
|
setSearch(symbol);
|
||||||
// Reset selected level in flamegraph when selecting row in top table
|
// Reset selected level in flamegraph when selecting row in top table
|
||||||
setTopLevelIndex(0);
|
setTopLevelIndex(0);
|
||||||
|
setSelectedBarIndex(0);
|
||||||
setRangeMin(0);
|
setRangeMin(0);
|
||||||
setRangeMax(1);
|
setRangeMax(1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[search, setRangeMax, setRangeMin, setSearch, setTopLevelIndex]
|
[search, setRangeMax, setRangeMin, setSearch, setTopLevelIndex, setSelectedBarIndex]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { headerGroups, rows, prepareRow } = useTable(options, useSortBy, useAbsoluteLayout);
|
const { headerGroups, rows, prepareRow } = useTable(options, useSortBy, useAbsoluteLayout);
|
||||||
@ -105,15 +108,15 @@ const FlameGraphTopTable = ({
|
|||||||
let row = rows[index];
|
let row = rows[index];
|
||||||
prepareRow(row);
|
prepareRow(row);
|
||||||
|
|
||||||
const rowValue = row.values[ColumnTypes.Symbol.toLowerCase()];
|
const symbol = row.values[ColumnTypes.Symbol.toLowerCase()];
|
||||||
const classNames = cx(rowValue === search && styles.matchedRow, styles.row);
|
const classNames = cx(symbol === search && styles.matchedRow, styles.row);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...row.getRowProps({ style })}
|
{...row.getRowProps({ style })}
|
||||||
className={classNames}
|
className={classNames}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
rowClicked(rowValue);
|
rowClicked(symbol);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{row.cells.map((cell) => {
|
{row.cells.map((cell) => {
|
||||||
|
@ -27,6 +27,7 @@ describe('FlameGraphTopTableContainer', () => {
|
|||||||
search={search}
|
search={search}
|
||||||
setSearch={setSearch}
|
setSearch={setSearch}
|
||||||
setTopLevelIndex={jest.fn()}
|
setTopLevelIndex={jest.fn()}
|
||||||
|
setSelectedBarIndex={jest.fn()}
|
||||||
setRangeMin={jest.fn()}
|
setRangeMin={jest.fn()}
|
||||||
setRangeMax={jest.fn()}
|
setRangeMax={jest.fn()}
|
||||||
/>
|
/>
|
||||||
|
@ -18,6 +18,7 @@ type Props = {
|
|||||||
search: string;
|
search: string;
|
||||||
setSearch: (search: string) => void;
|
setSearch: (search: string) => void;
|
||||||
setTopLevelIndex: (level: number) => void;
|
setTopLevelIndex: (level: number) => void;
|
||||||
|
setSelectedBarIndex: (bar: number) => void;
|
||||||
setRangeMin: (range: number) => void;
|
setRangeMin: (range: number) => void;
|
||||||
setRangeMax: (range: number) => void;
|
setRangeMax: (range: number) => void;
|
||||||
};
|
};
|
||||||
@ -30,6 +31,7 @@ const FlameGraphTopTableContainer = ({
|
|||||||
search,
|
search,
|
||||||
setSearch,
|
setSearch,
|
||||||
setTopLevelIndex,
|
setTopLevelIndex,
|
||||||
|
setSelectedBarIndex,
|
||||||
setRangeMin,
|
setRangeMin,
|
||||||
setRangeMax,
|
setRangeMax,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
@ -113,6 +115,7 @@ const FlameGraphTopTableContainer = ({
|
|||||||
search={search}
|
search={search}
|
||||||
setSearch={setSearch}
|
setSearch={setSearch}
|
||||||
setTopLevelIndex={setTopLevelIndex}
|
setTopLevelIndex={setTopLevelIndex}
|
||||||
|
setSelectedBarIndex={setSelectedBarIndex}
|
||||||
setRangeMin={setRangeMin}
|
setRangeMin={setRangeMin}
|
||||||
setRangeMax={setRangeMax}
|
setRangeMax={setRangeMax}
|
||||||
/>
|
/>
|
||||||
@ -133,7 +136,9 @@ const getStyles = (selectedView: SelectedView, app: CoreApp) => {
|
|||||||
float: left;
|
float: left;
|
||||||
margin-right: ${marginRight};
|
margin-right: ${marginRight};
|
||||||
width: ${selectedView === SelectedView.TopTable ? '100%' : `calc(50% - ${marginRight})`};
|
width: ${selectedView === SelectedView.TopTable ? '100%' : `calc(50% - ${marginRight})`};
|
||||||
${app !== CoreApp.Explore ? 'height: calc(100% - 44px)' : ''}; // 44px to adjust for header pushing content down
|
${app !== CoreApp.Explore
|
||||||
|
? 'height: calc(100% - 50px)'
|
||||||
|
: 'height: calc(100% + 50px)'}; // 50px to adjust for header pushing content down
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
export type TooltipData = {
|
export type TooltipData = {
|
||||||
name: string;
|
name: string;
|
||||||
percentTitle: string;
|
|
||||||
percentValue: number;
|
percentValue: number;
|
||||||
percentSelf: number;
|
percentSelf: number;
|
||||||
unitTitle: string;
|
unitTitle: string;
|
||||||
@ -9,6 +8,13 @@ export type TooltipData = {
|
|||||||
samples: string;
|
samples: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Metadata = {
|
||||||
|
percentValue: number;
|
||||||
|
unitTitle: string;
|
||||||
|
unitValue: string;
|
||||||
|
samples: string;
|
||||||
|
};
|
||||||
|
|
||||||
export enum SampleUnit {
|
export enum SampleUnit {
|
||||||
Bytes = 'bytes',
|
Bytes = 'bytes',
|
||||||
Short = 'short',
|
Short = 'short',
|
||||||
|
Loading…
Reference in New Issue
Block a user