Commit c318e74a authored by Tristan Read's avatar Tristan Read Committed by Andrew Fontaine

Add Alert Details header

parent f1ef6fdc
...@@ -2,18 +2,21 @@ ...@@ -2,18 +2,21 @@
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { import {
GlAlert, GlAlert,
GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlNewDropdown, GlNewDropdown,
GlNewDropdownItem, GlNewDropdownItem,
GlSprintf,
GlTabs, GlTabs,
GlTab, GlTab,
GlButton, GlButton,
} from '@gitlab/ui'; } from '@gitlab/ui';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import query from '../graphql/queries/details.query.graphql'; import query from '../graphql/queries/details.query.graphql';
import { fetchPolicies } from '~/lib/graphql'; import { fetchPolicies } from '~/lib/graphql';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { ALERTS_SEVERITY_LABELS } from '../constants';
export default { export default {
statuses: { statuses: {
...@@ -27,16 +30,21 @@ export default { ...@@ -27,16 +30,21 @@ export default {
), ),
fullAlertDetailsTitle: s__('AlertManagement|Full alert details'), fullAlertDetailsTitle: s__('AlertManagement|Full alert details'),
overviewTitle: s__('AlertManagement|Overview'), overviewTitle: s__('AlertManagement|Overview'),
reportedAt: s__('AlertManagement|Reported %{when}'),
reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'),
}, },
severityLabels: ALERTS_SEVERITY_LABELS,
components: { components: {
GlAlert, GlAlert,
GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlNewDropdown, GlNewDropdown,
GlNewDropdownItem, GlNewDropdownItem,
timeAgoTooltip, GlSprintf,
GlTab, GlTab,
GlTabs, GlTabs,
GlButton, GlButton,
TimeAgoTooltip,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
props: { props: {
...@@ -79,6 +87,11 @@ export default { ...@@ -79,6 +87,11 @@ export default {
loading() { loading() {
return this.$apollo.queries.alert.loading; return this.$apollo.queries.alert.loading;
}, },
reportedAtMessage() {
return this.alert?.monitoringTool
? this.$options.i18n.reportedAtWithTool
: this.$options.i18n.reportedAt;
},
showErrorMsg() { showErrorMsg() {
return this.errored && !this.isErrorDismissed; return this.errored && !this.isErrorDismissed;
}, },
...@@ -95,11 +108,31 @@ export default { ...@@ -95,11 +108,31 @@ export default {
<gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError"> <gl-alert v-if="showErrorMsg" variant="danger" @dismiss="dismissError">
{{ $options.i18n.errorMsg }} {{ $options.i18n.errorMsg }}
</gl-alert> </gl-alert>
<div v-if="loading"><gl-loading-icon size="lg" class="mt-3" /></div> <div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div>
<div v-if="alert" class="alert-management-details">
<div <div
v-if="alert" class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-px-1 gl-py-6 gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid"
class="gl-display-flex justify-content-end gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid gl-p-4" >
<div data-testid="alert-header">
<div
class="gl-display-inline-flex gl-align-items-center gl-justify-content-space-between"
> >
<gl-icon
class="gl-mr-3"
:size="12"
:name="`severity-${alert.severity.toLowerCase()}`"
:class="`icon-${alert.severity.toLowerCase()}`"
/>
<strong>{{ $options.severityLabels[alert.severity] }}</strong>
</div>
<span class="gl-shim-mx-2">&bull;</span>
<gl-sprintf :message="reportedAtMessage">
<template #when>
<time-ago-tooltip :time="alert.createdAt" />
</template>
<template #tool>{{ alert.monitoringTool }}</template>
</gl-sprintf>
</div>
<gl-button <gl-button
v-if="glFeatures.createIssueFromAlertEnabled" v-if="glFeatures.createIssueFromAlertEnabled"
data-testid="createIssueBtn" data-testid="createIssueBtn"
...@@ -120,7 +153,7 @@ export default { ...@@ -120,7 +153,7 @@ export default {
v-for="(label, field) in $options.statuses" v-for="(label, field) in $options.statuses"
:key="field" :key="field"
data-testid="statusDropdownItem" data-testid="statusDropdownItem"
class="align-middle" class="gl-vertical-align-middle"
>{{ label }} >{{ label }}
</gl-new-dropdown-item> </gl-new-dropdown-item>
</gl-new-dropdown> </gl-new-dropdown>
...@@ -149,4 +182,5 @@ export default { ...@@ -149,4 +182,5 @@ export default {
<gl-tab data-testid="fullDetailsTab" :title="$options.i18n.fullAlertDetailsTitle" /> <gl-tab data-testid="fullDetailsTab" :title="$options.i18n.fullAlertDetailsTitle" />
</gl-tabs> </gl-tabs>
</div> </div>
</div>
</template> </template>
...@@ -3,6 +3,7 @@ query alertDetails($fullPath: ID!, $alertId: String) { ...@@ -3,6 +3,7 @@ query alertDetails($fullPath: ID!, $alertId: String) {
alertManagementAlerts(iid: $alertId) { alertManagementAlerts(iid: $alertId) {
nodes { nodes {
iid iid
createdAt
endedAt endedAt
eventCount eventCount
monitoringTool monitoringTool
......
.alert-management-list { .alert-management-list,
.alert-management-details {
.icon-critical { .icon-critical {
color: $red-800; color: $red-800;
} }
......
...@@ -97,6 +97,11 @@ ...@@ -97,6 +97,11 @@
padding-top: 16px; padding-top: 16px;
} }
.gl-shim-mx-2 {
margin-left: 4px;
margin-right: 4px;
}
.gl-text-purple { color: $purple; } .gl-text-purple { color: $purple; }
.gl-text-gray-800 { color: $gray-800; } .gl-text-gray-800 { color: $gray-800; }
.gl-bg-purple-light { background-color: $purple-light; } .gl-bg-purple-light { background-color: $purple-light; }
......
...@@ -1769,6 +1769,12 @@ msgstr "" ...@@ -1769,6 +1769,12 @@ msgstr ""
msgid "AlertManagement|Overview" msgid "AlertManagement|Overview"
msgstr "" msgstr ""
msgid "AlertManagement|Reported %{when}"
msgstr ""
msgid "AlertManagement|Reported %{when} by %{tool}"
msgstr ""
msgid "AlertManagement|Resolved" msgid "AlertManagement|Resolved"
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import { GlAlert, GlLoadingIcon } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
import AlertDetails from '~/alert_management/components/alert_details.vue'; import AlertDetails from '~/alert_management/components/alert_details.vue';
...@@ -11,18 +11,20 @@ describe('AlertDetails', () => { ...@@ -11,18 +11,20 @@ describe('AlertDetails', () => {
const newIssuePath = 'root/alerts/-/issues/new'; const newIssuePath = 'root/alerts/-/issues/new';
function mountComponent({ function mountComponent({
data = { alert: {} }, data,
createIssueFromAlertEnabled = false, createIssueFromAlertEnabled = false,
loading = false, loading = false,
mountMethod = shallowMount,
stubs = {},
} = {}) { } = {}) {
wrapper = shallowMount(AlertDetails, { wrapper = mountMethod(AlertDetails, {
propsData: { propsData: {
alertId: 'alertId', alertId: 'alertId',
projectPath: 'projectPath', projectPath: 'projectPath',
newIssuePath, newIssuePath,
}, },
data() { data() {
return data; return { alert: { ...mockAlert }, ...data };
}, },
provide: { provide: {
glFeatures: { createIssueFromAlertEnabled }, glFeatures: { createIssueFromAlertEnabled },
...@@ -36,6 +38,7 @@ describe('AlertDetails', () => { ...@@ -36,6 +38,7 @@ describe('AlertDetails', () => {
}, },
}, },
}, },
stubs,
}); });
} }
...@@ -149,5 +152,33 @@ describe('AlertDetails', () => { ...@@ -149,5 +152,33 @@ describe('AlertDetails', () => {
expect(wrapper.find(GlAlert).exists()).toBe(false); expect(wrapper.find(GlAlert).exists()).toBe(false);
}); });
}); });
describe('header', () => {
const findHeader = () => wrapper.find('[data-testid="alert-header"]');
const stubs = { TimeAgoTooltip: '<span>now</span>' };
describe('individual header fields', () => {
describe.each`
severity | createdAt | monitoringTool | result
${'MEDIUM'} | ${'2020-04-17T23:18:14.996Z'} | ${null} | ${'Medium • Reported now'}
${'INFO'} | ${'2020-04-17T23:18:14.996Z'} | ${'Datadog'} | ${'Info • Reported now by Datadog'}
`(
`When severity=$severity, createdAt=$createdAt, monitoringTool=$monitoringTool`,
({ severity, createdAt, monitoringTool, result }) => {
beforeEach(() => {
mountComponent({
data: { alert: { ...mockAlert, severity, createdAt, monitoringTool } },
mountMethod: mount,
stubs,
});
});
it('header text is shown correctly', () => {
expect(findHeader().text()).toBe(result);
});
},
);
});
});
}); });
}); });
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