Commit b600f42c authored by Jarka Kadlecová's avatar Jarka Kadlecová Committed by Jarka Košanová

Add support for sorting epics

parent 9554796e
module SortingHelper
prepend ::EE::SortingHelper
def sort_options_hash
{
sort_value_created_date => sort_title_created_date,
......
......@@ -77,7 +77,7 @@ It will display a dropdown menu, from which you can add an author. You can also
text to search by epic title or description. When done, press <kbd>Enter</kbd> on your
keyboard to filter the list.
You can also sort epics list by **Created date** or **Last updated**.
You can also sort epics list by **Created date**, **Last updated**, **Planned start date**, or **Planned finish date**.
![epics sort](img/epics_sort.png)
......
......@@ -6,6 +6,8 @@ An Epic within a group containing **Planned start date** and/or **Planned finish
can be visualized in a form of a timeline (e.g. a Gantt chart). The Epics Roadmap page
shows such a visualization for all the epics which are under a group and/or its subgroups.
Epics in the view can be sorted by **Created date**, **Last updated**, **Planned start date**, or **Planned finish date**.
![roadmap view](img/roadmap_view.png)
## Timeline duration
......
# frozen_string_literal: true
module EpicsActions
private
def finder_type
EpicsFinder
end
def collection_type
@collection_type ||= 'Epic'
end
def default_sort_order
sort_value_end_date
end
def update_cookie_value(value)
case value
when 'start_date_asc' then sort_value_start_date
when 'end_date_asc' then sort_value_end_date
else
super(value)
end
end
end
......@@ -4,6 +4,7 @@ class Groups::EpicsController < Groups::ApplicationController
include ToggleAwardEmoji
include ToggleSubscriptionAction
include RendersNotes
include EpicsActions
before_action :check_epics_available!
before_action :epic, except: [:index, :create]
......@@ -90,14 +91,6 @@ class Groups::EpicsController < Groups::ApplicationController
EpicsFinder
end
def collection_type
@collection_type ||= 'Epic'
end
# we don't support custom sorting for epics and therefore don't want to use the issuable_sort cookie
def set_sort_order_from_cookie
end
def preload_for_collection
@preload_for_collection ||= [:group, :author]
end
......@@ -113,6 +106,7 @@ class Groups::EpicsController < Groups::ApplicationController
end
def filter_params
set_sort_order_from_cookie
super.merge(start_date: params[:start_date], end_date: params[:end_date])
end
end
module Groups
class RoadmapController < Groups::ApplicationController
include IssuableCollections
include EpicsActions
before_action :check_epics_available!
before_action :group
before_action :persist_roadmap_layout, only: [:show]
def show
# show roadmap for a group
set_sort_order_from_cookie
@sort = params[:sort] || default_sort_order
@epics_count = EpicsFinder.new(current_user, group_id: @group.id).execute.count
end
......
# frozen_string_literal: true
module EE
module SortingHelper
extend ::Gitlab::Utils::Override
override :sort_options_hash
def sort_options_hash
{
sort_value_start_date => sort_title_start_date,
sort_value_end_date => sort_title_end_date
}.merge(super)
end
def sort_title_start_date
s_('SortOptions|Planned start date')
end
def sort_title_end_date
s_('SortOptions|Planned finish date')
end
def sort_value_start_date
'start_date_asc'
end
def sort_value_end_date
'end_date_asc'
end
end
end
......@@ -29,6 +29,14 @@ module EE
nulls_first = ::Gitlab::Database.postgresql? ? 'NULLS FIRST' : ''
reorder("COALESCE(start_date, end_date) ASC #{nulls_first}")
end
scope :order_start_date_asc, -> do
reorder(::Gitlab::Database.nulls_last_order('start_date'), 'id DESC')
end
scope :order_end_date_asc, -> do
reorder(::Gitlab::Database.nulls_last_order('end_date'), 'id DESC')
end
end
class_methods do
......@@ -71,8 +79,10 @@ module EE
end
def order_by(method)
if method.to_s == 'start_or_end_date'
order_start_or_end_date_asc
case method.to_s
when 'start_or_end_date' then order_start_or_end_date_asc
when 'start_date_asc' then order_start_date_asc
when 'end_date_asc' then order_end_date_asc
else
super
end
......
......@@ -7,7 +7,7 @@
- has_filters_applied = params[:label_name].present? || params[:author_username].present? || params[:search].present?
- if @epics_count != 0
= render 'shared/epic/search_bar', type: :epics, hide_sort_dropdown: true, show_roadmap_presets: true
= render 'shared/epic/search_bar', type: :epics, show_roadmap_presets: true
#js-roadmap{ data: { epics_path: group_epics_path(@group, format: :json), group_id: @group.id, empty_state_illustration: image_path('illustrations/epics/roadmap.svg'), has_filters_applied: "#{has_filters_applied}", new_epic_endpoint: group_epics_path(@group), preset_type: roadmap_layout } }
- else
......
......@@ -8,3 +8,5 @@
%li
= sortable_item(sort_title_created_date, page_filter_path(sort: sort_value_created_date, label: true), sorted_by)
= sortable_item(sort_title_recently_updated, page_filter_path(sort: sort_value_recently_updated, label: true), sorted_by)
= sortable_item(sort_title_start_date, page_filter_path(sort: sort_value_start_date, label: true), sorted_by)
= sortable_item(sort_title_end_date, page_filter_path(sort: sort_value_end_date, label: true), sorted_by)
---
title: Add support for sorting epics
merge_request: 6885
author:
type: added
......@@ -57,6 +57,13 @@ describe Groups::EpicsController do
expect(response).to have_gitlab_http_status(200)
end
it 'stores sorting param in a cookie' do
get :index, group_id: group, sort: 'start_date_asc'
expect(cookies['epic_sort']).to eq('start_date_asc')
expect(response).to have_gitlab_http_status(200)
end
context 'with page param' do
let(:last_page) { group.epics.page.total_pages }
......
# frozen_string_literal: true
require 'spec_helper'
describe Groups::RoadmapController do
let(:group) { create(:group, :private) }
let(:epic) { create(:epic, group: group) }
let(:user) { create(:user) }
describe '#show' do
before do
sign_in(user)
group.add_developer(user)
end
context 'when epics feature is disabled' do
it "returns 404 status" do
get :show, group_id: group
expect(response).to have_gitlab_http_status(404)
end
end
context 'when epics feature is enabled' do
before do
stub_licensed_features(epics: true)
end
it "returns 200 status" do
get :show, group_id: group
expect(response).to have_gitlab_http_status(200)
end
it 'stores sorting param in a cookie' do
get :show, group_id: group, sort: 'start_date_asc'
expect(cookies['epic_sort']).to eq('start_date_asc')
expect(response).to have_gitlab_http_status(200)
end
end
end
end
......@@ -11,7 +11,9 @@ describe 'epics list', :js do
end
context 'when epics exist for the group' do
let!(:epics) { create_list(:epic, 2, group: group) }
let!(:epic1) { create(:epic, group: group, end_date: 10.days.ago) }
let!(:epic2) { create(:epic, group: group, start_date: 2.days.ago) }
let!(:epic3) { create(:epic, group: group, start_date: 10.days.ago, end_date: 5.days.ago) }
before do
visit group_epics_path(group)
......@@ -19,7 +21,7 @@ describe 'epics list', :js do
it 'shows the epics in the navigation sidebar' do
expect(first('.nav-sidebar .active a .nav-item-name')).to have_content('Epics')
expect(first('.nav-sidebar .active a .count')).to have_content('2')
expect(first('.nav-sidebar .active a .count')).to have_content('3')
end
it 'renders the filtered search bar correctly' do
......@@ -28,24 +30,78 @@ describe 'epics list', :js do
end
end
it 'renders the list correctly' do
it 'sorts by end_date ASC by default' do
expect(page).to have_button('Planned finish date')
page.within('.content-wrapper .content') do
expect(find('.top-area')).to have_content('All 3')
page.within(".issuable-list") do
page.within("li:nth-child(1)") do
expect(page).to have_content(epic1.title)
end
page.within("li:nth-child(2)") do
expect(page).to have_content(epic3.title)
end
page.within("li:nth-child(3)") do
expect(page).to have_content(epic2.title)
end
end
end
end
it 'sorts by the selected value and stores the selection for epic list & roadmap' do
page.within('.epics-other-filters') do
click_button 'Planned finish date'
sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
expect(sort_options[0]).to eq('Created date')
expect(sort_options[1]).to eq('Last updated')
expect(sort_options[2]).to eq('Planned start date')
expect(sort_options[3]).to eq('Planned finish date')
click_link 'Planned start date'
end
expect(page).to have_button('Planned start date')
page.within('.content-wrapper .content') do
expect(find('.top-area')).to have_content('All 2')
within('.issuable-list') do
expect(page).to have_content(epics.first.title)
expect(page).to have_content(epics.second.title)
expect(find('.top-area')).to have_content('All 3')
page.within(".issuable-list") do
page.within("li:nth-child(1)") do
expect(page).to have_content(epic3.title)
end
page.within("li:nth-child(2)") do
expect(page).to have_content(epic2.title)
end
page.within("li:nth-child(3)") do
expect(page).to have_content(epic1.title)
end
end
end
visit group_epics_path(group)
expect(page).to have_button('Planned start date')
visit group_roadmap_path(group)
expect(page).to have_button('Planned start date')
end
it 'renders the epic detail correctly after clicking the link' do
page.within('.content-wrapper .content .issuable-list') do
click_link(epics.first.title)
click_link(epic1.title)
end
wait_for_requests
expect(page.find('.issuable-details h2.title')).to have_content(epics.first.title)
expect(page.find('.issuable-details h2.title')).to have_content(epic1.title)
end
end
......
......@@ -43,8 +43,22 @@ describe 'group epic roadmap', :js do
end
it 'renders the filtered search bar correctly' do
page.within('.content-wrapper .content') do
expect(page).to have_css('.epics-filters')
page.within('.content-wrapper .content .epics-filters') do
expect(page).to have_css('.filtered-search-box')
end
end
it 'renders the sort dropdown correctly' do
page.within('.content-wrapper .content .epics-filters') do
expect(page).to have_css('.filter-dropdown-container')
find('.dropdown-toggle').click
page.within('.dropdown-menu') do
expect(page).to have_selector('li a', count: 4)
expect(page).to have_content('Created date')
expect(page).to have_content('Last updated')
expect(page).to have_content('Planned start date')
expect(page).to have_content('Planned finish date')
end
end
end
......
......@@ -6,7 +6,7 @@ describe EpicsFinder do
let(:group) { create(:group, :private) }
let(:another_group) { create(:group) }
let!(:epic1) { create(:epic, group: group, title: 'This is awesome epic', created_at: 1.week.ago) }
let!(:epic2) { create(:epic, group: group, created_at: 4.days.ago, author: user, start_date: 2.days.ago) }
let!(:epic2) { create(:epic, group: group, created_at: 4.days.ago, author: user, start_date: 2.days.ago, end_date: 3.days.from_now) }
let!(:epic3) { create(:epic, group: group, description: 'not so awesome', start_date: 5.days.ago, end_date: 3.days.ago) }
let!(:epic4) { create(:epic, group: another_group) }
......@@ -61,6 +61,16 @@ describe EpicsFinder do
expect(amount).to be <= 7
end
context 'sorting' do
it 'sorts correctly when supported sorting param provided' do
expect(epics(sort: :start_date_asc)).to eq([epic3, epic2, epic1])
end
it 'sorts by id when not supported sorting param provided' do
expect(epics(sort: :not_supported_param)).to eq([epic3, epic2, epic1])
end
end
context 'by created_at' do
it 'returns all epics created before the given date' do
expect(epics(created_before: 2.days.ago)).to contain_exactly(epic1, epic2)
......
......@@ -30,16 +30,42 @@ describe Epic do
end
end
describe '.order_start_or_end_date_asc' do
let(:group) { create(:group) }
describe 'ordering' do
let!(:epic1) { create(:epic, start_date: 7.days.ago, end_date: 3.days.ago, updated_at: 3.days.ago, created_at: 7.days.ago) }
let!(:epic2) { create(:epic, start_date: 3.days.ago, updated_at: 10.days.ago, created_at: 12.days.ago) }
let!(:epic3) { create(:epic, end_date: 5.days.ago, updated_at: 5.days.ago, created_at: 6.days.ago) }
let!(:epic4) { create(:epic) }
def epics(order_by)
described_class.order_by(order_by)
end
it 'orders by start_or_end_date' do
expect(epics(:start_or_end_date)).to eq([epic4, epic1, epic3, epic2])
end
it 'orders by start_date ASC' do
expect(epics(:start_date_asc)).to eq([epic1, epic2, epic4, epic3])
end
it 'orders by end_date ASC' do
expect(epics(:end_date_asc)).to eq([epic3, epic1, epic4, epic2])
end
it 'orders by updated_at ASC' do
expect(epics(:updated_asc)).to eq([epic2, epic3, epic1, epic4])
end
it 'returns epics sorted by start or end date' do
epic1 = create(:epic, group: group, start_date: 7.days.ago, end_date: 3.days.ago)
epic2 = create(:epic, group: group, start_date: 3.days.ago)
epic3 = create(:epic, group: group, end_date: 5.days.ago)
epic4 = create(:epic, group: group)
it 'orders by updated_at DESC' do
expect(epics(:updated_desc)).to eq([epic4, epic1, epic3, epic2])
end
it 'orders by created_at ASC' do
expect(epics(:created_asc)).to eq([epic2, epic1, epic3, epic4])
end
expect(described_class.order_start_or_end_date_asc).to eq([epic4, epic1, epic3, epic2])
it 'orders by created_at DESC' do
expect(epics(:created_desc)).to eq([epic4, epic3, epic1, epic2])
end
end
......
......@@ -6744,6 +6744,12 @@ msgstr ""
msgid "SortOptions|Oldest updated"
msgstr ""
msgid "SortOptions|Planned finish date"
msgstr ""
msgid "SortOptions|Planned start date"
msgstr ""
msgid "SortOptions|Popularity"
msgstr ""
......
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