Merge branch '56992-add-filtering-to-project-dashboard-fe' into 'master'

Resolve "Add filtering to project dashboard [FE]"

Closes #56992

See merge request gitlab-org/gitlab-ce!25231
This commit is contained in:
Kushal Pandya 2019-05-07 06:45:11 +00:00
commit e33a8baff5
23 changed files with 575 additions and 51 deletions

View file

@ -1446,3 +1446,86 @@ pre.light-well {
}
}
}
.project-filters {
.btn svg {
color: $gl-gray-700;
}
.button-filter-group {
.btn {
width: 96px;
}
a {
color: $black;
}
.active {
background: $btn-active-gray;
}
}
.filtered-search-dropdown-label {
min-width: 68px;
@include media-breakpoint-down(xs) {
min-width: 60px;
}
}
.filtered-search {
min-width: 30%;
flex-basis: 0;
.project-filter-form .project-filter-form-field {
padding-right: $gl-padding-8;
}
.filtered-search,
.filtered-search-nav,
.filtered-search-dropdown {
flex-basis: 0;
}
@include media-breakpoint-down(lg) {
min-width: 15%;
.project-filter-form-field {
min-width: 150px;
}
}
@include media-breakpoint-down(md) {
min-width: 30%;
}
}
.filtered-search-box {
border-radius: 3px 0 0 3px;
}
.dropdown-menu-toggle {
margin-left: $gl-padding-8;
}
@include media-breakpoint-down(md) {
.extended-filtered-search-box {
min-width: 55%;
}
.filtered-search-dropdown {
width: 50%;
.dropdown-menu-toggle {
width: 100%;
}
}
}
@include media-breakpoint-down(xs) {
.filtered-search-dropdown {
width: 100%;
}
}
}

View file

@ -239,8 +239,10 @@ module ProjectsHelper
end
# rubocop: enable CodeReuse/ActiveRecord
# TODO: Remove this method when removing the feature flag
# https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/11209#note_162234863
def show_projects?(projects, params)
!!(params[:personal] || params[:name] || any_projects?(projects))
Feature.enabled?(:project_list_filter_bar) || !!(params[:personal] || params[:name] || any_projects?(projects))
end
def push_to_create_project_command(user = current_user)

View file

@ -128,7 +128,7 @@ module SearchHelper
# rubocop: disable CodeReuse/ActiveRecord
def projects_autocomplete(term, limit = 5)
current_user.authorized_projects.order_id_desc.search_by_title(term)
.sorted_by_stars.non_archived.limit(limit).map do |p|
.sorted_by_stars_desc.non_archived.limit(limit).map do |p|
{
category: "Projects",
id: p.id,

View file

@ -30,13 +30,20 @@ module SortingHelper
end
def projects_sort_options_hash
Feature.enabled?(:project_list_filter_bar) && !current_controller?('admin/projects') ? projects_sort_common_options_hash : old_projects_sort_options_hash
end
# TODO: Simplify these sorting options
# https://gitlab.com/gitlab-org/gitlab-ce/issues/60798
# https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/11209#note_162234858
def old_projects_sort_options_hash
options = {
sort_value_latest_activity => sort_title_latest_activity,
sort_value_name => sort_title_name,
sort_value_oldest_activity => sort_title_oldest_activity,
sort_value_oldest_created => sort_title_oldest_created,
sort_value_recently_created => sort_title_recently_created,
sort_value_most_stars => sort_title_most_stars
sort_value_stars_desc => sort_title_most_stars
}
if current_controller?('admin/projects')
@ -46,6 +53,41 @@ module SortingHelper
options
end
def projects_sort_common_options_hash
{
sort_value_latest_activity => sort_title_latest_activity,
sort_value_recently_created => sort_title_created_date,
sort_value_name => sort_title_name,
sort_value_stars_desc => sort_title_stars
}
end
def projects_sort_option_titles
{
sort_value_latest_activity => sort_title_latest_activity,
sort_value_recently_created => sort_title_created_date,
sort_value_name => sort_title_name,
sort_value_stars_desc => sort_title_stars,
sort_value_oldest_activity => sort_title_latest_activity,
sort_value_oldest_created => sort_title_created_date,
sort_value_name_desc => sort_title_name,
sort_value_stars_asc => sort_title_stars
}
end
def projects_reverse_sort_options_hash
{
sort_value_latest_activity => sort_value_oldest_activity,
sort_value_recently_created => sort_value_oldest_created,
sort_value_name => sort_value_name_desc,
sort_value_stars_desc => sort_value_stars_asc,
sort_value_oldest_activity => sort_value_latest_activity,
sort_value_oldest_created => sort_value_recently_created,
sort_value_name_desc => sort_value_name,
sort_value_stars_asc => sort_value_stars_desc
}
end
def groups_sort_options_hash
{
sort_value_name => sort_title_name,
@ -59,7 +101,7 @@ module SortingHelper
def subgroups_sort_options_hash
groups_sort_options_hash.merge(
sort_value_most_stars => sort_title_most_stars
sort_value_stars_desc => sort_title_most_stars
)
end
@ -176,6 +218,8 @@ module SortingHelper
end
end
# TODO: dedupicate issuable and project sort direction
# https://gitlab.com/gitlab-org/gitlab-ce/issues/60798
def issuable_sort_direction_button(sort_value)
link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort'
reverse_sort = issuable_reverse_sort_order_hash[sort_value]
@ -187,7 +231,23 @@ module SortingHelper
link_class += ' disabled'
end
link_to(reverse_url, type: 'button', class: link_class, title: 'Sort direction') do
link_to(reverse_url, type: 'button', class: link_class, title: s_('SortOptions|Sort direction')) do
sprite_icon("sort-#{issuable_sort_icon_suffix(sort_value)}", size: 16)
end
end
def project_sort_direction_button(sort_value)
link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort'
reverse_sort = projects_reverse_sort_options_hash[sort_value]
if reverse_sort
reverse_url = filter_projects_path(sort: reverse_sort)
else
reverse_url = '#'
link_class += ' disabled'
end
link_to(reverse_url, type: 'button', class: link_class, title: s_('SortOptions|Sort direction')) do
sprite_icon("sort-#{issuable_sort_icon_suffix(sort_value)}", size: 16)
end
end
@ -325,6 +385,10 @@ module SortingHelper
s_('SortOptions|Most stars')
end
def sort_title_stars
s_('SortOptions|Stars')
end
def sort_title_oldest_last_activity
s_('SortOptions|Oldest last activity')
end
@ -466,10 +530,14 @@ module SortingHelper
'contacted_asc'
end
def sort_value_most_stars
def sort_value_stars_desc
'stars_desc'
end
def sort_value_stars_asc
'stars_asc'
end
def sort_value_oldest_last_activity
'last_activity_on_asc'
end

View file

@ -357,7 +357,8 @@ class Project < ApplicationRecord
# last_activity_at is throttled every minute, but last_repository_updated_at is updated with every push
scope :sorted_by_activity, -> { reorder("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC") }
scope :sorted_by_stars, -> { reorder(star_count: :desc) }
scope :sorted_by_stars_desc, -> { reorder(star_count: :desc) }
scope :sorted_by_stars_asc, -> { reorder(star_count: :asc) }
scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
@ -544,7 +545,9 @@ class Project < ApplicationRecord
when 'latest_activity_asc'
reorder(last_activity_at: :asc)
when 'stars_desc'
sorted_by_stars
sorted_by_stars_desc
when 'stars_asc'
sorted_by_stars_asc
else
order_by(method)
end

View file

@ -7,7 +7,7 @@
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
.prepend-top-default
.search-holder
= render 'shared/projects/search_form', autofocus: true, icon: true
= render 'shared/projects/search_form', autofocus: true, icon: true, admin_view: true
.dropdown
- toggle_text = 'Namespace'
- if params[:namespace_id].present?

View file

@ -1,3 +1,6 @@
- project_tab_filter = local_assigns.fetch(:project_tab_filter, "")
- feature_project_list_filter_bar = Feature.enabled?(:project_list_filter_bar)
= content_for :flash_message do
= render 'shared/project_limit'
@ -6,24 +9,27 @@
- if current_user.can_create_project?
.page-title-controls
= link_to "New project", new_project_path, class: "btn btn-success"
= link_to _("New project"), new_project_path, class: "btn btn-success"
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.nav-links.scrolling-tabs.mobile-separator.nav.nav-tabs
%ul.nav-links.scrolling-tabs.mobile-separator.nav.nav-tabs{ class: ('border-0' if feature_project_list_filter_bar) }
= nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, class: 'shortcuts-activity', data: {placement: 'right'} do
Your projects
= _("Your projects")
%span.badge.badge-pill= limited_counter_with_delimiter(@total_user_projects_count)
= nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, data: {placement: 'right'} do
Starred projects
= _("Starred projects")
%span.badge.badge-pill= limited_counter_with_delimiter(@total_starred_projects_count)
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
= link_to explore_root_path, data: {placement: 'right'} do
Explore projects
.nav-controls
= render 'shared/projects/search_form'
= render 'shared/projects/dropdown'
= _("Explore projects")
- unless feature_project_list_filter_bar
.nav-controls
= render 'shared/projects/search_form'
= render 'shared/projects/dropdown'
- if feature_project_list_filter_bar
.project-filters
= render 'shared/projects/search_bar', project_tab_filter: project_tab_filter

View file

@ -1,6 +1,21 @@
.nav-block
%ul.nav-links.mobile-separator.nav.nav-tabs
= nav_link(html_options: { class: ("active" unless params[:personal].present?) }) do
= link_to s_('DashboardProjects|All'), dashboard_projects_path
= nav_link(html_options: { class: ("active" if params[:personal].present?) }) do
= link_to s_('DashboardProjects|Personal'), filter_projects_path(personal: true)
- inactive_class = 'btn p-2'
- active_class = 'btn p-2 active'
- project_tab_filter = local_assigns.fetch(:project_tab_filter, "")
- is_explore_trending = project_tab_filter == :explore_trending
- feature_project_list_filter_bar = Feature.enabled?(:project_list_filter_bar)
.nav-block{ class: ("w-100" if feature_project_list_filter_bar) }
- if feature_project_list_filter_bar
.btn-group.button-filter-group.d-flex.m-0.p-0
- if project_tab_filter == :explore || is_explore_trending
= link_to s_('DashboardProjects|Trending'), trending_explore_projects_path, class: is_explore_trending ? active_class : inactive_class
= link_to s_('DashboardProjects|All'), explore_projects_path, class: is_explore_trending ? inactive_class : active_class
- else
= link_to s_('DashboardProjects|All'), dashboard_projects_path, class: params[:personal].present? ? inactive_class : active_class
= link_to s_('DashboardProjects|Personal'), filter_projects_path(personal: true), class: params[:personal].present? ? active_class : inactive_class
- else
%ul.nav-links.mobile-separator.nav.nav-tabs
= nav_link(html_options: { class: ("active" unless params[:personal].present?) }) do
= link_to s_('DashboardProjects|All'), dashboard_projects_path
= nav_link(html_options: { class: ("active" if params[:personal].present?) }) do
= link_to s_('DashboardProjects|Personal'), filter_projects_path(personal: true)

View file

@ -13,7 +13,7 @@
= render "projects/last_push"
- if show_projects?(@projects, params)
= render 'dashboard/projects_head'
= render 'nav'
= render 'nav' unless Feature.enabled?(:project_list_filter_bar)
= render 'projects'
- else
= render "zero_authorized_projects"

View file

@ -8,7 +8,7 @@
%div{ class: container_class }
= render "projects/last_push"
= render 'dashboard/projects_head'
= render 'dashboard/projects_head', project_tab_filter: :starred
- if params[:filter_projects] || any_projects?(@projects)
= render 'projects'

View file

@ -1,8 +1,12 @@
- has_label = local_assigns.fetch(:has_label, false)
- feature_project_list_filter_bar = Feature.enabled?(:project_list_filter_bar)
- if current_user
.dropdown
.dropdown.js-project-filter-dropdown-wrap{ class: ('d-flex flex-grow-1 flex-shrink-1' if feature_project_list_filter_bar) }
%button.dropdown-menu-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe', class: 'mt-1')
%span.light.ml-3= _("Visibility:")
- unless has_label
= icon('globe', class: 'mt-1')
%span.light.ml-3= _("Visibility:")
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else

View file

@ -5,9 +5,9 @@
= render_dashboard_gold_trial(current_user)
- if current_user
= render 'dashboard/projects_head'
= render 'dashboard/projects_head', project_tab_filter: :explore
- else
= render 'explore/head'
= render 'explore/projects/nav'
= render 'explore/projects/nav' unless Feature.enabled?(:project_list_filter_bar) && current_user
= render 'projects', projects: @projects

View file

@ -5,9 +5,9 @@
= render_dashboard_gold_trial(current_user)
- if current_user
= render 'dashboard/projects_head'
= render 'dashboard/projects_head', project_tab_filter: :starred
- else
= render 'explore/head'
= render 'explore/projects/nav'
= render 'explore/projects/nav' unless Feature.enabled?(:project_list_filter_bar) && current_user
= render 'projects', projects: @projects

View file

@ -5,9 +5,9 @@
= render_dashboard_gold_trial(current_user)
- if current_user
= render 'dashboard/projects_head'
= render 'dashboard/projects_head', project_tab_filter: :explore_trending
- else
= render 'explore/head'
= render 'explore/projects/nav'
= render 'explore/projects/nav' unless Feature.enabled?(:project_list_filter_bar) && current_user
= render 'projects', projects: @projects

View file

@ -24,10 +24,10 @@
%li.divider
%li.js-filter-archived-projects
= link_to filter_groups_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
Hide archived projects
= _("Hide archived projects")
%li.js-filter-archived-projects
= link_to filter_groups_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
Show archived projects
= _("Show archived projects")
%li.js-filter-archived-projects
= link_to filter_groups_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
Show archived projects only
= _("Show archived projects only")

View file

@ -1,10 +1,9 @@
- @sort ||= sort_value_latest_activity
.dropdown.js-project-filter-dropdown-wrap
- toggle_text = projects_sort_options_hash[@sort]
= dropdown_toggle(toggle_text, { toggle: 'dropdown', display: 'static' }, { id: 'sort-projects-dropdown' })
= dropdown_toggle(projects_sort_options_hash[@sort], { toggle: 'dropdown', display: 'static' }, { id: 'sort-projects-dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
Sort by
= _("Sort by")
- projects_sort_options_hash.each do |value, title|
%li
= link_to filter_projects_path(sort: value), class: ("is-active" if @sort == value) do
@ -13,29 +12,29 @@
%li.divider
%li
= link_to filter_projects_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
Hide archived projects
= _("Hide archived projects")
%li
= link_to filter_projects_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
Show archived projects
= _("Show archived projects")
%li
= link_to filter_projects_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
Show archived projects only
= _("Show archived projects only")
- if current_user
%li.divider
%li
= link_to filter_projects_path(personal: nil), class: ("is-active" unless params[:personal].present?) do
Owned by anyone
= _("Owned by anyone")
%li
= link_to filter_projects_path(personal: true), class: ("is-active" if params[:personal].present?) do
Owned by me
= _("Owned by me")
- if @group && @group.shared_projects.present?
%li.divider
%li
= link_to filter_projects_path(shared: nil), class: ("is-active" unless params[:shared].present?) do
All projects
= _("All projects")
%li
= link_to filter_projects_path(shared: 0), class: ("is-active" if params[:shared] == '0') do
Hide shared projects
= _("Hide shared projects")
%li
= link_to filter_projects_path(shared: 1), class: ("is-active" if params[:shared] == '1') do
Hide group projects
= _("Hide group projects")

View file

@ -0,0 +1,28 @@
- @sort ||= sort_value_latest_activity
- project_tab_filter = local_assigns.fetch(:project_tab_filter, "")
- flex_grow_and_shrink_xs = 'd-flex flex-xs-grow-1 flex-xs-shrink-1 flex-grow-0 flex-shrink-0'
.filtered-search-block.row-content-block.bt-0
.filtered-search-wrapper.d-flex.flex-nowrap.flex-column.flex-sm-wrap.flex-sm-row.flex-xl-nowrap
- unless project_tab_filter == :starred
.filtered-search-nav.mb-2.mb-lg-0{ class: flex_grow_and_shrink_xs }
= render 'dashboard/projects/nav', project_tab_filter: project_tab_filter
.filtered-search.d-flex.flex-grow-1.flex-shrink-1.w-100.mb-2.mb-lg-0.ml-0{ class: project_tab_filter == :starred ? "extended-filtered-search-box mb-2 mb-lg-0" : "ml-sm-3" }
.btn-group.w-100{ role: "group" }
.btn-group.w-100{ role: "group" }
.filtered-search-box.m-0
.filtered-search-box-input-container.pl-2
= render 'shared/projects/search_form', admin_view: false, search_form_placeholder: _("Search projects...")
%button.btn.btn-secondary{ type: 'submit', form: 'project-filter-form' }
= sprite_icon('search', size: 16, css_class: 'search-icon ')
.filtered-search-dropdown.flex-row.align-items-center.mb-2.m-sm-0#filtered-search-visibility-dropdown{ class: flex_grow_and_shrink_xs }
.filtered-search-dropdown-label.p-0.pl-sm-3.font-weight-bold
%span
= _("Visibility")
= render 'explore/projects/filter', has_label: true
.filtered-search-dropdown.flex-row.align-items-center.m-sm-0#filtered-search-sorting-dropdown{ class: flex_grow_and_shrink_xs }
.filtered-search-dropdown-label.p-0.pl-sm-3.font-weight-bold
%span
= _("Sort by")
= render 'shared/projects/sort_dropdown'

View file

@ -1,7 +1,10 @@
- form_field_classes = local_assigns[:admin_view] || !Feature.enabled?(:project_list_filter_bar) ? 'input-short js-projects-list-filter' : ''
- placeholder = local_assigns[:search_form_placeholder] ? search_form_placeholder : 'Filter by name...'
= form_tag filter_projects_path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
= search_field_tag :name, params[:name],
placeholder: 'Filter by name...',
class: 'project-filter-form-field form-control input-short js-projects-list-filter',
placeholder: placeholder,
class: "project-filter-form-field form-control #{form_field_classes}",
spellcheck: false,
id: 'project-filter-form-field',
tabindex: "2",

View file

@ -0,0 +1,39 @@
- @sort ||= sort_value_latest_activity
- toggle_text = projects_sort_option_titles[@sort]
.btn-group.w-100{ role: "group" }
.btn-group.w-100.dropdown.js-project-filter-dropdown-wrap{ role: "group" }
%button#sort-projects-dropdown.btn.btn-default.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
= toggle_text
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
%li.dropdown-header
= _("Sort by")
- projects_sort_options_hash.each do |value, title|
%li
= link_to title, filter_projects_path(sort: value), class: ("is-active" if toggle_text == title)
%li.divider
%li
= link_to filter_projects_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
= _("Hide archived projects")
%li
= link_to filter_projects_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
= _("Show archived projects")
%li
= link_to filter_projects_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
= _("Show archived projects only")
- if current_user && @group && @group.shared_projects.present?
%li.divider
%li
= link_to filter_projects_path(shared: nil), class: ("is-active" unless params[:shared].present?) do
= _("All projects")
%li
= link_to filter_projects_path(shared: 0), class: ("is-active" if params[:shared] == '0') do
= _("Hide shared projects")
%li
= link_to filter_projects_path(shared: 1), class: ("is-active" if params[:shared] == '1') do
= _("Hide group projects")
= project_sort_direction_button(@sort)

View file

@ -754,6 +754,9 @@ msgstr ""
msgid "All merge conflicts were resolved. The merge request can now be merged."
msgstr ""
msgid "All projects"
msgstr ""
msgid "All todos were marked as done."
msgstr ""
@ -3072,6 +3075,9 @@ msgstr ""
msgid "DashboardProjects|Personal"
msgstr ""
msgid "DashboardProjects|Trending"
msgstr ""
msgid "Data is still calculating..."
msgstr ""
@ -4756,9 +4762,15 @@ msgstr ""
msgid "Help page text and support page url."
msgstr ""
msgid "Hide archived projects"
msgstr ""
msgid "Hide file browser"
msgstr ""
msgid "Hide group projects"
msgstr ""
msgid "Hide host keys manual input"
msgstr ""
@ -4768,6 +4780,9 @@ msgstr ""
msgid "Hide payload"
msgstr ""
msgid "Hide shared projects"
msgstr ""
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] ""
@ -6527,6 +6542,12 @@ msgstr ""
msgid "Overview"
msgstr ""
msgid "Owned by anyone"
msgstr ""
msgid "Owned by me"
msgstr ""
msgid "Owner"
msgstr ""
@ -8229,6 +8250,9 @@ msgstr ""
msgid "Search projects"
msgstr ""
msgid "Search projects..."
msgstr ""
msgid "Search users"
msgstr ""
@ -8526,6 +8550,12 @@ msgstr ""
msgid "Show all activity"
msgstr ""
msgid "Show archived projects"
msgstr ""
msgid "Show archived projects only"
msgstr ""
msgid "Show command"
msgstr ""
@ -8792,6 +8822,12 @@ msgstr ""
msgid "SortOptions|Recent sign in"
msgstr ""
msgid "SortOptions|Sort direction"
msgstr ""
msgid "SortOptions|Stars"
msgstr ""
msgid "SortOptions|Start later"
msgstr ""
@ -10550,6 +10586,9 @@ msgstr ""
msgid "Viewing commit"
msgstr ""
msgid "Visibility"
msgstr ""
msgid "Visibility and access controls"
msgstr ""

View file

@ -112,6 +112,14 @@ describe 'Dashboard Projects' do
expect(first('.project-row')).to have_content(project_with_most_stars.title)
end
it 'shows tabs to filter by all projects or personal' do
visit dashboard_projects_path
segmented_button = page.find('.filtered-search-nav .button-filter-group')
expect(segmented_button).to have_content 'All'
expect(segmented_button).to have_content 'Personal'
end
end
context 'when on Starred projects tab', :js do
@ -134,6 +142,12 @@ describe 'Dashboard Projects' do
expect(find('.nav-links li:nth-child(1) .badge-pill')).to have_content(1)
expect(find('.nav-links li:nth-child(2) .badge-pill')).to have_content(1)
end
it 'does not show tabs to filter by all projects or personal' do
visit(starred_dashboard_projects_path)
expect(page).not_to have_content '.filtered-search-nav'
end
end
describe 'with a pipeline', :clean_gitlab_redis_shared_state do

View file

@ -14,6 +14,7 @@ describe 'Dashboard > User filters projects' do
describe 'filtering personal projects' do
before do
stub_feature_flags(project_list_filter_bar: false)
project2.add_developer(user)
visit dashboard_projects_path
@ -30,6 +31,7 @@ describe 'Dashboard > User filters projects' do
describe 'filtering starred projects', :js do
before do
stub_feature_flags(project_list_filter_bar: false)
user.toggle_star(project)
visit dashboard_projects_path
@ -42,4 +44,219 @@ describe 'Dashboard > User filters projects' do
expect(page).not_to have_content('You don\'t have starred projects yet')
end
end
describe 'without search bar', :js do
before do
stub_feature_flags(project_list_filter_bar: false)
project2.add_developer(user)
visit dashboard_projects_path
end
it 'autocompletes searches upon typing', :js do
expect(page).to have_content 'Victorialand'
expect(page).to have_content 'Treasure'
fill_in 'project-filter-form-field', with: 'Lord beerus\n'
expect(page).not_to have_content 'Victorialand'
expect(page).not_to have_content 'Treasure'
end
end
describe 'with search bar', :js do
before do
stub_feature_flags(project_list_filter_bar: true)
project2.add_developer(user)
visit dashboard_projects_path
end
# TODO: move these helpers somewhere more useful
def click_sort_direction
page.find('.filtered-search-block #filtered-search-sorting-dropdown .reverse-sort-btn').click
end
def select_dropdown_option(selector, label)
dropdown = page.find(selector)
dropdown.click
dropdown.find('.dropdown-menu a', text: label, match: :first).click
end
def expect_to_see_projects(sorted_projects)
list = page.all('.projects-list .project-name').map(&:text)
expect(list).to match(sorted_projects)
end
describe 'Search' do
it 'executes when the search button is clicked' do
expect(page).to have_content 'Victorialand'
expect(page).to have_content 'Treasure'
fill_in 'project-filter-form-field', with: 'Lord vegeta\n'
find('.filtered-search .btn').click
expect(page).not_to have_content 'Victorialand'
expect(page).not_to have_content 'Treasure'
end
it 'will execute when i press enter' do
expect(page).to have_content 'Victorialand'
expect(page).to have_content 'Treasure'
fill_in 'project-filter-form-field', with: 'Lord frieza\n'
find('#project-filter-form-field').native.send_keys :enter
expect(page).not_to have_content 'Victorialand'
expect(page).not_to have_content 'Treasure'
end
end
describe 'Filter' do
before do
private_project = create(:project, :private, name: 'Private project', namespace: user.namespace)
internal_project = create(:project, :internal, name: 'Internal project', namespace: user.namespace)
private_project.add_maintainer(user)
internal_project.add_maintainer(user)
end
it 'filters private projects only' do
select_dropdown_option '#filtered-search-visibility-dropdown', 'Private'
expect(current_url).to match(/visibility_level=0/)
list = page.all('.projects-list .project-name').map(&:text)
expect(list).to match(["Private project", "Treasure", "Victorialand"])
end
it 'filters internal projects only' do
select_dropdown_option '#filtered-search-visibility-dropdown', 'Internal'
expect(current_url).to match(/visibility_level=10/)
list = page.all('.projects-list .project-name').map(&:text)
expect(list).to match(['Internal project'])
end
it 'filters any project' do
select_dropdown_option '#filtered-search-visibility-dropdown', 'Any'
list = page.all('.projects-list .project-name').map(&:text)
expect(list).to match(["Internal project", "Private project", "Treasure", "Victorialand"])
end
end
describe 'Sorting' do
before do
[
{ name: 'Red ribbon army', created_at: 2.days.ago },
{ name: 'Cell saga', created_at: Time.now },
{ name: 'Frieza saga', created_at: 10.days.ago }
].each do |item|
project = create(:project, name: item[:name], namespace: user.namespace, created_at: item[:created_at])
project.add_developer(user)
end
user.toggle_star(project)
user.toggle_star(project2)
user2.toggle_star(project2)
end
it 'includes sorting direction' do
sorting_dropdown = page.find('.filtered-search-block #filtered-search-sorting-dropdown')
expect(sorting_dropdown).to have_css '.reverse-sort-btn'
end
it 'has all sorting options', :js do
sorting_dropdown = page.find('.filtered-search-block #filtered-search-sorting-dropdown')
sorting_option_labels = ['Last updated', 'Created date', 'Name', 'Stars']
sorting_dropdown.click
sorting_option_labels.each do |label|
expect(sorting_dropdown).to have_content(label)
end
end
it 'defaults to "Last updated"', :js do
page.find('.filtered-search-block #filtered-search-sorting-dropdown').click
active_sorting_option = page.first('.filtered-search-block #filtered-search-sorting-dropdown .is-active')
expect(active_sorting_option).to have_content 'Last updated'
end
context 'Sorting by name' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Name'
desc = ['Victorialand', 'Treasure', 'Red ribbon army', 'Frieza saga', 'Cell saga']
asc = ['Cell saga', 'Frieza saga', 'Red ribbon army', 'Treasure', 'Victorialand']
click_sort_direction
expect_to_see_projects(desc)
click_sort_direction
expect_to_see_projects(asc)
end
end
context 'Sorting by Last updated' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Last updated'
desc = ["Frieza saga", "Red ribbon army", "Victorialand", "Treasure", "Cell saga"]
asc = ["Cell saga", "Treasure", "Victorialand", "Red ribbon army", "Frieza saga"]
click_sort_direction
expect_to_see_projects(desc)
click_sort_direction
expect_to_see_projects(asc)
end
end
context 'Sorting by Created date' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Created date'
desc = ["Frieza saga", "Red ribbon army", "Victorialand", "Treasure", "Cell saga"]
asc = ["Cell saga", "Treasure", "Victorialand", "Red ribbon army", "Frieza saga"]
click_sort_direction
expect_to_see_projects(desc)
click_sort_direction
expect_to_see_projects(asc)
end
end
context 'Sorting by Stars' do
it 'sorts the project list' do
select_dropdown_option '#filtered-search-sorting-dropdown', 'Stars'
desc = ["Red ribbon army", "Cell saga", "Frieza saga", "Victorialand", "Treasure"]
asc = ["Treasure", "Victorialand", "Red ribbon army", "Cell saga", "Frieza saga"]
click_sort_direction
expect_to_see_projects(desc)
click_sort_direction
expect_to_see_projects(asc)
end
end
end
end
end

View file

@ -445,6 +445,10 @@ describe ProjectsHelper do
Project.all
end
before do
stub_feature_flags(project_list_filter_bar: false)
end
it 'returns true when there are projects' do
expect(helper.show_projects?(projects, {})).to eq(true)
end