BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
c73f0c48
Commit
c73f0c48
authored
Aug 02, 2017
by
Phil Hughes
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'add-filtered-search-group-issues-ce' into 'master'
Add filtered search to group issue dashboard Closes #33575 See merge request !13167
parents
6f66b19b
974a0402
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
400 additions
and
45 deletions
+400
-45
dispatcher.js
app/assets/javascripts/dispatcher.js
+10
-2
ajax.js
app/assets/javascripts/droplab/plugins/ajax.js
+12
-1
dropdown_non_user.js
app/assets/javascripts/filtered_search/dropdown_non_user.js
+2
-1
dropdown_utils.js
app/assets/javascripts/filtered_search/dropdown_utils.js
+60
-0
filtered_search_dropdown_manager.js
...ripts/filtered_search/filtered_search_dropdown_manager.js
+1
-0
filtered_search_manager.js
...ts/javascripts/filtered_search/filtered_search_manager.js
+3
-3
filtered_search_visual_tokens.js
...ascripts/filtered_search/filtered_search_visual_tokens.js
+40
-15
labels_select.js
app/assets/javascripts/labels_select.js
+2
-12
ajax_cache.js
app/assets/javascripts/lib/utils/ajax_cache.js
+4
-0
search_helper.rb
app/helpers/search_helper.rb
+12
-4
issues.html.haml
app/views/groups/issues.html.haml
+5
-1
_search_bar.html.haml
app/views/shared/issuable/_search_bar.html.haml
+2
-1
add-filtered-search-group-issues-ce.yml
...gelogs/unreleased/add-filtered-search-group-issues-ce.yml
+4
-0
issues_spec.rb
spec/features/groups/issues_spec.rb
+5
-5
search_helper_spec.rb
spec/helpers/search_helper_spec.rb
+34
-0
ajax_spec.js
spec/javascripts/droplab/plugins/ajax_spec.js
+36
-0
dropdown_utils_spec.js
spec/javascripts/filtered_search/dropdown_utils_spec.js
+96
-0
filtered_search_visual_tokens_spec.js
...pts/filtered_search/filtered_search_visual_tokens_spec.js
+63
-0
ajax_cache_spec.js
spec/javascripts/lib/utils/ajax_cache_spec.js
+9
-0
No files found.
app/assets/javascripts/dispatcher.js
View file @
c73f0c48
...
...
@@ -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
.
FilteredSearchManager
&&
document
.
querySelector
(
'.filtered-search'
)
)
{
if
(
filteredSearchEnabled
)
{
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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -127,15 +127,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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
-
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 @
c73f0c48
---
title
:
Add filtered search to group issue dashboard
merge_request
:
author
:
spec/features/groups/issues_spec.rb
View file @
c73f0c48
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 @
c73f0c48
...
...
@@ -68,4 +68,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 @
c73f0c48
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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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 @
c73f0c48
...
...
@@ -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