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
1
Merge Requests
1
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
nexedi
gitlab-ce
Commits
dde689ef
Commit
dde689ef
authored
Aug 02, 2017
by
Clement Ho
Committed by
Phil Hughes
Aug 02, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add filtered search to group issue dashboard (EE)
parent
16c0b6e1
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
400 additions
and
46 deletions
+400
-46
app/assets/javascripts/dispatcher.js
app/assets/javascripts/dispatcher.js
+10
-2
app/assets/javascripts/droplab/plugins/ajax.js
app/assets/javascripts/droplab/plugins/ajax.js
+12
-1
app/assets/javascripts/filtered_search/dropdown_non_user.js
app/assets/javascripts/filtered_search/dropdown_non_user.js
+2
-1
app/assets/javascripts/filtered_search/dropdown_utils.js
app/assets/javascripts/filtered_search/dropdown_utils.js
+60
-0
app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
...ripts/filtered_search/filtered_search_dropdown_manager.js
+1
-0
app/assets/javascripts/filtered_search/filtered_search_manager.js
...ts/javascripts/filtered_search/filtered_search_manager.js
+3
-3
app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
...ascripts/filtered_search/filtered_search_visual_tokens.js
+40
-15
app/assets/javascripts/labels_select.js
app/assets/javascripts/labels_select.js
+2
-12
app/assets/javascripts/lib/utils/ajax_cache.js
app/assets/javascripts/lib/utils/ajax_cache.js
+4
-0
app/helpers/ee/search_helper.rb
app/helpers/ee/search_helper.rb
+3
-1
app/helpers/search_helper.rb
app/helpers/search_helper.rb
+12
-4
app/views/groups/issues.html.haml
app/views/groups/issues.html.haml
+6
-1
app/views/shared/issuable/_search_bar.html.haml
app/views/shared/issuable/_search_bar.html.haml
+2
-1
spec/features/groups/issues_spec.rb
spec/features/groups/issues_spec.rb
+5
-5
spec/helpers/search_helper_spec.rb
spec/helpers/search_helper_spec.rb
+34
-0
spec/javascripts/droplab/plugins/ajax_spec.js
spec/javascripts/droplab/plugins/ajax_spec.js
+36
-0
spec/javascripts/filtered_search/dropdown_utils_spec.js
spec/javascripts/filtered_search/dropdown_utils_spec.js
+96
-0
spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
...pts/filtered_search/filtered_search_visual_tokens_spec.js
+63
-0
spec/javascripts/lib/utils/ajax_cache_spec.js
spec/javascripts/lib/utils/ajax_cache_spec.js
+9
-0
No files found.
app/assets/javascripts/dispatcher.js
View file @
dde689ef
...
...
@@ -143,6 +143,8 @@ import AuditLogs from './audit_logs';
.
init
();
}
const
filteredSearchEnabled
=
gl
.
FilteredSearchManager
&&
document
.
querySelector
(
'
.filtered-search
'
);
switch
(
page
)
{
case
'
profiles:preferences:show
'
:
initExperimentalFlags
();
...
...
@@ -159,7 +161,7 @@ import AuditLogs from './audit_logs';
break
;
case
'
projects:merge_requests:index
'
:
case
'
projects:issues:index
'
:
if
(
gl
.
FilteredSearchManager
&&
document
.
querySelector
(
'
.filtered-search
'
)
)
{
if
(
filteredSearchEnabled
)
{
const
filteredSearchManager
=
new
gl
.
FilteredSearchManager
(
page
===
'
projects:issues:index
'
?
'
issues
'
:
'
merge_requests
'
);
filteredSearchManager
.
setup
();
}
...
...
@@ -187,11 +189,17 @@ import AuditLogs from './audit_logs';
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 @
dde689ef
...
...
@@ -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 @
dde689ef
...
...
@@ -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 @
dde689ef
...
...
@@ -75,6 +75,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
setDataValueIfSelected
(
filter
,
selected
)
{
const
dataValue
=
selected
.
getAttribute
(
'
data-value
'
);
...
...
app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
View file @
dde689ef
...
...
@@ -58,6 +58,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 @
dde689ef
...
...
@@ -28,13 +28,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 @
dde689ef
...
...
@@ -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 @
dde689ef
...
...
@@ -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 @
dde689ef
...
...
@@ -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/ee/search_helper.rb
View file @
dde689ef
...
...
@@ -10,8 +10,10 @@ module EE
private
def
search_multiple_assignees?
(
type
)
context
=
@project
.
presence
||
@group
type
==
:issues
&&
@projec
t
.
feature_available?
(
:multiple_issue_assignees
)
contex
t
.
feature_available?
(
:multiple_issue_assignees
)
end
end
end
app/helpers/search_helper.rb
View file @
dde689ef
...
...
@@ -137,15 +137,23 @@ module SearchHelper
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 @
dde689ef
...
...
@@ -4,6 +4,11 @@
=
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'
=
webpack_bundle_tag
'issues'
-
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 +25,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 @
dde689ef
-
type
=
local_assigns
.
fetch
(
:type
)
-
board
=
local_assigns
.
fetch
(
:board
,
nil
)
-
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
}
...
...
@@ -22,7 +23,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
...
...
spec/features/groups/issues_spec.rb
View file @
dde689ef
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 @@ feature 'Group issues page' do
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 @
dde689ef
...
...
@@ -101,4 +101,38 @@ describe SearchHelper do
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 @
dde689ef
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 @
dde689ef
...
...
@@ -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 @
dde689ef
...
...
@@ -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
'
);
expect
(
token
.
style
.
backgroundColor
).
not
.
toEqual
(
originalBackgroundColor
);
});
it
(
'
should not set backgroundColor when it is a linear-gradient
'
,
()
=>
{
const
token
=
subject
.
setTokenStyle
(
bugLabelToken
,
'
linear-gradient(135deg, red, blue)
'
,
'
white
'
);
expect
(
token
.
style
.
backgroundColor
).
toEqual
(
bugLabelToken
.
style
.
backgroundColor
);
});
it
(
'
should set textColor
'
,
()
=>
{
const
token
=
subject
.
setTokenStyle
(
bugLabelToken
,
'
white
'
,
'
black
'
);
expect
(
token
.
style
.
color
).
toEqual
(
'
black
'
);
expect
(
token
.
style
.
color
).
not
.
toEqual
(
originalTextColor
);
});
it
(
'
should add inverted class when textColor is #FFFFFF
'
,
()
=>
{
const
token
=
subject
.
setTokenStyle
(
bugLabelToken
,
'
black
'
,
'
#FFFFFF
'
);
expect
(
token
.
style
.
color
).
toEqual
(
'
rgb(255, 255, 255)
'
);
expect
(
token
.
style
.
color
).
not
.
toEqual
(
originalTextColor
);
expect
(
token
.
querySelector
(
'
.remove-token
'
).
classList
.
contains
(
'
inverted
'
)).
toEqual
(
true
);
});
});
describe
(
'
preprocessLabel
'
,
()
=>
{
const
endpoint
=
'
endpoint
'
;
it
(
'
does not preprocess more than once
'
,
()
=>
{
let
labels
=
[];
spyOn
(
gl
.
DropdownUtils
,
'
duplicateLabelPreprocessing
'
).
and
.
callFake
(()
=>
[]);
labels
=
gl
.
FilteredSearchVisualTokens
.
preprocessLabel
(
endpoint
,
labels
);
gl
.
FilteredSearchVisualTokens
.
preprocessLabel
(
endpoint
,
labels
);
expect
(
gl
.
DropdownUtils
.
duplicateLabelPreprocessing
.
calls
.
count
()).
toEqual
(
1
);
});
describe
(
'
not preprocessed before
'
,
()
=>
{
it
(
'
returns preprocessed labels
'
,
()
=>
{
let
labels
=
[];
expect
(
labels
.
preprocessed
).
not
.
toEqual
(
true
);
labels
=
gl
.
FilteredSearchVisualTokens
.
preprocessLabel
(
endpoint
,
labels
);
expect
(
labels
.
preprocessed
).
toEqual
(
true
);
});
it
(
'
overrides AjaxCache with preprocessed results
'
,
()
=>
{
spyOn
(
AjaxCache
,
'
override
'
).
and
.
callFake
(()
=>
{});
gl
.
FilteredSearchVisualTokens
.
preprocessLabel
(
endpoint
,
[]);
expect
(
AjaxCache
.
override
.
calls
.
count
()).
toEqual
(
1
);
});
});
});
describe
(
'
updateLabelTokenColor
'
,
()
=>
{
const
jsonFixtureName
=
'
labels/project_labels.json
'
;
const
dummyEndpoint
=
'
/dummy/endpoint
'
;
...
...
spec/javascripts/lib/utils/ajax_cache_spec.js
View file @
dde689ef
...
...
@@ -77,6 +77,15 @@ describe('AjaxCache', () => {
});
});
describe
(
'
override
'
,
()
=>
{
it
(
'
overrides existing cache
'
,
()
=>
{
AjaxCache
.
internalStorage
.
endpoint
=
'
existing-endpoint
'
;
AjaxCache
.
override
(
'
endpoint
'
,
'
new-endpoint
'
);
expect
(
AjaxCache
.
internalStorage
.
endpoint
).
toEqual
(
'
new-endpoint
'
);
});
});
describe
(
'
retrieve
'
,
()
=>
{
let
ajaxSpy
;
...
...
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