Commit bdb69dfe authored by Dmytro Zaporozhets's avatar Dmytro Zaporozhets

Merge branch '13847-add-epic-dropdown-new-issue-page' into 'master'

Add Epics select dropdown in New Issue page

Closes #13847

See merge request gitlab-org/gitlab!32572
parents 3986aa16 88c4d24f
......@@ -403,6 +403,10 @@ module Issuable
participants(user).include?(user)
end
def can_assign_epic?(user)
false
end
def to_hook_data(user, old_associations: {})
changes = previous_changes
......
......@@ -31,7 +31,7 @@
= form.label :confidential, class: 'form-check-label' do
This issue is confidential and should only be visible to team members with at least Reporter access.
= render 'shared/issuable/form/metadata', issuable: issuable, form: form
= render 'shared/issuable/form/metadata', issuable: issuable, form: form, project: project
= render_if_exists 'shared/issuable/approvals', issuable: issuable, presenter: presenter, form: form
......
- project = local_assigns.fetch(:project)
- issuable = local_assigns.fetch(:issuable)
- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
......@@ -10,6 +11,9 @@
%div{ class: (has_due_date ? "col-lg-6" : "col-12") }
.form-group.row.merge-request-assignee
= render "shared/issuable/form/metadata_issuable_assignee", issuable: issuable, form: form, has_due_date: has_due_date
= render_if_exists "shared/issuable/form/epic", issuable: issuable, form: form, project: project
.form-group.row.issue-milestone
= form.label :milestone_id, "Milestone", class: "col-form-label #{has_due_date ? "col-md-2 col-lg-4" : "col-sm-2"}"
.col-sm-10{ class: ("col-md-8" if has_due_date) }
......@@ -22,11 +26,11 @@
.issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
= render_if_exists "shared/issuable/form/weight", issuable: issuable, form: form
= render_if_exists "shared/issuable/form/merge_request_blocks", issuable: issuable, form: form
- if has_due_date
- if has_due_date || issuable.supports_weight?
.col-lg-6
= render_if_exists "shared/issuable/form/weight", issuable: issuable, form: form
.form-group.row
= form.label :due_date, "Due date", class: "col-form-label col-md-2 col-lg-4"
.col-8
......
......@@ -4,24 +4,25 @@ group: Project Management
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
---
# Managing Issues
# Managing issues
[GitLab Issues](index.md) are the fundamental medium for collaborating on ideas and
planning work in GitLab. [Creating](#create-a-new-issue), [moving](#moving-issues),
[closing](#closing-issues), and [deleting](#deleting-issues) are key actions that
you can do with issues.
## Create a new Issue
## Create a new issue
When you create a new issue, you'll be prompted to fill in the [data and fields of the issue](issue_data_and_actions.md), as illustrated below. If you know
the values you want to assign to an issue, you can use the [Quick actions](../quick_actions.md)
feature to input values, instead of selecting them from lists.
When you create a new issue, you'll be prompted to fill in the [data and fields of the issue](issue_data_and_actions.md),
as illustrated below. If you know the values you want to assign to an issue, you can use the
[Quick actions](../quick_actions.md) feature to input values, instead of selecting them from lists.
![New issue from the issues list](img/new_issue.png)
While creating an issue, you can associate it to an existing epic from current group by
selecting it using **Epic** dropdown.
### Accessing the new Issue form
### Accessing the New Issue form
There are many ways to get to the new Issue form from within a project:
There are many ways to get to the New Issue form from within a project:
- Navigate to your **Project's Dashboard** > **Issues** > **New Issue**:
......@@ -42,9 +43,28 @@ There are many ways to get to the new Issue form from within a project:
![From the issue board](img/new_issue_from_issue_board.png)
### Elements of the New Issue form
> Ability to add the new issue to an epic [was introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13847)
> in [GitLab Premium](https://about.gitlab.com/pricing/) 13.1.
![New issue from the issues list](img/new_issue_v13_1.png)
When you're creating a new issue, these are the fields you can fill in:
- Title
- Description
- Checkbox to make the issue confidential
- Assignee
- Weight
- Epic **(PREMIUM)**
- Due date
- Milestone
- Labels
### New issue from the group-level Issue Tracker
Go to the Group dashboard and click "Issues" in the sidebar to visit the Issue Tracker
Go to the Group dashboard and click **Issues** in the sidebar to visit the Issue Tracker
for all projects in your Group. Select the project you'd like to add an issue for
using the dropdown button at the top-right of the page.
......@@ -153,7 +173,7 @@ issues.each do |issue|
end; nil
```
## Closing Issues
## Closing issues
When you decide that an issue is resolved, or no longer needed, you can close the issue
using the close button:
......@@ -251,7 +271,7 @@ In order to change the default issue closing pattern, GitLab administrators must
[`gitlab.rb` or `gitlab.yml` file](../../../administration/issue_closing_pattern.md)
of your installation.
## Deleting Issues
## Deleting issues
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/2982) in GitLab 8.6
......
import EpicsSelect from 'ee/vue_shared/components/sidebar/epics_select/epics_select_bundle';
import WeightSelect from 'ee/weight_select';
import initForm from '~/pages/projects/issues/form';
export default () => {
// eslint-disable-next-line no-new
new EpicsSelect();
// eslint-disable-next-line no-new
new WeightSelect();
initForm();
......
import Vue from 'vue';
import EpicsSelect from 'ee/vue_shared/components/sidebar/epics_select/base.vue';
import { noneEpic } from 'ee/vue_shared/constants';
import { DropdownVariant } from 'ee/vue_shared/components/sidebar/epics_select/constants';
export default () => {
const el = document.getElementById('js-epic-select-root');
const epicFormFieldEl = document.getElementById('issue_epic_id');
if (!el && !epicFormFieldEl) {
return false;
}
return new Vue({
el,
components: {
EpicsSelect,
},
data() {
return {
selectedEpic: noneEpic,
};
},
methods: {
handleEpicSelect(selectedEpic) {
this.selectedEpic = selectedEpic;
epicFormFieldEl.setAttribute('value', selectedEpic.id);
},
},
render(createElement) {
return createElement('epics-select', {
props: {
groupId: parseInt(el.dataset.groupId, 10),
issueId: 0,
epicIssueId: 0,
canEdit: true,
initialEpic: this.selectedEpic,
initialEpicLoading: false,
variant: DropdownVariant.Standalone,
},
on: {
onEpicSelect: this.handleEpicSelect.bind(this),
},
});
},
});
};
......@@ -121,6 +121,10 @@ module EE
project&.feature_available?(:issue_weights)
end
def can_assign_epic?(user)
user&.can?(:admin_epic, project.group)
end
def related_issues(current_user, preload: nil)
related_issues = ::Issue
.select(['issues.*', 'issue_links.id AS issue_link_id',
......
- project = local_assigns.fetch(:project)
- issuable = local_assigns.fetch(:issuable)
- return unless issuable.can_assign_epic?(current_user)
- form = local_assigns.fetch(:form)
.form-group.row.issue-epic
= form.label :label_ids, class: "col-form-label col-md-2 col-lg-4" do
= _('Epic')
.col-md-10.col-lg-8
.issuable-form-select-holder
%input{ id: 'issue_epic_id', type: 'hidden', name: 'issue[epic_id]' }
#js-epic-select-root{ data: { group_id: project.group.id } }
- issuable = local_assigns.fetch(:issuable)
- return unless issuable.supports_weight?
- has_due_date = issuable.has_attribute?(:due_date)
- form = local_assigns.fetch(:form)
......
---
title: Add ability to select Epic while creating a New Issue
merge_request: 32572
author:
type: added
......@@ -3,13 +3,16 @@
require "spec_helper"
RSpec.describe "User creates issue", :js do
let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
let(:group) { create(:group, :public) }
let(:project) { create(:project_empty_repo, :public, namespace: group) }
let!(:epic) { create(:epic, group: group, title: 'Sample epic', author: user) }
before do
stub_licensed_features(issue_weights: true)
stub_licensed_features(epics: true)
project.add_developer(user)
group.add_developer(user)
sign_in(user)
visit(new_project_issue_path(project))
......@@ -32,4 +35,28 @@ RSpec.describe "User creates issue", :js do
expect(page).to have_content(issue_title)
end
end
context "with epic set" do
it "creates issue" do
issue_title = "500 error on profile"
fill_in("Title", with: issue_title)
page.within('.issue-epic .js-epic-block') do
page.find('.js-epic-select').click
wait_for_requests
page.find('.dropdown-content .gl-link', text: epic.title).click
end
click_button("Submit issue")
wait_for_all_requests
page.within(".js-epic-block .js-epic-label") do
expect(page).to have_content(epic.title)
end
expect(page).to have_content(issue_title)
end
end
end
......@@ -678,4 +678,49 @@ RSpec.describe Issue do
it { is_expected.to be_falsey }
end
end
describe '#can_assign_epic?' do
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:project) { create(:project, group: group) }
let(:issue) { create(:issue, project: project) }
subject { issue.can_assign_epic?(user) }
context 'when epics feature is available' do
before do
stub_licensed_features(epics: true)
end
context 'when a user is not a project member' do
it 'returns false' do
expect(subject).to be_falsey
end
end
context 'when a user is a project member' do
it 'returns false' do
project.add_developer(user)
expect(subject).to be_falsey
end
end
context 'when a user is a group member' do
it 'returns true' do
group.add_developer(user)
expect(subject).to be_truthy
end
end
end
context 'when epics feature is not available' do
it 'returns false' do
group.add_developer(user)
expect(subject).to be_falsey
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment