Flamegraph: Refactor css styling (#78698)

This commit is contained in:
Andrej Ocenas 2023-12-03 12:00:42 +01:00 committed by GitHub
parent 4a6ff8cf69
commit b40913c5e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 105 additions and 76 deletions

View File

@ -653,9 +653,7 @@ exports[`better eslint`] = {
], ],
"packages/grafana-flamegraph/src/TopTable/FlameGraphTopTableContainer.tsx:5381": [ "packages/grafana-flamegraph/src/TopTable/FlameGraphTopTableContainer.tsx:5381": [
[0, 0, 0, "Styles should be written using objects.", "0"], [0, 0, 0, "Styles should be written using objects.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"], [0, 0, 0, "Styles should be written using objects.", "1"]
[0, 0, 0, "Styles should be written using objects.", "2"],
[0, 0, 0, "Styles should be written using objects.", "3"]
], ],
"packages/grafana-runtime/src/analytics/types.ts:5381": [ "packages/grafana-runtime/src/analytics/types.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]

View File

@ -178,7 +178,6 @@ const getStyles = () => ({
graph: css` graph: css`
label: graph; label: graph;
overflow: auto; overflow: auto;
height: 100%;
flex-grow: 1; flex-grow: 1;
flex-basis: 50%; flex-basis: 50%;
`, `,

View File

@ -245,7 +245,6 @@ const getStyles = () => ({
graph: css({ graph: css({
label: 'graph', label: 'graph',
overflow: 'auto', overflow: 'auto',
height: '100%',
flexGrow: 1, flexGrow: 1,
flexBasis: '50%', flexBasis: '50%',
}), }),

View File

@ -98,7 +98,7 @@ const FlameGraphContainer = ({
return new FlameGraphDataContainer(data, { collapsing: !disableCollapsing }, theme); return new FlameGraphDataContainer(data, { collapsing: !disableCollapsing }, theme);
}, [data, theme, disableCollapsing]); }, [data, theme, disableCollapsing]);
const [colorScheme, setColorScheme] = useColorScheme(dataContainer); const [colorScheme, setColorScheme] = useColorScheme(dataContainer);
const styles = getStyles(theme, vertical); const styles = getStyles(theme);
// If user resizes window with both as the selected view // If user resizes window with both as the selected view
useEffect(() => { useEffect(() => {
@ -144,6 +144,65 @@ const FlameGraphContainer = ({
return null; return null;
} }
const flameGraph = (
<FlameGraph
data={dataContainer}
rangeMin={rangeMin}
rangeMax={rangeMax}
search={search}
setRangeMin={setRangeMin}
setRangeMax={setRangeMax}
onItemFocused={(data) => setFocusedItemData(data)}
focusedItemData={focusedItemData}
textAlign={textAlign}
sandwichItem={sandwichItem}
onSandwich={(label: string) => {
resetFocus();
setSandwichItem(label);
}}
onFocusPillClick={resetFocus}
onSandwichPillClick={resetSandwich}
colorScheme={colorScheme}
showFlameGraphOnly={showFlameGraphOnly}
collapsing={!disableCollapsing}
/>
);
const table = (
<FlameGraphTopTableContainer
data={dataContainer}
onSymbolClick={onSymbolClick}
search={search}
sandwichItem={sandwichItem}
onSandwich={setSandwichItem}
onSearch={setSearch}
onTableSort={onTableSort}
/>
);
let body;
if (showFlameGraphOnly || selectedView === SelectedView.FlameGraph) {
body = flameGraph;
} else if (selectedView === SelectedView.TopTable) {
body = <div className={styles.tableContainer}>{table}</div>;
} else if (selectedView === SelectedView.Both) {
if (vertical) {
body = (
<div>
<div className={styles.verticalGraphContainer}>{flameGraph}</div>
<div className={styles.verticalTableContainer}>{table}</div>
</div>
);
} else {
body = (
<div className={styles.horizontalContainer}>
<div className={styles.horizontalTableContainer}>{table}</div>
<div className={styles.horizontalGraphContainer}>{flameGraph}</div>
</div>
);
}
}
return ( return (
// We add the theme context to bridge the gap if this is rendered in non grafana environment where the context // We add the theme context to bridge the gap if this is rendered in non grafana environment where the context
// isn't already provided. // isn't already provided.
@ -178,45 +237,7 @@ const FlameGraphContainer = ({
/> />
)} )}
<div className={styles.body}> <div className={styles.body}>{body}</div>
{!showFlameGraphOnly && selectedView !== SelectedView.FlameGraph && (
<FlameGraphTopTableContainer
data={dataContainer}
onSymbolClick={onSymbolClick}
height={selectedView === SelectedView.TopTable || vertical ? 600 : undefined}
search={search}
sandwichItem={sandwichItem}
onSandwich={setSandwichItem}
onSearch={setSearch}
onTableSort={onTableSort}
vertical={vertical}
/>
)}
{selectedView !== SelectedView.TopTable && (
<FlameGraph
data={dataContainer}
rangeMin={rangeMin}
rangeMax={rangeMax}
search={search}
setRangeMin={setRangeMin}
setRangeMax={setRangeMax}
onItemFocused={(data) => setFocusedItemData(data)}
focusedItemData={focusedItemData}
textAlign={textAlign}
sandwichItem={sandwichItem}
onSandwich={(label: string) => {
resetFocus();
setSandwichItem(label);
}}
onFocusPillClick={resetFocus}
onSandwichPillClick={resetSandwich}
colorScheme={colorScheme}
showFlameGraphOnly={showFlameGraphOnly}
collapsing={!disableCollapsing}
/>
)}
</div>
</div> </div>
</ThemeContext.Provider> </ThemeContext.Provider>
); );
@ -234,12 +255,13 @@ function useColorScheme(dataContainer: FlameGraphDataContainer | undefined) {
return [colorScheme, setColorScheme] as const; return [colorScheme, setColorScheme] as const;
} }
function getStyles(theme: GrafanaTheme2, vertical?: boolean) { function getStyles(theme: GrafanaTheme2) {
return { return {
container: css({ container: css({
label: 'container', label: 'container',
overflow: 'auto',
height: '100%', height: '100%',
display: vertical ? 'block' : 'flex', display: 'flex',
flex: '1 1 0', flex: '1 1 0',
flexDirection: 'column', flexDirection: 'column',
minHeight: 0, minHeight: 0,
@ -247,12 +269,39 @@ function getStyles(theme: GrafanaTheme2, vertical?: boolean) {
}), }),
body: css({ body: css({
label: 'body', label: 'body',
display: 'flex',
flexGrow: 1, flexGrow: 1,
}),
tableContainer: css({
// This is not ideal for dashboard panel where it creates a double scroll. In a panel it should be 100% but then
// in explore we need a specific height.
height: 800,
}),
horizontalContainer: css({
label: 'horizontalContainer',
display: 'flex',
minHeight: 0, minHeight: 0,
height: vertical ? undefined : '100vh', flexDirection: 'row',
flexDirection: vertical ? 'column-reverse' : 'row',
columnGap: theme.spacing(1), columnGap: theme.spacing(1),
width: '100%',
}),
horizontalGraphContainer: css({
flexBasis: '50%',
}),
horizontalTableContainer: css({
flexBasis: '50%',
maxHeight: 800,
}),
verticalGraphContainer: css({
marginBottom: theme.spacing(1),
}),
verticalTableContainer: css({
height: 800,
}), }),
}; };
} }

View File

@ -29,17 +29,15 @@ import { TableData } from '../types';
type Props = { type Props = {
data: FlameGraphDataContainer; data: FlameGraphDataContainer;
onSymbolClick: (symbol: string) => void; onSymbolClick: (symbol: string) => void;
height?: number;
search?: string; search?: string;
sandwichItem?: string; sandwichItem?: string;
onSearch: (str: string) => void; onSearch: (str: string) => void;
onSandwich: (str?: string) => void; onSandwich: (str?: string) => void;
onTableSort?: (sort: string) => void; onTableSort?: (sort: string) => void;
vertical?: boolean;
}; };
const FlameGraphTopTableContainer = React.memo( const FlameGraphTopTableContainer = React.memo(
({ data, onSymbolClick, height, search, onSearch, sandwichItem, onSandwich, onTableSort, vertical }: Props) => { ({ data, onSymbolClick, search, onSearch, sandwichItem, onSandwich, onTableSort }: Props) => {
const table = useMemo(() => { const table = useMemo(() => {
// Group the data by label, we show only one row per label and sum the values // Group the data by label, we show only one row per label and sum the values
// TODO: should be by filename + funcName + linenumber? // TODO: should be by filename + funcName + linenumber?
@ -57,21 +55,14 @@ const FlameGraphTopTableContainer = React.memo(
return table; return table;
}, [data]); }, [data]);
const rowHeight = 35; const styles = useStyles2(getStyles);
// When we use normal layout we size the table to have the same height as the flamegraph to look good side by side.
// In vertical layout we don't need that so this is a bit arbitrary. We want some max limit
// so we don't show potentially thousands of rows at once which can hinder performance (the table is virtualized
// so with some max height it handles it fine)
const tableHeight = vertical ? Math.min(Object.keys(table).length * rowHeight, 800) : 0;
const styles = useStyles2(getStyles, tableHeight);
const theme = useTheme2(); const theme = useTheme2();
const [sort, setSort] = useState<TableSortByFieldState[]>([{ displayName: 'Self', desc: true }]); const [sort, setSort] = useState<TableSortByFieldState[]>([{ displayName: 'Self', desc: true }]);
return ( return (
<div className={styles.topTableContainer} data-testid="topTable"> <div className={styles.topTableContainer} data-testid="topTable">
<AutoSizer style={{ width: '100%', height }}> <AutoSizer style={{ width: '100%' }}>
{({ width, height }) => { {({ width, height }) => {
if (width < 3 || height < 3) { if (width < 3 || height < 3) {
return null; return null;
@ -319,21 +310,14 @@ function ActionCell(props: ActionCellProps) {
); );
} }
const getStyles = (theme: GrafanaTheme2, height: number) => { const getStyles = (theme: GrafanaTheme2) => {
return { return {
topTableContainer: css` topTableContainer: css({
label: topTableContainer; label: 'topTableContainer',
flex-grow: 1; padding: theme.spacing(1),
flex-basis: 50%; backgroundColor: theme.colors.background.secondary,
overflow: hidden; height: '100%',
padding: ${theme.spacing(1)}; }),
background-color: ${theme.colors.background.secondary};
${height
? css`
min-height: ${height}px;
`
: ''}
`,
}; };
}; };