spanSamplesTable.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import {CSSProperties} from 'react';
  2. import {Link} from 'react-router';
  3. import GridEditable, {
  4. COL_WIDTH_UNDEFINED,
  5. GridColumnHeader,
  6. } from 'sentry/components/gridEditable';
  7. import {useLocation} from 'sentry/utils/useLocation';
  8. import {DurationComparisonCell} from 'sentry/views/starfish/components/samplesTable/common';
  9. import {DurationCell} from 'sentry/views/starfish/components/tableCells/durationCell';
  10. import {
  11. OverflowEllipsisTextContainer,
  12. TextAlignRight,
  13. } from 'sentry/views/starfish/components/textAlign';
  14. import {SpanSample} from 'sentry/views/starfish/queries/useSpanSamples';
  15. type Keys =
  16. | 'transaction_id'
  17. | 'timestamp'
  18. | 'duration'
  19. | 'p95_comparison'
  20. | 'avg_comparison';
  21. type TableColumnHeader = GridColumnHeader<Keys>;
  22. const COLUMN_ORDER: TableColumnHeader[] = [
  23. {
  24. key: 'transaction_id',
  25. name: 'Event ID',
  26. width: COL_WIDTH_UNDEFINED,
  27. },
  28. {
  29. key: 'duration',
  30. name: 'Span Duration',
  31. width: COL_WIDTH_UNDEFINED,
  32. },
  33. {
  34. key: 'avg_comparison',
  35. name: 'Compared to Average',
  36. width: COL_WIDTH_UNDEFINED,
  37. },
  38. ];
  39. type SpanTableRow = {
  40. op: string;
  41. transaction: {
  42. id: string;
  43. 'project.name': string;
  44. timestamp: string;
  45. 'transaction.duration': number;
  46. };
  47. } & SpanSample;
  48. type Props = {
  49. avg: number;
  50. data: SpanTableRow[];
  51. isLoading: boolean;
  52. highlightedSpanId?: string;
  53. onMouseLeaveSample?: () => void;
  54. onMouseOverSample?: (sample: SpanSample) => void;
  55. };
  56. export function SpanSamplesTable({
  57. isLoading,
  58. data,
  59. avg,
  60. highlightedSpanId,
  61. onMouseLeaveSample,
  62. onMouseOverSample,
  63. }: Props) {
  64. const location = useLocation();
  65. function handleMouseOverBodyCell(row: SpanTableRow) {
  66. if (onMouseOverSample) {
  67. onMouseOverSample(row);
  68. }
  69. }
  70. function handleMouseLeave() {
  71. if (onMouseLeaveSample) {
  72. onMouseLeaveSample();
  73. }
  74. }
  75. function renderHeadCell(column: GridColumnHeader): React.ReactNode {
  76. if (
  77. column.key === 'p95_comparison' ||
  78. column.key === 'avg_comparison' ||
  79. column.key === 'duration'
  80. ) {
  81. return (
  82. <TextAlignRight>
  83. <OverflowEllipsisTextContainer>{column.name}</OverflowEllipsisTextContainer>
  84. </TextAlignRight>
  85. );
  86. }
  87. return <OverflowEllipsisTextContainer>{column.name}</OverflowEllipsisTextContainer>;
  88. }
  89. function renderBodyCell(column: GridColumnHeader, row: SpanTableRow): React.ReactNode {
  90. const shouldHighlight = row.span_id === highlightedSpanId;
  91. const commonProps = {
  92. style: (shouldHighlight ? {fontWeight: 'bold'} : {}) satisfies CSSProperties,
  93. onMouseEnter: () => handleMouseOverBodyCell(row),
  94. };
  95. if (column.key === 'transaction_id') {
  96. return (
  97. <Link
  98. to={`/performance/${row.project}:${row['transaction.id']}#span-${row.span_id}`}
  99. {...commonProps}
  100. >
  101. {row['transaction.id'].slice(0, 8)}
  102. </Link>
  103. );
  104. }
  105. if (column.key === 'duration') {
  106. return (
  107. <DurationCell containerProps={commonProps} milliseconds={row['span.self_time']} />
  108. );
  109. }
  110. if (column.key === 'avg_comparison') {
  111. return (
  112. <DurationComparisonCell
  113. containerProps={commonProps}
  114. duration={row['span.self_time']}
  115. compareToDuration={avg}
  116. />
  117. );
  118. }
  119. return <span {...commonProps}>{row[column.key]}</span>;
  120. }
  121. return (
  122. <div onMouseLeave={handleMouseLeave}>
  123. <GridEditable
  124. isLoading={isLoading}
  125. data={data}
  126. columnOrder={COLUMN_ORDER}
  127. columnSortBy={[]}
  128. grid={{
  129. renderHeadCell,
  130. renderBodyCell,
  131. }}
  132. location={location}
  133. />
  134. </div>
  135. );
  136. }