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)
- 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)
= 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
......
- 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
super
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
......
# 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
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
let!(:current_user) { create(:user) }
......
......@@ -5,6 +5,8 @@
module Sidebars
module Concerns
module HasPill
include ActionView::Helpers::NumberHelper
def has_pill?
false
end
......@@ -18,6 +20,17 @@ module Sidebars
def pill_html_options
{}
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
......@@ -34,9 +34,6 @@ module QA
element :ldap_synchronization_link
element :billing_link
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
element :security_compliance_link
......@@ -120,7 +117,7 @@ module QA
def click_group_epics_link
within_sidebar do
click_element(:group_epics_link)
click_element(:sidebar_menu_link, menu_item: 'Epics')
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