import type {ComponentProps, CSSProperties, ReactNode} from 'react'; import {forwardRef} from 'react'; import styled from '@emotion/styled'; import classNames from 'classnames'; import {Cell, Text} from 'sentry/components/replays/virtualizedGrid/bodyCell'; import TextOverflow from 'sentry/components/textOverflow'; import {Tooltip} from 'sentry/components/tooltip'; import {IconFire, IconInfo, IconWarning} from 'sentry/icons'; import {space} from 'sentry/styles/space'; import type useCrumbHandlers from 'sentry/utils/replays/hooks/useCrumbHandlers'; import type {HydratedA11yFrame} from 'sentry/utils/replays/hydrateA11yFrame'; import useUrlParams from 'sentry/utils/useUrlParams'; import type useSortAccessibility from 'sentry/views/replays/detail/accessibility/useSortAccessibility'; const EMPTY_CELL = '--'; interface Props extends ReturnType { a11yIssue: HydratedA11yFrame; columnIndex: number; currentHoverTime: number | undefined; currentTime: number; onClickCell: (props: {dataIndex: number; rowIndex: number}) => void; rowIndex: number; sortConfig: ReturnType['sortConfig']; style: CSSProperties; } const AccessibilityTableCell = forwardRef( ( { a11yIssue, columnIndex, currentHoverTime, currentTime, onClickCell, onMouseEnter, onMouseLeave, rowIndex, sortConfig, style, }: Props, ref ) => { // Rows include the sortable header, the dataIndex does not const dataIndex = rowIndex - 1; const {getParamValue} = useUrlParams('a_detail_row', ''); const isSelected = getParamValue() === String(dataIndex); const IMPACT_ICON_MAPPING: Record = { minor: , moderate: , serious: , critical: , }; const hasOccurred = currentTime >= a11yIssue.offsetMs; const isBeforeHover = currentHoverTime === undefined || currentHoverTime >= a11yIssue.offsetMs; const isByTimestamp = sortConfig.by === 'timestampMs'; const isAsc = isByTimestamp ? sortConfig.asc : undefined; const columnProps = { className: classNames({ beforeCurrentTime: isByTimestamp ? isAsc ? hasOccurred : !hasOccurred : undefined, afterCurrentTime: isByTimestamp ? isAsc ? !hasOccurred : hasOccurred : undefined, beforeHoverTime: isByTimestamp && currentHoverTime !== undefined ? isAsc ? isBeforeHover : !isBeforeHover : undefined, afterHoverTime: isByTimestamp && currentHoverTime !== undefined ? isAsc ? !isBeforeHover : isBeforeHover : undefined, }), hasOccurred: isByTimestamp ? hasOccurred : undefined, isSelected, onClick: () => onClickCell({dataIndex, rowIndex}), onMouseEnter: () => onMouseEnter(a11yIssue), onMouseLeave: () => onMouseLeave(a11yIssue), ref, style, } as ComponentProps; const renderFns = [ () => ( {a11yIssue.impact ? ( {IMPACT_ICON_MAPPING[a11yIssue.impact]} ) : ( EMPTY_CELL )} ), () => ( {a11yIssue.id ?? EMPTY_CELL} ), () => ( {a11yIssue.element.element ?? EMPTY_CELL} ), ]; return renderFns[columnIndex](); } ); export default AccessibilityTableCell; const StyledTextOverflow = styled(TextOverflow)` padding-right: ${space(1)}; `; const StyledCell = styled(Cell)<{ impact: HydratedA11yFrame['impact']; isRowSelected: boolean; }>` background: ${p => p.isSelected ? p.theme.purple300 : p.impact === 'serious' ? p.theme.yellow100 : p.impact === 'critical' ? p.theme.red100 : 'transparent'}; `;