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
27a0aada
Commit
27a0aada
authored
Apr 29, 2020
by
Martin Wortschack
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add label token component
- Use label token in code review analytics filtered search
parent
ac620b29
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
227 additions
and
13 deletions
+227
-13
ee/app/assets/javascripts/analytics/code_review_analytics/components/filter_bar.vue
...analytics/code_review_analytics/components/filter_bar.vue
+15
-1
ee/app/assets/javascripts/analytics/shared/components/tokens/label_token.vue
...cripts/analytics/shared/components/tokens/label_token.vue
+106
-0
ee/spec/frontend/analytics/code_review_analytics/components/filter_bar_spec.js
...ytics/code_review_analytics/components/filter_bar_spec.js
+22
-6
ee/spec/frontend/analytics/code_review_analytics/mock_data.js
...pec/frontend/analytics/code_review_analytics/mock_data.js
+2
-4
ee/spec/frontend/analytics/shared/components/tokens/label_token_spec.js
...nd/analytics/shared/components/tokens/label_token_spec.js
+75
-0
ee/spec/frontend/analytics/shared/components/tokens/milestone_token_spec.js
...nalytics/shared/components/tokens/milestone_token_spec.js
+1
-1
ee/spec/frontend/analytics/shared/components/tokens/mock_data.js
.../frontend/analytics/shared/components/tokens/mock_data.js
+6
-1
No files found.
ee/app/assets/javascripts/analytics/code_review_analytics/components/filter_bar.vue
View file @
27a0aada
...
...
@@ -3,6 +3,7 @@ import { mapState, mapActions } from 'vuex';
import
{
GlFilteredSearch
}
from
'
@gitlab/ui
'
;
import
{
__
}
from
'
~/locale
'
;
import
MilestoneToken
from
'
../../shared/components/tokens/milestone_token.vue
'
;
import
LabelToken
from
'
../../shared/components/tokens/label_token.vue
'
;
export
default
{
components
:
{
...
...
@@ -19,6 +20,8 @@ export default {
labelsPath
:
'
labelsPath
'
,
milestones
:
state
=>
state
.
milestones
.
data
,
milestonesLoading
:
state
=>
state
.
milestones
.
isLoading
,
labels
:
state
=>
state
.
labels
.
data
,
labelsLoading
:
state
=>
state
.
labels
.
isLoading
,
}),
tokens
()
{
return
[
...
...
@@ -32,14 +35,25 @@ export default {
symbol
:
'
%
'
,
isLoading
:
this
.
milestonesLoading
,
},
{
icon
:
'
labels
'
,
title
:
__
(
'
Label
'
),
type
:
'
label
'
,
token
:
LabelToken
,
labels
:
this
.
labels
,
unique
:
false
,
symbol
:
'
~
'
,
isLoading
:
this
.
labelsLoading
,
},
];
},
},
created
()
{
this
.
fetchMilestones
();
this
.
fetchLabels
();
},
methods
:
{
...
mapActions
(
'
filters
'
,
[
'
fetchMilestones
'
,
'
setFilters
'
]),
...
mapActions
(
'
filters
'
,
[
'
fetchMilestones
'
,
'
fetchLabels
'
,
'
setFilters
'
]),
processFilters
(
filters
)
{
return
filters
.
reduce
((
acc
,
token
)
=>
{
const
{
type
,
value
}
=
token
;
...
...
ee/app/assets/javascripts/analytics/shared/components/tokens/label_token.vue
0 → 100644
View file @
27a0aada
<
script
>
import
{
GlFilteredSearchToken
,
GlFilteredSearchSuggestion
,
GlDropdownDivider
,
GlLoadingIcon
,
}
from
'
@gitlab/ui
'
;
import
{
__
}
from
'
~/locale
'
;
export
default
{
components
:
{
GlFilteredSearchToken
,
GlFilteredSearchSuggestion
,
GlDropdownDivider
,
GlLoadingIcon
,
},
inheritAttrs
:
false
,
props
:
{
config
:
{
type
:
Object
,
required
:
true
,
},
value
:
{
type
:
Object
,
required
:
true
,
},
},
computed
:
{
labels
()
{
return
this
.
config
.
labels
;
},
filteredLabels
()
{
return
this
.
labels
.
filter
(
label
=>
label
.
title
.
toLowerCase
().
indexOf
(
this
.
value
.
data
?.
toLowerCase
())
!==
-
1
)
.
map
(
label
=>
({
...
label
,
value
:
this
.
getEscapedText
(
label
.
title
),
}));
},
},
methods
:
{
getEscapedText
(
text
)
{
let
escapedText
=
text
;
const
hasSpace
=
text
.
indexOf
(
'
'
)
!==
-
1
;
const
hasDoubleQuote
=
text
.
indexOf
(
'
"
'
)
!==
-
1
;
// Encapsulate value with quotes if it has spaces
// Known side effect: values's with both single and double quotes
// won't escape properly
if
(
hasSpace
)
{
if
(
hasDoubleQuote
)
{
escapedText
=
`'
${
text
}
'`
;
}
else
{
// Encapsulate singleQuotes or if it hasSpace
escapedText
=
`"
${
text
}
"`
;
}
}
return
escapedText
;
},
},
defaultSuggestions
:
[
// eslint-disable-next-line @gitlab/require-i18n-strings
{
value
:
'
None
'
,
text
:
__
(
'
None
'
)
},
// eslint-disable-next-line @gitlab/require-i18n-strings
{
value
:
'
Any
'
,
text
:
__
(
'
Any
'
)
},
],
};
</
script
>
<
template
>
<gl-filtered-search-token
:config=
"config"
v-bind=
"
{ ...this.$attrs }" v-on="$listeners">
<template
#view
="
{ inputValue }">
<template
v-if=
"config.symbol"
>
{{
config
.
symbol
}}
</
template
>
{{ inputValue }}
</template>
<
template
#suggestions
>
<gl-filtered-search-suggestion
v-for=
"suggestion in $options.defaultSuggestions"
:key=
"suggestion.value"
:value=
"suggestion.value"
>
{{
suggestion
.
text
}}
</gl-filtered-search-suggestion
>
<gl-dropdown-divider
v-if=
"config.isLoading || filteredLabels.length"
/>
<gl-loading-icon
v-if=
"config.isLoading"
/>
<template
v-else
>
<gl-filtered-search-suggestion
v-for=
"label in filteredLabels"
ref=
"labelItem"
:key=
"label.id"
:value=
"label.value"
>
<div
class=
"d-flex"
>
<span
class=
"d-inline-block mr-2 gl-w-16 gl-h-16 border-radius-small"
:style=
"
{
backgroundColor: label.color,
}"
>
</span>
<span>
{{
label
.
title
}}
</span>
</div>
</gl-filtered-search-suggestion>
</
template
>
</template>
</gl-filtered-search-token>
</template>
ee/spec/frontend/analytics/code_review_analytics/components/filter_bar_spec.js
View file @
27a0aada
...
...
@@ -3,12 +3,13 @@ import Vuex from 'vuex';
import
{
GlFilteredSearch
}
from
'
@gitlab/ui
'
;
import
FilterBar
from
'
ee/analytics/code_review_analytics/components/filter_bar.vue
'
;
import
createFiltersState
from
'
ee/analytics/code_review_analytics/store/modules/filters/state
'
;
import
{
mockMilestones
}
from
'
../mock_data
'
;
import
{
mockMilestones
,
mockLabels
}
from
'
../mock_data
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
Vuex
);
const
milestoneTokenType
=
'
milestone
'
;
const
labelTokenType
=
'
label
'
;
describe
(
'
FilteredSearchBar
'
,
()
=>
{
let
wrapper
;
...
...
@@ -29,6 +30,7 @@ describe('FilteredSearchBar', () => {
},
actions
:
{
fetchMilestones
:
jest
.
fn
(),
fetchLabels
:
jest
.
fn
(),
setFilters
:
setFiltersMock
,
},
},
...
...
@@ -61,15 +63,19 @@ describe('FilteredSearchBar', () => {
describe
(
'
when the state has data
'
,
()
=>
{
beforeEach
(()
=>
{
vuexStore
=
createStore
({
milestones
:
{
data
:
mockMilestones
}
});
vuexStore
=
createStore
({
milestones
:
{
data
:
mockMilestones
},
labels
:
{
data
:
mockLabels
},
});
wrapper
=
createComponent
(
vuexStore
);
});
it
(
'
displays the milestone token
'
,
()
=>
{
it
(
'
displays the milestone
and label
token
'
,
()
=>
{
const
tokens
=
findFilteredSearch
().
props
(
'
availableTokens
'
);
expect
(
tokens
).
toHaveLength
(
1
);
expect
(
tokens
).
toHaveLength
(
2
);
expect
(
tokens
[
0
].
type
).
toBe
(
milestoneTokenType
);
expect
(
tokens
[
1
].
type
).
toBe
(
labelTokenType
);
});
it
(
'
displays options in the milestone token
'
,
()
=>
{
...
...
@@ -77,23 +83,33 @@ describe('FilteredSearchBar', () => {
expect
(
milestoneToken
).
toHaveLength
(
mockMilestones
.
length
);
});
it
(
'
displays options in the label token
'
,
()
=>
{
const
{
labels
:
labelToken
}
=
getSearchToken
(
labelTokenType
);
expect
(
labelToken
).
toHaveLength
(
mockLabels
.
length
);
});
});
describe
(
'
when the user interacts
'
,
()
=>
{
beforeEach
(()
=>
{
vuexStore
=
createStore
({
milestones
:
{
data
:
mockMilestones
}
});
vuexStore
=
createStore
({
milestones
:
{
data
:
mockMilestones
},
labels
:
{
data
:
mockLabels
},
});
wrapper
=
createComponent
(
vuexStore
);
});
it
(
'
clicks on the search button, setFilters is dispatched
'
,
()
=>
{
findFilteredSearch
().
vm
.
$emit
(
'
submit
'
,
[
{
type
:
'
milestone
'
,
value
:
{
data
:
'
my-milestone
'
,
operator
:
'
=
'
}
},
{
type
:
'
label
'
,
value
:
{
data
:
'
my-label
'
,
operator
:
'
=
'
}
},
]);
expect
(
setFiltersMock
).
toHaveBeenCalledWith
(
expect
.
anything
(),
{
label_name
:
undefined
,
label_name
:
[
'
my-label
'
]
,
milestone_title
:
[
'
my-milestone
'
],
},
undefined
,
...
...
ee/spec/frontend/analytics/code_review_analytics/mock_data.js
View file @
27a0aada
...
...
@@ -66,8 +66,6 @@ export const mockMilestones = [
];
export
const
mockLabels
=
[
[
{
id
:
74
,
title
:
'
Alero
'
,
color
:
'
#6235f2
'
,
text_color
:
'
#FFFFFF
'
},
{
id
:
9
,
title
:
'
Amsche
'
,
color
:
'
#581cc8
'
,
text_color
:
'
#FFFFFF
'
},
],
{
id
:
74
,
title
:
'
Alero
'
,
color
:
'
#6235f2
'
,
text_color
:
'
#FFFFFF
'
},
{
id
:
9
,
title
:
'
Amsche
'
,
color
:
'
#581cc8
'
,
text_color
:
'
#FFFFFF
'
},
];
ee/spec/frontend/analytics/shared/components/tokens/label_token_spec.js
0 → 100644
View file @
27a0aada
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlFilteredSearchSuggestion
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
LabelToken
from
'
ee/analytics/shared/components/tokens/label_token.vue
'
;
import
{
mockLabels
}
from
'
./mock_data
'
;
describe
(
'
MilestoneToken
'
,
()
=>
{
let
wrapper
;
const
defaultValue
=
{
data
:
''
};
const
defaultConfig
=
{
icon
:
'
labels
'
,
title
:
'
Label
'
,
type
:
'
label
'
,
labels
:
mockLabels
,
unique
:
false
,
symbol
:
'
~
'
,
isLoading
:
false
,
};
const
stubs
=
{
GlFilteredSearchToken
:
{
template
:
`<div><slot name="suggestions"></slot></div>`
,
},
};
const
createComponent
=
(
props
=
{},
options
)
=>
{
wrapper
=
shallowMount
(
LabelToken
,
{
propsData
:
{
config
:
defaultConfig
,
value
:
defaultValue
,
...
props
,
},
...
options
,
});
};
const
findFilteredSearchSuggestion
=
index
=>
wrapper
.
findAll
(
GlFilteredSearchSuggestion
).
at
(
index
);
const
findAllLabelSuggestions
=
()
=>
wrapper
.
findAll
({
ref
:
'
labelItem
'
});
const
findLoadingIcon
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
it
(
'
renders a loading icon
'
,
()
=>
{
createComponent
({
config
:
{
isLoading
:
true
},
value
:
{}
},
{
stubs
});
expect
(
findLoadingIcon
().
exists
()).
toBe
(
true
);
});
describe
(
'
suggestions
'
,
()
=>
{
describe
(
'
default suggestions
'
,
()
=>
{
it
.
each
`
text | dropdownIndex
${
'
None
'
}
|
${
0
}
${
'
Any
'
}
|
${
1
}
`
(
'
renders the "$text" suggestion
'
,
({
text
,
dropdownIndex
})
=>
{
createComponent
(
null
,
{
stubs
});
expect
(
findFilteredSearchSuggestion
(
dropdownIndex
).
text
()).
toEqual
(
text
);
});
});
describe
(
'
when no search term is given
'
,
()
=>
{
it
(
'
renders two label suggestions
'
,
()
=>
{
createComponent
(
null
,
{
stubs
});
expect
(
findAllLabelSuggestions
()).
toHaveLength
(
2
);
});
});
describe
(
'
when the search term "Alero" is given
'
,
()
=>
{
it
(
'
renders one label suggestion that matches the search term
'
,
()
=>
{
createComponent
({
value
:
{
data
:
'
Alero
'
}
},
{
stubs
});
expect
(
findAllLabelSuggestions
()).
toHaveLength
(
1
);
});
});
});
});
ee/spec/frontend/analytics/shared/components/tokens/milestone_token_spec.js
View file @
27a0aada
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlFilteredSearchSuggestion
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
MilestoneToken
from
'
ee/analytics/shared/components/tokens/milestone_token.vue
'
;
import
mockMilestones
from
'
./mock_data
'
;
import
{
mockMilestones
}
from
'
./mock_data
'
;
describe
(
'
MilestoneToken
'
,
()
=>
{
let
wrapper
;
...
...
ee/spec/frontend/analytics/shared/components/tokens/mock_data.js
View file @
27a0aada
export
default
[
export
const
mockMilestones
=
[
{
id
:
41
,
title
:
'
Sprint - Eligendi et aut pariatur ab rerum vel.
'
,
...
...
@@ -28,3 +28,8 @@ export default [
name
:
'
v4.0
'
,
},
];
export
const
mockLabels
=
[
{
id
:
74
,
title
:
'
Alero
'
,
color
:
'
#6235f2
'
,
text_color
:
'
#FFFFFF
'
},
{
id
:
9
,
title
:
'
Amsche
'
,
color
:
'
#581cc8
'
,
text_color
:
'
#FFFFFF
'
},
];
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