spanSiblingGroupBar.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import {Fragment} from 'react';
  2. import type {SpanBarType} from 'sentry/components/performance/waterfall/constants';
  3. import {
  4. ConnectorBar,
  5. TOGGLE_BORDER_BOX,
  6. TreeConnector,
  7. } from 'sentry/components/performance/waterfall/treeConnector';
  8. import {t} from 'sentry/locale';
  9. import type {AggregateEventTransaction, EventTransaction} from 'sentry/types/event';
  10. import {trackAnalytics} from 'sentry/utils/analytics';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import {SpanGroupBar} from './spanGroupBar';
  13. import SpanRectangle from './spanRectangle';
  14. import {SpanRectangleOverlay} from './spanRectangleOverlay';
  15. import type {EnhancedSpan, ProcessedSpanType, SpanType, TreeDepthType} from './types';
  16. import type {SpanBoundsType, SpanGeneratedBoundsType, VerticalMark} from './utils';
  17. import {
  18. getSpanGroupBounds,
  19. isOrphanSpan,
  20. isOrphanTreeDepth,
  21. unwrapTreeDepth,
  22. } from './utils';
  23. export type SpanSiblingGroupBarProps = {
  24. addContentSpanBarRef: (instance: HTMLDivElement | null) => void;
  25. continuingTreeDepths: Array<TreeDepthType>;
  26. didAnchoredSpanMount: () => boolean;
  27. event: Readonly<EventTransaction | AggregateEventTransaction>;
  28. generateBounds: (bounds: SpanBoundsType) => SpanGeneratedBoundsType;
  29. getCurrentLeftPos: () => number;
  30. isEmbeddedSpanTree: boolean;
  31. isLastSibling: boolean;
  32. occurrence: number;
  33. onWheel: (deltaX: number) => void;
  34. removeContentSpanBarRef: (instance: HTMLDivElement | null) => void;
  35. span: ProcessedSpanType;
  36. spanGrouping: EnhancedSpan[];
  37. spanNumber: number;
  38. toggleSiblingSpanGroup: (span: SpanType, occurrence: number) => void;
  39. treeDepth: number;
  40. measurements?: Map<number, VerticalMark>;
  41. spanBarType?: SpanBarType;
  42. };
  43. export default function SpanSiblingGroupBar(props: SpanSiblingGroupBarProps) {
  44. const {
  45. continuingTreeDepths,
  46. event,
  47. generateBounds,
  48. getCurrentLeftPos,
  49. isLastSibling,
  50. span,
  51. spanGrouping,
  52. spanNumber,
  53. occurrence,
  54. toggleSiblingSpanGroup,
  55. onWheel,
  56. addContentSpanBarRef,
  57. removeContentSpanBarRef,
  58. isEmbeddedSpanTree,
  59. didAnchoredSpanMount,
  60. spanBarType,
  61. measurements,
  62. } = props;
  63. const organization = useOrganization();
  64. function renderGroupSpansTitle(): React.ReactNode {
  65. if (spanGrouping.length === 0) {
  66. return '';
  67. }
  68. const operation = spanGrouping[0].span.op;
  69. const description = spanGrouping[0].span.description;
  70. if (!description || !operation) {
  71. if (description) {
  72. return <strong>{`${t('Autogrouped')} \u2014 ${description}`}</strong>;
  73. }
  74. if (operation) {
  75. return <strong>{`${t('Autogrouped')} \u2014 ${operation}`}</strong>;
  76. }
  77. return <strong>{`${t('Autogrouped')} \u2014 ${t('siblings')}`}</strong>;
  78. }
  79. return (
  80. <Fragment>
  81. <strong>{`${t('Autogrouped')} \u2014 ${operation} \u2014 `}</strong>
  82. {description}
  83. </Fragment>
  84. );
  85. }
  86. function renderSpanTreeConnector() {
  87. const {treeDepth: spanTreeDepth} = props;
  88. const connectorBars: Array<React.ReactNode> = continuingTreeDepths.map(treeDepth => {
  89. const depth: number = unwrapTreeDepth(treeDepth);
  90. if (depth === 0) {
  91. // do not render a connector bar at depth 0,
  92. // if we did render a connector bar, this bar would be placed at depth -1
  93. // which does not exist.
  94. return null;
  95. }
  96. const left = ((spanTreeDepth - depth) * (TOGGLE_BORDER_BOX / 2) + 2) * -1;
  97. return (
  98. <ConnectorBar
  99. style={{left}}
  100. key={`span-group-${depth}`}
  101. orphanBranch={isOrphanTreeDepth(treeDepth)}
  102. />
  103. );
  104. });
  105. return (
  106. <TreeConnector isLast={isLastSibling} hasToggler orphanBranch={isOrphanSpan(span)}>
  107. {connectorBars}
  108. </TreeConnector>
  109. );
  110. }
  111. function renderSpanRectangles() {
  112. return (
  113. <Fragment>
  114. {spanGrouping.map((_, index) => (
  115. <SpanRectangle
  116. key={index}
  117. spanGrouping={spanGrouping}
  118. bounds={getSpanGroupBounds([spanGrouping[index]], generateBounds)}
  119. spanBarType={spanBarType}
  120. />
  121. ))}
  122. <SpanRectangleOverlay
  123. spanGrouping={spanGrouping}
  124. bounds={getSpanGroupBounds(spanGrouping, generateBounds)}
  125. spanBarType={spanBarType}
  126. />
  127. </Fragment>
  128. );
  129. }
  130. return (
  131. <SpanGroupBar
  132. event={event}
  133. measurements={measurements}
  134. span={span}
  135. spanGrouping={spanGrouping}
  136. treeDepth={props.treeDepth}
  137. spanNumber={spanNumber}
  138. generateBounds={generateBounds}
  139. toggleSpanGroup={() => {
  140. toggleSiblingSpanGroup?.(spanGrouping[0].span, occurrence);
  141. isEmbeddedSpanTree &&
  142. trackAnalytics('issue_details.performance.autogrouped_siblings_toggle', {
  143. organization,
  144. });
  145. }}
  146. renderSpanTreeConnector={renderSpanTreeConnector}
  147. renderGroupSpansTitle={renderGroupSpansTitle}
  148. renderSpanRectangles={renderSpanRectangles}
  149. onWheel={onWheel}
  150. addContentSpanBarRef={addContentSpanBarRef}
  151. removeContentSpanBarRef={removeContentSpanBarRef}
  152. didAnchoredSpanMount={didAnchoredSpanMount}
  153. getCurrentLeftPos={getCurrentLeftPos}
  154. spanBarType={spanBarType}
  155. />
  156. );
  157. }