Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
heptapod
heptapod
Commits
09d56e82c574
Commit
c73f0c48
authored
Aug 02, 2017
by
Phil Hughes
Browse files
Merge branch 'add-filtered-search-group-issues-ce' into 'master'
Add filtered search to group issue dashboard Closes #33575 See merge request !13167
parents
cf146ca79fff
6e698a998797
Changes
19
Hide whitespace changes
Inline
Side-by-side
app/assets/javascripts/dispatcher.js
View file @
09d56e82
...
...
@@ -139,6 +139,8 @@ import GpgBadges from './gpg_badges';
.
init
();
}
const
filteredSearchEnabled
=
gl
.
FilteredSearchManager
&&
document
.
querySelector
(
'
.filtered-search
'
);
switch
(
page
)
{
case
'
profiles:preferences:show
'
:
initExperimentalFlags
();
...
...
@@ -155,7 +157,7 @@ import GpgBadges from './gpg_badges';
break
;
case
'
projects:merge_requests:index
'
:
case
'
projects:issues:index
'
:
if
(
gl
.
F
ilteredSearch
Manager
&&
document
.
querySelector
(
'
.filtered-search
'
)
)
{
if
(
f
ilteredSearch
Enabled
)
{
const
filteredSearchManager
=
new
gl
.
FilteredSearchManager
(
page
===
'
projects:issues:index
'
?
'
issues
'
:
'
merge_requests
'
);
filteredSearchManager
.
setup
();
}
...
...
@@ -183,11 +185,17 @@ import GpgBadges from './gpg_badges';
break
;
case
'
dashboard:issues
'
:
case
'
dashboard:merge_requests
'
:
case
'
groups:issues
'
:
case
'
groups:merge_requests
'
:
new
ProjectSelect
();
initLegacyFilters
();
break
;
case
'
groups:issues
'
:
if
(
filteredSearchEnabled
)
{
const
filteredSearchManager
=
new
gl
.
FilteredSearchManager
(
'
issues
'
);
filteredSearchManager
.
setup
();
}
new
ProjectSelect
();
break
;
case
'
dashboard:todos:index
'
:
new
Todos
();
break
;
...
...
app/assets/javascripts/droplab/plugins/ajax.js
View file @
09d56e82
...
...
@@ -11,6 +11,16 @@ const Ajax = {
if
(
!
self
.
destroyed
)
self
.
hook
.
list
[
config
.
method
].
call
(
self
.
hook
.
list
,
data
);
},
preprocessing
:
function
preprocessing
(
config
,
data
)
{
let
results
=
data
;
if
(
config
.
preprocessing
&&
!
data
.
preprocessed
)
{
results
=
config
.
preprocessing
(
data
);
AjaxCache
.
override
(
config
.
endpoint
,
results
);
}
return
results
;
},
init
:
function
init
(
hook
)
{
var
self
=
this
;
self
.
destroyed
=
false
;
...
...
@@ -31,7 +41,8 @@ const Ajax = {
dynamicList
.
outerHTML
=
loadingTemplate
.
outerHTML
;
}
AjaxCache
.
retrieve
(
config
.
endpoint
)
return
AjaxCache
.
retrieve
(
config
.
endpoint
)
.
then
(
self
.
preprocessing
.
bind
(
null
,
config
))
.
then
((
data
)
=>
self
.
_loadData
(
data
,
config
,
self
))
.
catch
(
config
.
onError
);
},
...
...
app/assets/javascripts/filtered_search/dropdown_non_user.js
View file @
09d56e82
...
...
@@ -6,7 +6,7 @@ import './filtered_search_dropdown';
class
DropdownNonUser
extends
gl
.
FilteredSearchDropdown
{
constructor
(
options
=
{})
{
const
{
input
,
endpoint
,
symbol
}
=
options
;
const
{
input
,
endpoint
,
symbol
,
preprocessing
}
=
options
;
super
(
options
);
this
.
symbol
=
symbol
;
this
.
config
=
{
...
...
@@ -14,6 +14,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
endpoint
,
method
:
'
setData
'
,
loadingTemplate
:
this
.
loadingTemplate
,
preprocessing
,
onError
()
{
/* eslint-disable no-new */
new
Flash
(
'
An error occured fetching the dropdown data.
'
);
...
...
app/assets/javascripts/filtered_search/dropdown_utils.js
View file @
09d56e82
...
...
@@ -50,6 +50,66 @@ class DropdownUtils {
return
updatedItem
;
}
static
mergeDuplicateLabels
(
dataMap
,
newLabel
)
{
const
updatedMap
=
dataMap
;
const
key
=
newLabel
.
title
;
const
hasKeyProperty
=
Object
.
prototype
.
hasOwnProperty
.
call
(
updatedMap
,
key
);
if
(
!
hasKeyProperty
)
{
updatedMap
[
key
]
=
newLabel
;
}
else
{
const
existing
=
updatedMap
[
key
];
if
(
!
existing
.
multipleColors
)
{
existing
.
multipleColors
=
[
existing
.
color
];
}
existing
.
multipleColors
.
push
(
newLabel
.
color
);
}
return
updatedMap
;
}
static
duplicateLabelColor
(
labelColors
)
{
const
colors
=
labelColors
;
const
spacing
=
100
/
colors
.
length
;
// Reduce the colors to 4
colors
.
length
=
Math
.
min
(
colors
.
length
,
4
);
const
color
=
colors
.
map
((
c
,
i
)
=>
{
const
percentFirst
=
Math
.
floor
(
spacing
*
i
);
const
percentSecond
=
Math
.
floor
(
spacing
*
(
i
+
1
));
return
`
${
c
}
${
percentFirst
}
%,
${
c
}
${
percentSecond
}
%`
;
}).
join
(
'
,
'
);
return
`linear-gradient(
${
color
}
)`
;
}
static
duplicateLabelPreprocessing
(
data
)
{
const
results
=
[];
const
dataMap
=
{};
data
.
forEach
(
DropdownUtils
.
mergeDuplicateLabels
.
bind
(
null
,
dataMap
));
Object
.
keys
(
dataMap
)
.
forEach
((
key
)
=>
{
const
label
=
dataMap
[
key
];
if
(
label
.
multipleColors
)
{
label
.
color
=
DropdownUtils
.
duplicateLabelColor
(
label
.
multipleColors
);
label
.
text_color
=
'
#000000
'
;
}
results
.
push
(
label
);
});
results
.
preprocessed
=
true
;
return
results
;
}
static
filterHint
(
config
,
item
)
{
const
{
input
,
allowedKeys
}
=
config
;
const
updatedItem
=
item
;
...
...
app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
View file @
09d56e82
...
...
@@ -54,6 +54,7 @@ class FilteredSearchDropdownManager {
extraArguments
:
{
endpoint
:
`
${
this
.
baseEndpoint
}
/labels.json`
,
symbol
:
'
~
'
,
preprocessing
:
gl
.
DropdownUtils
.
duplicateLabelPreprocessing
,
},
element
:
this
.
container
.
querySelector
(
'
#js-dropdown-label
'
),
},
...
...
app/assets/javascripts/filtered_search/filtered_search_manager.js
View file @
09d56e82
...
...
@@ -20,13 +20,13 @@ class FilteredSearchManager {
allowedKeys
:
this
.
filteredSearchTokenKeys
.
getKeys
(),
});
this
.
searchHistoryDropdownElement
=
document
.
querySelector
(
'
.js-filtered-search-history-dropdown
'
);
const
project
Path
=
this
.
searchHistoryDropdownElement
?
this
.
searchHistoryDropdownElement
.
dataset
.
projectF
ullPath
:
'
project
'
;
const
full
Path
=
this
.
searchHistoryDropdownElement
?
this
.
searchHistoryDropdownElement
.
dataset
.
f
ullPath
:
'
project
'
;
let
recentSearchesPagePrefix
=
'
issue-recent-searches
'
;
if
(
this
.
page
===
'
merge_requests
'
)
{
recentSearchesPagePrefix
=
'
merge-request-recent-searches
'
;
}
const
recentSearchesKey
=
`
${
project
Path
}
-
${
recentSearchesPagePrefix
}
`
;
const
recentSearchesKey
=
`
${
full
Path
}
-
${
recentSearchesPagePrefix
}
`
;
this
.
recentSearchesService
=
new
RecentSearchesService
(
recentSearchesKey
);
}
...
...
app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
View file @
09d56e82
...
...
@@ -58,29 +58,54 @@ class FilteredSearchVisualTokens {
`
;
}
static
setTokenStyle
(
tokenContainer
,
backgroundColor
,
textColor
)
{
const
token
=
tokenContainer
;
// Labels with linear gradient should not override default background color
if
(
backgroundColor
.
indexOf
(
'
linear-gradient
'
)
===
-
1
)
{
token
.
style
.
backgroundColor
=
backgroundColor
;
}
token
.
style
.
color
=
textColor
;
if
(
textColor
===
'
#FFFFFF
'
)
{
const
removeToken
=
token
.
querySelector
(
'
.remove-token
'
);
removeToken
.
classList
.
add
(
'
inverted
'
);
}
return
token
;
}
static
preprocessLabel
(
labelsEndpoint
,
labels
)
{
let
processed
=
labels
;
if
(
!
labels
.
preprocessed
)
{
processed
=
gl
.
DropdownUtils
.
duplicateLabelPreprocessing
(
labels
);
AjaxCache
.
override
(
labelsEndpoint
,
processed
);
processed
.
preprocessed
=
true
;
}
return
processed
;
}
static
updateLabelTokenColor
(
tokenValueContainer
,
tokenValue
)
{
const
filteredSearchInput
=
FilteredSearchContainer
.
container
.
querySelector
(
'
.filtered-search
'
);
const
baseEndpoint
=
filteredSearchInput
.
dataset
.
baseEndpoint
;
const
labelsEndpoint
=
`
${
baseEndpoint
}
/labels.json`
;
return
AjaxCache
.
retrieve
(
labelsEndpoint
)
.
then
((
labels
)
=>
{
const
matchingLabel
=
(
labels
||
[]).
find
(
label
=>
`~
${
gl
.
DropdownUtils
.
getEscapedText
(
label
.
title
)}
`
===
tokenValue
);
if
(
!
matchingLabel
)
{
return
;
}
.
then
(
FilteredSearchVisualTokens
.
preprocessLabel
.
bind
(
null
,
labelsEndpoint
))
.
then
((
labels
)
=>
{
const
matchingLabel
=
(
labels
||
[]).
find
(
label
=>
`~
${
gl
.
DropdownUtils
.
getEscapedText
(
label
.
title
)}
`
===
tokenValue
);
const
tokenValueStyle
=
tokenValueContainer
.
style
;
tokenValueStyle
.
backgroundColor
=
matchingLabel
.
color
;
tokenValueStyle
.
color
=
matchingLabel
.
text_color
;
if
(
!
matchingLabel
)
{
return
;
}
if
(
matchingLabel
.
text_color
===
'
#FFFFFF
'
)
{
const
removeToken
=
tokenValueContainer
.
querySelector
(
'
.remove-token
'
);
removeToken
.
classList
.
add
(
'
inverted
'
);
}
})
.
catch
(()
=>
new
Flash
(
'
An error occurred while fetching label colors.
'
));
FilteredSearchVisualTokens
.
setTokenStyle
(
tokenValueContainer
,
matchingLabel
.
color
,
matchingLabel
.
text_color
);
})
.
catch
(()
=>
new
Flash
(
'
An error occurred while fetching label colors.
'
));
}
static
updateUserTokenAppearance
(
tokenValueContainer
,
tokenValueElement
,
tokenValue
)
{
...
...
app/assets/javascripts/labels_select.js
View file @
09d56e82
...
...
@@ -3,6 +3,7 @@
/* global ListLabel */
import
IssuableBulkUpdateActions
from
'
./issuable_bulk_update_actions
'
;
import
DropdownUtils
from
'
./filtered_search/dropdown_utils
'
;
(
function
()
{
this
.
LabelsSelect
=
(
function
()
{
...
...
@@ -218,18 +219,7 @@ import IssuableBulkUpdateActions from './issuable_bulk_update_actions';
}
}
if
(
label
.
duplicate
)
{
spacing
=
100
/
label
.
color
.
length
;
// Reduce the colors to 4
label
.
color
=
label
.
color
.
filter
(
function
(
color
,
i
)
{
return
i
<
4
;
});
color
=
_
.
map
(
label
.
color
,
function
(
color
,
i
)
{
var
percentFirst
,
percentSecond
;
percentFirst
=
Math
.
floor
(
spacing
*
i
);
percentSecond
=
Math
.
floor
(
spacing
*
(
i
+
1
));
return
color
+
"
"
+
percentFirst
+
"
%,
"
+
color
+
"
"
+
percentSecond
+
"
%
"
;
}).
join
(
'
,
'
);
color
=
"
linear-gradient(
"
+
color
+
"
)
"
;
color
=
gl
.
DropdownUtils
.
duplicateLabelColor
(
label
.
color
);
}
else
{
if
(
label
.
color
!=
null
)
{
...
...
app/assets/javascripts/lib/utils/ajax_cache.js
View file @
09d56e82
...
...
@@ -6,6 +6,10 @@ class AjaxCache extends Cache {
this
.
pendingRequests
=
{
};
}
override
(
endpoint
,
data
)
{
this
.
internalStorage
[
endpoint
]
=
data
;
}
retrieve
(
endpoint
,
forceRetrieve
)
{
if
(
this
.
hasData
(
endpoint
)
&&
!
forceRetrieve
)
{
return
Promise
.
resolve
(
this
.
get
(
endpoint
));
...
...
app/helpers/search_helper.rb
View file @
09d56e82
...
...
@@ -127,15 +127,23 @@ def search_filter_path(options = {})
end
def
search_filter_input_options
(
type
)
{
opts
=
{
id:
"filtered-search-
#{
type
}
"
,
placeholder:
'Search or filter results...'
,
data:
{
'project-id'
=>
@project
.
id
,
'username-params'
=>
@users
.
to_json
(
only:
[
:id
,
:username
]),
'base-endpoint'
=>
project_path
(
@project
)
'username-params'
=>
@users
.
to_json
(
only:
[
:id
,
:username
])
}
}
if
@project
.
present?
opts
[
:data
][
'project-id'
]
=
@project
.
id
opts
[
:data
][
'base-endpoint'
]
=
project_path
(
@project
)
else
# Group context
opts
[
:data
][
'base-endpoint'
]
=
group_canonical_path
(
@group
)
end
opts
end
# Sanitize a HTML field for search display. Most tags are stripped out and the
...
...
app/views/groups/issues.html.haml
View file @
09d56e82
...
...
@@ -4,6 +4,10 @@
=
content_for
:meta_tags
do
=
auto_discovery_link_tag
(
:atom
,
params
.
merge
(
rss_url_options
),
title:
"
#{
@group
.
name
}
issues"
)
-
content_for
:page_specific_javascripts
do
=
webpack_bundle_tag
'common_vue'
=
webpack_bundle_tag
'filtered_search'
-
if
show_new_nav?
&&
group_issues_exists
-
content_for
:breadcrumbs_extra
do
=
link_to
params
.
merge
(
rss_url_options
),
class:
'btn btn-default append-right-10'
do
...
...
@@ -20,7 +24,7 @@
Subscribe
=
render
'shared/new_project_item_select'
,
path:
'issues/new'
,
label:
"New issue"
=
render
'shared/issuable/
filte
r'
,
type: :issues
=
render
'shared/issuable/
search_ba
r'
,
type: :issues
.row-content-block.second-block
Only issues from the
...
...
app/views/shared/issuable/_search_bar.html.haml
View file @
09d56e82
-
type
=
local_assigns
.
fetch
(
:type
)
-
block_css_class
=
type
!=
:boards_modal
?
'row-content-block second-block'
:
''
-
full_path
=
@project
.
present?
?
@project
.
full_path
:
@group
.
full_path
.issues-filters
.issues-details-filters.filtered-search-block
{
class:
block_css_class
,
"v-pre"
=>
type
==
:boards_modal
}
...
...
@@ -18,7 +19,7 @@
dropdown_class:
"filtered-search-history-dropdown"
,
content_class:
"filtered-search-history-dropdown-content"
,
title:
"Recent searches"
})
do
.js-filtered-search-history-dropdown
{
data:
{
project_
full_path:
@project
.
full_path
}
}
.js-filtered-search-history-dropdown
{
data:
{
full_path:
full_path
}
}
.filtered-search-box-input-container.droplab-dropdown
.scroll-container
%ul
.tokens-container.list-unstyled
...
...
changelogs/unreleased/add-filtered-search-group-issues-ce.yml
0 → 100644
View file @
09d56e82
---
title
:
Add filtered search to group issue dashboard
merge_request
:
author
:
spec/features/groups/issues_spec.rb
View file @
09d56e82
require
'spec_helper'
feature
'Group issues page'
do
include
FilteredSearchHelpers
let
(
:path
)
{
issues_group_path
(
group
)
}
let
(
:issuable
)
{
create
(
:issue
,
project:
project
,
title:
"this is my created issuable"
)}
...
...
@@ -31,12 +33,10 @@
let
(
:path
)
{
issues_group_path
(
group
)
}
it
'filters by only group users'
do
click_button
(
'Assignee'
)
wait_for_requests
filtered_search
.
set
(
'assignee:'
)
expect
(
find
(
'
.
dropdown-
menu-
assignee'
)).
to
have_
link
(
user
.
name
)
expect
(
find
(
'
.
dropdown-
menu-
assignee'
)).
not_to
have_
link
(
user2
.
name
)
expect
(
find
(
'
#js-
dropdown-assignee
.filter-dropdown
'
)).
to
have_
content
(
user
.
name
)
expect
(
find
(
'
#js-
dropdown-assignee
.filter-dropdown
'
)).
not_to
have_
content
(
user2
.
name
)
end
end
end
spec/helpers/search_helper_spec.rb
View file @
09d56e82
...
...
@@ -68,4 +68,38 @@ def simple_sanitize(str)
end
end
end
describe
'search_filter_input_options'
do
context
'project'
do
before
do
@project
=
create
(
:project
,
:repository
)
end
it
'includes id with type'
do
expect
(
search_filter_input_options
(
'type'
)[
:id
]).
to
eq
(
'filtered-search-type'
)
end
it
'includes project-id'
do
expect
(
search_filter_input_options
(
''
)[
:data
][
'project-id'
]).
to
eq
(
@project
.
id
)
end
it
'includes project base-endpoint'
do
expect
(
search_filter_input_options
(
''
)[
:data
][
'base-endpoint'
]).
to
eq
(
project_path
(
@project
))
end
end
context
'group'
do
before
do
@group
=
create
(
:group
,
name:
'group'
)
end
it
'does not includes project-id'
do
expect
(
search_filter_input_options
(
''
)[
:data
][
'project-id'
]).
to
eq
(
nil
)
end
it
'includes group base-endpoint'
do
expect
(
search_filter_input_options
(
''
)[
:data
][
'base-endpoint'
]).
to
eq
(
"/groups
#{
group_path
(
@group
)
}
"
)
end
end
end
end
spec/javascripts/droplab/plugins/ajax_spec.js
0 → 100644
View file @
09d56e82
import
AjaxCache
from
'
~/lib/utils/ajax_cache
'
;
import
Ajax
from
'
~/droplab/plugins/ajax
'
;
describe
(
'
Ajax
'
,
()
=>
{
describe
(
'
preprocessing
'
,
()
=>
{
const
config
=
{};
describe
(
'
is not configured
'
,
()
=>
{
it
(
'
passes the data through
'
,
()
=>
{
const
data
=
[
'
data
'
];
expect
(
Ajax
.
preprocessing
(
config
,
data
)).
toEqual
(
data
);
});
});
describe
(
'
is configured
'
,
()
=>
{
const
processedArray
=
[
'
processed
'
];
beforeEach
(()
=>
{
config
.
preprocessing
=
()
=>
processedArray
;
spyOn
(
config
,
'
preprocessing
'
).
and
.
callFake
(()
=>
processedArray
);
});
it
(
'
calls preprocessing
'
,
()
=>
{
Ajax
.
preprocessing
(
config
,
[]);
expect
(
config
.
preprocessing
.
calls
.
count
()).
toBe
(
1
);
});
it
(
'
overrides AjaxCache
'
,
()
=>
{
spyOn
(
AjaxCache
,
'
override
'
).
and
.
callFake
((
endpoint
,
results
)
=>
expect
(
results
).
toEqual
(
processedArray
));
Ajax
.
preprocessing
(
config
,
[]);
expect
(
AjaxCache
.
override
.
calls
.
count
()).
toBe
(
1
);
});
});
});
});
spec/javascripts/filtered_search/dropdown_utils_spec.js
View file @
09d56e82
...
...
@@ -191,6 +191,102 @@ describe('Dropdown Utils', () => {
});
});
describe
(
'
mergeDuplicateLabels
'
,
()
=>
{
const
dataMap
=
{
label
:
{
title
:
'
label
'
,
color
:
'
#FFFFFF
'
,
},
};
it
(
'
should add label to dataMap if it is not a duplicate
'
,
()
=>
{
const
newLabel
=
{
title
:
'
new-label
'
,
color
:
'
#000000
'
,
};
const
updated
=
gl
.
DropdownUtils
.
mergeDuplicateLabels
(
dataMap
,
newLabel
);
expect
(
updated
[
newLabel
.
title
]).
toEqual
(
newLabel
);
});
it
(
'
should merge colors if label is a duplicate
'
,
()
=>
{
const
duplicate
=
{
title
:
'
label
'
,
color
:
'
#000000
'
,
};
const
updated
=
gl
.
DropdownUtils
.
mergeDuplicateLabels
(
dataMap
,
duplicate
);
expect
(
updated
.
label
.
multipleColors
).
toEqual
([
dataMap
.
label
.
color
,
duplicate
.
color
]);
});
});
describe
(
'
duplicateLabelColor
'
,
()
=>
{
it
(
'
should linear-gradient 2 colors
'
,
()
=>
{
const
gradient
=
gl
.
DropdownUtils
.
duplicateLabelColor
([
'
#FFFFFF
'
,
'
#000000
'
]);
expect
(
gradient
).
toEqual
(
'
linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)
'
);
});
it
(
'
should linear-gradient 3 colors
'
,
()
=>
{
const
gradient
=
gl
.
DropdownUtils
.
duplicateLabelColor
([
'
#FFFFFF
'
,
'
#000000
'
,
'
#333333
'
]);
expect
(
gradient
).
toEqual
(
'
linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)
'
);
});
it
(
'
should linear-gradient 4 colors
'
,
()
=>
{
const
gradient
=
gl
.
DropdownUtils
.
duplicateLabelColor
([
'
#FFFFFF
'
,
'
#000000
'
,
'
#333333
'
,
'
#DDDDDD
'
]);
expect
(
gradient
).
toEqual
(
'
linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)
'
);
});
it
(
'
should not linear-gradient more than 4 colors
'
,
()
=>
{
const
gradient
=
gl
.
DropdownUtils
.
duplicateLabelColor
([
'
#FFFFFF
'
,
'
#000000
'
,
'
#333333
'
,
'
#DDDDDD
'
,
'
#EEEEEE
'
]);
expect
(
gradient
.
indexOf
(
'
#EEEEEE
'
)
===
-
1
).
toEqual
(
true
);
});
});
describe
(
'
duplicateLabelPreprocessing
'
,
()
=>
{
it
(
'
should set preprocessed to true
'
,
()
=>
{
const
results
=
gl
.
DropdownUtils
.
duplicateLabelPreprocessing
([]);
expect
(
results
.
preprocessed
).
toEqual
(
true
);
});
it
(
'
should not mutate existing data if there are no duplicates
'
,
()
=>
{
const
data
=
[{
title
:
'
label1
'
,
color
:
'
#FFFFFF
'
,
},
{
title
:
'
label2
'
,
color
:
'
#000000
'
,
}];
const
results
=
gl
.
DropdownUtils
.
duplicateLabelPreprocessing
(
data
);
expect
(
results
.
length
).
toEqual
(
2
);
expect
(
results
[
0
]).
toEqual
(
data
[
0
]);
expect
(
results
[
1
]).
toEqual
(
data
[
1
]);
});
describe
(
'
duplicate labels
'
,
()
=>
{
const
data
=
[{
title
:
'
label
'
,
color
:
'
#FFFFFF
'
,
},
{
title
:
'
label
'
,
color
:
'
#000000
'
,
}];
const
results
=
gl
.
DropdownUtils
.
duplicateLabelPreprocessing
(
data
);
it
(
'
should merge duplicate labels
'
,
()
=>
{
expect
(
results
.
length
).
toEqual
(
1
);
});
it
(
'
should convert multiple colored labels into linear-gradient
'
,
()
=>
{
expect
(
results
[
0
].
color
).
toEqual
(
gl
.
DropdownUtils
.
duplicateLabelColor
([
'
#FFFFFF
'
,
'
#000000
'
]));
});
it
(
'
should set multiple colored label text color to black
'
,
()
=>
{
expect
(
results
[
0
].
text_color
).
toEqual
(
'
#000000
'
);
});
});
});
describe
(
'
setDataValueIfSelected
'
,
()
=>
{
beforeEach
(()
=>
{
spyOn
(
gl
.
FilteredSearchDropdownManager
,
'
addWordToInput
'
)
...
...
spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
View file @
09d56e82
...
...
@@ -797,6 +797,69 @@ describe('Filtered Search Visual Tokens', () => {
});
});
describe
(
'
setTokenStyle
'
,
()
=>
{
let
originalTextColor
;
beforeEach
(()
=>
{
originalTextColor
=
bugLabelToken
.
style
.
color
;
});
it
(
'
should set backgroundColor
'
,
()
=>
{
const
originalBackgroundColor
=
bugLabelToken
.
style
.
backgroundColor
;
const
token
=
subject
.
setTokenStyle
(
bugLabelToken
,
'
blue
'
,
'
white
'
);
expect
(
token
.
style
.
backgroundColor
).
toEqual
(
'
blue
'
);