Merge branch '34810-vue-pagination' into 'master'
Prevent default click since we change the url manually Closes #34810 See merge request !12721
This commit is contained in:
commit
8906bbae38
4 changed files with 189 additions and 138 deletions
|
@ -46,6 +46,8 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changePage(e) {
|
changePage(e) {
|
||||||
|
if (e.target.parentElement.classList.contains('disabled')) return;
|
||||||
|
|
||||||
const text = e.target.innerText;
|
const text = e.target.innerText;
|
||||||
const { totalPages, nextPage, previousPage } = this.pageInfo;
|
const { totalPages, nextPage, previousPage } = this.pageInfo;
|
||||||
|
|
||||||
|
@ -82,7 +84,9 @@ export default {
|
||||||
const page = this.pageInfo.page;
|
const page = this.pageInfo.page;
|
||||||
const items = [];
|
const items = [];
|
||||||
|
|
||||||
if (page > 1) items.push({ title: FIRST });
|
if (page > 1) {
|
||||||
|
items.push({ title: FIRST, first: true });
|
||||||
|
}
|
||||||
|
|
||||||
if (page > 1) {
|
if (page > 1) {
|
||||||
items.push({ title: PREV, prev: true });
|
items.push({ title: PREV, prev: true });
|
||||||
|
@ -110,7 +114,9 @@ export default {
|
||||||
items.push({ title: NEXT, next: true });
|
items.push({ title: NEXT, next: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (total - page >= 1) items.push({ title: LAST, last: true });
|
if (total - page >= 1) {
|
||||||
|
items.push({ title: LAST, last: true });
|
||||||
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
},
|
},
|
||||||
|
@ -124,13 +130,15 @@ export default {
|
||||||
v-for="item in getItems"
|
v-for="item in getItems"
|
||||||
:class="{
|
:class="{
|
||||||
page: item.page,
|
page: item.page,
|
||||||
prev: item.prev,
|
'js-previous-button': item.prev,
|
||||||
next: item.next,
|
'js-next-button': item.next,
|
||||||
|
'js-last-button': item.last,
|
||||||
|
'js-first-button': item.first,
|
||||||
separator: item.separator,
|
separator: item.separator,
|
||||||
active: item.active,
|
active: item.active,
|
||||||
disabled: item.disabled
|
disabled: item.disabled
|
||||||
}">
|
}">
|
||||||
<a @click="changePage($event)">{{item.title}}</a>
|
<a @click.prevent="changePage($event)">{{item.title}}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
4
changelogs/unreleased/34810-vue-pagination.yml
Normal file
4
changelogs/unreleased/34810-vue-pagination.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
title: Prevent disabled pagination button to be clicked
|
||||||
|
merge_request:
|
||||||
|
author:
|
|
@ -143,6 +143,7 @@ import '~/lib/utils/common_utils';
|
||||||
|
|
||||||
it('should return valid parameter', () => {
|
it('should return valid parameter', () => {
|
||||||
const value = gl.utils.getParameterByName('scope');
|
const value = gl.utils.getParameterByName('scope');
|
||||||
|
expect(gl.utils.getParameterByName('p')).toEqual('2');
|
||||||
expect(value).toBe('all');
|
expect(value).toBe('all');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,150 +1,188 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import paginationComp from '~/vue_shared/components/table_pagination.vue';
|
import paginationComp from '~/vue_shared/components/table_pagination.vue';
|
||||||
import '~/lib/utils/common_utils';
|
|
||||||
|
|
||||||
describe('Pagination component', () => {
|
describe('Pagination component', () => {
|
||||||
let component;
|
let component;
|
||||||
let PaginationComponent;
|
let PaginationComponent;
|
||||||
|
let spy;
|
||||||
const changeChanges = {
|
let mountComponet;
|
||||||
one: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
const change = (one) => {
|
|
||||||
changeChanges.one = one;
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
spy = jasmine.createSpy('spy');
|
||||||
PaginationComponent = Vue.extend(paginationComp);
|
PaginationComponent = Vue.extend(paginationComp);
|
||||||
});
|
|
||||||
|
|
||||||
it('should render and start at page 1', () => {
|
mountComponet = function (props) {
|
||||||
component = new PaginationComponent({
|
return new PaginationComponent({
|
||||||
propsData: {
|
propsData: props,
|
||||||
pageInfo: {
|
|
||||||
totalPages: 10,
|
|
||||||
nextPage: 2,
|
|
||||||
previousPage: '',
|
|
||||||
},
|
|
||||||
change,
|
|
||||||
},
|
|
||||||
}).$mount();
|
}).$mount();
|
||||||
|
};
|
||||||
expect(component.$el.classList).toContain('gl-pagination');
|
|
||||||
|
|
||||||
component.changePage({ target: { innerText: '1' } });
|
|
||||||
|
|
||||||
expect(changeChanges.one).toEqual(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should go to the previous page', () => {
|
describe('render', () => {
|
||||||
component = new PaginationComponent({
|
describe('prev button', () => {
|
||||||
propsData: {
|
it('should be disabled and non clickable', () => {
|
||||||
|
component = mountComponet({
|
||||||
|
pageInfo: {
|
||||||
|
nextPage: 2,
|
||||||
|
page: 1,
|
||||||
|
perPage: 20,
|
||||||
|
previousPage: NaN,
|
||||||
|
total: 84,
|
||||||
|
totalPages: 5,
|
||||||
|
},
|
||||||
|
change: spy,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
component.$el.querySelector('.js-previous-button').classList.contains('disabled'),
|
||||||
|
).toEqual(true);
|
||||||
|
|
||||||
|
component.$el.querySelector('.js-previous-button a').click();
|
||||||
|
|
||||||
|
expect(spy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be enabled and clickable', () => {
|
||||||
|
component = mountComponet({
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
totalPages: 10,
|
|
||||||
nextPage: 3,
|
nextPage: 3,
|
||||||
|
page: 2,
|
||||||
|
perPage: 20,
|
||||||
previousPage: 1,
|
previousPage: 1,
|
||||||
|
total: 84,
|
||||||
|
totalPages: 5,
|
||||||
},
|
},
|
||||||
change,
|
change: spy,
|
||||||
},
|
|
||||||
}).$mount();
|
|
||||||
|
|
||||||
component.changePage({ target: { innerText: 'Prev' } });
|
|
||||||
|
|
||||||
expect(changeChanges.one).toEqual(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should go to the next page', () => {
|
component.$el.querySelector('.js-previous-button a').click();
|
||||||
component = new PaginationComponent({
|
|
||||||
propsData: {
|
expect(spy).toHaveBeenCalledWith(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('first button', () => {
|
||||||
|
it('should call the change callback with the first page', () => {
|
||||||
|
component = mountComponet({
|
||||||
|
pageInfo: {
|
||||||
|
nextPage: 3,
|
||||||
|
page: 2,
|
||||||
|
perPage: 20,
|
||||||
|
previousPage: 1,
|
||||||
|
total: 84,
|
||||||
|
totalPages: 5,
|
||||||
|
},
|
||||||
|
change: spy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const button = component.$el.querySelector('.js-first-button a');
|
||||||
|
|
||||||
|
expect(button.textContent.trim()).toEqual('« First');
|
||||||
|
|
||||||
|
button.click();
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('last button', () => {
|
||||||
|
it('should call the change callback with the last page', () => {
|
||||||
|
component = mountComponet({
|
||||||
|
pageInfo: {
|
||||||
|
nextPage: 3,
|
||||||
|
page: 2,
|
||||||
|
perPage: 20,
|
||||||
|
previousPage: 1,
|
||||||
|
total: 84,
|
||||||
|
totalPages: 5,
|
||||||
|
},
|
||||||
|
change: spy,
|
||||||
|
});
|
||||||
|
|
||||||
|
const button = component.$el.querySelector('.js-last-button a');
|
||||||
|
|
||||||
|
expect(button.textContent.trim()).toEqual('Last »');
|
||||||
|
|
||||||
|
button.click();
|
||||||
|
|
||||||
|
expect(spy).toHaveBeenCalledWith(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('next button', () => {
|
||||||
|
it('should be disabled and non clickable', () => {
|
||||||
|
component = mountComponet({
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
totalPages: 10,
|
|
||||||
nextPage: 5,
|
nextPage: 5,
|
||||||
previousPage: 3,
|
page: 5,
|
||||||
|
perPage: 20,
|
||||||
|
previousPage: 1,
|
||||||
|
total: 84,
|
||||||
|
totalPages: 5,
|
||||||
},
|
},
|
||||||
change,
|
change: spy,
|
||||||
},
|
|
||||||
}).$mount();
|
|
||||||
|
|
||||||
component.changePage({ target: { innerText: 'Next' } });
|
|
||||||
|
|
||||||
expect(changeChanges.one).toEqual(5);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should go to the last page', () => {
|
expect(
|
||||||
component = new PaginationComponent({
|
component.$el.querySelector('.js-next-button').textContent.trim(),
|
||||||
propsData: {
|
).toEqual('Next');
|
||||||
pageInfo: {
|
|
||||||
totalPages: 10,
|
|
||||||
nextPage: 5,
|
|
||||||
previousPage: 3,
|
|
||||||
},
|
|
||||||
change,
|
|
||||||
},
|
|
||||||
}).$mount();
|
|
||||||
|
|
||||||
component.changePage({ target: { innerText: 'Last »' } });
|
component.$el.querySelector('.js-next-button a').click();
|
||||||
|
|
||||||
expect(changeChanges.one).toEqual(10);
|
expect(spy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should go to the first page', () => {
|
it('should be enabled and clickable', () => {
|
||||||
component = new PaginationComponent({
|
component = mountComponet({
|
||||||
propsData: {
|
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
totalPages: 10,
|
nextPage: 4,
|
||||||
nextPage: 5,
|
page: 3,
|
||||||
previousPage: 3,
|
perPage: 20,
|
||||||
|
previousPage: 2,
|
||||||
|
total: 84,
|
||||||
|
totalPages: 5,
|
||||||
},
|
},
|
||||||
change,
|
change: spy,
|
||||||
},
|
|
||||||
}).$mount();
|
|
||||||
|
|
||||||
component.changePage({ target: { innerText: '« First' } });
|
|
||||||
|
|
||||||
expect(changeChanges.one).toEqual(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should do nothing', () => {
|
component.$el.querySelector('.js-next-button a').click();
|
||||||
component = new PaginationComponent({
|
|
||||||
propsData: {
|
expect(spy).toHaveBeenCalledWith(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('numbered buttons', () => {
|
||||||
|
it('should render 5 pages', () => {
|
||||||
|
component = mountComponet({
|
||||||
pageInfo: {
|
pageInfo: {
|
||||||
|
nextPage: 4,
|
||||||
|
page: 3,
|
||||||
|
perPage: 20,
|
||||||
|
previousPage: 2,
|
||||||
|
total: 84,
|
||||||
|
totalPages: 5,
|
||||||
|
},
|
||||||
|
change: spy,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(component.$el.querySelectorAll('.page').length).toEqual(5);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render the spread operator', () => {
|
||||||
|
component = mountComponet({
|
||||||
|
pageInfo: {
|
||||||
|
nextPage: 4,
|
||||||
|
page: 3,
|
||||||
|
perPage: 20,
|
||||||
|
previousPage: 2,
|
||||||
|
total: 84,
|
||||||
totalPages: 10,
|
totalPages: 10,
|
||||||
nextPage: 2,
|
|
||||||
previousPage: '',
|
|
||||||
},
|
},
|
||||||
change,
|
change: spy,
|
||||||
},
|
});
|
||||||
}).$mount();
|
|
||||||
|
|
||||||
component.changePage({ target: { innerText: '...' } });
|
expect(component.$el.querySelector('.separator').textContent.trim()).toEqual('...');
|
||||||
|
});
|
||||||
expect(changeChanges.one).toEqual(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('paramHelper', () => {
|
|
||||||
afterEach(() => {
|
|
||||||
window.history.pushState({}, null, '');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can parse url parameters correctly', () => {
|
|
||||||
window.history.pushState({}, null, '?scope=all&p=2');
|
|
||||||
|
|
||||||
const scope = gl.utils.getParameterByName('scope');
|
|
||||||
const p = gl.utils.getParameterByName('p');
|
|
||||||
|
|
||||||
expect(scope).toEqual('all');
|
|
||||||
expect(p).toEqual('2');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns null if param not in url', () => {
|
|
||||||
window.history.pushState({}, null, '?p=2');
|
|
||||||
|
|
||||||
const scope = gl.utils.getParameterByName('scope');
|
|
||||||
const p = gl.utils.getParameterByName('p');
|
|
||||||
|
|
||||||
expect(scope).toEqual(null);
|
|
||||||
expect(p).toEqual('2');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue