networkHeaderCell.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import {ComponentProps, CSSProperties, forwardRef, ReactNode} from 'react';
  2. import styled from '@emotion/styled';
  3. import ExternalLink from 'sentry/components/links/externalLink';
  4. import {Tooltip} from 'sentry/components/tooltip';
  5. import {IconArrow, IconInfo} from 'sentry/icons';
  6. import {t, tct} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import useSortNetwork from 'sentry/views/replays/detail/network/useSortNetwork';
  9. type SortConfig = ReturnType<typeof useSortNetwork>['sortConfig'];
  10. type Props = {
  11. handleSort: ReturnType<typeof useSortNetwork>['handleSort'];
  12. index: number;
  13. sortConfig: SortConfig;
  14. style: CSSProperties;
  15. };
  16. const SizeInfoIcon = styled(IconInfo)`
  17. display: block;
  18. `;
  19. const COLUMNS: {
  20. field: SortConfig['by'];
  21. label: string;
  22. tooltipTitle?: ComponentProps<typeof Tooltip>['title'];
  23. }[] = [
  24. {field: 'method', label: t('Method')},
  25. {
  26. field: 'status',
  27. label: t('Status'),
  28. tooltipTitle: tct(
  29. 'If the status is [zero], the resource might be a cross-origin request.[linebreak][linebreak]Configure the server to respond with the CORS header [header] to see the actual response codes. [mozilla].',
  30. {
  31. zero: <code>0</code>,
  32. header: <code>Access-Control-Allow-Origin</code>,
  33. linebreak: <br />,
  34. mozilla: (
  35. <ExternalLink href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus#cross-origin_response_status_codes">
  36. Learn more on MDN
  37. </ExternalLink>
  38. ),
  39. }
  40. ),
  41. },
  42. {field: 'description', label: t('Path')},
  43. {
  44. field: 'op',
  45. label: t('Type'),
  46. },
  47. {
  48. field: 'size',
  49. label: t('Size'),
  50. tooltipTitle: t(
  51. 'The number used for fetch/xhr is the response body size. It is possible the network transfer size is smaller due to compression.'
  52. ),
  53. },
  54. {field: 'duration', label: t('Duration')},
  55. {field: 'startTimestamp', label: t('Timestamp')},
  56. ];
  57. export const COLUMN_COUNT = COLUMNS.length;
  58. function CatchClicks({children}: {children: ReactNode}) {
  59. return <div onClick={e => e.stopPropagation()}>{children}</div>;
  60. }
  61. const NetworkHeaderCell = forwardRef<HTMLButtonElement, Props>(
  62. ({handleSort, index, sortConfig, style}: Props, ref) => {
  63. const {field, label, tooltipTitle} = COLUMNS[index];
  64. return (
  65. <HeaderButton style={style} onClick={() => handleSort(field)} ref={ref}>
  66. {label}
  67. {tooltipTitle ? (
  68. <Tooltip isHoverable title={<CatchClicks>{tooltipTitle}</CatchClicks>}>
  69. <SizeInfoIcon size="xs" />
  70. </Tooltip>
  71. ) : null}
  72. <IconArrow
  73. color="gray300"
  74. size="xs"
  75. direction={sortConfig.by === field && !sortConfig.asc ? 'down' : 'up'}
  76. style={{visibility: sortConfig.by === field ? 'visible' : 'hidden'}}
  77. />
  78. </HeaderButton>
  79. );
  80. }
  81. );
  82. const HeaderButton = styled('button')`
  83. border: 0;
  84. border-bottom: 1px solid ${p => p.theme.border};
  85. background: ${p => p.theme.backgroundSecondary};
  86. color: ${p => p.theme.subText};
  87. font-size: ${p => p.theme.fontSizeSmall};
  88. font-weight: 600;
  89. line-height: 16px;
  90. text-align: unset;
  91. text-transform: uppercase;
  92. width: 100%;
  93. display: flex;
  94. align-items: center;
  95. justify-content: space-between;
  96. padding: ${space(0.5)} ${space(1)} ${space(0.5)} ${space(1.5)};
  97. svg {
  98. margin-left: ${space(0.25)};
  99. }
  100. `;
  101. export default NetworkHeaderCell;