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
754c99fb
Commit
754c99fb
authored
Oct 14, 2020
by
Florie Guibert
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Swimlanes - Add group_by URL param
Add group_by URL param for Swimlanes
parent
1fb57db4
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
150 additions
and
15 deletions
+150
-15
app/assets/javascripts/boards/filtered_search_boards.js
app/assets/javascripts/boards/filtered_search_boards.js
+2
-1
ee/app/assets/javascripts/boards/components/epics_swimlanes.vue
.../assets/javascripts/boards/components/epics_swimlanes.vue
+5
-1
ee/app/assets/javascripts/boards/constants.js
ee/app/assets/javascripts/boards/constants.js
+4
-0
ee/app/assets/javascripts/boards/stores/actions.js
ee/app/assets/javascripts/boards/stores/actions.js
+24
-6
ee/app/assets/javascripts/boards/stores/mutation_types.js
ee/app/assets/javascripts/boards/stores/mutation_types.js
+1
-0
ee/app/assets/javascripts/boards/stores/mutations.js
ee/app/assets/javascripts/boards/stores/mutations.js
+5
-0
ee/spec/features/boards/swimlanes/epics_swimlanes_spec.rb
ee/spec/features/boards/swimlanes/epics_swimlanes_spec.rb
+27
-1
ee/spec/frontend/boards/stores/actions_spec.js
ee/spec/frontend/boards/stores/actions_spec.js
+67
-6
ee/spec/frontend/boards/stores/mutations_spec.js
ee/spec/frontend/boards/stores/mutations_spec.js
+15
-0
No files found.
app/assets/javascripts/boards/filtered_search_boards.js
View file @
754c99fb
...
...
@@ -25,7 +25,8 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
}
updateObject
(
path
)
{
this
.
store
.
path
=
path
.
substr
(
1
);
const
groupByParam
=
new
URLSearchParams
(
window
.
location
.
search
).
get
(
'
group_by
'
);
this
.
store
.
path
=
`
${
path
.
substr
(
1
)}${
groupByParam
?
`&group_by=
${
groupByParam
}
`
:
''
}
`
;
if
(
gon
.
features
.
boardsWithSwimlanes
||
gon
.
features
.
graphqlBoardLists
)
{
boardsStore
.
updateFiltersUrl
();
...
...
ee/app/assets/javascripts/boards/components/epics_swimlanes.vue
View file @
754c99fb
...
...
@@ -67,7 +67,10 @@ export default {
return
this
.
canAdminList
?
options
:
{};
},
hasMoreUnassignedIssues
()
{
return
this
.
lists
.
some
(
list
=>
this
.
pageInfoByListId
[
list
.
id
]?.
hasNextPage
);
return
(
this
.
unassignedIssuesCount
>
0
&&
this
.
lists
.
some
(
list
=>
this
.
pageInfoByListId
[
list
.
id
]?.
hasNextPage
)
);
},
},
methods
:
{
...
...
@@ -98,6 +101,7 @@ export default {
<
template
>
<div
class=
"board-swimlanes gl-white-space-nowrap gl-pb-5 gl-px-3"
data-testid=
"board-swimlanes"
data_qa_selector=
"board_epics_swimlanes"
>
<component
...
...
ee/app/assets/javascripts/boards/constants.js
View file @
754c99fb
...
...
@@ -6,6 +6,10 @@ export const EpicFilterType = {
none
:
'
None
'
,
};
export
const
GroupByParamType
=
{
epic
:
'
epic
'
,
};
export
default
{
DRAGGABLE_TAG
,
EpicFilterType
,
...
...
ee/app/assets/javascripts/boards/stores/actions.js
View file @
754c99fb
...
...
@@ -3,10 +3,11 @@ import Cookies from 'js-cookie';
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
boardsStore
from
'
~/boards/stores/boards_store
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
{
historyPushState
,
parseBoolean
}
from
'
~/lib/utils/common_utils
'
;
import
{
setUrlParams
,
removeParams
}
from
'
~/lib/utils/url_utility
'
;
import
actionsCE
from
'
~/boards/stores/actions
'
;
import
{
BoardType
,
ListType
}
from
'
~/boards/constants
'
;
import
{
EpicFilterType
}
from
'
../constants
'
;
import
{
EpicFilterType
,
GroupByParamType
}
from
'
../constants
'
;
import
boardsStoreEE
from
'
./boards_store_ee
'
;
import
*
as
types
from
'
./mutation_types
'
;
import
*
as
typesCE
from
'
~/boards/stores/mutation_types
'
;
...
...
@@ -70,7 +71,7 @@ const fetchAndFormatListIssues = (state, extraVariables) => {
export
default
{
...
actionsCE
,
setFilters
:
({
commit
},
filters
)
=>
{
setFilters
:
({
commit
,
dispatch
},
filters
)
=>
{
const
filterParams
=
pick
(
filters
,
[
'
assigneeUsername
'
,
'
authorUsername
'
,
...
...
@@ -82,6 +83,10 @@ export default {
'
weight
'
,
]);
if
(
filters
.
groupBy
===
GroupByParamType
.
epic
)
{
dispatch
(
'
setEpicSwimlanes
'
);
}
if
(
filterParams
.
epicId
===
EpicFilterType
.
any
||
filterParams
.
epicId
===
EpicFilterType
.
none
)
{
filterParams
.
epicWildcardId
=
filterParams
.
epicId
.
toUpperCase
();
filterParams
.
epicId
=
undefined
;
...
...
@@ -232,13 +237,16 @@ export default {
fetchIssuesForList
:
({
state
,
commit
},
{
listId
,
fetchNext
=
false
,
noEpicIssues
=
false
})
=>
{
commit
(
types
.
REQUEST_ISSUES_FOR_LIST
,
{
listId
,
fetchNext
});
const
{
filterParams
}
=
state
;
const
{
epicId
,
...
filterParams
}
=
state
.
filterParams
;
if
(
noEpicIssues
&&
epicId
!==
undefined
)
{
return
null
;
}
const
variables
=
{
id
:
listId
,
filters
:
noEpicIssues
?
{
...
filterParams
,
epicWildcardId
:
EpicFilterType
.
none
.
toUpperCase
()
}
:
filterParams
,
:
{
...
filterParams
,
epicId
}
,
after
:
fetchNext
?
state
.
pageInfoByListId
[
listId
].
endCursor
:
undefined
,
first
:
20
,
};
...
...
@@ -275,13 +283,23 @@ export default {
commit
(
types
.
TOGGLE_EPICS_SWIMLANES
);
if
(
state
.
isShowingEpicsSwimlanes
)
{
dispatch
(
'
fetchEpicsSwimlanes
'
,
{}).
catch
(()
=>
commit
(
types
.
RECEIVE_SWIMLANES_FAILURE
));
historyPushState
(
setUrlParams
({
group_by
:
GroupByParamType
.
epic
},
window
.
location
.
href
));
dispatch
(
'
fetchEpicsSwimlanes
'
,
{});
}
else
if
(
!
gon
.
features
.
graphqlBoardLists
)
{
historyPushState
(
removeParams
([
'
group_by
'
]));
boardsStore
.
create
();
eventHub
.
$emit
(
'
initialBoardLoad
'
);
}
else
{
historyPushState
(
removeParams
([
'
group_by
'
]));
}
},
setEpicSwimlanes
:
({
commit
,
dispatch
})
=>
{
commit
(
types
.
SET_EPICS_SWIMLANES
);
dispatch
(
'
fetchEpicsSwimlanes
'
,
{});
},
resetEpics
:
({
commit
})
=>
{
commit
(
types
.
RESET_EPICS
);
},
...
...
ee/app/assets/javascripts/boards/stores/mutation_types.js
View file @
754c99fb
...
...
@@ -18,6 +18,7 @@ export const REQUEST_ISSUES_FOR_EPIC = 'REQUEST_ISSUES_FOR_EPIC';
export
const
RECEIVE_ISSUES_FOR_EPIC_SUCCESS
=
'
RECEIVE_ISSUES_FOR_EPIC_SUCCESS
'
;
export
const
RECEIVE_ISSUES_FOR_EPIC_FAILURE
=
'
RECEIVE_ISSUES_FOR_EPIC_FAILURE
'
;
export
const
TOGGLE_EPICS_SWIMLANES
=
'
TOGGLE_EPICS_SWIMLANES
'
;
export
const
SET_EPICS_SWIMLANES
=
'
SET_EPICS_SWIMLANES
'
;
export
const
RECEIVE_BOARD_LISTS_SUCCESS
=
'
RECEIVE_BOARD_LISTS_SUCCESS
'
;
export
const
RECEIVE_SWIMLANES_FAILURE
=
'
RECEIVE_SWIMLANES_FAILURE
'
;
export
const
RECEIVE_FIRST_EPICS_SUCCESS
=
'
RECEIVE_FIRST_EPICS_SUCCESS
'
;
...
...
ee/app/assets/javascripts/boards/stores/mutations.js
View file @
754c99fb
...
...
@@ -110,6 +110,11 @@ export default {
state
.
epicsSwimlanesFetchInProgress
=
true
;
},
[
mutationTypes
.
SET_EPICS_SWIMLANES
]:
state
=>
{
state
.
isShowingEpicsSwimlanes
=
true
;
state
.
epicsSwimlanesFetchInProgress
=
true
;
},
[
mutationTypes
.
RECEIVE_BOARD_LISTS_SUCCESS
]:
(
state
,
boardLists
)
=>
{
state
.
boardLists
=
boardLists
;
state
.
epicsSwimlanesFetchInProgress
=
false
;
...
...
ee/spec/features/boards/swimlanes/epics_swimlanes_spec.rb
View file @
754c99fb
...
...
@@ -21,6 +21,27 @@ RSpec.describe 'epics swimlanes', :js do
let_it_be
(
:epic_issue1
)
{
create
(
:epic_issue
,
epic:
epic1
,
issue:
issue1
)
}
let_it_be
(
:epic_issue2
)
{
create
(
:epic_issue
,
epic:
epic2
,
issue:
issue2
)
}
context
'link to swimlanes view'
do
before
do
stub_licensed_features
(
epics:
true
)
sign_in
(
user
)
visit_epics_swimlanes_page
end
it
'displays epics swimlanes when link to boards with group_by epic in URL'
do
expect
(
page
).
to
have_selector
(
'[data-testid="board-swimlanes"]'
)
epic_lanes
=
page
.
all
(
:css
,
'.board-epic-lane'
)
expect
(
epic_lanes
.
length
).
to
eq
(
2
)
end
it
'displays issue not assigned to epic in unassigned issues lane'
do
page
.
within
(
'.board-lane-unassigned-issues-title'
)
do
expect
(
page
.
find
(
'span[data-testid="issues-lane-issue-count"]'
)).
to
have_content
(
'1'
)
end
end
end
before
do
stub_licensed_features
(
epics:
true
,
swimlanes:
true
)
sign_in
(
user
)
...
...
@@ -30,7 +51,7 @@ RSpec.describe 'epics swimlanes', :js do
context
'switch to swimlanes view'
do
it
'displays epics swimlanes when selecting Epic in Group by dropdown'
do
expect
(
page
).
to
have_
css
(
'.board-swimlanes
'
)
expect
(
page
).
to
have_
selector
(
'[data-testid="board-swimlanes"]
'
)
epic_lanes
=
page
.
all
(
:css
,
'.board-epic-lane'
)
expect
(
epic_lanes
.
length
).
to
eq
(
2
)
...
...
@@ -111,4 +132,9 @@ RSpec.describe 'epics swimlanes', :js do
page
.
find
(
'.dropdown-item'
,
text:
'Epic'
).
click
end
end
def
visit_epics_swimlanes_page
visit
"
#{
project_boards_path
(
project
)
}
?group_by=epic"
wait_for_requests
end
end
ee/spec/frontend/boards/stores/actions_spec.js
View file @
754c99fb
...
...
@@ -3,8 +3,11 @@ import MockAdapter from 'axios-mock-adapter';
import
boardsStoreEE
from
'
ee/boards/stores/boards_store_ee
'
;
import
actions
,
{
gqlClient
}
from
'
ee/boards/stores/actions
'
;
import
*
as
types
from
'
ee/boards/stores/mutation_types
'
;
import
{
GroupByParamType
}
from
'
ee/boards/constants
'
;
import
testAction
from
'
helpers/vuex_action_helper
'
;
import
*
as
typesCE
from
'
~/boards/stores/mutation_types
'
;
import
*
as
commonUtils
from
'
~/lib/utils/common_utils
'
;
import
{
setUrlParams
,
removeParams
}
from
'
~/lib/utils/url_utility
'
;
import
{
ListType
}
from
'
~/boards/constants
'
;
import
{
formatListIssues
}
from
'
~/boards/boards_util
'
;
import
{
...
...
@@ -28,6 +31,7 @@ let mock;
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
window
.
gon
=
{
features
:
{}
};
jest
.
spyOn
(
commonUtils
,
'
historyPushState
'
);
});
afterEach
(()
=>
{
...
...
@@ -35,7 +39,7 @@ afterEach(() => {
});
describe
(
'
setFilters
'
,
()
=>
{
it
(
'
should commit mutation SET_FILTERS, updates epicId with global id
'
,
done
=>
{
it
(
'
should commit mutation SET_FILTERS, updates epicId with global id
'
,
()
=>
{
const
state
=
{
filters
:
{},
};
...
...
@@ -43,17 +47,16 @@ describe('setFilters', () => {
const
filters
=
{
labelName
:
'
label
'
,
epicId
:
1
};
const
updatedFilters
=
{
labelName
:
'
label
'
,
epicId
:
'
gid://gitlab/Epic/1
'
};
testAction
(
return
testAction
(
actions
.
setFilters
,
filters
,
state
,
[{
type
:
types
.
SET_FILTERS
,
payload
:
updatedFilters
}],
[],
done
,
);
});
it
(
'
should commit mutation SET_FILTERS, updates epicWildcardId
'
,
done
=>
{
it
(
'
should commit mutation SET_FILTERS, updates epicWildcardId
'
,
()
=>
{
const
state
=
{
filters
:
{},
};
...
...
@@ -61,13 +64,29 @@ describe('setFilters', () => {
const
filters
=
{
labelName
:
'
label
'
,
epicId
:
'
None
'
};
const
updatedFilters
=
{
labelName
:
'
label
'
,
epicWildcardId
:
'
NONE
'
};
testAction
(
return
testAction
(
actions
.
setFilters
,
filters
,
state
,
[{
type
:
types
.
SET_FILTERS
,
payload
:
updatedFilters
}],
[],
done
,
);
});
it
(
'
should commit mutation SET_FILTERS, dispatches setEpicSwimlanes action if filters contain groupBy epic
'
,
()
=>
{
const
state
=
{
filters
:
{},
};
const
filters
=
{
labelName
:
'
label
'
,
epicId
:
1
,
groupBy
:
'
epic
'
};
const
updatedFilters
=
{
labelName
:
'
label
'
,
epicId
:
'
gid://gitlab/Epic/1
'
};
return
testAction
(
actions
.
setFilters
,
filters
,
state
,
[{
type
:
types
.
SET_FILTERS
,
payload
:
updatedFilters
}],
[{
type
:
'
setEpicSwimlanes
'
}],
);
});
});
...
...
@@ -391,6 +410,48 @@ describe('toggleEpicSwimlanes', () => {
state
,
[{
type
:
types
.
TOGGLE_EPICS_SWIMLANES
}],
[],
()
=>
{
expect
(
commonUtils
.
historyPushState
).
toHaveBeenCalledWith
(
removeParams
([
'
group_by
'
]));
},
);
});
it
(
'
should dispatch fetchEpicsSwimlanes action when isShowingEpicsSwimlanes is true
'
,
()
=>
{
jest
.
spyOn
(
gqlClient
,
'
query
'
).
mockResolvedValue
({});
const
state
=
{
isShowingEpicsSwimlanes
:
true
,
endpoints
:
{
fullPath
:
'
gitlab-org
'
,
boardId
:
1
,
},
};
return
testAction
(
actions
.
toggleEpicSwimlanes
,
null
,
state
,
[{
type
:
types
.
TOGGLE_EPICS_SWIMLANES
}],
[{
type
:
'
fetchEpicsSwimlanes
'
,
payload
:
{}
}],
()
=>
{
expect
(
commonUtils
.
historyPushState
).
toHaveBeenCalledWith
(
setUrlParams
({
group_by
:
GroupByParamType
.
epic
},
window
.
location
.
href
),
);
},
);
});
});
describe
(
'
setEpicSwimlanes
'
,
()
=>
{
it
(
'
should commit mutation SET_EPICS_SWIMLANES and dispatch fetchEpicsSwimlanes action
'
,
()
=>
{
jest
.
spyOn
(
gqlClient
,
'
query
'
).
mockResolvedValue
({});
return
testAction
(
actions
.
setEpicSwimlanes
,
null
,
{},
[{
type
:
types
.
SET_EPICS_SWIMLANES
}],
[{
type
:
'
fetchEpicsSwimlanes
'
,
payload
:
{}
}],
);
});
});
...
...
ee/spec/frontend/boards/stores/mutations_spec.js
View file @
754c99fb
...
...
@@ -178,6 +178,21 @@ describe('TOGGLE_EPICS_SWIMLANES', () => {
});
});
describe
(
'
SET_EPICS_SWIMLANES
'
,
()
=>
{
it
(
'
set isShowingEpicsSwimlanes and epicsSwimlanesFetchInProgress to true
'
,
()
=>
{
state
=
{
...
state
,
isShowingEpicsSwimlanes
:
false
,
epicsSwimlanesFetchInProgress
:
false
,
};
mutations
.
SET_EPICS_SWIMLANES
(
state
);
expect
(
state
.
isShowingEpicsSwimlanes
).
toBe
(
true
);
expect
(
state
.
epicsSwimlanesFetchInProgress
).
toBe
(
true
);
});
});
describe
(
'
RECEIVE_BOARD_LISTS_SUCCESS
'
,
()
=>
{
it
(
'
sets epicsSwimlanesFetchInProgress to false and populates boardLists with payload
'
,
()
=>
{
state
=
{
...
...
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