spanSamplesTable.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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. | 'profile_id'
  18. | 'timestamp'
  19. | 'duration'
  20. | 'p95_comparison'
  21. | 'avg_comparison';
  22. export type SamplesTableColumnHeader = GridColumnHeader<Keys>;
  23. const DEFAULT_COLUMN_ORDER: SamplesTableColumnHeader[] = [
  24. {
  25. key: 'transaction_id',
  26. name: 'Event ID',
  27. width: COL_WIDTH_UNDEFINED,
  28. },
  29. {
  30. key: 'duration',
  31. name: 'Span Duration',
  32. width: COL_WIDTH_UNDEFINED,
  33. },
  34. {
  35. key: 'avg_comparison',
  36. name: 'Compared to Average',
  37. width: COL_WIDTH_UNDEFINED,
  38. },
  39. ];
  40. type SpanTableRow = {
  41. op: string;
  42. transaction: {
  43. id: string;
  44. 'project.name': string;
  45. timestamp: string;
  46. 'transaction.duration': number;
  47. };
  48. } & SpanSample;
  49. type Props = {
  50. avg: number;
  51. data: SpanTableRow[];
  52. isLoading: boolean;
  53. columnOrder?: SamplesTableColumnHeader[];
  54. highlightedSpanId?: string;
  55. onMouseLeaveSample?: () => void;
  56. onMouseOverSample?: (sample: SpanSample) => void;
  57. };
  58. export function SpanSamplesTable({
  59. isLoading,
  60. data,
  61. avg,
  62. highlightedSpanId,
  63. onMouseLeaveSample,
  64. onMouseOverSample,
  65. columnOrder,
  66. }: Props) {
  67. const location = useLocation();
  68. function handleMouseOverBodyCell(row: SpanTableRow) {
  69. if (onMouseOverSample) {
  70. onMouseOverSample(row);
  71. }
  72. }
  73. function handleMouseLeave() {
  74. if (onMouseLeaveSample) {
  75. onMouseLeaveSample();
  76. }
  77. }
  78. function renderHeadCell(column: GridColumnHeader): React.ReactNode {
  79. if (
  80. column.key === 'p95_comparison' ||
  81. column.key === 'avg_comparison' ||
  82. column.key === 'duration'
  83. ) {
  84. return (
  85. <TextAlignRight>
  86. <OverflowEllipsisTextContainer>{column.name}</OverflowEllipsisTextContainer>
  87. </TextAlignRight>
  88. );
  89. }
  90. return <OverflowEllipsisTextContainer>{column.name}</OverflowEllipsisTextContainer>;
  91. }
  92. function renderBodyCell(column: GridColumnHeader, row: SpanTableRow): React.ReactNode {
  93. const shouldHighlight = row.span_id === highlightedSpanId;
  94. const commonProps = {
  95. style: (shouldHighlight ? {fontWeight: 'bold'} : {}) satisfies CSSProperties,
  96. onMouseEnter: () => handleMouseOverBodyCell(row),
  97. };
  98. if (column.key === 'transaction_id') {
  99. return (
  100. <Link
  101. to={`/performance/${row.project}:${row['transaction.id']}#span-${row.span_id}`}
  102. {...commonProps}
  103. >
  104. {row['transaction.id'].slice(0, 8)}
  105. </Link>
  106. );
  107. }
  108. if (column.key === 'profile_id') {
  109. return row.profile_id ? (
  110. <Link
  111. {...commonProps}
  112. to={`/profiling/profile/${row.project}/${row.profile_id}/flamechart/`}
  113. >
  114. {row.profile_id.slice(0, 8)}
  115. </Link>
  116. ) : (
  117. <div {...commonProps}>(no value)</div>
  118. );
  119. }
  120. if (column.key === 'duration') {
  121. return (
  122. <DurationCell containerProps={commonProps} milliseconds={row['span.self_time']} />
  123. );
  124. }
  125. if (column.key === 'avg_comparison') {
  126. return (
  127. <DurationComparisonCell
  128. containerProps={commonProps}
  129. duration={row['span.self_time']}
  130. compareToDuration={avg}
  131. />
  132. );
  133. }
  134. return <span {...commonProps}>{row[column.key]}</span>;
  135. }
  136. return (
  137. <div onMouseLeave={handleMouseLeave}>
  138. <GridEditable
  139. isLoading={isLoading}
  140. data={data}
  141. columnOrder={columnOrder ?? DEFAULT_COLUMN_ORDER}
  142. columnSortBy={[]}
  143. grid={{
  144. renderHeadCell,
  145. renderBodyCell,
  146. }}
  147. location={location}
  148. />
  149. </div>
  150. );
  151. }