@@ -3,6 +3,7 @@ import styled from '@emotion/styled';
3
3
import type { LocationDescriptor } from 'history' ;
4
4
5
5
import { LinkButton } from 'sentry/components/button' ;
6
+ import ExternalLink from 'sentry/components/links/externalLink' ;
6
7
import Link from 'sentry/components/links/link' ;
7
8
import { generateTraceTarget } from 'sentry/components/quickTrace/utils' ;
8
9
import { IconOpen } from 'sentry/icons' ;
@@ -11,6 +12,7 @@ import type {Event} from 'sentry/types/event';
11
12
import { type Group , IssueCategory } from 'sentry/types/group' ;
12
13
import type { Organization } from 'sentry/types/organization' ;
13
14
import { defined } from 'sentry/utils' ;
15
+ import { trackAnalytics } from 'sentry/utils/analytics' ;
14
16
import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams' ;
15
17
import { useLocation } from 'sentry/utils/useLocation' ;
16
18
import useOrganization from 'sentry/utils/useOrganization' ;
@@ -58,9 +60,15 @@ interface EventTraceViewInnerProps {
58
60
event : Event ;
59
61
organization : Organization ;
60
62
traceId : string ;
63
+ traceTarget : LocationDescriptor ;
61
64
}
62
65
63
- function EventTraceViewInner ( { event, organization, traceId} : EventTraceViewInnerProps ) {
66
+ function EventTraceViewInner ( {
67
+ event,
68
+ organization,
69
+ traceId,
70
+ traceTarget,
71
+ } : EventTraceViewInnerProps ) {
64
72
const timestamp = new Date ( event . dateReceived ) . getTime ( ) / 1e3 ;
65
73
66
74
const trace = useTrace ( {
@@ -108,7 +116,14 @@ function EventTraceViewInner({event, organization, traceId}: EventTraceViewInner
108
116
replay = { null }
109
117
event = { event }
110
118
/>
111
- < IssuesTraceOverlay event = { event } />
119
+ < IssuesTraceOverlayContainer
120
+ href = { getHrefFromTraceTarget ( traceTarget ) }
121
+ onClick = { ( ) => {
122
+ trackAnalytics ( 'issue_details.view_full_trace_waterfall_clicked' , {
123
+ organization,
124
+ } ) ;
125
+ } }
126
+ />
112
127
</ IssuesTraceContainer >
113
128
</ TraceStateProvider >
114
129
) ;
@@ -129,39 +144,6 @@ function getHrefFromTraceTarget(traceTarget: LocationDescriptor) {
129
144
return `${ traceTarget . pathname } ?${ searchParams . toString ( ) } ` ;
130
145
}
131
146
132
- function IssuesTraceOverlay ( { event} : { event : Event } ) {
133
- const location = useLocation ( ) ;
134
- const organization = useOrganization ( ) ;
135
-
136
- const traceTarget = generateTraceTarget (
137
- event ,
138
- organization ,
139
- {
140
- ...location ,
141
- query : {
142
- ...location . query ,
143
- groupId : event . groupID ,
144
- } ,
145
- } ,
146
- TraceViewSources . ISSUE_DETAILS
147
- ) ;
148
-
149
- return (
150
- < IssuesTraceOverlayContainer >
151
- < LinkButton
152
- size = "sm"
153
- icon = { < IconOpen /> }
154
- href = { getHrefFromTraceTarget ( traceTarget ) }
155
- external
156
- analyticsEventName = "Issue Details: View Full Trace"
157
- analyticsEventKey = "issue_details.view_full_trace"
158
- >
159
- { t ( 'View Full Trace' ) }
160
- </ LinkButton >
161
- </ IssuesTraceOverlayContainer >
162
- ) ;
163
- }
164
-
165
147
function OneOtherIssueEvent ( { event} : { event : Event } ) {
166
148
const location = useLocation ( ) ;
167
149
const organization = useOrganization ( ) ;
@@ -200,52 +182,69 @@ const IssuesTraceContainer = styled('div')`
200
182
position: relative;
201
183
` ;
202
184
203
- const IssuesTraceOverlayContainer = styled ( 'div' ) `
185
+ const IssuesTraceOverlayContainer = styled ( ExternalLink ) `
204
186
position: absolute;
205
187
inset: 0;
206
188
z-index: 10;
207
-
208
- a {
209
- display: none;
210
- position: absolute;
211
- top: 50%;
212
- left: 50%;
213
- transform: translate(-50%, -50%);
214
- }
215
-
216
- &:hover {
217
- background-color: rgba(128, 128, 128, 0.4);
218
-
219
- a {
220
- display: block;
221
- }
222
- }
223
189
` ;
224
190
225
- interface EventTraceViewProps extends Omit < EventTraceViewInnerProps , 'traceId' > {
191
+ interface EventTraceViewProps {
192
+ event : Event ;
226
193
group : Group ;
194
+ organization : Organization ;
227
195
}
228
196
229
197
export function EventTraceView ( { group, event, organization} : EventTraceViewProps ) {
230
198
const traceId = event . contexts . trace ?. trace_id ;
199
+ const location = useLocation ( ) ;
200
+
231
201
if ( ! traceId ) {
232
202
return null ;
233
203
}
234
204
205
+ const traceTarget = generateTraceTarget (
206
+ event ,
207
+ organization ,
208
+ {
209
+ ...location ,
210
+ query : {
211
+ ...location . query ,
212
+ groupId : event . groupID ,
213
+ } ,
214
+ } ,
215
+ TraceViewSources . ISSUE_DETAILS
216
+ ) ;
217
+
235
218
const hasProfilingFeature = organization . features . includes ( 'profiling' ) ;
236
219
const hasTracePreviewFeature =
237
220
hasProfilingFeature &&
238
221
// Only display this for error or default events since performance events are handled elsewhere
239
222
group . issueCategory !== IssueCategory . PERFORMANCE ;
240
223
241
224
return (
242
- < InterimSection type = { SectionKey . TRACE } title = { t ( 'Trace' ) } >
225
+ < InterimSection
226
+ type = { SectionKey . TRACE }
227
+ title = { t ( 'Trace Preview' ) }
228
+ actions = {
229
+ < LinkButton
230
+ size = "xs"
231
+ icon = { < IconOpen /> }
232
+ href = { getHrefFromTraceTarget ( traceTarget ) }
233
+ external
234
+ analyticsEventName = "Issue Details: View Full Trace Action Button Clicked"
235
+ analyticsEventKey = "issue_details.view_full_trace_action_button_clicked"
236
+ >
237
+ { t ( 'View Full Trace' ) }
238
+ </ LinkButton >
239
+ }
240
+ >
243
241
< OneOtherIssueEvent event = { event } />
244
242
{ hasTracePreviewFeature && (
245
243
< EventTraceViewInner
246
244
event = { event }
247
245
organization = { organization }
248
246
traceId = { traceId }
247
+ traceTarget = { traceTarget }
249
248
/>
250
249
) }
251
250
</ InterimSection >
0 commit comments