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:
- 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.
![epic view](img/epic_view_v12.3.png)
![epic view](img/epic_view_v13.0.png)
## Adding an issue to an epic
......@@ -75,6 +75,7 @@ the issue is automatically unlinked from its current parent.
To add an issue to an epic:
1. Click the **Add** dropdown button.
1. Click **Add an issue**.
1. Identify the issue to be added, using either of the following methods:
- 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
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. From the **Project** dropdown, select the project in which the issue should be created.
1. Click **Create issue**.
......@@ -128,6 +129,7 @@ the maximum depth being 5.
To add a child epic to an epic:
1. Click the **Add** dropdown button.
1. Click **Add an epic**.
1. Identify the epic to be added, using either of the following methods:
- Paste the link of the epic.
......
......@@ -72,7 +72,11 @@ export default {
mounted() {
this.setupAutoComplete();
if (this.focusOnMount) {
this.$refs.input.focus();
this.$nextTick()
.then(() => {
this.$refs.input.focus();
})
.catch(() => {});
}
},
beforeUpdate() {
......
......@@ -29,7 +29,11 @@ export default {
},
},
mounted() {
this.$refs.input.focus();
this.$nextTick()
.then(() => {
this.$refs.input.focus();
})
.catch(() => {});
},
methods: {
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';
import { GlLoadingIcon } from '@gitlab/ui';
import { issuableTypesMap } from 'ee/related_issues/constants';
import AddItemForm from 'ee/related_issues/components/add_issuable_form.vue';
import SlotSwitch from '~/vue_shared/components/slot_switch.vue';
import CreateEpicForm from './create_epic_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 RelatedItemsTreeHeader from './related_items_tree_header.vue';
......@@ -34,7 +31,6 @@ export default {
CreateEpicForm,
TreeItemRemoveModal,
CreateIssueForm,
IssueActionsSplitButton,
SlotSwitch,
},
computed: {
......@@ -133,12 +129,6 @@ export default {
this.toggleCreateEpicForm({ toggleState: false });
this.setItemInputValue('');
},
handleShowAddIssueForm() {
this.toggleAddItemForm({ toggleState: true, issuableType: issuableTypesMap.ISSUE });
},
handleShowCreateIssueForm() {
this.toggleCreateIssueForm({ toggleState: true });
},
},
};
</script>
......@@ -156,14 +146,7 @@ export default {
'overflow-auto': directChildren.length > $options.OVERFLOW_AFTER,
}"
>
<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>
<related-items-tree-header :class="{ 'border-bottom-0': itemsFetchResultEmpty }" />
<slot-switch
v-if="visibleForm"
:active-slot-names="[visibleForm]"
......
<script>
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 EpicActionsSplitButton from './epic_actions_split_button.vue';
import EpicActionsSplitButton from './epic_issue_actions_split_button.vue';
import EpicHealthStatus from './epic_health_status.vue';
export default {
components: {
GlDeprecatedButton,
GlTooltip,
GlIcon,
EpicHealthStatus,
......@@ -26,17 +25,25 @@ export default {
},
},
methods: {
...mapActions(['toggleAddItemForm', 'toggleCreateEpicForm', 'setItemInputValue']),
showAddEpicForm() {
...mapActions([
'toggleCreateIssueForm',
'toggleAddItemForm',
'toggleCreateEpicForm',
'setItemInputValue',
]),
showAddIssueForm() {
this.setItemInputValue('');
this.toggleAddItemForm({
issuableType: issuableTypesMap.EPIC,
issuableType: issuableTypesMap.ISSUE,
toggleState: true,
});
},
showAddIssueForm() {
this.setItemInputValue('');
showCreateIssueForm() {
this.toggleCreateIssueForm({ toggleState: true });
},
showAddEpicForm() {
this.toggleAddItemForm({
issuableType: issuableTypesMap.ISSUE,
issuableType: issuableTypesMap.EPIC,
toggleState: true,
});
},
......@@ -88,24 +95,18 @@ export default {
</div>
<epic-health-status v-if="healthStatus" :health-status="healthStatus" />
</div>
<div class="d-inline-flex flex-column flex-sm-row js-button-container">
<template v-if="parentItem.userPermissions.adminEpic">
<epic-actions-split-button
v-if="allowSubEpics"
class="qa-add-epics-button mb-2 mb-sm-0"
@showAddEpicForm="showAddEpicForm"
@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>
<div
v-if="parentItem.userPermissions.adminEpic"
class="d-inline-flex flex-column flex-sm-row js-button-container"
>
<epic-actions-split-button
:allow-sub-epics="allowSubEpics"
class="js-add-epics-issues-button qa-add-epics-button mb-2 mb-sm-0"
@showAddIssueForm="showAddIssueForm"
@showCreateIssueForm="showCreateIssueForm"
@showAddEpicForm="showAddEpicForm"
@showCreateEpicForm="showCreateEpicForm"
/>
</div>
</div>
</template>
......@@ -142,17 +142,20 @@ export default {
state.showAddItemForm = toggleState;
state.showCreateEpicForm = false;
state.showCreateIssueForm = false;
},
[types.TOGGLE_CREATE_EPIC_FORM](state, { toggleState }) {
state.showCreateEpicForm = toggleState;
state.showAddItemForm = false;
state.showCreateIssueForm = false;
state.issuableType = issuableTypesMap.EPIC;
},
[types.TOGGLE_CREATE_ISSUE_FORM](state, { toggleState }) {
state.showCreateIssueForm = toggleState;
state.showAddItemForm = false;
state.showCreateEpicForm = false;
},
[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
end
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
......@@ -69,8 +69,9 @@ describe 'Epic Issues', :js do
let(:issue_invalid) { create(:issue) }
let(:epic_to_add) { create(:epic, group: group) }
def add_issues(references, button_selector: '.js-issue-actions-split-button > button:first-child')
find(".related-items-tree-container #{button_selector}").click
def add_issues(references)
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)
# When adding long references, for some reason the input gets stuck
# waiting for more text. Send a keystroke before clicking the button to
......@@ -82,7 +83,8 @@ describe 'Epic Issues', :js do
end
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').send_keys(:tab)
......@@ -100,8 +102,8 @@ describe 'Epic Issues', :js 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"]')
find('.related-items-tree-container .js-add-epics-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-toggle').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"]')
end
......@@ -222,8 +224,10 @@ describe 'Epic Issues', :js do
stub_licensed_features(epics: true, subepics: false)
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
......@@ -231,7 +235,7 @@ describe 'Epic Issues', :js do
it 'user can add new issues to the epic' do
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_content("Issue cannot be found.")
......
......@@ -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 createDefaultStore from 'ee/related_items_tree/store';
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 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 axios from '~/lib/utils/axios_utils';
import { getJSONFixture } from 'helpers/fixtures';
......@@ -41,12 +38,7 @@ describe('RelatedItemsTreeApp', () => {
let axiosMock;
let wrapper;
const findAddItemForm = () => wrapper.find(AddItemForm);
const findCreateIssueForm = () => wrapper.find(CreateIssueForm);
const findIssueActionsSplitButton = () => wrapper.find(IssueActionsSplitButton);
const showCreateIssueForm = () => {
findIssueActionsSplitButton().vm.$emit('showCreateIssueForm');
};
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
......@@ -249,73 +241,4 @@ describe('RelatedItemsTreeApp', () => {
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 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 createDefaultStore from 'ee/related_items_tree/store';
import * as epicUtils from 'ee/related_items_tree/utils/epic_utils';
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';
......@@ -41,8 +41,7 @@ describe('RelatedItemsTree', () => {
describe('RelatedItemsTreeHeader', () => {
let wrapper;
const findAddIssuesButton = () => wrapper.find(GlDeprecatedButton);
const findEpicsSplitButton = () => wrapper.find(EpicActionsSplitButton);
const findEpicsIssuesSplitButton = () => wrapper.find(EpicActionsSplitButton);
afterEach(() => {
wrapper.destroy();
......@@ -64,7 +63,7 @@ describe('RelatedItemsTree', () => {
});
});
describe('epic actions split button', () => {
describe('epic issue actions split button', () => {
beforeEach(() => {
wrapper = createComponent();
});
......@@ -82,7 +81,7 @@ describe('RelatedItemsTree', () => {
});
it('dispatches toggleAddItemForm action', () => {
findEpicsSplitButton().vm.$emit('showAddEpicForm');
findEpicsIssuesSplitButton().vm.$emit('showAddEpicForm');
expect(toggleAddItemForm).toHaveBeenCalled();
......@@ -108,7 +107,7 @@ describe('RelatedItemsTree', () => {
});
it('dispatches toggleCreateEpicForm action', () => {
findEpicsSplitButton().vm.$emit('showCreateEpicForm');
findEpicsIssuesSplitButton().vm.$emit('showCreateEpicForm');
expect(toggleCreateEpicForm).toHaveBeenCalled();
......@@ -118,38 +117,28 @@ describe('RelatedItemsTree', () => {
expect(payload).toEqual({ toggleState: true });
});
});
});
describe('add issues button', () => {
beforeEach(() => {
wrapper = createComponent();
});
describe('click event', () => {
describe('showAddIssueForm event', () => {
let toggleAddItemForm;
let setItemInputValue;
beforeEach(() => {
setItemInputValue = jest.fn();
toggleAddItemForm = jest.fn();
setItemInputValue = jest.fn();
wrapper.vm.$store.hotUpdate({
actions: {
setItemInputValue,
toggleAddItemForm,
setItemInputValue,
},
});
});
it('dispatches setItemInputValue and toggleAddItemForm action', () => {
findAddIssuesButton().vm.$emit('click');
expect(setItemInputValue).toHaveBeenCalled();
expect(setItemInputValue.mock.calls[setItemInputValue.mock.calls.length - 1][1]).toBe('');
it('dispatches toggleAddItemForm action', () => {
findEpicsIssuesSplitButton().vm.$emit('showAddIssueForm');
expect(toggleAddItemForm).toHaveBeenCalled();
const payload = toggleAddItemForm.mock.calls[setItemInputValue.mock.calls.length - 1][1];
const payload = toggleAddItemForm.mock.calls[0][1];
expect(payload).toEqual({
issuableType: issuableTypesMap.ISSUE,
......@@ -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', () => {
......@@ -170,42 +183,17 @@ describe('RelatedItemsTree', () => {
expect(badgesContainerEl.isVisible()).toBe(true);
});
describe('when sub-epics feature is available', () => {
it('renders epics count and gl-icon', () => {
const epicsEl = wrapper.findAll('.issue-count-badge > span').at(0);
const epicIcon = epicsEl.find(GlIcon);
expect(epicsEl.text().trim()).toContain('2');
expect(epicIcon.isVisible()).toBe(true);
expect(epicIcon.props('name')).toBe('epic');
});
it('renders epics count and gl-icon', () => {
const epicsEl = wrapper.findAll('.issue-count-badge > span').at(0);
const epicIcon = epicsEl.find(GlIcon);
it('renders `Add an epic` dropdown button', () => {
expect(findEpicsSplitButton().isVisible()).toBe(true);
});
expect(epicsEl.text().trim()).toContain('2');
expect(epicIcon.isVisible()).toBe(true);
expect(epicIcon.props('name')).toBe('epic');
});
describe('when sub-epics feature is not available', () => {
beforeEach(() => {
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 `Add` dropdown button', () => {
expect(findEpicsIssuesSplitButton().isVisible()).toBe(true);
});
it('renders issues count and gl-icon', () => {
......@@ -216,38 +204,6 @@ describe('RelatedItemsTree', () => {
expect(issueIcon.isVisible()).toBe(true);
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 ""
msgid "Add a link"
msgstr ""
msgid "Add a new issue"
msgstr ""
msgid "Add a numbered list"
msgstr ""
......@@ -1206,7 +1209,7 @@ msgstr ""
msgid "Add an SSH key"
msgstr ""
msgid "Add an existing issue to the epic."
msgid "Add an existing issue"
msgstr ""
msgid "Add an issue"
......@@ -6153,9 +6156,6 @@ msgstr ""
msgid "Create a new issue"
msgstr ""
msgid "Create a new issue and add it to the epic."
msgstr ""
msgid "Create a new repository"
msgstr ""
......@@ -6165,9 +6165,6 @@ msgstr ""
msgid "Create an account using:"
msgstr ""
msgid "Create an issue"
msgstr ""
msgid "Create an issue. Issues are created for each alert triggered."
msgstr ""
......@@ -8324,10 +8321,10 @@ msgstr ""
msgid "Epics, Issues, and Merge Requests"
msgstr ""
msgid "Epics|Add an epic"
msgid "Epics|Add a new epic"
msgstr ""
msgid "Epics|Add an existing epic as a child epic."
msgid "Epics|Add an existing epic"
msgstr ""
msgid "Epics|An error occurred while saving the %{epicDateType} date"
......@@ -8339,12 +8336,6 @@ msgstr ""
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
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?"
msgstr ""
......
......@@ -20,8 +20,8 @@ module QA
element :add_issue_input
end
view 'ee/app/assets/javascripts/related_items_tree/components/issue_actions_split_button.vue' do
element :issue_actions_split_button
view 'ee/app/assets/javascripts/related_items_tree/components/epic_issue_actions_split_button.vue' do
element :epic_issue_actions_split_button
end
view 'ee/app/assets/javascripts/related_items_tree/components/tree_item.vue' do
......@@ -33,7 +33,7 @@ module QA
end
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
# Clicking the title blurs the input
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