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
efe1beb2
Commit
efe1beb2
authored
Feb 25, 2021
by
Florie Guibert
Committed by
Kushal Pandya
Feb 25, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add label list to Epic board
Add new label list to epic board using add column
parent
b420142b
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
245 additions
and
22 deletions
+245
-22
app/assets/javascripts/boards/components/board_add_new_column.vue
...ts/javascripts/boards/components/board_add_new_column.vue
+5
-4
app/assets/javascripts/boards/stores/actions.js
app/assets/javascripts/boards/stores/actions.js
+7
-3
app/assets/stylesheets/page_bundles/boards.scss
app/assets/stylesheets/page_bundles/boards.scss
+1
-0
app/views/shared/issuable/_search_bar.html.haml
app/views/shared/issuable/_search_bar.html.haml
+1
-1
ee/app/assets/javascripts/boards/graphql/epic_board_list.fragment.graphql
...vascripts/boards/graphql/epic_board_list.fragment.graphql
+11
-0
ee/app/assets/javascripts/boards/graphql/epic_board_list_create.mutation.graphql
...ts/boards/graphql/epic_board_list_create.mutation.graphql
+10
-0
ee/app/assets/javascripts/boards/graphql/epic_board_lists.query.graphql
...javascripts/boards/graphql/epic_board_lists.query.graphql
+2
-8
ee/app/assets/javascripts/boards/stores/actions.js
ee/app/assets/javascripts/boards/stores/actions.js
+45
-0
ee/app/assets/javascripts/boards/stores/mutation_types.js
ee/app/assets/javascripts/boards/stores/mutation_types.js
+1
-0
ee/app/assets/javascripts/epic_boards/index.js
ee/app/assets/javascripts/epic_boards/index.js
+16
-0
ee/spec/features/epic_boards/epic_boards_spec.rb
ee/spec/features/epic_boards/epic_boards_spec.rb
+24
-2
ee/spec/frontend/boards/stores/actions_spec.js
ee/spec/frontend/boards/stores/actions_spec.js
+108
-0
spec/frontend/boards/stores/actions_spec.js
spec/frontend/boards/stores/actions_spec.js
+14
-4
No files found.
app/assets/javascripts/boards/components/board_add_new_column.vue
View file @
efe1beb2
...
...
@@ -45,7 +45,7 @@ export default {
};
},
computed
:
{
...
mapState
([
'
labels
'
,
'
labelsLoading
'
]),
...
mapState
([
'
labels
'
,
'
labelsLoading
'
,
'
isEpicBoard
'
]),
...
mapGetters
([
'
getListByLabelId
'
,
'
shouldUseGraphQL
'
]),
selectedLabel
()
{
return
this
.
labels
.
find
(({
id
})
=>
id
===
this
.
selectedLabelId
);
...
...
@@ -57,7 +57,7 @@ export default {
methods
:
{
...
mapActions
([
'
createList
'
,
'
fetchLabels
'
,
'
highlightList
'
,
'
setAddColumnFormVisibility
'
]),
getListByLabel
(
label
)
{
if
(
this
.
shouldUseGraphQL
)
{
if
(
this
.
shouldUseGraphQL
||
this
.
isEpicBoard
)
{
return
this
.
getListByLabelId
(
label
);
}
return
boardsStore
.
findListByLabelId
(
label
.
id
);
...
...
@@ -66,7 +66,7 @@ export default {
return
Boolean
(
this
.
getListByLabel
(
label
));
},
highlight
(
listId
)
{
if
(
this
.
shouldUseGraphQL
)
{
if
(
this
.
shouldUseGraphQL
||
this
.
isEpicBoard
)
{
this
.
highlightList
(
listId
);
}
else
{
const
list
=
boardsStore
.
state
.
lists
.
find
(({
id
})
=>
id
===
listId
);
...
...
@@ -95,7 +95,7 @@ export default {
return
;
}
if
(
this
.
shouldUseGraphQL
)
{
if
(
this
.
shouldUseGraphQL
||
this
.
isEpicBoard
)
{
this
.
createList
({
labelId
:
this
.
selectedLabelId
});
}
else
{
boardsStore
.
new
({
...
...
@@ -127,6 +127,7 @@ export default {
<
template
>
<div
class=
"board-add-new-list board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal gl-flex-shrink-0"
data-testid=
"board-add-new-column"
data-qa-selector=
"board_add_new_list"
>
<div
...
...
app/assets/javascripts/boards/stores/actions.js
View file @
efe1beb2
...
...
@@ -128,7 +128,11 @@ export default {
},
flashAnimationDuration
);
},
createList
:
(
createList
:
({
dispatch
},
{
backlog
,
labelId
,
milestoneId
,
assigneeId
})
=>
{
dispatch
(
'
createIssueList
'
,
{
backlog
,
labelId
,
milestoneId
,
assigneeId
});
},
createIssueList
:
(
{
state
,
commit
,
dispatch
,
getters
},
{
backlog
,
labelId
,
milestoneId
,
assigneeId
},
)
=>
{
...
...
@@ -172,7 +176,7 @@ export default {
},
fetchLabels
:
({
state
,
commit
,
getters
},
searchTerm
)
=>
{
const
{
fullPath
,
boardType
}
=
state
;
const
{
fullPath
,
boardType
,
isEpicBoard
}
=
state
;
const
variables
=
{
fullPath
,
...
...
@@ -191,7 +195,7 @@ export default {
.
then
(({
data
})
=>
{
let
labels
=
data
[
boardType
]?.
labels
.
nodes
;
if
(
!
getters
.
shouldUseGraphQL
)
{
if
(
!
getters
.
shouldUseGraphQL
&&
!
isEpicBoard
)
{
labels
=
labels
.
map
((
label
)
=>
({
...
label
,
id
:
getIdFromGraphQLId
(
label
.
id
),
...
...
app/assets/stylesheets/page_bundles/boards.scss
View file @
efe1beb2
...
...
@@ -30,6 +30,7 @@
color
:
var
(
--
gray-500
,
$gray-500
);
}
[
data-page
$
=
'epic_boards:show'
],
.issue-boards-page
{
.content-wrapper
{
padding-bottom
:
0
;
...
...
app/views/shared/issuable/_search_bar.html.haml
View file @
efe1beb2
...
...
@@ -197,7 +197,7 @@
#js-board-epics-swimlanes-toggle
.js-board-config
{
data:
{
can_admin_list:
user_can_admin_list
.
to_s
,
has_scope:
board
.
scoped?
.
to_s
}
}
-
if
user_can_admin_list
-
if
Feature
.
enabled?
(
:board_new_list
,
board
.
resource_parent
,
default_enabled: :yaml
)
-
if
Feature
.
enabled?
(
:board_new_list
,
board
.
resource_parent
,
default_enabled: :yaml
)
||
board
.
to_type
==
"EpicBoard"
.js-create-column-trigger
{
data:
board_list_data
}
-
else
=
render
'shared/issuable/board_create_list_dropdown'
,
board:
board
...
...
ee/app/assets/javascripts/boards/graphql/epic_board_list.fragment.graphql
0 → 100644
View file @
efe1beb2
#import "~/graphql_shared/fragments/label.fragment.graphql"
fragment
EpicBoardListFragment
on
EpicList
{
id
title
position
listType
label
{
...
Label
}
}
ee/app/assets/javascripts/boards/graphql/epic_board_list_create.mutation.graphql
0 → 100644
View file @
efe1beb2
#import "./epic_board_list.fragment.graphql"
mutation
CreateEpicBoardList
(
$boardId
:
BoardsEpicBoardID
!,
$backlog
:
Boolean
,
$labelId
:
LabelID
)
{
epicBoardListCreate
(
input
:
{
boardId
:
$boardId
,
backlog
:
$backlog
,
labelId
:
$labelId
})
{
list
{
...
EpicBoardListFragment
}
errors
}
}
ee/app/assets/javascripts/boards/graphql/epic_board_lists.query.graphql
View file @
efe1beb2
#import "
~/graphql_shared/fragments/label
.fragment.graphql"
#import "
./epic_board_list
.fragment.graphql"
query
ListEpics
(
$fullPath
:
ID
!,
$boardId
:
BoardsEpicBoardID
!)
{
group
(
fullPath
:
$fullPath
)
{
epicBoard
(
id
:
$boardId
)
{
lists
{
nodes
{
id
title
position
listType
label
{
...
Label
}
...
EpicBoardListFragment
}
}
}
...
...
ee/app/assets/javascripts/boards/stores/actions.js
View file @
efe1beb2
...
...
@@ -30,6 +30,7 @@ import {
import
{
EpicFilterType
,
IterationFilterType
,
GroupByParamType
}
from
'
../constants
'
;
import
epicQuery
from
'
../graphql/epic.query.graphql
'
;
import
createEpicBoardListMutation
from
'
../graphql/epic_board_list_create.mutation.graphql
'
;
import
epicBoardListsQuery
from
'
../graphql/epic_board_lists.query.graphql
'
;
import
epicsSwimlanesQuery
from
'
../graphql/epics_swimlanes.query.graphql
'
;
import
issueMoveListMutation
from
'
../graphql/issue_move_list.mutation.graphql
'
;
...
...
@@ -554,4 +555,48 @@ export default {
})
.
catch
(()
=>
commit
(
types
.
RECEIVE_BOARD_LISTS_FAILURE
));
},
createList
:
({
state
,
dispatch
},
{
backlog
,
labelId
,
milestoneId
,
assigneeId
})
=>
{
const
{
isEpicBoard
}
=
state
;
if
(
!
isEpicBoard
)
{
dispatch
(
'
createIssueList
'
,
{
backlog
,
labelId
,
milestoneId
,
assigneeId
});
}
else
{
dispatch
(
'
createEpicList
'
,
{
backlog
,
labelId
});
}
},
createEpicList
:
({
state
,
commit
,
dispatch
,
getters
},
{
backlog
,
labelId
})
=>
{
const
{
boardId
}
=
state
;
const
existingList
=
getters
.
getListByLabelId
(
labelId
);
if
(
existingList
)
{
dispatch
(
'
highlightList
'
,
existingList
.
id
);
return
;
}
gqlClient
.
mutate
({
mutation
:
createEpicBoardListMutation
,
variables
:
{
boardId
:
fullEpicBoardId
(
boardId
),
backlog
,
labelId
,
},
})
.
then
(({
data
})
=>
{
if
(
data
?.
epicBoardListCreate
?.
errors
.
length
)
{
commit
(
types
.
CREATE_LIST_FAILURE
);
}
else
{
const
list
=
data
.
epicBoardListCreate
?.
list
;
dispatch
(
'
addList
'
,
list
);
dispatch
(
'
highlightList
'
,
list
.
id
);
}
})
.
catch
((
e
)
=>
{
commit
(
types
.
CREATE_LIST_FAILURE
);
throw
e
;
});
},
};
ee/app/assets/javascripts/boards/stores/mutation_types.js
View file @
efe1beb2
...
...
@@ -31,4 +31,5 @@ export const SET_FILTERS = 'SET_FILTERS';
export
const
MOVE_ISSUE
=
'
MOVE_ISSUE
'
;
export
const
MOVE_ISSUE_SUCCESS
=
'
MOVE_ISSUE_SUCCESS
'
;
export
const
MOVE_ISSUE_FAILURE
=
'
MOVE_ISSUE_FAILURE
'
;
export
const
CREATE_LIST_FAILURE
=
'
CREATE_LIST_FAILURE
'
;
export
const
SET_BOARD_EPIC_USER_PREFERENCES
=
'
SET_BOARD_EPIC_USER_PREFERENCES
'
;
ee/app/assets/javascripts/epic_boards/index.js
View file @
efe1beb2
...
...
@@ -9,6 +9,7 @@ import { mapActions } from 'vuex';
import
BoardSidebar
from
'
ee_component/boards/components/board_sidebar
'
;
import
toggleLabels
from
'
ee_component/boards/toggle_labels
'
;
import
BoardAddNewColumnTrigger
from
'
~/boards/components/board_add_new_column_trigger.vue
'
;
import
BoardContent
from
'
~/boards/components/board_content.vue
'
;
import
BoardAddIssuesModal
from
'
~/boards/components/modal/index.vue
'
;
import
mountMultipleBoardsSwitcher
from
'
~/boards/mount_multiple_boards_switcher
'
;
...
...
@@ -110,6 +111,21 @@ export default () => {
},
});
const
createColumnTriggerEl
=
document
.
querySelector
(
'
.js-create-column-trigger
'
);
if
(
createColumnTriggerEl
)
{
// eslint-disable-next-line no-new
new
Vue
({
el
:
createColumnTriggerEl
,
components
:
{
BoardAddNewColumnTrigger
,
},
store
,
render
(
createElement
)
{
return
createElement
(
BoardAddNewColumnTrigger
);
},
});
}
toggleLabels
();
mountMultipleBoardsSwitcher
({
...
...
ee/spec/features/epic_boards/epic_boards_spec.rb
View file @
efe1beb2
...
...
@@ -8,16 +8,19 @@ RSpec.describe 'epic boards', :js do
let_it_be
(
:epic_board
)
{
create
(
:epic_board
,
group:
group
)
}
let_it_be
(
:label
)
{
create
(
:group_label
,
group:
group
,
name:
'Label1'
)
}
let_it_be
(
:label2
)
{
create
(
:group_label
,
group:
group
,
name:
'Label2'
)
}
let_it_be
(
:label_list
)
{
create
(
:epic_list
,
epic_board:
epic_board
,
label:
label
,
position:
0
)
}
let_it_be
(
:backlog_list
)
{
create
(
:epic_list
,
epic_board:
epic_board
,
list_type: :closed
)
}
let_it_be
(
:closed_list
)
{
create
(
:epic_list
,
epic_board:
epic_board
,
list_type: :backlog
)
}
let_it_be
(
:epic1
)
{
create
(
:epic
,
group:
group
,
labels:
[
label
],
title:
'Epic1'
)
}
let_it_be
(
:epic2
)
{
create
(
:epic
,
group:
group
,
title:
'Epic2'
)
}
let_it_be
(
:epic3
)
{
create
(
:epic
,
group:
group
,
labels:
[
label2
],
title:
'Epic3'
)
}
context
'display epics in board'
do
before
do
stub_licensed_features
(
epics:
true
)
group
.
add_maintainer
(
user
)
sign_in
(
user
)
visit_epic_boards_page
end
...
...
@@ -34,10 +37,14 @@ RSpec.describe 'epic boards', :js do
end
end
it
'displays
one epic
in Open list'
do
it
'displays
two epics
in Open list'
do
page
.
within
(
"[data-board-type='backlog']"
)
do
expect
(
page
).
to
have_selector
(
'.board-card'
,
count:
1
)
expect
(
page
).
to
have_selector
(
'.board-card'
,
count:
2
)
page
.
within
(
first
(
'.board-card'
))
do
expect
(
page
).
to
have_content
(
'Epic3'
)
end
page
.
within
(
'.board-card:nth-child(2)'
)
do
expect
(
page
).
to
have_content
(
'Epic2'
)
end
end
...
...
@@ -51,6 +58,21 @@ RSpec.describe 'epic boards', :js do
end
end
end
it
'creates new column for label containing labeled issue'
do
click_button
'Create list'
wait_for_all_requests
page
.
within
(
"[data-testid='board-add-new-column']"
)
do
find
(
'label'
,
text:
label2
.
title
).
click
click_button
'Add'
end
wait_for_all_requests
expect
(
page
).
to
have_selector
(
'.board'
,
text:
label2
.
title
)
expect
(
find
(
'.board:nth-child(3) .board-card'
)).
to
have_content
(
epic3
.
title
)
end
end
def
visit_epic_boards_page
...
...
ee/spec/frontend/boards/stores/actions_spec.js
View file @
efe1beb2
...
...
@@ -947,4 +947,112 @@ describe('moveIssue', () => {
done
,
);
});
describe
.
each
`
isEpicBoard | dispatchedAction
${
false
}
|
${
'
createIssueList
'
}
${
true
}
|
${
'
createEpicList
'
}
`
(
'
createList
'
,
({
isEpicBoard
,
dispatchedAction
})
=>
{
it
(
`should dispatch
${
dispatchedAction
}
action when isEpicBoard is
${
isEpicBoard
}
on state`
,
async
()
=>
{
await
testAction
({
action
:
actions
.
createList
,
payload
:
{
backlog
:
true
},
state
:
{
isEpicBoard
},
expectedActions
:
[{
type
:
dispatchedAction
,
payload
:
{
backlog
:
true
}
}],
});
});
});
describe
(
'
createEpicList
'
,
()
=>
{
let
commit
;
let
dispatch
;
let
getters
;
beforeEach
(()
=>
{
commit
=
jest
.
fn
();
dispatch
=
jest
.
fn
();
getters
=
{
getListByLabelId
:
jest
.
fn
(),
};
});
it
(
'
should dispatch addList action when creating backlog list
'
,
async
()
=>
{
const
backlogList
=
{
id
:
'
gid://gitlab/List/1
'
,
listType
:
'
backlog
'
,
title
:
'
Open
'
,
position
:
0
,
};
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
({
data
:
{
epicBoardListCreate
:
{
list
:
backlogList
,
errors
:
[],
},
},
});
await
actions
.
createEpicList
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
addList
'
,
backlogList
);
});
it
(
'
dispatches highlightList after addList has succeeded
'
,
async
()
=>
{
const
list
=
{
id
:
'
gid://gitlab/List/1
'
,
listType
:
'
label
'
,
title
:
'
Open
'
,
labelId
:
'
4
'
,
};
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
({
data
:
{
epicBoardListCreate
:
{
list
,
errors
:
[],
},
},
});
await
actions
.
createEpicList
({
getters
,
state
,
commit
,
dispatch
},
{
labelId
:
'
4
'
});
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
addList
'
,
list
);
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
highlightList
'
,
list
.
id
);
});
it
(
'
should commit CREATE_LIST_FAILURE mutation when API returns an error
'
,
async
()
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
({
data
:
{
epicBoardListCreate
:
{
list
:
{},
errors
:
[{
foo
:
'
bar
'
}],
},
},
});
await
actions
.
createEpicList
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
expect
(
commit
).
toHaveBeenCalledWith
(
types
.
CREATE_LIST_FAILURE
);
});
it
(
'
highlights list and does not re-query if it already exists
'
,
async
()
=>
{
const
existingList
=
{
id
:
'
gid://gitlab/List/1
'
,
listType
:
'
label
'
,
title
:
'
Some label
'
,
position
:
1
,
};
getters
=
{
getListByLabelId
:
jest
.
fn
().
mockReturnValue
(
existingList
),
};
await
actions
.
createEpicList
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
highlightList
'
,
existingList
.
id
);
expect
(
dispatch
).
toHaveBeenCalledTimes
(
1
);
expect
(
commit
).
not
.
toHaveBeenCalled
();
});
});
});
spec/frontend/boards/stores/actions_spec.js
View file @
efe1beb2
...
...
@@ -210,6 +210,16 @@ describe('fetchIssueLists', () => {
});
describe
(
'
createList
'
,
()
=>
{
it
(
'
should dispatch createIssueList action
'
,
()
=>
{
testAction
({
action
:
actions
.
createList
,
payload
:
{
backlog
:
true
},
expectedActions
:
[{
type
:
'
createIssueList
'
,
payload
:
{
backlog
:
true
}
}],
});
});
});
describe
(
'
createIssueList
'
,
()
=>
{
let
commit
;
let
dispatch
;
let
getters
;
...
...
@@ -249,7 +259,7 @@ describe('createList', () => {
}),
);
await
actions
.
createList
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
await
actions
.
create
Issue
List
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
addList
'
,
backlogList
);
});
...
...
@@ -271,7 +281,7 @@ describe('createList', () => {
},
});
await
actions
.
createList
({
getters
,
state
,
commit
,
dispatch
},
{
labelId
:
'
4
'
});
await
actions
.
create
Issue
List
({
getters
,
state
,
commit
,
dispatch
},
{
labelId
:
'
4
'
});
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
addList
'
,
list
);
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
highlightList
'
,
list
.
id
);
...
...
@@ -289,7 +299,7 @@ describe('createList', () => {
}),
);
await
actions
.
createList
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
await
actions
.
create
Issue
List
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
expect
(
commit
).
toHaveBeenCalledWith
(
types
.
CREATE_LIST_FAILURE
);
});
...
...
@@ -306,7 +316,7 @@ describe('createList', () => {
getListByLabelId
:
jest
.
fn
().
mockReturnValue
(
existingList
),
};
await
actions
.
createList
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
await
actions
.
create
Issue
List
({
getters
,
state
,
commit
,
dispatch
},
{
backlog
:
true
});
expect
(
dispatch
).
toHaveBeenCalledWith
(
'
highlightList
'
,
existingList
.
id
);
expect
(
dispatch
).
toHaveBeenCalledTimes
(
1
);
...
...
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