diff --git a/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx b/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx index ccbff2e2dcf86..a1ad9f1214b6e 100644 --- a/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx +++ b/static/app/components/replays/breadcrumbs/breadcrumbItem.tsx @@ -9,7 +9,6 @@ import {CodeSnippet} from 'sentry/components/codeSnippet'; import {Flex} from 'sentry/components/container/flex'; import ErrorBoundary from 'sentry/components/errorBoundary'; import Link from 'sentry/components/links/link'; -import ObjectInspector from 'sentry/components/objectInspector'; import PanelItem from 'sentry/components/panels/panelItem'; import {OpenReplayComparisonButton} from 'sentry/components/replays/breadcrumbs/openReplayComparisonButton'; import {useReplayContext} from 'sentry/components/replays/replayContext'; @@ -88,17 +87,19 @@ function BreadcrumbItem({ {description} ) : ( - - + { + onInspectorExpanded( + path, + Object.fromEntries(expandedPaths.map(item => [item, true])) + ); }} + data={description} + withAnnotatedText /> - + ); }, [description, expandPaths, onInspectorExpanded]); @@ -295,17 +296,19 @@ function WebVitalData({ } return ( - { - onInspectorExpanded( - path, - Object.fromEntries(expandedPaths.map(item => [item, true])) - ); - }} - data={webVitalData} - withAnnotatedText - /> + + { + onInspectorExpanded( + path, + Object.fromEntries(expandedPaths.map(item => [item, true])) + ); + }} + data={webVitalData} + withAnnotatedText + /> + ); } @@ -376,10 +379,6 @@ const CrumbIssueWrapper = styled('div')` color: ${p => p.theme.subText}; `; -const InspectorWrapper = styled('div')` - font-family: ${p => p.theme.text.familyMono}; -`; - const CrumbDetails = styled('div')` display: flex; flex-direction: column; @@ -521,4 +520,10 @@ const SelectorButton = styled(Button)` min-height: auto; `; +const Wrapper = styled('div')` + pre { + margin: 0; + } +`; + export default memo(BreadcrumbItem); diff --git a/static/app/utils/replays/getFrameDetails.tsx b/static/app/utils/replays/getFrameDetails.tsx index 4cd54bc56fd93..6e9df5e34f260 100644 --- a/static/app/utils/replays/getFrameDetails.tsx +++ b/static/app/utils/replays/getFrameDetails.tsx @@ -430,9 +430,9 @@ const MAPPER_FOR_FRAME: Record Details> = { const MAPPER_DEFAULT = (frame): Details => ({ color: 'gray300', - description: frame.message ?? '', - tabKey: TabKey.CONSOLE, - title: defaultTitle(frame), + description: frame.message ?? frame.data ?? '', + tabKey: TabKey.BREADCRUMBS, + title: toTitleCase(defaultTitle(frame)), icon: , }); diff --git a/static/app/utils/replays/replayReader.spec.tsx b/static/app/utils/replays/replayReader.spec.tsx index e1f0cc75640c8..9a61ea252ad5a 100644 --- a/static/app/utils/replays/replayReader.spec.tsx +++ b/static/app/utils/replays/replayReader.spec.tsx @@ -167,10 +167,11 @@ describe('ReplayReader', () => { }, { method: 'getConsoleFrames', - expected: [ - expect.objectContaining({category: 'console'}), - expect.objectContaining({category: 'redux.action'}), - ], + expected: [expect.objectContaining({category: 'console'})], + }, + { + method: 'getCustomFrames', + expected: [expect.objectContaining({category: 'redux.action'})], }, { method: 'getNetworkFrames', @@ -196,6 +197,7 @@ describe('ReplayReader', () => { expected: [ expect.objectContaining({category: 'replay.init'}), expect.objectContaining({category: 'ui.slowClickDetected'}), + expect.objectContaining({category: 'redux.action'}), expect.objectContaining({op: 'navigation.navigate'}), // prefer the nav span over the breadcrumb expect.objectContaining({category: 'ui.click'}), expect.objectContaining({category: 'ui.click'}), diff --git a/static/app/utils/replays/replayReader.tsx b/static/app/utils/replays/replayReader.tsx index 16c2bf8bef8f0..2a6d6f6d3ad6f 100644 --- a/static/app/utils/replays/replayReader.tsx +++ b/static/app/utils/replays/replayReader.tsx @@ -538,10 +538,7 @@ export default class ReplayReader { getErrorFrames = () => this._errors; getConsoleFrames = memoize(() => - this._sortedBreadcrumbFrames.filter( - frame => - frame.category === 'console' || !BreadcrumbCategories.includes(frame.category) - ) + this._sortedBreadcrumbFrames.filter(frame => frame.category === 'console') ); getNavigationFrames = memoize(() => @@ -589,11 +586,18 @@ export default class ReplayReader { this._sortedSpanFrames.filter((frame): frame is MemoryFrame => frame.op === 'memory') ); + getCustomFrames = memoize(() => + this._sortedBreadcrumbFrames.filter( + frame => !BreadcrumbCategories.includes(frame.category) + ) + ); + getChapterFrames = memoize(() => this._trimFramesToClipWindow( [ ...this.getPerfFrames(), ...this.getWebVitalFrames(), + ...this.getCustomFrames(), ...this._sortedBreadcrumbFrames.filter(frame => [ 'replay.hydrate-error', diff --git a/static/app/utils/replays/types.tsx b/static/app/utils/replays/types.tsx index cd7c710fdad96..2c004272f2bd8 100644 --- a/static/app/utils/replays/types.tsx +++ b/static/app/utils/replays/types.tsx @@ -350,6 +350,7 @@ export const BreadcrumbCategories = [ 'device.battery', 'device.connectivity', 'device.orientation', + 'feedback', 'navigation', 'replay.init', 'replay.mutations', diff --git a/static/app/views/replays/detail/breadcrumbs/useBreadcrumbFilters.tsx b/static/app/views/replays/detail/breadcrumbs/useBreadcrumbFilters.tsx index 7e90013f0343b..9ead461ded05e 100644 --- a/static/app/views/replays/detail/breadcrumbs/useBreadcrumbFilters.tsx +++ b/static/app/views/replays/detail/breadcrumbs/useBreadcrumbFilters.tsx @@ -52,6 +52,7 @@ const TYPE_TO_LABEL: Record = { tap: 'User Tap', device: 'Device', app: 'App', + custom: 'Custom', }; const OPORCATEGORY_TO_TYPE: Record = { @@ -114,6 +115,13 @@ function useBreadcrumbFilters({frames}: Options): Return { const type = useMemo(() => decodeList(query.f_b_type), [query.f_b_type]); const searchTerm = decodeScalar(query.f_b_search, '').toLowerCase(); + // add custom breadcrumbs to filter + frames.forEach(frame => { + if (!(getFrameOpOrCategory(frame) in OPORCATEGORY_TO_TYPE)) { + OPORCATEGORY_TO_TYPE[getFrameOpOrCategory(frame)] = 'custom'; + } + }); + const items = useMemo(() => { // flips OPORCATERGORY_TO_TYPE and prevents overwriting nav entry, nav entry becomes nav: ['navigation','navigation.push'] const TYPE_TO_OPORCATEGORY = Object.entries(OPORCATEGORY_TO_TYPE).reduce(