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
afe8d6ae
Commit
afe8d6ae
authored
Aug 10, 2020
by
Florie Guibert
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Swimlanes - Fetch issues using GraphQL endpoint
- Fetch issues for all board lists though VueX action
parent
1c45c9e2
Changes
30
Show whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
373 additions
and
129 deletions
+373
-129
app/assets/javascripts/boards/boards_util.js
app/assets/javascripts/boards/boards_util.js
+21
-0
app/assets/javascripts/boards/components/board_content.vue
app/assets/javascripts/boards/components/board_content.vue
+3
-2
app/assets/javascripts/boards/index.js
app/assets/javascripts/boards/index.js
+2
-2
app/assets/javascripts/boards/models/list.js
app/assets/javascripts/boards/models/list.js
+3
-1
app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
...scripts/boards/queries/board_list_shared.fragment.graphql
+1
-0
app/assets/javascripts/boards/queries/group_lists_issues.query.graphql
...vascripts/boards/queries/group_lists_issues.query.graphql
+18
-0
app/assets/javascripts/boards/queries/issue.fragment.graphql
app/assets/javascripts/boards/queries/issue.fragment.graphql
+31
-0
app/assets/javascripts/boards/queries/project_lists_issues.query.graphql
...scripts/boards/queries/project_lists_issues.query.graphql
+18
-0
app/assets/javascripts/boards/stores/actions.js
app/assets/javascripts/boards/stores/actions.js
+35
-2
app/assets/javascripts/boards/stores/boards_store.js
app/assets/javascripts/boards/stores/boards_store.js
+26
-13
app/assets/javascripts/boards/stores/mutation_types.js
app/assets/javascripts/boards/stores/mutation_types.js
+4
-1
app/assets/javascripts/boards/stores/mutations.js
app/assets/javascripts/boards/stores/mutations.js
+17
-1
app/assets/javascripts/boards/stores/state.js
app/assets/javascripts/boards/stores/state.js
+4
-0
app/assets/stylesheets/pages/boards.scss
app/assets/stylesheets/pages/boards.scss
+4
-4
ee/app/assets/javascripts/boards/components/epic_lane.vue
ee/app/assets/javascripts/boards/components/epic_lane.vue
+27
-5
ee/app/assets/javascripts/boards/components/epics_swimlanes.vue
.../assets/javascripts/boards/components/epics_swimlanes.vue
+39
-11
ee/app/assets/javascripts/boards/components/issues_lane_list.vue
...assets/javascripts/boards/components/issues_lane_list.vue
+17
-0
ee/app/assets/javascripts/boards/stores/actions.js
ee/app/assets/javascripts/boards/stores/actions.js
+15
-20
ee/app/assets/javascripts/boards/stores/mutation_types.js
ee/app/assets/javascripts/boards/stores/mutation_types.js
+1
-1
ee/app/assets/javascripts/boards/stores/mutations.js
ee/app/assets/javascripts/boards/stores/mutations.js
+2
-2
ee/app/assets/javascripts/boards/stores/state.js
ee/app/assets/javascripts/boards/stores/state.js
+1
-0
ee/spec/frontend/boards/components/board_list_spec.js
ee/spec/frontend/boards/components/board_list_spec.js
+1
-0
ee/spec/frontend/boards/components/epic_lane_spec.js
ee/spec/frontend/boards/components/epic_lane_spec.js
+10
-0
ee/spec/frontend/boards/components/issues_lane_list_spec.js
ee/spec/frontend/boards/components/issues_lane_list_spec.js
+2
-0
ee/spec/frontend/boards/stores/actions_spec.js
ee/spec/frontend/boards/stores/actions_spec.js
+0
-26
ee/spec/frontend/boards/stores/mutations_spec.js
ee/spec/frontend/boards/stores/mutations_spec.js
+5
-5
spec/frontend/boards/issue_spec.js
spec/frontend/boards/issue_spec.js
+2
-23
spec/frontend/boards/mock_data.js
spec/frontend/boards/mock_data.js
+23
-0
spec/frontend/boards/stores/actions_spec.js
spec/frontend/boards/stores/actions_spec.js
+6
-6
spec/frontend/boards/stores/mutations_spec.js
spec/frontend/boards/stores/mutations_spec.js
+35
-4
No files found.
app/assets/javascripts/boards/boards_util.js
View file @
afe8d6ae
import
{
getIdFromGraphQLId
}
from
'
~/graphql_shared/utils
'
;
import
ListIssue
from
'
ee_else_ce/boards/models/issue
'
;
export
function
getMilestone
()
{
return
null
;
}
export
function
formatListIssues
(
listIssues
)
{
return
listIssues
.
nodes
.
reduce
((
map
,
list
)
=>
{
return
{
...
map
,
[
list
.
id
]:
list
.
issues
.
nodes
.
map
(
i
=>
new
ListIssue
({
...
i
,
id
:
getIdFromGraphQLId
(
i
.
id
),
labels
:
i
.
labels
?.
nodes
||
[],
assignees
:
i
.
assignees
?.
nodes
||
[],
}),
),
};
},
{});
}
export
default
{
getMilestone
,
formatListIssues
,
};
app/assets/javascripts/boards/components/board_content.vue
View file @
afe8d6ae
...
...
@@ -42,7 +42,7 @@ export default {
},
},
computed
:
{
...
mapState
([
'
isShowingEpicsSwimlanes
'
]),
...
mapState
([
'
isShowingEpicsSwimlanes
'
,
'
boardLists
'
]),
isSwimlanesOn
()
{
return
this
.
glFeatures
.
boardsWithSwimlanes
&&
this
.
isShowingEpicsSwimlanes
;
},
...
...
@@ -73,11 +73,12 @@ export default {
<epics-swimlanes
v-else
ref=
"swimlanes"
:lists=
"
l
ists"
:lists=
"
boardL
ists"
:can-admin-list=
"canAdminList"
:disabled=
"disabled"
:board-id=
"boardId"
:group-id=
"groupId"
:root-path=
"rootPath"
/>
</div>
</
template
>
app/assets/javascripts/boards/index.js
View file @
afe8d6ae
...
...
@@ -117,7 +117,7 @@ export default () => {
boardId
:
this
.
boardId
,
fullPath
:
$boardApp
.
dataset
.
fullPath
,
};
this
.
set
Endpoints
(
endpoints
);
this
.
set
InitialBoardData
({
...
endpoints
,
boardType
:
this
.
parent
}
);
boardsStore
.
setEndpoints
(
endpoints
);
boardsStore
.
rootPath
=
this
.
boardsEndpoint
;
...
...
@@ -189,7 +189,7 @@ export default () => {
}
},
methods
:
{
...
mapActions
([
'
set
Endpoints
'
]),
...
mapActions
([
'
set
InitialBoardData
'
]),
updateTokens
()
{
this
.
filterManager
.
updateTokens
();
},
...
...
app/assets/javascripts/boards/models/list.js
View file @
afe8d6ae
...
...
@@ -60,7 +60,9 @@ class List {
this
.
title
=
this
.
milestone
.
title
;
}
if
(
!
typeInfo
.
isBlank
&&
this
.
id
)
{
// doNotFetchIssues is a temporary workaround until issues are fetched using GraphQL on issue boards
// Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/229416
if
(
!
typeInfo
.
isBlank
&&
this
.
id
&&
!
obj
.
doNotFetchIssues
)
{
this
.
getIssues
().
catch
(()
=>
{
// TODO: handle request error
});
...
...
app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
View file @
afe8d6ae
...
...
@@ -4,6 +4,7 @@ fragment BoardListShared on BoardList {
position
listType
collapsed
maxIssueCount
label
{
id
title
...
...
app/assets/javascripts/boards/queries/group_lists_issues.query.graphql
0 → 100644
View file @
afe8d6ae
#import "./issue.fragment.graphql"
query
GroupListIssues
(
$fullPath
:
ID
!,
$boardId
:
ID
!)
{
group
(
fullPath
:
$fullPath
)
{
board
(
id
:
$boardId
)
{
lists
{
nodes
{
id
issues
{
nodes
{
...
IssueNode
}
}
}
}
}
}
}
app/assets/javascripts/boards/queries/issue.fragment.graphql
0 → 100644
View file @
afe8d6ae
#import "~/graphql_shared/fragments/user.fragment.graphql"
fragment
IssueNode
on
Issue
{
id
iid
title
referencePath
:
reference
(
full
:
true
)
dueDate
timeEstimate
weight
confidential
webUrl
subscribed
blocked
epic
{
id
}
assignees
{
nodes
{
...
User
}
}
labels
{
nodes
{
id
title
color
description
}
}
}
app/assets/javascripts/boards/queries/project_lists_issues.query.graphql
0 → 100644
View file @
afe8d6ae
#import "./issue.fragment.graphql"
query
ProjectListIssues
(
$fullPath
:
ID
!,
$boardId
:
ID
!)
{
project
(
fullPath
:
$fullPath
)
{
board
(
id
:
$boardId
)
{
lists
{
nodes
{
id
issues
{
nodes
{
...
IssueNode
}
}
}
}
}
}
}
app/assets/javascripts/boards/stores/actions.js
View file @
afe8d6ae
import
*
as
types
from
'
./mutation_types
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
{
BoardType
}
from
'
~/boards/constants
'
;
import
{
formatListIssues
}
from
'
../boards_util
'
;
import
groupListsIssuesQuery
from
'
../queries/group_lists_issues.query.graphql
'
;
import
projectListsIssuesQuery
from
'
../queries/project_lists_issues.query.graphql
'
;
const
gqlClient
=
createDefaultClient
();
const
notImplemented
=
()
=>
{
/* eslint-disable-next-line @gitlab/require-i18n-strings */
...
...
@@ -6,8 +13,8 @@ const notImplemented = () => {
};
export
default
{
set
Endpoints
:
({
commit
},
endpoints
)
=>
{
commit
(
types
.
SET_
ENDPOINTS
,
endpoints
);
set
InitialBoardData
:
({
commit
},
data
)
=>
{
commit
(
types
.
SET_
INITIAL_BOARD_DATA
,
data
);
},
setActiveId
({
commit
},
id
)
{
...
...
@@ -38,6 +45,32 @@ export default {
notImplemented
();
},
fetchIssuesForAllLists
:
({
state
,
commit
})
=>
{
commit
(
types
.
REQUEST_ISSUES_FOR_ALL_LISTS
);
const
{
endpoints
,
boardType
}
=
state
;
const
{
fullPath
,
boardId
}
=
endpoints
;
const
query
=
boardType
===
BoardType
.
group
?
groupListsIssuesQuery
:
projectListsIssuesQuery
;
const
variables
=
{
fullPath
,
boardId
:
`gid://gitlab/Board/
${
boardId
}
`
,
};
return
gqlClient
.
query
({
query
,
variables
,
})
.
then
(({
data
})
=>
{
const
{
lists
}
=
data
[
boardType
]?.
board
;
const
listIssues
=
formatListIssues
(
lists
);
commit
(
types
.
RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS
,
listIssues
);
})
.
catch
(()
=>
commit
(
types
.
RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE
));
},
moveIssue
:
()
=>
{
notImplemented
();
},
...
...
app/assets/javascripts/boards/stores/boards_store.js
View file @
afe8d6ae
...
...
@@ -81,7 +81,7 @@ const boardsStore = {
showPage
(
page
)
{
this
.
state
.
currentPage
=
page
;
},
addList
(
listObj
)
{
updateListPosition
(
listObj
)
{
const
listType
=
listObj
.
listType
||
listObj
.
list_type
;
let
{
position
}
=
listObj
;
if
(
listType
===
ListType
.
closed
)
{
...
...
@@ -91,6 +91,10 @@ const boardsStore = {
}
const
list
=
new
List
({
...
listObj
,
position
});
return
list
;
},
addList
(
listObj
)
{
const
list
=
this
.
updateListPosition
(
listObj
);
this
.
state
.
lists
=
sortBy
([...
this
.
state
.
lists
,
list
],
'
position
'
);
return
list
;
},
...
...
@@ -850,19 +854,28 @@ const boardsStore = {
},
refreshIssueData
(
issue
,
obj
)
{
issue
.
id
=
obj
.
id
;
issue
.
iid
=
obj
.
iid
;
issue
.
title
=
obj
.
title
;
issue
.
confidential
=
obj
.
confidential
;
issue
.
dueDate
=
obj
.
due_date
;
issue
.
sidebarInfoEndpoint
=
obj
.
issue_sidebar_endpoint
;
issue
.
referencePath
=
obj
.
reference_path
;
issue
.
path
=
obj
.
real_path
;
issue
.
toggleSubscriptionEndpoint
=
obj
.
toggle_subscription_endpoint
;
// issue.id = obj.id;
// issue.iid = obj.iid;
// issue.title = obj.title;
// issue.confidential = obj.confidential;
// issue.dueDate = obj.due_date || obj.dueDate;
// issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
// issue.referencePath = obj.reference_path || obj.referencePath;
// issue.path = obj.real_path || obj.webUrl;
// issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
// issue.project_id = obj.project_id;
// issue.timeEstimate = obj.time_estimate || obj.timeEstimate;
// issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
// issue.blocked = obj.blocked;
// issue.epic = obj.epic;
const
convertedObj
=
convertObjectPropsToCamelCase
(
obj
,
{
dropKeys
:
[
'
issue_sidebar_endpoint
'
,
'
real_path
'
,
'
webUrl
'
],
});
convertedObj
.
sidebarInfoEndpoint
=
obj
.
issue_sidebar_endpoint
;
issue
.
path
=
obj
.
real_path
||
obj
.
webUrl
;
issue
.
project_id
=
obj
.
project_id
;
issue
.
timeEstimate
=
obj
.
time_estimate
;
issue
.
assignableLabelsEndpoint
=
obj
.
assignable_labels_endpoint
;
issue
.
blocked
=
obj
.
blocked
;
Object
.
assign
(
issue
,
convertedObj
);
if
(
obj
.
project
)
{
issue
.
project
=
new
IssueProject
(
obj
.
project
);
...
...
app/assets/javascripts/boards/stores/mutation_types.js
View file @
afe8d6ae
export
const
SET_
ENDPOINTS
=
'
SET_ENDPOINTS
'
;
export
const
SET_
INITIAL_BOARD_DATA
=
'
SET_INITIAL_BOARD_DATA
'
;
export
const
REQUEST_ADD_LIST
=
'
REQUEST_ADD_LIST
'
;
export
const
RECEIVE_ADD_LIST_SUCCESS
=
'
RECEIVE_ADD_LIST_SUCCESS
'
;
export
const
RECEIVE_ADD_LIST_ERROR
=
'
RECEIVE_ADD_LIST_ERROR
'
;
...
...
@@ -8,6 +8,9 @@ export const RECEIVE_UPDATE_LIST_ERROR = 'RECEIVE_UPDATE_LIST_ERROR';
export
const
REQUEST_REMOVE_LIST
=
'
REQUEST_REMOVE_LIST
'
;
export
const
RECEIVE_REMOVE_LIST_SUCCESS
=
'
RECEIVE_REMOVE_LIST_SUCCESS
'
;
export
const
RECEIVE_REMOVE_LIST_ERROR
=
'
RECEIVE_REMOVE_LIST_ERROR
'
;
export
const
REQUEST_ISSUES_FOR_ALL_LISTS
=
'
REQUEST_ISSUES_FOR_ALL_LISTS
'
;
export
const
RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS
=
'
RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS
'
;
export
const
RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE
=
'
RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE
'
;
export
const
REQUEST_ADD_ISSUE
=
'
REQUEST_ADD_ISSUE
'
;
export
const
RECEIVE_ADD_ISSUE_SUCCESS
=
'
RECEIVE_ADD_ISSUE_SUCCESS
'
;
export
const
RECEIVE_ADD_ISSUE_ERROR
=
'
RECEIVE_ADD_ISSUE_ERROR
'
;
...
...
app/assets/javascripts/boards/stores/mutations.js
View file @
afe8d6ae
...
...
@@ -6,8 +6,10 @@ const notImplemented = () => {
};
export
default
{
[
mutationTypes
.
SET_ENDPOINTS
]:
(
state
,
endpoints
)
=>
{
[
mutationTypes
.
SET_INITIAL_BOARD_DATA
]:
(
state
,
data
)
=>
{
const
{
boardType
,
...
endpoints
}
=
data
;
state
.
endpoints
=
endpoints
;
state
.
boardType
=
boardType
;
},
[
mutationTypes
.
SET_ACTIVE_ID
](
state
,
id
)
{
...
...
@@ -50,6 +52,20 @@ export default {
notImplemented
();
},
[
mutationTypes
.
REQUEST_ISSUES_FOR_ALL_LISTS
]:
state
=>
{
state
.
isLoadingIssues
=
true
;
},
[
mutationTypes
.
RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS
]:
(
state
,
listIssues
)
=>
{
state
.
issuesByListId
=
listIssues
;
state
.
isLoadingIssues
=
false
;
},
[
mutationTypes
.
RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE
]:
state
=>
{
state
.
listIssueFetchFailure
=
true
;
state
.
isLoadingIssues
=
false
;
},
[
mutationTypes
.
REQUEST_ADD_ISSUE
]:
()
=>
{
notImplemented
();
},
...
...
app/assets/javascripts/boards/stores/state.js
View file @
afe8d6ae
...
...
@@ -2,6 +2,10 @@ import { inactiveId } from '~/boards/constants';
export
default
()
=>
({
endpoints
:
{},
boardType
:
null
,
isShowingLabels
:
true
,
activeId
:
inactiveId
,
issuesByListId
:
{},
isLoadingIssues
:
false
,
listIssueFetchFailure
:
false
,
});
app/assets/stylesheets/pages/boards.scss
View file @
afe8d6ae
...
...
@@ -598,17 +598,17 @@
$epic-icons-spacing
:
40px
;
.board-epic-lane
{
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-width
}
-
$epic-icons-spacing
);
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-width
}
-
#{
$epic-icons-spacing
}
);
.page-with-icon-sidebar
&
{
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-collapsed-width
}
-
$epic-icons-spacing
);
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-collapsed-width
}
-
#{
$epic-icons-spacing
}
);
}
.page-with-icon-sidebar
.is-compact
&
{
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-collapsed-width
}
-
#{
$gutter-width
}
-
$epic-icons-spacing
);
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-collapsed-width
}
-
#{
$gutter-width
}
-
#{
$epic-icons-spacing
}
);
}
.is-compact
&
{
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-width
}
-
#{
$gutter-width
}
-
$epic-icons-spacing
);
max-width
:
calc
(
100vw
-
#{
$contextual-sidebar-width
}
-
#{
$gutter-width
}
-
#{
$epic-icons-spacing
}
);
}
}
ee/app/assets/javascripts/boards/components/epic_lane.vue
View file @
afe8d6ae
...
...
@@ -27,6 +27,23 @@ export default {
type
:
Array
,
required
:
true
,
},
issues
:
{
type
:
Object
,
required
:
true
,
},
isLoadingIssues
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
disabled
:
{
type
:
Boolean
,
required
:
true
,
},
rootPath
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
...
...
@@ -73,10 +90,12 @@ export default {
},
},
methods
:
{
epicIssuesForList
(
listIssues
)
{
return
this
.
epic
.
issues
.
filter
(
epicIssue
=>
Boolean
(
listIssues
.
find
(
listIssue
=>
String
(
listIssue
.
iid
)
===
epicIssue
.
iid
)),
);
epicIssuesForList
(
listId
)
{
if
(
this
.
issues
[
listId
])
{
return
this
.
issues
[
listId
].
filter
(
issue
=>
issue
.
epic
&&
issue
.
epic
.
id
===
this
.
epic
.
id
);
}
return
[];
},
toggleExpanded
()
{
this
.
isExpanded
=
!
this
.
isExpanded
;
...
...
@@ -137,7 +156,10 @@ export default {
v-for=
"list in lists"
:key=
"`${list.id}-issues`"
:list=
"list"
:issues=
"epicIssuesForList(list.issues)"
:issues=
"epicIssuesForList(list.id)"
:is-loading=
"isLoadingIssues"
:disabled=
"disabled"
:root-path=
"rootPath"
/>
</div>
</div>
...
...
ee/app/assets/javascripts/boards/components/epics_swimlanes.vue
View file @
afe8d6ae
<
script
>
import
{
mapState
}
from
'
vuex
'
;
import
{
map
Actions
,
map
State
}
from
'
vuex
'
;
import
{
n__
}
from
'
~/locale
'
;
import
{
GlIcon
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
BoardListHeader
from
'
ee_else_ce/boards/components/board_list_header.vue
'
;
...
...
@@ -39,14 +39,30 @@ export default {
required
:
false
,
default
:
0
,
},
rootPath
:
{
type
:
String
,
required
:
true
,
},
},
computed
:
{
...
mapState
([
'
epics
'
]),
issuesCount
()
{
return
this
.
lists
.
reduce
((
total
,
list
)
=>
total
+
list
.
issues
.
length
,
0
);
...
mapState
([
'
epics
'
,
'
issuesByListId
'
,
'
isLoadingIssues
'
]),
unassignedIssuesCount
()
{
return
this
.
lists
.
reduce
((
total
,
list
)
=>
total
+
this
.
unassignedIssues
(
list
).
length
,
0
);
},
unassignedIssuesCountTooltipText
()
{
return
n__
(
`%d unassigned issue`
,
`%d unassigned issues`
,
this
.
unassignedIssuesCount
);
},
},
issuesCountTooltipText
()
{
return
n__
(
`%d unassigned issue`
,
`%d unassigned issues`
,
this
.
issuesCount
);
mounted
()
{
this
.
fetchIssuesForAllLists
();
},
methods
:
{
...
mapActions
([
'
fetchIssuesForAllLists
'
]),
unassignedIssues
(
list
)
{
if
(
this
.
issuesByListId
[
list
.
id
])
{
return
this
.
issuesByListId
[
list
.
id
].
filter
(
i
=>
i
.
epic
===
null
);
}
return
[];
},
},
};
...
...
@@ -78,7 +94,16 @@ export default {
</div>
</div>
<div
class=
"board-epics-swimlanes gl-display-table"
>
<epic-lane
v-for=
"epic in epics"
:key=
"epic.id"
:epic=
"epic"
:lists=
"lists"
/>
<epic-lane
v-for=
"epic in epics"
:key=
"epic.id"
:epic=
"epic"
:lists=
"lists"
:issues=
"issuesByListId"
:is-loading-issues=
"isLoadingIssues"
:disabled=
"disabled"
:root-path=
"rootPath"
/>
<div
class=
"board-lane-unassigned-issues gl-sticky gl-display-inline-block gl-left-0"
>
<div
class=
"gl-left-0 gl-py-5 gl-px-3 gl-display-flex gl-align-items-center"
>
<span
...
...
@@ -88,14 +113,14 @@ export default {
</span>
<span
v-gl-tooltip
.
hover
:title=
"
i
ssuesCountTooltipText"
:title=
"
unassignedI
ssuesCountTooltipText"
class=
"gl-display-flex gl-align-items-center gl-text-gray-700"
tabindex=
"0"
:aria-label=
"
i
ssuesCountTooltipText"
:aria-label=
"
unassignedI
ssuesCountTooltipText"
data-testid=
"issues-lane-issue-count"
>
<gl-icon
class=
"gl-mr-2 gl-flex-shrink-0"
name=
"issues"
aria-hidden=
"true"
/>
<span
aria-hidden=
"true"
>
{{
i
ssuesCount
}}
</span>
<span
aria-hidden=
"true"
>
{{
unassignedI
ssuesCount
}}
</span>
</span>
</div>
</div>
...
...
@@ -104,9 +129,12 @@ export default {
v-for=
"list in lists"
:key=
"`$
{list.id}-issues`"
:list="list"
:issues="
list.issues
"
:issues="
unassignedIssues(list)
"
:group-id="groupId"
:is-unassigned-issues-lane="true"
:is-loading="isLoadingIssues"
:disabled="disabled"
:root-path="rootPath"
/>
</div>
</div>
...
...
ee/app/assets/javascripts/boards/components/issues_lane_list.vue
View file @
afe8d6ae
<
script
>
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
eventHub
from
'
~/boards/eventhub
'
;
import
BoardCard
from
'
~/boards/components/board_card.vue
'
;
import
BoardNewIssue
from
'
~/boards/components/board_new_issue.vue
'
;
...
...
@@ -7,12 +8,17 @@ export default {
components
:
{
BoardCard
,
BoardNewIssue
,
GlLoadingIcon
,
},
props
:
{
list
:
{
type
:
Object
,
required
:
true
,
},
disabled
:
{
type
:
Boolean
,
required
:
true
,
},
issues
:
{
type
:
Array
,
required
:
true
,
...
...
@@ -27,6 +33,15 @@ export default {
required
:
false
,
default
:
false
,
},
isLoading
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
rootPath
:
{
type
:
String
,
required
:
true
,
},
},
data
()
{
return
{
...
...
@@ -56,6 +71,7 @@ export default {
:class=
"
{ 'is-collapsed': !list.isExpanded }"
>
<div
class=
"board-inner gl-rounded-base gl-relative gl-w-full"
>
<gl-loading-icon
v-if=
"isLoading"
class=
"gl-p-2"
/>
<board-new-issue
v-if=
"list.type !== 'closed' && showIssueForm && isUnassignedIssuesLane"
:group-id=
"groupId"
...
...
@@ -69,6 +85,7 @@ export default {
:index=
"index"
:list=
"list"
:issue=
"issue"
:root-path=
"rootPath"
/>
</ul>
</div>
...
...
ee/app/assets/javascripts/boards/stores/actions.js
View file @
afe8d6ae
import
axios
from
'
axios
'
;
import
{
sortBy
}
from
'
lodash
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
actionsCE
from
'
~/boards/stores/actions
'
;
import
boardsStoreEE
from
'
./boards_store_ee
'
;
import
*
as
types
from
'
./mutation_types
'
;
...
...
@@ -14,7 +16,7 @@ const notImplemented = () => {
const
gqlClient
=
createDefaultClient
();
const
fetchEpicsSwimlanes
=
({
endpoints
})
=>
{
const
fetchEpicsSwimlanes
=
({
endpoints
,
boardType
})
=>
{
const
{
fullPath
,
boardId
}
=
endpoints
;
const
query
=
epicsSwimlanes
;
...
...
@@ -29,7 +31,8 @@ const fetchEpicsSwimlanes = ({ endpoints }) => {
variables
,
})
.
then
(({
data
})
=>
{
return
data
;
const
{
lists
}
=
data
[
boardType
]?.
board
;
return
lists
?.
nodes
;
});
};
...
...
@@ -101,33 +104,25 @@ export default {
notImplemented
();
},
toggleEpicSwimlanes
:
({
state
,
commit
,
dispatch
})
=>
{
toggleEpicSwimlanes
:
({
state
,
commit
})
=>
{
commit
(
types
.
TOGGLE_EPICS_SWIMLANES
);
if
(
state
.
isShowingEpicsSwimlanes
)
{
Promise
.
all
([
fetchEpicsSwimlanes
(
state
),
fetchEpics
(
state
)])
.
then
(([
swimlanes
,
epics
])
=>
{
if
(
swimlanes
)
{
dispatch
(
'
receiveSwimlanesSuccess
'
,
swimlanes
);
.
then
(([
lists
,
epics
])
=>
{
if
(
lists
)
{
let
boardLists
=
lists
.
map
(
list
=>
boardsStore
.
updateListPosition
({
...
list
,
doNotFetchIssues
:
true
}),
);
boardLists
=
sortBy
([...
boardLists
],
'
position
'
);
commit
(
types
.
RECEIVE_BOARD_LISTS_SUCCESS
,
boardLists
);
}
if
(
epics
)
{
dispatch
(
'
receiveEpicsSuccess
'
,
epics
);
commit
(
types
.
RECEIVE_EPICS_SUCCESS
,
epics
);
}
})
.
catch
(()
=>
dispatch
(
'
receiveSwimlanesFailure
'
));
.
catch
(()
=>
commit
(
types
.
RECEIVE_SWIMLANES_FAILURE
));
}
},
receiveSwimlanesSuccess
:
({
commit
},
swimlanes
)
=>
{
commit
(
types
.
RECEIVE_SWIMLANES_SUCCESS
,
swimlanes
);
},
receiveSwimlanesFailure
:
({
commit
})
=>
{
commit
(
types
.
RECEIVE_SWIMLANES_FAILURE
);
},
receiveEpicsSuccess
:
({
commit
},
swimlanes
)
=>
{
commit
(
types
.
RECEIVE_EPICS_SUCCESS
,
swimlanes
);
},
};
ee/app/assets/javascripts/boards/stores/mutation_types.js
View file @
afe8d6ae
...
...
@@ -12,7 +12,7 @@ export const RECEIVE_REMOVE_BOARD_SUCCESS = 'RECEIVE_REMOVE_BOARD_SUCCESS';
export
const
RECEIVE_REMOVE_BOARD_ERROR
=
'
RECEIVE_REMOVE_BOARD_ERROR
'
;
export
const
TOGGLE_PROMOTION_STATE
=
'
TOGGLE_PROMOTION_STATE
'
;
export
const
TOGGLE_EPICS_SWIMLANES
=
'
TOGGLE_EPICS_SWIMLANES
'
;
export
const
RECEIVE_
SWIMLANES_SUCCESS
=
'
RECEIVE_SWIMLANE
S_SUCCESS
'
;
export
const
RECEIVE_
BOARD_LISTS_SUCCESS
=
'
RECEIVE_BOARD_LIST
S_SUCCESS
'
;
export
const
RECEIVE_SWIMLANES_FAILURE
=
'
RECEIVE_SWIMLANES_FAILURE
'
;
export
const
RECEIVE_EPICS_SUCCESS
=
'
RECEIVE_EPICS_SUCCESS
'
;
export
const
SET_SHOW_LABELS
=
'
SET_SHOW_LABELS
'
;
ee/app/assets/javascripts/boards/stores/mutations.js
View file @
afe8d6ae
...
...
@@ -69,8 +69,8 @@ export default {
state
.
epicsSwimlanesFetchInProgress
=
true
;
},
[
mutationTypes
.
RECEIVE_
SWIMLANES_SUCCESS
]:
(
state
,
swimlane
s
)
=>
{
state
.
epicsSwimlanes
=
swimlane
s
;
[
mutationTypes
.
RECEIVE_
BOARD_LISTS_SUCCESS
]:
(
state
,
boardList
s
)
=>
{
state
.
boardLists
=
boardList
s
;
state
.
epicsSwimlanesFetchInProgress
=
false
;
},
...
...
ee/app/assets/javascripts/boards/stores/state.js
View file @
afe8d6ae
...
...
@@ -8,4 +8,5 @@ export default () => ({
epicsSwimlanesFetchFailure
:
false
,
epicsSwimlanes
:
{},
epics
:
{},
boardLists
:
[],
});
ee/spec/frontend/boards/components/board_list_spec.js
View file @
afe8d6ae
...
...
@@ -10,6 +10,7 @@ describe('BoardList Component', () => {
path
:
'
/test
'
,
},
real_path
:
''
,
webUrl
:
''
,
};
const
componentProps
=
{
...
...
ee/spec/frontend/boards/components/epic_lane_spec.js
View file @
afe8d6ae
...
...
@@ -19,9 +19,19 @@ describe('EpicLane', () => {
});
const
createComponent
=
(
props
=
{})
=>
{
const
issues
=
mockLists
.
reduce
((
map
,
list
)
=>
{
return
{
...
map
,
[
list
.
id
]:
mockIssues
,
};
},
{});
const
defaultProps
=
{
epic
:
mockEpic
,
lists
:
mockLists
.
map
(
listMock
=>
Vue
.
observable
(
new
List
(
listMock
))),
issues
,
disabled
:
false
,
rootPath
:
'
/
'
,
};
wrapper
=
shallowMount
(
EpicLane
,
{
...
...
ee/spec/frontend/boards/components/issues_lane_list_spec.js
View file @
afe8d6ae
...
...
@@ -51,6 +51,8 @@ describe('IssuesLaneList', () => {
propsData
:
{
list
,
issues
:
mockIssues
,
disabled
:
false
,
rootPath
:
'
/
'
,
},
});
};
...
...
ee/spec/frontend/boards/stores/actions_spec.js
View file @
afe8d6ae
...
...
@@ -99,29 +99,3 @@ describe('toggleEpicSwimlanes', () => {
);
});
});
describe
(
'
receiveSwimlanesSuccess
'
,
()
=>
{
it
(
'
should commit mutation RECEIVE_SWIMLANES_SUCCESS
'
,
done
=>
{
testAction
(
actions
.
receiveSwimlanesSuccess
,
{},
{},
[{
type
:
types
.
RECEIVE_SWIMLANES_SUCCESS
,
payload
:
{}
}],
[],
done
,
);
});
});
describe
(
'
receiveSwimlanesFailure
'
,
()
=>
{
it
(
'
should commit mutation RECEIVE_SWIMLANES_SUCCESS
'
,
done
=>
{
testAction
(
actions
.
receiveSwimlanesFailure
,
null
,
{},
[{
type
:
types
.
RECEIVE_SWIMLANES_FAILURE
}],
[],
done
,
);
});
});
ee/spec/frontend/boards/stores/mutations_spec.js
View file @
afe8d6ae
...
...
@@ -103,17 +103,17 @@ describe('TOGGLE_EPICS_SWIMLANES', () => {
});
});
describe
(
'
RECEIVE_
SWIMLANE
S_SUCCESS
'
,
()
=>
{
it
(
'
sets epicsSwimlanesFetchInProgress to false and populates
epicsSwimlane
s with payload
'
,
()
=>
{
describe
(
'
RECEIVE_
BOARD_LIST
S_SUCCESS
'
,
()
=>
{
it
(
'
sets epicsSwimlanesFetchInProgress to false and populates
boardList
s with payload
'
,
()
=>
{
const
state
=
{
epicsSwimlanesFetchInProgress
:
true
,
epicsSwimlane
s
:
{},
boardList
s
:
{},
};
mutations
.
RECEIVE_
SWIMLANE
S_SUCCESS
(
state
,
mockLists
);
mutations
.
RECEIVE_
BOARD_LIST
S_SUCCESS
(
state
,
mockLists
);
expect
(
state
.
epicsSwimlanesFetchInProgress
).
toBe
(
false
);
expect
(
state
.
epicsSwimlane
s
).
toEqual
(
mockLists
);
expect
(
state
.
boardList
s
).
toEqual
(
mockLists
);
});
});
...
...
spec/frontend/boards/issue_spec.js
View file @
afe8d6ae
...
...
@@ -5,7 +5,7 @@ import '~/boards/models/assignee';
import
'
~/boards/models/issue
'
;
import
'
~/boards/models/list
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
{
setMockEndpoints
}
from
'
./mock_data
'
;
import
{
setMockEndpoints
,
mockIssue
}
from
'
./mock_data
'
;
describe
(
'
Issue model
'
,
()
=>
{
let
issue
;
...
...
@@ -14,28 +14,7 @@ describe('Issue model', () => {
setMockEndpoints
();
boardsStore
.
create
();
issue
=
new
ListIssue
({
title
:
'
Testing
'
,
id
:
1
,
iid
:
1
,
confidential
:
false
,
labels
:
[
{
id
:
1
,
title
:
'
test
'
,
color
:
'
red
'
,
description
:
'
testing
'
,
},
],
assignees
:
[
{
id
:
1
,
name
:
'
name
'
,
username
:
'
username
'
,
avatar_url
:
'
http://avatar_url
'
,
},
],
});
issue
=
new
ListIssue
(
mockIssue
);
});
it
(
'
has label
'
,
()
=>
{
...
...
spec/frontend/boards/mock_data.js
View file @
afe8d6ae
...
...
@@ -92,6 +92,29 @@ export const mockMilestone = {
due_date
:
'
2019-12-31
'
,
};
export
const
mockIssue
=
{
title
:
'
Testing
'
,
id
:
1
,
iid
:
1
,
confidential
:
false
,
labels
:
[
{
id
:
1
,
title
:
'
test
'
,
color
:
'
red
'
,
description
:
'
testing
'
,
},
],
assignees
:
[
{
id
:
1
,
name
:
'
name
'
,
username
:
'
username
'
,
avatar_url
:
'
http://avatar_url
'
,
},
],
};
export
const
BoardsMockData
=
{
GET
:
{
'
/test/-/boards/1/lists/300/issues?id=300&page=1
'
:
{
...
...
spec/frontend/boards/stores/actions_spec.js
View file @
afe8d6ae
...
...
@@ -9,18 +9,18 @@ const expectNotImplemented = action => {
});
};
describe
(
'
set
Endpoints
'
,
()
=>
{
it
(
'
sets
endpoints
object
'
,
()
=>
{
const
mock
Endpoints
=
{
describe
(
'
set
InitialBoardData
'
,
()
=>
{
it
(
'
sets
data
object
'
,
()
=>
{
const
mock
Data
=
{
foo
:
'
bar
'
,
bar
:
'
baz
'
,
};
return
testAction
(
actions
.
set
Endpoints
,
mock
Endpoints
,
actions
.
set
InitialBoardData
,
mock
Data
,
{},
[{
type
:
types
.
SET_
ENDPOINTS
,
payload
:
mockEndpoints
}],
[{
type
:
types
.
SET_
INITIAL_BOARD_DATA
,
payload
:
mockData
}],
[],
);
});
...
...
spec/frontend/boards/stores/mutations_spec.js
View file @
afe8d6ae
import
mutations
from
'
~/boards/stores/mutations
'
;
import
*
as
types
from
'
~/boards/stores/mutation_types
'
;
import
defaultState
from
'
~/boards/stores/state
'
;
import
{
mockIssue
}
from
'
../mock_data
'
;
const
expectNotImplemented
=
action
=>
{
it
(
'
is not implemented
'
,
()
=>
{
...
...
@@ -15,7 +15,7 @@ describe('Board Store Mutations', () => {
state
=
defaultState
();
});
describe
(
'
SET_
ENDPOINTS
'
,
()
=>
{
describe
(
'
SET_
INITIAL_BOARD_DATA
'
,
()
=>
{
it
(
'
Should set initial Boards data to state
'
,
()
=>
{
const
endpoints
=
{
boardsEndpoint
:
'
/boards/
'
,
...
...
@@ -25,15 +25,17 @@ describe('Board Store Mutations', () => {
boardId
:
1
,
fullPath
:
'
gitlab-org
'
,
};
const
boardType
=
'
group
'
;
mutations
[
types
.
SET_ENDPOINTS
](
state
,
endpoints
);
mutations
.
SET_INITIAL_BOARD_DATA
(
state
,
{
...
endpoints
,
boardType
}
);
expect
(
state
.
endpoints
).
toEqual
(
endpoints
);
expect
(
state
.
boardType
).
toEqual
(
boardType
);
});
});
describe
(
'
SET_ACTIVE_ID
'
,
()
=>
{
it
(
'
updates aciveListId to be the value that is passed
'
,
()
=>
{
it
(
'
updates ac
t
iveListId to be the value that is passed
'
,
()
=>
{
const
expectedId
=
1
;
mutations
.
SET_ACTIVE_ID
(
state
,
expectedId
);
...
...
@@ -78,6 +80,35 @@ describe('Board Store Mutations', () => {
expectNotImplemented
(
mutations
.
RECEIVE_REMOVE_LIST_ERROR
);
});
describe
(
'
REQUEST_ISSUES_FOR_ALL_LISTS
'
,
()
=>
{
it
(
'
sets isLoadingIssues to true
'
,
()
=>
{
expect
(
state
.
isLoadingIssues
).
toBe
(
false
);
mutations
.
REQUEST_ISSUES_FOR_ALL_LISTS
(
state
);
expect
(
state
.
isLoadingIssues
).
toBe
(
true
);
});
});
describe
(
'
RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS
'
,
()
=>
{
it
(
'
sets isLoadingIssues to false and updates issuesByListId object
'
,
()
=>
{
const
listIssues
=
{
'
1
'
:
[
mockIssue
],
};
state
=
{
...
state
,
isLoadingIssues
:
true
,
issuesByListId
:
{},
};
mutations
.
RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS
(
state
,
listIssues
);
expect
(
state
.
isLoadingIssues
).
toBe
(
false
);
expect
(
state
.
issuesByListId
).
toEqual
(
listIssues
);
});
});
describe
(
'
REQUEST_ADD_ISSUE
'
,
()
=>
{
expectNotImplemented
(
mutations
.
REQUEST_ADD_ISSUE
);
});
...
...
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