Commit 25ad9b16 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska Committed by Natalia Tepluhina

Add alerts count badge to alerts tabs

parent 88edbd78
......@@ -10,14 +10,16 @@ import {
GlDropdownItem,
GlTabs,
GlTab,
GlBadge,
} from '@gitlab/ui';
import createFlash from '~/flash';
import { s__ } from '~/locale';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
import { fetchPolicies } from '~/lib/graphql';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
import getAlerts from '../graphql/queries/getAlerts.query.graphql';
import getAlerts from '../graphql/queries/get_alerts.query.graphql';
import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql';
import { ALERTS_STATUS, ALERTS_STATUS_TABS, ALERTS_SEVERITY_LABELS } from '../constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
......@@ -88,8 +90,8 @@ export default {
GlIcon,
GlTabs,
GlTab,
GlBadge,
},
mixins: [glFeatureFlagsMixin()],
props: {
projectPath: {
type: String,
......@@ -114,6 +116,7 @@ export default {
},
apollo: {
alerts: {
fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
query: getAlerts,
variables() {
return {
......@@ -122,12 +125,23 @@ export default {
};
},
update(data) {
return data.project.alertManagementAlerts.nodes;
return data.project?.alertManagementAlerts?.nodes;
},
error() {
this.errored = true;
},
},
alertsCount: {
query: getAlertsCountByStatus,
variables() {
return {
projectPath: this.projectPath,
};
},
update(data) {
return data.project?.alertManagementAlertStatusCounts;
},
},
},
data() {
return {
......@@ -139,7 +153,9 @@ export default {
},
computed: {
showNoAlertsMsg() {
return !this.errored && !this.loading && !this.alerts?.length && !this.isAlertDismissed;
return (
!this.errored && !this.loading && this.alertsCount?.all === 0 && !this.isAlertDismissed
);
},
showErrorMsg() {
return this.errored && !this.isErrorAlertDismissed;
......@@ -171,6 +187,7 @@ export default {
})
.then(() => {
this.$apollo.queries.alerts.refetch();
this.$apollo.queries.alertsCount.refetch();
})
.catch(() => {
createFlash(
......@@ -196,10 +213,13 @@ export default {
{{ $options.i18n.errorMsg }}
</gl-alert>
<gl-tabs v-if="glFeatures.alertListStatusFilteringEnabled" @input="filterAlertsByStatus">
<gl-tabs @input="filterAlertsByStatus">
<gl-tab v-for="tab in $options.statusTabs" :key="tab.status">
<template slot="title">
<span>{{ tab.title }}</span>
<gl-badge v-if="alertsCount" pill size="sm" class="gl-tab-counter-badge">
{{ alertsCount[tab.status.toLowerCase()] }}
</gl-badge>
</template>
</gl-tab>
</gl-tabs>
......
#import "./listItem.fragment.graphql"
#import "./list_item.fragment.graphql"
fragment AlertDetailItem on AlertManagementAlert {
...AlertListItem
......
#import "../fragments/listItem.fragment.graphql"
#import "../fragments/list_item.fragment.graphql"
query getAlerts($projectPath: ID!, $statuses: [AlertManagementStatus!]) {
project(fullPath: $projectPath) {
......
query getAlertsCount($projectPath: ID!) {
project(fullPath: $projectPath) {
alertManagementAlertStatusCounts {
all
open
acknowledged
resolved
triggered
}
}
}
......@@ -3,7 +3,6 @@
class Projects::AlertManagementController < Projects::ApplicationController
before_action :authorize_read_alert_management_alert!
before_action do
push_frontend_feature_flag(:alert_list_status_filtering_enabled)
push_frontend_feature_flag(:alert_management_create_alert_issue)
push_frontend_feature_flag(:alert_assignee, project)
end
......
---
title: Organize alerts by status tabs
merge_request: 32582
author:
type: added
......@@ -8,6 +8,7 @@ import {
GlDropdownItem,
GlIcon,
GlTab,
GlBadge,
} from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
......@@ -33,10 +34,19 @@ describe('AlertManagementList', () => {
const findLoader = () => wrapper.find(GlLoadingIcon);
const findStatusDropdown = () => wrapper.find(GlDropdown);
const findStatusFilterTabs = () => wrapper.findAll(GlTab);
const findStatusFilterBadge = () => wrapper.findAll(GlBadge);
const findDateFields = () => wrapper.findAll(TimeAgo);
const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem);
const findSeverityFields = () => wrapper.findAll('[data-testid="severityField"]');
const alertsCount = {
acknowledged: 6,
all: 16,
open: 14,
resolved: 2,
triggered: 10,
};
function mountComponent({
props = {
alertManagementEnabled: false,
......@@ -44,7 +54,6 @@ describe('AlertManagementList', () => {
},
data = {},
loading = false,
alertListStatusFilteringEnabled = false,
stubs = {},
} = {}) {
wrapper = mount(AlertManagementList, {
......@@ -54,11 +63,6 @@ describe('AlertManagementList', () => {
emptyAlertSvgPath: 'illustration/path',
...props,
},
provide: {
glFeatures: {
alertListStatusFilteringEnabled,
},
},
data() {
return data;
},
......@@ -93,42 +97,25 @@ describe('AlertManagementList', () => {
});
describe('Status Filter Tabs', () => {
describe('alertListStatusFilteringEnabled feature flag enabled', () => {
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts },
loading: false,
alertListStatusFilteringEnabled: true,
stubs: {
GlTab: true,
},
});
});
it('should display filter tabs for all statuses', () => {
const tabs = findStatusFilterTabs().wrappers;
tabs.forEach((tab, i) => {
expect(tab.text()).toContain(ALERTS_STATUS_TABS[i].title);
});
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, alertsCount },
loading: false,
stubs: {
GlTab: true,
},
});
});
describe('alertListStatusFilteringEnabled feature flag disabled', () => {
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts },
loading: false,
alertListStatusFilteringEnabled: false,
stubs: {
GlTab: true,
},
});
});
it('should display filter tabs with alerts count badge for each status', () => {
const tabs = findStatusFilterTabs().wrappers;
const badges = findStatusFilterBadge();
it('should NOT display tabs', () => {
expect(findStatusFilterTabs()).not.toExist();
tabs.forEach((tab, i) => {
const status = ALERTS_STATUS_TABS[i].status.toLowerCase();
expect(tab.text()).toContain(ALERTS_STATUS_TABS[i].title);
expect(badges.at(i).text()).toContain(alertsCount[status]);
});
});
});
......@@ -137,7 +124,7 @@ describe('AlertManagementList', () => {
it('loading state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: null },
data: { alerts: null, alertsCount: null },
loading: true,
});
expect(findAlertsTable().exists()).toBe(true);
......@@ -152,7 +139,7 @@ describe('AlertManagementList', () => {
it('error state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: null, errored: true },
data: { alerts: null, alertsCount: null, errored: true },
loading: false,
});
expect(findAlertsTable().exists()).toBe(true);
......@@ -169,7 +156,7 @@ describe('AlertManagementList', () => {
it('empty state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: [], errored: false },
data: { alerts: [], alertsCount: { all: 0 }, errored: false },
loading: false,
});
expect(findAlertsTable().exists()).toBe(true);
......@@ -186,7 +173,7 @@ describe('AlertManagementList', () => {
it('has data state', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false },
data: { alerts: mockAlerts, alertsCount, errored: false },
loading: false,
});
expect(findLoader().exists()).toBe(false);
......@@ -202,7 +189,7 @@ describe('AlertManagementList', () => {
it('displays status dropdown', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false },
data: { alerts: mockAlerts, alertsCount, errored: false },
loading: false,
});
expect(findStatusDropdown().exists()).toBe(true);
......@@ -211,7 +198,7 @@ describe('AlertManagementList', () => {
it('shows correct severity icons', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false },
data: { alerts: mockAlerts, alertsCount, errored: false },
loading: false,
});
......@@ -228,7 +215,7 @@ describe('AlertManagementList', () => {
it('renders severity text', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false },
data: { alerts: mockAlerts, alertsCount, errored: false },
loading: false,
});
......@@ -242,7 +229,7 @@ describe('AlertManagementList', () => {
it('navigates to the detail page when alert row is clicked', () => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false },
data: { alerts: mockAlerts, alertsCount, errored: false },
loading: false,
});
......@@ -266,6 +253,7 @@ describe('AlertManagementList', () => {
severity: 'high',
},
],
alertsCount,
errored: false,
},
loading: false,
......@@ -286,6 +274,7 @@ describe('AlertManagementList', () => {
severity: 'high',
},
],
alertsCount,
errored: false,
},
loading: false,
......@@ -312,7 +301,7 @@ describe('AlertManagementList', () => {
beforeEach(() => {
mountComponent({
props: { alertManagementEnabled: true, userCanEnableAlertManagement: true },
data: { alerts: mockAlerts, errored: false },
data: { alerts: mockAlerts, alertsCount, errored: false },
loading: false,
});
});
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment