Merge branch 'issue-filter-name-options' into 'master'
Issuable filtering improvements This improves the filtering of issues and merge requests by creating a single file that encapsulates all the filtering. Previously this was done with a file for issues and a file for merge requests. Created the ability for the text search to be done alongside other filterables. Previously because this was outside the filterable form, this wasn't possible and would instead do either filter dropdown or text filter - not both. Fixes #4252 Fixed issue with not being able to filter and sort issues without refreshing the page. Fixes #15269 See merge request !3699
This commit is contained in:
commit
0cc060bc5f
11 changed files with 203 additions and 71 deletions
|
@ -16,7 +16,6 @@ class Dispatcher
|
|||
shortcut_handler = null
|
||||
switch page
|
||||
when 'projects:issues:index'
|
||||
Issues.init()
|
||||
Issuable.init()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:issues:show'
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
issuable_created = false
|
||||
@Issuable =
|
||||
init: ->
|
||||
Issuable.initTemplates()
|
||||
Issuable.initSearch()
|
||||
unless issuable_created
|
||||
issuable_created = true
|
||||
Issuable.initTemplates()
|
||||
Issuable.initSearch()
|
||||
Issuable.initChecks()
|
||||
|
||||
initTemplates: ->
|
||||
Issuable.labelRow = _.template(
|
||||
|
@ -19,7 +23,16 @@
|
|||
.on 'keyup', ->
|
||||
clearTimeout(@timer)
|
||||
@timer = setTimeout( ->
|
||||
Issuable.filterResults $('#issue_search_form')
|
||||
$search = $('#issue_search')
|
||||
$form = $('.js-filter-form')
|
||||
$input = $("input[name='#{$search.attr('name')}']", $form)
|
||||
|
||||
if $input.length is 0
|
||||
$form.append "<input type='hidden' name='#{$search.attr('name')}' value='#{_.escape($search.val())}'/>"
|
||||
else
|
||||
$input.val $search.val()
|
||||
|
||||
Issuable.filterResults $form
|
||||
, 500)
|
||||
|
||||
toggleLabelFilters: ->
|
||||
|
@ -59,15 +72,22 @@
|
|||
dataType: "json"
|
||||
|
||||
reload: ->
|
||||
if Issues.created
|
||||
Issues.initChecks()
|
||||
if Issuable.created
|
||||
Issuable.initChecks()
|
||||
|
||||
$('#filter_issue_search').val($('#issue_search').val())
|
||||
|
||||
initChecks: ->
|
||||
$('.check_all_issues').on 'click', ->
|
||||
$('.selected_issue').prop('checked', @checked)
|
||||
Issuable.checkChanged()
|
||||
|
||||
$('.selected_issue').on 'change', Issuable.checkChanged
|
||||
|
||||
updateStateFilters: ->
|
||||
stateFilters = $('.issues-state-filters')
|
||||
stateFilters = $('.issues-state-filters, .dropdown-menu-sort')
|
||||
newParams = {}
|
||||
paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search']
|
||||
paramKeys = ['author_id', 'milestone_title', 'assignee_id', 'issue_search', 'issue_search']
|
||||
|
||||
for paramKey in paramKeys
|
||||
newParams[paramKey] = gl.utils.getParameterValues(paramKey)[0] or ''
|
||||
|
@ -82,3 +102,17 @@
|
|||
else
|
||||
newUrl = gl.utils.mergeUrlParams(newParams, initialUrl)
|
||||
$(this).attr 'href', newUrl
|
||||
|
||||
checkChanged: ->
|
||||
checked_issues = $('.selected_issue:checked')
|
||||
if checked_issues.length > 0
|
||||
ids = $.map checked_issues, (value) ->
|
||||
$(value).data('id')
|
||||
|
||||
$('#update_issues_ids').val ids
|
||||
$('.issues-other-filters').hide()
|
||||
$('.issues_bulk_update').show()
|
||||
else
|
||||
$('#update_issues_ids').val []
|
||||
$('.issues_bulk_update').hide()
|
||||
$('.issues-other-filters').show()
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
@Issues =
|
||||
init: ->
|
||||
Issues.created = true
|
||||
Issues.initChecks()
|
||||
|
||||
$("body").on "ajax:success", ".close_issue, .reopen_issue", ->
|
||||
t = $(this)
|
||||
totalIssues = undefined
|
||||
reopen = t.hasClass("reopen_issue")
|
||||
$(".issue_counter").each ->
|
||||
issue = $(this)
|
||||
totalIssues = parseInt($(this).html(), 10)
|
||||
if reopen and issue.closest(".main_menu").length
|
||||
$(this).html totalIssues + 1
|
||||
else
|
||||
$(this).html totalIssues - 1
|
||||
|
||||
initChecks: ->
|
||||
$(".check_all_issues").click ->
|
||||
$(".selected_issue").prop("checked", @checked)
|
||||
Issues.checkChanged()
|
||||
|
||||
$(".selected_issue").bind "change", Issues.checkChanged
|
||||
|
||||
checkChanged: ->
|
||||
checked_issues = $(".selected_issue:checked")
|
||||
if checked_issues.length > 0
|
||||
ids = []
|
||||
$.each checked_issues, (index, value) ->
|
||||
ids.push $(value).attr("data-id")
|
||||
|
||||
$("#update_issues_ids").val ids
|
||||
$(".issues-other-filters").hide()
|
||||
$(".issues_bulk_update").show()
|
||||
else
|
||||
$("#update_issues_ids").val []
|
||||
$(".issues_bulk_update").hide()
|
||||
$(".issues-other-filters").show()
|
|
@ -26,10 +26,19 @@
|
|||
newUrl = decodeURIComponent(url)
|
||||
for paramName, paramValue of params
|
||||
pattern = new RegExp "\\b(#{paramName}=).*?(&|$)"
|
||||
if url.search(pattern) >= 0
|
||||
if not paramValue?
|
||||
newUrl = newUrl.replace pattern, ''
|
||||
else if url.search(pattern) isnt -1
|
||||
newUrl = newUrl.replace pattern, "$1#{paramValue}$2"
|
||||
else
|
||||
newUrl = "#{newUrl}#{(if newUrl.indexOf('?') > 0 then '&' else '?')}#{paramName}=#{paramValue}"
|
||||
|
||||
# Remove a trailing ampersand
|
||||
lastChar = newUrl[newUrl.length - 1]
|
||||
|
||||
if lastChar is '&'
|
||||
newUrl = newUrl.slice 0, -1
|
||||
|
||||
newUrl
|
||||
|
||||
# removes parameter query string from url. returns the modified url
|
||||
|
|
|
@ -119,7 +119,7 @@
|
|||
}
|
||||
|
||||
input {
|
||||
height: 34px;
|
||||
height: 35px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
|
|
|
@ -40,11 +40,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.issue-search-form {
|
||||
margin: 0;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
form.edit-issue {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
@ -262,6 +262,8 @@ module ApplicationHelper
|
|||
assignee_id: params[:assignee_id],
|
||||
author_id: params[:author_id],
|
||||
sort: params[:sort],
|
||||
issue_search: params[:issue_search],
|
||||
label_name: params[:label_name]
|
||||
}
|
||||
|
||||
options = exist_opts.merge(options)
|
||||
|
@ -272,16 +274,11 @@ module ApplicationHelper
|
|||
end
|
||||
end
|
||||
|
||||
path = request.path
|
||||
path << "?#{options.to_param}"
|
||||
if add_label
|
||||
if params[:label_name].present? and params[:label_name].respond_to?('any?')
|
||||
params[:label_name].each do |label|
|
||||
path << "&label_name[]=#{label}"
|
||||
end
|
||||
end
|
||||
end
|
||||
path
|
||||
params = options.compact
|
||||
|
||||
params.delete(:label_name) unless add_label
|
||||
|
||||
"#{request.path}?#{params.to_param}"
|
||||
end
|
||||
|
||||
def outdated_browser?
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
- else
|
||||
= sort_title_recently_created
|
||||
%b.caret
|
||||
%ul.dropdown-menu.dropdown-menu-align-right
|
||||
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
|
||||
%li
|
||||
= link_to page_filter_path(sort: sort_value_recently_created) do
|
||||
= sort_title_recently_created
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
.issues-filters
|
||||
.issues-details-filters.row-content-block.second-block
|
||||
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name]), method: :get, class: 'filter-form' do
|
||||
= form_tag page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :issue_search]), method: :get, class: 'filter-form js-filter-form' do
|
||||
- if params[:issue_search].present?
|
||||
= hidden_field_tag :issue_search, params[:issue_search]
|
||||
- if controller.controller_name == 'issues' && can?(current_user, :admin_issue, @project)
|
||||
.check-all-holder
|
||||
= check_box_tag "check_all_issues", nil, false,
|
||||
|
|
|
@ -1,8 +1,2 @@
|
|||
= form_tag(path, method: :get, id: "issue_search_form", class: 'issue-search-form') do
|
||||
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by name ...', class: 'form-control issue_search search-text-input input-short', spellcheck: false }
|
||||
= hidden_field_tag :state, params['state']
|
||||
= hidden_field_tag :scope, params['scope']
|
||||
= hidden_field_tag :assignee_id, params['assignee_id']
|
||||
= hidden_field_tag :author_id, params['author_id']
|
||||
= hidden_field_tag :milestone_id, params['milestone_id']
|
||||
= hidden_field_tag :label_id, params['label_id']
|
||||
|
|
|
@ -154,4 +154,144 @@ describe 'Filter issues', feature: true do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'filter issues by text' do
|
||||
before do
|
||||
create(:issue, title: "Bug", project: project)
|
||||
|
||||
bug_label = create(:label, project: project, title: 'bug')
|
||||
milestone = create(:milestone, title: "8", project: project)
|
||||
|
||||
issue = create(:issue,
|
||||
title: "Bug 2",
|
||||
project: project,
|
||||
milestone: milestone,
|
||||
author: user,
|
||||
assignee: user)
|
||||
issue.labels << bug_label
|
||||
|
||||
visit namespace_project_issues_path(project.namespace, project)
|
||||
end
|
||||
|
||||
context 'only text', js: true do
|
||||
it 'should filter issues by searched text' do
|
||||
fill_in 'issue_search', with: 'Bug'
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 2)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should not show any issues' do
|
||||
fill_in 'issue_search', with: 'testing'
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to_not have_selector('.issue')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'text and dropdown options', js: true do
|
||||
it 'should filter by text and label' do
|
||||
fill_in 'issue_search', with: 'Bug'
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 2)
|
||||
end
|
||||
|
||||
click_button 'Label'
|
||||
page.within '.labels-filter' do
|
||||
click_link 'bug'
|
||||
end
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should filter by text and milestone' do
|
||||
fill_in 'issue_search', with: 'Bug'
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 2)
|
||||
end
|
||||
|
||||
click_button 'Milestone'
|
||||
page.within '.milestone-filter' do
|
||||
click_link '8'
|
||||
end
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should filter by text and assignee' do
|
||||
fill_in 'issue_search', with: 'Bug'
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 2)
|
||||
end
|
||||
|
||||
click_button 'Assignee'
|
||||
page.within '.dropdown-menu-assignee' do
|
||||
click_link user.name
|
||||
end
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 1)
|
||||
end
|
||||
end
|
||||
|
||||
it 'should filter by text and author' do
|
||||
fill_in 'issue_search', with: 'Bug'
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 2)
|
||||
end
|
||||
|
||||
click_button 'Author'
|
||||
page.within '.dropdown-menu-author' do
|
||||
click_link user.name
|
||||
end
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'filter issues and sort', js: true do
|
||||
before do
|
||||
bug_label = create(:label, project: project, title: 'bug')
|
||||
bug_one = create(:issue, title: "Frontend", project: project)
|
||||
bug_two = create(:issue, title: "Bug 2", project: project)
|
||||
|
||||
bug_one.labels << bug_label
|
||||
bug_two.labels << bug_label
|
||||
|
||||
visit namespace_project_issues_path(project.namespace, project)
|
||||
end
|
||||
|
||||
it 'should be able to filter and sort issues' do
|
||||
click_button 'Label'
|
||||
page.within '.labels-filter' do
|
||||
click_link 'bug'
|
||||
end
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(page).to have_selector('.issue', count: 2)
|
||||
end
|
||||
|
||||
click_button 'Last created'
|
||||
page.within '.dropdown-menu-sort' do
|
||||
click_link 'Oldest created'
|
||||
end
|
||||
|
||||
page.within '.issues-list' do
|
||||
expect(first('.issue')).to have_content('Frontend')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue