Commit 437db4bf authored by Francisco Javier López's avatar Francisco Javier López Committed by Anastasia McDonald

Refactor Epics menu

In this commit we're refactoring the Epic menu
in the group sidebar.

We're moving away from partial defined logic to object
contained one. Now menus are defined in objects that
will contain all the information instead of in partials.
parent 2b8b1f2a
- issues_count = cached_issuables_count(@group, type: :issues) - issues_count = cached_issuables_count(@group, type: :issues)
- merge_requests_count = cached_issuables_count(@group, type: :merge_requests) - merge_requests_count = cached_issuables_count(@group, type: :merge_requests)
= render_if_exists "layouts/nav/ee/epic_link", group: @group
- if group_sidebar_link?(:issues) - if group_sidebar_link?(:issues)
= nav_link(path: group_issues_sub_menu_items, unless: -> { current_path?('issues_analytics#show') }) do = nav_link(path: group_issues_sub_menu_items, unless: -> { current_path?('issues_analytics#show') }) do
= link_to issues_group_path(@group), data: { qa_selector: 'group_issues_item' }, class: 'has-sub-items' do = link_to issues_group_path(@group), data: { qa_selector: 'group_issues_item' }, class: 'has-sub-items' do
......
- return unless group_sidebar_link?(:epics)
- epics_count = cached_issuables_count(group, type: :epics)
- epics_items = ['epics#show', 'epics#index', 'epic_boards#index', 'epic_boards#show', 'roadmap#show']
= nav_link(path: epics_items) do
= link_to group_epics_path(group), class: 'qa-group-epics-link has-sub-items' do
.nav-icon-container
= sprite_icon('epic')
%span.nav-item-name
= _('Epics')
%span.badge.badge-pill.count= number_with_delimiter(epics_count)
%ul.sidebar-sub-level-items
= nav_link(path: epics_items, html_options: { class: "fly-out-top-item" } ) do
= link_to group_epics_path(group) do
%strong.fly-out-top-item-name= _('Epics')
%span.badge.badge-pill.count.epic_counter.fly-out-badge= number_with_delimiter(epics_count)
%li.divider.fly-out-top-item
= nav_link(path: 'epics#index', html_options: { class: 'home' }) do
= link_to group_epics_path(group), title: 'List' do
%span= _('List')
= nav_link(path: ['epic_boards#index', 'epic_boards#show'], html_options: { class: "home" }) do
= link_to group_epic_boards_path(group), title: 'Boards' do
%span= _('Boards')
= nav_link(path: 'roadmap#show', html_options: { class: 'home' }) do
= link_to group_roadmap_path(group), title: 'Roadmap' do
%span= _('Roadmap')
...@@ -11,6 +11,7 @@ module EE ...@@ -11,6 +11,7 @@ module EE
super super
insert_menu_before(::Sidebars::Groups::Menus::GroupInformationMenu, ::Sidebars::Groups::Menus::TrialExperimentMenu.new(context)) insert_menu_before(::Sidebars::Groups::Menus::GroupInformationMenu, ::Sidebars::Groups::Menus::TrialExperimentMenu.new(context))
insert_menu_after(::Sidebars::Groups::Menus::GroupInformationMenu, ::Sidebars::Groups::Menus::EpicsMenu.new(context))
end end
end end
end end
......
# frozen_string_literal: true
module Sidebars
module Groups
module Menus
class EpicsMenu < ::Sidebars::Menu
include Gitlab::Utils::StrongMemoize
override :configure_menu_items
def configure_menu_items
return false unless can?(context.current_user, :read_epic, context.group)
add_item(epic_list_menu_item)
add_item(boards_menu_item)
add_item(roadmap_menu_item)
true
end
override :link
def link
renderable_items.first.link
end
override :title
def title
_('Epics')
end
override :sprite_icon
def sprite_icon
'epic'
end
override :active_routes
def active_routes
{ controller: :epics }
end
override :has_pill?
def has_pill?
true
end
override :pill_count
def pill_count
strong_memoize(:pill_count) do
count_service = ::Groups::EpicsCountService
count = count_service.new(context.group, context.current_user).count
format_cached_count(count_service, count)
end
end
private
def epic_list_menu_item
::Sidebars::MenuItem.new(
title: _('List'),
link: group_epics_path(context.group),
active_routes: { path: 'epics#index' },
container_html_options: { class: 'home' },
item_id: :epic_list
)
end
def boards_menu_item
::Sidebars::MenuItem.new(
title: _('Boards'),
link: group_epic_boards_path(context.group),
active_routes: { path: %w[epic_boards#index epic_boards#show] },
container_html_options: { class: 'home' },
item_id: :boards
)
end
def roadmap_menu_item
::Sidebars::MenuItem.new(
title: _('Roadmap'),
link: group_roadmap_path(context.group),
active_routes: { path: 'roadmap#show' },
container_html_options: { class: 'home' },
item_id: :roadmap
)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Sidebars::Groups::Menus::EpicsMenu do
let_it_be(:owner) { create(:user) }
let_it_be(:group) do
build(:group, :private).tap do |g|
g.add_owner(owner)
end
end
let(:user) { owner }
let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) }
let(:menu) { described_class.new(context) }
describe 'Menu Items' do
subject { menu.renderable_items }
before do
stub_licensed_features(epics: true)
end
describe 'when the user has access to epics' do
it 'has all the menus' do
expect(subject.map(&:item_id)).to include(:epic_list, :boards, :roadmap)
end
end
describe 'when the user does not have access' do
let(:user) { nil }
specify { is_expected.to be_empty }
end
end
it_behaves_like 'pill_count formatted results' do
let(:count_service) { ::Groups::EpicsCountService }
end
end
...@@ -76,6 +76,51 @@ RSpec.describe 'layouts/nav/sidebar/_group' do ...@@ -76,6 +76,51 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
end end
end end
describe 'Epics menu' do
let_it_be(:user) { create(:user) }
let_it_be(:group) do
create(:group).tap do |g|
g.add_maintainer(user)
end
end
before do
stub_licensed_features(epics: true)
allow(view).to receive(:current_user).and_return(user)
end
it 'has a link to the epic list path' do
render
expect(rendered).to have_link('Epics', href: group_epics_path(group))
end
describe 'List' do
it 'has a link to the epic list path' do
render
expect(rendered).to have_link('List', href: group_epics_path(group))
end
end
describe 'Boards' do
it 'has a link to the epic boards path' do
render
expect(rendered).to have_link('Boards', href: group_epic_boards_path(group))
end
end
describe 'Roadmap' do
it 'has a link to the epic roadmap path' do
render
expect(rendered).to have_link('Roadmap', href: group_roadmap_path(group))
end
end
end
describe 'DevOps adoption link' do describe 'DevOps adoption link' do
let!(:current_user) { create(:user) } let!(:current_user) { create(:user) }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
module Sidebars module Sidebars
module Concerns module Concerns
module HasPill module HasPill
include ActionView::Helpers::NumberHelper
def has_pill? def has_pill?
false false
end end
...@@ -18,6 +20,17 @@ module Sidebars ...@@ -18,6 +20,17 @@ module Sidebars
def pill_html_options def pill_html_options
{} {}
end end
def format_cached_count(count_service, count)
if count > count_service::CACHED_COUNT_THRESHOLD
number_to_human(
count,
units: { thousand: 'k', million: 'm' }, precision: 1, significant: false, format: '%n%u'
)
else
number_with_delimiter(count)
end
end
end end
end end
end end
...@@ -34,9 +34,6 @@ module QA ...@@ -34,9 +34,6 @@ module QA
element :ldap_synchronization_link element :ldap_synchronization_link
element :billing_link element :billing_link
end end
view 'ee/app/views/layouts/nav/ee/_epic_link.html.haml' do
element :group_epics_link
end
view 'ee/app/views/layouts/nav/ee/_security_link.html.haml' do view 'ee/app/views/layouts/nav/ee/_security_link.html.haml' do
element :security_compliance_link element :security_compliance_link
...@@ -120,7 +117,7 @@ module QA ...@@ -120,7 +117,7 @@ module QA
def click_group_epics_link def click_group_epics_link
within_sidebar do within_sidebar do
click_element(:group_epics_link) click_element(:sidebar_menu_link, menu_item: 'Epics')
end end
end end
......
# frozen_string_literal: true
RSpec.shared_examples_for 'pill_count formatted results' do
let(:count_service) { raise NotImplementedError }
subject(:pill_count) { menu.pill_count }
it 'returns all digits for count value under 1000' do
allow_next_instance_of(count_service) do |service|
allow(service).to receive(:count).and_return(999)
end
expect(pill_count).to eq('999')
end
it 'returns truncated digits for count value over 1000' do
allow_next_instance_of(count_service) do |service|
allow(service).to receive(:count).and_return(2300)
end
expect(pill_count).to eq('2.3k')
end
it 'returns truncated digits for count value over 10000' do
allow_next_instance_of(count_service) do |service|
allow(service).to receive(:count).and_return(12560)
end
expect(pill_count).to eq('12.6k')
end
it 'returns truncated digits for count value over 100000' do
allow_next_instance_of(count_service) do |service|
allow(service).to receive(:count).and_return(112560)
end
expect(pill_count).to eq('112.6k')
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