Files
2024-02-15 17:01:50 +00:00

155 lines
4.8 KiB
TypeScript

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<typeof useCrumbHandlers> {
a11yIssue: HydratedA11yFrame;
columnIndex: number;
currentHoverTime: number | undefined;
currentTime: number;
onClickCell: (props: {dataIndex: number; rowIndex: number}) => void;
rowIndex: number;
sortConfig: ReturnType<typeof useSortAccessibility>['sortConfig'];
style: CSSProperties;
}
const AccessibilityTableCell = forwardRef<HTMLDivElement, Props>(
(
{
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<keyof HydratedA11yFrame['impact'], ReactNode> = {
minor: <IconInfo size="xs" />,
moderate: <IconInfo size="xs" />,
serious: <IconWarning size="xs" color={isSelected ? 'white' : 'yellow400'} />,
critical: <IconFire size="xs" color={isSelected ? 'white' : 'red400'} />,
};
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<typeof Cell>;
const renderFns = [
() => (
<StyledCell {...columnProps} impact={a11yIssue.impact} isRowSelected={isSelected}>
<Text>
{a11yIssue.impact ? (
<Tooltip title={a11yIssue.impact ?? EMPTY_CELL}>
{IMPACT_ICON_MAPPING[a11yIssue.impact]}
</Tooltip>
) : (
EMPTY_CELL
)}
</Text>
</StyledCell>
),
() => (
<StyledCell {...columnProps} impact={a11yIssue.impact} isRowSelected={isSelected}>
<Text>{a11yIssue.id ?? EMPTY_CELL}</Text>
</StyledCell>
),
() => (
<StyledCell {...columnProps} impact={a11yIssue.impact} isRowSelected={isSelected}>
<Tooltip
title={a11yIssue.element.element ?? EMPTY_CELL}
isHoverable
showOnlyOnOverflow
overlayStyle={{maxWidth: '500px !important'}}
>
<StyledTextOverflow>
{a11yIssue.element.element ?? EMPTY_CELL}
</StyledTextOverflow>
</Tooltip>
</StyledCell>
),
];
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'}
`;