From ed1f110499d8e2aa011e17f1ad8a038e209842fb Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 1 Mar 2017 16:17:26 +0000 Subject: Fixes filtering by name reseting archive filter Previously the search form just wasn't including any params that had previously been set, so when you filtered by name, it would reset all the params & therefore ignoring the archived param Closes #28007 --- app/controllers/concerns/filter_projects.rb | 2 +- app/views/admin/projects/index.html.haml | 11 ++--------- app/views/dashboard/_projects_head.html.haml | 5 +++-- app/views/groups/show.html.haml | 5 +++-- app/views/shared/projects/_dropdown.html.haml | 17 +++++++++-------- app/views/shared/projects/_filter_fields.html.haml | 11 +++++++++++ .../unreleased/dashboard-filter-search-keep-params.yml | 4 ++++ spec/features/dashboard/archived_projects_spec.rb | 15 +++++++++++++++ 8 files changed, 48 insertions(+), 22 deletions(-) create mode 100644 app/views/shared/projects/_filter_fields.html.haml create mode 100644 changelogs/unreleased/dashboard-filter-search-keep-params.yml diff --git a/app/controllers/concerns/filter_projects.rb b/app/controllers/concerns/filter_projects.rb index 586f97c5eb4..6014112256a 100644 --- a/app/controllers/concerns/filter_projects.rb +++ b/app/controllers/concerns/filter_projects.rb @@ -8,7 +8,7 @@ module FilterProjects extend ActiveSupport::Concern def filter_projects(projects) - projects = projects.search(params[:filter_projects]) if params[:filter_projects].present? + projects = projects.search(params[:name]) if params[:name].present? projects = projects.non_archived if params[:archived].blank? projects = projects.personal(current_user) if params[:personal].present? && current_user diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index c35945c5a35..121662a2ea6 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -8,6 +8,7 @@ .top-area .prepend-top-default = form_tag admin_projects_path, method: :get do |f| + = render "shared/projects/filter_fields" .search-holder .search-field-holder = search_field_tag :name, params[:name], class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false, placeholder: 'Search by name' @@ -15,20 +16,12 @@ - if params[:visibility_level].present? = hidden_field_tag 'visibility_level', params[:visibility_level] - - if params[:sort].present? - = hidden_field_tag 'sort', params[:sort] - - - if params[:personal].present? - = hidden_field_tag 'visibility_level', 'true' - - - if params[:archived].present? - = hidden_field_tag 'archived', 'true' - = icon("search", class: "search-icon") .dropdown - toggle_text = 'Namespace' - if params[:namespace_id].present? + = hidden_field_tag :namespace_id, params[:namespace_id] - namespace = Namespace.find(params[:namespace_id]) - toggle_text = "#{namespace.kind}: #{namespace.full_path}" = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' }) diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 48b0fd504f4..795ad9f33c1 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -13,8 +13,9 @@ Explore projects .nav-controls - = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| - = search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2" + = form_tag request.fullpath, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| + = render "shared/projects/filter_fields" + = search_field_tag :name, params[:name], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2" = render 'shared/projects/dropdown' - if current_user.can_create_project? = link_to new_project_path, class: 'btn btn-new' do diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 8f0f2708194..5a7b0c1534b 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -11,8 +11,9 @@ .top-area = render 'groups/show_nav' .nav-controls - = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false + = form_tag request.fullpath, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| + = render "shared/projects/filter_fields" + = search_field_tag :name, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false = render 'shared/projects/dropdown' - if can? current_user, :create_projects, @group = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index c19697802ce..7e5c00e5608 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -1,4 +1,5 @@ - @sort ||= sort_value_recently_updated +- name = params[:name] - personal = params[:personal] - archived = params[:archived] - shared = params[:shared] @@ -11,32 +12,32 @@ Sort by - projects_sort_options_hash.each do |value, title| %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: value, archived: archived, personal: personal), class: ("is-active" if @sort == value) do + = link_to filter_projects_path(namespace_id: namespace_id, sort: value, archived: archived, personal: personal, name: name), class: ("is-active" if @sort == value) do = title %li.divider %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: nil), class: ("is-active" unless params[:archived].present?) do + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: nil, name: name), class: ("is-active" unless params[:archived].present?) do Hide archived projects %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: true), class: ("is-active" if params[:archived].present?) do + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: true, name: name), class: ("is-active" if params[:archived].present?) do Show archived projects - if current_user %li.divider %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: nil), class: ("is-active" unless personal.present?) do + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: nil, name: name), class: ("is-active" unless personal.present?) do Owned by anyone %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: true), class: ("is-active" if personal.present?) do + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: true, name: name), class: ("is-active" if personal.present?) do Owned by me - if @group && @group.shared_projects.present? %li.divider %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: nil), class: ("is-active" unless shared.present?) do + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: nil, name: name), class: ("is-active" unless shared.present?) do All projects %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 0), class: ("is-active" if shared == '0') do + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 0, name: name), class: ("is-active" if shared == '0') do Hide shared projects %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 1), class: ("is-active" if shared == '1') do + = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 1, name: name), class: ("is-active" if shared == '1') do Hide group projects diff --git a/app/views/shared/projects/_filter_fields.html.haml b/app/views/shared/projects/_filter_fields.html.haml new file mode 100644 index 00000000000..26362ad1eb7 --- /dev/null +++ b/app/views/shared/projects/_filter_fields.html.haml @@ -0,0 +1,11 @@ +- if params[:sort].present? + = hidden_field_tag :sort, params[:sort] + +- if params[:personal].present? + = hidden_field_tag :personal, params[:personal] + +- if params[:archived].present? + = hidden_field_tag :archived, params[:archived] + +- if params[:visibility_level].present? + = hidden_field_tag :visibility_level, params[:visibility_level] diff --git a/changelogs/unreleased/dashboard-filter-search-keep-params.yml b/changelogs/unreleased/dashboard-filter-search-keep-params.yml new file mode 100644 index 00000000000..a140715b7a2 --- /dev/null +++ b/changelogs/unreleased/dashboard-filter-search-keep-params.yml @@ -0,0 +1,4 @@ +--- +title: Dashboard project search keeps selected sort & filters +merge_request: +author: diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb index 038c1641be9..f33bcbb5318 100644 --- a/spec/features/dashboard/archived_projects_spec.rb +++ b/spec/features/dashboard/archived_projects_spec.rb @@ -25,4 +25,19 @@ RSpec.describe 'Dashboard Archived Project', feature: true do expect(page).to have_link(project.name) expect(page).to have_link(archived_project.name) end + + it 'searchs archived projects', :js do + click_button 'Last updated' + click_link 'Show archived projects' + + expect(page).to have_link(project.name) + expect(page).to have_link(archived_project.name) + + fill_in 'project-filter-form-field', with: archived_project.name + + find('#project-filter-form-field').native.send_keys :return + + expect(page).not_to have_link(project.name) + expect(page).to have_link(archived_project.name) + end end -- cgit v1.2.1 From 14873c837b1baefe90df8cf38e117c8336f66040 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 1 Mar 2017 16:54:13 +0000 Subject: Fixed incorrect empty state showing --- app/views/dashboard/projects/index.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index f0adbad8412..4b095f52643 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -6,13 +6,13 @@ .user-callout{ 'callout-svg' => custom_icon('icon_customization') } -- if @projects.any? || params[:filter_projects] +- if @projects.any? || params[:name] = render 'dashboard/projects_head' - if @last_push = render "events/event_last_push", event: @last_push -- if @projects.any? || params[:filter_projects] +- if @projects.any? || params[:name] = render 'projects' - else = render "zero_authorized_projects" -- cgit v1.2.1 From 8fd5aeee7fee7506c44674d61f794ff8685b90e6 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 1 Mar 2017 16:57:11 +0000 Subject: Fixed filter_projects param still used in group controller --- app/controllers/groups_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 7ed54479599..15db5b7762d 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -108,7 +108,7 @@ class GroupsController < Groups::ApplicationController @projects = @projects.sorted_by_activity @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.page(params[:page]) if params[:filter_projects].blank? + @projects = @projects.page(params[:page]) if params[:name].blank? end def authorize_create_group! -- cgit v1.2.1 From 5bb6a85b902c6096970d6c82bb63cae7985e55e8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Fri, 3 Mar 2017 12:13:03 +0200 Subject: Refactor projects filtering by name Reuse same search form and behavior for dashboard#projects, group#projects and admin#projects. Repsect all other options like sorting, personal filter when search projects by name. Create FilterableList JS class to handle identical behaviour of projects and groups lists. This change also makes filtering and sorting availabe on explore#projects and explore#groups no matter if you are logged in or not. Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/dispatcher.js.es6 | 13 +++- app/assets/javascripts/filterable_list.js | 47 +++++++++++++ app/assets/javascripts/groups_list.js | 44 ++---------- app/assets/javascripts/projects_list.js | 63 ++++------------- app/assets/stylesheets/pages/search.scss | 6 +- app/controllers/admin/projects_controller.rb | 9 +++ app/helpers/explore_helper.rb | 7 +- app/views/admin/projects/_projects.html.haml | 32 +++++++++ app/views/admin/projects/index.html.haml | 78 ++++++---------------- app/views/dashboard/_groups_head.html.haml | 3 +- app/views/dashboard/_projects_head.html.haml | 4 +- app/views/dashboard/projects/index.html.haml | 1 - app/views/explore/groups/_nav.html.haml | 8 +++ app/views/explore/groups/index.html.haml | 2 +- app/views/explore/projects/_nav.html.haml | 27 +++++--- app/views/explore/projects/index.html.haml | 7 +- app/views/groups/show.html.haml | 4 +- app/views/shared/groups/_search_form.html.haml | 2 + app/views/shared/projects/_dropdown.html.haml | 21 +++--- app/views/shared/projects/_filter_fields.html.haml | 11 --- app/views/shared/projects/_list.html.haml | 5 +- app/views/shared/projects/_search_form.html.haml | 23 +++++++ features/steps/project/fork.rb | 2 +- 23 files changed, 212 insertions(+), 207 deletions(-) create mode 100644 app/assets/javascripts/filterable_list.js create mode 100644 app/views/admin/projects/_projects.html.haml create mode 100644 app/views/explore/groups/_nav.html.haml create mode 100644 app/views/shared/groups/_search_form.html.haml delete mode 100644 app/views/shared/projects/_filter_fields.html.haml create mode 100644 app/views/shared/projects/_search_form.html.haml diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6 index fc25122aedc..ef5785b5532 100644 --- a/app/assets/javascripts/dispatcher.js.es6 +++ b/app/assets/javascripts/dispatcher.js.es6 @@ -36,6 +36,7 @@ /* global Shortcuts */ import GroupsList from './groups_list'; +import ProjectsList from './projects_list'; const ShortcutsBlob = require('./shortcuts_blob'); const UserCallout = require('./user_callout'); @@ -98,6 +99,14 @@ const UserCallout = require('./user_callout'); case 'dashboard:todos:index': new gl.Todos(); break; + case 'dashboard:projects:index': + case 'dashboard:projects:starred': + case 'explore:projects:index': + case 'explore:projects:trending': + case 'explore:projects:starred': + case 'admin:projects:index': + new ProjectsList(); + break; case 'dashboard:groups:index': case 'explore:groups:index': new GroupsList(); @@ -163,9 +172,6 @@ const UserCallout = require('./user_callout'); case 'dashboard:activity': new gl.Activities(); break; - case 'dashboard:projects:starred': - new gl.Activities(); - break; case 'projects:commit:show': new Commit(); new gl.Diff(); @@ -208,6 +214,7 @@ const UserCallout = require('./user_callout'); shortcut_handler = new ShortcutsNavigation(); new NotificationsForm(); new NotificationsDropdown(); + new ProjectsList(); break; case 'groups:group_members:index': new gl.MemberExpirationDate(); diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js new file mode 100644 index 00000000000..f498c3ea973 --- /dev/null +++ b/app/assets/javascripts/filterable_list.js @@ -0,0 +1,47 @@ +/** + * Makes search request for content when user types a value in the search input. + * Updates the html content of the page with the received one. + */ +export default class FilterableList { + constructor(form, filter, holder) { + this.filterForm = form; + this.listFilterElement = filter; + this.listHolderElement = holder; + + this.initSearch(); + } + + initSearch() { + this.debounceFilter = _.debounce(this.filterResults.bind(this), 500); + + this.listFilterElement.removeEventListener('input', this.debounceFilter); + this.listFilterElement.addEventListener('input', this.debounceFilter); + } + + filterResults() { + const form = this.filterForm; + const filterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`; + + $(this.listHolderElement).fadeTo(250, 0.5); + + return $.ajax({ + url: form.getAttribute('action'), + data: $(form).serialize(), + type: 'GET', + dataType: 'json', + context: this, + complete() { + $(this.listHolderElement).fadeTo(250, 1); + }, + success(data) { + this.listHolderElement.innerHTML = data.html; + + // Change url so if user reload a page - search results are saved + return window.history.replaceState({ + page: filterUrl, + + }, document.title, filterUrl); + }, + }); + } +} diff --git a/app/assets/javascripts/groups_list.js b/app/assets/javascripts/groups_list.js index 0ef81e49444..49b29affaa5 100644 --- a/app/assets/javascripts/groups_list.js +++ b/app/assets/javascripts/groups_list.js @@ -1,47 +1,15 @@ +import FilterableList from './filterable_list'; + /** - * Based on project list search. * Makes search request for groups when user types a value in the search input. * Updates the html content of the page with the received one. */ export default class GroupsList { constructor() { - this.groupsListFilterElement = document.querySelector('.js-groups-list-filter'); - this.groupsListHolderElement = document.querySelector('.js-groups-list-holder'); - - this.initSearch(); - } - - initSearch() { - this.debounceFilter = _.debounce(this.filterResults.bind(this), 500); - - this.groupsListFilterElement.removeEventListener('input', this.debounceFilter); - this.groupsListFilterElement.addEventListener('input', this.debounceFilter); - } - - filterResults() { - const form = document.querySelector('form#group-filter-form'); - const groupFilterUrl = `${form.getAttribute('action')}?${$(form).serialize()}`; - - $(this.groupsListHolderElement).fadeTo(250, 0.5); - - return $.ajax({ - url: form.getAttribute('action'), - data: $(form).serialize(), - type: 'GET', - dataType: 'json', - context: this, - complete() { - $(this.groupsListHolderElement).fadeTo(250, 1); - }, - success(data) { - this.groupsListHolderElement.innerHTML = data.html; - - // Change url so if user reload a page - search results are saved - return window.history.replaceState({ - page: groupFilterUrl, + var form = document.querySelector('form#group-filter-form'); + var filter = document.querySelector('.js-groups-list-filter'); + var holder = document.querySelector('.js-groups-list-holder'); - }, document.title, groupFilterUrl); - }, - }); + new FilterableList(form, filter, holder); } } diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js index acdf9b7eb5a..383c2815457 100644 --- a/app/assets/javascripts/projects_list.js +++ b/app/assets/javascripts/projects_list.js @@ -1,50 +1,15 @@ -/* eslint-disable func-names, space-before-function-paren, object-shorthand, quotes, no-var, one-var, one-var-declaration-per-line, prefer-arrow-callback, consistent-return, no-unused-vars, camelcase, prefer-template, comma-dangle, max-len */ +import FilterableList from './filterable_list'; -(function() { - window.ProjectsList = { - init: function() { - $(".projects-list-filter").off('keyup'); - this.initSearch(); - return this.initPagination(); - }, - initSearch: function() { - var debounceFilter, projectsListFilter; - projectsListFilter = $('.projects-list-filter'); - debounceFilter = _.debounce(window.ProjectsList.filterResults, 500); - return projectsListFilter.on('keyup', function(e) { - if (projectsListFilter.val() !== '') { - return debounceFilter(); - } - }); - }, - filterResults: function() { - var form, project_filter_url, search; - $('.projects-list-holder').fadeTo(250, 0.5); - form = null; - form = $("form#project-filter-form"); - search = $(".projects-list-filter").val(); - project_filter_url = form.attr('action') + '?' + form.serialize(); - return $.ajax({ - type: "GET", - url: form.attr('action'), - data: form.serialize(), - complete: function() { - return $('.projects-list-holder').fadeTo(250, 1); - }, - success: function(data) { - $('.projects-list-holder').replaceWith(data.html); - return history.replaceState({ - page: project_filter_url - // Change url so if user reload a page - search results are saved - }, document.title, project_filter_url); - }, - dataType: "json" - }); - }, - initPagination: function() { - return $('.projects-list-holder .pagination').on('ajax:success', function(e, data) { - return $('.projects-list-holder').replaceWith(data.html); - }); - } - }; -}).call(window); +/** + * Makes search request for projects when user types a value in the search input. + * Updates the html content of the page with the received one. + */ +export default class ProjectsList { + constructor() { + var form = document.querySelector('form#project-filter-form'); + var filter = document.querySelector('.js-projects-list-filter'); + var holder = document.querySelector('.js-projects-list-holder'); + + new FilterableList(form, filter, holder); + } +} diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 88ea92c5afb..543d2ece3df 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -182,7 +182,8 @@ input[type="checkbox"]:hover { display: flex; } - .search-field-holder { + .search-field-holder, + .project-filter-form { -webkit-flex: 1 0 auto; flex: 1 0 auto; position: relative; @@ -201,7 +202,8 @@ input[type="checkbox"]:hover { pointer-events: none; } - .search-text-input { + .search-text-input, + .project-filter-form-field { padding-left: $gl-padding + 15px; padding-right: $gl-padding + 15px; } diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 39c8c6d8a0c..daecfc832bf 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -14,6 +14,15 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]) + + respond_to do |format| + format.html + format.json do + render json: { + html: view_to_html_string("admin/projects/_projects", locals: { projects: @projects }) + } + end + end end def show diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb index bbcb52f7eaf..cb9bb33f492 100644 --- a/app/helpers/explore_helper.rb +++ b/app/helpers/explore_helper.rb @@ -1,11 +1,16 @@ module ExploreHelper def filter_projects_path(options = {}) exist_opts = { - sort: params[:sort], + sort: params[:sort] || @sort, scope: params[:scope], group: params[:group], tag: params[:tag], visibility_level: params[:visibility_level], + name: params[:name], + personal: params[:personal], + archived: params[:archived], + shared: params[:shared], + namespace_id: params[:namespace_id], } options = exist_opts.merge(options) diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml new file mode 100644 index 00000000000..c1a9f8d6ddd --- /dev/null +++ b/app/views/admin/projects/_projects.html.haml @@ -0,0 +1,32 @@ +.js-projects-list-holder + - if @projects.any? + %ul.projects-list.content-list + - @projects.each_with_index do |project| + %li.project-row + .controls + - if project.archived + %span.label.label-warning archived + %span.badge + = storage_counter(project.statistics.storage_size) + = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn" + = link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove" + .title + = link_to [:admin, project.namespace.becomes(Namespace), project] do + .dash-project-avatar + .avatar-container.s40 + = project_icon(project, alt: '', class: 'avatar project-avatar s40') + %span.project-full-name + %span.namespace-name + - if project.namespace + = project.namespace.human_name + \/ + %span.project-name.filter-title + = project.name + + - if project.description.present? + .description + = markdown_field(project, :description) + + = paginate @projects, theme: 'gitlab' + - else + .nothing-here-block No projects found diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index 121662a2ea6..3301f55b8a8 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -7,33 +7,24 @@ %div{ class: container_class } .top-area .prepend-top-default - = form_tag admin_projects_path, method: :get do |f| - = render "shared/projects/filter_fields" - .search-holder - .search-field-holder - = search_field_tag :name, params[:name], class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false, placeholder: 'Search by name' - - - if params[:visibility_level].present? - = hidden_field_tag 'visibility_level', params[:visibility_level] - - = icon("search", class: "search-icon") - - .dropdown - - toggle_text = 'Namespace' - - if params[:namespace_id].present? - = hidden_field_tag :namespace_id, params[:namespace_id] - - namespace = Namespace.find(params[:namespace_id]) - - toggle_text = "#{namespace.kind}: #{namespace.full_path}" - = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' }) - .dropdown-menu.dropdown-select.dropdown-menu-align-right - = dropdown_title('Namespaces') - = dropdown_filter("Search for Namespace") - = dropdown_content - = dropdown_loading - = render 'shared/projects/dropdown' - = link_to new_project_path, class: 'btn btn-new' do - New Project - = button_tag "Search", class: "btn btn-primary btn-search hide" + .search-holder + = render 'shared/projects/search_form', autofocus: true, icon: true + .dropdown + - toggle_text = 'Namespace' + - if params[:namespace_id].present? + = hidden_field_tag :namespace_id, params[:namespace_id] + - namespace = Namespace.find(params[:namespace_id]) + - toggle_text = "#{namespace.kind}: #{namespace.full_path}" + = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { toggle_class: 'js-namespace-select large' }) + .dropdown-menu.dropdown-select.dropdown-menu-align-right + = dropdown_title('Namespaces') + = dropdown_filter("Search for Namespace") + = dropdown_content + = dropdown_loading + = render 'shared/projects/dropdown' + = link_to new_project_path, class: 'btn btn-new' do + New Project + = button_tag "Search", class: "btn btn-primary btn-search hide" %ul.nav-links - opts = params[:visibility_level].present? ? {} : { page: admin_projects_path } @@ -51,35 +42,4 @@ = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do Public - .projects-list-holder - - if @projects.any? - %ul.projects-list.content-list - - @projects.each_with_index do |project| - %li.project-row - .controls - - if project.archived - %span.label.label-warning archived - %span.badge - = storage_counter(project.statistics.storage_size) - = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn" - = link_to 'Delete', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-remove" - .title - = link_to [:admin, project.namespace.becomes(Namespace), project] do - .dash-project-avatar - .avatar-container.s40 - = project_icon(project, alt: '', class: 'avatar project-avatar s40') - %span.project-full-name - %span.namespace-name - - if project.namespace - = project.namespace.human_name - \/ - %span.project-name.filter-title - = project.name - - - if project.description.present? - .description - = markdown_field(project, :description) - - = paginate @projects, theme: 'gitlab' - - else - .nothing-here-block No projects found + = render 'projects' diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml index c6d5937a3c3..13eaba41f4c 100644 --- a/app/views/dashboard/_groups_head.html.haml +++ b/app/views/dashboard/_groups_head.html.haml @@ -7,8 +7,7 @@ = link_to explore_groups_path, title: 'Explore groups' do Explore Groups .nav-controls - = form_tag request.path, method: :get, class: 'group-filter-form', id: 'group-filter-form' do |f| - = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name...', class: 'group-filter-form-field form-control input-short js-groups-list-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2" + = render 'shared/groups/search_form' = render 'shared/groups/dropdown' - if current_user.can_create_group? = link_to new_group_path, class: "btn btn-new" do diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml index 795ad9f33c1..600ee63a5c0 100644 --- a/app/views/dashboard/_projects_head.html.haml +++ b/app/views/dashboard/_projects_head.html.haml @@ -13,9 +13,7 @@ Explore projects .nav-controls - = form_tag request.fullpath, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| - = render "shared/projects/filter_fields" - = search_field_tag :name, params[:name], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2" + = render 'shared/projects/search_form' = render 'shared/projects/dropdown' - if current_user.can_create_project? = link_to new_project_path, class: 'btn btn-new' do diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml index 4b095f52643..eef794dbd51 100644 --- a/app/views/dashboard/projects/index.html.haml +++ b/app/views/dashboard/projects/index.html.haml @@ -5,7 +5,6 @@ - header_title "Projects", dashboard_projects_path .user-callout{ 'callout-svg' => custom_icon('icon_customization') } - - if @projects.any? || params[:name] = render 'dashboard/projects_head' diff --git a/app/views/explore/groups/_nav.html.haml b/app/views/explore/groups/_nav.html.haml new file mode 100644 index 00000000000..c8d95b52156 --- /dev/null +++ b/app/views/explore/groups/_nav.html.haml @@ -0,0 +1,8 @@ +.top-area + %ul.nav-links + = nav_link(page: explore_groups_path) do + = link_to explore_groups_path do + Explore Groups + .nav-controls + = render 'shared/groups/search_form' + = render 'shared/groups/dropdown' diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml index 7f1bacc91cb..8374f5a009f 100644 --- a/app/views/explore/groups/index.html.haml +++ b/app/views/explore/groups/index.html.haml @@ -5,7 +5,7 @@ = render 'dashboard/groups_head' - else = render 'explore/head' - + = render 'nav' - if @groups.present? = render 'groups' diff --git a/app/views/explore/projects/_nav.html.haml b/app/views/explore/projects/_nav.html.haml index 614b5431779..e0a2a1e9c96 100644 --- a/app/views/explore/projects/_nav.html.haml +++ b/app/views/explore/projects/_nav.html.haml @@ -1,10 +1,17 @@ -%ul.nav-links - = nav_link(page: [trending_explore_projects_path, explore_root_path]) do - = link_to trending_explore_projects_path do - Trending - = nav_link(page: starred_explore_projects_path) do - = link_to starred_explore_projects_path do - Most stars - = nav_link(page: explore_projects_path) do - = link_to explore_projects_path do - All +.top-area + %ul.nav-links + = nav_link(page: [trending_explore_projects_path, explore_root_path]) do + = link_to trending_explore_projects_path do + Trending + = nav_link(page: starred_explore_projects_path) do + = link_to starred_explore_projects_path do + Most stars + = nav_link(page: explore_projects_path) do + = link_to explore_projects_path do + All + + .nav-controls + - unless current_user + = render 'shared/projects/search_form' + = render 'shared/projects/dropdown' + = render 'filter' diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml index 42b50481b9d..ec461755103 100644 --- a/app/views/explore/projects/index.html.haml +++ b/app/views/explore/projects/index.html.haml @@ -6,10 +6,5 @@ - else = render 'explore/head' -.top-area - = render 'explore/projects/nav' - - .nav-controls - = render 'filter' - += render 'explore/projects/nav' = render 'projects', projects: @projects diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 5a7b0c1534b..18997baa998 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -11,9 +11,7 @@ .top-area = render 'groups/show_nav' .nav-controls - = form_tag request.fullpath, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f| - = render "shared/projects/filter_fields" - = search_field_tag :name, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false + = render 'shared/projects/search_form' = render 'shared/projects/dropdown' - if can? current_user, :create_projects, @group = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-new pull-right' do diff --git a/app/views/shared/groups/_search_form.html.haml b/app/views/shared/groups/_search_form.html.haml new file mode 100644 index 00000000000..ad7a7faedf1 --- /dev/null +++ b/app/views/shared/groups/_search_form.html.haml @@ -0,0 +1,2 @@ += form_tag request.path, method: :get, class: 'group-filter-form', id: 'group-filter-form' do |f| + = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name...', class: 'group-filter-form-field form-control input-short js-groups-list-filter', spellcheck: false, id: 'group-filter-form-field', tabindex: "2" diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml index 7e5c00e5608..2d25b8aad62 100644 --- a/app/views/shared/projects/_dropdown.html.haml +++ b/app/views/shared/projects/_dropdown.html.haml @@ -1,9 +1,4 @@ - @sort ||= sort_value_recently_updated -- name = params[:name] -- personal = params[:personal] -- archived = params[:archived] -- shared = params[:shared] -- namespace_id = params[:namespace_id] .dropdown - toggle_text = projects_sort_options_hash[@sort] = dropdown_toggle(toggle_text, { toggle: 'dropdown' }, { id: 'sort-projects-dropdown' }) @@ -12,32 +7,32 @@ Sort by - projects_sort_options_hash.each do |value, title| %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: value, archived: archived, personal: personal, name: name), class: ("is-active" if @sort == value) do + = link_to filter_projects_path(sort: value), class: ("is-active" if @sort == value) do = title %li.divider %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: nil, name: name), class: ("is-active" unless params[:archived].present?) do + = link_to filter_projects_path(archived: nil), class: ("is-active" unless params[:archived].present?) do Hide archived projects %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, archived: true, name: name), class: ("is-active" if params[:archived].present?) do + = link_to filter_projects_path(archived: true), class: ("is-active" if params[:archived].present?) do Show archived projects - if current_user %li.divider %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: nil, name: name), class: ("is-active" unless personal.present?) do + = link_to filter_projects_path(personal: nil), class: ("is-active" unless params[:personal].present?) do Owned by anyone %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, personal: true, name: name), class: ("is-active" if personal.present?) do + = link_to filter_projects_path(personal: true), class: ("is-active" if params[:personal].present?) do Owned by me - if @group && @group.shared_projects.present? %li.divider %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: nil, name: name), class: ("is-active" unless shared.present?) do + = link_to filter_projects_path(shared: nil), class: ("is-active" unless params[:shared].present?) do All projects %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 0, name: name), class: ("is-active" if shared == '0') do + = link_to filter_projects_path(shared: 0), class: ("is-active" if params[:shared] == '0') do Hide shared projects %li - = link_to filter_projects_path(namespace_id: namespace_id, sort: @sort, shared: 1, name: name), class: ("is-active" if shared == '1') do + = link_to filter_projects_path(shared: 1), class: ("is-active" if params[:shared] == '1') do Hide group projects diff --git a/app/views/shared/projects/_filter_fields.html.haml b/app/views/shared/projects/_filter_fields.html.haml deleted file mode 100644 index 26362ad1eb7..00000000000 --- a/app/views/shared/projects/_filter_fields.html.haml +++ /dev/null @@ -1,11 +0,0 @@ -- if params[:sort].present? - = hidden_field_tag :sort, params[:sort] - -- if params[:personal].present? - = hidden_field_tag :personal, params[:personal] - -- if params[:archived].present? - = hidden_field_tag :archived, params[:archived] - -- if params[:visibility_level].present? - = hidden_field_tag :visibility_level, params[:visibility_level] diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml index 3a9dd37dc7d..c57282c5742 100644 --- a/app/views/shared/projects/_list.html.haml +++ b/app/views/shared/projects/_list.html.haml @@ -8,7 +8,7 @@ - show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true - remote = false unless local_assigns[:remote] == true -.projects-list-holder +.js-projects-list-holder - if projects.any? %ul.projects-list.content-list - projects.each_with_index do |project, i| @@ -25,6 +25,3 @@ = paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages - else .nothing-here-block No projects found - -:javascript - ProjectsList.init(); diff --git a/app/views/shared/projects/_search_form.html.haml b/app/views/shared/projects/_search_form.html.haml new file mode 100644 index 00000000000..b89194bcc67 --- /dev/null +++ b/app/views/shared/projects/_search_form.html.haml @@ -0,0 +1,23 @@ += 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', + spellcheck: false, + id: 'project-filter-form-field', + tabindex: "2", + autofocus: local_assigns[:autofocus] + + - if local_assigns[:icon] + = icon("search", class: "search-icon") + + - if params[:sort].present? + = hidden_field_tag :sort, params[:sort] + + - if params[:personal].present? + = hidden_field_tag :personal, params[:personal] + + - if params[:archived].present? + = hidden_field_tag :archived, params[:archived] + + - if params[:visibility_level].present? + = hidden_field_tag :visibility_level, params[:visibility_level] diff --git a/features/steps/project/fork.rb b/features/steps/project/fork.rb index 9a6c04fba7a..79db9728227 100644 --- a/features/steps/project/fork.rb +++ b/features/steps/project/fork.rb @@ -56,7 +56,7 @@ class Spinach::Features::ProjectFork < Spinach::FeatureSteps end step 'I should see my fork on the list' do - page.within('.projects-list-holder') do + page.within('.js-projects-list-holder') do project = @user.fork_of(@project) expect(page).to have_content("#{project.namespace.human_name} / #{project.name}") end -- cgit v1.2.1 From 1d49d065ed168fcc3653d0d4681485edd524043e Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Fri, 3 Mar 2017 18:03:58 +0000 Subject: Removed empty options from URL --- app/helpers/explore_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb index cb9bb33f492..7bd212a3ef9 100644 --- a/app/helpers/explore_helper.rb +++ b/app/helpers/explore_helper.rb @@ -13,7 +13,7 @@ module ExploreHelper namespace_id: params[:namespace_id], } - options = exist_opts.merge(options) + options = exist_opts.merge(options).delete_if { |key, value| value.blank? } request_path_with_options(options) end -- cgit v1.2.1 From 8a910ba297b229171a64794b8958401431354b4a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 4 Mar 2017 13:04:00 +0200 Subject: Improve projects/groups list js code Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/filterable_list.js | 2 -- app/assets/javascripts/groups_list.js | 11 +++++++---- app/assets/javascripts/projects_list.js | 11 +++++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js index f498c3ea973..47a40e28461 100644 --- a/app/assets/javascripts/filterable_list.js +++ b/app/assets/javascripts/filterable_list.js @@ -7,8 +7,6 @@ export default class FilterableList { this.filterForm = form; this.listFilterElement = filter; this.listHolderElement = holder; - - this.initSearch(); } initSearch() { diff --git a/app/assets/javascripts/groups_list.js b/app/assets/javascripts/groups_list.js index 49b29affaa5..56a8cbf6d03 100644 --- a/app/assets/javascripts/groups_list.js +++ b/app/assets/javascripts/groups_list.js @@ -6,10 +6,13 @@ import FilterableList from './filterable_list'; */ export default class GroupsList { constructor() { - var form = document.querySelector('form#group-filter-form'); - var filter = document.querySelector('.js-groups-list-filter'); - var holder = document.querySelector('.js-groups-list-holder'); + const form = document.querySelector('form#group-filter-form'); + const filter = document.querySelector('.js-groups-list-filter'); + const holder = document.querySelector('.js-groups-list-holder'); - new FilterableList(form, filter, holder); + if (form && filter && holder) { + const list = new FilterableList(form, filter, holder); + list.initSearch(); + } } } diff --git a/app/assets/javascripts/projects_list.js b/app/assets/javascripts/projects_list.js index 383c2815457..c67d59d2be5 100644 --- a/app/assets/javascripts/projects_list.js +++ b/app/assets/javascripts/projects_list.js @@ -6,10 +6,13 @@ import FilterableList from './filterable_list'; */ export default class ProjectsList { constructor() { - var form = document.querySelector('form#project-filter-form'); - var filter = document.querySelector('.js-projects-list-filter'); - var holder = document.querySelector('.js-projects-list-holder'); + const form = document.querySelector('form#project-filter-form'); + const filter = document.querySelector('.js-projects-list-filter'); + const holder = document.querySelector('.js-projects-list-holder'); - new FilterableList(form, filter, holder); + if (form && filter && holder) { + const list = new FilterableList(form, filter, holder); + list.initSearch(); + } } } -- cgit v1.2.1 From c13c1e1b166edd5520057814dc4698450b578c9a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 4 Mar 2017 15:30:12 +0200 Subject: Improve explore projects spinach test Project name "Internal" is too generic and can lead to false positive/negative when there is a visibility filter on the page. So we ensure we check for project inside list holder css class. Signed-off-by: Dmitriy Zaporozhets --- features/steps/shared/project.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb index dae248b8b7e..345a28f27dc 100644 --- a/features/steps/shared/project.rb +++ b/features/steps/shared/project.rb @@ -166,11 +166,15 @@ module SharedProject end step 'I should see project "Internal"' do - expect(page).to have_content "Internal" + page.within '.js-projects-list-holder' do + expect(page).to have_content "Internal" + end end step 'I should not see project "Internal"' do - expect(page).not_to have_content "Internal" + page.within '.js-projects-list-holder' do + expect(page).not_to have_content "Internal" + end end step 'public project "Community"' do -- cgit v1.2.1