import type {CSSProperties} from 'react'; import {useCallback} from 'react'; import styled from '@emotion/styled'; import classNames from 'classnames'; import ErrorBoundary from 'sentry/components/errorBoundary'; import {Tooltip} from 'sentry/components/tooltip'; import {IconClose, IconInfo, IconWarning} from 'sentry/icons'; import {space} from 'sentry/styles/space'; import {BreadcrumbLevelType} from 'sentry/types/breadcrumbs'; import type useCrumbHandlers from 'sentry/utils/replays/hooks/useCrumbHandlers'; import type {BreadcrumbFrame, ConsoleFrame} from 'sentry/utils/replays/types'; import MessageFormatter from 'sentry/views/replays/detail/console/messageFormatter'; import TimestampButton from 'sentry/views/replays/detail/timestampButton'; import type {OnDimensionChange} from 'sentry/views/replays/detail/useVirtualizedInspector'; interface Props extends ReturnType { currentHoverTime: number | undefined; currentTime: number; frame: BreadcrumbFrame; index: number; startTimestampMs: number; style: CSSProperties; expandPaths?: string[]; onDimensionChange?: OnDimensionChange; } export default function ConsoleLogRow({ currentHoverTime, currentTime, expandPaths, frame, onMouseEnter, onMouseLeave, index, onClickTimestamp, onDimensionChange, startTimestampMs, style, }: Props) { const handleDimensionChange = useCallback( (path, expandedState, e) => onDimensionChange?.(index, path, expandedState, e), [onDimensionChange, index] ); const hasOccurred = currentTime >= frame.offsetMs; const isBeforeHover = currentHoverTime === undefined || currentHoverTime >= frame.offsetMs; return ( onMouseEnter(frame)} onMouseLeave={() => onMouseLeave(frame)} style={style} > { event.stopPropagation(); onClickTimestamp(frame); }} startTimestampMs={startTimestampMs} timestampMs={frame.timestampMs} /> ); } const ConsoleLog = styled('div')<{ hasOccurred: boolean; level: undefined | string; }>` display: grid; grid-template-columns: 12px 1fr max-content; gap: ${space(0.75)}; align-items: baseline; padding: ${space(0.5)} ${space(1)}; font-size: ${p => p.theme.fontSizeSmall}; background-color: ${p => ['warning', 'error'].includes(String(p.level)) ? p.theme.alert[String(p.level)].backgroundLight : 'inherit'}; color: ${p => p.theme.gray400}; /* Show the timestamp button "Play" icon when we hover the row. This is a really generic selector that could find many things, but for now it only targets the one thing that we expect. */ &:hover button > svg { visibility: visible; } `; const ICONS = { [BreadcrumbLevelType.ERROR]: ( ), [BreadcrumbLevelType.WARNING]: ( ), [BreadcrumbLevelType.INFO]: ( ), }; const MediumFontSize = styled('span')` font-size: ${p => p.theme.fontSizeMedium}; `; function ConsoleLevelIcon({level}: {level: string | undefined}) { return level && level in ICONS ? ( {ICONS[level]} ) : ( ); } const Message = styled('div')` font-family: ${p => p.theme.text.familyMono}; white-space: pre-wrap; word-break: break-word; `;