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:
commit
e33a8baff5
23 changed files with 575 additions and 51 deletions
|
@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
|
28
app/views/shared/projects/_search_bar.html.haml
Normal file
28
app/views/shared/projects/_search_bar.html.haml
Normal 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'
|
||||
|
|
@ -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",
|
||||
|
|
39
app/views/shared/projects/_sort_dropdown.html.haml
Normal file
39
app/views/shared/projects/_sort_dropdown.html.haml
Normal 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)
|
|
@ -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 ""
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue