grafana-flamegraph: Add storybook stories for major components (#113375)

This commit is contained in:
Bryan Huhta
2025-11-25 15:19:29 -06:00
committed by GitHub
parent 235ebbf5c5
commit fee3f12e4e
13 changed files with 850 additions and 43 deletions
@@ -0,0 +1,31 @@
import { Meta, StoryObj } from '@storybook/react';
import { createDataFrame } from '@grafana/data';
import { ColorScheme, SelectedView } from '../types';
import FlameGraph from './FlameGraph';
import { CollapsedMap, FlameGraphDataContainer } from './dataTransform';
import { data } from './testData/dataBasic';
const meta: Meta<typeof FlameGraph> = {
title: 'FlameGraph',
component: FlameGraph,
args: {
rangeMin: 0,
rangeMax: 1,
textAlign: 'left',
colorScheme: ColorScheme.PackageBased,
selectedView: SelectedView.Both,
search: '',
},
};
export default meta;
export const Basic: StoryObj<typeof meta> = {
render: (args) => {
const dataContainer = new FlameGraphDataContainer(createDataFrame(data), { collapsing: false });
return <FlameGraph {...args} data={dataContainer} collapsedMap={new CollapsedMap()} />;
},
};
@@ -0,0 +1,32 @@
import { DataFrameDTO } from '@grafana/data';
export const data: DataFrameDTO = {
name: 'response',
refId: 'A',
// @ts-ignore
meta: { preferredVisualisationType: 'flamegraph' },
fields: [
{
name: 'level',
values: [0, 1, 2, 3, 2, 1],
},
{
name: 'value',
values: [10000, 5000, 4000, 3000, 500, 3000],
config: {
unit: 'ms',
},
},
{
name: 'self',
values: [10000, 500, 1000, 3000, 500, 3000],
config: {
unit: 'ms',
},
},
{
name: 'label',
values: ['total', 'fn_1', 'fn_2', 'fn_3', 'fn_4', 'fn_5'],
},
],
};
@@ -0,0 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react';
import { createDataFrame } from '@grafana/data';
import { useTheme2 } from '@grafana/ui';
import { data } from './FlameGraph/testData/dataNestedSet';
import FlameGraphContainer, { Props } from './FlameGraphContainer';
const WrappedFlameGraph = (props: Omit<Props, 'getTheme'>) => {
const theme = useTheme2();
const df = createDataFrame(data);
return <FlameGraphContainer {...props} data={df} getTheme={() => theme} />;
};
const meta: Meta<typeof FlameGraphContainer> = {
title: 'FlameGraphContainer',
render: (args) => {
return <WrappedFlameGraph {...args} />;
},
};
export default meta;
export const Basic: StoryObj<typeof meta> = {};
@@ -0,0 +1,41 @@
import { Meta, StoryObj } from '@storybook/react';
import { createDataFrame } from '@grafana/data';
import { FlameGraphDataContainer } from '../FlameGraph/dataTransform';
import { data } from '../FlameGraph/testData/dataNestedSet';
import { ColorScheme } from '../types';
import FlameGraphTopTableContainer from './FlameGraphTopTableContainer';
const meta: Meta<typeof FlameGraphTopTableContainer> = {
title: 'TopTable',
component: FlameGraphTopTableContainer,
args: {
colorScheme: ColorScheme.ValueBased,
},
decorators: [
(Story) => (
<div style={{ width: '100%', height: '600px' }}>
<Story />
</div>
),
],
};
export default meta;
export const Basic: StoryObj<typeof meta> = {
render: (args) => {
const dataContainer = new FlameGraphDataContainer(createDataFrame(data), { collapsing: true });
return (
<FlameGraphTopTableContainer
{...args}
data={dataContainer}
onSymbolClick={() => {}}
onSearch={() => {}}
onSandwich={() => {}}
/>
);
},
};
@@ -144,6 +144,7 @@ function buildFilteredTable(data: FlameGraphDataContainer, matchedLabels?: Set<s
// Add current call to the stack
callStack.push(label);
}
return filteredTable;
}
@@ -0,0 +1,36 @@
import { Decorator } from '@storybook/react';
import * as React from 'react';
import { getThemeById, ThemeContext } from '@grafana/data';
import { GlobalStyles } from '@grafana/ui';
interface ThemeableStoryProps {
themeId: string;
}
const ThemeableStory = ({ children, themeId }: React.PropsWithChildren<ThemeableStoryProps>) => {
const theme = getThemeById(themeId);
const css = `
#storybook-root {
padding: ${theme.spacing(2)};
}
body {
background: ${theme.colors.background.primary};
}
`;
return (
<ThemeContext.Provider value={theme}>
<GlobalStyles />
<style>{css}</style>
{children}
</ThemeContext.Provider>
);
};
export const withTheme =
(): Decorator =>
// eslint-disable-next-line react/display-name
(story, context) => <ThemeableStory themeId={context.globals.theme}>{story()}</ThemeableStory>;
@@ -0,0 +1,12 @@
import { Decorator } from '@storybook/react';
import { useEffect } from 'react';
import { setTimeZoneResolver } from '@grafana/data';
export const withTimeZone = (): Decorator => (Story, context) => {
useEffect(() => {
setTimeZoneResolver(() => context.globals.timeZone ?? 'browser');
}, [context.globals.timeZone]);
return Story();
};