Commit 9bc13613 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Merge branch '338395-sync-pagination-params-to-url-integration-overrides' into 'master'

Integration overrides: sync page to URL params

See merge request gitlab-org/gitlab!75317
parents 71eb80a6 8089a2fe
...@@ -6,8 +6,12 @@ import { DEFAULT_PER_PAGE } from '~/api'; ...@@ -6,8 +6,12 @@ import { DEFAULT_PER_PAGE } from '~/api';
import { fetchOverrides } from '~/integrations/overrides/api'; import { fetchOverrides } from '~/integrations/overrides/api';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { truncateNamespace } from '~/lib/utils/text_utility'; import { truncateNamespace } from '~/lib/utils/text_utility';
import { getParameterByName } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue';
const DEFAULT_PAGE = 1;
export default { export default {
name: 'IntegrationOverrides', name: 'IntegrationOverrides',
...@@ -18,6 +22,7 @@ export default { ...@@ -18,6 +22,7 @@ export default {
GlTable, GlTable,
GlAlert, GlAlert,
ProjectAvatar, ProjectAvatar,
UrlSync,
}, },
props: { props: {
overridesPath: { overridesPath: {
...@@ -35,7 +40,7 @@ export default { ...@@ -35,7 +40,7 @@ export default {
return { return {
isLoading: true, isLoading: true,
overrides: [], overrides: [],
page: 1, page: DEFAULT_PAGE,
totalItems: 0, totalItems: 0,
errorMessage: null, errorMessage: null,
}; };
...@@ -44,12 +49,21 @@ export default { ...@@ -44,12 +49,21 @@ export default {
showPagination() { showPagination() {
return this.totalItems > this.$options.DEFAULT_PER_PAGE && this.overrides.length > 0; return this.totalItems > this.$options.DEFAULT_PER_PAGE && this.overrides.length > 0;
}, },
query() {
return {
page: this.page,
};
},
}, },
mounted() { created() {
this.loadOverrides(); const initialPage = this.getInitialPage();
this.loadOverrides(initialPage);
}, },
methods: { methods: {
loadOverrides(page = this.page) { getInitialPage() {
return getParameterByName('page') ?? DEFAULT_PAGE;
},
loadOverrides(page) {
this.isLoading = true; this.isLoading = true;
this.errorMessage = null; this.errorMessage = null;
...@@ -119,14 +133,16 @@ export default { ...@@ -119,14 +133,16 @@ export default {
</template> </template>
</gl-table> </gl-table>
<div class="gl-display-flex gl-justify-content-center gl-mt-5"> <div class="gl-display-flex gl-justify-content-center gl-mt-5">
<template v-if="showPagination">
<gl-pagination <gl-pagination
v-if="showPagination"
:per-page="$options.DEFAULT_PER_PAGE" :per-page="$options.DEFAULT_PER_PAGE"
:total-items="totalItems" :total-items="totalItems"
:value="page" :value="page"
:disabled="isLoading" :disabled="isLoading"
@input="loadOverrides" @input="loadOverrides"
/> />
<url-sync :query="query" />
</template>
</div> </div>
</div> </div>
</template> </template>
...@@ -8,6 +8,7 @@ import IntegrationOverrides from '~/integrations/overrides/components/integratio ...@@ -8,6 +8,7 @@ import IntegrationOverrides from '~/integrations/overrides/components/integratio
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue'; import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
import UrlSync from '~/vue_shared/components/url_sync.vue';
const mockOverrides = Array(DEFAULT_PER_PAGE * 3) const mockOverrides = Array(DEFAULT_PER_PAGE * 3)
.fill(1) .fill(1)
...@@ -26,9 +27,10 @@ describe('IntegrationOverrides', () => { ...@@ -26,9 +27,10 @@ describe('IntegrationOverrides', () => {
overridesPath: 'mock/overrides', overridesPath: 'mock/overrides',
}; };
const createComponent = ({ mountFn = shallowMount } = {}) => { const createComponent = ({ mountFn = shallowMount, stubs } = {}) => {
wrapper = mountFn(IntegrationOverrides, { wrapper = mountFn(IntegrationOverrides, {
propsData: defaultProps, propsData: defaultProps,
stubs,
}); });
}; };
...@@ -127,27 +129,58 @@ describe('IntegrationOverrides', () => { ...@@ -127,27 +129,58 @@ describe('IntegrationOverrides', () => {
}); });
describe('pagination', () => { describe('pagination', () => {
it('triggers fetch when `input` event is emitted', async () => { describe('when total items does not exceed the page limit', () => {
it('does not render', async () => {
mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, [mockOverrides[0]], {
'X-TOTAL': DEFAULT_PER_PAGE - 1,
'X-PAGE': 1,
});
createComponent(); createComponent();
jest.spyOn(axios, 'get');
// wait for initial load
await waitForPromises(); await waitForPromises();
await findPagination().vm.$emit('input', 2); expect(findPagination().exists()).toBe(false);
expect(axios.get).toHaveBeenCalledWith(defaultProps.overridesPath, {
params: { page: 2, per_page: DEFAULT_PER_PAGE },
}); });
}); });
it('does not render with <=1 page', async () => { describe('when total items exceeds the page limit', () => {
const mockPage = 2;
beforeEach(async () => {
createComponent({ stubs: { UrlSync } });
mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, [mockOverrides[0]], { mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, [mockOverrides[0]], {
'X-TOTAL': 1, 'X-TOTAL': DEFAULT_PER_PAGE * 2,
'X-PAGE': 1, 'X-PAGE': mockPage,
}); });
createComponent(); // wait for initial load
await waitForPromises(); await waitForPromises();
});
expect(findPagination().exists()).toBe(false); it('renders', () => {
expect(findPagination().exists()).toBe(true);
});
describe('when navigating to a page', () => {
beforeEach(async () => {
jest.spyOn(axios, 'get');
// trigger a page change
await findPagination().vm.$emit('input', mockPage);
});
it('performs GET request with correct params', () => {
expect(axios.get).toHaveBeenCalledWith(defaultProps.overridesPath, {
params: { page: mockPage, per_page: DEFAULT_PER_PAGE },
});
});
it('updates `page` URL parameter', () => {
expect(window.location.search).toBe(`?page=${mockPage}`);
});
});
}); });
}); });
}); });
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