Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

feat(pagination): Add force-ellipses and boundary-link-numbers options #3565

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ lib-cov
*.swp
*.swo
.DS_Store
.idea

pids
logs
Expand Down
19 changes: 14 additions & 5 deletions src/pagination/docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,22 @@ <h4>Default</h4>
<pre>The selected page no: {{currentPage}}</pre>
<button type="button" class="btn btn-info" ng-click="setPage(3)">Set current page to: 3</button>

<hr />
<h4>Limit the maximum visible buttons</h4>
<h6><code>rotate</code> defaulted to <code>true</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" num-pages="numPages"></uib-pagination>
<h6><code>rotate</code> defaulted to <code>true</code> and <code>force-ellipses</code> set to <code>true</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" force-ellipses="true"></uib-pagination>
<h6><code>rotate</code> set to <code>false</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" rotate="false"></uib-pagination>
<h6><code>boundary-link-numbers</code> set to <code>true</code> and <code>rotate</code> defaulted to <code>true</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true"></uib-pagination>
<h6><code>boundary-link-numbers</code> set to <code>true</code> and <code>rotate</code> set to <code>false</code>:</h6>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true" rotate="false"></uib-pagination>
<pre>Page: {{bigCurrentPage}} / {{numPages}}</pre>

<hr />
<h4>Pager</h4>
<uib-pager total-items="totalItems" ng-model="currentPage"></uib-pager>

<hr />
<h4>Limit the maximum visible buttons</h4>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true"></uib-pagination>
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" rotate="false" num-pages="numPages"></uib-pagination>
<pre>Page: {{bigCurrentPage}} / {{numPages}}</pre>
</div>
8 changes: 8 additions & 0 deletions src/pagination/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Settings can be provided as attributes in the `<uib-pagination>` or globally con
* `rotate`
_(Defaults: true)_ :
Whether to keep current page in the middle of the visible ones.

* `force-ellipses`
_(Defaults: false)_ :
Also displays ellipses when `rotate` is true and `max-size` is smaller than the number of pages.

* `direction-links`
_(Default: true)_ :
Expand All @@ -60,6 +64,10 @@ Settings can be provided as attributes in the `<uib-pagination>` or globally con
* `last-text`
_(Default: 'Last')_ :
Text for Last button.

* `boundary-link-numbers`
_(Default: false)_ :
Whether to always display the first and last page numbers. If `max-size` is smaller than the number of pages, then the first and last page numbers are still shown with ellipses in-between as necessary. NOTE: `max-size` refers to the center of the range. This option may add up to 2 more numbers on each side of the displayed range for the end value and what would be an ellipsis but is replaced by a number because it is sequential.

* `template-url`
_(Default: 'template/pagination/pagination.html')_ :
Expand Down
37 changes: 31 additions & 6 deletions src/pagination/pagination.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ angular.module('ui.bootstrap.pagination', [])

$scope.$watch('totalItems', function(newTotal, oldTotal) {
if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
$scope.totalPages = self.calculateTotalPages();
$scope.totalPages = self.calculateTotalPages();
updatePage();
}
});
Expand Down Expand Up @@ -80,12 +80,14 @@ angular.module('ui.bootstrap.pagination', [])
.constant('uibPaginationConfig', {
itemsPerPage: 10,
boundaryLinks: false,
boundaryLinkNumbers: false,
directionLinks: true,
firstText: 'First',
previousText: 'Previous',
nextText: 'Next',
lastText: 'Last',
rotate: true
rotate: true,
forceEllipses: false
})

.directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, paginationConfig) {
Expand Down Expand Up @@ -115,7 +117,9 @@ angular.module('ui.bootstrap.pagination', [])

// Setup configuration parameters
var maxSize = angular.isDefined(attrs.maxSize) ? scope.$parent.$eval(attrs.maxSize) : paginationConfig.maxSize,
rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate;
rotate = angular.isDefined(attrs.rotate) ? scope.$parent.$eval(attrs.rotate) : paginationConfig.rotate,
forceEllipses = angular.isDefined(attrs.forceEllipses) ? scope.$parent.$eval(attrs.forceEllipses) : paginationConfig.forceEllipses,
boundaryLinkNumbers = angular.isDefined(attrs.boundaryLinkNumbers) ? scope.$parent.$eval(attrs.boundaryLinkNumbers) : paginationConfig.boundaryLinkNumbers;
scope.boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$parent.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
scope.directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$parent.$eval(attrs.directionLinks) : paginationConfig.directionLinks;

Expand Down Expand Up @@ -172,18 +176,39 @@ angular.module('ui.bootstrap.pagination', [])
}

// Add links to move between page sets
if (isMaxSized && ! rotate) {
if (startPage > 1) {
if ( isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
if ( startPage > 1 ) {
if(!boundaryLinkNumbers || startPage > 3){ //need ellipsis for all options unless range is too close to beginning
var previousPageSet = makePage(startPage - 1, '...', false);
pages.unshift(previousPageSet);
}
if(boundaryLinkNumbers){
if(startPage === 3){ //need to replace ellipsis when the buttons would be sequential
var secondPageLink = makePage(2, '2', false);
pages.unshift(secondPageLink);
}
//add the first page
var firstPageLink = makePage(1, '1', false);
pages.unshift(firstPageLink);
}
}

if (endPage < totalPages) {
if(!boundaryLinkNumbers || endPage < totalPages - 2){ //need ellipsis for all options unless range is too close to end
var nextPageSet = makePage(endPage + 1, '...', false);
pages.push(nextPageSet);
}
if(boundaryLinkNumbers){
if(endPage === totalPages - 2){ //need to replace ellipsis when the buttons would be sequential
var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
pages.push(secondToLastPageLink);
}
//add the last page
var lastPageLink = makePage(totalPages, totalPages, false);
pages.push(lastPageLink);
}
}
}

return pages;
}

Expand Down
184 changes: 184 additions & 0 deletions src/pagination/test/pagination.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ describe('pagination directive', function() {
return element.find('li').eq(index);
}

// Returns a comma-separated string that represents the pager, like: "Prev, 1, 2, 3, Next"
function getPaginationAsText(){
var len = getPaginationBarSize(), outItems = [];
for(var i = 0; i < len; i++){
outItems.push(getPaginationEl(i).text());
}
return outItems.join(', ');
}

function clickPaginationEl(index) {
getPaginationEl(index).find('a').click();
}
Expand Down Expand Up @@ -353,6 +362,160 @@ describe('pagination directive', function() {
});
});

describe('with `force-ellipses` option', function () {
beforeEach(function () {
$rootScope.total = 98; // 10 pages
$rootScope.currentPage = 3;
$rootScope.maxSize = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" force-ellipses="true"></uib-pagination>')($rootScope);
$rootScope.$digest();
});

it('contains maxsize + 3 li elements', function() {
expect(getPaginationBarSize()).toBe($rootScope.maxSize + 3);
expect(getPaginationEl(0).text()).toBe('Previous');
expect(getPaginationEl(-1).text()).toBe('Next');
expect(getPaginationEl(-2).text()).toBe('...');
});

it('shows the page number in middle after the next link is clicked', function() {
updateCurrentPage(6);
clickPaginationEl(-1);

expect($rootScope.currentPage).toBe(7);
expect(getPaginationEl(4)).toHaveClass('active');
expect(getPaginationEl(4).text()).toBe(''+$rootScope.currentPage);
});

it('shows the page number in middle after the prev link is clicked', function() {
updateCurrentPage(7);
clickPaginationEl(0);

expect($rootScope.currentPage).toBe(6);
expect(getPaginationEl(4)).toHaveClass('active');
expect(getPaginationEl(4).text()).toBe(''+$rootScope.currentPage);
});

it('changes pagination bar size when max-size value changed', function() {
$rootScope.maxSize = 7;
$rootScope.$digest();
expect(getPaginationBarSize()).toBe(10);
});

it('should display an ellipsis on the right if the last displayed page\'s number is less than the last page', function() {
updateCurrentPage(1);
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, ..., Next');
});

it('should display an ellipsis on the left if the first displayed page\'s number is greater than 1', function() {
updateCurrentPage(10);
expect(getPaginationAsText()).toBe('Previous, ..., 6, 7, 8, 9, 10, Next');
});

it('should display both ellipsis\' if the displayed range is in the middle', function() {
updateCurrentPage(5);
expect(getPaginationAsText()).toBe('Previous, ..., 3, 4, 5, 6, 7, ..., Next');
});

it('should not display any ellipses if the number of pages >= maxsize', function() {
$rootScope.maxSize = 10;
$rootScope.$digest();
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Next');
});
});

describe('with `boundary-link-numbers` option', function () {
beforeEach(function () {
$rootScope.total = 98; // 10 pages
$rootScope.currentPage = 3;
$rootScope.maxSize = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" boundary-link-numbers="true"></uib-pagination>')($rootScope);
$rootScope.$digest();
});

it('contains maxsize + 4 li elements', function() {
expect(getPaginationBarSize()).toBe($rootScope.maxSize + 4);
expect(getPaginationEl(0).text()).toBe('Previous');
expect(getPaginationEl(-1).text()).toBe('Next');
expect(getPaginationEl(-2).text()).toBe('10');
expect(getPaginationEl(-3).text()).toBe('...');
});

it('shows the page number in middle after the next link is clicked', function() {
updateCurrentPage(6);
clickPaginationEl(-1);

expect($rootScope.currentPage).toBe(7);
expect(getPaginationEl(5)).toHaveClass('active');
expect(getPaginationEl(5).text()).toBe(''+$rootScope.currentPage);
});

it('shows the page number in middle after the prev link is clicked', function() {
updateCurrentPage(7);
clickPaginationEl(0);

expect($rootScope.currentPage).toBe(6);
expect(getPaginationEl(5)).toHaveClass('active');
expect(getPaginationEl(5).text()).toBe(''+$rootScope.currentPage);
});

it('changes pagination bar size when max-size value changed', function() {
$rootScope.maxSize = 7;
$rootScope.$digest();
expect(getPaginationBarSize()).toBe(11);
});

it('should display an ellipsis on the right if the last displayed page\'s number is less than the last page', function() {
updateCurrentPage(1);
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, ..., 10, Next');
});

it('should display an ellipsis on the left if the first displayed page\'s number is greater than 1', function() {
updateCurrentPage(10);
expect(getPaginationAsText()).toBe('Previous, 1, ..., 6, 7, 8, 9, 10, Next');
});

it('should display both ellipses if the displayed range is in the middle', function() {
$rootScope.maxSize = 3;
$rootScope.$digest();
updateCurrentPage(6);
expect(getPaginationAsText()).toBe('Previous, 1, ..., 5, 6, 7, ..., 10, Next');
});

it('should not display any ellipses if the number of pages >= maxsize', function() {
$rootScope.maxSize = 10;
$rootScope.$digest();
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, Next');
});

it('should not display an ellipsis on the left if the start page is 2', function() {
updateCurrentPage(4);
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, ..., 10, Next');
});

it('should not display an ellipsis on the left if the start page is 3', function() {
updateCurrentPage(5);
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, ..., 10, Next');
});

it('should not display an ellipsis on the right if the end page is totalPages - 1', function() {
updateCurrentPage(7);
expect(getPaginationAsText()).toBe('Previous, 1, ..., 5, 6, 7, 8, 9, 10, Next');
});

it('should not display an ellipsis on the right if the end page is totalPages - 2', function() {
updateCurrentPage(6);
expect(getPaginationAsText()).toBe('Previous, 1, ..., 4, 5, 6, 7, 8, 9, 10, Next');
});

it('should not display any ellipses if the number of pages <= maxsize + 4 and current page is in center', function() {
$rootScope.total = 88; // 9 pages
$rootScope.$digest();
updateCurrentPage(5);
expect(getPaginationAsText()).toBe('Previous, 1, 2, 3, 4, 5, 6, 7, 8, 9, Next');
});
});

describe('with `max-size` option & no `rotate`', function() {
beforeEach(function() {
$rootScope.total = 115; // 12 pages
Expand Down Expand Up @@ -688,6 +851,27 @@ describe('pagination directive', function() {

expect(getPaginationBarSize()).toBe(4);
});

it('should take forceEllipses defaults into account', function () {
paginationConfig.forceEllipses = true;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="2"></uib-pagination>')($rootScope);
$rootScope.$digest();

// Should contain 2 nav buttons, 2 pages, and 2 ellipsis since the currentPage defaults to 3, which is in the middle
expect(getPaginationBarSize()).toBe(6);
});

it('should take boundaryLinkNumbers defaults into account', function () {
paginationConfig.boundaryLinkNumbers = true;
$rootScope.total = 88; // 9 pages
$rootScope.currentPage = 5;
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="3"></uib-pagination>')($rootScope);
$rootScope.$digest();

// Should contain 2 nav buttons, 2 pages, 2 ellipsis, and 2 extra end numbers since the currentPage is in the middle
expect(getPaginationBarSize()).toBe(9);
expect(getPaginationAsText()).toBe('Previous, 1, ..., 4, 5, 6, ..., 9, Next');
});
});

describe('override configuration from attributes', function() {
Expand Down