networkTableCell.tsx 4.9 KB

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