Skip to content

Commit 5c29cde

Browse files
authored
fix(perf): Tags page Add to filter button in table (#86839)
Fixes #78725 The Add to filter button was not working due to it using an older link component which does not work as well using the react-router version we are on now. This PR updates it use a simple clickable div with the `useNavigate` hook to achieve the intended functionality. The page had to be converted from a Class component to a FC as well, in order to make use of hooks. ![image](https://github.com/user-attachments/assets/c0f76e45-1b96-4f97-8a54-509fb83099c1)
1 parent 4ca32a0 commit 5c29cde

File tree

1 file changed

+106
-107
lines changed

1 file changed

+106
-107
lines changed

static/app/views/performance/transactionSummary/transactionTags/tagValueTable.tsx

+106-107
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import {Component} from 'react';
1+
import {useState} from 'react';
22
import styled from '@emotion/styled';
3-
import type {Location, LocationDescriptorObject} from 'history';
3+
import type {LocationDescriptorObject} from 'history';
44

55
import type {GridColumn} from 'sentry/components/gridEditable';
66
import GridEditable, {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
@@ -26,6 +26,8 @@ import type {
2626
import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
2727
import {decodeScalar} from 'sentry/utils/queryString';
2828
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
29+
import {useLocation} from 'sentry/utils/useLocation';
30+
import {useNavigate} from 'sentry/utils/useNavigate';
2931
import CellAction, {Actions, updateQuery} from 'sentry/views/discover/table/cellAction';
3032
import type {TableColumn} from 'sentry/views/discover/table/types';
3133

@@ -42,7 +44,6 @@ type Props = {
4244
aggregateColumn: string;
4345
eventView: EventView;
4446
isLoading: boolean;
45-
location: Location;
4647
organization: Organization;
4748
pageLinks: string | null;
4849
projects: Project[];
@@ -52,21 +53,26 @@ type Props = {
5253
tagKey?: string;
5354
};
5455

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 = (
6471
sortedEventView: EventView,
6572
tableMeta: TableData['meta'],
6673
column: TableColumn<TagsTableColumnKeys>,
6774
columnInfo: TagsTableColumn
68-
): React.ReactNode {
69-
const {location} = this.props;
75+
): React.ReactNode => {
7076
const align = fieldAlignment(column.key, column.type, tableMeta);
7177
const field = {field: column.key, width: column.width};
7278

@@ -96,40 +102,41 @@ export class TagValueTable extends Component<Props, State> {
96102
generateSortLink={generateSortLink}
97103
/>
98104
);
99-
}
105+
};
100106

101-
renderHeadCellWithMeta = (
107+
const renderHeadCellWithMeta = (
102108
sortedEventView: EventView,
103109
tableMeta: TableData['meta'],
104110
columns: TagsTableColumn[]
105111
) => {
106112
return (column: TableColumn<TagsTableColumnKeys>, index: number): React.ReactNode =>
107-
this.renderHeadCell(sortedEventView, tableMeta, column, columns[index]!);
113+
renderHeadCell(sortedEventView, tableMeta, column, columns[index]!);
108114
};
109115

110-
handleTagValueClick = (location: Location, tagKey: string, tagValue: string) => {
116+
const handleTagValueClick = (tagValue: string) => {
111117
const queryString = decodeScalar(location.query.query);
112118
const conditions = new MutableSearch(queryString ?? '');
113119

114-
conditions.addFilterValues(tagKey, [tagValue]);
120+
conditions.addFilterValues(tagKey ?? '', [tagValue]);
115121

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+
},
122129
},
123-
});
130+
{replace: false}
131+
);
124132
};
125133

126-
handleCellAction = (
134+
const handleCellAction = (
127135
column: TableColumn<TagsTableColumnKeys>,
128136
tagValue: string | number,
129137
actionRow: any
130138
) => {
131139
return (action: Actions) => {
132-
const {eventView, location, organization} = this.props;
133140
trackTagPageInteraction(organization);
134141

135142
const searchConditions = normalizeSearchConditions(eventView.query);
@@ -147,8 +154,7 @@ export class TagValueTable extends Component<Props, State> {
147154
};
148155
};
149156

150-
generateReleaseLocation = (release: string) => {
151-
const {organization, location} = this.props;
157+
const generateReleaseLocation = (release: string) => {
152158
const {project} = location.query;
153159

154160
return {
@@ -159,21 +165,18 @@ export class TagValueTable extends Component<Props, State> {
159165
};
160166
};
161167

162-
handleReleaseLinkClicked = () => {
163-
const {organization} = this.props;
168+
const handleReleaseLinkClicked = () => {
164169
trackAnalytics('performance_views.tags.jump_to_release', {
165170
organization,
166171
});
167172
};
168173

169-
renderBodyCell = (
170-
parentProps: Props,
174+
const renderBodyCell = (
171175
column: TableColumn<TagsTableColumnKeys>,
172176
dataRow: TableDataRow
173177
): React.ReactNode => {
174178
// @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
175179
const value = dataRow[column.key];
176-
const {location, eventView, organization} = parentProps;
177180

178181
if (column.key === 'key') {
179182
return dataRow.tags_key;
@@ -188,13 +191,13 @@ export class TagValueTable extends Component<Props, State> {
188191
<CellAction
189192
column={column}
190193
dataRow={actionRow}
191-
handleCellAction={this.handleCellAction(column, dataRow.tags_value, actionRow)}
194+
handleCellAction={handleCellAction(column, dataRow.tags_value, actionRow)}
192195
allowActions={allowActions}
193196
>
194197
{column.name === 'release' ? (
195198
<Link
196-
to={this.generateReleaseLocation(dataRow.tags_value)}
197-
onClick={this.handleReleaseLinkClicked}
199+
to={generateReleaseLocation(dataRow.tags_value)}
200+
onClick={handleReleaseLinkClicked}
198201
>
199202
<TagValue row={dataRow} />
200203
</Link>
@@ -214,19 +217,20 @@ export class TagValueTable extends Component<Props, State> {
214217
const disabled = searchConditions.hasFilter(dataRow.tags_key);
215218
return (
216219
<AlignRight>
217-
<Link
220+
<LinkContainer
218221
disabled={disabled}
219-
to=""
220222
onClick={() => {
223+
if (disabled) {
224+
return;
225+
}
226+
221227
trackTagPageInteraction(organization);
222-
this.handleTagValueClick(location, dataRow.tags_key, dataRow.tags_value);
228+
handleTagValueClick(dataRow.tags_value);
223229
}}
224230
>
225-
<LinkContainer>
226-
<IconAdd isCircled />
227-
{t('Add to filter')}
228-
</LinkContainer>
229-
</Link>
231+
<IconAdd isCircled />
232+
{t('Add to filter')}
233+
</LinkContainer>
230234
</AlignRight>
231235
);
232236
}
@@ -260,73 +264,59 @@ export class TagValueTable extends Component<Props, State> {
260264
return value;
261265
};
262266

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);
269271

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
273275
? Number(nextColumn.width)
274276
: COL_WIDTH_UNDEFINED;
275-
this.setState({widths});
277+
setWidths(newWidths);
276278
};
277279

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';
298288
}
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
307301
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+
);
330320
}
331321

332322
const StyledPanelTable = styled('div')`
@@ -341,12 +331,21 @@ const AlignRight = styled('div')`
341331
flex: 1;
342332
`;
343333

344-
const LinkContainer = styled('div')`
334+
const LinkContainer = styled('div')<{disabled?: boolean}>`
335+
cursor: pointer;
336+
color: ${p => p.theme.linkColor};
345337
display: grid;
346338
grid-auto-flow: column;
347339
gap: ${space(0.5)};
348340
justify-content: flex-end;
349341
align-items: center;
342+
${p =>
343+
p.disabled &&
344+
`
345+
opacity: 0.5;
346+
color: ${p.theme.gray300};
347+
cursor: default;
348+
`}
350349
`;
351350

352351
export default TagValueTable;

0 commit comments

Comments
 (0)