Commit b7c0fd4d authored by Coung Ngo's avatar Coung Ngo

Update incidents to use issue header ellipsis dropdown

Since incidents are an issue type and should mirror issue
actions such as New incident and Report abuse, incidents
should be updated to use the new issue header ellipsis
dropdown to keep it in sync with issue functionality
parent 8188bc2d
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
import { GlButton, GlDropdown, GlDropdownItem, GlIcon, GlLink, GlModal } from '@gitlab/ui'; import { GlButton, GlDropdown, GlDropdownItem, GlIcon, GlLink, GlModal } from '@gitlab/ui';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { IssuableType } from '~/issuable_show/constants';
import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants'; import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants';
import { __ } from '~/locale'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
import updateIssueMutation from '../queries/update_issue.mutation.graphql'; import updateIssueMutation from '../queries/update_issue.mutation.graphql';
export default { export default {
...@@ -22,18 +24,41 @@ export default { ...@@ -22,18 +24,41 @@ export default {
text: __('Yes, close issue'), text: __('Yes, close issue'),
attributes: [{ variant: 'warning' }], attributes: [{ variant: 'warning' }],
}, },
inject: [ inject: {
'canCreateIssue', canCreateIssue: {
'canReopenIssue', default: false,
'canReportSpam', },
'canUpdateIssue', canReopenIssue: {
'iid', default: false,
'isIssueAuthor', },
'newIssuePath', canReportSpam: {
'projectPath', default: false,
'reportAbusePath', },
'submitAsSpamPath', canUpdateIssue: {
], default: false,
},
iid: {
default: '',
},
isIssueAuthor: {
default: false,
},
issueType: {
default: IssuableType.Issue,
},
newIssuePath: {
default: '',
},
projectPath: {
default: '',
},
reportAbusePath: {
default: '',
},
submitAsSpamPath: {
default: '',
},
},
data() { data() {
return { return {
isUpdatingState: false, isUpdatingState: false,
...@@ -45,12 +70,22 @@ export default { ...@@ -45,12 +70,22 @@ export default {
return this.getNoteableData.state === IssuableStatus.Closed; return this.getNoteableData.state === IssuableStatus.Closed;
}, },
buttonText() { buttonText() {
return this.isClosed ? __('Reopen issue') : __('Close issue'); return this.isClosed
? sprintf(__('Reopen %{issueType}'), { issueType: this.issueType })
: sprintf(__('Close %{issueType}'), { issueType: this.issueType });
}, },
buttonVariant() { buttonVariant() {
return this.isClosed ? 'default' : 'warning'; return this.isClosed ? 'default' : 'warning';
}, },
showToggleIssueButton() { dropdownText() {
return sprintf(__('%{issueType} actions'), {
issueType: capitalizeFirstCharacter(this.issueType),
});
},
newIssueTypeText() {
return sprintf(__('New %{issueType}'), { issueType: this.issueType });
},
showToggleIssueStateButton() {
const canClose = !this.isClosed && this.canUpdateIssue; const canClose = !this.isClosed && this.canUpdateIssue;
const canReopen = this.isClosed && this.canReopenIssue; const canReopen = this.isClosed && this.canReopenIssue;
return canClose || canReopen; return canClose || canReopen;
...@@ -106,16 +141,16 @@ export default { ...@@ -106,16 +141,16 @@ export default {
<template> <template>
<div class="detail-page-header-actions"> <div class="detail-page-header-actions">
<gl-dropdown class="gl-display-block gl-display-sm-none!" block :text="__('Issue actions')"> <gl-dropdown class="gl-display-block gl-display-sm-none!" block :text="dropdownText">
<gl-dropdown-item <gl-dropdown-item
v-if="showToggleIssueButton" v-if="showToggleIssueStateButton"
:disabled="isUpdatingState" :disabled="isUpdatingState"
@click="toggleIssueState" @click="toggleIssueState"
> >
{{ buttonText }} {{ buttonText }}
</gl-dropdown-item> </gl-dropdown-item>
<gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath"> <gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath">
{{ __('New issue') }} {{ newIssueTypeText }}
</gl-dropdown-item> </gl-dropdown-item>
<gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath"> <gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath">
{{ __('Report abuse') }} {{ __('Report abuse') }}
...@@ -131,7 +166,7 @@ export default { ...@@ -131,7 +166,7 @@ export default {
</gl-dropdown> </gl-dropdown>
<gl-button <gl-button
v-if="showToggleIssueButton" v-if="showToggleIssueStateButton"
class="gl-display-none gl-display-sm-inline-flex!" class="gl-display-none gl-display-sm-inline-flex!"
category="secondary" category="secondary"
:loading="isUpdatingState" :loading="isUpdatingState"
...@@ -149,11 +184,11 @@ export default { ...@@ -149,11 +184,11 @@ export default {
> >
<template #button-content> <template #button-content>
<gl-icon name="ellipsis_v" aria-hidden="true" /> <gl-icon name="ellipsis_v" aria-hidden="true" />
<span class="gl-sr-only">{{ __('Actions') }}</span> <span class="gl-sr-only">{{ dropdownText }}</span>
</template> </template>
<gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath"> <gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath">
{{ __('New issue') }} {{ newIssueTypeText }}
</gl-dropdown-item> </gl-dropdown-item>
<gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath"> <gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath">
{{ __('Report abuse') }} {{ __('Report abuse') }}
......
...@@ -48,6 +48,7 @@ export function initIssueHeaderActions(store) { ...@@ -48,6 +48,7 @@ export function initIssueHeaderActions(store) {
canUpdateIssue: parseBoolean(el.dataset.canUpdateIssue), canUpdateIssue: parseBoolean(el.dataset.canUpdateIssue),
iid: el.dataset.iid, iid: el.dataset.iid,
isIssueAuthor: parseBoolean(el.dataset.isIssueAuthor), isIssueAuthor: parseBoolean(el.dataset.isIssueAuthor),
issueType: el.dataset.issueType,
newIssuePath: el.dataset.newIssuePath, newIssuePath: el.dataset.newIssuePath,
projectPath: el.dataset.projectPath, projectPath: el.dataset.projectPath,
reportAbusePath: el.dataset.reportAbusePath, reportAbusePath: el.dataset.reportAbusePath,
......
...@@ -153,18 +153,21 @@ module IssuesHelper ...@@ -153,18 +153,21 @@ module IssuesHelper
} }
end end
def issue_header_actions_data(project, issue, current_user) def issue_header_actions_data(project, issuable, current_user)
new_issuable_params = ({ issuable_template: 'incident', issue: { issue_type: 'incident' } } if issuable.incident?)
{ {
can_create_issue: show_new_issue_link?(project).to_s, can_create_issue: show_new_issue_link?(project).to_s,
can_reopen_issue: can?(current_user, :reopen_issue, issue).to_s, can_reopen_issue: can?(current_user, :reopen_issue, issuable).to_s,
can_report_spam: issue.submittable_as_spam_by?(current_user).to_s, can_report_spam: issuable.submittable_as_spam_by?(current_user).to_s,
can_update_issue: can?(current_user, :update_issue, issue).to_s, can_update_issue: can?(current_user, :update_issue, issuable).to_s,
iid: issue.iid, iid: issuable.iid,
is_issue_author: (issue.author == current_user).to_s, is_issue_author: (issuable.author == current_user).to_s,
new_issue_path: new_project_issue_path(project), issue_type: issuable_display_type(issuable),
new_issue_path: new_project_issue_path(project, new_issuable_params),
project_path: project.full_path, project_path: project.full_path,
report_abuse_path: new_abuse_report_path(user_id: issue.author.id, ref_url: issue_url(issue)), report_abuse_path: new_abuse_report_path(user_id: issuable.author.id, ref_url: issue_url(issuable)),
submit_as_spam_path: mark_as_spam_project_issue_path(project, issue) submit_as_spam_path: mark_as_spam_project_issue_path(project, issuable)
} }
end end
end end
......
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
%a.btn.gl-button.btn-default.float-right.gl-display-block.d-sm-none.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" } %a.btn.gl-button.btn-default.float-right.gl-display-block.d-sm-none.gutter-toggle.issuable-gutter-toggle.js-sidebar-toggle{ href: "#" }
= sprite_icon('chevron-double-lg-left') = sprite_icon('chevron-double-lg-left')
- if Feature.enabled?(:vue_issue_header, @project) && display_issuable_type == 'issue' - if Feature.enabled?(:vue_issue_header, @project)
.js-issue-header-actions{ data: issue_header_actions_data(@project, @issue, current_user) } .js-issue-header-actions{ data: issue_header_actions_data(@project, issuable, current_user) }
- else - else
.detail-page-header-actions.js-issuable-actions.js-issuable-buttons{ data: { "action": "close-reopen" } } .detail-page-header-actions.js-issuable-actions.js-issuable-buttons{ data: { "action": "close-reopen" } }
.clearfix.issue-btn-group.dropdown .clearfix.issue-btn-group.dropdown
......
...@@ -526,6 +526,9 @@ msgstr "" ...@@ -526,6 +526,9 @@ msgstr ""
msgid "%{issuableType} will be removed! Are you sure?" msgid "%{issuableType} will be removed! Are you sure?"
msgstr "" msgstr ""
msgid "%{issueType} actions"
msgstr ""
msgid "%{issuesCount} issues with a limit of %{maxIssueCount}" msgid "%{issuesCount} issues with a limit of %{maxIssueCount}"
msgstr "" msgstr ""
...@@ -5538,13 +5541,13 @@ msgstr "" ...@@ -5538,13 +5541,13 @@ msgstr ""
msgid "Close %{display_issuable_type}" msgid "Close %{display_issuable_type}"
msgstr "" msgstr ""
msgid "Close %{tabname}" msgid "Close %{issueType}"
msgstr "" msgstr ""
msgid "Close epic" msgid "Close %{tabname}"
msgstr "" msgstr ""
msgid "Close issue" msgid "Close epic"
msgstr "" msgstr ""
msgid "Close milestone" msgid "Close milestone"
...@@ -14855,9 +14858,6 @@ msgstr "" ...@@ -14855,9 +14858,6 @@ msgstr ""
msgid "Issue Boards" msgid "Issue Boards"
msgstr "" msgstr ""
msgid "Issue actions"
msgstr ""
msgid "Issue already promoted to epic." msgid "Issue already promoted to epic."
msgstr "" msgstr ""
...@@ -17963,6 +17963,9 @@ msgstr "" ...@@ -17963,6 +17963,9 @@ msgstr ""
msgid "New %{display_issuable_type}" msgid "New %{display_issuable_type}"
msgstr "" msgstr ""
msgid "New %{issueType}"
msgstr ""
msgid "New Application" msgid "New Application"
msgstr "" msgstr ""
...@@ -22471,10 +22474,10 @@ msgstr "" ...@@ -22471,10 +22474,10 @@ msgstr ""
msgid "Reopen %{display_issuable_type}" msgid "Reopen %{display_issuable_type}"
msgstr "" msgstr ""
msgid "Reopen epic" msgid "Reopen %{issueType}"
msgstr "" msgstr ""
msgid "Reopen issue" msgid "Reopen epic"
msgstr "" msgstr ""
msgid "Reopen milestone" msgid "Reopen milestone"
......
import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui'; import { GlButton, GlDropdown, GlDropdownItem, GlLink, GlModal } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { IssuableType } from '~/issuable_show/constants';
import HeaderActions from '~/issue_show/components/header_actions.vue'; import HeaderActions from '~/issue_show/components/header_actions.vue';
import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants'; import { IssuableStatus, IssueStateEvent } from '~/issue_show/constants';
import createStore from '~/notes/stores'; import createStore from '~/notes/stores';
...@@ -20,6 +21,7 @@ describe('HeaderActions component', () => { ...@@ -20,6 +21,7 @@ describe('HeaderActions component', () => {
canUpdateIssue: true, canUpdateIssue: true,
iid: '32', iid: '32',
isIssueAuthor: true, isIssueAuthor: true,
issueType: IssuableType.Issue,
newIssuePath: 'gitlab-org/gitlab-test/-/issues/new', newIssuePath: 'gitlab-org/gitlab-test/-/issues/new',
projectPath: 'gitlab-org/gitlab-test', projectPath: 'gitlab-org/gitlab-test',
reportAbusePath: reportAbusePath:
...@@ -74,16 +76,21 @@ describe('HeaderActions component', () => { ...@@ -74,16 +76,21 @@ describe('HeaderActions component', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe.each`
issueType
${IssuableType.Issue}
${IssuableType.Incident}
`('when issue type is $issueType', ({ issueType }) => {
describe('close/reopen button', () => { describe('close/reopen button', () => {
describe.each` describe.each`
description | issueState | buttonText | newIssueState description | issueState | buttonText | newIssueState
${'when the issue is open'} | ${IssuableStatus.Open} | ${'Close issue'} | ${IssueStateEvent.Close} ${`when the ${issueType} is open`} | ${IssuableStatus.Open} | ${`Close ${issueType}`} | ${IssueStateEvent.Close}
${'when the issue is closed'} | ${IssuableStatus.Closed} | ${'Reopen issue'} | ${IssueStateEvent.Reopen} ${`when the ${issueType} is closed`} | ${IssuableStatus.Closed} | ${`Reopen ${issueType}`} | ${IssueStateEvent.Reopen}
`('$description', ({ issueState, buttonText, newIssueState }) => { `('$description', ({ issueState, buttonText, newIssueState }) => {
beforeEach(() => { beforeEach(() => {
dispatchEventSpy = jest.spyOn(document, 'dispatchEvent'); dispatchEventSpy = jest.spyOn(document, 'dispatchEvent');
wrapper = mountComponent({ issueState }); wrapper = mountComponent({ props: { issueType }, issueState });
}); });
it(`has text "${buttonText}"`, () => { it(`has text "${buttonText}"`, () => {
...@@ -123,10 +130,10 @@ describe('HeaderActions component', () => { ...@@ -123,10 +130,10 @@ describe('HeaderActions component', () => {
`('$description', ({ isCloseIssueItemVisible, findDropdownItems }) => { `('$description', ({ isCloseIssueItemVisible, findDropdownItems }) => {
describe.each` describe.each`
description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam description | itemText | isItemVisible | canUpdateIssue | canCreateIssue | isIssueAuthor | canReportSpam
${'when user can update issue'} | ${'Close issue'} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true} ${`when user can update ${issueType}`} | ${`Close ${issueType}`} | ${isCloseIssueItemVisible} | ${true} | ${true} | ${true} | ${true}
${'when user cannot update issue'} | ${'Close issue'} | ${false} | ${false} | ${true} | ${true} | ${true} ${`when user cannot update ${issueType}`} | ${`Close ${issueType}`} | ${false} | ${false} | ${true} | ${true} | ${true}
${'when user can create issue'} | ${'New issue'} | ${true} | ${true} | ${true} | ${true} | ${true} ${`when user can create ${issueType}`} | ${`New ${issueType}`} | ${true} | ${true} | ${true} | ${true} | ${true}
${'when user cannot create issue'} | ${'New issue'} | ${false} | ${true} | ${false} | ${true} | ${true} ${`when user cannot create ${issueType}`} | ${`New ${issueType}`} | ${false} | ${true} | ${false} | ${true} | ${true}
${'when user can report abuse'} | ${'Report abuse'} | ${true} | ${true} | ${true} | ${false} | ${true} ${'when user can report abuse'} | ${'Report abuse'} | ${true} | ${true} | ${true} | ${false} | ${true}
${'when user cannot report abuse'} | ${'Report abuse'} | ${false} | ${true} | ${true} | ${true} | ${true} ${'when user cannot report abuse'} | ${'Report abuse'} | ${false} | ${true} | ${true} | ${true} | ${true}
${'when user can submit as spam'} | ${'Submit as spam'} | ${true} | ${true} | ${true} | ${true} | ${true} ${'when user can submit as spam'} | ${'Submit as spam'} | ${true} | ${true} | ${true} | ${true} | ${true}
...@@ -147,6 +154,7 @@ describe('HeaderActions component', () => { ...@@ -147,6 +154,7 @@ describe('HeaderActions component', () => {
canUpdateIssue, canUpdateIssue,
canCreateIssue, canCreateIssue,
isIssueAuthor, isIssueAuthor,
issueType,
canReportSpam, canReportSpam,
}, },
}); });
...@@ -162,6 +170,7 @@ describe('HeaderActions component', () => { ...@@ -162,6 +170,7 @@ describe('HeaderActions component', () => {
}, },
); );
}); });
});
describe('modal', () => { describe('modal', () => {
const blockedByIssues = [ const blockedByIssues = [
......
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