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
a249542e
Commit
a249542e
authored
Aug 25, 2020
by
Florie Guibert
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Swimlanes - Reorder lists
Use drag & drop to reorder lists on board swimlanes
parent
b221119c
Changes
18
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
274 additions
and
47 deletions
+274
-47
app/assets/javascripts/boards/components/board_list_header.vue
...ssets/javascripts/boards/components/board_list_header.vue
+9
-2
app/assets/javascripts/boards/models/list.js
app/assets/javascripts/boards/models/list.js
+1
-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/board_list_update.mutation.graphql
...scripts/boards/queries/board_list_update.mutation.graphql
+10
-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
+0
-15
app/assets/javascripts/boards/stores/mutation_types.js
app/assets/javascripts/boards/stores/mutation_types.js
+2
-3
app/assets/javascripts/boards/stores/mutations.js
app/assets/javascripts/boards/stores/mutations.js
+12
-8
app/assets/javascripts/boards/stores/state.js
app/assets/javascripts/boards/stores/state.js
+1
-0
ee/app/assets/javascripts/boards/components/epics_swimlanes.vue
.../assets/javascripts/boards/components/epics_swimlanes.vue
+44
-3
ee/app/assets/javascripts/boards/constants.js
ee/app/assets/javascripts/boards/constants.js
+5
-0
ee/app/assets/javascripts/boards/models/list.js
ee/app/assets/javascripts/boards/models/list.js
+1
-1
ee/app/assets/javascripts/boards/queries/board_list.fragment.graphql
...ts/javascripts/boards/queries/board_list.fragment.graphql
+1
-0
ee/spec/frontend/boards/components/epics_swimlanes_spec.js
ee/spec/frontend/boards/components/epics_swimlanes_spec.js
+66
-3
ee/spec/frontend/boards/mock_data.js
ee/spec/frontend/boards/mock_data.js
+2
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/boards/stores/actions_spec.js
spec/frontend/boards/stores/actions_spec.js
+52
-1
spec/frontend/boards/stores/mutations_spec.js
spec/frontend/boards/stores/mutations_spec.js
+29
-8
No files found.
app/assets/javascripts/boards/components/board_list_header.vue
View file @
a249542e
<
script
>
<
script
>
import
{
mapActions
}
from
'
vuex
'
;
import
{
import
{
GlButton
,
GlButton
,
GlButtonGroup
,
GlButtonGroup
,
...
@@ -17,6 +18,7 @@ import boardsStore from '../stores/boards_store';
...
@@ -17,6 +18,7 @@ import boardsStore from '../stores/boards_store';
import
eventHub
from
'
../eventhub
'
;
import
eventHub
from
'
../eventhub
'
;
import
{
ListType
}
from
'
../constants
'
;
import
{
ListType
}
from
'
../constants
'
;
import
{
isScopedLabel
}
from
'
~/lib/utils/common_utils
'
;
import
{
isScopedLabel
}
from
'
~/lib/utils/common_utils
'
;
import
glFeatureFlagMixin
from
'
~/vue_shared/mixins/gl_feature_flags_mixin
'
;
export
default
{
export
default
{
components
:
{
components
:
{
...
@@ -32,7 +34,7 @@ export default {
...
@@ -32,7 +34,7 @@ export default {
directives
:
{
directives
:
{
GlTooltip
:
GlTooltipDirective
,
GlTooltip
:
GlTooltipDirective
,
},
},
mixins
:
[
isWipLimitsOn
],
mixins
:
[
isWipLimitsOn
,
glFeatureFlagMixin
()
],
props
:
{
props
:
{
list
:
{
list
:
{
type
:
Object
,
type
:
Object
,
...
@@ -128,6 +130,7 @@ export default {
...
@@ -128,6 +130,7 @@ export default {
},
},
},
},
methods
:
{
methods
:
{
...
mapActions
([
'
updateList
'
]),
showScopedLabels
(
label
)
{
showScopedLabels
(
label
)
{
return
boardsStore
.
scopedLabels
.
enabled
&&
isScopedLabel
(
label
);
return
boardsStore
.
scopedLabels
.
enabled
&&
isScopedLabel
(
label
);
},
},
...
@@ -144,7 +147,11 @@ export default {
...
@@ -144,7 +147,11 @@ export default {
}
}
if
(
this
.
isLoggedIn
)
{
if
(
this
.
isLoggedIn
)
{
this
.
list
.
update
();
if
(
this
.
glFeatures
.
boardsWithSwimlanes
&&
this
.
isSwimlanesHeader
)
{
this
.
updateList
({
listId
:
this
.
list
.
id
,
collapsed
:
!
this
.
list
.
isExpanded
});
}
else
{
this
.
list
.
update
();
}
}
}
// When expanding/collapsing, the tooltip on the caret button sometimes stays open.
// When expanding/collapsing, the tooltip on the caret button sometimes stays open.
...
...
app/assets/javascripts/boards/models/list.js
View file @
a249542e
...
@@ -47,7 +47,7 @@ class List {
...
@@ -47,7 +47,7 @@ class List {
this
.
loading
=
true
;
this
.
loading
=
true
;
this
.
loadingMore
=
false
;
this
.
loadingMore
=
false
;
this
.
issues
=
obj
.
issues
||
[];
this
.
issues
=
obj
.
issues
||
[];
this
.
issuesSize
=
obj
.
issuesSize
?
obj
.
issuesSize
:
0
;
this
.
issuesSize
=
obj
.
issuesSize
||
obj
.
issuesCount
||
0
;
this
.
maxIssueCount
=
obj
.
maxIssueCount
||
obj
.
max_issue_count
||
0
;
this
.
maxIssueCount
=
obj
.
maxIssueCount
||
obj
.
max_issue_count
||
0
;
if
(
obj
.
label
)
{
if
(
obj
.
label
)
{
...
...
app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
View file @
a249542e
...
@@ -4,6 +4,7 @@ fragment BoardListShared on BoardList {
...
@@ -4,6 +4,7 @@ fragment BoardListShared on BoardList {
position
position
listType
listType
collapsed
collapsed
issuesCount
label
{
label
{
id
id
title
title
...
...
app/assets/javascripts/boards/queries/board_list_update.mutation.graphql
0 → 100644
View file @
a249542e
#import "./board_list.fragment.graphql"
mutation
UpdateBoardList
(
$listId
:
ID
!,
$position
:
Int
,
$collapsed
:
Boolean
)
{
updateBoardList
(
input
:
{
listId
:
$listId
,
position
:
$position
,
collapsed
:
$collapsed
})
{
list
{
...
BoardListFragment
}
errors
}
}
app/assets/javascripts/boards/stores/actions.js
View file @
a249542e
...
@@ -15,6 +15,7 @@ import projectListsIssuesQuery from '../queries/project_lists_issues.query.graph
...
@@ -15,6 +15,7 @@ import projectListsIssuesQuery from '../queries/project_lists_issues.query.graph
import
projectBoardQuery
from
'
../queries/project_board.query.graphql
'
;
import
projectBoardQuery
from
'
../queries/project_board.query.graphql
'
;
import
groupBoardQuery
from
'
../queries/group_board.query.graphql
'
;
import
groupBoardQuery
from
'
../queries/group_board.query.graphql
'
;
import
createBoardListMutation
from
'
../queries/board_list_create.mutation.graphql
'
;
import
createBoardListMutation
from
'
../queries/board_list_create.mutation.graphql
'
;
import
updateBoardListMutation
from
'
../queries/board_list_update.mutation.graphql
'
;
const
notImplemented
=
()
=>
{
const
notImplemented
=
()
=>
{
/* eslint-disable-next-line @gitlab/require-i18n-strings */
/* eslint-disable-next-line @gitlab/require-i18n-strings */
...
@@ -147,8 +148,40 @@ export default {
...
@@ -147,8 +148,40 @@ export default {
notImplemented
();
notImplemented
();
},
},
updateList
:
()
=>
{
moveList
:
({
state
,
commit
,
dispatch
},
{
listId
,
position
,
moveNewIndexListUp
})
=>
{
notImplemented
();
const
{
boardLists
}
=
state
;
const
movedList
=
boardLists
.
find
(({
id
})
=>
id
===
listId
);
const
listAtNewIndex
=
boardLists
[
position
+
1
];
movedList
.
position
=
position
;
listAtNewIndex
.
position
=
moveNewIndexListUp
?
listAtNewIndex
.
position
+
1
:
listAtNewIndex
.
position
-
1
;
commit
(
types
.
MOVE_LIST
,
{
movedList
,
listAtNewIndex
,
});
dispatch
(
'
updateList
'
,
{
listId
,
position
});
},
updateList
:
({
commit
},
{
listId
,
position
,
collapsed
})
=>
{
gqlClient
.
mutate
({
mutation
:
updateBoardListMutation
,
variables
:
{
listId
,
position
,
collapsed
,
},
})
.
then
(({
data
})
=>
{
if
(
data
?.
updateBoardList
?.
errors
.
length
)
{
commit
(
types
.
UPDATE_LIST_FAILURE
);
}
})
.
catch
(()
=>
{
commit
(
types
.
UPDATE_LIST_FAILURE
);
});
},
},
deleteList
:
()
=>
{
deleteList
:
()
=>
{
...
...
app/assets/javascripts/boards/stores/boards_store.js
View file @
a249542e
...
@@ -858,21 +858,6 @@ const boardsStore = {
...
@@ -858,21 +858,6 @@ const boardsStore = {
},
},
refreshIssueData
(
issue
,
obj
)
{
refreshIssueData
(
issue
,
obj
)
{
// 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
,
{
const
convertedObj
=
convertObjectPropsToCamelCase
(
obj
,
{
dropKeys
:
[
'
issue_sidebar_endpoint
'
,
'
real_path
'
,
'
webUrl
'
],
dropKeys
:
[
'
issue_sidebar_endpoint
'
,
'
real_path
'
,
'
webUrl
'
],
});
});
...
...
app/assets/javascripts/boards/stores/mutation_types.js
View file @
a249542e
...
@@ -7,9 +7,8 @@ export const SHOW_PROMOTION_LIST = 'SHOW_PROMOTION_LIST';
...
@@ -7,9 +7,8 @@ export const SHOW_PROMOTION_LIST = 'SHOW_PROMOTION_LIST';
export
const
REQUEST_ADD_LIST
=
'
REQUEST_ADD_LIST
'
;
export
const
REQUEST_ADD_LIST
=
'
REQUEST_ADD_LIST
'
;
export
const
RECEIVE_ADD_LIST_SUCCESS
=
'
RECEIVE_ADD_LIST_SUCCESS
'
;
export
const
RECEIVE_ADD_LIST_SUCCESS
=
'
RECEIVE_ADD_LIST_SUCCESS
'
;
export
const
RECEIVE_ADD_LIST_ERROR
=
'
RECEIVE_ADD_LIST_ERROR
'
;
export
const
RECEIVE_ADD_LIST_ERROR
=
'
RECEIVE_ADD_LIST_ERROR
'
;
export
const
REQUEST_UPDATE_LIST
=
'
REQUEST_UPDATE_LIST
'
;
export
const
MOVE_LIST
=
'
MOVE_LIST
'
;
export
const
RECEIVE_UPDATE_LIST_SUCCESS
=
'
RECEIVE_UPDATE_LIST_SUCCESS
'
;
export
const
UPDATE_LIST_FAILURE
=
'
UPDATE_LIST_FAILURE
'
;
export
const
RECEIVE_UPDATE_LIST_ERROR
=
'
RECEIVE_UPDATE_LIST_ERROR
'
;
export
const
REQUEST_REMOVE_LIST
=
'
REQUEST_REMOVE_LIST
'
;
export
const
REQUEST_REMOVE_LIST
=
'
REQUEST_REMOVE_LIST
'
;
export
const
RECEIVE_REMOVE_LIST_SUCCESS
=
'
RECEIVE_REMOVE_LIST_SUCCESS
'
;
export
const
RECEIVE_REMOVE_LIST_SUCCESS
=
'
RECEIVE_REMOVE_LIST_SUCCESS
'
;
export
const
RECEIVE_REMOVE_LIST_ERROR
=
'
RECEIVE_REMOVE_LIST_ERROR
'
;
export
const
RECEIVE_REMOVE_LIST_ERROR
=
'
RECEIVE_REMOVE_LIST_ERROR
'
;
...
...
app/assets/javascripts/boards/stores/mutations.js
View file @
a249542e
import
Vue
from
'
vue
'
;
import
{
sortBy
}
from
'
lodash
'
;
import
*
as
mutationTypes
from
'
./mutation_types
'
;
import
*
as
mutationTypes
from
'
./mutation_types
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
__
}
from
'
~/locale
'
;
...
@@ -44,16 +46,18 @@ export default {
...
@@ -44,16 +46,18 @@ export default {
notImplemented
();
notImplemented
();
},
},
[
mutationTypes
.
REQUEST_UPDATE_LIST
]:
()
=>
{
[
mutationTypes
.
MOVE_LIST
]:
(
state
,
{
movedList
,
listAtNewIndex
})
=>
{
notImplemented
();
const
{
boardLists
}
=
state
;
state
.
boardListsPreviousState
=
boardLists
;
const
movedListIndex
=
state
.
boardLists
.
findIndex
(
l
=>
l
.
id
===
movedList
.
id
);
Vue
.
set
(
boardLists
,
movedListIndex
,
movedList
);
Vue
.
set
(
boardLists
,
movedListIndex
.
position
+
1
,
listAtNewIndex
);
state
.
boardLists
=
sortBy
(
boardLists
,
'
position
'
);
},
},
[
mutationTypes
.
RECEIVE_UPDATE_LIST_SUCCESS
]:
()
=>
{
[
mutationTypes
.
UPDATE_LIST_FAILURE
]:
state
=>
{
notImplemented
();
state
.
boardLists
=
state
.
boardListsPreviousState
;
},
state
.
error
=
__
(
'
An error occurred while updating the list. Please try again.
'
);
[
mutationTypes
.
RECEIVE_UPDATE_LIST_ERROR
]:
()
=>
{
notImplemented
();
},
},
[
mutationTypes
.
REQUEST_REMOVE_LIST
]:
()
=>
{
[
mutationTypes
.
REQUEST_REMOVE_LIST
]:
()
=>
{
...
...
app/assets/javascripts/boards/stores/state.js
View file @
a249542e
...
@@ -9,6 +9,7 @@ export default () => ({
...
@@ -9,6 +9,7 @@ export default () => ({
activeId
:
inactiveId
,
activeId
:
inactiveId
,
sidebarType
:
''
,
sidebarType
:
''
,
boardLists
:
[],
boardLists
:
[],
boardListsPreviousState
:
[],
issuesByListId
:
{},
issuesByListId
:
{},
issues
:
{},
issues
:
{},
isLoadingIssues
:
false
,
isLoadingIssues
:
false
,
...
...
ee/app/assets/javascripts/boards/components/epics_swimlanes.vue
View file @
a249542e
<
script
>
<
script
>
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
GlIcon
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
{
GlIcon
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
Draggable
from
'
vuedraggable
'
;
import
BoardListHeader
from
'
ee_else_ce/boards/components/board_list_header.vue
'
;
import
BoardListHeader
from
'
ee_else_ce/boards/components/board_list_header.vue
'
;
import
{
draggableTag
}
from
'
../constants
'
;
import
defaultSortableConfig
from
'
~/sortable/sortable_config
'
;
import
{
n__
}
from
'
~/locale
'
;
import
{
n__
}
from
'
~/locale
'
;
import
EpicLane
from
'
./epic_lane.vue
'
;
import
EpicLane
from
'
./epic_lane.vue
'
;
import
IssuesLaneList
from
'
./issues_lane_list.vue
'
;
import
IssuesLaneList
from
'
./issues_lane_list.vue
'
;
...
@@ -53,12 +56,44 @@ export default {
...
@@ -53,12 +56,44 @@ export default {
unassignedIssuesCountTooltipText
()
{
unassignedIssuesCountTooltipText
()
{
return
n__
(
`%d unassigned issue`
,
`%d unassigned issues`
,
this
.
unassignedIssuesCount
);
return
n__
(
`%d unassigned issue`
,
`%d unassigned issues`
,
this
.
unassignedIssuesCount
);
},
},
treeRootWrapper
()
{
return
this
.
canAdminList
?
Draggable
:
draggableTag
;
},
treeRootOptions
()
{
const
options
=
{
...
defaultSortableConfig
,
fallbackOnBody
:
false
,
group
:
'
board-swimlanes
'
,
tag
:
draggableTag
,
draggable
:
'
.is-draggable
'
,
'
ghost-class
'
:
'
swimlane-header-drag-active
'
,
value
:
this
.
lists
,
};
return
this
.
canAdminList
?
options
:
{};
},
},
},
mounted
()
{
mounted
()
{
this
.
fetchIssuesForAllLists
();
this
.
fetchIssuesForAllLists
();
},
},
methods
:
{
methods
:
{
...
mapActions
([
'
fetchIssuesForAllLists
'
]),
...
mapActions
([
'
fetchIssuesForAllLists
'
,
'
moveList
'
]),
handleDragOnEnd
(
params
)
{
const
{
newIndex
,
oldIndex
,
item
}
=
params
;
const
{
listId
}
=
item
.
dataset
;
const
newPosition
=
newIndex
-
1
;
let
moveNewIndexListUp
=
false
;
if
(
newIndex
<
oldIndex
)
{
moveNewIndexListUp
=
true
;
}
this
.
moveList
({
listId
,
position
:
newPosition
,
moveNewIndexListUp
,
});
},
},
},
};
};
</
script
>
</
script
>
...
@@ -68,16 +103,22 @@ export default {
...
@@ -68,16 +103,22 @@ export default {
class=
"board-swimlanes gl-white-space-nowrap gl-pb-5 gl-px-3"
class=
"board-swimlanes gl-white-space-nowrap gl-pb-5 gl-px-3"
data_qa_selector=
"board_epics_swimlanes"
data_qa_selector=
"board_epics_swimlanes"
>
>
<div
<component
:is=
"treeRootWrapper"
v-bind=
"treeRootOptions"
class=
"board-swimlanes-headers gl-display-table gl-sticky gl-pt-5 gl-bg-white gl-top-0 gl-z-index-3"
class=
"board-swimlanes-headers gl-display-table gl-sticky gl-pt-5 gl-bg-white gl-top-0 gl-z-index-3"
@
end=
"handleDragOnEnd"
>
>
<div
<div
v-for=
"list in lists"
v-for=
"list in lists"
:key=
"list.id"
:key=
"list.id"
:class=
"
{
:class=
"
{
'is-collapsed': !list.isExpanded,
'is-collapsed': !list.isExpanded,
'is-draggable': !list.preset,
}"
}"
class="board gl-px-3 gl-vertical-align-top gl-white-space-normal"
class="board gl-px-3 gl-vertical-align-top gl-white-space-normal"
:data-list-id="list.id"
data-testid="board-header-container"
>
>
<board-list-header
<board-list-header
:can-admin-list=
"canAdminList"
:can-admin-list=
"canAdminList"
...
@@ -87,7 +128,7 @@ export default {
...
@@ -87,7 +128,7 @@ export default {
:is-swimlanes-header=
"true"
:is-swimlanes-header=
"true"
/>
/>
</div>
</div>
</
div
>
</
component
>
<div
class=
"board-epics-swimlanes gl-display-table"
>
<div
class=
"board-epics-swimlanes gl-display-table"
>
<epic-lane
<epic-lane
v-for=
"epic in epics"
v-for=
"epic in epics"
...
...
ee/app/assets/javascripts/boards/constants.js
0 → 100644
View file @
a249542e
export
const
draggableTag
=
'
div
'
;
export
default
{
draggableTag
,
};
ee/app/assets/javascripts/boards/models/list.js
View file @
a249542e
...
@@ -14,7 +14,7 @@ const EE_TYPES = {
...
@@ -14,7 +14,7 @@ const EE_TYPES = {
class
ListEE
extends
List
{
class
ListEE
extends
List
{
constructor
(...
args
)
{
constructor
(...
args
)
{
super
(...
args
);
super
(...
args
);
this
.
totalWeight
=
0
;
this
.
totalWeight
=
args
[
0
]?.
totalWeight
||
0
;
}
}
getTypeInfo
(
type
)
{
getTypeInfo
(
type
)
{
...
...
ee/app/assets/javascripts/boards/queries/board_list.fragment.graphql
View file @
a249542e
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
fragment
BoardListFragment
on
BoardList
{
fragment
BoardListFragment
on
BoardList
{
...
BoardListShared
...
BoardListShared
maxIssueCount
maxIssueCount
totalWeight
assignee
{
assignee
{
id
id
name
name
...
...
ee/spec/frontend/boards/components/epics_swimlanes_spec.js
View file @
a249542e
import
Vuex
from
'
vuex
'
;
import
Vuex
from
'
vuex
'
;
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
createLocalVue
,
shallowMount
}
from
'
@vue/test-utils
'
;
import
Draggable
from
'
vuedraggable
'
;
import
EpicsSwimlanes
from
'
ee/boards/components/epics_swimlanes.vue
'
;
import
EpicsSwimlanes
from
'
ee/boards/components/epics_swimlanes.vue
'
;
import
BoardListHeader
from
'
ee_else_ce/boards/components/board_list_header.vue
'
;
import
BoardListHeader
from
'
ee_else_ce/boards/components/board_list_header.vue
'
;
import
EpicLane
from
'
ee/boards/components/epic_lane.vue
'
;
import
EpicLane
from
'
ee/boards/components/epic_lane.vue
'
;
import
IssueLaneList
from
'
ee/boards/components/issues_lane_list.vue
'
;
import
IssueLaneList
from
'
ee/boards/components/issues_lane_list.vue
'
;
import
getters
from
'
ee/boards/stores/getters
'
;
import
getters
from
'
ee/boards/stores/getters
'
;
import
{
draggableTag
}
from
'
ee/boards/constants
'
;
import
{
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
mockListsWithModel
,
mockEpics
,
mockIssuesByListId
,
issues
}
from
'
../mock_data
'
;
import
{
mockListsWithModel
,
mockEpics
,
mockIssuesByListId
,
issues
}
from
'
../mock_data
'
;
...
@@ -29,7 +31,7 @@ describe('EpicsSwimlanes', () => {
...
@@ -29,7 +31,7 @@ describe('EpicsSwimlanes', () => {
});
});
};
};
const
createComponent
=
()
=>
{
const
createComponent
=
(
props
=
{}
)
=>
{
const
store
=
createStore
();
const
store
=
createStore
();
const
defaultProps
=
{
const
defaultProps
=
{
lists
:
mockListsWithModel
,
lists
:
mockListsWithModel
,
...
@@ -40,7 +42,7 @@ describe('EpicsSwimlanes', () => {
...
@@ -40,7 +42,7 @@ describe('EpicsSwimlanes', () => {
wrapper
=
shallowMount
(
EpicsSwimlanes
,
{
wrapper
=
shallowMount
(
EpicsSwimlanes
,
{
localVue
,
localVue
,
propsData
:
defaultProps
,
propsData
:
{
...
defaultProps
,
...
props
}
,
store
,
store
,
});
});
};
};
...
@@ -49,6 +51,49 @@ describe('EpicsSwimlanes', () => {
...
@@ -49,6 +51,49 @@ describe('EpicsSwimlanes', () => {
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
describe
(
'
computed
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
describe
(
'
treeRootWrapper
'
,
()
=>
{
it
(
'
should return Draggable reference when canAdminList prop is true
'
,
()
=>
{
wrapper
.
setProps
({
canAdminList
:
true
});
expect
(
wrapper
.
vm
.
treeRootWrapper
).
toBe
(
Draggable
);
});
it
(
'
should return string "div" when canAdminList prop is false
'
,
()
=>
{
expect
(
wrapper
.
vm
.
treeRootWrapper
).
toBe
(
draggableTag
);
});
});
describe
(
'
treeRootOptions
'
,
()
=>
{
it
(
'
should return object containing Vue.Draggable config extended from `defaultSortableConfig` when canAdminList prop is true
'
,
()
=>
{
wrapper
.
setProps
({
canAdminList
:
true
});
expect
(
wrapper
.
vm
.
treeRootOptions
).
toEqual
(
expect
.
objectContaining
({
animation
:
200
,
forceFallback
:
true
,
fallbackClass
:
'
is-dragging
'
,
fallbackOnBody
:
false
,
ghostClass
:
'
is-ghost
'
,
group
:
'
board-swimlanes
'
,
tag
:
draggableTag
,
draggable
:
'
.is-draggable
'
,
'
ghost-class
'
:
'
swimlane-header-drag-active
'
,
value
:
mockListsWithModel
,
}),
);
});
it
(
'
should return an empty object when canAdminList prop is false
'
,
()
=>
{
expect
(
wrapper
.
vm
.
treeRootOptions
).
toEqual
(
expect
.
objectContaining
({}));
});
});
});
describe
(
'
template
'
,
()
=>
{
describe
(
'
template
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
createComponent
();
createComponent
();
...
@@ -68,7 +113,25 @@ describe('EpicsSwimlanes', () => {
...
@@ -68,7 +113,25 @@ describe('EpicsSwimlanes', () => {
it
(
'
displays issues icon and count for unassigned issue
'
,
()
=>
{
it
(
'
displays issues icon and count for unassigned issue
'
,
()
=>
{
expect
(
wrapper
.
find
(
GlIcon
).
props
(
'
name
'
)).
toEqual
(
'
issues
'
);
expect
(
wrapper
.
find
(
GlIcon
).
props
(
'
name
'
)).
toEqual
(
'
issues
'
);
expect
(
wrapper
.
find
(
'
[data-testid="issues-lane-issue-count"
'
).
text
()).
toEqual
(
'
2
'
);
expect
(
wrapper
.
find
(
'
[data-testid="issues-lane-issue-count"]
'
).
text
()).
toEqual
(
'
2
'
);
});
it
(
'
makes non preset lists draggable
'
,
()
=>
{
expect
(
wrapper
.
findAll
(
'
[data-testid="board-header-container"]
'
)
.
at
(
1
)
.
classes
(),
).
toContain
(
'
is-draggable
'
);
});
it
(
'
does not make preset lists draggable
'
,
()
=>
{
expect
(
wrapper
.
findAll
(
'
[data-testid="board-header-container"]
'
)
.
at
(
0
)
.
classes
(),
).
not
.
toContain
(
'
is-draggable
'
);
});
});
});
});
});
});
ee/spec/frontend/boards/mock_data.js
View file @
a249542e
...
@@ -12,6 +12,7 @@ export const mockLists = [
...
@@ -12,6 +12,7 @@ export const mockLists = [
maxIssueCount
:
0
,
maxIssueCount
:
0
,
assignee
:
null
,
assignee
:
null
,
milestone
:
null
,
milestone
:
null
,
preset
:
true
,
},
},
{
{
id
:
'
gid://gitlab/List/2
'
,
id
:
'
gid://gitlab/List/2
'
,
...
@@ -29,6 +30,7 @@ export const mockLists = [
...
@@ -29,6 +30,7 @@ export const mockLists = [
maxIssueCount
:
0
,
maxIssueCount
:
0
,
assignee
:
null
,
assignee
:
null
,
milestone
:
null
,
milestone
:
null
,
preset
:
false
,
},
},
];
];
...
...
locale/gitlab.pot
View file @
a249542e
...
@@ -2870,6 +2870,9 @@ msgstr ""
...
@@ -2870,6 +2870,9 @@ msgstr ""
msgid "An error occurred while updating the comment"
msgid "An error occurred while updating the comment"
msgstr ""
msgstr ""
msgid "An error occurred while updating the list. Please try again."
msgstr ""
msgid "An error occurred while validating group path"
msgid "An error occurred while validating group path"
msgstr ""
msgstr ""
...
...
spec/frontend/boards/stores/actions_spec.js
View file @
a249542e
import
testAction
from
'
helpers/vuex_action_helper
'
;
import
testAction
from
'
helpers/vuex_action_helper
'
;
import
{
mockListsWithModel
}
from
'
../mock_data
'
;
import
actions
,
{
gqlClient
}
from
'
~/boards/stores/actions
'
;
import
actions
,
{
gqlClient
}
from
'
~/boards/stores/actions
'
;
import
*
as
types
from
'
~/boards/stores/mutation_types
'
;
import
*
as
types
from
'
~/boards/stores/mutation_types
'
;
import
{
inactiveId
,
ListType
}
from
'
~/boards/constants
'
;
import
{
inactiveId
,
ListType
}
from
'
~/boards/constants
'
;
...
@@ -160,8 +161,58 @@ describe('createList', () => {
...
@@ -160,8 +161,58 @@ describe('createList', () => {
});
});
});
});
describe
(
'
moveList
'
,
()
=>
{
it
(
'
should commit MOVE_LIST mutation and dispatch updateList action
'
,
done
=>
{
const
state
=
{
endpoints
:
{
fullPath
:
'
gitlab-org
'
,
boardId
:
'
1
'
},
boardType
:
'
group
'
,
disabled
:
false
,
boardLists
:
mockListsWithModel
,
};
testAction
(
actions
.
moveList
,
{
listId
:
'
gid://gitlab/List/1
'
,
position
:
0
,
moveNewIndexListUp
:
true
},
state
,
[
{
type
:
types
.
MOVE_LIST
,
payload
:
{
movedList
:
mockListsWithModel
[
0
],
listAtNewIndex
:
mockListsWithModel
[
1
]
},
},
],
[{
type
:
'
updateList
'
,
payload
:
{
listId
:
'
gid://gitlab/List/1
'
,
position
:
0
}
}],
done
,
);
});
});
describe
(
'
updateList
'
,
()
=>
{
describe
(
'
updateList
'
,
()
=>
{
expectNotImplemented
(
actions
.
updateList
);
it
(
'
should commit UPDATE_LIST_FAILURE mutation when API returns an error
'
,
done
=>
{
jest
.
spyOn
(
gqlClient
,
'
mutate
'
).
mockResolvedValue
({
data
:
{
updateBoardList
:
{
list
:
{},
errors
:
[{
foo
:
'
bar
'
}],
},
},
});
const
state
=
{
endpoints
:
{
fullPath
:
'
gitlab-org
'
,
boardId
:
'
1
'
},
boardType
:
'
group
'
,
disabled
:
false
,
boardLists
:
[{
type
:
'
closed
'
}],
};
testAction
(
actions
.
updateList
,
{
listId
:
'
gid://gitlab/List/1
'
,
position
:
1
},
state
,
[{
type
:
types
.
UPDATE_LIST_FAILURE
}],
[],
done
,
);
});
});
});
describe
(
'
deleteList
'
,
()
=>
{
describe
(
'
deleteList
'
,
()
=>
{
...
...
spec/frontend/boards/stores/mutations_spec.js
View file @
a249542e
import
mutations
from
'
~/boards/stores/mutations
'
;
import
mutations
from
'
~/boards/stores/mutations
'
;
import
*
as
types
from
'
~/boards/stores/mutation_types
'
;
import
*
as
types
from
'
~/boards/stores/mutation_types
'
;
import
defaultState
from
'
~/boards/stores/state
'
;
import
defaultState
from
'
~/boards/stores/state
'
;
import
{
listObj
,
listObjDuplicate
,
mockIssue
}
from
'
../mock_data
'
;
import
{
listObj
,
listObjDuplicate
,
mockIssue
,
mockListsWithModel
}
from
'
../mock_data
'
;
const
expectNotImplemented
=
action
=>
{
const
expectNotImplemented
=
action
=>
{
it
(
'
is not implemented
'
,
()
=>
{
it
(
'
is not implemented
'
,
()
=>
{
...
@@ -92,16 +92,37 @@ describe('Board Store Mutations', () => {
...
@@ -92,16 +92,37 @@ describe('Board Store Mutations', () => {
expectNotImplemented
(
mutations
.
RECEIVE_ADD_LIST_ERROR
);
expectNotImplemented
(
mutations
.
RECEIVE_ADD_LIST_ERROR
);
});
});
describe
(
'
REQUEST_UPDATE_LIST
'
,
()
=>
{
describe
(
'
MOVE_LIST
'
,
()
=>
{
expectNotImplemented
(
mutations
.
REQUEST_UPDATE_LIST
);
it
(
'
updates boardLists state with reordered lists and saves previous order
'
,
()
=>
{
});
state
=
{
...
state
,
boardLists
:
mockListsWithModel
,
};
describe
(
'
RECEIVE_UPDATE_LIST_SUCCESS
'
,
()
=>
{
mutations
.
MOVE_LIST
(
state
,
{
expectNotImplemented
(
mutations
.
RECEIVE_UPDATE_LIST_SUCCESS
);
movedList
:
mockListsWithModel
[
0
],
listAtNewIndex
:
mockListsWithModel
[
1
],
});
expect
(
state
.
boardLists
).
toEqual
([
mockListsWithModel
[
1
],
mockListsWithModel
[
0
]]);
expect
(
state
.
boardListsPreviousState
).
toEqual
(
mockListsWithModel
);
});
});
});
describe
(
'
RECEIVE_UPDATE_LIST_ERROR
'
,
()
=>
{
describe
(
'
UPDATE_LIST_FAILURE
'
,
()
=>
{
expectNotImplemented
(
mutations
.
RECEIVE_UPDATE_LIST_ERROR
);
it
(
'
updates boardLists state with previous order and sets error message
'
,
()
=>
{
state
=
{
...
state
,
boardLists
:
[
mockListsWithModel
[
1
],
mockListsWithModel
[
0
]],
boardListsPreviousState
:
mockListsWithModel
,
error
:
undefined
,
};
mutations
.
UPDATE_LIST_FAILURE
(
state
);
expect
(
state
.
boardLists
).
toEqual
(
mockListsWithModel
);
expect
(
state
.
error
).
toEqual
(
'
An error occurred while updating the list. Please try again.
'
);
});
});
});
describe
(
'
REQUEST_REMOVE_LIST
'
,
()
=>
{
describe
(
'
REQUEST_REMOVE_LIST
'
,
()
=>
{
...
...
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