Commit ea04e393 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'psi-iteration-pagination-sensation' into 'master'

Add pagination to iterations list

See merge request gitlab-org/gitlab!37052
parents c7fc7839 a377c0e8
---
title: Add pagination to iterations list
merge_request: 37052
author:
type: added
<script> <script>
import { GlButton, GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui'; import { GlAlert, GlButton, GlLoadingIcon, GlPagination, GlTab, GlTabs } from '@gitlab/ui';
import { __ } from '~/locale';
import IterationsList from './iterations_list.vue'; import IterationsList from './iterations_list.vue';
import GroupIterationQuery from '../queries/group_iterations.query.graphql'; import GroupIterationQuery from '../queries/group_iterations.query.graphql';
const pageSize = 20;
export default { export default {
components: { components: {
IterationsList, IterationsList,
GlAlert,
GlButton, GlButton,
GlLoadingIcon, GlLoadingIcon,
GlPagination,
GlTab, GlTab,
GlTabs, GlTabs,
}, },
...@@ -28,26 +33,60 @@ export default { ...@@ -28,26 +33,60 @@ export default {
}, },
}, },
apollo: { apollo: {
iterations: { group: {
query: GroupIterationQuery, query: GroupIterationQuery,
update: data => data.group.iterations.nodes,
variables() { variables() {
return this.queryVariables;
},
update: data => {
return { return {
fullPath: this.groupPath, iterations: data.group?.iterations?.nodes || [],
state: this.state, pageInfo: data.group?.iterations?.pageInfo || {},
}; };
}, },
error() {
this.error = __('Error loading iterations');
},
}, },
}, },
data() { data() {
return { return {
iterations: [], group: {
iterations: [],
pageInfo: {
hasNextPage: true,
hasPreviousPage: false,
},
},
pagination: {
currentPage: 1,
},
tabIndex: 0, tabIndex: 0,
error: '',
}; };
}, },
computed: { computed: {
queryVariables() {
const vars = {
fullPath: this.groupPath,
state: this.state,
};
if (this.pagination.beforeCursor) {
vars.beforeCursor = this.pagination.beforeCursor;
vars.lastPageSize = pageSize;
} else {
vars.afterCursor = this.pagination.afterCursor;
vars.firstPageSize = pageSize;
}
return vars;
},
iterations() {
return this.group.iterations;
},
loading() { loading() {
return this.$apollo.queries.iterations.loading; return this.$apollo.queries.group.loading;
}, },
state() { state() {
switch (this.tabIndex) { switch (this.tabIndex) {
...@@ -60,12 +99,38 @@ export default { ...@@ -60,12 +99,38 @@ export default {
return 'all'; return 'all';
} }
}, },
prevPage() {
return Number(this.group.pageInfo.hasPreviousPage);
},
nextPage() {
return Number(this.group.pageInfo.hasNextPage);
},
},
methods: {
handlePageChange(page) {
const { startCursor, endCursor } = this.group.pageInfo;
if (page > this.pagination.currentPage) {
this.pagination = {
afterCursor: endCursor,
currentPage: page,
};
} else {
this.pagination = {
beforeCursor: startCursor,
currentPage: page,
};
}
},
handleTabChange() {
this.pagination = { currentPage: 1 };
},
}, },
}; };
</script> </script>
<template> <template>
<gl-tabs v-model="tabIndex"> <gl-tabs v-model="tabIndex" @activate-tab="handleTabChange">
<gl-tab v-for="tab in [__('Open'), __('Closed'), __('All')]" :key="tab"> <gl-tab v-for="tab in [__('Open'), __('Closed'), __('All')]" :key="tab">
<template #title> <template #title>
{{ tab }} {{ tab }}
...@@ -73,7 +138,23 @@ export default { ...@@ -73,7 +138,23 @@ export default {
<div v-if="loading" class="gl-my-5"> <div v-if="loading" class="gl-my-5">
<gl-loading-icon size="lg" /> <gl-loading-icon size="lg" />
</div> </div>
<iterations-list v-else :iterations="iterations" /> <div v-else-if="error">
<gl-alert variant="danger" @dismiss="error = ''">
{{ error }}
</gl-alert>
</div>
<div v-else>
<iterations-list :iterations="iterations" />
<gl-pagination
v-if="prevPage || nextPage"
:value="pagination.currentPage"
:prev-page="prevPage"
:next-page="nextPage"
align="center"
class="gl-pagination gl-mt-3"
@input="handlePageChange"
/>
</div>
</gl-tab> </gl-tab>
<template v-if="canAdmin" #tabs-end> <template v-if="canAdmin" #tabs-end>
<li class="gl-ml-auto gl-display-flex gl-align-items-center"> <li class="gl-ml-auto gl-display-flex gl-align-items-center">
......
query GroupIterations($fullPath: ID!, $state: IterationState!) { #import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query GroupIterations(
$fullPath: ID!
$state: IterationState!
$beforeCursor: String = ""
$afterCursor: String = ""
$firstPageSize: Int
$lastPageSize: Int
) {
group(fullPath: $fullPath) { group(fullPath: $fullPath) {
iterations(state: $state, first: 20, includeAncestors: false) { iterations(
state: $state
includeAncestors: false
before: $beforeCursor
after: $afterCursor
first: $firstPageSize
last: $lastPageSize
) {
nodes { nodes {
title title
state state
...@@ -9,6 +25,9 @@ query GroupIterations($fullPath: ID!, $state: IterationState!) { ...@@ -9,6 +25,9 @@ query GroupIterations($fullPath: ID!, $state: IterationState!) {
startDate startDate
dueDate dueDate
} }
pageInfo {
...PageInfo
}
} }
} }
} }
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 { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlLoadingIcon, GlTab, GlTabs } from '@gitlab/ui'; import { GlAlert, GlLoadingIcon, GlPagination, GlTab, GlTabs } from '@gitlab/ui';
describe('Iterations tabs', () => { describe('Iterations tabs', () => {
let wrapper; let wrapper;
...@@ -14,7 +14,7 @@ describe('Iterations tabs', () => { ...@@ -14,7 +14,7 @@ describe('Iterations tabs', () => {
propsData: props, propsData: props,
mocks: { mocks: {
$apollo: { $apollo: {
queries: { iterations: { loading } }, queries: { group: { loading } },
}, },
}, },
stubs: { stubs: {
...@@ -61,4 +61,93 @@ describe('Iterations tabs', () => { ...@@ -61,4 +61,93 @@ describe('Iterations tabs', () => {
expect(wrapper.vm.state).toEqual('all'); expect(wrapper.vm.state).toEqual('all');
}); });
describe('pagination', () => {
const findPagination = () => wrapper.find(GlPagination);
const setPage = page => {
findPagination().vm.$emit('input', page);
return findPagination().vm.$nextTick();
};
beforeEach(() => {
mountComponent({
loading: false,
});
wrapper.setData({
group: {
pageInfo: {
hasNextPage: true,
hasPreviousPage: false,
startCursor: 'first-item',
endCursor: 'last-item',
},
},
});
});
it('passes prev, next, and current page props', () => {
expect(findPagination().exists()).toBe(true);
expect(findPagination().props()).toEqual(
expect.objectContaining({
value: wrapper.vm.pagination.currentPage,
prevPage: wrapper.vm.prevPage,
nextPage: wrapper.vm.nextPage,
}),
);
});
it('updates query variables when going to previous page', async () => {
await setPage(1);
expect(wrapper.vm.queryVariables).toEqual({
beforeCursor: 'first-item',
lastPageSize: 20,
fullPath: defaultProps.groupPath,
state: 'opened',
});
});
it('updates query variables when going to next page', async () => {
await setPage(2);
expect(wrapper.vm.queryVariables).toEqual({
afterCursor: 'last-item',
firstPageSize: 20,
fullPath: defaultProps.groupPath,
state: 'opened',
});
});
it('resets pagination when changing tabs', async () => {
await setPage(2);
expect(wrapper.vm.pagination).toEqual({
currentPage: 2,
afterCursor: 'last-item',
});
wrapper.find(GlTabs).vm.$emit('activate-tab', 2);
await wrapper.vm.$nextTick();
expect(wrapper.vm.pagination).toEqual({
currentPage: 1,
});
});
});
describe('error', () => {
beforeEach(() => {
mountComponent({
loading: false,
});
wrapper.setData({
error: 'Oh no!',
});
});
it('tab shows error in alert', () => {
expect(wrapper.find(GlAlert).text()).toContain('Oh no!');
});
});
}); });
...@@ -9400,6 +9400,9 @@ msgstr "" ...@@ -9400,6 +9400,9 @@ msgstr ""
msgid "Error loading issues" msgid "Error loading issues"
msgstr "" msgstr ""
msgid "Error loading iterations"
msgstr ""
msgid "Error loading last commit." msgid "Error loading last commit."
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment