summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2017-03-02 13:53:10 +0000
committerDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>2017-03-02 13:53:10 +0000
commitbb062ebdb68e5d4d9a92339948ddaaa68cdcf36c (patch)
tree3953ff8489a4435fc9493f5c84f311ba796aae72
parente9d75a4b856513ce848c0a704cced54cff9f48f8 (diff)
parentf440bfd247fe6e69bc913ec60e3535fda4797031 (diff)
downloadgitlab-ce-bb062ebdb68e5d4d9a92339948ddaaa68cdcf36c.tar.gz
Merge branch 'dz-dashboard-groups-search' into 'master'
Add filter and sorting to dashboard groups page See merge request !9619
-rw-r--r--app/assets/javascripts/dispatcher.js.es66
-rw-r--r--app/assets/javascripts/groups_list.js47
-rw-r--r--app/controllers/dashboard/groups_controller.rb14
-rw-r--r--app/controllers/explore/groups_controller.rb11
-rw-r--r--app/helpers/explore_helper.rb14
-rw-r--r--app/views/dashboard/_groups_head.html.haml7
-rw-r--r--app/views/dashboard/groups/_groups.html.haml6
-rw-r--r--app/views/dashboard/groups/index.html.haml7
-rw-r--r--app/views/explore/groups/_groups.html.haml6
-rw-r--r--app/views/explore/groups/index.html.haml38
-rw-r--r--app/views/shared/groups/_dropdown.html.haml18
-rw-r--r--changelogs/unreleased/dz-dashboard-groups-search.yml4
-rw-r--r--config/webpack.config.js1
-rw-r--r--spec/features/dashboard/groups_list_spec.rb47
-rw-r--r--spec/features/explore/groups_list_spec.rb46
15 files changed, 225 insertions, 47 deletions
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 0f678492d4c..fc25122aedc 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -35,6 +35,8 @@
/* global Labels */
/* global Shortcuts */
+import GroupsList from './groups_list';
+
const ShortcutsBlob = require('./shortcuts_blob');
const UserCallout = require('./user_callout');
@@ -96,6 +98,10 @@ const UserCallout = require('./user_callout');
case 'dashboard:todos:index':
new gl.Todos();
break;
+ case 'dashboard:groups:index':
+ case 'explore:groups:index':
+ new GroupsList();
+ break;
case 'projects:milestones:new':
case 'projects:milestones:edit':
case 'projects:milestones:update':
diff --git a/app/assets/javascripts/groups_list.js b/app/assets/javascripts/groups_list.js
new file mode 100644
index 00000000000..0ef81e49444
--- /dev/null
+++ b/app/assets/javascripts/groups_list.js
@@ -0,0 +1,47 @@
+/**
+ * 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,
+
+ }, document.title, groupFilterUrl);
+ },
+ });
+ }
+}
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index 0b7cf8167f0..d03265e9f20 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,5 +1,17 @@
class Dashboard::GroupsController < Dashboard::ApplicationController
def index
- @group_members = current_user.group_members.includes(source: :route).page(params[:page])
+ @group_members = current_user.group_members.includes(source: :route).joins(:group)
+ @group_members = @group_members.merge(Group.search(params[:filter_groups])) if params[:filter_groups].present?
+ @group_members = @group_members.merge(Group.sort(@sort = params[:sort]))
+ @group_members = @group_members.page(params[:page])
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("dashboard/groups/_groups", locals: { group_members: @group_members })
+ }
+ end
+ end
end
end
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index a962f9a0937..68228c095da 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -1,8 +1,17 @@
class Explore::GroupsController < Explore::ApplicationController
def index
@groups = GroupsFinder.new.execute(current_user)
- @groups = @groups.search(params[:search]) if params[:search].present?
+ @groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present?
@groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page])
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: {
+ html: view_to_html_string("explore/groups/_groups", locals: { groups: @groups })
+ }
+ end
+ end
end
end
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index 2b1f3825adc..bbcb52f7eaf 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -9,12 +9,20 @@ module ExploreHelper
}
options = exist_opts.merge(options)
- path = request.path
- path << "?#{options.to_param}"
- path
+ request_path_with_options(options)
+ end
+
+ def filter_groups_path(options = {})
+ request_path_with_options(options)
end
def explore_controller?
controller.class.name.split("::").first == "Explore"
end
+
+ private
+
+ def request_path_with_options(options = {})
+ request.path + "?#{options.to_param}"
+ end
end
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 23c145ebbb4..c6d5937a3c3 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -6,7 +6,10 @@
= nav_link(page: explore_groups_path) do
= link_to explore_groups_path, title: 'Explore groups' do
Explore Groups
- - if current_user.can_create_group?
- .nav-controls
+ .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/dropdown'
+ - if current_user.can_create_group?
= link_to new_group_path, class: "btn btn-new" do
New Group
diff --git a/app/views/dashboard/groups/_groups.html.haml b/app/views/dashboard/groups/_groups.html.haml
new file mode 100644
index 00000000000..6c3bf1a2b3b
--- /dev/null
+++ b/app/views/dashboard/groups/_groups.html.haml
@@ -0,0 +1,6 @@
+.js-groups-list-holder
+ %ul.content-list
+ - @group_members.each do |group_member|
+ = render 'shared/groups/group', group: group_member.group, group_member: group_member
+
+ = paginate @group_members, theme: 'gitlab'
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 1a679c51774..73ab2c95ff9 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -5,9 +5,4 @@
- if @group_members.empty?
= render 'empty_state'
- else
- %ul.content-list
- - @group_members.each do |group_member|
- - group = group_member.group
- = render 'shared/groups/group', group: group, group_member: group_member
-
- = paginate @group_members, theme: 'gitlab'
+ = render 'groups'
diff --git a/app/views/explore/groups/_groups.html.haml b/app/views/explore/groups/_groups.html.haml
new file mode 100644
index 00000000000..794c6d1d170
--- /dev/null
+++ b/app/views/explore/groups/_groups.html.haml
@@ -0,0 +1,6 @@
+.js-groups-list-holder
+ %ul.content-list
+ - @groups.each do |group|
+ = render 'shared/groups/group', group: group
+
+ = paginate @groups, theme: 'gitlab'
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 73cf6e87eb4..7f1bacc91cb 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -6,40 +6,10 @@
- else
= render 'explore/head'
-.row-content-block.clearfix
- .pull-left
- = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
- = hidden_field_tag :sort, @sort
- .form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search", spellcheck: false
- .form-group
- = button_tag 'Search', class: "btn btn-default"
-
- .pull-right
- .dropdown.inline
- %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
- %span.light
- - if @sort.present?
- = sort_options_hash[@sort]
- - else
- = sort_title_recently_created
- = icon('chevron-down')
- %ul.dropdown-menu.dropdown-menu-align-right
- %li
- = link_to explore_groups_path(sort: sort_value_recently_created) do
- = sort_title_recently_created
- = link_to explore_groups_path(sort: sort_value_oldest_created) do
- = sort_title_oldest_created
- = link_to explore_groups_path(sort: sort_value_recently_updated) do
- = sort_title_recently_updated
- = link_to explore_groups_path(sort: sort_value_oldest_updated) do
- = sort_title_oldest_updated
-
-%ul.content-list
- - @groups.each do |group|
- = render 'shared/groups/group', group: group
- - unless @groups.present?
- .nothing-here-block No public groups
+- if @groups.present?
+ = render 'groups'
+- else
+ .nothing-here-block No public groups
= paginate @groups, theme: "gitlab"
diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml
new file mode 100644
index 00000000000..37589b634fa
--- /dev/null
+++ b/app/views/shared/groups/_dropdown.html.haml
@@ -0,0 +1,18 @@
+.dropdown.inline
+ %button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
+ %span.light
+ - if @sort.present?
+ = sort_options_hash[@sort]
+ - else
+ = sort_title_recently_created
+ = icon('chevron-down')
+ %ul.dropdown-menu.dropdown-menu-align-right
+ %li
+ = link_to filter_groups_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to filter_groups_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to filter_groups_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to filter_groups_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
diff --git a/changelogs/unreleased/dz-dashboard-groups-search.yml b/changelogs/unreleased/dz-dashboard-groups-search.yml
new file mode 100644
index 00000000000..c473cba774d
--- /dev/null
+++ b/changelogs/unreleased/dz-dashboard-groups-search.yml
@@ -0,0 +1,4 @@
+---
+title: Add filter and sorting to dashboard groups page
+merge_request: 9619
+author:
diff --git a/config/webpack.config.js b/config/webpack.config.js
index a71ec0c5f52..e91794208e6 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -28,6 +28,7 @@ var config = {
environments_folder: './environments/folder/environments_folder_bundle.js',
filtered_search: './filtered_search/filtered_search_bundle.js',
graphs: './graphs/graphs_bundle.js',
+ groups_list: './groups_list.js',
issuable: './issuable/issuable_bundle.js',
merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js',
merge_request_widget: './merge_request_widget/ci_bundle.js',
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
new file mode 100644
index 00000000000..ca04107d33a
--- /dev/null
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe 'Dashboard Groups page', js: true, feature: true do
+ include WaitForAjax
+
+ let!(:user) { create :user }
+ let!(:group) { create(:group) }
+ let!(:nested_group) { create(:group, :nested) }
+ let!(:another_group) { create(:group) }
+
+ before do
+ group.add_owner(user)
+ nested_group.add_owner(user)
+
+ login_as(user)
+
+ visit dashboard_groups_path
+ end
+
+ it 'shows groups user is member of' do
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ end
+
+ it 'filters groups' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).not_to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ end
+
+ it 'resets search when user cleans the input' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ fill_in 'filter_groups', with: ""
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(nested_group.full_name)
+ expect(page).not_to have_content(another_group.full_name)
+ expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+ end
+end
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
new file mode 100644
index 00000000000..773ae4b38bc
--- /dev/null
+++ b/spec/features/explore/groups_list_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe 'Explore Groups page', js: true, feature: true do
+ include WaitForAjax
+
+ let!(:user) { create :user }
+ let!(:group) { create(:group) }
+ let!(:public_group) { create(:group, :public) }
+ let!(:private_group) { create(:group, :private) }
+
+ before do
+ group.add_owner(user)
+
+ login_as(user)
+
+ visit explore_groups_path
+ end
+
+ it 'shows groups user is member of' do
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ end
+
+ it 'filters groups' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).not_to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ end
+
+ it 'resets search when user cleans the input' do
+ fill_in 'filter_groups', with: group.name
+ wait_for_ajax
+
+ fill_in 'filter_groups', with: ""
+ wait_for_ajax
+
+ expect(page).to have_content(group.full_name)
+ expect(page).to have_content(public_group.full_name)
+ expect(page).not_to have_content(private_group.full_name)
+ expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
+ end
+end