accessibilityTableCell.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import {ComponentProps, CSSProperties, forwardRef} from 'react';
  2. import classNames from 'classnames';
  3. import {
  4. ButtonWrapper,
  5. Cell,
  6. CodeHighlightCell,
  7. Text,
  8. } from 'sentry/components/replays/virtualizedGrid/bodyCell';
  9. import {Tooltip} from 'sentry/components/tooltip';
  10. import {IconFire, IconInfo, IconWarning} from 'sentry/icons';
  11. import type useCrumbHandlers from 'sentry/utils/replays/hooks/useCrumbHandlers';
  12. import {HydratedA11yFrame} from 'sentry/utils/replays/hydrateA11yRecord';
  13. import {Color} from 'sentry/utils/theme';
  14. import useUrlParams from 'sentry/utils/useUrlParams';
  15. import useSortAccessibility from 'sentry/views/replays/detail/accessibility/useSortAccessibility';
  16. import TimestampButton from 'sentry/views/replays/detail/timestampButton';
  17. const EMPTY_CELL = '--';
  18. const IMPACT_ICON_MAPPING: Record<keyof HydratedA11yFrame['impact'], Color> = {
  19. minor: <IconInfo size="xs" />,
  20. moderate: <IconInfo size="xs" />,
  21. serious: <IconWarning size="xs" color="yellow400" />,
  22. critical: <IconFire size="xs" color="red400" />,
  23. };
  24. interface Props extends ReturnType<typeof useCrumbHandlers> {
  25. a11yIssue: HydratedA11yFrame;
  26. columnIndex: number;
  27. currentHoverTime: number | undefined;
  28. currentTime: number;
  29. onClickCell: (props: {dataIndex: number; rowIndex: number}) => void;
  30. rowIndex: number;
  31. sortConfig: ReturnType<typeof useSortAccessibility>['sortConfig'];
  32. startTimestampMs: number;
  33. style: CSSProperties;
  34. }
  35. const AccessibilityTableCell = forwardRef<HTMLDivElement, Props>(
  36. (
  37. {
  38. a11yIssue,
  39. columnIndex,
  40. currentHoverTime,
  41. currentTime,
  42. onClickCell,
  43. onClickTimestamp,
  44. onMouseEnter,
  45. onMouseLeave,
  46. rowIndex,
  47. sortConfig,
  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('a_detail_row', '');
  56. const isSelected = getParamValue() === String(dataIndex);
  57. const hasOccurred = currentTime >= a11yIssue.offsetMs;
  58. const isBeforeHover =
  59. currentHoverTime === undefined || currentHoverTime >= a11yIssue.offsetMs;
  60. const isByTimestamp = sortConfig.by === 'timestampMs';
  61. const isAsc = isByTimestamp ? sortConfig.asc : undefined;
  62. const columnProps = {
  63. className: classNames({
  64. beforeCurrentTime: isByTimestamp
  65. ? isAsc
  66. ? hasOccurred
  67. : !hasOccurred
  68. : undefined,
  69. afterCurrentTime: isByTimestamp
  70. ? isAsc
  71. ? !hasOccurred
  72. : hasOccurred
  73. : undefined,
  74. beforeHoverTime:
  75. isByTimestamp && currentHoverTime !== undefined
  76. ? isAsc
  77. ? isBeforeHover
  78. : !isBeforeHover
  79. : undefined,
  80. afterHoverTime:
  81. isByTimestamp && currentHoverTime !== undefined
  82. ? isAsc
  83. ? !isBeforeHover
  84. : isBeforeHover
  85. : undefined,
  86. }),
  87. hasOccurred: isByTimestamp ? hasOccurred : undefined,
  88. isSelected,
  89. onClick: () => onClickCell({dataIndex, rowIndex}),
  90. onMouseEnter: () => onMouseEnter(a11yIssue),
  91. onMouseLeave: () => onMouseLeave(a11yIssue),
  92. ref,
  93. style,
  94. } as ComponentProps<typeof Cell>;
  95. const renderFns = [
  96. () => (
  97. <Cell {...columnProps}>
  98. <Text>
  99. {a11yIssue.impact ? (
  100. <Tooltip title={a11yIssue.impact ?? EMPTY_CELL}>
  101. {IMPACT_ICON_MAPPING[a11yIssue.impact]}
  102. </Tooltip>
  103. ) : (
  104. EMPTY_CELL
  105. )}
  106. </Text>
  107. </Cell>
  108. ),
  109. () => (
  110. <Cell {...columnProps}>
  111. <Text>{a11yIssue.id ?? EMPTY_CELL}</Text>
  112. </Cell>
  113. ),
  114. () => (
  115. <Cell {...columnProps}>
  116. <CodeHighlightCell language="html" hideCopyButton>
  117. {a11yIssue.elements?.[0].element ?? EMPTY_CELL}
  118. </CodeHighlightCell>
  119. </Cell>
  120. ),
  121. () => (
  122. <Cell {...columnProps} numeric>
  123. <ButtonWrapper>
  124. <TimestampButton
  125. format="mm:ss.SSS"
  126. onClick={event => {
  127. event.stopPropagation();
  128. onClickTimestamp(a11yIssue);
  129. }}
  130. startTimestampMs={startTimestampMs}
  131. timestampMs={a11yIssue.timestampMs}
  132. />
  133. </ButtonWrapper>
  134. </Cell>
  135. ),
  136. ];
  137. return renderFns[columnIndex]();
  138. }
  139. );
  140. export default AccessibilityTableCell;