Skip to content

Commit 64d35c6

Browse files
authored
feat(alerts): Add control for filtering by alert type (#58323)
Add a control tor filtering by alert types on the alert list. - requires #58327 - relates to #58139
1 parent 67dab7a commit 64d35c6

File tree

4 files changed

+118
-5
lines changed

4 files changed

+118
-5
lines changed

static/app/views/alerts/filterBar.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ import ButtonBar from 'sentry/components/buttonBar';
55
import {CompactSelect} from 'sentry/components/compactSelect';
66
import {ProjectPageFilter} from 'sentry/components/organizations/projectPageFilter';
77
import SearchBar from 'sentry/components/searchBar';
8+
import {SegmentedControl} from 'sentry/components/segmentedControl';
89
import {t} from 'sentry/locale';
910
import {space} from 'sentry/styles/space';
1011

1112
import TeamFilter from './list/rules/teamFilter';
12-
import {getQueryStatus, getTeamParams} from './utils';
13+
import {DatasetOption, getQueryDataset, getQueryStatus, getTeamParams} from './utils';
1314

1415
interface Props {
1516
location: Location<any>;
1617
onChangeFilter: (activeFilters: string[]) => void;
1718
onChangeSearch: (query: string) => void;
1819
hasStatusFilters?: boolean;
20+
onChangeDataset?: (dataset: DatasetOption) => void;
1921
onChangeStatus?: (status: string) => void;
2022
}
2123

@@ -24,10 +26,12 @@ function FilterBar({
2426
onChangeSearch,
2527
onChangeFilter,
2628
onChangeStatus,
29+
onChangeDataset,
2730
hasStatusFilters,
2831
}: Props) {
2932
const selectedTeams = getTeamParams(location.query.team);
3033
const selectedStatus = getQueryStatus(location.query.status);
34+
const selectedDataset = getQueryDataset(location.query.dataset);
3135

3236
return (
3337
<Wrapper>
@@ -57,6 +61,28 @@ function FilterBar({
5761
onChange={({value}) => onChangeStatus(value)}
5862
/>
5963
)}
64+
{onChangeDataset && (
65+
<SegmentedControlWrapper>
66+
<SegmentedControl<DatasetOption>
67+
aria-label={t('Alert type')}
68+
value={selectedDataset}
69+
onChange={onChangeDataset}
70+
>
71+
<SegmentedControl.Item key={DatasetOption.ALL}>
72+
{t('All')}
73+
</SegmentedControl.Item>
74+
<SegmentedControl.Item key={DatasetOption.ERRORS}>
75+
{t('Errors')}
76+
</SegmentedControl.Item>
77+
<SegmentedControl.Item key={DatasetOption.SESSIONS}>
78+
{t('Sessions')}
79+
</SegmentedControl.Item>
80+
<SegmentedControl.Item key={DatasetOption.PERFORMANCE}>
81+
{t('Performance')}
82+
</SegmentedControl.Item>
83+
</SegmentedControl>
84+
</SegmentedControlWrapper>
85+
)}
6086
</FilterButtons>
6187
<SearchBar
6288
placeholder={t('Search by name')}
@@ -74,20 +100,24 @@ const Wrapper = styled('div')`
74100
gap: ${space(1.5)};
75101
margin-bottom: ${space(2)};
76102
77-
@media (min-width: ${p => p.theme.breakpoints.small}) {
103+
@media (min-width: ${p => p.theme.breakpoints.large}) {
78104
grid-template-columns: min-content 1fr;
79105
}
80106
`;
81107

82108
const FilterButtons = styled(ButtonBar)`
83-
@media (max-width: ${p => p.theme.breakpoints.small}) {
109+
@media (max-width: ${p => p.theme.breakpoints.large}) {
84110
display: flex;
85111
align-items: flex-start;
86112
gap: ${space(1.5)};
87113
}
88114
89-
@media (min-width: ${p => p.theme.breakpoints.small}) {
115+
@media (min-width: ${p => p.theme.breakpoints.large}) {
90116
display: grid;
91117
grid-auto-columns: minmax(auto, 300px);
92118
}
93119
`;
120+
121+
const SegmentedControlWrapper = styled('div')`
122+
width: max-content;
123+
`;

static/app/views/alerts/list/rules/alertRulesList.spec.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import OrganizationStore from 'sentry/stores/organizationStore';
1717
import ProjectsStore from 'sentry/stores/projectsStore';
1818
import TeamStore from 'sentry/stores/teamStore';
1919
import {IncidentStatus} from 'sentry/views/alerts/types';
20+
import {DatasetOption} from 'sentry/views/alerts/utils';
2021

2122
import AlertRulesList from './alertRulesList';
2223

@@ -292,6 +293,43 @@ describe('AlertRulesList', () => {
292293
);
293294
});
294295

296+
it('searches by alert type', async () => {
297+
const {routerContext, organization, router} = initializeOrg();
298+
render(<AlertRulesList />, {context: routerContext, organization});
299+
300+
const performanceControl = await screen.getByRole('radio', {name: 'Performance'});
301+
expect(performanceControl).toBeInTheDocument();
302+
await userEvent.click(performanceControl);
303+
304+
expect(router.push).toHaveBeenCalledWith(
305+
expect.objectContaining({
306+
query: {
307+
dataset: DatasetOption.PERFORMANCE,
308+
},
309+
})
310+
);
311+
});
312+
313+
it('calls api with correct query params when searching by alert type', () => {
314+
const {routerContext, organization} = initializeOrg({
315+
router: {
316+
location: {
317+
query: {
318+
dataset: DatasetOption.PERFORMANCE,
319+
},
320+
},
321+
},
322+
});
323+
render(<AlertRulesList />, {context: routerContext, organization});
324+
325+
expect(rulesMock).toHaveBeenCalledWith(
326+
'/organizations/org-slug/combined-rules/',
327+
expect.objectContaining({
328+
query: expect.objectContaining({dataset: ['generic_metrics', 'transactions']}),
329+
})
330+
);
331+
});
332+
295333
it('uses empty team query parameter when removing all teams', async () => {
296334
const {routerContext, organization, router} = initializeOrg({
297335
router: {

static/app/views/alerts/list/rules/alertRulesList.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,13 @@ import useRouter from 'sentry/utils/useRouter';
3838

3939
import FilterBar from '../../filterBar';
4040
import {AlertRuleType, CombinedMetricIssueAlerts} from '../../types';
41-
import {getTeamParams, isIssueAlert} from '../../utils';
41+
import {
42+
DatasetOption,
43+
datasetToQueryParam,
44+
getQueryDataset,
45+
getTeamParams,
46+
isIssueAlert,
47+
} from '../../utils';
4248
import AlertHeader from '../header';
4349

4450
import RuleListRow from './row';
@@ -50,6 +56,7 @@ function getAlertListQueryKey(orgSlug: string, query: Location['query']): ApiQue
5056
const queryParams = {...query};
5157
queryParams.expand = ['latestIncident', 'lastTriggered'];
5258
queryParams.team = getTeamParams(queryParams.team!);
59+
queryParams.dataset = datasetToQueryParam[getQueryDataset(queryParams.dataset!)];
5360

5461
if (!queryParams.sort) {
5562
queryParams.sort = defaultSort;
@@ -107,6 +114,17 @@ function AlertRulesList() {
107114
});
108115
};
109116

117+
const handleChangeDataset = (value: DatasetOption): void => {
118+
const {cursor: _cursor, page: _page, ...currentQuery} = location.query;
119+
router.push({
120+
pathname: location.pathname,
121+
query: {
122+
...currentQuery,
123+
dataset: value === DatasetOption.ALL ? undefined : value,
124+
},
125+
});
126+
};
127+
110128
const handleOwnerChange = (
111129
projectId: string,
112130
rule: CombinedMetricIssueAlerts,
@@ -179,6 +197,7 @@ function AlertRulesList() {
179197
<Layout.Main fullWidth>
180198
<FilterBar
181199
location={location}
200+
onChangeDataset={handleChangeDataset}
182201
onChangeFilter={handleChangeFilter}
183202
onChangeSearch={handleChangeSearch}
184203
/>

static/app/views/alerts/utils/index.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ export function isIssueAlert(
3737
return !data.hasOwnProperty('triggers');
3838
}
3939

40+
export enum DatasetOption {
41+
ALL = 'all',
42+
ERRORS = 'errors',
43+
SESSIONS = 'sessions',
44+
PERFORMANCE = 'performance',
45+
}
46+
4047
export const DATA_SOURCE_LABELS = {
4148
[Dataset.ERRORS]: t('Errors'),
4249
[Dataset.TRANSACTIONS]: t('Transactions'),
@@ -195,3 +202,22 @@ export function getTeamParams(team?: string | string[]): string[] {
195202

196203
return toArray(team);
197204
}
205+
206+
const datasetValues = new Set(Object.values(DatasetOption));
207+
export function getQueryDataset(dataset: any): DatasetOption {
208+
if ((datasetValues as Set<any>).has(dataset)) {
209+
return dataset as DatasetOption;
210+
}
211+
return DatasetOption.ALL;
212+
}
213+
214+
export const datasetToQueryParam: Record<DatasetOption, Dataset[] | undefined> = {
215+
[DatasetOption.ALL]: undefined,
216+
[DatasetOption.ERRORS]: [Dataset.ERRORS],
217+
[DatasetOption.SESSIONS]: [Dataset.METRICS],
218+
[DatasetOption.PERFORMANCE]: [
219+
Dataset.GENERIC_METRICS,
220+
// TODO(telemetry-experience): remove this once we migrated all performance alerts to generic metrics
221+
Dataset.TRANSACTIONS,
222+
],
223+
};

0 commit comments

Comments
 (0)