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>
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 GroupIterationQuery from '../queries/group_iterations.query.graphql';
const pageSize = 20;
export default {
components: {
IterationsList,
GlAlert,
GlButton,
GlLoadingIcon,
GlPagination,
GlTab,
GlTabs,
},
......@@ -28,26 +33,60 @@ export default {
},
},
apollo: {
iterations: {
group: {
query: GroupIterationQuery,
update: data => data.group.iterations.nodes,
variables() {
return this.queryVariables;
},
update: data => {
return {
fullPath: this.groupPath,
state: this.state,
iterations: data.group?.iterations?.nodes || [],
pageInfo: data.group?.iterations?.pageInfo || {},
};
},
error() {
this.error = __('Error loading iterations');
},
},
},
data() {
return {
group: {
iterations: [],
pageInfo: {
hasNextPage: true,
hasPreviousPage: false,
},
},
pagination: {
currentPage: 1,
},
tabIndex: 0,
error: '',
};
},
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() {
return this.$apollo.queries.iterations.loading;
return this.$apollo.queries.group.loading;
},
state() {
switch (this.tabIndex) {
......@@ -60,12 +99,38 @@ export default {
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>
<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">
<template #title>
{{ tab }}
......@@ -73,7 +138,23 @@ export default {
<div v-if="loading" class="gl-my-5">
<gl-loading-icon size="lg" />
</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>
<template v-if="canAdmin" #tabs-end>
<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) {
iterations(state: $state, first: 20, includeAncestors: false) {
iterations(
state: $state
includeAncestors: false
before: $beforeCursor
after: $afterCursor
first: $firstPageSize
last: $lastPageSize
) {
nodes {
title
state
......@@ -9,6 +25,9 @@ query GroupIterations($fullPath: ID!, $state: IterationState!) {
startDate
dueDate
}
pageInfo {
...PageInfo
}
}
}
}
import Iterations from 'ee/iterations/components/iterations.vue';
import IterationsList from 'ee/iterations/components/iterations_list.vue';
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', () => {
let wrapper;
......@@ -14,7 +14,7 @@ describe('Iterations tabs', () => {
propsData: props,
mocks: {
$apollo: {
queries: { iterations: { loading } },
queries: { group: { loading } },
},
},
stubs: {
......@@ -61,4 +61,93 @@ describe('Iterations tabs', () => {
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 ""
msgid "Error loading issues"
msgstr ""
msgid "Error loading iterations"
msgstr ""
msgid "Error loading last commit."
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