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
52aeccae
Commit
52aeccae
authored
Mar 23, 2022
by
Scott Stern
Committed by
Jose Ivan Vargas
Mar 23, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
When no iterations are present show empty state
Changelog: added EE: true
parent
09289e92
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
128 additions
and
44 deletions
+128
-44
ee/app/assets/javascripts/iterations/components/iterations.vue
...p/assets/javascripts/iterations/components/iterations.vue
+32
-2
ee/app/assets/javascripts/sidebar/constants.js
ee/app/assets/javascripts/sidebar/constants.js
+1
-1
ee/app/views/projects/iterations/index.html.haml
ee/app/views/projects/iterations/index.html.haml
+2
-1
ee/spec/features/groups/iterations/user_views_iteration_spec.rb
...c/features/groups/iterations/user_views_iteration_spec.rb
+1
-1
ee/spec/frontend/iterations/components/iterations_spec.js
ee/spec/frontend/iterations/components/iterations_spec.js
+58
-36
ee/spec/frontend/iterations/mock_data.js
ee/spec/frontend/iterations/mock_data.js
+28
-0
locale/gitlab.pot
locale/gitlab.pot
+6
-3
No files found.
ee/app/assets/javascripts/iterations/components/iterations.vue
View file @
52aeccae
<
script
>
<
script
>
import
{
GlAlert
,
GlButton
,
GlLoadingIcon
,
GlPagination
,
GlTab
,
GlTabs
}
from
'
@gitlab/ui
'
;
import
emptyStateSvg
from
'
@gitlab/svgs/dist/illustrations/issues.svg
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
GlAlert
,
GlButton
,
GlLoadingIcon
,
GlPagination
,
GlTab
,
GlTabs
,
GlEmptyState
,
}
from
'
@gitlab/ui
'
;
import
{
__
,
s__
}
from
'
~/locale
'
;
import
{
Namespace
}
from
'
../constants
'
;
import
{
Namespace
}
from
'
../constants
'
;
import
IterationsQuery
from
'
../queries/iterations.query.graphql
'
;
import
IterationsQuery
from
'
../queries/iterations.query.graphql
'
;
import
IterationsList
from
'
./iterations_list.vue
'
;
import
IterationsList
from
'
./iterations_list.vue
'
;
...
@@ -8,6 +17,14 @@ import IterationsList from './iterations_list.vue';
...
@@ -8,6 +17,14 @@ import IterationsList from './iterations_list.vue';
const
pageSize
=
20
;
const
pageSize
=
20
;
export
default
{
export
default
{
i18n
:
{
emptyStateDescription
:
s__
(
'
Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics.
'
,
),
newIteration
:
s__
(
'
Iterations|New iteration
'
),
noIterationsFound
:
s__
(
'
Iterations|No iterations found
'
),
},
emptySvgPath
:
`data:image/svg+xml;utf8,
${
encodeURIComponent
(
emptyStateSvg
)}
`
,
components
:
{
components
:
{
IterationsList
,
IterationsList
,
GlAlert
,
GlAlert
,
...
@@ -16,6 +33,7 @@ export default {
...
@@ -16,6 +33,7 @@ export default {
GlPagination
,
GlPagination
,
GlTab
,
GlTab
,
GlTabs
,
GlTabs
,
GlEmptyState
,
},
},
props
:
{
props
:
{
fullPath
:
{
fullPath
:
{
...
@@ -113,6 +131,9 @@ export default {
...
@@ -113,6 +131,9 @@ export default {
nextPage
()
{
nextPage
()
{
return
Number
(
this
.
namespace
.
pageInfo
.
hasNextPage
);
return
Number
(
this
.
namespace
.
pageInfo
.
hasNextPage
);
},
},
showEmptyState
()
{
return
this
.
iterations
.
length
===
0
&&
!
this
.
loading
;
},
},
},
methods
:
{
methods
:
{
handlePageChange
(
page
)
{
handlePageChange
(
page
)
{
...
@@ -151,6 +172,15 @@ export default {
...
@@ -151,6 +172,15 @@ export default {
{{ error }}
{{ error }}
</gl-alert>
</gl-alert>
</div>
</div>
<gl-empty-state
v-else-if=
"showEmptyState"
:svg-path=
"$options.emptySvgPath"
:title=
"$options.i18n.noIterationsFound"
:primary-button-text=
"$options.i18n.newIteration"
:primary-button-link=
"newIterationPath"
:description=
"$options.i18n.emptyStateDescription"
/>
<div
v-else
>
<div
v-else
>
<iterations-list
:iterations=
"iterations"
:namespace-type=
"namespaceType"
/>
<iterations-list
:iterations=
"iterations"
:namespace-type=
"namespaceType"
/>
<gl-pagination
<gl-pagination
...
...
ee/app/assets/javascripts/sidebar/constants.js
View file @
52aeccae
...
@@ -48,7 +48,7 @@ export const iterationSelectTextMap = {
...
@@ -48,7 +48,7 @@ export const iterationSelectTextMap = {
iterationSelectFail
:
__
(
'
Failed to set iteration on this issue. Please try again.
'
),
iterationSelectFail
:
__
(
'
Failed to set iteration on this issue. Please try again.
'
),
currentIterationFetchError
:
__
(
'
Failed to fetch the iteration for this issue. Please try again.
'
),
currentIterationFetchError
:
__
(
'
Failed to fetch the iteration for this issue. Please try again.
'
),
iterationsFetchError
:
__
(
'
Failed to fetch the iterations for the group. Please try again.
'
),
iterationsFetchError
:
__
(
'
Failed to fetch the iterations for the group. Please try again.
'
),
noIterationsFound
:
__
(
'
No iterations found
'
),
noIterationsFound
:
s__
(
'
Iterations|
No iterations found
'
),
};
};
export
const
noIteration
=
null
;
export
const
noIteration
=
null
;
...
...
ee/app/views/projects/iterations/index.html.haml
View file @
52aeccae
-
page_title
_
(
"Iterations"
)
-
page_title
_
(
"Iterations"
)
-
iterations_path
=
@project
&
.
group
?
new_group_iteration_path
(
@project
&
.
group
)
:
''
.js-iterations-list
{
data:
{
full_path:
@project
.
full_path
}
}
.js-iterations-list
{
data:
{
full_path:
@project
.
full_path
,
new_iteration_path:
iterations_path
}
}
ee/spec/features/groups/iterations/user_views_iteration_spec.rb
View file @
52aeccae
...
@@ -92,7 +92,7 @@ RSpec.describe 'User views iteration' do
...
@@ -92,7 +92,7 @@ RSpec.describe 'User views iteration' do
wait_for_requests
wait_for_requests
expect
(
page
).
to
have_content
(
'No iterations
to show
'
)
expect
(
page
).
to
have_content
(
'No iterations
found
'
)
expect
(
page
).
not_to
have_content
(
iteration
.
title
)
expect
(
page
).
not_to
have_content
(
iteration
.
title
)
end
end
end
end
...
...
ee/spec/frontend/iterations/components/iterations_spec.js
View file @
52aeccae
import
{
GlAlert
,
GlLoadingIcon
,
GlPagination
,
GlTab
,
GlTabs
}
from
'
@gitlab/ui
'
;
import
{
GlAlert
,
Gl
EmptyState
,
Gl
LoadingIcon
,
GlPagination
,
GlTab
,
GlTabs
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
nextTick
}
from
'
vue
'
;
import
Vue
,
{
nextTick
}
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
Iterations
from
'
ee/iterations/components/iterations.vue
'
;
import
Iterations
from
'
ee/iterations/components/iterations.vue
'
;
import
IterationsList
from
'
ee/iterations/components/iterations_list.vue
'
;
import
IterationsList
from
'
ee/iterations/components/iterations_list.vue
'
;
import
{
Namespace
}
from
'
ee/iterations/constants
'
;
import
{
Namespace
}
from
'
ee/iterations/constants
'
;
import
query
from
'
ee/iterations/queries/iterations.query.graphql
'
;
import
createMockApollo
from
'
helpers/mock_apollo_helper
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
mockGroupIterations
,
mockGroupIterationsEmpty
}
from
'
../mock_data
'
;
describe
(
'
Iterations
'
,
()
=>
{
describe
(
'
Iterations
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
let
mockApollo
;
const
defaultProps
=
{
const
defaultProps
=
{
fullPath
:
'
gitlab-org
'
,
fullPath
:
'
gitlab-org
'
,
};
};
const
mountComponent
=
({
props
=
defaultProps
,
loading
=
false
}
=
{})
=>
{
const
mountComponent
=
({
props
=
defaultProps
,
queryResponse
=
mockGroupIterations
,
queryHandler
=
jest
.
fn
().
mockResolvedValue
(
queryResponse
),
}
=
{})
=>
{
Vue
.
use
(
VueApollo
);
mockApollo
=
createMockApollo
([[
query
,
queryHandler
]]);
wrapper
=
shallowMount
(
Iterations
,
{
wrapper
=
shallowMount
(
Iterations
,
{
apolloProvider
:
mockApollo
,
propsData
:
props
,
propsData
:
props
,
mocks
:
{
$apollo
:
{
queries
:
{
namespace
:
{
loading
}
},
},
},
stubs
:
{
stubs
:
{
GlLoadingIcon
,
GlLoadingIcon
,
GlTab
,
GlTab
,
...
@@ -29,23 +39,22 @@ describe('Iterations', () => {
...
@@ -29,23 +39,22 @@ describe('Iterations', () => {
afterEach
(()
=>
{
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
.
destroy
();
wrapper
=
null
;
});
});
it
(
'
hides list while loading
'
,
()
=>
{
it
(
'
hides list while loading
'
,
()
=>
{
mountComponent
({
mountComponent
();
loading
:
true
,
});
expect
(
wrapper
.
findComponent
(
GlLoadingIcon
).
exists
()).
toBeTruthy
();
expect
(
wrapper
.
findComponent
(
GlLoadingIcon
).
exists
()).
toBeTruthy
();
expect
(
wrapper
.
findComponent
(
IterationsList
).
exists
()).
toBeFalsy
();
expect
(
wrapper
.
findComponent
(
IterationsList
).
exists
()).
toBeFalsy
();
});
});
it
(
'
shows iterations list
when not loading
'
,
()
=>
{
it
(
'
shows iterations list
after loading
'
,
async
()
=>
{
mountComponent
({
mountComponent
({
loading
:
false
,
props
:
{
...
defaultProps
,
newIterationPath
:
'
iterations
'
}
,
});
});
await
waitForPromises
();
expect
(
wrapper
.
findComponent
(
GlLoadingIcon
).
exists
()).
toBeFalsy
();
expect
(
wrapper
.
findComponent
(
GlLoadingIcon
).
exists
()).
toBeFalsy
();
expect
(
wrapper
.
findComponent
(
IterationsList
).
exists
()).
toBeTruthy
();
expect
(
wrapper
.
findComponent
(
IterationsList
).
exists
()).
toBeTruthy
();
});
});
...
@@ -64,6 +73,26 @@ describe('Iterations', () => {
...
@@ -64,6 +73,26 @@ describe('Iterations', () => {
expect
(
wrapper
.
vm
.
state
).
toEqual
(
'
all
'
);
expect
(
wrapper
.
vm
.
state
).
toEqual
(
'
all
'
);
});
});
describe
(
'
when loading is false and iterations are empty
'
,
()
=>
{
beforeEach
(
async
()
=>
{
mountComponent
({
props
:
{
...
defaultProps
,
newIterationPath
:
'
iterations
'
,
},
queryResponse
:
mockGroupIterationsEmpty
,
});
await
waitForPromises
();
});
it
(
'
renders GlEmptyState with the correct props
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
GlEmptyState
).
props
()).
toEqual
(
expect
.
objectContaining
({
primaryButtonLink
:
'
iterations
'
}),
);
});
});
describe
(
'
pagination
'
,
()
=>
{
describe
(
'
pagination
'
,
()
=>
{
const
findPagination
=
()
=>
wrapper
.
findComponent
(
GlPagination
);
const
findPagination
=
()
=>
wrapper
.
findComponent
(
GlPagination
);
const
setPage
=
async
(
page
)
=>
{
const
setPage
=
async
(
page
)
=>
{
...
@@ -71,22 +100,12 @@ describe('Iterations', () => {
...
@@ -71,22 +100,12 @@ describe('Iterations', () => {
await
nextTick
();
await
nextTick
();
};
};
beforeEach
(()
=>
{
beforeEach
(
async
()
=>
{
mountComponent
({
mountComponent
({
loading
:
false
,
queryResponse
:
mockGroupIterations
,
});
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper
.
setData
({
namespace
:
{
pageInfo
:
{
hasNextPage
:
true
,
hasPreviousPage
:
false
,
startCursor
:
'
first-item
'
,
endCursor
:
'
last-item
'
,
},
},
});
});
await
waitForPromises
();
});
});
it
(
'
passes prev, next, and current page props
'
,
()
=>
{
it
(
'
passes prev, next, and current page props
'
,
()
=>
{
...
@@ -184,19 +203,22 @@ describe('Iterations', () => {
...
@@ -184,19 +203,22 @@ describe('Iterations', () => {
});
});
describe
(
'
error
'
,
()
=>
{
describe
(
'
error
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(
async
()
=>
{
mountComponent
({
mountComponent
({
loading
:
false
,
queryHandler
:
jest
.
fn
().
mockRejectedValue
({
});
data
:
{
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
group
:
{
// eslint-disable-next-line no-restricted-syntax
errors
:
[
'
oh no
'
],
wrapper
.
setData
({
},
error
:
'
Oh no!
'
,
},
}),
});
});
await
waitForPromises
();
});
});
it
(
'
tab shows error in alert
'
,
()
=>
{
it
(
'
tab shows error in alert
'
,
()
=>
{
expect
(
wrapper
.
findComponent
(
GlAlert
).
text
()).
toContain
(
'
Oh no!
'
);
expect
(
wrapper
.
findComponent
(
GlAlert
).
text
()).
toContain
(
'
Error loading iterations
'
);
});
});
});
});
});
});
ee/spec/frontend/iterations/mock_data.js
View file @
52aeccae
...
@@ -10,6 +10,7 @@ export const mockIterationNode = {
...
@@ -10,6 +10,7 @@ export const mockIterationNode = {
state
:
iterationStates
.
upcoming
,
state
:
iterationStates
.
upcoming
,
title
:
'
top-level-iteration
'
,
title
:
'
top-level-iteration
'
,
webPath
:
'
/groups/top-level-group/-/iterations/4
'
,
webPath
:
'
/groups/top-level-group/-/iterations/4
'
,
scopedPath
:
'
/groups/top-level-group/-/iterations/4
'
,
__typename
:
'
Iteration
'
,
__typename
:
'
Iteration
'
,
};
};
...
@@ -29,6 +30,33 @@ export const mockGroupIterations = {
...
@@ -29,6 +30,33 @@ export const mockGroupIterations = {
id
:
'
gid://gitlab/Group/114
'
,
id
:
'
gid://gitlab/Group/114
'
,
iterations
:
{
iterations
:
{
nodes
:
[
mockIterationNode
],
nodes
:
[
mockIterationNode
],
pageInfo
:
{
hasNextPage
:
true
,
hasPreviousPage
:
true
,
startCursor
:
'
first-item
'
,
endCursor
:
'
last-item
'
,
__typename
:
'
PageInfo
'
,
},
__typename
:
'
IterationConnection
'
,
},
__typename
:
'
Group
'
,
},
},
};
export
const
mockGroupIterationsEmpty
=
{
data
:
{
group
:
{
id
:
'
gid://gitlab/Group/114
'
,
iterations
:
{
nodes
:
[],
pageInfo
:
{
hasNextPage
:
false
,
hasPreviousPage
:
false
,
startCursor
:
''
,
endCursor
:
''
,
__typename
:
'
PageInfo
'
,
},
__typename
:
'
IterationConnection
'
,
__typename
:
'
IterationConnection
'
,
},
},
__typename
:
'
Group
'
,
__typename
:
'
Group
'
,
...
...
locale/gitlab.pot
View file @
52aeccae
...
@@ -21000,6 +21000,9 @@ msgstr ""
...
@@ -21000,6 +21000,9 @@ msgstr ""
msgid "Iterations|Iteration scheduling will be handled automatically"
msgid "Iterations|Iteration scheduling will be handled automatically"
msgstr ""
msgstr ""
msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics."
msgstr ""
msgid "Iterations|Move incomplete issues to the next iteration"
msgid "Iterations|Move incomplete issues to the next iteration"
msgstr ""
msgstr ""
...
@@ -21015,6 +21018,9 @@ msgstr ""
...
@@ -21015,6 +21018,9 @@ msgstr ""
msgid "Iterations|No iteration cadences to show."
msgid "Iterations|No iteration cadences to show."
msgstr ""
msgstr ""
msgid "Iterations|No iterations found"
msgstr ""
msgid "Iterations|No iterations in cadence."
msgid "Iterations|No iterations in cadence."
msgstr ""
msgstr ""
...
@@ -25151,9 +25157,6 @@ msgstr ""
...
@@ -25151,9 +25157,6 @@ msgstr ""
msgid "No iteration"
msgid "No iteration"
msgstr ""
msgstr ""
msgid "No iterations found"
msgstr ""
msgid "No iterations to show"
msgid "No iterations to show"
msgstr ""
msgstr ""
...
...
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