diff --git a/static/app/components/events/interfaces/performance/eventTraceView.spec.tsx b/static/app/components/events/interfaces/performance/eventTraceView.spec.tsx
index a01c8f1e53f503..a14134f75d6d75 100644
--- a/static/app/components/events/interfaces/performance/eventTraceView.spec.tsx
+++ b/static/app/components/events/interfaces/performance/eventTraceView.spec.tsx
@@ -22,7 +22,7 @@ window.ResizeObserver = ResizeObserver;
describe('EventTraceView', () => {
const traceId = 'this-is-a-good-trace-id';
const {organization, project} = initializeData({
- features: ['profiling', 'issue-details-always-show-trace'],
+ features: ['profiling'],
});
const group = GroupFixture();
const event = EventFixture({
@@ -103,6 +103,32 @@ describe('EventTraceView', () => {
});
it('still renders trace link for performance issues', async () => {
+ const oneOtherIssueEvent: TraceEventResponse = {
+ data: [
+ {
+ // In issuePlatform, the message contains the title and the transaction
+ message: '/api/slow/ Slow DB Query SELECT "sentry_monitorcheckin"."monitor_id"',
+ timestamp: '2024-01-24T09:09:03+00:00',
+ 'issue.id': 1000,
+ project: project.slug,
+ 'project.name': project.name,
+ title: 'Slow DB Query',
+ id: 'abc',
+ transaction: 'n/a',
+ culprit: '/api/slow/',
+ 'event.type': '',
+ },
+ ],
+ meta: {fields: {}, units: {}},
+ };
+ MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/events/`,
+ body: oneOtherIssueEvent,
+ });
+ MockApiClient.addMockResponse({
+ url: `/organizations/${organization.slug}/projects/`,
+ body: [],
+ });
const perfGroup = GroupFixture({issueCategory: IssueCategory.PERFORMANCE});
const perfEvent = EventFixture({
occurrence: {
@@ -129,6 +155,9 @@ describe('EventTraceView', () => {
expect(
await screen.findByRole('link', {name: 'View Full Trace'})
).toBeInTheDocument();
+ expect(
+ screen.getByText('One other issue appears in the same trace.')
+ ).toBeInTheDocument();
});
it('does not render the trace preview if it has no transactions', async () => {
@@ -154,8 +183,5 @@ describe('EventTraceView', () => {
render();
expect(await screen.findByText('Trace')).toBeInTheDocument();
- expect(
- await screen.findByRole('link', {name: 'View Full Trace'})
- ).toBeInTheDocument();
});
});
diff --git a/static/app/components/events/interfaces/performance/eventTraceView.tsx b/static/app/components/events/interfaces/performance/eventTraceView.tsx
index f47817c661bb32..508a4ba2610c2b 100644
--- a/static/app/components/events/interfaces/performance/eventTraceView.tsx
+++ b/static/app/components/events/interfaces/performance/eventTraceView.tsx
@@ -1,7 +1,8 @@
-import {useMemo} from 'react';
+import {Fragment, useMemo} from 'react';
import styled from '@emotion/styled';
import {LinkButton} from 'sentry/components/button';
+import Link from 'sentry/components/links/link';
import {generateTraceTarget} from 'sentry/components/quickTrace/utils';
import {IconOpen} from 'sentry/icons';
import {t} from 'sentry/locale';
@@ -9,11 +10,13 @@ import {space} from 'sentry/styles/space';
import type {Event} from 'sentry/types/event';
import {type Group, IssueCategory} from 'sentry/types/group';
import type {Organization} from 'sentry/types/organization';
+import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {SectionKey} from 'sentry/views/issueDetails/streamline/context';
import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection';
-import {TraceDataSection} from 'sentry/views/issueDetails/traceDataSection';
+import {TraceIssueEvent} from 'sentry/views/issueDetails/traceTimeline/traceIssue';
+import {useTraceTimelineEvents} from 'sentry/views/issueDetails/traceTimeline/useTraceTimelineEvents';
import {IssuesTraceWaterfall} from 'sentry/views/performance/newTraceDetails/issuesTraceWaterfall';
import {useIssuesTraceTree} from 'sentry/views/performance/newTraceDetails/traceApi/useIssuesTraceTree';
import {useTrace} from 'sentry/views/performance/newTraceDetails/traceApi/useTrace';
@@ -128,16 +131,47 @@ function IssuesTraceOverlay({event}: {event: Event}) {
return (
- }
- aria-label={t('Open Trace')}
- to={traceTarget}
- />
+ } to={traceTarget}>
+ {t('View Full Trace')}
+
);
}
+function OneOtherIssueEvent({event}: {event: Event}) {
+ const location = useLocation();
+ const organization = useOrganization();
+ const {isLoading, oneOtherIssueEvent} = useTraceTimelineEvents({event});
+ useRouteAnalyticsParams(oneOtherIssueEvent ? {has_related_trace_issue: true} : {});
+
+ if (isLoading || !oneOtherIssueEvent) {
+ return null;
+ }
+
+ const traceTarget = generateTraceTarget(
+ event,
+ organization,
+ {
+ ...location,
+ query: {
+ ...location.query,
+ groupId: event.groupID,
+ },
+ },
+ TraceViewSources.ISSUE_DETAILS
+ );
+
+ return (
+
+
+ {t('One other issue appears in the same trace. ')}
+ {t('View Full Trace')}
+
+
+
+ );
+}
+
const IssuesTraceContainer = styled('div')`
position: relative;
`;
@@ -165,18 +199,14 @@ export function EventTraceView({group, event, organization}: EventTraceViewProps
}
const hasProfilingFeature = organization.features.includes('profiling');
- const hasIssueDetailsTrace = organization.features.includes(
- 'issue-details-always-show-trace'
- );
const hasTracePreviewFeature =
hasProfilingFeature &&
- hasIssueDetailsTrace &&
// Only display this for error or default events since performance events are handled elsewhere
group.issueCategory !== IssueCategory.PERFORMANCE;
return (
-
+
{hasTracePreviewFeature && (
= {};
@@ -28,6 +30,11 @@ export function TraceDataSection({event}: {event: Event}) {
return null;
}
+ const noEvents = !isLoading && traceEvents.length === 0;
+ if (hasStreamlinedUI && (!oneOtherIssueEvent || noEvents)) {
+ return null;
+ }
+
return (