1
- import { Component } from 'react' ;
1
+ import { useState } from 'react' ;
2
2
import styled from '@emotion/styled' ;
3
- import type { Location , LocationDescriptorObject } from 'history' ;
3
+ import type { LocationDescriptorObject } from 'history' ;
4
4
5
5
import type { GridColumn } from 'sentry/components/gridEditable' ;
6
6
import GridEditable , { COL_WIDTH_UNDEFINED } from 'sentry/components/gridEditable' ;
@@ -26,6 +26,8 @@ import type {
26
26
import { VisuallyCompleteWithData } from 'sentry/utils/performanceForSentry' ;
27
27
import { decodeScalar } from 'sentry/utils/queryString' ;
28
28
import { MutableSearch } from 'sentry/utils/tokenizeSearch' ;
29
+ import { useLocation } from 'sentry/utils/useLocation' ;
30
+ import { useNavigate } from 'sentry/utils/useNavigate' ;
29
31
import CellAction , { Actions , updateQuery } from 'sentry/views/discover/table/cellAction' ;
30
32
import type { TableColumn } from 'sentry/views/discover/table/types' ;
31
33
@@ -42,7 +44,6 @@ type Props = {
42
44
aggregateColumn : string ;
43
45
eventView : EventView ;
44
46
isLoading : boolean ;
45
- location : Location ;
46
47
organization : Organization ;
47
48
pageLinks : string | null ;
48
49
projects : Project [ ] ;
@@ -52,21 +53,26 @@ type Props = {
52
53
tagKey ?: string ;
53
54
} ;
54
55
55
- type State = {
56
- widths : number [ ] ;
57
- } ;
58
-
59
- export class TagValueTable extends Component < Props , State > {
60
- state : State = {
61
- widths : [ ] ,
62
- } ;
63
- renderHeadCell (
56
+ export function TagValueTable ( {
57
+ aggregateColumn,
58
+ eventView,
59
+ isLoading,
60
+ organization,
61
+ pageLinks,
62
+ tableData,
63
+ onCursor,
64
+ tagKey,
65
+ } : Props ) {
66
+ const [ widths , setWidths ] = useState < number [ ] > ( [ ] ) ;
67
+ const navigate = useNavigate ( ) ;
68
+ const location = useLocation ( ) ;
69
+
70
+ const renderHeadCell = (
64
71
sortedEventView : EventView ,
65
72
tableMeta : TableData [ 'meta' ] ,
66
73
column : TableColumn < TagsTableColumnKeys > ,
67
74
columnInfo : TagsTableColumn
68
- ) : React . ReactNode {
69
- const { location} = this . props ;
75
+ ) : React . ReactNode => {
70
76
const align = fieldAlignment ( column . key , column . type , tableMeta ) ;
71
77
const field = { field : column . key , width : column . width } ;
72
78
@@ -96,40 +102,41 @@ export class TagValueTable extends Component<Props, State> {
96
102
generateSortLink = { generateSortLink }
97
103
/>
98
104
) ;
99
- }
105
+ } ;
100
106
101
- renderHeadCellWithMeta = (
107
+ const renderHeadCellWithMeta = (
102
108
sortedEventView : EventView ,
103
109
tableMeta : TableData [ 'meta' ] ,
104
110
columns : TagsTableColumn [ ]
105
111
) => {
106
112
return ( column : TableColumn < TagsTableColumnKeys > , index : number ) : React . ReactNode =>
107
- this . renderHeadCell ( sortedEventView , tableMeta , column , columns [ index ] ! ) ;
113
+ renderHeadCell ( sortedEventView , tableMeta , column , columns [ index ] ! ) ;
108
114
} ;
109
115
110
- handleTagValueClick = ( location : Location , tagKey : string , tagValue : string ) => {
116
+ const handleTagValueClick = ( tagValue : string ) => {
111
117
const queryString = decodeScalar ( location . query . query ) ;
112
118
const conditions = new MutableSearch ( queryString ?? '' ) ;
113
119
114
- conditions . addFilterValues ( tagKey , [ tagValue ] ) ;
120
+ conditions . addFilterValues ( tagKey ?? '' , [ tagValue ] ) ;
115
121
116
- const query = conditions . formatString ( ) ;
117
- browserHistory . push ( {
118
- pathname : location . pathname ,
119
- query : {
120
- ...location . query ,
121
- query : String ( query ) . trim ( ) ,
122
+ navigate (
123
+ {
124
+ ...location ,
125
+ query : {
126
+ ...location . query ,
127
+ query : conditions . formatString ( ) ,
128
+ } ,
122
129
} ,
123
- } ) ;
130
+ { replace : false }
131
+ ) ;
124
132
} ;
125
133
126
- handleCellAction = (
134
+ const handleCellAction = (
127
135
column : TableColumn < TagsTableColumnKeys > ,
128
136
tagValue : string | number ,
129
137
actionRow : any
130
138
) => {
131
139
return ( action : Actions ) => {
132
- const { eventView, location, organization} = this . props ;
133
140
trackTagPageInteraction ( organization ) ;
134
141
135
142
const searchConditions = normalizeSearchConditions ( eventView . query ) ;
@@ -147,8 +154,7 @@ export class TagValueTable extends Component<Props, State> {
147
154
} ;
148
155
} ;
149
156
150
- generateReleaseLocation = ( release : string ) => {
151
- const { organization, location} = this . props ;
157
+ const generateReleaseLocation = ( release : string ) => {
152
158
const { project} = location . query ;
153
159
154
160
return {
@@ -159,21 +165,18 @@ export class TagValueTable extends Component<Props, State> {
159
165
} ;
160
166
} ;
161
167
162
- handleReleaseLinkClicked = ( ) => {
163
- const { organization} = this . props ;
168
+ const handleReleaseLinkClicked = ( ) => {
164
169
trackAnalytics ( 'performance_views.tags.jump_to_release' , {
165
170
organization,
166
171
} ) ;
167
172
} ;
168
173
169
- renderBodyCell = (
170
- parentProps : Props ,
174
+ const renderBodyCell = (
171
175
column : TableColumn < TagsTableColumnKeys > ,
172
176
dataRow : TableDataRow
173
177
) : React . ReactNode => {
174
178
// @ts -expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
175
179
const value = dataRow [ column . key ] ;
176
- const { location, eventView, organization} = parentProps ;
177
180
178
181
if ( column . key === 'key' ) {
179
182
return dataRow . tags_key ;
@@ -188,13 +191,13 @@ export class TagValueTable extends Component<Props, State> {
188
191
< CellAction
189
192
column = { column }
190
193
dataRow = { actionRow }
191
- handleCellAction = { this . handleCellAction ( column , dataRow . tags_value , actionRow ) }
194
+ handleCellAction = { handleCellAction ( column , dataRow . tags_value , actionRow ) }
192
195
allowActions = { allowActions }
193
196
>
194
197
{ column . name === 'release' ? (
195
198
< Link
196
- to = { this . generateReleaseLocation ( dataRow . tags_value ) }
197
- onClick = { this . handleReleaseLinkClicked }
199
+ to = { generateReleaseLocation ( dataRow . tags_value ) }
200
+ onClick = { handleReleaseLinkClicked }
198
201
>
199
202
< TagValue row = { dataRow } />
200
203
</ Link >
@@ -214,19 +217,20 @@ export class TagValueTable extends Component<Props, State> {
214
217
const disabled = searchConditions . hasFilter ( dataRow . tags_key ) ;
215
218
return (
216
219
< AlignRight >
217
- < Link
220
+ < LinkContainer
218
221
disabled = { disabled }
219
- to = ""
220
222
onClick = { ( ) => {
223
+ if ( disabled ) {
224
+ return ;
225
+ }
226
+
221
227
trackTagPageInteraction ( organization ) ;
222
- this . handleTagValueClick ( location , dataRow . tags_key , dataRow . tags_value ) ;
228
+ handleTagValueClick ( dataRow . tags_value ) ;
223
229
} }
224
230
>
225
- < LinkContainer >
226
- < IconAdd isCircled />
227
- { t ( 'Add to filter' ) }
228
- </ LinkContainer >
229
- </ Link >
231
+ < IconAdd isCircled />
232
+ { t ( 'Add to filter' ) }
233
+ </ LinkContainer >
230
234
</ AlignRight >
231
235
) ;
232
236
}
@@ -260,73 +264,59 @@ export class TagValueTable extends Component<Props, State> {
260
264
return value ;
261
265
} ;
262
266
263
- renderBodyCellWithData = ( parentProps : Props ) => {
264
- return (
265
- column : TableColumn < TagsTableColumnKeys > ,
266
- dataRow : TableDataRow
267
- ) : React . ReactNode => this . renderBodyCell ( parentProps , column , dataRow ) ;
268
- } ;
267
+ const renderBodyCellWithData = (
268
+ column : TableColumn < TagsTableColumnKeys > ,
269
+ dataRow : TableDataRow
270
+ ) : React . ReactNode => renderBodyCell ( column , dataRow ) ;
269
271
270
- handleResizeColumn = ( columnIndex : number , nextColumn : GridColumn ) => {
271
- const widths : number [ ] = [ ... this . state . widths ] ;
272
- widths [ columnIndex ] = nextColumn . width
272
+ const handleResizeColumn = ( columnIndex : number , nextColumn : GridColumn ) => {
273
+ const newWidths : number [ ] = [ ...widths ] ;
274
+ newWidths [ columnIndex ] = nextColumn . width
273
275
? Number ( nextColumn . width )
274
276
: COL_WIDTH_UNDEFINED ;
275
- this . setState ( { widths } ) ;
277
+ setWidths ( newWidths ) ;
276
278
} ;
277
279
278
- render ( ) {
279
- const {
280
- eventView,
281
- tagKey,
282
- isLoading,
283
- tableData,
284
- aggregateColumn,
285
- pageLinks,
286
- onCursor,
287
- } = this . props ;
288
-
289
- const newColumns = [ ...TAGS_TABLE_COLUMN_ORDER ] . map ( c => {
290
- const newColumn = { ...c } ;
291
- if ( c . key === 'tagValue' && tagKey ) {
292
- newColumn . name = tagKey ;
293
- }
294
- if ( c . key === 'aggregate' ) {
295
- if ( aggregateColumn === 'measurements.lcp' ) {
296
- newColumn . name = 'Avg LCP' ;
297
- }
280
+ const newColumns = [ ...TAGS_TABLE_COLUMN_ORDER ] . map ( c => {
281
+ const newColumn = { ...c } ;
282
+ if ( c . key === 'tagValue' && tagKey ) {
283
+ newColumn . name = tagKey ;
284
+ }
285
+ if ( c . key === 'aggregate' ) {
286
+ if ( aggregateColumn === 'measurements.lcp' ) {
287
+ newColumn . name = 'Avg LCP' ;
298
288
}
299
- return newColumn ;
300
- } ) ;
301
-
302
- return (
303
- < StyledPanelTable >
304
- < VisuallyCompleteWithData
305
- id = "TransactionTags-TagValueTable"
306
- hasData = { ! ! tableData ?. data ?. length }
289
+ }
290
+ return newColumn ;
291
+ } ) ;
292
+
293
+ return (
294
+ < StyledPanelTable >
295
+ < VisuallyCompleteWithData
296
+ id = "TransactionTags-TagValueTable"
297
+ hasData = { ! ! tableData ?. data ?. length }
298
+ isLoading = { isLoading }
299
+ >
300
+ < GridEditable
307
301
isLoading = { isLoading }
308
- >
309
- < GridEditable
310
- isLoading = { isLoading }
311
- data = { tableData ?. data ? tableData . data : [ ] }
312
- columnOrder = { newColumns }
313
- columnSortBy = { [ ] }
314
- grid = { {
315
- renderHeadCell : this . renderHeadCellWithMeta (
316
- eventView ,
317
- tableData ? tableData . meta : { } ,
318
- newColumns
319
- ) as any ,
320
- renderBodyCell : this . renderBodyCellWithData ( this . props ) as any ,
321
- onResizeColumn : this . handleResizeColumn ,
322
- } }
323
- />
324
- </ VisuallyCompleteWithData >
325
-
326
- < Pagination pageLinks = { pageLinks } onCursor = { onCursor } size = "sm" />
327
- </ StyledPanelTable >
328
- ) ;
329
- }
302
+ data = { tableData ?. data ? tableData . data : [ ] }
303
+ columnOrder = { newColumns }
304
+ columnSortBy = { [ ] }
305
+ grid = { {
306
+ renderHeadCell : renderHeadCellWithMeta (
307
+ eventView ,
308
+ tableData ? tableData . meta : { } ,
309
+ newColumns
310
+ ) as any ,
311
+ renderBodyCell : renderBodyCellWithData as any ,
312
+ onResizeColumn : handleResizeColumn ,
313
+ } }
314
+ />
315
+ </ VisuallyCompleteWithData >
316
+
317
+ < Pagination pageLinks = { pageLinks } onCursor = { onCursor } size = "sm" />
318
+ </ StyledPanelTable >
319
+ ) ;
330
320
}
331
321
332
322
const StyledPanelTable = styled ( 'div' ) `
@@ -341,12 +331,21 @@ const AlignRight = styled('div')`
341
331
flex: 1;
342
332
` ;
343
333
344
- const LinkContainer = styled ( 'div' ) `
334
+ const LinkContainer = styled ( 'div' ) < { disabled ?: boolean } > `
335
+ cursor: pointer;
336
+ color: ${ p => p . theme . linkColor } ;
345
337
display: grid;
346
338
grid-auto-flow: column;
347
339
gap: ${ space ( 0.5 ) } ;
348
340
justify-content: flex-end;
349
341
align-items: center;
342
+ ${ p =>
343
+ p . disabled &&
344
+ `
345
+ opacity: 0.5;
346
+ color: ${ p . theme . gray300 } ;
347
+ cursor: default;
348
+ ` }
350
349
` ;
351
350
352
351
export default TagValueTable ;
0 commit comments