import countBy from 'lodash/countBy'; import {ROW_HEIGHT} from 'sentry/components/performance/waterfall/constants'; import {DurationPill, RowRectangle} from 'sentry/components/performance/waterfall/rowBar'; import { ConnectorBar, TOGGLE_BORDER_BOX, TreeConnector, } from 'sentry/components/performance/waterfall/treeConnector'; import { getDurationDisplay, getHumanDuration, toPercent, } from 'sentry/components/performance/waterfall/utils'; import {t} from 'sentry/locale'; import {EventTransaction} from 'sentry/types/event'; import theme from 'sentry/utils/theme'; import {SpanGroupBar} from './spanGroupBar'; import {EnhancedSpan, ProcessedSpanType, TreeDepthType} from './types'; import { getSpanGroupBounds, getSpanGroupTimestamps, getSpanOperation, isOrphanSpan, isOrphanTreeDepth, SpanBoundsType, SpanGeneratedBoundsType, unwrapTreeDepth, } from './utils'; type Props = { continuingTreeDepths: Array; event: Readonly; generateBounds: (bounds: SpanBoundsType) => SpanGeneratedBoundsType; generateContentSpanBarRef: () => (instance: HTMLDivElement | null) => void; onWheel: (deltaX: number) => void; span: Readonly; spanGrouping: EnhancedSpan[]; spanNumber: number; toggleSpanGroup: () => void; treeDepth: number; }; export function SpanDescendantGroupBar(props: Props) { const { continuingTreeDepths, event, generateBounds, span, spanGrouping, spanNumber, toggleSpanGroup, onWheel, generateContentSpanBarRef, } = props; function renderGroupSpansTitle() { if (spanGrouping.length === 0) { return ''; } const operationCounts = countBy(spanGrouping, enhancedSpan => getSpanOperation(enhancedSpan.span) ); const hasOthers = Object.keys(operationCounts).length > 1; const [mostFrequentOperationName] = Object.entries(operationCounts).reduce( (acc, [operationNameKey, count]) => { if (count > acc[1]) { return [operationNameKey, count]; } return acc; } ); return ( {`${t('Autogrouped ')}\u2014 ${mostFrequentOperationName}${ hasOthers ? t(' and more') : '' }`} ); } function renderSpanTreeConnector() { const {treeDepth: spanTreeDepth} = props; const connectorBars: Array = continuingTreeDepths.map(treeDepth => { const depth: number = unwrapTreeDepth(treeDepth); if (depth === 0) { // do not render a connector bar at depth 0, // if we did render a connector bar, this bar would be placed at depth -1 // which does not exist. return null; } const left = ((spanTreeDepth - depth) * (TOGGLE_BORDER_BOX / 2) + 2) * -1; return ( ); }); connectorBars.push( ); return ( {connectorBars} ); } function renderSpanRectangles() { const bounds = getSpanGroupBounds(spanGrouping, generateBounds); const durationDisplay = getDurationDisplay(bounds); const {startTimestamp, endTimestamp} = getSpanGroupTimestamps(spanGrouping); const duration = Math.abs(endTimestamp - startTimestamp); const durationString = getHumanDuration(duration); return ( {durationString} ); } return ( ); }