123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- 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'};
- `;
|