diff --git a/packages/grafana-flamegraph/src/CallTree/FlameGraphCallTreeContainer.tsx b/packages/grafana-flamegraph/src/CallTree/FlameGraphCallTreeContainer.tsx index 0a304e1d435..d3daf8a8f7b 100644 --- a/packages/grafana-flamegraph/src/CallTree/FlameGraphCallTreeContainer.tsx +++ b/packages/grafana-flamegraph/src/CallTree/FlameGraphCallTreeContainer.tsx @@ -1,11 +1,11 @@ import { css, cx } from '@emotion/css'; -import { memo, useEffect, useMemo, useRef, useState } from 'react'; +import { memo, useEffect, useMemo, useState } from 'react'; import { useTable, useSortBy, useExpanded, Column, Row, UseExpandedRowProps } from 'react-table'; import AutoSizer from 'react-virtualized-auto-sizer'; import { useDebounce, usePrevious } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; -import { Button, ButtonGroup, Dropdown, Input, Menu, useStyles2, useTheme2 } from '@grafana/ui'; +import { Button, Dropdown, Input, Menu, useStyles2, useTheme2 } from '@grafana/ui'; import { byPackageGradient, byValueGradient, diffColorBlindGradient, diffDefaultGradient } from '../FlameGraph/colors'; import { getBarColorByDiff, getBarColorByPackage, getBarColorByValue } from '../FlameGraph/colors'; @@ -106,12 +106,6 @@ const FlameGraphCallTreeContainer = memo( expandedKeys: Object.keys(baseExpanded).filter(k => baseExpanded[k]), }); - // If there's a focused node, expand it using react-table's row ID - // The focused node becomes the root, so its react-table ID is "0" - if (focusedNodeId && nodes.length > 0 && nodes[0].id === focusedNodeId && nodes[0].hasChildren) { - baseExpanded['0'] = true; - } - if (effectiveMatchedLabels && effectiveMatchedLabels.size > 0) { const matchExpanded = getExpandedStateForMatches(nodes, matchingIds); console.log('Adding match expansion:', { @@ -123,7 +117,7 @@ const FlameGraphCallTreeContainer = memo( return { ...baseExpanded, ...matchExpanded }; } return baseExpanded; - }, [nodes, effectiveMatchedLabels, matchingIds, focusedNodeId]); + }, [nodes, effectiveMatchedLabels, matchingIds]); // Create a key that changes when expansion should reset, forcing table remount const tableKey = useMemo(() => { @@ -133,10 +127,6 @@ const FlameGraphCallTreeContainer = memo( return `table-${expandedKeys}${focusPart}`; }, [calculatedExpanded, focusedNodeId]); - // Callback to handle recursive expand/collapse - will be populated after tableInstance is created - const handleToggleExpandRef = useRef<((node: CallTreeNode, rowId: string, isExpanded: boolean) => void) | null>(null); - - // Define columns const columns: Column[] = useMemo(() => { if (data.isDiffFlamegraph()) { @@ -151,11 +141,6 @@ const FlameGraphCallTreeContainer = memo( setFocusedNodeId(nodeId); setLocalSearch(''); }} - onToggleExpand={(isExpanded) => { - if (handleToggleExpandRef.current) { - handleToggleExpandRef.current(row.original, row.id, isExpanded); - } - }} isFocusedRoot={row.id === '0'} styles={styles} /> @@ -231,11 +216,6 @@ const FlameGraphCallTreeContainer = memo( setFocusedNodeId(nodeId); setLocalSearch(''); }} - onToggleExpand={(isExpanded) => { - if (handleToggleExpandRef.current) { - handleToggleExpandRef.current(row.original, row.id, isExpanded); - } - }} isFocusedRoot={row.id === '0'} styles={styles} /> @@ -334,22 +314,7 @@ const FlameGraphCallTreeContainer = memo( useExpanded ); - const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, state, toggleAllRowsExpanded, toggleRowExpanded } = tableInstance; - - // Set up the recursive expand/collapse handler now that we have toggleRowExpanded - // We need to find the actual row ID that react-table uses, which may differ from node.id - handleToggleExpandRef.current = (node: CallTreeNode, rowId: string, isExpanded: boolean) => { - const toggleRecursive = (rId: string, n: CallTreeNode, expand: boolean) => { - toggleRowExpanded(rId, expand); - if (n.subRows) { - n.subRows.forEach((child, index) => { - const childRowId = `${rId}.${index}`; - toggleRecursive(childRowId, child, expand); - }); - } - }; - toggleRecursive(rowId, node, !isExpanded); - }; + const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow, state } = tableInstance; console.log('Table state:', { visibleRows: rows.length, @@ -412,26 +377,6 @@ const FlameGraphCallTreeContainer = memo(
- -
@@ -614,36 +559,16 @@ function getRowBackgroundColor( function ActionsCell({ row, onFocus, - onToggleExpand, isFocusedRoot, styles, }: { row: Row & UseExpandedRowProps; onFocus: (nodeId: string) => void; - onToggleExpand: (isExpanded: boolean) => void; isFocusedRoot: boolean; styles: any; }) { - const hasChildren = row.original.hasChildren; - const isExpanded = row.isExpanded; - return (
- + {row.original.childCount > 0 && ( + + {row.original.childCount} {row.original.childCount === 1 ? 'child' : 'children'}, {row.original.subtreeSize} {row.original.subtreeSize === 1 ? 'node' : 'nodes'} + + )}
); } @@ -1031,6 +961,12 @@ function getStyles(theme: GrafanaTheme2) { fontSize: theme.typography.fontSize, textAlign: 'left', }), + nodeBadge: css({ + marginLeft: theme.spacing(1), + fontSize: theme.typography.bodySmall.fontSize, + color: theme.colors.text.secondary, + whiteSpace: 'nowrap', + }), sortIndicator: css({ marginLeft: '4px', fontSize: '10px', diff --git a/packages/grafana-flamegraph/src/CallTree/utils.ts b/packages/grafana-flamegraph/src/CallTree/utils.ts index 566ffc7f811..6b75a4fc7a0 100644 --- a/packages/grafana-flamegraph/src/CallTree/utils.ts +++ b/packages/grafana-flamegraph/src/CallTree/utils.ts @@ -10,6 +10,8 @@ export interface CallTreeNode { depth: number; // Indentation level parentId?: string; // Parent node ID hasChildren: boolean; // Has expandable children + childCount: number; // Number of direct children + subtreeSize: number; // Total number of nodes in subtree (excluding self) levelItem: LevelItem; // Reference to original data subRows?: CallTreeNode[]; // Child nodes for react-table useExpanded isLastChild: boolean; // Whether this is the last child of its parent @@ -80,6 +82,10 @@ export function buildCallTreeNode( }) : undefined; + // Calculate child count and subtree size + const childCount = rootItem.children.length; + const subtreeSize = subRows ? subRows.reduce((sum, child) => sum + child.subtreeSize + 1, 0) : 0; + const node: CallTreeNode = { id: nodeId, label, @@ -90,6 +96,8 @@ export function buildCallTreeNode( depth, parentId, hasChildren: rootItem.children.length > 0, + childCount, + subtreeSize, levelItem: rootItem, subRows, isLastChild: false, // Will be set by parent