Commit 490fd60f authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '219381-manual-incident-creation' into 'master'

Create incident from the list page

See merge request gitlab-org/gitlab!37802
parents fc4f0d40 a1123ec1
......@@ -7,9 +7,11 @@ import {
GlAvatarLink,
GlAvatar,
GlTooltipDirective,
GlButton,
} from '@gitlab/ui';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { s__ } from '~/locale';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import getIncidents from '../graphql/queries/get_incidents.query.graphql';
import { I18N } from '../constants';
......@@ -48,12 +50,13 @@ export default {
GlAvatarsInline,
GlAvatarLink,
GlAvatar,
GlButton,
TimeAgoTooltip,
},
directives: {
GlTooltip: GlTooltipDirective,
},
inject: ['projectPath'],
inject: ['projectPath', 'newIssuePath', 'incidentTemplateName'],
apollo: {
incidents: {
query: getIncidents,
......@@ -73,6 +76,7 @@ export default {
return {
errored: false,
isErrorAlertDismissed: false,
redirecting: false,
};
},
computed: {
......@@ -90,6 +94,9 @@ export default {
[bodyTrClass]: !this.loading && this.hasIncidents,
};
},
newIncidentPath() {
return mergeUrlParams({ issuable_template: this.incidentTemplateName }, this.newIssuePath);
},
},
methods: {
hasAssignees(assignees) {
......@@ -104,6 +111,21 @@ export default {
{{ $options.i18n.errorMsg }}
</gl-alert>
<div class="gl-display-flex gl-justify-content-end">
<gl-button
class="gl-mt-3 create-incident-button"
data-testid="createIncidentBtn"
:loading="redirecting"
:disabled="redirecting"
category="primary"
variant="success"
:href="newIncidentPath"
@click="redirecting = true"
>
{{ $options.i18n.createIncidentBtnLabel }}
</gl-button>
</div>
<h4 class="gl-display-block d-md-none my-3">
{{ s__('IncidentManagement|Incidents') }}
</h4>
......
......@@ -5,4 +5,5 @@ export const I18N = {
errorMsg: s__('IncidentManagement|There was an error displaying the incidents.'),
noIncidents: s__('IncidentManagement|No incidents to display.'),
unassigned: s__('IncidentManagement|Unassigned'),
createIncidentBtnLabel: s__('IncidentManagement|Create incident'),
};
......@@ -8,7 +8,7 @@ export default () => {
const selector = '#js-incidents';
const domEl = document.querySelector(selector);
const { projectPath } = domEl.dataset;
const { projectPath, newIssuePath, incidentTemplateName } = domEl.dataset;
const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
......@@ -18,17 +18,15 @@ export default () => {
el: selector,
provide: {
projectPath,
incidentTemplateName,
newIssuePath,
},
apolloProvider,
components: {
IncidentsList,
},
render(createElement) {
return createElement('incidents-list', {
props: {
projectPath,
},
});
return createElement('incidents-list');
},
});
};
......@@ -88,4 +88,10 @@
background-color: $white-normal;
}
}
@include media-breakpoint-down(xs) {
.create-incident-button {
@include gl-w-full;
}
}
}
......@@ -3,7 +3,9 @@
module Projects::IncidentsHelper
def incidents_data(project)
{
'project-path' => project.full_path
'project-path' => project.full_path,
'new-issue-path' => new_project_issue_path(project),
'incident-template-name' => 'incident'
}
end
end
---
title: Create incident from the incidents list page
merge_request: 37802
author:
type: added
......@@ -12701,6 +12701,9 @@ msgstr ""
msgid "IncidentManagement|Assignees"
msgstr ""
msgid "IncidentManagement|Create incident"
msgstr ""
msgid "IncidentManagement|Date created"
msgstr ""
......
......@@ -7,6 +7,8 @@ import mockIncidents from '../mocks/incidents.json';
describe('Incidents List', () => {
let wrapper;
const newIssuePath = 'namespace/project/-/issues/new';
const incidentTemplateName = 'incident';
const findTable = () => wrapper.find(GlTable);
const findTableRows = () => wrapper.findAll('table tbody tr');
......@@ -14,6 +16,7 @@ describe('Incidents List', () => {
const findLoader = () => wrapper.find(GlLoadingIcon);
const findTimeAgo = () => wrapper.findAll(TimeAgoTooltip);
const findAssingees = () => wrapper.findAll('[data-testid="incident-assignees"]');
const findCreateIncidentBtn = () => wrapper.find('[data-testid="createIncidentBtn"]');
function mountComponent({ data = { incidents: [] }, loading = false }) {
wrapper = mount(IncidentsList, {
......@@ -31,8 +34,11 @@ describe('Incidents List', () => {
},
provide: {
projectPath: '/project/path',
newIssuePath,
incidentTemplateName,
},
stubs: {
GlButton: true,
GlAvatar: true,
},
});
......@@ -107,4 +113,27 @@ describe('Incidents List', () => {
});
});
});
describe('Create Incident', () => {
beforeEach(() => {
mountComponent({
data: { incidents: [] },
loading: false,
});
});
it('shows the button linking to new incidents page with prefilled incident template', () => {
expect(findCreateIncidentBtn().exists()).toBe(true);
expect(findCreateIncidentBtn().attributes('href')).toBe(
`${newIssuePath}?issuable_template=${incidentTemplateName}`,
);
});
it('sets button loading on click', () => {
findCreateIncidentBtn().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(findCreateIncidentBtn().attributes('loading')).toBe('true');
});
});
});
});
......@@ -7,12 +7,17 @@ RSpec.describe Projects::IncidentsHelper do
let(:project) { create(:project) }
let(:project_path) { project.full_path }
let(:new_issue_path) { new_project_issue_path(project) }
describe '#incidents_data' do
subject(:data) { helper.incidents_data(project) }
it 'returns frontend configuration' do
expect(data).to match('project-path' => project_path)
expect(data).to match(
'project-path' => project_path,
'new-issue-path' => new_issue_path,
'incident-template-name' => 'incident'
)
end
end
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