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
fc2962ff
Commit
fc2962ff
authored
Nov 02, 2021
by
Florie Guibert
Committed by
Kushal Pandya
Nov 02, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor labels selector in board scope
parent
e94f48e8
Changes
31
Hide whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
569 additions
and
221 deletions
+569
-221
app/assets/javascripts/boards/boards_util.js
app/assets/javascripts/boards/boards_util.js
+4
-0
app/assets/javascripts/boards/components/board_form.vue
app/assets/javascripts/boards/components/board_form.vue
+1
-38
app/assets/javascripts/boards/components/boards_selector.vue
app/assets/javascripts/boards/components/boards_selector.vue
+0
-26
app/assets/javascripts/boards/index.js
app/assets/javascripts/boards/index.js
+2
-0
app/assets/javascripts/boards/mount_multiple_boards_switcher.js
...sets/javascripts/boards/mount_multiple_boards_switcher.js
+4
-4
app/assets/javascripts/graphql_shared/queries/group_users_search.query.graphql
...s/graphql_shared/queries/group_users_search.query.graphql
+1
-0
app/assets/javascripts/graphql_shared/queries/project_user_members_search.query.graphql
..._shared/queries/project_user_members_search.query.graphql
+1
-0
app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
...d/components/dropdown/dropdown_widget/dropdown_widget.vue
+56
-41
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue
...ar/labels_select_widget/dropdown_contents_create_view.vue
+0
-5
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
...mponents/sidebar/labels_select_widget/dropdown_header.vue
+1
-1
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
...r/labels_select_widget/graphql/group_labels.query.graphql
+2
-1
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
...labels_select_widget/graphql/project_labels.query.graphql
+1
-0
app/views/shared/boards/_switcher.html.haml
app/views/shared/boards/_switcher.html.haml
+0
-4
ee/app/assets/javascripts/boards/components/assignee_select.vue
.../assets/javascripts/boards/components/assignee_select.vue
+0
-10
ee/app/assets/javascripts/boards/components/board_scope.vue
ee/app/assets/javascripts/boards/components/board_scope.vue
+7
-48
ee/app/assets/javascripts/boards/components/labels_select.vue
...pp/assets/javascripts/boards/components/labels_select.vue
+253
-0
ee/app/assets/javascripts/boards/components/milestone_select.vue
...assets/javascripts/boards/components/milestone_select.vue
+0
-10
ee/spec/features/boards/scoped_issue_board_spec.rb
ee/spec/features/boards/scoped_issue_board_spec.rb
+3
-3
ee/spec/features/epic_boards/epic_boards_spec.rb
ee/spec/features/epic_boards/epic_boards_spec.rb
+1
-1
ee/spec/features/labels_hierarchy_spec.rb
ee/spec/features/labels_hierarchy_spec.rb
+1
-1
ee/spec/frontend/boards/components/assignee_select_spec.js
ee/spec/frontend/boards/components/assignee_select_spec.js
+3
-0
ee/spec/frontend/boards/components/board_form_spec.js
ee/spec/frontend/boards/components/board_form_spec.js
+0
-3
ee/spec/frontend/boards/components/board_scope_spec.js
ee/spec/frontend/boards/components/board_scope_spec.js
+8
-10
ee/spec/frontend/boards/components/boards_selector_spec.js
ee/spec/frontend/boards/components/boards_selector_spec.js
+0
-4
ee/spec/frontend/boards/components/labels_select_spec.js
ee/spec/frontend/boards/components/labels_select_spec.js
+161
-0
locale/gitlab.pot
locale/gitlab.pot
+15
-0
spec/frontend/boards/components/board_form_spec.js
spec/frontend/boards/components/board_form_spec.js
+0
-3
spec/frontend/boards/components/boards_selector_spec.js
spec/frontend/boards/components/boards_selector_spec.js
+0
-4
spec/frontend/boards/mock_data.js
spec/frontend/boards/mock_data.js
+41
-0
spec/frontend/vue_shared/components/dropdown/dropdown_widget_spec.js
...nd/vue_shared/components/dropdown/dropdown_widget_spec.js
+2
-4
spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
...ared/components/sidebar/labels_select_widget/mock_data.js
+1
-0
No files found.
app/assets/javascripts/boards/boards_util.js
View file @
fc2962ff
import
{
sortBy
,
cloneDeep
}
from
'
lodash
'
;
import
{
sortBy
,
cloneDeep
}
from
'
lodash
'
;
import
{
isGid
}
from
'
~/graphql_shared/utils
'
;
import
{
ListType
,
MilestoneIDs
}
from
'
./constants
'
;
import
{
ListType
,
MilestoneIDs
}
from
'
./constants
'
;
export
function
getMilestone
()
{
export
function
getMilestone
()
{
...
@@ -95,6 +96,9 @@ export function fullMilestoneId(id) {
...
@@ -95,6 +96,9 @@ export function fullMilestoneId(id) {
}
}
export
function
fullLabelId
(
label
)
{
export
function
fullLabelId
(
label
)
{
if
(
isGid
(
label
.
id
))
{
return
label
.
id
;
}
if
(
label
.
project_id
&&
label
.
project_id
!==
null
)
{
if
(
label
.
project_id
&&
label
.
project_id
!==
null
)
{
return
`gid://gitlab/ProjectLabel/
${
label
.
id
}
`
;
return
`gid://gitlab/ProjectLabel/
${
label
.
id
}
`
;
}
}
...
...
app/assets/javascripts/boards/components/board_form.vue
View file @
fc2962ff
...
@@ -57,39 +57,16 @@ export default {
...
@@ -57,39 +57,16 @@ export default {
type
:
Boolean
,
type
:
Boolean
,
required
:
true
,
required
:
true
,
},
},
labelsPath
:
{
type
:
String
,
required
:
true
,
},
labelsWebUrl
:
{
type
:
String
,
required
:
true
,
},
scopedIssueBoardFeatureEnabled
:
{
scopedIssueBoardFeatureEnabled
:
{
type
:
Boolean
,
type
:
Boolean
,
required
:
false
,
required
:
false
,
default
:
false
,
default
:
false
,
},
},
projectId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
groupId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
weights
:
{
weights
:
{
type
:
Array
,
type
:
Array
,
required
:
false
,
required
:
false
,
default
:
()
=>
[],
default
:
()
=>
[],
},
},
enableScopedLabels
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
currentBoard
:
{
currentBoard
:
{
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
...
@@ -288,16 +265,7 @@ export default {
...
@@ -288,16 +265,7 @@ export default {
this
.
board
.
iteration_id
=
iterationId
;
this
.
board
.
iteration_id
=
iterationId
;
},
},
setBoardLabels
(
labels
)
{
setBoardLabels
(
labels
)
{
labels
.
forEach
((
label
)
=>
{
this
.
board
.
labels
=
labels
;
if
(
label
.
set
&&
!
this
.
board
.
labels
.
find
((
l
)
=>
l
.
id
===
label
.
id
))
{
this
.
board
.
labels
.
push
({
...
label
,
textColor
:
label
.
text_color
,
});
}
else
if
(
!
label
.
set
)
{
this
.
board
.
labels
=
this
.
board
.
labels
.
filter
((
selected
)
=>
selected
.
id
!==
label
.
id
);
}
});
},
},
setAssignee
(
assigneeId
)
{
setAssignee
(
assigneeId
)
{
this
.
$set
(
this
.
board
,
'
assignee
'
,
{
this
.
$set
(
this
.
board
,
'
assignee
'
,
{
...
@@ -371,11 +339,6 @@ export default {
...
@@ -371,11 +339,6 @@ export default {
:collapse-scope=
"isNewForm"
:collapse-scope=
"isNewForm"
:board=
"board"
:board=
"board"
:can-admin-board=
"canAdminBoard"
:can-admin-board=
"canAdminBoard"
:labels-path=
"labelsPath"
:labels-web-url=
"labelsWebUrl"
:enable-scoped-labels=
"enableScopedLabels"
:project-id=
"projectId"
:group-id=
"groupId"
:weights=
"weights"
:weights=
"weights"
@
set-iteration=
"setIteration"
@
set-iteration=
"setIteration"
@
set-board-labels=
"setBoardLabels"
@
set-board-labels=
"setBoardLabels"
...
...
app/assets/javascripts/boards/components/boards_selector.vue
View file @
fc2962ff
...
@@ -64,22 +64,6 @@ export default {
...
@@ -64,22 +64,6 @@ export default {
type
:
Boolean
,
type
:
Boolean
,
required
:
true
,
required
:
true
,
},
},
labelsPath
:
{
type
:
String
,
required
:
true
,
},
labelsWebUrl
:
{
type
:
String
,
required
:
true
,
},
projectId
:
{
type
:
Number
,
required
:
true
,
},
groupId
:
{
type
:
Number
,
required
:
true
,
},
scopedIssueBoardFeatureEnabled
:
{
scopedIssueBoardFeatureEnabled
:
{
type
:
Boolean
,
type
:
Boolean
,
required
:
true
,
required
:
true
,
...
@@ -88,11 +72,6 @@ export default {
...
@@ -88,11 +72,6 @@ export default {
type
:
Array
,
type
:
Array
,
required
:
true
,
required
:
true
,
},
},
enabledScopedLabels
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
},
data
()
{
data
()
{
return
{
return
{
...
@@ -354,14 +333,9 @@ export default {
...
@@ -354,14 +333,9 @@ export default {
<board-form
<board-form
v-if=
"currentPage"
v-if=
"currentPage"
:labels-path=
"labelsPath"
:labels-web-url=
"labelsWebUrl"
:project-id=
"projectId"
:group-id=
"groupId"
:can-admin-board=
"canAdminBoard"
:can-admin-board=
"canAdminBoard"
:scoped-issue-board-feature-enabled=
"scopedIssueBoardFeatureEnabled"
:scoped-issue-board-feature-enabled=
"scopedIssueBoardFeatureEnabled"
:weights=
"weights"
:weights=
"weights"
:enable-scoped-labels=
"enabledScopedLabels"
:current-board=
"currentBoard"
:current-board=
"currentBoard"
:current-page=
"currentPage"
:current-page=
"currentPage"
@
cancel=
"cancel"
@
cancel=
"cancel"
...
...
app/assets/javascripts/boards/index.js
View file @
fc2962ff
...
@@ -142,5 +142,7 @@ export default () => {
...
@@ -142,5 +142,7 @@ export default () => {
fullPath
:
$boardApp
.
dataset
.
fullPath
,
fullPath
:
$boardApp
.
dataset
.
fullPath
,
rootPath
:
$boardApp
.
dataset
.
boardsEndpoint
,
rootPath
:
$boardApp
.
dataset
.
boardsEndpoint
,
recentBoardsEndpoint
:
$boardApp
.
dataset
.
recentBoardsEndpoint
,
recentBoardsEndpoint
:
$boardApp
.
dataset
.
recentBoardsEndpoint
,
allowScopedLabels
:
$boardApp
.
dataset
.
scopedLabels
,
labelsManagePath
:
$boardApp
.
dataset
.
labelsManagePath
,
});
});
};
};
app/assets/javascripts/boards/mount_multiple_boards_switcher.js
View file @
fc2962ff
...
@@ -13,6 +13,7 @@ const apolloProvider = new VueApollo({
...
@@ -13,6 +13,7 @@ const apolloProvider = new VueApollo({
export
default
(
params
=
{})
=>
{
export
default
(
params
=
{})
=>
{
const
boardsSwitcherElement
=
document
.
getElementById
(
'
js-multiple-boards-switcher
'
);
const
boardsSwitcherElement
=
document
.
getElementById
(
'
js-multiple-boards-switcher
'
);
const
{
dataset
}
=
boardsSwitcherElement
;
return
new
Vue
({
return
new
Vue
({
el
:
boardsSwitcherElement
,
el
:
boardsSwitcherElement
,
components
:
{
components
:
{
...
@@ -24,18 +25,17 @@ export default (params = {}) => {
...
@@ -24,18 +25,17 @@ export default (params = {}) => {
fullPath
:
params
.
fullPath
,
fullPath
:
params
.
fullPath
,
rootPath
:
params
.
rootPath
,
rootPath
:
params
.
rootPath
,
recentBoardsEndpoint
:
params
.
recentBoardsEndpoint
,
recentBoardsEndpoint
:
params
.
recentBoardsEndpoint
,
allowScopedLabels
:
params
.
allowScopedLabels
,
labelsManagePath
:
params
.
labelsManagePath
,
allowLabelCreate
:
parseBoolean
(
dataset
.
canAdminBoard
),
},
},
data
()
{
data
()
{
const
{
dataset
}
=
boardsSwitcherElement
;
const
boardsSelectorProps
=
{
const
boardsSelectorProps
=
{
...
dataset
,
...
dataset
,
currentBoard
:
JSON
.
parse
(
dataset
.
currentBoard
),
currentBoard
:
JSON
.
parse
(
dataset
.
currentBoard
),
hasMissingBoards
:
parseBoolean
(
dataset
.
hasMissingBoards
),
hasMissingBoards
:
parseBoolean
(
dataset
.
hasMissingBoards
),
canAdminBoard
:
parseBoolean
(
dataset
.
canAdminBoard
),
canAdminBoard
:
parseBoolean
(
dataset
.
canAdminBoard
),
multipleIssueBoardsAvailable
:
parseBoolean
(
dataset
.
multipleIssueBoardsAvailable
),
multipleIssueBoardsAvailable
:
parseBoolean
(
dataset
.
multipleIssueBoardsAvailable
),
projectId
:
dataset
.
projectId
?
Number
(
dataset
.
projectId
)
:
0
,
groupId
:
Number
(
dataset
.
groupId
),
scopedIssueBoardFeatureEnabled
:
parseBoolean
(
dataset
.
scopedIssueBoardFeatureEnabled
),
scopedIssueBoardFeatureEnabled
:
parseBoolean
(
dataset
.
scopedIssueBoardFeatureEnabled
),
weights
:
JSON
.
parse
(
dataset
.
weights
),
weights
:
JSON
.
parse
(
dataset
.
weights
),
};
};
...
...
app/assets/javascripts/graphql_shared/queries/group_users_search.query.graphql
View file @
fc2962ff
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
query
usersSearch
(
$search
:
String
!,
$fullPath
:
ID
!)
{
query
usersSearch
(
$search
:
String
!,
$fullPath
:
ID
!)
{
workspace
:
group
(
fullPath
:
$fullPath
)
{
workspace
:
group
(
fullPath
:
$fullPath
)
{
id
users
:
groupMembers
(
search
:
$search
,
relations
:
[
DIRECT
,
INHERITED
])
{
users
:
groupMembers
(
search
:
$search
,
relations
:
[
DIRECT
,
INHERITED
])
{
nodes
{
nodes
{
user
{
user
{
...
...
app/assets/javascripts/graphql_shared/queries/project_user_members_search.query.graphql
View file @
fc2962ff
query
searchProjectMembers
(
$fullPath
:
ID
!,
$search
:
String
)
{
query
searchProjectMembers
(
$fullPath
:
ID
!,
$search
:
String
)
{
project
(
fullPath
:
$fullPath
)
{
project
(
fullPath
:
$fullPath
)
{
id
projectMembers
(
search
:
$search
)
{
projectMembers
(
search
:
$search
)
{
nodes
{
nodes
{
user
{
user
{
...
...
app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
View file @
fc2962ff
...
@@ -45,7 +45,7 @@ export default {
...
@@ -45,7 +45,7 @@ export default {
default
:
false
,
default
:
false
,
},
},
selected
:
{
selected
:
{
type
:
Object
,
type
:
[
Object
,
Array
]
,
required
:
false
,
required
:
false
,
default
:
()
=>
{},
default
:
()
=>
{},
},
},
...
@@ -54,6 +54,11 @@ export default {
...
@@ -54,6 +54,11 @@ export default {
required
:
false
,
required
:
false
,
default
:
''
,
default
:
''
,
},
},
allowMultiselect
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
},
computed
:
{
computed
:
{
isSearchEmpty
()
{
isSearchEmpty
()
{
...
@@ -66,8 +71,14 @@ export default {
...
@@ -66,8 +71,14 @@ export default {
methods
:
{
methods
:
{
selectOption
(
option
)
{
selectOption
(
option
)
{
this
.
$emit
(
'
set-option
'
,
option
||
null
);
this
.
$emit
(
'
set-option
'
,
option
||
null
);
if
(
!
this
.
allowMultiselect
)
{
this
.
$refs
.
dropdown
.
hide
();
}
},
},
isSelected
(
option
)
{
isSelected
(
option
)
{
if
(
Array
.
isArray
(
this
.
selected
))
{
return
this
.
selected
.
some
((
label
)
=>
label
.
title
===
option
.
title
);
}
return
(
return
(
this
.
selected
&&
this
.
selected
&&
((
option
.
name
&&
this
.
selected
.
name
===
option
.
name
)
||
((
option
.
name
&&
this
.
selected
.
name
===
option
.
name
)
||
...
@@ -78,7 +89,7 @@ export default {
...
@@ -78,7 +89,7 @@ export default {
this
.
$refs
.
dropdown
.
show
();
this
.
$refs
.
dropdown
.
show
();
},
},
setFocus
()
{
setFocus
()
{
this
.
$refs
.
search
.
focusInput
();
this
.
$refs
.
search
?
.
focusInput
();
},
},
setSearchTerm
(
search
)
{
setSearchTerm
(
search
)
{
this
.
$emit
(
'
set-search
'
,
search
);
this
.
$emit
(
'
set-search
'
,
search
);
...
@@ -108,56 +119,60 @@ export default {
...
@@ -108,56 +119,60 @@ export default {
@
shown=
"setFocus"
@
shown=
"setFocus"
>
>
<template
#header
>
<template
#header
>
<gl-search-box-by-type
<slot
name=
"header"
>
ref=
"search"
<gl-search-box-by-type
:value=
"searchTerm"
ref=
"search"
:placeholder=
"searchText"
:value=
"searchTerm"
class=
"js-dropdown-input-field"
:placeholder=
"searchText"
@
input=
"setSearchTerm"
class=
"js-dropdown-input-field"
/>
@
input=
"setSearchTerm"
/>
</slot>
</
template
>
</
template
>
<gl-dropdown-form
class=
"gl-relative gl-min-h-7"
>
<slot
name=
"default"
>
<gl-loading-icon
<gl-dropdown-form
class=
"gl-relative gl-min-h-7"
>
v-if=
"isLoading"
<gl-loading-icon
size=
"md"
v-if=
"isLoading"
class=
"gl-absolute gl-left-0 gl-top-0 gl-right-0"
size=
"md"
/>
class=
"gl-absolute gl-left-0 gl-top-0 gl-right-0"
<
template
v-else
>
/>
<template
v-if=
"isSearchEmpty && presetOptions.length > 0"
>
<
template
v-else
>
<template
v-if=
"isSearchEmpty && presetOptions.length > 0"
>
<gl-dropdown-item
v-for=
"option in presetOptions"
:key=
"option.id"
:is-checked=
"isSelected(option)"
:is-check-centered=
"true"
:is-check-item=
"true"
@
click.native.capture.stop=
"selectOption(option)"
>
<slot
name=
"preset-item"
:item=
"option"
>
{{
option
.
title
}}
</slot>
</gl-dropdown-item>
<gl-dropdown-divider
/>
</
template
>
<gl-dropdown-item
<gl-dropdown-item
v-for=
"option in
presetO
ptions"
v-for=
"option in
o
ptions"
:key=
"option.id"
:key=
"option.id"
:is-checked=
"isSelected(option)"
:is-checked=
"isSelected(option)"
:is-check-centered=
"true"
:is-check-centered=
"true"
:is-check-item=
"true"
:is-check-item=
"true"
@
click=
"selectOption(option)"
:avatar-url=
"avatarUrl(option)"
:secondary-text=
"secondaryText(option)"
data-testid=
"unselected-option"
@
click.native.capture.stop=
"selectOption(option)"
>
>
<slot
name=
"
preset-
item"
:item=
"option"
>
<slot
name=
"item"
:item=
"option"
>
{{ option.title }}
{{ option.title }}
</slot>
</slot>
</gl-dropdown-item>
</gl-dropdown-item>
<gl-dropdown-divider
/>
<gl-dropdown-item
v-if=
"noOptionsFound"
class=
"gl-pl-6!"
>
{{ $options.i18n.noMatchingResults }}
</gl-dropdown-item>
</template>
</template>
<gl-dropdown-item
</gl-dropdown-form>
v-for=
"option in options"
</slot>
:key=
"option.id"
:is-checked=
"isSelected(option)"
:is-check-centered=
"true"
:is-check-item=
"true"
:avatar-url=
"avatarUrl(option)"
:secondary-text=
"secondaryText(option)"
data-testid=
"unselected-option"
@
click=
"selectOption(option)"
>
<slot
name=
"item"
:item=
"option"
>
{{ option.title }}
</slot>
</gl-dropdown-item>
<gl-dropdown-item
v-if=
"noOptionsFound"
class=
"gl-pl-6!"
>
{{ $options.i18n.noMatchingResults }}
</gl-dropdown-item>
</template>
</gl-dropdown-form>
<
template
#footer
>
<
template
#footer
>
<slot
name=
"footer"
></slot>
<slot
name=
"footer"
></slot>
</
template
>
</
template
>
...
...
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue
View file @
fc2962ff
...
@@ -32,11 +32,6 @@ export default {
...
@@ -32,11 +32,6 @@ export default {
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
},
},
issuableType
:
{
type
:
String
,
required
:
false
,
default
:
undefined
,
},
workspaceType
:
{
workspaceType
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
...
...
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
View file @
fc2962ff
...
@@ -39,7 +39,7 @@ export default {
...
@@ -39,7 +39,7 @@ export default {
},
},
methods
:
{
methods
:
{
focusInput
()
{
focusInput
()
{
this
.
$refs
.
searchInput
.
focusInput
();
this
.
$refs
.
searchInput
?
.
focusInput
();
},
},
},
},
};
};
...
...
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
View file @
fc2962ff
...
@@ -2,7 +2,8 @@
...
@@ -2,7 +2,8 @@
query
groupLabels
(
$fullPath
:
ID
!,
$searchTerm
:
String
)
{
query
groupLabels
(
$fullPath
:
ID
!,
$searchTerm
:
String
)
{
workspace
:
group
(
fullPath
:
$fullPath
)
{
workspace
:
group
(
fullPath
:
$fullPath
)
{
labels
(
searchTerm
:
$searchTerm
,
onlyGroupLabels
:
true
)
{
id
labels
(
searchTerm
:
$searchTerm
,
onlyGroupLabels
:
true
,
includeAncestorGroups
:
true
)
{
nodes
{
nodes
{
...
Label
...
Label
}
}
...
...
app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
View file @
fc2962ff
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
query
projectLabels
(
$fullPath
:
ID
!,
$searchTerm
:
String
)
{
query
projectLabels
(
$fullPath
:
ID
!,
$searchTerm
:
String
)
{
workspace
:
project
(
fullPath
:
$fullPath
)
{
workspace
:
project
(
fullPath
:
$fullPath
)
{
id
labels
(
searchTerm
:
$searchTerm
,
includeAncestorGroups
:
true
)
{
labels
(
searchTerm
:
$searchTerm
,
includeAncestorGroups
:
true
)
{
nodes
{
nodes
{
...
Label
...
Label
...
...
app/views/shared/boards/_switcher.html.haml
View file @
fc2962ff
...
@@ -9,9 +9,5 @@
...
@@ -9,9 +9,5 @@
has_missing_boards:
(
!
multiple_boards_available?
&&
current_board_parent
.
boards
.
size
>
1
).
to_s
,
has_missing_boards:
(
!
multiple_boards_available?
&&
current_board_parent
.
boards
.
size
>
1
).
to_s
,
can_admin_board:
can?
(
current_user
,
:admin_issue_board
,
parent
).
to_s
,
can_admin_board:
can?
(
current_user
,
:admin_issue_board
,
parent
).
to_s
,
multiple_issue_boards_available:
parent
.
multiple_issue_boards_available?
.
to_s
,
multiple_issue_boards_available:
parent
.
multiple_issue_boards_available?
.
to_s
,
labels_path:
labels_filter_path_with_defaults
(
only_group_labels:
true
,
include_descendant_groups:
true
),
labels_web_url:
parent
.
is_a?
(
Project
)
?
project_labels_path
(
@project
)
:
group_labels_path
(
@group
),
project_id:
@project
&
.
id
,
group_id:
@group
&
.
id
,
scoped_issue_board_feature_enabled:
Gitlab
.
ee?
&&
parent
.
feature_available?
(
:scoped_issue_board
)
?
'true'
:
'false'
,
scoped_issue_board_feature_enabled:
Gitlab
.
ee?
&&
parent
.
feature_available?
(
:scoped_issue_board
)
?
'true'
:
'false'
,
weights:
weights
.
to_json
}
}
weights:
weights
.
to_json
}
}
ee/app/assets/javascripts/boards/components/assignee_select.vue
View file @
fc2962ff
...
@@ -29,16 +29,6 @@ export default {
...
@@ -29,16 +29,6 @@ export default {
required
:
false
,
required
:
false
,
default
:
false
,
default
:
false
,
},
},
groupId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
projectId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
},
},
data
()
{
data
()
{
return
{
return
{
...
...
ee/app/assets/javascripts/boards/components/board_scope.vue
View file @
fc2962ff
<
script
>
<
script
>
import
{
mapGetters
}
from
'
vuex
'
;
import
{
mapGetters
}
from
'
vuex
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
import
LabelsSelect
from
'
~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
'
;
import
AssigneeSelect
from
'
./assignee_select.vue
'
;
import
AssigneeSelect
from
'
./assignee_select.vue
'
;
import
BoardScopeCurrentIteration
from
'
./board_scope_current_iteration.vue
'
;
import
BoardScopeCurrentIteration
from
'
./board_scope_current_iteration.vue
'
;
import
BoardLabelsSelect
from
'
./labels_select.vue
'
;
import
BoardMilestoneSelect
from
'
./milestone_select.vue
'
;
import
BoardMilestoneSelect
from
'
./milestone_select.vue
'
;
import
BoardWeightSelect
from
'
./weight_select.vue
'
;
import
BoardWeightSelect
from
'
./weight_select.vue
'
;
export
default
{
export
default
{
components
:
{
components
:
{
AssigneeSelect
,
AssigneeSelect
,
LabelsSelect
,
Board
LabelsSelect
,
BoardMilestoneSelect
,
BoardMilestoneSelect
,
BoardScopeCurrentIteration
,
BoardScopeCurrentIteration
,
BoardWeightSelect
,
BoardWeightSelect
,
...
@@ -28,29 +28,6 @@ export default {
...
@@ -28,29 +28,6 @@ export default {
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
labelsPath
:
{
type
:
String
,
required
:
true
,
},
labelsWebUrl
:
{
type
:
String
,
required
:
true
,
},
enableScopedLabels
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
projectId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
groupId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
weights
:
{
weights
:
{
type
:
Array
,
type
:
Array
,
required
:
false
,
required
:
false
,
...
@@ -103,8 +80,6 @@ export default {
...
@@ -103,8 +80,6 @@ export default {
<board-milestone-select
<board-milestone-select
v-if=
"isIssueBoard"
v-if=
"isIssueBoard"
:board=
"board"
:board=
"board"
:group-id=
"groupId"
:project-id=
"projectId"
:can-edit=
"canAdminBoard"
:can-edit=
"canAdminBoard"
@
set-milestone=
"$emit('set-milestone', $event)"
@
set-milestone=
"$emit('set-milestone', $event)"
/>
/>
...
@@ -116,33 +91,17 @@ export default {
...
@@ -116,33 +91,17 @@ export default {
@
set-iteration=
"$emit('set-iteration', $event)"
@
set-iteration=
"$emit('set-iteration', $event)"
/>
/>
<labels-select
<board-labels-select
:allow-label-edit=
"canAdminBoard"
:board=
"board"
:allow-label-create=
"canAdminBoard"
:can-edit=
"canAdminBoard"
:allow-label-remove=
"canAdminBoard"
:allow-multiselect=
"true"
:allow-scoped-labels=
"enableScopedLabels"
:selected-labels=
"board.labels"
:hide-collapsed-view=
"true"
:labels-fetch-path=
"labelsPath"
:labels-manage-path=
"labelsWebUrl"
:labels-filter-base-path=
"labelsWebUrl"
:labels-list-title=
"__('Select labels')"
:dropdown-button-text=
"__('Choose labels')"
variant=
"sidebar"
class=
"block labels"
@
onLabelRemove=
"handleLabelRemove"
@
onLabelRemove=
"handleLabelRemove"
@
updateSelectedLabels=
"handleLabelClick"
@
set-labels=
"handleLabelClick"
>
/>
{{
__
(
'
Any label
'
)
}}
</labels-select>
<assignee-select
<assignee-select
v-if=
"isIssueBoard"
v-if=
"isIssueBoard"
:board=
"board"
:board=
"board"
:can-edit=
"canAdminBoard"
:can-edit=
"canAdminBoard"
:project-id=
"projectId"
:group-id=
"groupId"
@
set-assignee=
"$emit('set-assignee', $event)"
@
set-assignee=
"$emit('set-assignee', $event)"
/>
/>
...
...
ee/app/assets/javascripts/boards/components/labels_select.vue
0 → 100644
View file @
fc2962ff
<
script
>
import
{
GlButton
}
from
'
@gitlab/ui
'
;
import
{
debounce
}
from
'
lodash
'
;
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
LabelItem
from
'
~/vue_shared/components/sidebar/labels_select_widget/label_item.vue
'
;
import
searchGroupLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
'
;
import
searchProjectLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
'
;
import
{
__
,
s__
,
sprintf
}
from
'
~/locale
'
;
import
DropdownValue
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue
'
;
import
DropdownContentsCreateView
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue
'
;
import
DropdownHeader
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
'
;
import
DropdownFooter
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue
'
;
import
DropdownWidget
from
'
~/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
'
;
export
default
{
components
:
{
DropdownWidget
,
GlButton
,
LabelItem
,
DropdownValue
,
DropdownContentsCreateView
,
DropdownHeader
,
DropdownFooter
,
},
inject
:
[
'
fullPath
'
],
props
:
{
board
:
{
type
:
Object
,
required
:
true
,
},
canEdit
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
},
data
()
{
return
{
search
:
''
,
labels
:
[],
selected
:
this
.
board
.
labels
,
isEditing
:
false
,
showDropdownContentsCreateView
:
false
,
};
},
apollo
:
{
labels
:
{
query
()
{
return
this
.
isProjectBoard
?
searchProjectLabels
:
searchGroupLabels
;
},
variables
()
{
return
{
fullPath
:
this
.
fullPath
,
searchTerm
:
this
.
search
,
first
:
20
,
};
},
skip
()
{
return
!
this
.
isEditing
;
},
update
(
data
)
{
return
data
.
workspace
?.
labels
?.
nodes
.
map
((
label
)
=>
({
...
label
,
id
:
getIdFromGraphQLId
(
label
.
id
),
}));
},
error
()
{
this
.
setError
({
message
:
this
.
$options
.
i18n
.
errorSearchingLabels
});
},
},
},
computed
:
{
...
mapState
([
'
boardType
'
]),
...
mapGetters
([
'
isProjectBoard
'
]),
isLabelsEmpty
()
{
return
this
.
selected
.
length
===
0
;
},
selectedLabelsIds
()
{
return
this
.
selected
.
map
((
label
)
=>
label
.
id
);
},
isLoading
()
{
return
this
.
$apollo
.
queries
.
labels
.
loading
;
},
selectText
()
{
if
(
!
this
.
selected
.
length
)
{
return
this
.
$options
.
i18n
.
selectLabel
;
}
else
if
(
this
.
selected
.
length
>
1
)
{
return
sprintf
(
s__
(
'
LabelSelect|%{firstLabelName} +%{remainingLabelCount} more
'
),
{
firstLabelName
:
this
.
selected
[
0
].
title
,
remainingLabelCount
:
this
.
selected
.
length
-
1
,
});
}
return
this
.
selected
[
0
].
title
;
},
footerCreateLabelTitle
()
{
return
sprintf
(
__
(
'
Create %{workspace} label
'
),
{
workspace
:
this
.
boardType
,
});
},
footerManageLabelTitle
()
{
return
sprintf
(
__
(
'
Manage %{workspace} labels
'
),
{
workspace
:
this
.
boardType
,
});
},
labelType
()
{
return
this
.
boardType
;
},
},
methods
:
{
...
mapActions
([
'
setError
'
]),
isLabelSelected
(
label
)
{
return
this
.
selectedLabelsIds
.
includes
(
label
.
id
);
},
selectLabel
(
label
)
{
let
labels
=
[];
if
(
this
.
isLabelSelected
(
label
))
{
labels
=
this
.
selected
.
filter
(({
id
})
=>
id
!==
label
.
id
);
}
else
{
labels
=
[...
this
.
selected
,
label
];
}
this
.
selected
=
labels
;
this
.
$emit
(
'
set-labels
'
,
labels
);
},
onLabelRemove
(
labelId
)
{
const
labels
=
this
.
selected
.
filter
(({
id
})
=>
getIdFromGraphQLId
(
id
)
!==
labelId
);
this
.
selected
=
labels
;
this
.
$emit
(
'
set-labels
'
,
labels
);
},
toggleEdit
()
{
if
(
!
this
.
isEditing
)
{
this
.
showDropdown
();
}
else
{
this
.
hideDropdown
();
}
},
showDropdown
()
{
this
.
isEditing
=
true
;
this
.
$refs
.
editDropdown
.
showDropdown
();
debounce
(()
=>
{
this
.
setFocus
();
},
50
)();
},
hideDropdown
()
{
this
.
isEditing
=
false
;
},
setSearch
(
search
)
{
this
.
search
=
search
;
},
toggleDropdownContentsCreateView
()
{
this
.
showDropdownContentsCreateView
=
!
this
.
showDropdownContentsCreateView
;
},
toggleDropdownContent
()
{
this
.
toggleDropdownContentsCreateView
();
// Required to recalculate dropdown position as its size changes
if
(
this
.
$refs
.
editDropdown
?.
$refs
.
dropdown
?.
$refs
.
dropdown
)
{
this
.
$refs
.
editDropdown
.
$refs
.
dropdown
.
$refs
.
dropdown
.
$_popper
.
scheduleUpdate
();
}
},
setFocus
()
{
this
.
$refs
.
header
?.
focusInput
();
},
},
i18n
:
{
label
:
s__
(
'
BoardScope|Labels
'
),
anyLabel
:
s__
(
'
BoardScope|Any label
'
),
selectLabel
:
s__
(
'
BoardScope|Choose labels
'
),
dropdownTitleText
:
s__
(
'
BoardScope|Select labels
'
),
errorSearchingLabels
:
s__
(
'
BoardScope|An error occurred while searching for labels, please try again.
'
,
),
edit
:
s__
(
'
BoardScope|Edit
'
),
},
};
</
script
>
<
template
>
<div
class=
"block labels labels-select-wrapper"
>
<div
class=
"title gl-mb-3"
>
{{
$options
.
i18n
.
label
}}
<gl-button
v-if=
"canEdit"
category=
"tertiary"
size=
"small"
class=
"edit-link float-right"
@
click=
"toggleEdit"
>
{{
$options
.
i18n
.
edit
}}
</gl-button>
</div>
<div
class=
"gl-text-gray-500 gl-mb-2"
data-testid=
"selected-labels"
>
<div
v-if=
"isLabelsEmpty"
>
{{
$options
.
i18n
.
anyLabel
}}
</div>
<dropdown-value
v-else
:disable-labels=
"isLoading"
:selected-labels=
"selected"
:allow-label-remove=
"canEdit"
:labels-filter-base-path=
"''"
:labels-filter-param=
"'label_name'"
class=
"gl-mb-2"
@
onLabelRemove=
"onLabelRemove"
/>
</div>
<dropdown-widget
v-show=
"isEditing"
ref=
"editDropdown"
:select-text=
"selectText"
:options=
"labels"
:is-loading=
"isLoading"
:selected=
"selected"
:search-term=
"search"
:allow-multiselect=
"true"
data-testid=
"labels-select-contents-list"
@
hide=
"hideDropdown"
@
set-option=
"selectLabel"
@
set-search=
"setSearch"
>
<template
#header
>
<dropdown-header
ref=
"header"
v-model=
"search"
:labels-create-title=
"footerCreateLabelTitle"
:labels-list-title=
"$options.i18n.dropdownTitleText"
:show-dropdown-contents-create-view=
"showDropdownContentsCreateView"
@
toggleDropdownContentsCreateView=
"toggleDropdownContent"
@
closeDropdown=
"hideDropdown"
@
input=
"setSearch"
/>
</
template
>
<
template
#item=
"{ item }"
>
<label-item
:label=
"item"
/>
</
template
>
<
template
v-if=
"showDropdownContentsCreateView"
#default
>
<dropdown-contents-create-view
:full-path=
"fullPath"
:workspace-type=
"boardType"
:attr-workspace-path=
"fullPath"
:label-create-type=
"labelType"
@
hideCreateView=
"toggleDropdownContent"
/>
</
template
>
<
template
#footer
>
<dropdown-footer
v-if=
"!showDropdownContentsCreateView"
:footer-create-label-title=
"footerCreateLabelTitle"
:footer-manage-label-title=
"footerManageLabelTitle"
@
toggleDropdownContentsCreateView=
"toggleDropdownContent"
/>
</
template
>
</dropdown-widget>
</div>
</template>
ee/app/assets/javascripts/boards/components/milestone_select.vue
View file @
fc2962ff
...
@@ -23,16 +23,6 @@ export default {
...
@@ -23,16 +23,6 @@ export default {
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
},
},
groupId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
projectId
:
{
type
:
Number
,
required
:
false
,
default
:
0
,
},
canEdit
:
{
canEdit
:
{
type
:
Boolean
,
type
:
Boolean
,
required
:
false
,
required
:
false
,
...
...
ee/spec/features/boards/scoped_issue_board_spec.rb
View file @
fc2962ff
...
@@ -116,7 +116,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -116,7 +116,7 @@ RSpec.describe 'Scoped issue boards', :js do
page
.
within
(
'.labels'
)
do
page
.
within
(
'.labels'
)
do
click_button
'Edit'
click_button
'Edit'
page
.
within
(
'
.labels-select-contents-list
'
)
do
page
.
within
(
'
[data-testid="labels-select-contents-list"]
'
)
do
expect
(
page
).
to
have_content
(
group_label
.
title
)
expect
(
page
).
to
have_content
(
group_label
.
title
)
expect
(
page
).
not_to
have_content
(
project_label
.
title
)
expect
(
page
).
not_to
have_content
(
project_label
.
title
)
end
end
...
@@ -381,7 +381,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -381,7 +381,7 @@ RSpec.describe 'Scoped issue boards', :js do
page
.
within
(
'.labels'
)
do
page
.
within
(
'.labels'
)
do
click_button
'Edit'
click_button
'Edit'
page
.
within
(
'
.labels-select-contents-list
'
)
do
page
.
within
(
'
[data-testid="labels-select-contents-list"]
'
)
do
expect
(
page
).
to
have_content
(
group_label
.
title
)
expect
(
page
).
to
have_content
(
group_label
.
title
)
expect
(
page
).
not_to
have_content
(
project_label
.
title
)
expect
(
page
).
not_to
have_content
(
project_label
.
title
)
end
end
...
@@ -581,7 +581,7 @@ RSpec.describe 'Scoped issue boards', :js do
...
@@ -581,7 +581,7 @@ RSpec.describe 'Scoped issue boards', :js do
click_button
'Edit'
click_button
'Edit'
if
value
.
is_a?
(
Array
)
if
value
.
is_a?
(
Array
)
value
.
each
{
|
value
|
click_
link
value
}
value
.
each
{
|
value
|
click_
on
value
}
elsif
filter
==
'weight'
elsif
filter
==
'weight'
page
.
within
(
".dropdown-menu"
)
do
page
.
within
(
".dropdown-menu"
)
do
click_button
value
click_button
value
...
...
ee/spec/features/epic_boards/epic_boards_spec.rb
View file @
fc2962ff
...
@@ -351,7 +351,7 @@ RSpec.describe 'epic boards', :js do
...
@@ -351,7 +351,7 @@ RSpec.describe 'epic boards', :js do
if
value
.
is_a?
(
Array
)
if
value
.
is_a?
(
Array
)
value
.
each
{
|
value
|
click_link
value
}
value
.
each
{
|
value
|
click_link
value
}
else
else
click_
link
value
click_
on
value
end
end
end
end
end
end
...
...
ee/spec/features/labels_hierarchy_spec.rb
View file @
fc2962ff
...
@@ -39,7 +39,7 @@ RSpec.describe 'Labels Hierarchy', :js do
...
@@ -39,7 +39,7 @@ RSpec.describe 'Labels Hierarchy', :js do
wait_for_requests
wait_for_requests
click_
link
label
.
title
click_
on
label
.
title
end
end
click_button
'Save changes'
click_button
'Save changes'
...
...
ee/spec/frontend/boards/components/assignee_select_spec.js
View file @
fc2962ff
...
@@ -35,6 +35,9 @@ describe('Assignee select component', () => {
...
@@ -35,6 +35,9 @@ describe('Assignee select component', () => {
const
createStore
=
({
isGroupBoard
=
false
,
isProjectBoard
=
false
}
=
{})
=>
{
const
createStore
=
({
isGroupBoard
=
false
,
isProjectBoard
=
false
}
=
{})
=>
{
store
=
new
Vuex
.
Store
({
store
=
new
Vuex
.
Store
({
...
defaultStore
,
...
defaultStore
,
actions
:
{
setError
:
jest
.
fn
(),
},
getters
:
{
getters
:
{
isGroupBoard
:
()
=>
isGroupBoard
,
isGroupBoard
:
()
=>
isGroupBoard
,
isProjectBoard
:
()
=>
isProjectBoard
,
isProjectBoard
:
()
=>
isProjectBoard
,
...
...
ee/spec/frontend/boards/components/board_form_spec.js
View file @
fc2962ff
...
@@ -8,7 +8,6 @@ import createEpicBoardMutation from 'ee/boards/graphql/epic_board_create.mutatio
...
@@ -8,7 +8,6 @@ import createEpicBoardMutation from 'ee/boards/graphql/epic_board_create.mutatio
import
destroyEpicBoardMutation
from
'
ee/boards/graphql/epic_board_destroy.mutation.graphql
'
;
import
destroyEpicBoardMutation
from
'
ee/boards/graphql/epic_board_destroy.mutation.graphql
'
;
import
updateEpicBoardMutation
from
'
ee/boards/graphql/epic_board_update.mutation.graphql
'
;
import
updateEpicBoardMutation
from
'
ee/boards/graphql/epic_board_update.mutation.graphql
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
formType
}
from
'
~/boards/constants
'
;
import
{
formType
}
from
'
~/boards/constants
'
;
...
@@ -37,8 +36,6 @@ const currentBoard = {
...
@@ -37,8 +36,6 @@ const currentBoard = {
const
defaultProps
=
{
const
defaultProps
=
{
canAdminBoard
:
false
,
canAdminBoard
:
false
,
labelsPath
:
`
${
TEST_HOST
}
/labels/path`
,
labelsWebUrl
:
`
${
TEST_HOST
}
/-/labels`
,
currentBoard
,
currentBoard
,
currentPage
:
''
,
currentPage
:
''
,
};
};
...
...
ee/spec/frontend/boards/components/board_scope_spec.js
View file @
fc2962ff
...
@@ -2,8 +2,9 @@ import { mount } from '@vue/test-utils';
...
@@ -2,8 +2,9 @@ import { mount } from '@vue/test-utils';
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
Vuex
from
'
vuex
'
;
import
BoardScope
from
'
ee/boards/components/board_scope.vue
'
;
import
BoardScope
from
'
ee/boards/components/board_scope.vue
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
BoardLabelsSelect
from
'
ee/boards/components/labels_select.vue
'
;
import
LabelsSelect
from
'
~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
'
;
import
{
mockLabel1
}
from
'
jest/boards/mock_data
'
;
Vue
.
use
(
Vuex
);
Vue
.
use
(
Vuex
);
...
@@ -26,17 +27,16 @@ describe('BoardScope', () => {
...
@@ -26,17 +27,16 @@ describe('BoardScope', () => {
store
,
store
,
propsData
:
{
propsData
:
{
collapseScope
:
false
,
collapseScope
:
false
,
canAdminBoard
:
fals
e
,
canAdminBoard
:
tru
e
,
board
:
{
board
:
{
labels
:
[],
labels
:
[],
assignee
:
{},
assignee
:
{},
},
},
labelsPath
:
`
${
TEST_HOST
}
/labels`
,
labelsWebUrl
:
`
${
TEST_HOST
}
/-/labels`
,
},
},
stubs
:
{
stubs
:
{
AssigneeSelect
:
true
,
AssigneeSelect
:
true
,
BoardMilestoneSelect
:
true
,
BoardMilestoneSelect
:
true
,
BoardLabelsSelect
:
true
,
},
},
});
});
}
}
...
@@ -49,15 +49,13 @@ describe('BoardScope', () => {
...
@@ -49,15 +49,13 @@ describe('BoardScope', () => {
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
const
findLabelSelect
=
()
=>
wrapper
.
findComponent
(
LabelsSelect
);
const
findLabelSelect
=
()
=>
wrapper
.
findComponent
(
Board
LabelsSelect
);
describe
(
'
BoardScope
'
,
()
=>
{
describe
(
'
BoardScope
'
,
()
=>
{
it
(
'
emits selected labels to be added and removed from the board
'
,
async
()
=>
{
it
(
'
emits selected labels to be added and removed from the board
'
,
async
()
=>
{
const
labels
=
[
{
id
:
'
1
'
,
set
:
true
,
color
:
'
#BADA55
'
,
text_color
:
'
#FFFFFF
'
}
];
const
labels
=
[
mockLabel1
];
expect
(
findLabelSelect
().
exists
()).
toBe
(
true
);
expect
(
findLabelSelect
().
exists
()).
toBe
(
true
);
expect
(
findLabelSelect
().
text
()).
toContain
(
'
Any label
'
);
findLabelSelect
().
vm
.
$emit
(
'
set-labels
'
,
labels
);
expect
(
findLabelSelect
().
props
(
'
selectedLabels
'
)).
toHaveLength
(
0
);
findLabelSelect
().
vm
.
$emit
(
'
updateSelectedLabels
'
,
labels
);
await
nextTick
();
await
nextTick
();
expect
(
wrapper
.
emitted
(
'
set-board-labels
'
)).
toEqual
([[
labels
]]);
expect
(
wrapper
.
emitted
(
'
set-board-labels
'
)).
toEqual
([[
labels
]]);
});
});
...
...
ee/spec/frontend/boards/components/boards_selector_spec.js
View file @
fc2962ff
...
@@ -86,10 +86,6 @@ describe('BoardsSelector', () => {
...
@@ -86,10 +86,6 @@ describe('BoardsSelector', () => {
hasMissingBoards
:
false
,
hasMissingBoards
:
false
,
canAdminBoard
:
true
,
canAdminBoard
:
true
,
multipleIssueBoardsAvailable
:
true
,
multipleIssueBoardsAvailable
:
true
,
labelsPath
:
`
${
TEST_HOST
}
/labels/path`
,
labelsWebUrl
:
`
${
TEST_HOST
}
/labels`
,
projectId
:
42
,
groupId
:
19
,
scopedIssueBoardFeatureEnabled
:
true
,
scopedIssueBoardFeatureEnabled
:
true
,
weights
:
[],
weights
:
[],
},
},
...
...
ee/spec/frontend/boards/components/labels_select_spec.js
0 → 100644
View file @
fc2962ff
import
{
GlButton
,
GlDropdown
,
GlDropdownItem
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
nextTick
}
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
Vuex
from
'
vuex
'
;
import
LabelsSelect
from
'
ee/boards/components/labels_select.vue
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
boardObj
,
mockProjectLabelsResponse
,
mockGroupLabelsResponse
,
mockLabel1
,
}
from
'
jest/boards/mock_data
'
;
import
defaultStore
from
'
~/boards/stores
'
;
import
searchGroupLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
'
;
import
searchProjectLabels
from
'
~/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
'
;
import
DropdownValue
from
'
~/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue
'
;
import
DropdownWidget
from
'
~/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
'
;
const
localVue
=
createLocalVue
();
localVue
.
use
(
VueApollo
);
describe
(
'
Labels select component
'
,
()
=>
{
let
wrapper
;
let
fakeApollo
;
let
store
;
const
selectedText
=
()
=>
wrapper
.
find
(
'
[data-testid="selected-labels"]
'
).
text
();
const
findEditButton
=
()
=>
wrapper
.
findComponent
(
GlButton
);
const
findDropdown
=
()
=>
wrapper
.
findComponent
(
DropdownWidget
);
const
findDropdownValue
=
()
=>
wrapper
.
findComponent
(
DropdownValue
);
const
projectLabelsQueryHandlerSuccess
=
jest
.
fn
().
mockResolvedValue
(
mockProjectLabelsResponse
);
const
groupLabelsQueryHandlerSuccess
=
jest
.
fn
().
mockResolvedValue
(
mockGroupLabelsResponse
);
async
function
openLabelsDropdown
()
{
findEditButton
().
vm
.
$emit
(
'
click
'
);
await
waitForPromises
();
}
const
createStore
=
({
isGroupBoard
=
false
,
isProjectBoard
=
false
}
=
{})
=>
{
store
=
new
Vuex
.
Store
({
...
defaultStore
,
actions
:
{
setError
:
jest
.
fn
(),
},
getters
:
{
isGroupBoard
:
()
=>
isGroupBoard
,
isProjectBoard
:
()
=>
isProjectBoard
,
},
state
:
{
boardType
:
isGroupBoard
?
'
group
'
:
'
project
'
,
},
});
};
const
createComponent
=
({
props
=
{}
}
=
{})
=>
{
fakeApollo
=
createMockApollo
([
[
searchProjectLabels
,
projectLabelsQueryHandlerSuccess
],
[
searchGroupLabels
,
groupLabelsQueryHandlerSuccess
],
]);
wrapper
=
shallowMount
(
LabelsSelect
,
{
localVue
,
store
,
apolloProvider
:
fakeApollo
,
propsData
:
{
board
:
boardObj
,
canEdit
:
true
,
...
props
,
},
provide
:
{
fullPath
:
'
gitlab-org
'
,
labelsManagePath
:
'
gitlab-org/labels
'
,
},
stubs
:
{
GlDropdown
,
GlDropdownItem
,
},
});
// We need to mock out `showDropdown` which
// invokes `show` method of BDropdown used inside GlDropdown.
wrapper
.
vm
.
$refs
.
editDropdown
.
showDropdown
=
jest
.
fn
();
};
beforeEach
(()
=>
{
createStore
({
isProjectBoard
:
true
});
createComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
fakeApollo
=
null
;
store
=
null
;
});
describe
(
'
when not editing
'
,
()
=>
{
it
(
'
defaults to Any label
'
,
()
=>
{
expect
(
selectedText
()).
toContain
(
'
Any label
'
);
});
it
(
'
skips the queries and does not render dropdown
'
,
()
=>
{
expect
(
projectLabelsQueryHandlerSuccess
).
not
.
toHaveBeenCalled
();
expect
(
findDropdown
().
isVisible
()).
toBe
(
false
);
});
it
(
'
renders selected labels in DropdownValue
'
,
async
()
=>
{
await
openLabelsDropdown
();
findDropdown
().
vm
.
$emit
(
'
set-option
'
,
mockLabel1
);
await
openLabelsDropdown
();
expect
(
findDropdownValue
().
isVisible
()).
toBe
(
true
);
expect
(
findDropdownValue
().
props
(
'
selectedLabels
'
)).
toEqual
([
mockLabel1
]);
});
});
describe
(
'
when editing
'
,
()
=>
{
it
(
'
trigger query and renders dropdown with passed labels
'
,
async
()
=>
{
await
openLabelsDropdown
();
expect
(
projectLabelsQueryHandlerSuccess
).
toHaveBeenCalled
();
expect
(
findDropdown
().
isVisible
()).
toBe
(
true
);
expect
(
findDropdown
().
props
(
'
options
'
)).
toHaveLength
(
2
);
});
});
describe
(
'
canEdit
'
,
()
=>
{
it
(
'
hides Edit button
'
,
async
()
=>
{
wrapper
.
setProps
({
canEdit
:
false
});
await
nextTick
();
expect
(
findEditButton
().
exists
()).
toBe
(
false
);
});
it
(
'
shows Edit button if true
'
,
()
=>
{
expect
(
findEditButton
().
exists
()).
toBe
(
true
);
});
});
it
.
each
`
boardType | mockedResponse | queryHandler | notCalledHandler
${
'
group
'
}
|
${
mockGroupLabelsResponse
}
|
${
groupLabelsQueryHandlerSuccess
}
|
${
projectLabelsQueryHandlerSuccess
}
${
'
project
'
}
|
${
mockProjectLabelsResponse
}
|
${
projectLabelsQueryHandlerSuccess
}
|
${
groupLabelsQueryHandlerSuccess
}
`
(
'
fetches $boardType labels
'
,
async
({
boardType
,
mockedResponse
,
queryHandler
,
notCalledHandler
})
=>
{
createStore
({
isProjectBoard
:
boardType
===
'
project
'
,
isGroupBoard
:
boardType
===
'
group
'
});
createComponent
({
[
queryHandler
]:
jest
.
fn
().
mockResolvedValue
(
mockedResponse
),
});
await
openLabelsDropdown
();
expect
(
queryHandler
).
toHaveBeenCalled
();
expect
(
notCalledHandler
).
not
.
toHaveBeenCalled
();
},
);
});
locale/gitlab.pot
View file @
fc2962ff
...
@@ -5491,6 +5491,9 @@ msgstr ""
...
@@ -5491,6 +5491,9 @@ msgstr ""
msgid "BoardScope|An error occurred while getting milestones, please try again."
msgid "BoardScope|An error occurred while getting milestones, please try again."
msgstr ""
msgstr ""
msgid "BoardScope|An error occurred while searching for labels, please try again."
msgstr ""
msgid "BoardScope|An error occurred while searching for users, please try again."
msgid "BoardScope|An error occurred while searching for users, please try again."
msgstr ""
msgstr ""
...
@@ -5500,12 +5503,21 @@ msgstr ""
...
@@ -5500,12 +5503,21 @@ msgstr ""
msgid "BoardScope|Any assignee"
msgid "BoardScope|Any assignee"
msgstr ""
msgstr ""
msgid "BoardScope|Any label"
msgstr ""
msgid "BoardScope|Assignee"
msgid "BoardScope|Assignee"
msgstr ""
msgstr ""
msgid "BoardScope|Choose labels"
msgstr ""
msgid "BoardScope|Edit"
msgid "BoardScope|Edit"
msgstr ""
msgstr ""
msgid "BoardScope|Labels"
msgstr ""
msgid "BoardScope|Milestone"
msgid "BoardScope|Milestone"
msgstr ""
msgstr ""
...
@@ -5518,6 +5530,9 @@ msgstr ""
...
@@ -5518,6 +5530,9 @@ msgstr ""
msgid "BoardScope|Select assignee"
msgid "BoardScope|Select assignee"
msgstr ""
msgstr ""
msgid "BoardScope|Select labels"
msgstr ""
msgid "BoardScope|Select milestone"
msgid "BoardScope|Select milestone"
msgstr ""
msgstr ""
...
...
spec/frontend/boards/components/board_form_spec.js
View file @
fc2962ff
import
{
GlModal
}
from
'
@gitlab/ui
'
;
import
{
GlModal
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
setWindowLocation
from
'
helpers/set_window_location_helper
'
;
import
setWindowLocation
from
'
helpers/set_window_location_helper
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
BoardForm
from
'
~/boards/components/board_form.vue
'
;
import
BoardForm
from
'
~/boards/components/board_form.vue
'
;
...
@@ -31,8 +30,6 @@ const currentBoard = {
...
@@ -31,8 +30,6 @@ const currentBoard = {
const
defaultProps
=
{
const
defaultProps
=
{
canAdminBoard
:
false
,
canAdminBoard
:
false
,
labelsPath
:
`
${
TEST_HOST
}
/labels/path`
,
labelsWebUrl
:
`
${
TEST_HOST
}
/-/labels`
,
currentBoard
,
currentBoard
,
currentPage
:
''
,
currentPage
:
''
,
};
};
...
...
spec/frontend/boards/components/boards_selector_spec.js
View file @
fc2962ff
...
@@ -78,10 +78,6 @@ describe('BoardsSelector', () => {
...
@@ -78,10 +78,6 @@ describe('BoardsSelector', () => {
hasMissingBoards
:
false
,
hasMissingBoards
:
false
,
canAdminBoard
:
true
,
canAdminBoard
:
true
,
multipleIssueBoardsAvailable
:
true
,
multipleIssueBoardsAvailable
:
true
,
labelsPath
:
`
${
TEST_HOST
}
/labels/path`
,
labelsWebUrl
:
`
${
TEST_HOST
}
/labels`
,
projectId
:
42
,
groupId
:
19
,
scopedIssueBoardFeatureEnabled
:
true
,
scopedIssueBoardFeatureEnabled
:
true
,
weights
:
[],
weights
:
[],
},
},
...
...
spec/frontend/boards/mock_data.js
View file @
fc2962ff
...
@@ -12,6 +12,7 @@ export const boardObj = {
...
@@ -12,6 +12,7 @@ export const boardObj = {
id
:
1
,
id
:
1
,
name
:
'
test
'
,
name
:
'
test
'
,
milestone_id
:
null
,
milestone_id
:
null
,
labels
:
[],
};
};
export
const
listObj
=
{
export
const
listObj
=
{
...
@@ -609,3 +610,43 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
...
@@ -609,3 +610,43 @@ export const mockTokens = (fetchLabels, fetchAuthors, fetchMilestones) => [
unique
:
true
,
unique
:
true
,
},
},
];
];
export
const
mockLabel1
=
{
id
:
'
gid://gitlab/GroupLabel/121
'
,
title
:
'
To Do
'
,
color
:
'
#F0AD4E
'
,
textColor
:
'
#FFFFFF
'
,
description
:
null
,
};
export
const
mockLabel2
=
{
id
:
'
gid://gitlab/GroupLabel/122
'
,
title
:
'
Doing
'
,
color
:
'
#F0AD4E
'
,
textColor
:
'
#FFFFFF
'
,
description
:
null
,
};
export
const
mockProjectLabelsResponse
=
{
data
:
{
workspace
:
{
id
:
'
gid://gitlab/Project/1
'
,
labels
:
{
nodes
:
[
mockLabel1
,
mockLabel2
],
},
},
__typename
:
'
Project
'
,
},
};
export
const
mockGroupLabelsResponse
=
{
data
:
{
workspace
:
{
id
:
'
gid://gitlab/Group/1
'
,
labels
:
{
nodes
:
[
mockLabel1
,
mockLabel2
],
},
},
__typename
:
'
Group
'
,
},
};
spec/frontend/vue_shared/components/dropdown/dropdown_widget_spec.js
View file @
fc2962ff
...
@@ -34,6 +34,7 @@ describe('DropdownWidget component', () => {
...
@@ -34,6 +34,7 @@ describe('DropdownWidget component', () => {
// invokes `show` method of BDropdown used inside GlDropdown.
// invokes `show` method of BDropdown used inside GlDropdown.
// Context: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54895#note_524281679
// Context: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/54895#note_524281679
jest
.
spyOn
(
wrapper
.
vm
,
'
showDropdown
'
).
mockImplementation
();
jest
.
spyOn
(
wrapper
.
vm
,
'
showDropdown
'
).
mockImplementation
();
jest
.
spyOn
(
findDropdown
().
vm
,
'
hide
'
).
mockImplementation
();
};
};
beforeEach
(()
=>
{
beforeEach
(()
=>
{
...
@@ -67,10 +68,7 @@ describe('DropdownWidget component', () => {
...
@@ -67,10 +68,7 @@ describe('DropdownWidget component', () => {
});
});
it
(
'
emits set-option event when clicking on an option
'
,
async
()
=>
{
it
(
'
emits set-option event when clicking on an option
'
,
async
()
=>
{
wrapper
wrapper
.
findAll
(
'
[data-testid="unselected-option"]
'
).
at
(
1
).
trigger
(
'
click
'
);
.
findAll
(
'
[data-testid="unselected-option"]
'
)
.
at
(
1
)
.
vm
.
$emit
(
'
click
'
,
new
Event
(
'
click
'
));
await
wrapper
.
vm
.
$nextTick
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
wrapper
.
emitted
(
'
set-option
'
)).
toEqual
([[
wrapper
.
props
().
options
[
1
]]]);
expect
(
wrapper
.
emitted
(
'
set-option
'
)).
toEqual
([[
wrapper
.
props
().
options
[
1
]]]);
...
...
spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
View file @
fc2962ff
...
@@ -92,6 +92,7 @@ export const createLabelSuccessfulResponse = {
...
@@ -92,6 +92,7 @@ export const createLabelSuccessfulResponse = {
export
const
workspaceLabelsQueryResponse
=
{
export
const
workspaceLabelsQueryResponse
=
{
data
:
{
data
:
{
workspace
:
{
workspace
:
{
id
:
'
gid://gitlab/Project/126
'
,
labels
:
{
labels
:
{
nodes
:
[
nodes
:
[
{
{
...
...
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