Commit daf89072 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch '214419-consolidate-epic-tree-buttons-add-create-epics-and-issues' into 'master'

Consolidate epic tree buttons

See merge request gitlab-org/gitlab!30816
parents b4a35f5a 939784d2
...@@ -58,7 +58,7 @@ An epic's page contains the following tabs: ...@@ -58,7 +58,7 @@ An epic's page contains the following tabs:
- Hover over the total counts to see a breakdown of open and closed items. - Hover over the total counts to see a breakdown of open and closed items.
- **Roadmap**: a roadmap view of child epics which have start and due dates. - **Roadmap**: a roadmap view of child epics which have start and due dates.
![epic view](img/epic_view_v12.3.png) ![epic view](img/epic_view_v13.0.png)
## Adding an issue to an epic ## Adding an issue to an epic
...@@ -75,6 +75,7 @@ the issue is automatically unlinked from its current parent. ...@@ -75,6 +75,7 @@ the issue is automatically unlinked from its current parent.
To add an issue to an epic: To add an issue to an epic:
1. Click the **Add** dropdown button.
1. Click **Add an issue**. 1. Click **Add an issue**.
1. Identify the issue to be added, using either of the following methods: 1. Identify the issue to be added, using either of the following methods:
- Paste the link of the issue. - Paste the link of the issue.
...@@ -91,7 +92,7 @@ Creating an issue from an epic enables you to maintain focus on the broader cont ...@@ -91,7 +92,7 @@ Creating an issue from an epic enables you to maintain focus on the broader cont
To create an issue from an epic: To create an issue from an epic:
1. On the epic's page, under **Epics and Issues**, click the arrow next to **Add an issue** and select **Create new issue**. 1. On the epic's page, under **Epics and Issues**, click the **Add** dropdown button and select **Create new issue**.
1. Under **Title**, enter the title for the new issue. 1. Under **Title**, enter the title for the new issue.
1. From the **Project** dropdown, select the project in which the issue should be created. 1. From the **Project** dropdown, select the project in which the issue should be created.
1. Click **Create issue**. 1. Click **Create issue**.
...@@ -128,6 +129,7 @@ the maximum depth being 5. ...@@ -128,6 +129,7 @@ the maximum depth being 5.
To add a child epic to an epic: To add a child epic to an epic:
1. Click the **Add** dropdown button.
1. Click **Add an epic**. 1. Click **Add an epic**.
1. Identify the epic to be added, using either of the following methods: 1. Identify the epic to be added, using either of the following methods:
- Paste the link of the epic. - Paste the link of the epic.
......
...@@ -72,7 +72,11 @@ export default { ...@@ -72,7 +72,11 @@ export default {
mounted() { mounted() {
this.setupAutoComplete(); this.setupAutoComplete();
if (this.focusOnMount) { if (this.focusOnMount) {
this.$refs.input.focus(); this.$nextTick()
.then(() => {
this.$refs.input.focus();
})
.catch(() => {});
} }
}, },
beforeUpdate() { beforeUpdate() {
......
...@@ -29,7 +29,11 @@ export default { ...@@ -29,7 +29,11 @@ export default {
}, },
}, },
mounted() { mounted() {
this.$refs.input.focus(); this.$nextTick()
.then(() => {
this.$refs.input.focus();
})
.catch(() => {});
}, },
methods: { methods: {
onFormSubmit() { onFormSubmit() {
......
<script>
import SplitButton from '~/vue_shared/components/split_button.vue';
import { s__ } from '~/locale';
const actionItems = [
{
title: s__('Epics|Add an epic'),
description: s__('Epics|Add an existing epic as a child epic.'),
eventName: 'showAddEpicForm',
},
{
title: s__('Epics|Create new epic'),
description: s__('Epics|Create an epic within this group and add it as a child epic.'),
eventName: 'showCreateEpicForm',
},
];
export default {
actionItems,
components: {
SplitButton,
},
methods: {
change(item) {
this.$emit(item.eventName);
},
},
};
</script>
<template>
<split-button
:action-items="$options.actionItems"
class="js-add-epics-button"
menu-class="dropdown-menu-large"
right
size="sm"
v-on="$listeners"
@change="change"
/>
</template>
<script>
import { GlDropdown, GlDropdownDivider, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
import { s__, __ } from '~/locale';
const issueActionItems = [
{
title: __('Add a new issue'),
eventName: 'showCreateIssueForm',
},
{
title: __('Add an existing issue'),
eventName: 'showAddIssueForm',
},
];
const epicActionItems = [
{
title: s__('Epics|Add a new epic'),
eventName: 'showCreateEpicForm',
},
{
title: s__('Epics|Add an existing epic'),
eventName: 'showAddEpicForm',
},
];
export default {
epicActionItems,
issueActionItems,
components: {
GlDropdown,
GlDropdownDivider,
GlDropdownHeader,
GlDropdownItem,
},
props: {
allowSubEpics: {
type: Boolean,
required: false,
default: false,
},
},
methods: {
change({ eventName }) {
this.$emit(eventName);
},
},
};
</script>
<template>
<gl-dropdown
:text="__('Add')"
variant="secondary"
data-qa-selector="epic_issue_actions_split_button"
right
>
<gl-dropdown-header>{{ __('Issue') }}</gl-dropdown-header>
<gl-dropdown-item
v-for="item in $options.issueActionItems"
:key="item.eventName"
active-class="is-active"
@click="change(item)"
>
{{ item.title }}
</gl-dropdown-item>
<template v-if="allowSubEpics">
<gl-dropdown-divider />
<gl-dropdown-header>{{ __('Epic') }}</gl-dropdown-header>
<gl-dropdown-item
v-for="item in $options.epicActionItems"
:key="item.eventName"
active-class="is-active"
@click="change(item)"
>
{{ item.title }}
</gl-dropdown-item>
</template>
</gl-dropdown>
</template>
<script>
import SplitButton from '~/vue_shared/components/split_button.vue';
import { __ } from '~/locale';
const actionItems = [
{
title: __('Add an issue'),
description: __('Add an existing issue to the epic.'),
eventName: 'showAddIssueForm',
},
{
title: __('Create an issue'),
description: __('Create a new issue and add it to the epic.'),
eventName: 'showCreateIssueForm',
},
];
export default {
actionItems,
components: {
SplitButton,
},
methods: {
change(item) {
this.$emit(item.eventName);
},
},
};
</script>
<template>
<split-button
:action-items="$options.actionItems"
class="js-issue-actions-split-button"
data-qa-selector="issue_actions_split_button"
menu-class="dropdown-menu-large"
right
size="sm"
v-on="$listeners"
@change="change"
/>
</template>
...@@ -3,13 +3,10 @@ import { mapState, mapActions, mapGetters } from 'vuex'; ...@@ -3,13 +3,10 @@ import { mapState, mapActions, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import { issuableTypesMap } from 'ee/related_issues/constants';
import AddItemForm from 'ee/related_issues/components/add_issuable_form.vue'; import AddItemForm from 'ee/related_issues/components/add_issuable_form.vue';
import SlotSwitch from '~/vue_shared/components/slot_switch.vue'; import SlotSwitch from '~/vue_shared/components/slot_switch.vue';
import CreateEpicForm from './create_epic_form.vue'; import CreateEpicForm from './create_epic_form.vue';
import CreateIssueForm from './create_issue_form.vue'; import CreateIssueForm from './create_issue_form.vue';
import IssueActionsSplitButton from './issue_actions_split_button.vue';
import TreeItemRemoveModal from './tree_item_remove_modal.vue'; import TreeItemRemoveModal from './tree_item_remove_modal.vue';
import RelatedItemsTreeHeader from './related_items_tree_header.vue'; import RelatedItemsTreeHeader from './related_items_tree_header.vue';
...@@ -34,7 +31,6 @@ export default { ...@@ -34,7 +31,6 @@ export default {
CreateEpicForm, CreateEpicForm,
TreeItemRemoveModal, TreeItemRemoveModal,
CreateIssueForm, CreateIssueForm,
IssueActionsSplitButton,
SlotSwitch, SlotSwitch,
}, },
computed: { computed: {
...@@ -133,12 +129,6 @@ export default { ...@@ -133,12 +129,6 @@ export default {
this.toggleCreateEpicForm({ toggleState: false }); this.toggleCreateEpicForm({ toggleState: false });
this.setItemInputValue(''); this.setItemInputValue('');
}, },
handleShowAddIssueForm() {
this.toggleAddItemForm({ toggleState: true, issuableType: issuableTypesMap.ISSUE });
},
handleShowCreateIssueForm() {
this.toggleCreateIssueForm({ toggleState: true });
},
}, },
}; };
</script> </script>
...@@ -156,14 +146,7 @@ export default { ...@@ -156,14 +146,7 @@ export default {
'overflow-auto': directChildren.length > $options.OVERFLOW_AFTER, 'overflow-auto': directChildren.length > $options.OVERFLOW_AFTER,
}" }"
> >
<related-items-tree-header :class="{ 'border-bottom-0': itemsFetchResultEmpty }"> <related-items-tree-header :class="{ 'border-bottom-0': itemsFetchResultEmpty }" />
<issue-actions-split-button
slot="issueActions"
class="ml-0 ml-sm-1"
@showAddIssueForm="handleShowAddIssueForm"
@showCreateIssueForm="handleShowCreateIssueForm"
/>
</related-items-tree-header>
<slot-switch <slot-switch
v-if="visibleForm" v-if="visibleForm"
:active-slot-names="[visibleForm]" :active-slot-names="[visibleForm]"
......
<script> <script>
import { mapState, mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { GlDeprecatedButton, GlTooltip, GlIcon } from '@gitlab/ui'; import { GlTooltip, GlIcon } from '@gitlab/ui';
import { issuableTypesMap } from 'ee/related_issues/constants'; import { issuableTypesMap } from 'ee/related_issues/constants';
import EpicActionsSplitButton from './epic_actions_split_button.vue'; import EpicActionsSplitButton from './epic_issue_actions_split_button.vue';
import EpicHealthStatus from './epic_health_status.vue'; import EpicHealthStatus from './epic_health_status.vue';
export default { export default {
components: { components: {
GlDeprecatedButton,
GlTooltip, GlTooltip,
GlIcon, GlIcon,
EpicHealthStatus, EpicHealthStatus,
...@@ -26,17 +25,25 @@ export default { ...@@ -26,17 +25,25 @@ export default {
}, },
}, },
methods: { methods: {
...mapActions(['toggleAddItemForm', 'toggleCreateEpicForm', 'setItemInputValue']), ...mapActions([
showAddEpicForm() { 'toggleCreateIssueForm',
'toggleAddItemForm',
'toggleCreateEpicForm',
'setItemInputValue',
]),
showAddIssueForm() {
this.setItemInputValue('');
this.toggleAddItemForm({ this.toggleAddItemForm({
issuableType: issuableTypesMap.EPIC, issuableType: issuableTypesMap.ISSUE,
toggleState: true, toggleState: true,
}); });
}, },
showAddIssueForm() { showCreateIssueForm() {
this.setItemInputValue(''); this.toggleCreateIssueForm({ toggleState: true });
},
showAddEpicForm() {
this.toggleAddItemForm({ this.toggleAddItemForm({
issuableType: issuableTypesMap.ISSUE, issuableType: issuableTypesMap.EPIC,
toggleState: true, toggleState: true,
}); });
}, },
...@@ -88,24 +95,18 @@ export default { ...@@ -88,24 +95,18 @@ export default {
</div> </div>
<epic-health-status v-if="healthStatus" :health-status="healthStatus" /> <epic-health-status v-if="healthStatus" :health-status="healthStatus" />
</div> </div>
<div class="d-inline-flex flex-column flex-sm-row js-button-container"> <div
<template v-if="parentItem.userPermissions.adminEpic"> v-if="parentItem.userPermissions.adminEpic"
<epic-actions-split-button class="d-inline-flex flex-column flex-sm-row js-button-container"
v-if="allowSubEpics" >
class="qa-add-epics-button mb-2 mb-sm-0" <epic-actions-split-button
@showAddEpicForm="showAddEpicForm" :allow-sub-epics="allowSubEpics"
@showCreateEpicForm="showCreateEpicForm" class="js-add-epics-issues-button qa-add-epics-button mb-2 mb-sm-0"
/> @showAddIssueForm="showAddIssueForm"
@showCreateIssueForm="showCreateIssueForm"
<slot name="issueActions"> @showAddEpicForm="showAddEpicForm"
<gl-deprecated-button @showCreateEpicForm="showCreateEpicForm"
class="ml-1 js-add-issues-button qa-add-issues-button" />
size="sm"
@click="showAddIssueForm"
>{{ __('Add an issue') }}</gl-deprecated-button
>
</slot>
</template>
</div> </div>
</div> </div>
</template> </template>
...@@ -142,17 +142,20 @@ export default { ...@@ -142,17 +142,20 @@ export default {
state.showAddItemForm = toggleState; state.showAddItemForm = toggleState;
state.showCreateEpicForm = false; state.showCreateEpicForm = false;
state.showCreateIssueForm = false;
}, },
[types.TOGGLE_CREATE_EPIC_FORM](state, { toggleState }) { [types.TOGGLE_CREATE_EPIC_FORM](state, { toggleState }) {
state.showCreateEpicForm = toggleState; state.showCreateEpicForm = toggleState;
state.showAddItemForm = false; state.showAddItemForm = false;
state.showCreateIssueForm = false;
state.issuableType = issuableTypesMap.EPIC; state.issuableType = issuableTypesMap.EPIC;
}, },
[types.TOGGLE_CREATE_ISSUE_FORM](state, { toggleState }) { [types.TOGGLE_CREATE_ISSUE_FORM](state, { toggleState }) {
state.showCreateIssueForm = toggleState; state.showCreateIssueForm = toggleState;
state.showAddItemForm = false; state.showAddItemForm = false;
state.showCreateEpicForm = false;
}, },
[types.SET_PENDING_REFERENCES](state, references) { [types.SET_PENDING_REFERENCES](state, references) {
......
---
title: Consolidate epic tree buttons
merge_request: 30816
author:
type: changed
...@@ -60,7 +60,7 @@ describe 'Epic Issues', :js do ...@@ -60,7 +60,7 @@ describe 'Epic Issues', :js do
end end
it 'user cannot add new epics to the epic' do it 'user cannot add new epics to the epic' do
expect(page).not_to have_selector('.related-items-tree-container .js-add-epics-button') expect(page).not_to have_selector('.related-items-tree-container .js-add-epics-issues-button')
end end
end end
...@@ -69,8 +69,9 @@ describe 'Epic Issues', :js do ...@@ -69,8 +69,9 @@ describe 'Epic Issues', :js do
let(:issue_invalid) { create(:issue) } let(:issue_invalid) { create(:issue) }
let(:epic_to_add) { create(:epic, group: group) } let(:epic_to_add) { create(:epic, group: group) }
def add_issues(references, button_selector: '.js-issue-actions-split-button > button:first-child') def add_issues(references)
find(".related-items-tree-container #{button_selector}").click find(".related-items-tree-container .js-add-epics-issues-button").click
find('.related-items-tree-container .js-add-epics-issues-button .dropdown-item', text: 'Add an existing issue').click
find('.related-items-tree-container .js-add-issuable-form-input').set(references) find('.related-items-tree-container .js-add-issuable-form-input').set(references)
# When adding long references, for some reason the input gets stuck # When adding long references, for some reason the input gets stuck
# waiting for more text. Send a keystroke before clicking the button to # waiting for more text. Send a keystroke before clicking the button to
...@@ -82,7 +83,8 @@ describe 'Epic Issues', :js do ...@@ -82,7 +83,8 @@ describe 'Epic Issues', :js do
end end
def add_epics(references) def add_epics(references)
find('.related-items-tree-container .js-add-epics-button').click find('.related-items-tree-container .js-add-epics-issues-button').click
find('.related-items-tree-container .js-add-epics-issues-button .dropdown-item', text: 'Add an existing epic').click
find('.related-items-tree-container .js-add-issuable-form-input').set(references) find('.related-items-tree-container .js-add-issuable-form-input').set(references)
find('.related-items-tree-container .js-add-issuable-form-input').send_keys(:tab) find('.related-items-tree-container .js-add-issuable-form-input').send_keys(:tab)
...@@ -100,8 +102,8 @@ describe 'Epic Issues', :js do ...@@ -100,8 +102,8 @@ describe 'Epic Issues', :js do
it 'user can display create new epic form by clicking the dropdown item' do it 'user can display create new epic form by clicking the dropdown item' do
expect(page).not_to have_selector('input[placeholder="New epic title"]') expect(page).not_to have_selector('input[placeholder="New epic title"]')
find('.related-items-tree-container .js-add-epics-button .dropdown-toggle').click find('.related-items-tree-container .js-add-epics-issues-button .dropdown-toggle').click
find('.related-items-tree-container .js-add-epics-button .dropdown-item', text: 'Create new epic').click find('.related-items-tree-container .js-add-epics-issues-button .dropdown-item', text: 'Add a new epic').click
expect(page).to have_selector('input[placeholder="New epic title"]') expect(page).to have_selector('input[placeholder="New epic title"]')
end end
...@@ -222,8 +224,10 @@ describe 'Epic Issues', :js do ...@@ -222,8 +224,10 @@ describe 'Epic Issues', :js do
stub_licensed_features(epics: true, subepics: false) stub_licensed_features(epics: true, subepics: false)
visit_epic visit_epic
find('.related-items-tree-container .js-add-epics-issues-button').click
expect(page).not_to have_selector('.related-items-tree-container .js-add-epics-button') expect(page).not_to have_selector('.related-items-tree-container .js-add-epics-issues-button .dropdown-item', text: 'Add an existing epic')
expect(page).not_to have_selector('.related-items-tree-container .js-add-epics-issues-button .dropdown-item', text: 'Add a new epic')
end end
end end
end end
...@@ -231,7 +235,7 @@ describe 'Epic Issues', :js do ...@@ -231,7 +235,7 @@ describe 'Epic Issues', :js do
it 'user can add new issues to the epic' do it 'user can add new issues to the epic' do
references = "#{issue_to_add.to_reference(full: true)}" references = "#{issue_to_add.to_reference(full: true)}"
add_issues(references, button_selector: '.js-issue-actions-split-button') add_issues(references)
expect(page).not_to have_selector('.gl-field-error') expect(page).not_to have_selector('.gl-field-error')
expect(page).not_to have_content("Issue cannot be found.") expect(page).not_to have_content("Issue cannot be found.")
......
...@@ -6,10 +6,7 @@ import RelatedItemsTreeApp from 'ee/related_items_tree/components/related_items_ ...@@ -6,10 +6,7 @@ import RelatedItemsTreeApp from 'ee/related_items_tree/components/related_items_
import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue'; import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue';
import createDefaultStore from 'ee/related_items_tree/store'; import createDefaultStore from 'ee/related_items_tree/store';
import { issuableTypesMap } from 'ee/related_issues/constants'; import { issuableTypesMap } from 'ee/related_issues/constants';
import AddItemForm from 'ee/related_issues/components/add_issuable_form.vue';
import CreateIssueForm from 'ee/related_items_tree/components/create_issue_form.vue'; import CreateIssueForm from 'ee/related_items_tree/components/create_issue_form.vue';
import IssueActionsSplitButton from 'ee/related_items_tree/components/issue_actions_split_button.vue';
import { TEST_HOST } from 'spec/test_constants';
import AxiosMockAdapter from 'axios-mock-adapter'; import AxiosMockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { getJSONFixture } from 'helpers/fixtures'; import { getJSONFixture } from 'helpers/fixtures';
...@@ -41,12 +38,7 @@ describe('RelatedItemsTreeApp', () => { ...@@ -41,12 +38,7 @@ describe('RelatedItemsTreeApp', () => {
let axiosMock; let axiosMock;
let wrapper; let wrapper;
const findAddItemForm = () => wrapper.find(AddItemForm);
const findCreateIssueForm = () => wrapper.find(CreateIssueForm); const findCreateIssueForm = () => wrapper.find(CreateIssueForm);
const findIssueActionsSplitButton = () => wrapper.find(IssueActionsSplitButton);
const showCreateIssueForm = () => {
findIssueActionsSplitButton().vm.$emit('showCreateIssueForm');
};
beforeEach(() => { beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios); axiosMock = new AxiosMockAdapter(axios);
...@@ -249,73 +241,4 @@ describe('RelatedItemsTreeApp', () => { ...@@ -249,73 +241,4 @@ describe('RelatedItemsTreeApp', () => {
expect(findCreateIssueForm().exists()).toBe(false); expect(findCreateIssueForm().exists()).toBe(false);
}); });
}); });
describe('issue actions split button', () => {
beforeEach(() => {
wrapper = createComponent();
wrapper.vm.$store.state.itemsFetchInProgress = false;
return wrapper.vm.$nextTick();
});
it('renders issue actions split button', () => {
expect(findIssueActionsSplitButton().exists()).toBe(true);
});
describe('after split button emitted showAddIssueForm event', () => {
it('shows add item form', () => {
expect(findAddItemForm().exists()).toBe(false);
findIssueActionsSplitButton().vm.$emit('showAddIssueForm');
return wrapper.vm.$nextTick().then(() => {
expect(findAddItemForm().exists()).toBe(true);
});
});
});
describe('after split button emitted showCreateIssueForm event', () => {
it('shows create item form', () => {
expect(findCreateIssueForm().exists()).toBe(false);
showCreateIssueForm();
return wrapper.vm.$nextTick(() => {
expect(findCreateIssueForm().exists()).toBe(true);
});
});
});
describe('after create issue form emitted cancel event', () => {
beforeEach(() => showCreateIssueForm());
it('hides the form', () => {
expect(findCreateIssueForm().exists()).toBe(true);
findCreateIssueForm().vm.$emit('cancel');
return wrapper.vm.$nextTick().then(() => {
expect(findCreateIssueForm().exists()).toBe(false);
});
});
});
describe('after create issue form emitted submit event', () => {
beforeEach(() => showCreateIssueForm());
it('dispatches createNewIssue action', () => {
const issuesEndpoint = `${TEST_HOST}/issues`;
axiosMock.onPost(issuesEndpoint).replyOnce(200, {});
const params = {
issuesEndpoint,
title: 'some new issue',
};
findCreateIssueForm().vm.$emit('submit', params);
return axios.waitFor(issuesEndpoint).then(({ data }) => {
expect(JSON.parse(data).title).toBe(params.title);
});
});
});
});
}); });
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { GlDeprecatedButton, GlTooltip, GlIcon } from '@gitlab/ui'; import { GlTooltip, GlIcon } from '@gitlab/ui';
import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue'; import RelatedItemsTreeHeader from 'ee/related_items_tree/components/related_items_tree_header.vue';
import createDefaultStore from 'ee/related_items_tree/store'; import createDefaultStore from 'ee/related_items_tree/store';
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils'; import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
import { issuableTypesMap } from 'ee/related_issues/constants'; import { issuableTypesMap } from 'ee/related_issues/constants';
import EpicActionsSplitButton from 'ee/related_items_tree/components/epic_actions_split_button.vue'; import EpicActionsSplitButton from 'ee/related_items_tree/components/epic_issue_actions_split_button.vue';
import { mockInitialConfig, mockParentItem, mockQueryResponse } from '../mock_data'; import { mockInitialConfig, mockParentItem, mockQueryResponse } from '../mock_data';
...@@ -41,8 +41,7 @@ describe('RelatedItemsTree', () => { ...@@ -41,8 +41,7 @@ describe('RelatedItemsTree', () => {
describe('RelatedItemsTreeHeader', () => { describe('RelatedItemsTreeHeader', () => {
let wrapper; let wrapper;
const findAddIssuesButton = () => wrapper.find(GlDeprecatedButton); const findEpicsIssuesSplitButton = () => wrapper.find(EpicActionsSplitButton);
const findEpicsSplitButton = () => wrapper.find(EpicActionsSplitButton);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -64,7 +63,7 @@ describe('RelatedItemsTree', () => { ...@@ -64,7 +63,7 @@ describe('RelatedItemsTree', () => {
}); });
}); });
describe('epic actions split button', () => { describe('epic issue actions split button', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); wrapper = createComponent();
}); });
...@@ -82,7 +81,7 @@ describe('RelatedItemsTree', () => { ...@@ -82,7 +81,7 @@ describe('RelatedItemsTree', () => {
}); });
it('dispatches toggleAddItemForm action', () => { it('dispatches toggleAddItemForm action', () => {
findEpicsSplitButton().vm.$emit('showAddEpicForm'); findEpicsIssuesSplitButton().vm.$emit('showAddEpicForm');
expect(toggleAddItemForm).toHaveBeenCalled(); expect(toggleAddItemForm).toHaveBeenCalled();
...@@ -108,7 +107,7 @@ describe('RelatedItemsTree', () => { ...@@ -108,7 +107,7 @@ describe('RelatedItemsTree', () => {
}); });
it('dispatches toggleCreateEpicForm action', () => { it('dispatches toggleCreateEpicForm action', () => {
findEpicsSplitButton().vm.$emit('showCreateEpicForm'); findEpicsIssuesSplitButton().vm.$emit('showCreateEpicForm');
expect(toggleCreateEpicForm).toHaveBeenCalled(); expect(toggleCreateEpicForm).toHaveBeenCalled();
...@@ -118,38 +117,28 @@ describe('RelatedItemsTree', () => { ...@@ -118,38 +117,28 @@ describe('RelatedItemsTree', () => {
expect(payload).toEqual({ toggleState: true }); expect(payload).toEqual({ toggleState: true });
}); });
}); });
});
describe('add issues button', () => { describe('showAddIssueForm event', () => {
beforeEach(() => {
wrapper = createComponent();
});
describe('click event', () => {
let toggleAddItemForm; let toggleAddItemForm;
let setItemInputValue; let setItemInputValue;
beforeEach(() => { beforeEach(() => {
setItemInputValue = jest.fn();
toggleAddItemForm = jest.fn(); toggleAddItemForm = jest.fn();
setItemInputValue = jest.fn();
wrapper.vm.$store.hotUpdate({ wrapper.vm.$store.hotUpdate({
actions: { actions: {
setItemInputValue,
toggleAddItemForm, toggleAddItemForm,
setItemInputValue,
}, },
}); });
}); });
it('dispatches setItemInputValue and toggleAddItemForm action', () => { it('dispatches toggleAddItemForm action', () => {
findAddIssuesButton().vm.$emit('click'); findEpicsIssuesSplitButton().vm.$emit('showAddIssueForm');
expect(setItemInputValue).toHaveBeenCalled();
expect(setItemInputValue.mock.calls[setItemInputValue.mock.calls.length - 1][1]).toBe('');
expect(toggleAddItemForm).toHaveBeenCalled(); expect(toggleAddItemForm).toHaveBeenCalled();
const payload = toggleAddItemForm.mock.calls[setItemInputValue.mock.calls.length - 1][1]; const payload = toggleAddItemForm.mock.calls[0][1];
expect(payload).toEqual({ expect(payload).toEqual({
issuableType: issuableTypesMap.ISSUE, issuableType: issuableTypesMap.ISSUE,
...@@ -157,6 +146,30 @@ describe('RelatedItemsTree', () => { ...@@ -157,6 +146,30 @@ describe('RelatedItemsTree', () => {
}); });
}); });
}); });
describe('showCreateIssueForm event', () => {
let toggleCreateIssueForm;
beforeEach(() => {
toggleCreateIssueForm = jest.fn();
wrapper.vm.$store.hotUpdate({
actions: {
toggleCreateIssueForm,
},
});
});
it('dispatches toggleCreateIssueForm action', () => {
findEpicsIssuesSplitButton().vm.$emit('showCreateIssueForm');
expect(toggleCreateIssueForm).toHaveBeenCalled();
const payload =
toggleCreateIssueForm.mock.calls[toggleCreateIssueForm.mock.calls.length - 1][1];
expect(payload).toEqual({ toggleState: true });
});
});
}); });
describe('template', () => { describe('template', () => {
...@@ -170,42 +183,17 @@ describe('RelatedItemsTree', () => { ...@@ -170,42 +183,17 @@ describe('RelatedItemsTree', () => {
expect(badgesContainerEl.isVisible()).toBe(true); expect(badgesContainerEl.isVisible()).toBe(true);
}); });
describe('when sub-epics feature is available', () => { it('renders epics count and gl-icon', () => {
it('renders epics count and gl-icon', () => { const epicsEl = wrapper.findAll('.issue-count-badge > span').at(0);
const epicsEl = wrapper.findAll('.issue-count-badge > span').at(0); const epicIcon = epicsEl.find(GlIcon);
const epicIcon = epicsEl.find(GlIcon);
expect(epicsEl.text().trim()).toContain('2');
expect(epicIcon.isVisible()).toBe(true);
expect(epicIcon.props('name')).toBe('epic');
});
it('renders `Add an epic` dropdown button', () => { expect(epicsEl.text().trim()).toContain('2');
expect(findEpicsSplitButton().isVisible()).toBe(true); expect(epicIcon.isVisible()).toBe(true);
}); expect(epicIcon.props('name')).toBe('epic');
}); });
describe('when sub-epics feature is not available', () => { it('renders `Add` dropdown button', () => {
beforeEach(() => { expect(findEpicsIssuesSplitButton().isVisible()).toBe(true);
wrapper.vm.$store.commit('SET_INITIAL_CONFIG', {
...mockInitialConfig,
allowSubEpics: false,
});
return wrapper.vm.$nextTick();
});
it('does not render epics count and gl-icon', () => {
const countBadgesEl = wrapper.findAll('.issue-count-badge > span');
const badgeIcon = countBadgesEl.at(0).find(GlIcon);
expect(countBadgesEl).toHaveLength(1);
expect(badgeIcon.props('name')).toBe('issues');
});
it('does not render `Add an epic` dropdown button', () => {
expect(findEpicsSplitButton().exists()).toBe(false);
});
}); });
it('renders issues count and gl-icon', () => { it('renders issues count and gl-icon', () => {
...@@ -216,38 +204,6 @@ describe('RelatedItemsTree', () => { ...@@ -216,38 +204,6 @@ describe('RelatedItemsTree', () => {
expect(issueIcon.isVisible()).toBe(true); expect(issueIcon.isVisible()).toBe(true);
expect(issueIcon.props('name')).toBe('issues'); expect(issueIcon.props('name')).toBe('issues');
}); });
it('renders `Add an issue` dropdown button', () => {
const addIssueBtn = findAddIssuesButton();
expect(addIssueBtn.isVisible()).toBe(true);
expect(addIssueBtn.text()).toBe('Add an issue');
});
});
describe('slots', () => {
describe('issueActions', () => {
it('defaults to button', () => {
wrapper = createComponent();
expect(findAddIssuesButton().exists()).toBe(true);
});
it('uses provided slot content', () => {
const issueActions = {
template: '<p>custom content</p>',
};
wrapper = createComponent({
slots: {
issueActions,
},
});
expect(findAddIssuesButton().exists()).toBe(false);
expect(wrapper.find(issueActions).exists()).toBe(true);
});
});
}); });
}); });
}); });
...@@ -1191,6 +1191,9 @@ msgstr "" ...@@ -1191,6 +1191,9 @@ msgstr ""
msgid "Add a link" msgid "Add a link"
msgstr "" msgstr ""
msgid "Add a new issue"
msgstr ""
msgid "Add a numbered list" msgid "Add a numbered list"
msgstr "" msgstr ""
...@@ -1206,7 +1209,7 @@ msgstr "" ...@@ -1206,7 +1209,7 @@ msgstr ""
msgid "Add an SSH key" msgid "Add an SSH key"
msgstr "" msgstr ""
msgid "Add an existing issue to the epic." msgid "Add an existing issue"
msgstr "" msgstr ""
msgid "Add an issue" msgid "Add an issue"
...@@ -6153,9 +6156,6 @@ msgstr "" ...@@ -6153,9 +6156,6 @@ msgstr ""
msgid "Create a new issue" msgid "Create a new issue"
msgstr "" msgstr ""
msgid "Create a new issue and add it to the epic."
msgstr ""
msgid "Create a new repository" msgid "Create a new repository"
msgstr "" msgstr ""
...@@ -6165,9 +6165,6 @@ msgstr "" ...@@ -6165,9 +6165,6 @@ msgstr ""
msgid "Create an account using:" msgid "Create an account using:"
msgstr "" msgstr ""
msgid "Create an issue"
msgstr ""
msgid "Create an issue. Issues are created for each alert triggered." msgid "Create an issue. Issues are created for each alert triggered."
msgstr "" msgstr ""
...@@ -8324,10 +8321,10 @@ msgstr "" ...@@ -8324,10 +8321,10 @@ msgstr ""
msgid "Epics, Issues, and Merge Requests" msgid "Epics, Issues, and Merge Requests"
msgstr "" msgstr ""
msgid "Epics|Add an epic" msgid "Epics|Add a new epic"
msgstr "" msgstr ""
msgid "Epics|Add an existing epic as a child epic." msgid "Epics|Add an existing epic"
msgstr "" msgstr ""
msgid "Epics|An error occurred while saving the %{epicDateType} date" msgid "Epics|An error occurred while saving the %{epicDateType} date"
...@@ -8339,12 +8336,6 @@ msgstr "" ...@@ -8339,12 +8336,6 @@ msgstr ""
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?" msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "" msgstr ""
msgid "Epics|Create an epic within this group and add it as a child epic."
msgstr ""
msgid "Epics|Create new epic"
msgstr ""
msgid "Epics|How can I solve this?" msgid "Epics|How can I solve this?"
msgstr "" msgstr ""
......
...@@ -20,8 +20,8 @@ module QA ...@@ -20,8 +20,8 @@ module QA
element :add_issue_input element :add_issue_input
end end
view 'ee/app/assets/javascripts/related_items_tree/components/issue_actions_split_button.vue' do view 'ee/app/assets/javascripts/related_items_tree/components/epic_issue_actions_split_button.vue' do
element :issue_actions_split_button element :epic_issue_actions_split_button
end end
view 'ee/app/assets/javascripts/related_items_tree/components/tree_item.vue' do view 'ee/app/assets/javascripts/related_items_tree/components/tree_item.vue' do
...@@ -33,7 +33,7 @@ module QA ...@@ -33,7 +33,7 @@ module QA
end end
def add_issue_to_epic(issue_url) def add_issue_to_epic(issue_url)
find_element(:issue_actions_split_button).find('button', text: 'Add an issue').click find_element(:epic_issue_actions_split_button).find('button', text: 'Add an issue').click
fill_element :add_issue_input, issue_url fill_element :add_issue_input, issue_url
# Clicking the title blurs the input # Clicking the title blurs the input
click_element :title click_element :title
......
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