Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
54851c01
Commit
54851c01
authored
Apr 20, 2016
by
Jacob Schatz
Committed by
Yorick Peterse
Apr 21, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Merge branch 'multi-filter-labels' into 'master'
Mutliple label filter Fixes #989 See merge request !3438
parent
4715ab91
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
447 additions
and
137 deletions
+447
-137
app/assets/javascripts/dispatcher.js.coffee
app/assets/javascripts/dispatcher.js.coffee
+2
-1
app/assets/javascripts/gl_dropdown.js.coffee
app/assets/javascripts/gl_dropdown.js.coffee
+4
-4
app/assets/javascripts/issuable.js.coffee
app/assets/javascripts/issuable.js.coffee
+84
-0
app/assets/javascripts/issues.js.coffee
app/assets/javascripts/issues.js.coffee
+1
-50
app/assets/javascripts/labels_select.js.coffee
app/assets/javascripts/labels_select.js.coffee
+44
-19
app/assets/javascripts/lib/animate.js.coffee
app/assets/javascripts/lib/animate.js.coffee
+30
-4
app/assets/javascripts/lib/url_utility.js.coffee
app/assets/javascripts/lib/url_utility.js.coffee
+14
-2
app/assets/javascripts/merge_requests.js.coffee
app/assets/javascripts/merge_requests.js.coffee
+0
-35
app/assets/javascripts/milestone_select.js.coffee
app/assets/javascripts/milestone_select.js.coffee
+1
-1
app/assets/javascripts/users_select.js.coffee
app/assets/javascripts/users_select.js.coffee
+1
-1
app/controllers/projects/issues_controller.rb
app/controllers/projects/issues_controller.rb
+3
-2
app/controllers/projects/merge_requests_controller.rb
app/controllers/projects/merge_requests_controller.rb
+3
-2
app/finders/issuable_finder.rb
app/finders/issuable_finder.rb
+4
-2
app/helpers/application_helper.rb
app/helpers/application_helper.rb
+8
-1
app/helpers/issuables_helper.rb
app/helpers/issuables_helper.rb
+19
-0
app/models/label.rb
app/models/label.rb
+4
-0
app/views/shared/_label_row.html.haml
app/views/shared/_label_row.html.haml
+1
-1
app/views/shared/_labels_row.html.haml
app/views/shared/_labels_row.html.haml
+3
-0
app/views/shared/issuable/_filter.html.haml
app/views/shared/issuable/_filter.html.haml
+4
-3
app/views/shared/issuable/_label_dropdown.html.haml
app/views/shared/issuable/_label_dropdown.html.haml
+5
-3
app/views/shared/issuable/_nav.html.haml
app/views/shared/issuable/_nav.html.haml
+5
-5
features/project/issues/filter_labels.feature
features/project/issues/filter_labels.feature
+1
-0
features/steps/project/issues/filter_labels.rb
features/steps/project/issues/filter_labels.rb
+4
-0
spec/features/issues/filter_by_labels_spec.rb
spec/features/issues/filter_by_labels_spec.rb
+172
-0
spec/features/issues/filter_issues_spec.rb
spec/features/issues/filter_issues_spec.rb
+8
-1
spec/features/merge_requests/filter_by_milestone_spec.rb
spec/features/merge_requests/filter_by_milestone_spec.rb
+6
-0
spec/finders/issues_finder_spec.rb
spec/finders/issues_finder_spec.rb
+16
-0
No files found.
app/assets/javascripts/dispatcher.js.coffee
View file @
54851c01
...
...
@@ -17,6 +17,7 @@ class Dispatcher
switch
page
when
'projects:issues:index'
Issues
.
init
()
Issuable
.
init
()
shortcut_handler
=
new
ShortcutsNavigation
()
when
'projects:issues:show'
new
Issue
()
...
...
@@ -57,7 +58,7 @@ class Dispatcher
new
ZenMode
()
when
'projects:merge_requests:index'
shortcut_handler
=
new
ShortcutsNavigation
()
MergeRequests
.
init
()
Issuable
.
init
()
when
'dashboard:activity'
new
Activities
()
when
'dashboard:projects:starred'
...
...
app/assets/javascripts/gl_dropdown.js.coffee
View file @
54851c01
...
...
@@ -386,13 +386,13 @@ class GitLabDropdown
else
selectedObject
else
if
!
value
?
field
.
remove
()
if
not
@
options
.
multiSelect
if
not
@
options
.
multiSelect
or
el
.
hasClass
(
'dropdown-clear-active'
)
@
dropdown
.
find
(
".
#{
ACTIVE_CLASS
}
"
).
removeClass
ACTIVE_CLASS
@
dropdown
.
parent
().
find
(
"input[name='
#{
fieldName
}
']"
).
remove
()
if
!
value
?
field
.
remove
()
# Toggle active class for the tick mark
el
.
addClass
ACTIVE_CLASS
...
...
app/assets/javascripts/issuable.js.coffee
0 → 100644
View file @
54851c01
@
Issuable
=
init
:
->
Issuable
.
initTemplates
()
Issuable
.
initSearch
()
initTemplates
:
->
Issuable
.
labelRow
=
_
.
template
(
'<% _.each(labels, function(label){ %>
<span class="label-row">
<a href="#"><span class="label color-label has-tooltip" style="background-color: <%= label.color %>; color: <%= label.text_color %>" title="<%= _.escape(label.description) %>" data-container="body"><%= _.escape(label.title) %></span></a>
</span>
<% }); %>'
)
initSearch
:
->
@
timer
=
null
$
(
'#issue_search'
)
.
off
'keyup'
.
on
'keyup'
,
->
clearTimeout
(
@
timer
)
@
timer
=
setTimeout
(
->
Issuable
.
filterResults
$
(
'#issue_search_form'
)
,
500
)
toggleLabelFilters
:
->
$filteredLabels
=
$
(
'.filtered-labels'
)
if
$filteredLabels
.
find
(
'.label-row'
).
length
>
0
$filteredLabels
.
removeClass
(
'hidden'
)
else
$filteredLabels
.
addClass
(
'hidden'
)
filterResults
:
(
form
)
=>
formData
=
form
.
serialize
()
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
'opacity'
,
'0.5'
)
formAction
=
form
.
attr
(
'action'
)
issuesUrl
=
formAction
issuesUrl
+=
(
"
#{
if
formAction
.
indexOf
(
'?'
)
<
0
then
'?'
else
'&'
}
"
)
issuesUrl
+=
formData
$
.
ajax
type
:
'GET'
url
:
formAction
data
:
formData
complete
:
->
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
'opacity'
,
'1.0'
)
success
:
(
data
)
->
$
(
'.issues-holder, .merge-requests-holder'
).
html
(
data
.
html
)
# Change url so if user reload a page - search results are saved
history
.
replaceState
{
page
:
issuesUrl
},
document
.
title
,
issuesUrl
Issuable
.
reload
()
Issuable
.
updateStateFilters
()
$filteredLabels
=
$
(
'.filtered-labels'
)
if
typeof
Issuable
.
labelRow
is
'function'
$filteredLabels
.
html
(
Issuable
.
labelRow
(
data
))
Issuable
.
toggleLabelFilters
()
dataType
:
"json"
reload
:
->
if
Issues
.
created
Issues
.
initChecks
()
$
(
'#filter_issue_search'
).
val
(
$
(
'#issue_search'
).
val
())
updateStateFilters
:
->
stateFilters
=
$
(
'.issues-state-filters'
)
newParams
=
{}
paramKeys
=
[
'author_id'
,
'milestone_title'
,
'assignee_id'
,
'issue_search'
]
for
paramKey
in
paramKeys
newParams
[
paramKey
]
=
gl
.
utils
.
getParameterValues
(
paramKey
)[
0
]
or
''
if
stateFilters
.
length
stateFilters
.
find
(
'a'
).
each
->
initialUrl
=
gl
.
utils
.
removeParamQueryString
(
$
(
this
).
attr
(
'href'
),
'label_name[]'
)
labelNameValues
=
gl
.
utils
.
getParameterValues
(
'label_name[]'
)
if
labelNameValues
labelNameQueryString
=
(
"label_name[]=
#{
value
}
"
for
value
in
labelNameValues
).
join
(
'&'
)
newUrl
=
"
#{
gl
.
utils
.
mergeUrlParams
(
newParams
,
initialUrl
)
}
&
#{
labelNameQueryString
}
"
else
newUrl
=
gl
.
utils
.
mergeUrlParams
(
newParams
,
initialUrl
)
$
(
this
).
attr
'href'
,
newUrl
app/assets/javascripts/issues.js.coffee
View file @
54851c01
@
Issues
=
init
:
->
Issues
.
initSearch
()
Issues
.
created
=
true
Issues
.
initChecks
()
$
(
"body"
).
on
"ajax:success"
,
".close_issue, .reopen_issue"
,
->
...
...
@@ -15,10 +15,6 @@
else
$
(
this
).
html
totalIssues
-
1
reload
:
->
Issues
.
initChecks
()
$
(
'#filter_issue_search'
).
val
(
$
(
'#issue_search'
).
val
())
initChecks
:
->
$
(
".check_all_issues"
).
click
->
$
(
".selected_issue"
).
prop
(
"checked"
,
@
checked
)
...
...
@@ -26,51 +22,6 @@
$
(
".selected_issue"
).
bind
"change"
,
Issues
.
checkChanged
# Update state filters if present in page
updateStateFilters
:
->
stateFilters
=
$
(
'.issues-state-filters'
)
newParams
=
{}
paramKeys
=
[
'author_id'
,
'label_name'
,
'milestone_title'
,
'assignee_id'
,
'issue_search'
]
for
paramKey
in
paramKeys
newParams
[
paramKey
]
=
gl
.
utils
.
getUrlParameter
(
paramKey
)
or
''
if
stateFilters
.
length
stateFilters
.
find
(
'a'
).
each
->
initialUrl
=
$
(
this
).
attr
'href'
$
(
this
).
attr
'href'
,
gl
.
utils
.
mergeUrlParams
(
newParams
,
initialUrl
)
# Make sure we trigger ajax request only after user stop typing
initSearch
:
->
@
timer
=
null
$
(
"#issue_search"
).
keyup
->
clearTimeout
(
@
timer
)
@
timer
=
setTimeout
(
->
Issues
.
filterResults
$
(
"#issue_search_form"
)
,
500
)
filterResults
:
(
form
)
=>
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
"opacity"
,
'0.5'
)
formAction
=
form
.
attr
(
'action'
)
formData
=
form
.
serialize
()
issuesUrl
=
formAction
issuesUrl
+=
(
"
#{
if
formAction
.
indexOf
(
"?"
)
<
0
then
'?'
else
'&'
}
"
)
issuesUrl
+=
formData
$
.
ajax
type
:
"GET"
url
:
formAction
data
:
formData
complete
:
->
$
(
'.issues-holder, .merge-requests-holder'
).
css
(
"opacity"
,
'1.0'
)
success
:
(
data
)
->
$
(
'.issues-holder, .merge-requests-holder'
).
html
(
data
.
html
)
# Change url so if user reload a page - search results are saved
history
.
replaceState
{
page
:
issuesUrl
},
document
.
title
,
issuesUrl
Issues
.
reload
()
Issues
.
updateStateFilters
()
dataType
:
"json"
checkChanged
:
->
checked_issues
=
$
(
".selected_issue:checked"
)
if
checked_issues
.
length
>
0
...
...
app/assets/javascripts/labels_select.js.coffee
View file @
54851c01
...
...
@@ -6,7 +6,7 @@ class @LabelsSelect
labelUrl
=
$dropdown
.
data
(
'labels'
)
issueUpdateURL
=
$dropdown
.
data
(
'issueUpdate'
)
selectedLabel
=
$dropdown
.
data
(
'selected'
)
if
selectedLabel
?
if
selectedLabel
?
and
not
$dropdown
.
hasClass
'js-multiselect'
selectedLabel
=
selectedLabel
.
split
(
','
)
newLabelField
=
$
(
'#new_label_name'
)
newColorField
=
$
(
'#new_label_color'
)
...
...
@@ -16,6 +16,7 @@ class @LabelsSelect
abilityName
=
$dropdown
.
data
(
'ability-name'
)
$selectbox
=
$dropdown
.
closest
(
'.selectbox'
)
$block
=
$selectbox
.
closest
(
'.block'
)
$form
=
$dropdown
.
closest
(
'form'
)
$sidebarCollapsedValue
=
$block
.
find
(
'.sidebar-collapsed-icon span'
)
$value
=
$block
.
find
(
'.value'
)
$loading
=
$block
.
find
(
'.block-loading'
).
fadeOut
()
...
...
@@ -171,7 +172,7 @@ class @LabelsSelect
.
find
(
'a'
)
.
each
((
i
)
->
setTimeout
(
=>
gl
A
nimate
(
$
(
@
),
'pulse'
)
gl
.
animate
.
a
nimate
(
$
(
@
),
'pulse'
)
,
200
*
i
)
)
...
...
@@ -200,16 +201,21 @@ class @LabelsSelect
callback
data
renderRow
:
(
label
)
->
selectedClass
=
''
if
$selectbox
.
find
(
"input[type='hidden']
\
[name='
#{
$dropdown
.
data
(
'field-name'
)
}
']
\
[value='
#{
label
.
id
}
']"
).
length
selectedClass
=
'is-active'
removesAll
=
label
.
id
is
0
or
not
label
.
id
?
selectedClass
=
[]
if
$form
.
find
(
"input[type='hidden']
\
[name='
#{
$dropdown
.
data
(
'fieldName'
)
}
']
\
[value='
#{
this
.
id
(
label
)
}
']"
).
length
selectedClass
.
push
'is-active'
if
$dropdown
.
hasClass
(
'js-multiselect'
)
and
removesAll
selectedClass
.
push
'dropdown-clear-active'
color
=
if
label
.
color
?
then
"<span class='dropdown-label-box' style='background-color:
#{
label
.
color
}
'></span>"
else
""
"<li>
<a href='#' class='
#{
selectedClass
}
'>
<a href='#' class='
#{
selectedClass
.
join
(
' '
)
}
'>
#{
color
}
#{
_
.
escape
(
label
.
title
)
}
</a>
...
...
@@ -219,37 +225,56 @@ class @LabelsSelect
fields
:
[
'title'
]
selectable
:
true
toggleLabel
:
(
selected
)
->
toggleLabel
:
(
selected
,
el
)
->
selected_labels
=
$
(
'.js-label-select'
).
siblings
(
'.dropdown-menu-labels'
).
find
(
'.is-active'
)
if
selected
and
selected
.
title
?
selected
.
title
if
selected_labels
.
length
>
1
"
#{
selected
.
title
}
+
#{
selected_labels
.
length
-
1
}
more"
else
selected
.
title
else
if
not
selected
and
selected_labels
.
length
isnt
0
if
selected_labels
.
length
>
1
"
#{
$
(
selected_labels
[
0
]).
text
()
}
+
#{
selected_labels
.
length
-
1
}
more"
else
if
selected_labels
.
length
is
1
$
(
selected_labels
).
text
()
else
defaultLabel
fieldName
:
$dropdown
.
data
(
'field-name'
)
id
:
(
label
)
->
if
label
.
isAny
?
''
else
if
$dropdown
.
hasClass
"js-filter-submit"
if
$dropdown
.
hasClass
(
"js-filter-submit"
)
and
not
label
.
isAny
?
label
.
title
else
label
.
id
hidden
:
->
page
=
$
(
'body'
).
data
'page'
isIssueIndex
=
page
is
'projects:issues:index'
isMRIndex
=
page
is
'projects:merge_requests:index'
$selectbox
.
hide
()
# display:block overrides the hide-collapse rule
$value
.
removeAttr
(
'style'
)
if
$dropdown
.
hasClass
'js-multiselect'
saveLabelData
()
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
and
(
isIssueIndex
or
isMRIndex
)
selectedLabels
=
$dropdown
.
closest
(
'form'
)
.
find
(
"input:hidden[name='
#{
$dropdown
.
data
(
'fieldName'
)
}
']"
)
Issuable
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
$dropdown
.
closest
(
'form'
).
submit
()
else
saveLabelData
()
multiSelect
:
$dropdown
.
hasClass
'js-multiselect'
clicked
:
(
label
)
->
page
=
$
(
'body'
).
data
'page'
isIssueIndex
=
page
is
'projects:issues:index'
isMRIndex
=
page
is
page
is
'projects:merge_requests:index'
isMRIndex
=
page
is
'projects:merge_requests:index'
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
and
(
isIssueIndex
or
isMRIndex
)
selectedLabel
=
label
.
title
Issues
.
filterResults
$dropdown
.
closest
(
'form'
)
if
not
$dropdown
.
hasClass
'js-multiselect'
selectedLabel
=
label
.
title
Issuable
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
'js-filter-submit'
$dropdown
.
closest
(
'form'
).
submit
()
else
...
...
app/assets/javascripts/lib/animate.js.coffee
View file @
54851c01
((
w
)
->
if
not
w
.
gl
?
then
w
.
gl
=
{}
if
not
gl
.
animate
?
then
gl
.
animate
=
{}
w
.
glAnimate
=
(
$el
,
animation
,
done
)
->
gl
.
animate
.
animate
=
(
$el
,
animation
,
options
,
done
)
->
if
options
?
.
cssStart
?
$el
.
css
(
options
.
cssStart
)
$el
.
removeClass
()
.
removeClass
(
animation
+
' animated'
)
.
addClass
(
animation
+
' animated'
)
.
one
'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'
,
->
$
(
this
).
removeClass
()
$
(
this
).
removeClass
(
animation
+
' animated'
)
if
done
?
done
()
if
options
?
.
cssEnd
?
$el
.
css
(
options
.
cssEnd
)
return
return
return
gl
.
animate
.
animateEach
=
(
$els
,
animation
,
time
,
options
,
done
)
->
dfd
=
$
.
Deferred
()
if
not
$els
.
length
dfd
.
resolve
()
$els
.
each
((
i
)
->
setTimeout
(
=>
$this
=
$
(
@
)
gl
.
animate
.
animate
(
$this
,
animation
,
options
,
=>
if
i
is
$els
.
length
-
1
dfd
.
resolve
()
if
done
?
done
()
)
,
time
*
i
)
return
)
return
dfd
.
promise
()
return
)
window
\ No newline at end of file
app/assets/javascripts/lib/url_utility.js.coffee
View file @
54851c01
...
...
@@ -3,16 +3,20 @@
w
.
gl
?=
{}
w
.
gl
.
utils
?=
{}
w
.
gl
.
utils
.
getUrlParameter
=
(
sParam
)
->
# Returns an array containing the value(s) of the
# of the key passed as an argument
w
.
gl
.
utils
.
getParameterValues
=
(
sParam
)
->
sPageURL
=
decodeURIComponent
(
window
.
location
.
search
.
substring
(
1
))
sURLVariables
=
sPageURL
.
split
(
'&'
)
sParameterName
=
undefined
values
=
[]
i
=
0
while
i
<
sURLVariables
.
length
sParameterName
=
sURLVariables
[
i
].
split
(
'='
)
if
sParameterName
[
0
]
is
sParam
return
if
sParameterName
[
1
]
is
undefined
then
true
else
sParameterName
[
1
]
values
.
push
(
sParameterName
[
1
])
i
++
values
# #
# @param {Object} params - url keys and value to merge
...
...
@@ -28,4 +32,12 @@
newUrl
=
"
#{
newUrl
}#{
(
if
newUrl
.
indexOf
(
'?'
)
>
0
then
'&'
else
'?'
)
}#{
paramName
}
=
#{
paramValue
}
"
newUrl
# removes parameter query string from url. returns the modified url
w
.
gl
.
utils
.
removeParamQueryString
=
(
url
,
param
)
->
url
=
decodeURIComponent
(
url
)
urlVariables
=
url
.
split
(
'&'
)
(
variables
for
variables
in
urlVariables
when
variables
.
indexOf
(
param
)
is
-
1
).
join
(
'&'
)
)
window
app/assets/javascripts/merge_requests.js.coffee
deleted
100644 → 0
View file @
4715ab91
#
# * Filter merge requests
#
@
MergeRequests
=
init
:
->
MergeRequests
.
initSearch
()
# Make sure we trigger ajax request only after user stop typing
initSearch
:
->
@
timer
=
null
$
(
"#issue_search"
).
keyup
->
clearTimeout
(
@
timer
)
@
timer
=
setTimeout
(
MergeRequests
.
filterResults
,
500
)
filterResults
:
=>
form
=
$
(
"#issue_search_form"
)
search
=
$
(
"#issue_search"
).
val
()
$
(
'.merge-requests-holder'
).
css
(
"opacity"
,
'0.5'
)
issues_url
=
form
.
attr
(
'action'
)
+
'?'
+
form
.
serialize
()
$
.
ajax
type
:
"GET"
url
:
form
.
attr
(
'action'
)
data
:
form
.
serialize
()
complete
:
->
$
(
'.merge-requests-holder'
).
css
(
"opacity"
,
'1.0'
)
success
:
(
data
)
->
$
(
'.merge-requests-holder'
).
html
(
data
.
html
)
# Change url so if user reload a page - search results are saved
history
.
replaceState
{
page
:
issues_url
},
document
.
title
,
issues_url
MergeRequests
.
reload
()
dataType
:
"json"
reload
:
->
$
(
'#filter_issue_search'
).
val
(
$
(
'#issue_search'
).
val
())
app/assets/javascripts/milestone_select.js.coffee
View file @
54851c01
...
...
@@ -97,7 +97,7 @@ class @MilestoneSelect
selectedMilestone
=
selected
.
name
else
selectedMilestone
=
''
Issu
es
.
filterResults
$dropdown
.
closest
(
'form'
)
Issu
able
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
$dropdown
.
closest
(
'form'
).
submit
()
else
...
...
app/assets/javascripts/users_select.js.coffee
View file @
54851c01
...
...
@@ -158,7 +158,7 @@ class @UsersSelect
if
$dropdown
.
hasClass
(
'js-filter-submit'
)
and
(
isIssueIndex
or
isMRIndex
)
selectedId
=
user
.
id
Issu
es
.
filterResults
$dropdown
.
closest
(
'form'
)
Issu
able
.
filterResults
$dropdown
.
closest
(
'form'
)
else
if
$dropdown
.
hasClass
'js-filter-submit'
$dropdown
.
closest
(
'form'
).
submit
()
else
...
...
app/controllers/projects/issues_controller.rb
View file @
54851c01
...
...
@@ -33,14 +33,15 @@ class Projects::IssuesController < Projects::ApplicationController
end
@issues
=
@issues
.
page
(
params
[
:page
])
@label
=
@project
.
labels
.
find_by
(
title:
params
[
:label_name
])
@label
s
=
@project
.
labels
.
where
(
title:
params
[
:label_name
])
respond_to
do
|
format
|
format
.
html
format
.
atom
{
render
layout:
false
}
format
.
json
do
render
json:
{
html:
view_to_html_string
(
"projects/issues/_issues"
)
html:
view_to_html_string
(
"projects/issues/_issues"
),
labels:
@labels
.
as_json
(
methods: :text_color
)
}
end
end
...
...
app/controllers/projects/merge_requests_controller.rb
View file @
54851c01
...
...
@@ -38,13 +38,14 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_requests
=
@merge_requests
.
page
(
params
[
:page
])
@merge_requests
=
@merge_requests
.
preload
(
:target_project
)
@label
=
@project
.
labels
.
find_by
(
title:
params
[
:label_name
])
@label
s
=
@project
.
labels
.
where
(
title:
params
[
:label_name
])
respond_to
do
|
format
|
format
.
html
format
.
json
do
render
json:
{
html:
view_to_html_string
(
"projects/merge_requests/_merge_requests"
)
html:
view_to_html_string
(
"projects/merge_requests/_merge_requests"
),
labels:
@labels
.
as_json
(
methods: :text_color
)
}
end
end
...
...
app/finders/issuable_finder.rb
View file @
54851c01
...
...
@@ -117,7 +117,7 @@ class IssuableFinder
end
def
filter_by_no_label?
labels?
&&
params
[
:label_name
]
==
Label
::
None
.
title
labels?
&&
params
[
:label_name
]
.
include?
(
Label
::
None
.
title
)
end
def
labels
...
...
@@ -278,7 +278,9 @@ class IssuableFinder
end
end
items
# When filtering by multiple labels we may end up duplicating issues (if one
# has multiple labels). This ensures we only return unique issues.
items
.
distinct
end
def
label_names
...
...
app/helpers/application_helper.rb
View file @
54851c01
...
...
@@ -254,11 +254,11 @@ module ApplicationHelper
def
page_filter_path
(
options
=
{})
without
=
options
.
delete
(
:without
)
add_label
=
options
.
delete
(
:label
)
exist_opts
=
{
state:
params
[
:state
],
scope:
params
[
:scope
],
label_name:
params
[
:label_name
],
milestone_title:
params
[
:milestone_title
],
assignee_id:
params
[
:assignee_id
],
author_id:
params
[
:author_id
],
...
...
@@ -275,6 +275,13 @@ module ApplicationHelper
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
end
...
...
app/helpers/issuables_helper.rb
View file @
54851c01
...
...
@@ -16,6 +16,25 @@ module IssuablesHelper
base_issuable_scope
(
issuable
).
where
(
'iid > ?'
,
issuable
.
iid
).
last
end
def
multi_label_name
(
current_labels
,
default_label
)
# current_labels may be a string from before
if
current_labels
.
is_a?
(
Array
)
if
current_labels
.
count
>
1
"
#{
current_labels
[
0
]
}
+
#{
current_labels
.
count
-
1
}
more"
else
current_labels
[
0
]
end
elsif
current_labels
.
is_a?
(
String
)
if
current_labels
.
nil?
||
current_labels
.
empty?
default_label
else
current_labels
end
else
default_label
end
end
def
issuable_json_path
(
issuable
)
project
=
issuable
.
project
...
...
app/models/label.rb
View file @
54851c01
...
...
@@ -113,6 +113,10 @@ class Label < ActiveRecord::Base
template
end
def
text_color
LabelsHelper
::
text_color_for_bg
(
self
.
color
)
end
private
def
label_format_reference
(
format
=
:id
)
...
...
app/views/shared/_label_row.html.haml
View file @
54851c01
...
...
@@ -2,4 +2,4 @@
%span
.label-name
=
link_to_label
(
label
,
tooltip:
false
)
%span
.prepend-left-10
=
markdown
(
label
.
description
,
pipeline: :single_line
)
=
markdown
(
label
.
description
,
pipeline: :single_line
)
\ No newline at end of file
app/views/shared/_labels_row.html.haml
0 → 100644
View file @
54851c01
-
labels
.
each
do
|
label
|
%span
.label-row
=
link_to_label
(
label
,
tooltip:
false
)
app/views/shared/issuable/_filter.html.haml
View file @
54851c01
...
...
@@ -46,9 +46,10 @@
.filter-item.inline
=
button_tag
"Update issues"
,
class:
"btn update_selected_issues btn-save"
-
if
@label
.gray-content-block.second-block
=
render
"shared/label_row"
,
label:
@label
-
if
!
@labels
.
nil?
.gray-content-block.second-block.filtered-labels
{
class:
(
"hidden"
if
!
@labels
.
any?
)
}
-
if
@labels
.
any?
=
render
"shared/labels_row"
,
labels:
@labels
:javascript
new
UsersSelect
();
...
...
app/views/shared/issuable/_label_dropdown.html.haml
View file @
54851c01
-
if
params
[
:label_name
].
present?
=
hidden_field_tag
(
:label_name
,
params
[
:label_name
])
-
if
params
[
:label_name
].
respond_to?
(
'any?'
)
-
params
[
:label_name
].
each
do
|
label
|
=
hidden_field_tag
"label_name[]"
,
label
,
id:
nil
.dropdown
%button
.dropdown-menu-toggle.js-label-select.js-filter-submit.js-
extra-options
{
type:
"button"
,
data:
{
toggle:
"dropdown"
,
field_name:
"label_name
"
,
show_no:
"true"
,
show_any:
"true"
,
selected:
params
[
:label_name
],
project_id:
@project
.
try
(
:id
),
labels:
labels_filter_path
,
default_label:
"Label"
}}
%button
.dropdown-menu-toggle.js-label-select.js-filter-submit.js-
multiselect.js-extra-options
{
type:
"button"
,
data:
{
toggle:
"dropdown"
,
field_name:
"label_name[]
"
,
show_no:
"true"
,
show_any:
"true"
,
selected:
params
[
:label_name
],
project_id:
@project
.
try
(
:id
),
labels:
labels_filter_path
,
default_label:
"Label"
}}
%span
.dropdown-toggle-text
=
h
(
params
[
:label_name
].
presence
||
"Label"
)
=
h
(
multi_label_name
(
params
[
:label_name
],
"Label"
)
)
=
icon
(
'chevron-down'
)
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
.dropdown-page-one
...
...
app/views/shared/issuable/_nav.html.haml
View file @
54851c01
...
...
@@ -4,22 +4,22 @@
-
else
-
page_context_word
=
'issues'
%li
{
class:
(
"active"
if
params
[
:state
]
==
'opened'
)}
=
link_to
page_filter_path
(
state:
'opened'
),
title:
"Filter by
#{
page_context_word
}
that are currently opened."
do
=
link_to
page_filter_path
(
state:
'opened'
,
label:
true
),
title:
"Filter by
#{
page_context_word
}
that are currently opened."
do
#{
state_filters_text_for
(
:opened
,
@project
)
}
-
if
defined?
(
type
)
&&
type
==
:merge_requests
%li
{
class:
(
"active"
if
params
[
:state
]
==
'merged'
)}
=
link_to
page_filter_path
(
state:
'merged'
),
title:
'Filter by merge requests that are currently merged.'
do
=
link_to
page_filter_path
(
state:
'merged'
,
label:
true
),
title:
'Filter by merge requests that are currently merged.'
do
#{
state_filters_text_for
(
:merged
,
@project
)
}
%li
{
class:
(
"active"
if
params
[
:state
]
==
'closed'
)}
=
link_to
page_filter_path
(
state:
'closed'
),
title:
'Filter by merge requests that are currently closed and unmerged.'
do
=
link_to
page_filter_path
(
state:
'closed'
,
label:
true
),
title:
'Filter by merge requests that are currently closed and unmerged.'
do
#{
state_filters_text_for
(
:closed
,
@project
)
}
-
else
%li
{
class:
(
"active"
if
params
[
:state
]
==
'closed'
)}
=
link_to
page_filter_path
(
state:
'closed'
),
title:
'Filter by issues that are currently closed.'
do
=
link_to
page_filter_path
(
state:
'closed'
,
label:
true
),
title:
'Filter by issues that are currently closed.'
do
#{
state_filters_text_for
(
:closed
,
@project
)
}
%li
{
class:
(
"active"
if
params
[
:state
]
==
'all'
)}
=
link_to
page_filter_path
(
state:
'all'
),
title:
"Show all
#{
page_context_word
}
."
do
=
link_to
page_filter_path
(
state:
'all'
,
label:
true
),
title:
"Show all
#{
page_context_word
}
."
do
#{
state_filters_text_for
(
:all
,
@project
)
}
features/project/issues/filter_labels.feature
View file @
54851c01
...
...
@@ -12,6 +12,7 @@ Feature: Project Issues Filter Labels
@javascript
Scenario
:
I
filter by one label
Given
I click link
"bug"
And
I click
"dropdown close button"
Then
I should see
"Bugfix1"
in issues list
And
I should see
"Bugfix2"
in issues list
And
I should not see
"Feature1"
in issues list
...
...
features/steps/project/issues/filter_labels.rb
View file @
54851c01
...
...
@@ -32,6 +32,10 @@ class Spinach::Features::ProjectIssuesFilterLabels < Spinach::FeatureSteps
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
bug
\"
) a').click()"
)
end
step
'I click "dropdown close button"'
do
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
...
...
spec/features/issues/filter_by_labels_spec.rb
0 → 100644
View file @
54851c01
require
'rails_helper'
feature
'Issue filtering by Labels'
,
feature:
true
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let!
(
:user
)
{
create
(
:user
)}
let!
(
:label
)
{
create
(
:label
,
project:
project
)
}
before
do
[
'bug'
,
'feature'
,
'enhancement'
].
each
do
|
title
|
create
(
:label
,
project:
project
,
title:
title
)
end
issue1
=
create
(
:issue
,
title:
"Bugfix1"
,
project:
project
)
issue1
.
labels
<<
project
.
labels
.
find_by
(
title:
'bug'
)
issue2
=
create
(
:issue
,
title:
"Bugfix2"
,
project:
project
)
issue2
.
labels
<<
project
.
labels
.
find_by
(
title:
'bug'
)
issue2
.
labels
<<
project
.
labels
.
find_by
(
title:
'enhancement'
)
issue3
=
create
(
:issue
,
title:
"Feature1"
,
project:
project
)
issue3
.
labels
<<
project
.
labels
.
find_by
(
title:
'feature'
)
project
.
team
<<
[
user
,
:master
]
login_as
(
user
)
visit
namespace_project_issues_path
(
project
.
namespace
,
project
)
end
context
'filter by label bug'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
bug
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix1" and "Bugfix2" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix1"
expect
(
page
).
to
have_content
"Bugfix2"
end
it
'should not show "Feature1" in issues list'
do
expect
(
page
).
not_to
have_content
"Feature1"
end
it
'should show label "bug" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"bug"
end
it
'should not show label "feature" and "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"feature"
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"enhancement"
end
end
context
'filter by label feature'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
feature
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Feature1" in issues list'
do
expect
(
page
).
to
have_content
"Feature1"
end
it
'should not show "Bugfix1" and "Bugfix2" in issues list'
do
expect
(
page
).
not_to
have_content
"Bugfix2"
expect
(
page
).
not_to
have_content
"Bugfix1"
end
it
'should show label "feature" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"feature"
end
it
'should not show label "bug" and "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"bug"
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"enhancement"
end
end
context
'filter by label enhancement'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
enhancement
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix2" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix2"
end
it
'should not show "Feature1" and "Bugfix1" in issues list'
do
expect
(
page
).
not_to
have_content
"Feature1"
expect
(
page
).
not_to
have_content
"Bugfix1"
end
it
'should show label "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"enhancement"
end
it
'should not show label "feature" and "bug" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"bug"
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"feature"
end
end
context
'filter by label enhancement or feature'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
enhancement
\"
) a').click()"
)
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
feature
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix2" or "Feature1" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix2"
expect
(
page
).
to
have_content
"Feature1"
end
it
'should not show "Bugfix1" in issues list'
do
expect
(
page
).
not_to
have_content
"Bugfix1"
end
it
'should show label "enhancement" and "feature" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"enhancement"
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"feature"
end
it
'should not show label "bug" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"bug"
end
end
context
'filter by label enhancement or bug in issues list'
,
js:
true
do
before
do
page
.
find
(
'.js-label-select'
).
click
sleep
0.5
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
enhancement
\"
) a').click()"
)
execute_script
(
"$('.dropdown-menu-labels li:contains(
\"
bug
\"
) a').click()"
)
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
it
'should show issue "Bugfix2" or "Bugfix1" in issues list'
do
expect
(
page
).
to
have_content
"Bugfix2"
expect
(
page
).
to
have_content
"Bugfix1"
end
it
'should not show "Feature1"'
do
expect
(
page
).
not_to
have_content
"Feature1"
end
it
'should show label "bug" and "enhancement" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"bug"
expect
(
find
(
'.filtered-labels'
)).
to
have_content
"enhancement"
end
it
'should not show label "feature" in filtered-labels'
do
expect
(
find
(
'.filtered-labels'
)).
not_to
have_content
"feature"
end
end
end
spec/features/issues/filter_issues_spec.rb
View file @
54851c01
...
...
@@ -84,14 +84,20 @@ describe 'Filter issues', feature: true do
it
'should filter by any label'
do
find
(
'.dropdown-menu-labels a'
,
text:
'Any Label'
).
click
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
page
.
within
'.labels-filter'
do
expect
(
page
).
to
have_content
'Any Label'
end
expect
(
find
(
'.js-label-select .dropdown-toggle-text'
)).
to
have_content
(
'Label'
)
expect
(
find
(
'.js-label-select .dropdown-toggle-text'
)).
to
have_content
(
'
Any
Label'
)
end
it
'should filter by no label'
do
find
(
'.dropdown-menu-labels a'
,
text:
'No Label'
).
click
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
page
.
within
'.labels-filter'
do
expect
(
page
).
to
have_content
'No Label'
end
...
...
@@ -121,6 +127,7 @@ describe 'Filter issues', feature: true do
find
(
'.js-label-select'
).
click
find
(
'.dropdown-menu-labels .dropdown-content a'
,
text:
label
.
title
).
click
page
.
first
(
'.labels-filter .dropdown-title .dropdown-menu-close-icon'
).
click
sleep
2
end
...
...
spec/features/merge_requests/filter_by_milestone_spec.rb
View file @
54851c01
...
...
@@ -2,8 +2,14 @@ require 'rails_helper'
feature
'Merge Request filtering by Milestone'
,
feature:
true
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let!
(
:user
)
{
create
(
:user
)}
let
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
before
do
project
.
team
<<
[
user
,
:master
]
login_as
(
user
)
end
scenario
'filters by no Milestone'
,
js:
true
do
create
(
:merge_request
,
:with_diffs
,
source_project:
project
)
create
(
:merge_request
,
:simple
,
source_project:
project
,
milestone:
milestone
)
...
...
spec/finders/issues_finder_spec.rb
View file @
54851c01
...
...
@@ -62,6 +62,22 @@ describe IssuesFinder do
expect
(
issues
).
to
eq
([
issue2
])
end
it
'returns unique issues when filtering by multiple labels'
do
label2
=
create
(
:label
,
project:
project2
)
create
(
:label_link
,
label:
label2
,
target:
issue2
)
params
=
{
scope:
'all'
,
label_name:
[
label
.
title
,
label2
.
title
].
join
(
','
),
state:
'opened'
}
issues
=
IssuesFinder
.
new
(
user
,
params
).
execute
expect
(
issues
).
to
eq
([
issue2
])
end
it
'should filter by no label name'
do
params
=
{
scope:
"all"
,
label_name:
Label
::
None
.
title
,
state:
'opened'
}
issues
=
IssuesFinder
.
new
(
user
,
params
).
execute
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment