Commit 28592ae4 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'issues-filters' into 'master'

Big refactoring of issues filters

* No magic numbers for issues filtering
* Squash project users selectbox and users selectbox into one class
* Move from API autocomplete to GitLab internal one
* Smarter filter for project/group/all issues
* Use selectbox with searchbox for assignee/author/milestone/label
* Switch to ajax filter for issue author/assignee

Fixes https://gitlab.com/gitlab-org/gitlab-ce/issues/1035

See merge request !466
parents 708c39ff c1c93f4f
...@@ -43,6 +43,9 @@ v 7.10.0 (unreleased) ...@@ -43,6 +43,9 @@ v 7.10.0 (unreleased)
- Link note avatar to user. - Link note avatar to user.
- Make Git-over-SSH errors more descriptive. - Make Git-over-SSH errors more descriptive.
- Fix EmailsOnPush. - Fix EmailsOnPush.
- Refactor issue filtering
- AJAX selectbox for issue assignee and author filters
- Fix issue with missing options in issue filtering dropdown if selected one
v 7.9.0 v 7.9.0
- Send EmailsOnPush email when branch or tag is created or deleted. - Send EmailsOnPush email when branch or tag is created or deleted.
......
@Api = @Api =
groups_path: "/api/:version/groups.json" groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json" group_path: "/api/:version/groups/:id.json"
users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
namespaces_path: "/api/:version/namespaces.json" namespaces_path: "/api/:version/namespaces.json"
project_users_path: "/api/:version/projects/:id/users.json"
# Get 20 (depends on api) recent notes
# and sort the ascending from oldest to newest
notes: (project_id, callback) ->
url = Api.buildUrl(Api.notes_path)
url = url.replace(':id', project_id)
$.ajax(
url: url,
data:
private_token: gon.api_token
gfm: true
recent: true
dataType: "json"
).done (notes) ->
notes.sort (a, b) ->
return a.id - b.id
callback(notes)
user: (user_id, callback) ->
url = Api.buildUrl(Api.user_path)
url = url.replace(':id', user_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (user) ->
callback(user)
# Return users list. Filtered by query
# Only active users retrieved
users: (query, callback) ->
url = Api.buildUrl(Api.users_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
active: true
dataType: "json"
).done (users) ->
callback(users)
group: (group_id, callback) -> group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path) url = Api.buildUrl(Api.group_path)
...@@ -80,23 +30,6 @@ ...@@ -80,23 +30,6 @@
).done (groups) -> ).done (groups) ->
callback(groups) callback(groups)
# Return project users list. Filtered by query
# Only active users retrieved
projectUsers: (project_id, query, callback) ->
url = Api.buildUrl(Api.project_users_path)
url = url.replace(':id', project_id)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
active: true
dataType: "json"
).done (users) ->
callback(users)
# Return namespaces list. Filtered by query # Return namespaces list. Filtered by query
namespaces: (query, callback) -> namespaces: (query, callback) ->
url = Api.buildUrl(Api.namespaces_path) url = Api.buildUrl(Api.namespaces_path)
......
...@@ -127,7 +127,7 @@ class Dispatcher ...@@ -127,7 +127,7 @@ class Dispatcher
when 'show' when 'show'
new ProjectShow() new ProjectShow()
when 'issues', 'merge_requests' when 'issues', 'merge_requests'
new ProjectUsersSelect() new UsersSelect()
when 'wikis' when 'wikis'
new Wikis() new Wikis()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
......
class @ProjectUsersSelect
constructor: ->
$('.ajax-project-users-select').each (i, select) =>
project_id = $(select).data('project-id') || $('body').data('project-id')
$(select).select2
placeholder: $(select).data('placeholder') || "Search for a user"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.projectUsers project_id, query.term, (users) ->
data = { results: users }
if query.term.length == 0
nullUser = {
name: 'Unassigned',
avatar: null,
username: 'none',
id: -1
}
data.results.unshift(nullUser)
query.callback(data)
initSelection: (element, callback) ->
id = $(element).val()
if id != "" && id != "-1"
Api.user(id, callback)
formatResult: (args...) =>
@formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-project-users-dropdown"
dropdownAutoWidth: true
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
formatResult: (user) ->
if user.avatar_url
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>"
"<div class='user-result'>
#{avatarMarkup}
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
formatSelection: (user) ->
user.name
class @UsersSelect class @UsersSelect
constructor: -> constructor: ->
@usersPath = "/autocomplete/users.json"
@userPath = "/autocomplete/users/:id.json"
$('.ajax-users-select').each (i, select) => $('.ajax-users-select').each (i, select) =>
@projectId = $(select).data('project-id')
@groupId = $(select).data('group-id')
showNullUser = $(select).data('null-user')
showAnyUser = $(select).data('any-user')
$(select).select2 $(select).select2
placeholder: "Search for a user" placeholder: "Search for a user"
multiple: $(select).hasClass('multiselect') multiple: $(select).hasClass('multiselect')
minimumInputLength: 0 minimumInputLength: 0
query: (query) -> query: (query) =>
Api.users query.term, (users) -> @users query.term, (users) =>
data = { results: users } data = { results: users }
if query.term.length == 0
anyUser = {
name: 'Any',
avatar: null,
username: 'none',
id: null
}
nullUser = {
name: 'Unassigned',
avatar: null,
username: 'none',
id: 0
}
if showNullUser
data.results.unshift(nullUser)
if showAnyUser
data.results.unshift(anyUser)
query.callback(data) query.callback(data)
initSelection: (element, callback) -> initSelection: (element, callback) =>
id = $(element).val() id = $(element).val()
if id isnt "" if id != "" && id != "0"
Api.user(id, callback) @user(id, callback)
formatResult: (args...) => formatResult: (args...) =>
@formatResult(args...) @formatResult(args...)
...@@ -38,3 +66,34 @@ class @UsersSelect ...@@ -38,3 +66,34 @@ class @UsersSelect
formatSelection: (user) -> formatSelection: (user) ->
user.name user.name
user: (user_id, callback) =>
url = @buildUrl(@userPath)
url = url.replace(':id', user_id)
$.ajax(
url: url
dataType: "json"
).done (user) ->
callback(user)
# Return users list. Filtered by query
# Only active users retrieved
users: (query, callback) =>
url = @buildUrl(@usersPath)
$.ajax(
url: url
data:
search: query
per_page: 20
active: true
project_id: @projectId
group_id: @groupId
dataType: "json"
).done (users) ->
callback(users)
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
.select2-drop-active { .select2-drop-active {
border: 1px solid #BBB !important; border: 1px solid #BBB !important;
margin-top: 4px; margin-top: 4px;
font-size: 13px;
&.select2-drop-above { &.select2-drop-above {
margin-bottom: 8px; margin-bottom: 8px;
...@@ -106,3 +107,7 @@ ...@@ -106,3 +107,7 @@
font-weight: bolder; font-weight: bolder;
} }
} }
.ajax-users-dropdown {
min-width: 225px !important;
}
...@@ -45,3 +45,11 @@ ...@@ -45,3 +45,11 @@
.btn { font-size: 13px; } .btn { font-size: 13px; }
} }
.filter-item {
margin-right: 15px;
> span {
margin-right: 4px;
}
}
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
} }
@media (min-width: 800px) { @media (min-width: 800px) {
.issues-filters,
.issues_bulk_update { .issues_bulk_update {
select, .select2-container { select, .select2-container {
width: 120px !important; width: 120px !important;
...@@ -69,14 +70,16 @@ ...@@ -69,14 +70,16 @@
} }
@media (min-width: 1200px) { @media (min-width: 1200px) {
.issues-filters,
.issues_bulk_update { .issues_bulk_update {
select, .select2-container { select, .select2-container {
width: 160px !important; width: 140px !important;
display: inline-block; display: inline-block;
} }
} }
} }
.issues-filters,
.issues_bulk_update { .issues_bulk_update {
.select2-container .select2-choice { .select2-container .select2-choice {
color: #444 !important; color: #444 !important;
......
class AutocompleteController < ApplicationController
def users
@users =
if params[:project_id].present?
project = Project.find(params[:project_id])
if can?(current_user, :read_project, project)
project.team.users
end
elsif params[:group_id]
group = Group.find(params[:group_id])
if can?(current_user, :read_group, group)
group.users
end
else
User.all
end
@users = @users.search(params[:search]) if params[:search].present?
@users = @users.active
@users = @users.page(params[:page]).per(PER_PAGE)
render json: @users, only: [:name, :username, :id], methods: [:avatar_url]
end
def user
@user = User.find(params[:id])
render json: @user, only: [:name, :username, :id], methods: [:avatar_url]
end
end
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
require_relative 'projects_finder' require_relative 'projects_finder'
class IssuableFinder class IssuableFinder
NONE = '0'
attr_accessor :current_user, :params attr_accessor :current_user, :params
def execute(current_user, params) def execute(current_user, params)
...@@ -112,7 +114,7 @@ class IssuableFinder ...@@ -112,7 +114,7 @@ class IssuableFinder
def by_milestone(items) def by_milestone(items)
if params[:milestone_id].present? if params[:milestone_id].present?
items = items.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id])) items = items.where(milestone_id: (params[:milestone_id] == NONE ? nil : params[:milestone_id]))
end end
items items
...@@ -120,7 +122,7 @@ class IssuableFinder ...@@ -120,7 +122,7 @@ class IssuableFinder
def by_assignee(items) def by_assignee(items)
if params[:assignee_id].present? if params[:assignee_id].present?
items = items.where(assignee_id: (params[:assignee_id] == '0' ? nil : params[:assignee_id])) items = items.where(assignee_id: (params[:assignee_id] == NONE ? nil : params[:assignee_id]))
end end
items items
...@@ -128,7 +130,7 @@ class IssuableFinder ...@@ -128,7 +130,7 @@ class IssuableFinder
def by_author(items) def by_author(items)
if params[:author_id].present? if params[:author_id].present?
items = items.where(author_id: (params[:author_id] == '0' ? nil : params[:author_id])) items = items.where(author_id: (params[:author_id] == NONE ? nil : params[:author_id]))
end end
items items
......
...@@ -275,7 +275,9 @@ module ApplicationHelper ...@@ -275,7 +275,9 @@ module ApplicationHelper
'https://' + promo_host 'https://' + promo_host
end end
def page_filter_path(options={}) def page_filter_path(options = {})
without = options.delete(:without)
exist_opts = { exist_opts = {
state: params[:state], state: params[:state],
scope: params[:scope], scope: params[:scope],
...@@ -288,6 +290,12 @@ module ApplicationHelper ...@@ -288,6 +290,12 @@ module ApplicationHelper
options = exist_opts.merge(options) options = exist_opts.merge(options)
if without.present?
without.each do |key|
options.delete(key)
end
end
path = request.path path = request.path
path << "?#{options.to_param}" path << "?#{options.to_param}"
path path
......
...@@ -47,4 +47,9 @@ module LabelsHelper ...@@ -47,4 +47,9 @@ module LabelsHelper
"#FFF" "#FFF"
end end
end end
def project_labels_options(project)
options_for_select([['Any', nil]]) +
options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name])
end
end end
...@@ -19,4 +19,16 @@ module MilestonesHelper ...@@ -19,4 +19,16 @@ module MilestonesHelper
content_tag :div, nil, options content_tag :div, nil, options
end end
end end
def projects_milestones_options
milestones =
if @project
@project.milestones
else
Milestone.where(project_id: @projects)
end.active
options_for_select([['Any', nil]]) +
options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
end
end end
...@@ -4,18 +4,27 @@ module SelectsHelper ...@@ -4,18 +4,27 @@ module SelectsHelper
css_class << "multiselect " if opts[:multiple] css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '') css_class << (opts[:class] || '')
value = opts[:selected] || '' value = opts[:selected] || ''
placeholder = opts[:placeholder] || 'Search for a user'
hidden_field_tag(id, value, class: css_class) null_user = opts[:null_user] || false
end any_user = opts[:any_user] || false
def project_users_select_tag(id, opts = {}) html = {
css_class = "ajax-project-users-select " class: css_class,
css_class << "multiselect " if opts[:multiple] 'data-placeholder' => placeholder,
css_class << (opts[:class] || '') 'data-null-user' => null_user,
value = opts[:selected] || '' 'data-any-user' => any_user,
placeholder = opts[:placeholder] || 'Select user' }
project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id) unless opts[:scope] == :all
if @project
html['data-project-id'] = @project.id
elsif @group
html['data-group-id'] = @group.id
end
end
hidden_field_tag(id, value, html)
end end
def groups_select_tag(id, opts = {}) def groups_select_tag(id, opts = {})
......
...@@ -14,8 +14,8 @@ module Issues ...@@ -14,8 +14,8 @@ module Issues
issue.update_nth_task(params[:task_num].to_i, false) issue.update_nth_task(params[:task_num].to_i, false)
end end
params[:assignee_id] = "" if params[:assignee_id] == "-1" params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == "-1" params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
old_labels = issue.labels.to_a old_labels = issue.labels.to_a
......
...@@ -23,8 +23,8 @@ module MergeRequests ...@@ -23,8 +23,8 @@ module MergeRequests
merge_request.update_nth_task(params[:task_num].to_i, false) merge_request.update_nth_task(params[:task_num].to_i, false)
end end
params[:assignee_id] = "" if params[:assignee_id] == "-1" params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
params[:milestone_id] = "" if params[:milestone_id] == "-1" params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
old_labels = merge_request.labels.to_a old_labels = merge_request.labels.to_a
......
...@@ -35,8 +35,8 @@ ...@@ -35,8 +35,8 @@
%i.fa.fa-user %i.fa.fa-user
Assign to Assign to
.col-sm-10 .col-sm-10
= project_users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", = users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]",
placeholder: 'Select a user', class: 'custom-form-control', placeholder: 'Select a user', class: 'custom-form-control', null_user: true,
selected: issuable.assignee_id) selected: issuable.assignee_id)
&nbsp; &nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link' = link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- else - else
none none
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
= project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id) = users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id, null_user: true)
%div.prepend-top-20.clearfix %div.prepend-top-20.clearfix
.issuable-context-title .issuable-context-title
...@@ -44,5 +44,3 @@ ...@@ -44,5 +44,3 @@
:coffeescript :coffeescript
new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}") new Subscription("#{toggle_subscription_namespace_project_issue_path(@issue.project.namespace, @project, @issue)}")
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
.issues_bulk_update.hide .issues_bulk_update.hide
= form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
= select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control') = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
= project_users_select_tag('update[assignee_id]', placeholder: 'Assignee') = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true)
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', [] = hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :state_event, params[:state_event] = hidden_field_tag :state_event, params[:state_event]
......
...@@ -13,5 +13,5 @@ ...@@ -13,5 +13,5 @@
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true}) $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true})
$('.edit-issue.inline-update input[type="submit"]').hide(); $('.edit-issue.inline-update input[type="submit"]').hide();
new ProjectUsersSelect(); new UsersSelect()
new Issue(); new Issue();
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
%i.fa.fa-user %i.fa.fa-user
Assign to Assign to
.col-sm-10 .col-sm-10
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id) = users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
&nbsp; &nbsp;
= link_to 'Assign to me', '#', class: 'btn assign-to-me-link' = link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
.form-group .form-group
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
none none
.issuable-context-selectbox .issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) = users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id, null_user: true)
%div.prepend-top-20.clearfix %div.prepend-top-20.clearfix
.issuable-context-title .issuable-context-title
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
$('.context').html("#{escape_javascript(render partial: 'projects/merge_requests/show/context', locals: { issue: @issue })}"); $('.context').html("#{escape_javascript(render partial: 'projects/merge_requests/show/context', locals: { issue: @issue })}");
$('.context').effect('highlight'); $('.context').effect('highlight');
new ProjectUsersSelect(); new UsersSelect()
$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true}); $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true});
merge_request = new MergeRequest(); merge_request = new MergeRequest();
= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f| = form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f|
.form-group .form-group
= f.label :user_ids, "People", class: 'control-label' = f.label :user_ids, "People", class: 'control-label'
.col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all)
.form-group .form-group
= f.label :access_level, "Project Access", class: 'control-label' = f.label :access_level, "Project Access", class: 'control-label'
......
...@@ -15,105 +15,50 @@ ...@@ -15,105 +15,50 @@
All All
%div %div
- if controller.controller_name == 'issues' = form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_id, :label_name]), method: :get, class: 'filter-form' do
.check-all-holder - if controller.controller_name == 'issues'
= check_box_tag "check_all_issues", nil, false, .check-all-holder
class: "check_all_issues left", = check_box_tag "check_all_issues", nil, false,
disabled: !can?(current_user, :modify_issue, @project) class: "check_all_issues left",
.issues-other-filters disabled: !can?(current_user, :modify_issue, @project)
.dropdown.inline.assignee-filter .issues-other-filters
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} .filter-item.inline
%i.fa.fa-user %span.light
%span.light assignee: %i.fa.fa-user
- if @assignee.present? Assignee
%strong= @assignee.name %strong
- elsif params[:assignee_id] == "0" = users_select_tag(:assignee_id, selected: params[:assignee_id],
Unassigned placeholder: 'Any', class: 'trigger-submit', any_user: true, null_user: true)
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(assignee_id: nil) do
Any
= link_to page_filter_path(assignee_id: 0) do
Unassigned
- @assignees.sort_by(&:name).each do |user|
%li
= link_to page_filter_path(assignee_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.dropdown.inline.prepend-left-10.author-filter .filter-item.inline
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} %span.light
%i.fa.fa-user %i.fa.fa-user
%span.light author: Author
- if @author.present? %strong
%strong= @author.name = users_select_tag(:author_id, selected: params[:author_id],
- elsif params[:author_id] == "0" placeholder: 'Any', class: 'trigger-submit', any_user: true)
Unassigned
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(author_id: nil) do
Any
= link_to page_filter_path(author_id: 0) do
Unassigned
- @authors.sort_by(&:name).each do |user|
%li
= link_to page_filter_path(author_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.dropdown.inline.prepend-left-10.milestone-filter .filter-item.inline
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} %span.light
%i.fa.fa-clock-o %i.fa.fa-clock-o
%span.light milestone: Milestone
- if @milestone.present? %strong
%strong= @milestone.title = select_tag('milestone_id', projects_milestones_options, class: "select2 trigger-submit")
- elsif params[:milestone_id] == "0"
None (backlog)
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(milestone_id: nil) do
Any
= link_to page_filter_path(milestone_id: 0) do
None (backlog)
- @milestones.each do |milestone|
%li
= link_to page_filter_path(milestone_id: milestone.id) do
%strong= milestone.title
%small.light= milestone.expires_at
- if @project - if @project
.dropdown.inline.prepend-left-10.labels-filter .filter-item.inline
%button.dropdown-toggle.btn{type: 'button', "data-toggle" => "dropdown"} %span.light
%i.fa.fa-tags %i.fa.fa-tag
%span.light label: Label
- if params[:label_name].present? %strong
%strong= params[:label_name] = select_tag('label_name', project_labels_options(@project), class: "select2 trigger-submit")
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(label_name: nil) do
Any
- if @project.labels.any?
- @project.labels.each do |label|
%li
= link_to page_filter_path(label_name: label.name) do
= render_colored_label(label)
- else
%li
= link_to generate_namespace_project_labels_path(@project.namespace, @project, redirect: request.original_url), method: :post do
%i.fa.fa-plus-circle
Create default labels
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
:coffeescript
new UsersSelect()
$('form.filter-form').on 'submit', (event) ->
event.preventDefault()
Turbolinks.visit @.action + '&' + $(@).serialize()
...@@ -8,6 +8,11 @@ Gitlab::Application.routes.draw do ...@@ -8,6 +8,11 @@ Gitlab::Application.routes.draw do
authorizations: 'oauth/authorizations' authorizations: 'oauth/authorizations'
end end
# Autocomplete
get '/autocomplete/users' => 'autocomplete#users'
get '/autocomplete/users/:id' => 'autocomplete#user'
# Search # Search
get 'search' => 'search#show' get 'search' => 'search#show'
get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
......
...@@ -10,10 +10,12 @@ Feature: Dashboard Issues ...@@ -10,10 +10,12 @@ Feature: Dashboard Issues
Scenario: I should see assigned issues Scenario: I should see assigned issues
Then I should see issues assigned to me Then I should see issues assigned to me
@javascript
Scenario: I should see authored issues Scenario: I should see authored issues
When I click "Authored by me" link When I click "Authored by me" link
Then I should see issues authored by me Then I should see issues authored by me
@javascript
Scenario: I should see all issues Scenario: I should see all issues
When I click "All" link When I click "All" link
Then I should see all issues Then I should see all issues
...@@ -10,10 +10,12 @@ Feature: Dashboard Merge Requests ...@@ -10,10 +10,12 @@ Feature: Dashboard Merge Requests
Scenario: I should see assigned merge_requests Scenario: I should see assigned merge_requests
Then I should see merge requests assigned to me Then I should see merge requests assigned to me
@javascript
Scenario: I should see authored merge_requests Scenario: I should see authored merge_requests
When I click "Authored by me" link When I click "Authored by me" link
Then I should see merge requests authored by me Then I should see merge requests authored by me
@javascript
Scenario: I should see all merge_requests Scenario: I should see all merge_requests
When I click "All" link When I click "All" link
Then I should see all merge requests Then I should see all merge requests
...@@ -8,11 +8,7 @@ Feature: Project Issues Filter Labels ...@@ -8,11 +8,7 @@ Feature: Project Issues Filter Labels
And project "Shop" has issue "Feature1" with labels: "feature" And project "Shop" has issue "Feature1" with labels: "feature"
Given I visit project "Shop" issues page Given I visit project "Shop" issues page
Scenario: I should see project issues @javascript
Then I should see "bug" in labels filter
And I should see "feature" in labels filter
And I should see "enhancement" in labels filter
Scenario: I filter by one label Scenario: I filter by one label
Given I click link "bug" Given I click link "bug"
Then I should see "Bugfix1" in issues list Then I should see "Bugfix1" in issues list
......
class Spinach::Features::DashboardIssues < Spinach::FeatureSteps class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include Select2Helper
step 'I should see issues assigned to me' do step 'I should see issues assigned to me' do
should_see(assigned_issue) should_see(assigned_issue)
...@@ -35,21 +36,13 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps ...@@ -35,21 +36,13 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps
end end
step 'I click "Authored by me" link' do step 'I click "Authored by me" link' do
within ".assignee-filter" do select2(current_user.id, from: "#author_id")
click_link "Any" select2(nil, from: "#assignee_id")
end
within ".author-filter" do
click_link current_user.name
end
end end
step 'I click "All" link' do step 'I click "All" link' do
within ".author-filter" do select2(nil, from: "#author_id")
click_link "Any" select2(nil, from: "#assignee_id")
end
within ".assignee-filter" do
click_link "Any"
end
end end
def should_see(issue) def should_see(issue)
......
class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedPaths include SharedPaths
include Select2Helper
step 'I should see merge requests assigned to me' do step 'I should see merge requests assigned to me' do
should_see(assigned_merge_request) should_see(assigned_merge_request)
...@@ -39,21 +40,13 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps ...@@ -39,21 +40,13 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps
end end
step 'I click "Authored by me" link' do step 'I click "Authored by me" link' do
within ".assignee-filter" do select2(current_user.id, from: "#author_id")
click_link "Any" select2(nil, from: "#assignee_id")
end
within ".author-filter" do
click_link current_user.name
end
end end
step 'I click "All" link' do step 'I click "All" link' do
within ".author-filter" do select2(nil, from: "#author_id")
click_link "Any" select2(nil, from: "#assignee_id")
end
within ".assignee-filter" do
click_link "Any"
end
end end
def should_see(merge_request) def should_see(merge_request)
......
...@@ -2,24 +2,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps ...@@ -2,24 +2,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
include SharedAuthentication include SharedAuthentication
include SharedProject include SharedProject
include SharedPaths include SharedPaths
include Select2Helper
step 'I should see "bug" in labels filter' do
within ".labels-filter" do
page.should have_content "bug"
end
end
step 'I should see "feature" in labels filter' do
within ".labels-filter" do
page.should have_content "feature"
end
end
step 'I should see "enhancement" in labels filter' do
within ".labels-filter" do
page.should have_content "enhancement"
end
end
step 'I should see "Bugfix1" in issues list' do step 'I should see "Bugfix1" in issues list' do
within ".issues-list" do within ".issues-list" do
...@@ -46,9 +29,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps ...@@ -46,9 +29,7 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
end end
step 'I click link "bug"' do step 'I click link "bug"' do
within ".labels-filter" do select2('bug', from: "#label_name")
click_link "bug"
end
end end
step 'I click link "feature"' do step 'I click link "feature"' do
......
require 'spec_helper'
describe AutocompleteController do
let!(:project) { create(:project) }
let!(:user) { create(:user) }
let!(:user2) { create(:user) }
context 'project members' do
before do
sign_in(user)
project.team << [user, :master]
get(:users, project_id: project.id)
end
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(1) }
it { body.first["username"].should == user.username }
end
context 'group members' do
let(:group) { create(:group) }
before do
sign_in(user)
group.add_owner(user)
get(:users, group_id: group.id)
end
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(1) }
it { body.first["username"].should == user.username }
end
context 'all users' do
before do
sign_in(user)
get(:users)
end
let(:body) { JSON.parse(response.body) }
it { body.should be_kind_of(Array) }
it { body.size.should eq(User.count) }
end
end
...@@ -95,7 +95,7 @@ describe 'Issues', feature: true do ...@@ -95,7 +95,7 @@ describe 'Issues', feature: true do
let(:issue) { @issue } let(:issue) { @issue }
it 'should allow filtering by issues with no specified milestone' do it 'should allow filtering by issues with no specified milestone' do
visit namespace_project_issues_path(project.namespace, project, milestone_id: '0') visit namespace_project_issues_path(project.namespace, project, milestone_id: IssuableFinder::NONE)
expect(page).not_to have_content 'foobar' expect(page).not_to have_content 'foobar'
expect(page).to have_content 'barbaz' expect(page).to have_content 'barbaz'
...@@ -111,7 +111,7 @@ describe 'Issues', feature: true do ...@@ -111,7 +111,7 @@ describe 'Issues', feature: true do
end end
it 'should allow filtering by issues with no specified assignee' do it 'should allow filtering by issues with no specified assignee' do
visit namespace_project_issues_path(project.namespace, project, assignee_id: '0') visit namespace_project_issues_path(project.namespace, project, assignee_id: IssuableFinder::NONE)
expect(page).to have_content 'foobar' expect(page).to have_content 'foobar'
expect(page).not_to have_content 'barbaz' expect(page).not_to have_content 'barbaz'
......
...@@ -17,9 +17,9 @@ module Select2Helper ...@@ -17,9 +17,9 @@ module Select2Helper
selector = options[:from] selector = options[:from]
if options[:multiple] if options[:multiple]
execute_script("$('#{selector}').select2('val', ['#{value}']);") execute_script("$('#{selector}').select2('val', ['#{value}'], true);")
else else
execute_script("$('#{selector}').select2('val', '#{value}');") execute_script("$('#{selector}').select2('val', '#{value}', true);")
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment