Commit ee2a4a63 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch 'add-timeline-tab-timeline-event' into 'master'

Add timeline tab for incident view

See merge request gitlab-org/gitlab!80802
parents 3205338f 8cce3941
...@@ -5,6 +5,7 @@ import { trackIncidentDetailsViewsOptions } from '~/incidents/constants'; ...@@ -5,6 +5,7 @@ import { trackIncidentDetailsViewsOptions } from '~/incidents/constants';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DescriptionComponent from '../description.vue'; import DescriptionComponent from '../description.vue';
import getAlert from './graphql/queries/get_alert.graphql'; import getAlert from './graphql/queries/get_alert.graphql';
import HighlightBar from './highlight_bar.vue'; import HighlightBar from './highlight_bar.vue';
...@@ -17,7 +18,10 @@ export default { ...@@ -17,7 +18,10 @@ export default {
GlTabs, GlTabs,
HighlightBar, HighlightBar,
MetricsTab: () => import('ee_component/issues/show/components/incidents/metrics_tab.vue'), MetricsTab: () => import('ee_component/issues/show/components/incidents/metrics_tab.vue'),
TimelineTab: () =>
import('ee_component/issues/show/components/incidents/timeline_events_tab.vue'),
}, },
mixins: [glFeatureFlagsMixin()],
inject: ['fullPath', 'iid', 'uploadMetricsFeatureAvailable'], inject: ['fullPath', 'iid', 'uploadMetricsFeatureAvailable'],
apollo: { apollo: {
alert: { alert: {
...@@ -47,6 +51,9 @@ export default { ...@@ -47,6 +51,9 @@ export default {
loading() { loading() {
return this.$apollo.queries.alert.loading; return this.$apollo.queries.alert.loading;
}, },
incidentTabEnabled() {
return this.glFeatures.incidentTimelineEvents && this.glFeatures.incidentTimelineEventTab;
},
}, },
mounted() { mounted() {
this.trackPageViews(); this.trackPageViews();
...@@ -76,6 +83,7 @@ export default { ...@@ -76,6 +83,7 @@ export default {
> >
<alert-details-table :alert="alert" :loading="loading" /> <alert-details-table :alert="alert" :loading="loading" />
</gl-tab> </gl-tab>
<timeline-tab v-if="incidentTabEnabled" data-testid="timeline-events-tab" />
</gl-tabs> </gl-tabs>
</div> </div>
</template> </template>
...@@ -8,6 +8,8 @@ class Projects::IncidentsController < Projects::ApplicationController ...@@ -8,6 +8,8 @@ class Projects::IncidentsController < Projects::ApplicationController
before_action :load_incident, only: [:show] before_action :load_incident, only: [:show]
before_action do before_action do
push_frontend_feature_flag(:incident_escalations, @project) push_frontend_feature_flag(:incident_escalations, @project)
push_frontend_feature_flag(:incident_timeline_event_tab, @project, default_enabled: :yaml)
push_licensed_feature(:incident_timeline_events) if @project.licensed_feature_available?(:incident_timeline_events)
end end
feature_category :incident_management feature_category :incident_management
......
---
name: incident_timeline_event_tab
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80802
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/353426
milestone: '14.9'
type: development
group: group::respond
default_enabled: false
<script>
import { GlTab, GlButton } from '@gitlab/ui';
export default {
components: {
GlTab,
GlButton,
},
};
</script>
<template>
<gl-tab :title="s__('Incident|Timeline')">
<div class="gl-my-4">
<p>{{ s__('Incident|No timeline items have been added yet.') }}</p>
</div>
<gl-button class="gl-my-3">
{{ s__('Incident|Add new timeline event') }}
</gl-button>
</gl-tab>
</template>
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import TimelineEventsTab from 'ee/issues/show/components/incidents/timeline_events_tab.vue';
describe('TimlineEventsTab', () => {
let wrapper;
const mountComponent = () => {
wrapper = shallowMount(TimelineEventsTab);
};
beforeEach(() => {
mountComponent();
});
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
const findNoEventsLine = () => wrapper.find('p');
const findAddEventButton = () => wrapper.findComponent(GlButton);
describe('empty state', () => {
beforeEach(() => {
mountComponent();
});
it('renders the text', () => {
expect(findNoEventsLine().exists()).toBe(true);
expect(findNoEventsLine().text()).toBe('No timeline items have been added yet.');
});
it('renders the button', () => {
expect(findAddEventButton().exists()).toBe(true);
expect(findAddEventButton().text()).toBe('Add new timeline event');
});
});
});
...@@ -19418,6 +19418,9 @@ msgstr "" ...@@ -19418,6 +19418,9 @@ msgstr ""
msgid "Incidents|There was an issue uploading your image." msgid "Incidents|There was an issue uploading your image."
msgstr "" msgstr ""
msgid "Incident|Add new timeline event"
msgstr ""
msgid "Incident|Alert details" msgid "Incident|Alert details"
msgstr "" msgstr ""
...@@ -19439,6 +19442,9 @@ msgstr "" ...@@ -19439,6 +19442,9 @@ msgstr ""
msgid "Incident|Metrics" msgid "Incident|Metrics"
msgstr "" msgstr ""
msgid "Incident|No timeline items have been added yet."
msgstr ""
msgid "Incident|Summary" msgid "Incident|Summary"
msgstr "" msgstr ""
...@@ -19448,6 +19454,9 @@ msgstr "" ...@@ -19448,6 +19454,9 @@ msgstr ""
msgid "Incident|There was an issue loading incident data. Please try again." msgid "Incident|There was an issue loading incident data. Please try again."
msgstr "" msgstr ""
msgid "Incident|Timeline"
msgstr ""
msgid "Include author name in notification email body" msgid "Include author name in notification email body"
msgstr "" msgstr ""
......
import { GlTab } from '@gitlab/ui'; import { GlTab } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import merge from 'lodash/merge'; import merge from 'lodash/merge';
import waitForPromises from 'helpers/wait_for_promises';
import { trackIncidentDetailsViewsOptions } from '~/incidents/constants'; import { trackIncidentDetailsViewsOptions } from '~/incidents/constants';
import DescriptionComponent from '~/issues/show/components/description.vue'; import DescriptionComponent from '~/issues/show/components/description.vue';
import HighlightBar from '~/issues/show/components/incidents/highlight_bar.vue'; import HighlightBar from '~/issues/show/components/incidents/highlight_bar.vue';
...@@ -36,6 +35,7 @@ describe('Incident Tabs component', () => { ...@@ -36,6 +35,7 @@ describe('Incident Tabs component', () => {
fullPath: '', fullPath: '',
iid: '', iid: '',
uploadMetricsFeatureAvailable: true, uploadMetricsFeatureAvailable: true,
glFeatures: { incidentTimelineEventTab: true, incidentTimelineEvents: true },
}, },
data() { data() {
return { alert: mockAlert, ...data }; return { alert: mockAlert, ...data };
...@@ -58,6 +58,7 @@ describe('Incident Tabs component', () => { ...@@ -58,6 +58,7 @@ describe('Incident Tabs component', () => {
const findTabs = () => wrapper.findAll(GlTab); const findTabs = () => wrapper.findAll(GlTab);
const findSummaryTab = () => findTabs().at(0); const findSummaryTab = () => findTabs().at(0);
const findMetricsTab = () => wrapper.find('[data-testid="metrics-tab"]'); const findMetricsTab = () => wrapper.find('[data-testid="metrics-tab"]');
const findTimelineTab = () => wrapper.find('[data-testid="timeline-events-tab"]');
const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]'); const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]');
const findAlertDetailsComponent = () => wrapper.find(AlertDetailsTable); const findAlertDetailsComponent = () => wrapper.find(AlertDetailsTable);
const findDescriptionComponent = () => wrapper.find(DescriptionComponent); const findDescriptionComponent = () => wrapper.find(DescriptionComponent);
...@@ -73,6 +74,29 @@ describe('Incident Tabs component', () => { ...@@ -73,6 +74,29 @@ describe('Incident Tabs component', () => {
}); });
}); });
describe('incident timeline tab', () => {
beforeEach(() => {
mountComponent();
});
it('renders the timeline tab when feature flag is enabled', () => {
expect(findTimelineTab().exists()).toBe(true);
expect(findTimelineTab().attributes('title')).toBe('Timeline');
});
it('does not render timeline tab when feature flag is disabled', () => {
mountComponent({}, { provide: { glFeatures: { incidentTimelineEventTab: false } } });
expect(findTimelineTab().exists()).toBe(false);
});
it('does not render timeline tab when not available in license', () => {
mountComponent({}, { provide: { glFeatures: { incidentTimelineEvents: false } } });
expect(findTimelineTab().exists()).toBe(false);
});
});
describe('with an alert present', () => { describe('with an alert present', () => {
beforeEach(() => { beforeEach(() => {
mountComponent(); mountComponent();
...@@ -112,19 +136,15 @@ describe('Incident Tabs component', () => { ...@@ -112,19 +136,15 @@ describe('Incident Tabs component', () => {
}); });
describe('upload metrics feature available', () => { describe('upload metrics feature available', () => {
it('shows the metric tab when metrics are available', async () => { it('shows the metric tab when metrics are available', () => {
mountComponent({}, { provide: { uploadMetricsFeatureAvailable: true } }); mountComponent({}, { provide: { uploadMetricsFeatureAvailable: true } });
await waitForPromises();
expect(findMetricsTab().exists()).toBe(true); expect(findMetricsTab().exists()).toBe(true);
}); });
it('hides the tab when metrics are not available', async () => { it('hides the tab when metrics are not available', () => {
mountComponent({}, { provide: { uploadMetricsFeatureAvailable: false } }); mountComponent({}, { provide: { uploadMetricsFeatureAvailable: false } });
await waitForPromises();
expect(findMetricsTab().exists()).toBe(false); expect(findMetricsTab().exists()).toBe(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