Commit b38c9bd2 authored by Florie Guibert's avatar Florie Guibert

Consolidate epic tree buttons

Merge Add issue and epic buttons on epic tree
parent 72ec0a48
...@@ -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.
......
<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 epicActionItems = [
{
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',
},
];
const issueActionItems = [
{
title: s__('Add an issue'),
description: s__('Add an existing issue to the epic.'),
eventName: 'showAddIssueForm',
},
{
title: s__('Create an issue'),
description: s__('Create a new issue and add it to the epic.'),
eventName: 'showCreateIssueForm',
},
];
export default {
epicActionItems,
issueActionItems,
components: {
GlDropdown,
GlDropdownDivider,
GlDropdownHeader,
GlDropdownItem,
},
props: {
allowSubEpics: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
actionItems() {
return this.allowSubEpics ? [...epicActionItems, ...issueActionItems] : issueActionItems;
},
},
methods: {
change(item) {
this.$emit(item.eventName);
},
},
};
</script>
<template>
<gl-dropdown
:menu-class="`dropdown-menu-selectable`"
:text="s__('Add')"
variant="secondary"
data-qa-selector="epic_issue_actions_split_button"
v-on="$listeners"
>
<gl-dropdown-header>{{ s__('Issue') }}</gl-dropdown-header>
<template v-for="item in $options.issueActionItems">
<gl-dropdown-item :key="item.eventName" active-class="is-active" @click="change(item)">
{{ item.title }}
</gl-dropdown-item>
</template>
<template v-if="allowSubEpics">
<gl-dropdown-divider />
<gl-dropdown-header>{{ s__('Epic') }}</gl-dropdown-header>
<template v-for="item in $options.epicActionItems">
<gl-dropdown-item :key="item.eventName" active-class="is-active" @click="change(item)">
{{ item.title }}
</gl-dropdown-item>
</template>
</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,
}); });
}, },
...@@ -91,20 +98,13 @@ export default { ...@@ -91,20 +98,13 @@ export default {
<div class="d-inline-flex flex-column flex-sm-row js-button-container"> <div class="d-inline-flex flex-column flex-sm-row js-button-container">
<template v-if="parentItem.userPermissions.adminEpic"> <template v-if="parentItem.userPermissions.adminEpic">
<epic-actions-split-button <epic-actions-split-button
v-if="allowSubEpics" :allow-sub-epics="allowSubEpics"
class="qa-add-epics-button mb-2 mb-sm-0" class="js-add-epics-issues-button qa-add-epics-button mb-2 mb-sm-0"
@showAddIssueForm="showAddIssueForm"
@showCreateIssueForm="showCreateIssueForm"
@showAddEpicForm="showAddEpicForm" @showAddEpicForm="showAddEpicForm"
@showCreateEpicForm="showCreateEpicForm" @showCreateEpicForm="showCreateEpicForm"
/> />
<slot name="issueActions">
<gl-deprecated-button
class="ml-1 js-add-issues-button qa-add-issues-button"
size="sm"
@click="showAddIssueForm"
>{{ __('Add an issue') }}</gl-deprecated-button
>
</slot>
</template> </template>
</div> </div>
</div> </div>
......
---
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 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 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: 'Create 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 epic')
expect(page).not_to have_selector('.related-items-tree-container .js-add-epics-issues-button .dropdown-item', text: 'Create 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);
});
});
}); });
}); });
}); });
...@@ -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