diff --git a/static/app/components/replays/breadcrumbs/breadcrumbDescription.tsx b/static/app/components/replays/breadcrumbs/breadcrumbDescription.tsx index 3363cb16fe9ae3..e76dee2a2ef78a 100644 --- a/static/app/components/replays/breadcrumbs/breadcrumbDescription.tsx +++ b/static/app/components/replays/breadcrumbs/breadcrumbDescription.tsx @@ -1,95 +1,66 @@ import type {ReactNode} from 'react'; -import {isValidElement} from 'react'; +import {useCallback} from 'react'; import styled from '@emotion/styled'; import {Button} from 'sentry/components/core/button'; +import {Flex} from 'sentry/components/core/layout'; +import {Text} from 'sentry/components/core/text'; import {Tooltip} from 'sentry/components/core/tooltip'; -import StructuredEventData from 'sentry/components/structuredEventData'; import {t} from 'sentry/locale'; -import {space} from 'sentry/styles/space'; +import {trackAnalytics} from 'sentry/utils/analytics'; import type {ReplayFrame, WebVitalFrame} from 'sentry/utils/replays/types'; import {isSpanFrame} from 'sentry/utils/replays/types'; -import type {OnExpandCallback} from 'sentry/views/replays/detail/useVirtualizedInspector'; +import useOrganization from 'sentry/utils/useOrganization'; interface Props { allowShowSnippet: boolean; description: ReactNode; frame: ReplayFrame | WebVitalFrame; - onClickViewHtml: (e: React.MouseEvent) => void; - onInspectorExpanded: OnExpandCallback; + onShowSnippet: () => void; showSnippet: boolean; - className?: string; - expandPaths?: string[]; } export function BreadcrumbDescription({ - description, allowShowSnippet, - showSnippet, + description, frame, - expandPaths, - onInspectorExpanded, - onClickViewHtml, + onShowSnippet, + showSnippet, }: Props) { - if ( - typeof description === 'string' || - (description !== undefined && isValidElement(description)) - ) { - return ( - - - {description} - - - {allowShowSnippet && - !showSnippet && - frame.data?.nodeId !== undefined && - !isSpanFrame(frame) && ( - - {t('View HTML')} - - )} - - ); - } + const organization = useOrganization(); + const handleViewHtml = useCallback( + (e: React.MouseEvent) => { + onShowSnippet(); + e.preventDefault(); + e.stopPropagation(); + trackAnalytics('replay.view-html', { + organization, + breadcrumb_type: 'category' in frame ? frame.category : 'unknown', + }); + }, + [onShowSnippet, organization, frame] + ); return ( - - { - onInspectorExpanded( - path, - Object.fromEntries(expandedPaths.map(item => [item, true])) - ); - }} - data={description} - withAnnotatedText - /> - + + + + {description} + + + + {allowShowSnippet && + !showSnippet && + frame.data?.nodeId !== undefined && + !isSpanFrame(frame) && ( + + {t('View HTML')} + + )} + ); } -const Description = styled(Tooltip)` - ${p => p.theme.overflowEllipsis}; - font-size: 0.7rem; - font-variant-numeric: tabular-nums; - line-height: ${p => p.theme.text.lineHeightBody}; - color: ${p => p.theme.subText}; -`; - -const DescriptionWrapper = styled('div')` - display: flex; - gap: ${space(1)}; - justify-content: space-between; -`; - -const ViewHtmlButton = styled(Button)` +const NoWrapButton = styled(Button)` white-space: nowrap; `; - -const Wrapper = styled('div')` - pre { - margin: 0; - } -`; diff --git a/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx b/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx index 4c962a77a113cf..1ea994a7bae3ca 100644 --- a/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx +++ b/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx @@ -1,5 +1,5 @@ import type {CSSProperties} from 'react'; -import {useCallback, useEffect, useRef} from 'react'; +import {isValidElement, useEffect, useRef} from 'react'; import {useTheme} from '@emotion/react'; import styled from '@emotion/styled'; @@ -8,17 +8,16 @@ import {BreadcrumbCodeSnippet} from 'sentry/components/replays/breadcrumbs/bread import {BreadcrumbComparisonButton} from 'sentry/components/replays/breadcrumbs/breadcrumbComparisonButton'; import {BreadcrumbDescription} from 'sentry/components/replays/breadcrumbs/breadcrumbDescription'; import {BreadcrumbIssueLink} from 'sentry/components/replays/breadcrumbs/breadcrumbIssueLink'; +import {BreadcrumbStructuredData} from 'sentry/components/replays/breadcrumbs/breadcrumbStructuredData'; import {BreadcrumbWebVital} from 'sentry/components/replays/breadcrumbs/breadcrumbWebVital'; import {Timeline} from 'sentry/components/timeline'; import {space} from 'sentry/styles/space'; -import {trackAnalytics} from 'sentry/utils/analytics'; import type {Extraction} from 'sentry/utils/replays/extractDomNodes'; import getFrameDetails from 'sentry/utils/replays/getFrameDetails'; import useExtractDomNodes from 'sentry/utils/replays/hooks/useExtractDomNodes'; import {useReplayReader} from 'sentry/utils/replays/playback/providers/replayReaderProvider'; import type {ReplayFrame} from 'sentry/utils/replays/types'; import {isErrorFrame} from 'sentry/utils/replays/types'; -import useOrganization from 'sentry/utils/useOrganization'; import TimestampButton from 'sentry/views/replays/detail/timestampButton'; import type {OnExpandCallback} from 'sentry/views/replays/detail/useVirtualizedInspector'; @@ -42,7 +41,7 @@ interface Props { updateDimensions?: () => void; } -function BreadcrumbItem({ +export default function BreadcrumbItem({ className, frame, expandPaths, @@ -62,7 +61,6 @@ function BreadcrumbItem({ const {colorGraphicsToken, description, title, icon} = getFrameDetails(frame); const colorHex = theme.tokens.graphics[colorGraphicsToken]; const replay = useReplayReader(); - const organization = useOrganization(); const {data: extraction, isPending} = useExtractDomNodes({ replay, frame, @@ -82,19 +80,6 @@ function BreadcrumbItem({ } }, [isPending, updateDimensions, showSnippet]); - const handleViewHtml = useCallback( - (e: React.MouseEvent) => { - onShowSnippet(); - e.preventDefault(); - e.stopPropagation(); - trackAnalytics('replay.view-html', { - organization, - breadcrumb_type: 'category' in frame ? frame.category : 'unknown', - }); - }, - [onShowSnippet, organization, frame] - ); - return ( onMouseLeave(frame)} > - + {typeof description === 'string' || + (description !== undefined && isValidElement(description)) ? ( + + ) : ( + + )} p.theme.fontSize.sm}; align-self: flex-start; `; - -export default BreadcrumbItem; diff --git a/static/app/components/replays/breadcrumbs/breadcrumbStructuredData.tsx b/static/app/components/replays/breadcrumbs/breadcrumbStructuredData.tsx new file mode 100644 index 00000000000000..cd1d3166030173 --- /dev/null +++ b/static/app/components/replays/breadcrumbs/breadcrumbStructuredData.tsx @@ -0,0 +1,39 @@ +import type {ReactNode} from 'react'; +import styled from '@emotion/styled'; + +import StructuredEventData from 'sentry/components/structuredEventData'; +import type {OnExpandCallback} from 'sentry/views/replays/detail/useVirtualizedInspector'; + +interface Props { + description: ReactNode; + onInspectorExpanded: OnExpandCallback; + expandPaths?: string[]; +} + +export function BreadcrumbStructuredData({ + description, + expandPaths, + onInspectorExpanded, +}: Props) { + return ( + + { + onInspectorExpanded( + path, + Object.fromEntries(expandedPaths.map(item => [item, true])) + ); + }} + data={description} + withAnnotatedText + /> + + ); +} + +const NoMarginWrapper = styled('div')` + pre { + margin: 0; + } +`;