networkTableCell.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import {ComponentProps, CSSProperties, forwardRef} from 'react';
  2. import classNames from 'classnames';
  3. import FileSize from 'sentry/components/fileSize';
  4. import {
  5. ButtonWrapper,
  6. Cell,
  7. Text,
  8. } from 'sentry/components/replays/virtualizedGrid/bodyCell';
  9. import {Tooltip} from 'sentry/components/tooltip';
  10. import {
  11. getFrameMethod,
  12. getFrameStatus,
  13. getResponseBodySize,
  14. } from 'sentry/utils/replays/resourceFrame';
  15. import type {SpanFrame} from 'sentry/utils/replays/types';
  16. import useUrlParams from 'sentry/utils/useUrlParams';
  17. import useSortNetwork from 'sentry/views/replays/detail/network/useSortNetwork';
  18. import TimestampButton from 'sentry/views/replays/detail/timestampButton';
  19. import {operationName} from 'sentry/views/replays/detail/utils';
  20. const EMPTY_CELL = '--';
  21. type Props = {
  22. columnIndex: number;
  23. currentHoverTime: number | undefined;
  24. currentTime: number;
  25. frame: SpanFrame;
  26. onClickCell: (props: {dataIndex: number; rowIndex: number}) => void;
  27. onClickTimestamp: (crumb: SpanFrame) => void;
  28. onMouseEnter: (span: SpanFrame) => void;
  29. onMouseLeave: (span: SpanFrame) => void;
  30. rowIndex: number;
  31. sortConfig: ReturnType<typeof useSortNetwork>['sortConfig'];
  32. startTimestampMs: number;
  33. style: CSSProperties;
  34. };
  35. const NetworkTableCell = forwardRef<HTMLDivElement, Props>(
  36. (
  37. {
  38. columnIndex,
  39. currentHoverTime,
  40. currentTime,
  41. onMouseEnter,
  42. onMouseLeave,
  43. onClickCell,
  44. onClickTimestamp,
  45. rowIndex,
  46. sortConfig,
  47. frame,
  48. startTimestampMs,
  49. style,
  50. }: Props,
  51. ref
  52. ) => {
  53. // Rows include the sortable header, the dataIndex does not
  54. const dataIndex = rowIndex - 1;
  55. const {getParamValue} = useUrlParams('n_detail_row', '');
  56. const isSelected = getParamValue() === String(dataIndex);
  57. const method = getFrameMethod(frame);
  58. const statusCode = getFrameStatus(frame);
  59. const size = getResponseBodySize(frame);
  60. const hasOccurred = currentTime >= frame.offsetMs;
  61. const isBeforeHover =
  62. currentHoverTime === undefined || currentHoverTime >= frame.offsetMs;
  63. const isByTimestamp = sortConfig.by === 'startTimestamp';
  64. const isAsc = isByTimestamp ? sortConfig.asc : undefined;
  65. const columnProps = {
  66. className: classNames({
  67. beforeCurrentTime: isByTimestamp
  68. ? isAsc
  69. ? hasOccurred
  70. : !hasOccurred
  71. : undefined,
  72. afterCurrentTime: isByTimestamp
  73. ? isAsc
  74. ? !hasOccurred
  75. : hasOccurred
  76. : undefined,
  77. beforeHoverTime:
  78. isByTimestamp && currentHoverTime !== undefined
  79. ? isAsc
  80. ? isBeforeHover
  81. : !isBeforeHover
  82. : undefined,
  83. afterHoverTime:
  84. isByTimestamp && currentHoverTime !== undefined
  85. ? isAsc
  86. ? !isBeforeHover
  87. : isBeforeHover
  88. : undefined,
  89. }),
  90. hasOccurred: isByTimestamp ? hasOccurred : undefined,
  91. isSelected,
  92. isStatusError: typeof statusCode === 'number' && statusCode >= 400,
  93. onClick: () => onClickCell({dataIndex, rowIndex}),
  94. onMouseEnter: () => onMouseEnter(frame),
  95. onMouseLeave: () => onMouseLeave(frame),
  96. ref,
  97. style,
  98. } as ComponentProps<typeof Cell>;
  99. const renderFns = [
  100. () => (
  101. <Cell {...columnProps}>
  102. <Text>{method ? method : 'GET'}</Text>
  103. </Cell>
  104. ),
  105. () => (
  106. <Cell {...columnProps}>
  107. <Text>{typeof statusCode === 'number' ? statusCode : EMPTY_CELL}</Text>
  108. </Cell>
  109. ),
  110. () => (
  111. <Cell {...columnProps}>
  112. <Tooltip
  113. title={frame.description}
  114. isHoverable
  115. showOnlyOnOverflow
  116. overlayStyle={{maxWidth: '500px !important'}}
  117. >
  118. <Text>{frame.description || EMPTY_CELL}</Text>
  119. </Tooltip>
  120. </Cell>
  121. ),
  122. () => (
  123. <Cell {...columnProps}>
  124. <Tooltip title={operationName(frame.op)} isHoverable showOnlyOnOverflow>
  125. <Text>{operationName(frame.op)}</Text>
  126. </Tooltip>
  127. </Cell>
  128. ),
  129. () => (
  130. <Cell {...columnProps} numeric>
  131. <Text>
  132. {size === undefined ? EMPTY_CELL : <FileSize base={10} bytes={size} />}
  133. </Text>
  134. </Cell>
  135. ),
  136. () => (
  137. <Cell {...columnProps} numeric>
  138. <Text>{`${(frame.endTimestampMs - frame.timestampMs).toFixed(2)}ms`}</Text>
  139. </Cell>
  140. ),
  141. () => (
  142. <Cell {...columnProps} numeric>
  143. <ButtonWrapper>
  144. <TimestampButton
  145. format="mm:ss.SSS"
  146. onClick={event => {
  147. event.stopPropagation();
  148. onClickTimestamp(frame);
  149. }}
  150. startTimestampMs={startTimestampMs}
  151. timestampMs={frame.timestampMs}
  152. />
  153. </ButtonWrapper>
  154. </Cell>
  155. ),
  156. ];
  157. return renderFns[columnIndex]();
  158. }
  159. );
  160. export default NetworkTableCell;