Commit 45457dfa authored by Olena Horal-Koretska's avatar Olena Horal-Koretska Committed by Natalia Tepluhina

Incident highlight bar widget

parent 4e24daa6
query getHighlightBarInfo($iid: String!, $fullPath: ID!) {
project(fullPath: $fullPath) {
issue(iid: $iid) {
alertManagementAlert {
title
detailsUrl
createdAt
eventCount
}
}
}
}
<script>
import { GlLink } from '@gitlab/ui';
import { formatDate } from '~/lib/utils/datetime_utility';
import getHighlightBarInfo from './graphql/queries/get_highlight_bar_info.graphql';
export default {
components: {
GlLink,
},
inject: ['fullPath', 'iid'],
apollo: {
alert: {
query: getHighlightBarInfo,
variables() {
return {
fullPath: this.fullPath,
iid: this.iid,
};
},
update: data => data.project?.issue?.alertManagementAlert,
},
},
computed: {
startTime() {
return formatDate(this.alert.createdAt, 'yyyy-mm-dd Z');
},
},
};
</script>
<template>
<div
v-if="alert"
class="gl-border-solid gl-border-1 gl-border-gray-100 gl-p-5 gl-mb-3 gl-rounded-base gl-display-flex gl-justify-content-space-between"
>
<div class="text-truncate gl-pr-3">
<span class="gl-font-weight-bold">{{ s__('HighlightBar|Original alert:') }}</span>
<gl-link :href="alert.detailsUrl">{{ alert.title }}</gl-link>
</div>
<div class="gl-pr-3 gl-white-space-nowrap">
<span class="gl-font-weight-bold">{{ s__('HighlightBar|Alert start time:') }}</span>
{{ startTime }}
</div>
<div class="gl-white-space-nowrap">
<span class="gl-font-weight-bold">{{ s__('HighlightBar|Alert events:') }}</span>
<span>{{ alert.eventCount }}</span>
</div>
</div>
</template>
<script>
import { GlTab, GlTabs } from '@gitlab/ui';
import DescriptionComponent from './description.vue';
import DescriptionComponent from '../description.vue';
import HighlightBar from './highlight_bar/higlight_bar.vue';
export default {
components: {
GlTab,
GlTabs,
DescriptionComponent,
HighlightBar,
},
};
</script>
<template>
<div>
<gl-tabs
content-class="gl-reset-line-height gl-mt-3"
class="gl-mt-n3"
data-testid="incident-tabs"
>
<gl-tabs content-class="gl-reset-line-height" class="gl-mt-n3" data-testid="incident-tabs">
<gl-tab :title="__('Summary')">
<highlight-bar />
<description-component v-bind="$attrs" />
</gl-tab>
</gl-tabs>
......
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import issuableApp from './components/app.vue';
import incidentTabs from './components/incident_tabs.vue';
import incidentTabs from './components/incidents/incident_tabs.vue';
Vue.use(VueApollo);
export default function initIssuableApp(issuableData = {}) {
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
const { projectNamespace, projectPath, iid } = issuableData;
return new Vue({
el: document.getElementById('js-issuable-app'),
apolloProvider,
components: {
issuableApp,
},
provide: {
fullPath: `${projectNamespace}/${projectPath}`,
iid,
},
render(createElement) {
return createElement('issuable-app', {
props: {
......
......@@ -294,7 +294,8 @@ module IssuablesHelper
hasClosingMergeRequest: issuable.merge_requests_count(current_user) != 0,
issueType: issuable.issue_type,
zoomMeetingUrl: ZoomMeeting.canonical_meeting_url(issuable),
sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier # rubocop:disable CodeReuse/ActiveRecord
sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier, # rubocop:disable CodeReuse/ActiveRecord
iid: issuable.iid.to_s
}
end
......
---
title: Incident highlight bar widget
merge_request: 41702
author:
type: added
......@@ -12807,6 +12807,15 @@ msgstr ""
msgid "Highest role:"
msgstr ""
msgid "HighlightBar|Alert events:"
msgstr ""
msgid "HighlightBar|Alert start time:"
msgstr ""
msgid "HighlightBar|Original alert:"
msgstr ""
msgid "History"
msgstr ""
......
......@@ -14,7 +14,7 @@ import {
secondRequest,
zoomMeetingUrl,
} from '../mock_data';
import IncidentTabs from '~/issue_show/components/incident_tabs.vue';
import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue';
import DescriptionComponent from '~/issue_show/components/description.vue';
import PinnedLinks from '~/issue_show/components/pinned_links.vue';
......@@ -39,6 +39,13 @@ describe('Issuable output', () => {
const mountComponent = (props = {}) => {
wrapper = mount(IssuableApp, {
propsData: { ...appProps, ...props },
provide: {
fullPath: 'gitlab-org/incidents',
iid: '19',
},
stubs: {
HighlightBar: true,
},
});
};
......
import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import HighlightBar from '~/issue_show/components/incidents/highlight_bar/higlight_bar.vue';
import { formatDate } from '~/lib/utils/datetime_utility';
jest.mock('~/lib/utils/datetime_utility');
describe('Highlight Bar', () => {
let wrapper;
const alert = {
createdAt: '2020-05-29T10:39:22Z',
detailsUrl: 'http://127.0.0.1:3000/root/unique-alerts/-/alert_management/1/details',
eventCount: 1,
title: 'Alert 1',
};
const mountComponent = () => {
wrapper = shallowMount(HighlightBar, {
provide: {
fullPath: 'project/id',
iid: '1',
},
data() {
return { alert };
},
});
};
beforeEach(() => {
mountComponent();
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
});
const findLink = () => wrapper.find(GlLink);
it('renders a link to the alert page', () => {
expect(findLink().exists()).toBe(true);
expect(findLink().attributes('href')).toBe(alert.detailsUrl);
expect(findLink().text()).toContain(alert.title);
});
it('renders formatted start time of the alert', () => {
const formattedDate = '2020-05-29 UTC';
formatDate.mockReturnValueOnce(formattedDate);
mountComponent();
expect(formatDate).toHaveBeenCalledWith(alert.createdAt, 'yyyy-mm-dd Z');
expect(wrapper.text()).toContain(formattedDate);
});
it('renders a number of alert events', () => {
expect(wrapper.text()).toContain(alert.eventCount);
});
});
import { shallowMount } from '@vue/test-utils';
import { GlTab } from '@gitlab/ui';
import IncidentTabs from '~/issue_show/components/incident_tabs.vue';
import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue';
import { descriptionProps } from '../mock_data';
import DescriptionComponent from '~/issue_show/components/description.vue';
import HighlightBar from '~/issue_show/components/incidents/highlight_bar/higlight_bar.vue';
describe('Incident Tabs component', () => {
let wrapper;
......@@ -25,6 +26,7 @@ describe('Incident Tabs component', () => {
const findTabs = () => wrapper.findAll(GlTab);
const findSummaryTab = () => findTabs().at(0);
const findDescriptionComponent = () => wrapper.find(DescriptionComponent);
const findHighlightBarComponent = () => wrapper.find(HighlightBar);
describe('default state', () => {
it('renders the summary tab', async () => {
......@@ -33,8 +35,9 @@ describe('Incident Tabs component', () => {
expect(findSummaryTab().attributes('title')).toBe('Summary');
});
it('renders the description component', () => {
it('renders the description component with highlight bar', () => {
expect(findDescriptionComponent().exists()).toBe(true);
expect(findHighlightBarComponent().exists()).toBe(true);
});
it('passes all props to the description component', () => {
......
......@@ -198,7 +198,7 @@ RSpec.describe IssuablesHelper do
initialDescriptionHtml: '<p dir="auto">issue text</p>',
initialDescriptionText: 'issue text',
initialTaskStatus: '0 of 0 tasks completed',
issueType: 'issue'
iid: issue.iid.to_s
}
expect(helper.issuable_initial_data(issue)).to match(hash_including(expected_data))
end
......
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