Commit e77edc54 authored by Arturo Herrero's avatar Arturo Herrero

Merge branch '326428-fj-add-issues-menu' into 'master'

Add Issues menu

See merge request gitlab-org/gitlab!59363
parents f296d426 07488ef7
- if project_nav_tab? :issues
= nav_link(controller: @project.issues_enabled? ? ['projects/issues', :labels, :milestones, :boards, :iterations] : 'projects/issues') do
= link_to project_issues_path(@project), class: 'shortcuts-issues qa-issues-item' do
.nav-icon-container
= sprite_icon('issues')
%span.nav-item-name#js-onboarding-issues-link
= _('Issues')
- if @project.issues_enabled?
%span.badge.badge-pill.count.issue_counter
= number_with_delimiter(@project.open_issues_count(current_user))
%ul.sidebar-sub-level-items
= nav_link(controller: 'projects/issues', action: :index, html_options: { class: "fly-out-top-item" } ) do
= link_to project_issues_path(@project) do
%strong.fly-out-top-item-name
= _('Issues')
- if @project.issues_enabled?
%span.badge.badge-pill.count.issue_counter.fly-out-badge
= number_with_delimiter(@project.open_issues_count(current_user))
%li.divider.fly-out-top-item
= nav_link(controller: :issues, action: :index) do
= link_to project_issues_path(@project), title: _('Issues') do
%span
= _('List')
= nav_link(controller: :boards) do
= link_to project_boards_path(@project), title: boards_link_text, data: { qa_selector: "issue_boards_link" } do
%span
= boards_link_text
= nav_link(controller: :labels) do
= link_to project_labels_path(@project), title: _('Labels'), class: 'qa-labels-link' do
%span
= _('Labels')
= render 'projects/sidebar/issues_service_desk'
= nav_link(controller: :milestones) do
= link_to project_milestones_path(@project), title: _('Milestones'), class: 'qa-milestones-link' do
%span
= _('Milestones')
= render_if_exists 'layouts/nav/sidebar/project_iterations_link'
- if project_nav_tab?(:external_issue_tracker)
- issue_tracker = @project.external_issue_tracker
- if issue_tracker.is_a?(JiraService) && project_jira_issues_integration?
......
= nav_link(controller: :issues, action: :service_desk ) do
= link_to service_desk_project_issues_path(@project), title: 'Service Desk' do
= _('Service Desk')
- return unless @project.feature_available?(:iterations)
- return unless can?(current_user, :read_iteration, @project)
= nav_link(controller: :iterations) do
= link_to project_iterations_path(@project), title: _('Iterations') do
%span
= _('Iterations')
# frozen_string_literal: true
module EE
module Sidebars
module Projects
module Menus
module IssuesMenu
extend ::Gitlab::Utils::Override
override :configure_menu_items
def configure_menu_items
return false unless super
add_item(iterations_menu_item)
true
end
private
def iterations_menu_item
return unless context.project.licensed_feature_available?(:iterations)
return unless can?(context.current_user, :read_iteration, context.project)
::Sidebars::MenuItem.new(
title: _('Iterations'),
link: project_iterations_path(context.project),
active_routes: { controller: :iterations },
item_id: :iterations
)
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::IssuesMenu do
let(:project) { build(:project) }
let(:user) { project.owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
describe 'Iterations' do
subject { described_class.new(context).items.index { |e| e.item_id == :iterations} }
context 'when licensed feature iterations is not enabled' do
it 'does not include iterations menu item' do
stub_licensed_features(iterations: false)
is_expected.to be_nil
end
end
context 'when licensed feature iterations is enabled' do
before do
stub_licensed_features(iterations: true)
end
context 'when user can read iterations' do
it 'includes iterations menu item' do
is_expected.to be_present
end
end
context 'when user cannot read iterations' do
let(:user) { nil }
it 'does not include iterations menu item' do
is_expected.to be_nil
end
end
end
end
end
......@@ -26,14 +26,16 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'issue boards' do
it 'has boards tab' do
allow(view).to receive(:can?).and_return(true)
allow(License).to receive(:feature_available?).and_call_original
describe 'Issues' do
describe 'Iterations' do
it 'has a link to the issue iterations path' do
allow(view).to receive(:current_user).and_return(user)
stub_licensed_features(iterations: true)
render
render
expect(rendered).to have_css('a[title="Boards"]')
expect(rendered).to have_link('Iterations', href: project_iterations_path(project))
end
end
end
......@@ -308,66 +310,4 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
expect(rendered).to have_link('Operations', href: project_settings_operations_path(project))
end
end
describe 'iterations link' do
context 'with authorized user' do
let_it_be(:current_user) { create(:user) }
before do
project.add_guest(current_user)
allow(view).to receive(:current_user).and_return(current_user)
end
context 'with iterations licensed feature available' do
before do
stub_licensed_features(iterations: true)
end
it 'is visible' do
render
expect(rendered).to have_text 'Iterations'
end
end
context 'with iterations licensed feature disabled' do
before do
stub_licensed_features(iterations: false)
end
it 'is not visible' do
render
expect(rendered).not_to have_text 'Iterations'
end
end
end
context 'with unauthorized user' do
context 'with iterations licensed feature available' do
before do
stub_licensed_features(iterations: true)
end
it 'is not visible' do
render
expect(rendered).not_to have_text 'Iterations'
end
end
context 'with iterations licensed feature disabled' do
before do
stub_licensed_features(iterations: false)
end
it 'is not visible' do
render
expect(rendered).not_to have_text 'Iterations'
end
end
end
end
end
......@@ -9,7 +9,7 @@ module Sidebars
@link = link
@active_routes = active_routes
@item_id = item_id
@container_html_options = container_html_options
@container_html_options = { aria: { label: title } }.merge(container_html_options)
@sprite_icon = sprite_icon
@sprite_icon_html_options = sprite_icon_html_options
@hint_html_options = hint_html_options
......
# frozen_string_literal: true
module Sidebars
module Projects
module Menus
class IssuesMenu < ::Sidebars::Menu
include Gitlab::Utils::StrongMemoize
override :configure_menu_items
def configure_menu_items
return unless can?(context.current_user, :read_issue, context.project)
add_item(list_menu_item)
add_item(boards_menu_item)
add_item(labels_menu_item)
add_item(service_desk_menu_item)
add_item(milestones_menu_item)
true
end
override :link
def link
project_issues_path(context.project)
end
override :extra_container_html_options
def extra_container_html_options
{
class: 'shortcuts-issues'
}
end
override :title
def title
_('Issues')
end
override :title_html_options
def title_html_options
{
id: 'js-onboarding-issues-link'
}
end
override :sprite_icon
def sprite_icon
'issues'
end
override :active_routes
def active_routes
{ controller: 'projects/issues' }
end
override :has_pill?
def has_pill?
strong_memoize(:has_pill) do
context.project.issues_enabled?
end
end
override :pill_count
def pill_count
strong_memoize(:pill_count) do
context.project.open_issues_count(context.current_user)
end
end
override :pill_html_options
def pill_html_options
{
class: 'issue_counter'
}
end
private
def list_menu_item
::Sidebars::MenuItem.new(
title: _('List'),
link: project_issues_path(context.project),
active_routes: { path: 'projects/issues#index' },
container_html_options: { aria: { label: _('Issues') } },
item_id: :issue_list
)
end
def boards_menu_item
title = context.project.multiple_issue_boards_available? ? s_('IssueBoards|Boards') : s_('IssueBoards|Board')
::Sidebars::MenuItem.new(
title: title,
link: project_boards_path(context.project),
active_routes: { controller: :boards },
item_id: :boards
)
end
def labels_menu_item
::Sidebars::MenuItem.new(
title: _('Labels'),
link: project_labels_path(context.project),
active_routes: { controller: :labels },
item_id: :labels
)
end
def service_desk_menu_item
::Sidebars::MenuItem.new(
title: _('Service Desk'),
link: service_desk_project_issues_path(context.project),
active_routes: { path: 'issues#service_desk' },
item_id: :service_desk
)
end
def milestones_menu_item
::Sidebars::MenuItem.new(
title: _('Milestones'),
link: project_milestones_path(context.project),
active_routes: { controller: :milestones },
item_id: :milestones
)
end
end
end
end
end
Sidebars::Projects::Menus::IssuesMenu.prepend_if_ee('EE::Sidebars::Projects::Menus::IssuesMenu')
......@@ -10,6 +10,7 @@ module Sidebars
add_menu(Sidebars::Projects::Menus::ProjectOverviewMenu.new(context))
add_menu(Sidebars::Projects::Menus::LearnGitlabMenu.new(context))
add_menu(Sidebars::Projects::Menus::RepositoryMenu.new(context))
add_menu(Sidebars::Projects::Menus::IssuesMenu.new(context))
end
override :render_raw_menus_partial
......
......@@ -10,9 +10,7 @@ module QA
def self.prepended(base)
base.class_eval do
view 'app/views/shared/nav/_sidebar_menu_item.html.haml' do
element :sidebar_menu_item_link
end
prepend QA::Page::Project::SubMenus::Common
end
end
......
......@@ -338,8 +338,10 @@ module QA
end
end
def scroll_to_element(name, *args)
scroll_to(element_selector_css(name), *args)
def scroll_to_element(name, *kwargs)
text = kwargs.delete(:text)
scroll_to(element_selector_css(name, kwargs), text: text)
end
def element_selector_css(name, *attributes)
......
......@@ -23,10 +23,6 @@ module QA
element :wiki_link
end
view 'app/views/shared/nav/_sidebar_menu_item.html.haml' do
element :sidebar_menu_item_link
end
def click_merge_requests
within_sidebar do
click_element(:merge_requests_link)
......
......@@ -8,6 +8,20 @@ module QA
extend QA::Page::PageConcern
include QA::Page::SubMenus::Common
def self.included(base)
super
base.class_eval do
view 'app/views/shared/nav/_sidebar_menu_item.html.haml' do
element :sidebar_menu_item_link
end
view 'app/views/shared/nav/_sidebar_menu.html.haml' do
element :sidebar_menu_link
end
end
end
private
def sidebar_element
......
......@@ -12,32 +12,25 @@ module QA
base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/layouts/nav/sidebar/_project_menus.html.haml' do
element :issue_boards_link
element :issues_item
element :labels_link
element :milestones_link
end
end
end
def click_issues
within_sidebar do
click_link('Issues')
click_element(:sidebar_menu_link, menu_item: 'Issues')
end
end
def click_milestones
within_sidebar do
click_element :milestones_link
click_element(:sidebar_menu_item_link, menu_item: 'Milestones')
end
end
def go_to_boards
hover_issues do
within_submenu do
click_element(:issue_boards_link)
click_element(:sidebar_menu_item_link, menu_item: 'Boards')
end
end
end
......@@ -45,7 +38,7 @@ module QA
def go_to_labels
hover_issues do
within_submenu do
click_element(:labels_link)
click_element(:sidebar_menu_item_link, menu_item: 'Labels')
end
end
end
......@@ -53,7 +46,7 @@ module QA
def go_to_milestones
hover_issues do
within_submenu do
click_element(:milestones_link)
click_element(:sidebar_menu_item_link, menu_item: 'Milestones')
end
end
end
......@@ -62,8 +55,8 @@ module QA
def hover_issues
within_sidebar do
scroll_to_element(:issues_item)
find_element(:issues_item).hover
scroll_to_element(:sidebar_menu_link, menu_item: 'Issues')
find_element(:sidebar_menu_link, menu_item: 'Issues').hover
yield
end
......
......@@ -12,10 +12,6 @@ module QA
base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/shared/nav/_sidebar_menu.html.haml' do
element :sidebar_menu_link
end
end
end
......
......@@ -12,14 +12,6 @@ module QA
base.class_eval do
include QA::Page::Project::SubMenus::Common
view 'app/views/shared/nav/_sidebar_menu_item.html.haml' do
element :sidebar_menu_item_link
end
view 'app/views/shared/nav/_sidebar_menu.html.haml' do
element :sidebar_menu_link
end
end
end
......
......@@ -21,7 +21,7 @@ RSpec.describe 'Service Desk Issue Tracker', :js do
before do
visit project_path(project)
find('.sidebar-top-level-items .shortcuts-issues').click
find('.sidebar-sub-level-items a[title="Service Desk"]').click
find('.sidebar-sub-level-items a', text: 'Service Desk').click
end
it 'can navigate to the service desk from link in the sidebar' do
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::MenuItem do
let(:title) { 'foo' }
let(:html_options) { {} }
let(:menu_item) { described_class.new(title: title, active_routes: {}, link: '', container_html_options: html_options) }
it 'includes by default aria-label attribute set to the title' do
expect(menu_item.container_html_options).to eq({ aria: { label: title } })
end
context 'when aria-label is overridde during initialization' do
let(:html_options) { { aria: { label: 'bar' } } }
it 'sets the aria-label to the new attribute' do
expect(menu_item.container_html_options).to eq html_options
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::IssuesMenu do
let(:project) { build(:project) }
let(:user) { project.owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
subject { described_class.new(context) }
describe '#render?' do
context 'when user can read issues' do
it 'returns true' do
expect(subject.render?).to eq true
end
end
context 'when user cannot read issues' do
let(:user) { nil }
it 'returns false' do
expect(subject.render?).to eq false
end
end
end
describe '#has_pill?' do
context 'when issues feature is enabled' do
it 'returns true' do
expect(subject.has_pill?).to eq true
end
end
context 'when issue feature is disabled' do
it 'returns false' do
allow(project).to receive(:issues_enabled?).and_return(false)
expect(subject.has_pill?).to eq false
end
end
end
describe '#pill_count' do
it 'returns zero when there are no open issues' do
expect(subject.pill_count).to eq 0
end
it 'memoizes the query' do
subject.pill_count
control = ActiveRecord::QueryRecorder.new do
subject.pill_count
end
expect(control.count).to eq 0
end
context 'when there are open issues' do
it 'returns the number of open issues' do
create_list(:issue, 2, :opened, project: project)
create(:issue, :closed, project: project)
expect(subject.pill_count).to eq 2
end
end
end
end
......@@ -127,11 +127,57 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
describe 'issue boards' do
it 'has board tab' do
describe 'Issues' do
it 'has a link to the issue list path' do
render
expect(rendered).to have_css('a[title="Boards"]')
expect(rendered).to have_link('Issues', href: project_issues_path(project))
end
it 'shows pill with the number of open issues' do
render
expect(rendered).to have_css('span.badge.badge-pill.issue_counter')
end
describe 'Issue List' do
it 'has a link to the issue list path' do
render
expect(rendered).to have_link('List', href: project_issues_path(project))
end
end
describe 'Issue Boards' do
it 'has a link to the issue boards path' do
render
expect(rendered).to have_link('Boards', href: project_boards_path(project))
end
end
describe 'Labels' do
it 'has a link to the labels path' do
render
expect(rendered).to have_link('Labels', href: project_labels_path(project))
end
end
describe 'Service Desk' do
it 'has a link to the service desk path' do
render
expect(rendered).to have_link('Service Desk', href: service_desk_project_issues_path(project))
end
end
describe 'Milestones' do
it 'has a link to the milestones path' do
render
expect(rendered).to have_link('Milestones', href: project_milestones_path(project))
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