Skip to content

Commit b0a1575

Browse files
authored
Merge pull request #514 from dtaylor113/pagination
pfPagination: pagination controls
2 parents 3a2b6f2 + cd305bc commit b0a1575

File tree

8 files changed

+352
-0
lines changed

8 files changed

+352
-0
lines changed

Gruntfile.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,11 @@ module.exports = function (grunt) {
283283
cwd: 'src/',
284284
src: ['canvas-view/**/*.html'],
285285
dest: 'templates/canvas.js'
286+
},
287+
'patternfly.pagination': {
288+
cwd: 'src/',
289+
src: ['pagination/**/*.html'],
290+
dest: 'templates/pagination.js'
286291
}
287292
},
288293
// ng-annotate tries to make the code safe for minification automatically
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* @ngdoc directive
3+
* @name patternfly.pagination.component:pfPagination
4+
* @restrict E
5+
*
6+
* @param {number} pageNumber The current page number
7+
* @param {number} numTotalItems The total number of items in the data set. When a filter is applied, update the <code>numTotalItems</code>
8+
* accordingly.
9+
* @param {Array<Number>} pageSizeIncrements (optional) Page size increments for the 'per page' dropdown. If not
10+
* specified, the default values are: [5, 10, 20, 40, 80, 100]
11+
* @param {number} pageSize (optional) The initial page size to use. If not specified, the default will be
12+
* the first increment in the <code>pageSizeIncrements</code> array.
13+
* @description
14+
* Component for pagination controls used in various views (list, card, table)
15+
*
16+
* @example
17+
<example module="patternfly.pagination">
18+
19+
<file name="index.html">
20+
<div ng-controller="PageCtrl">
21+
<div class="col-md-12">
22+
<div ng-repeat="item in items | startFrom:(pageNumber - 1)*pageSize | limitTo:pageSize" class="col-md-12">
23+
<div class="row">
24+
<div class="col-md-3">
25+
<span>{{item.id}}</span>
26+
</div>
27+
<div class="col-md-7">
28+
<span>{{item.status}}</span>
29+
</div>
30+
<div class="col-md-2">
31+
<span>{{item.value}}</span>
32+
</div>
33+
</div>
34+
</div>
35+
</div>
36+
<pf-pagination
37+
page-size="pageSize",
38+
page-number="pageNumber"
39+
num-total-items="numTotalItems">
40+
</pf-pagination>
41+
</div>
42+
</file>
43+
<file name="script.js">
44+
angular.module( 'patternfly.pagination').controller( 'PageCtrl', function( $scope ) {
45+
$scope.pageSize = 10;
46+
$scope.pageNumber = 1;
47+
48+
$scope.items = [];
49+
for(i = 1; i <= 126; i++) {
50+
$scope.items.push({id: i, status: 'Ok', value: Math.floor(Math.random() * (1000 - 1 + 1)) + 1});
51+
}
52+
53+
$scope.numTotalItems = $scope.items.length;
54+
});
55+
</file>
56+
</example>
57+
*/
58+
angular.module('patternfly.pagination').component('pfPagination', {
59+
transclude: true,
60+
templateUrl: 'pagination/pagination.html',
61+
bindings: {
62+
pageNumber: '=',
63+
numTotalItems: "<",
64+
pageSizeIncrements: '<?',
65+
pageSize: "=?"
66+
},
67+
controller: function ($scope, $log) {
68+
'use strict';
69+
var ctrl = this;
70+
71+
var defaultPageSizeIncrements = [5, 10, 20, 40, 80, 100];
72+
73+
ctrl.$onInit = function () {
74+
if (angular.isUndefined(ctrl.pageSizeIncrements)) {
75+
ctrl.pageSizeIncrements = defaultPageSizeIncrements;
76+
}
77+
if (angular.isUndefined(ctrl.pageSize)) {
78+
ctrl.pageSize = ctrl.pageSizeIncrements[0];
79+
}
80+
ctrl.lastPageNumber = getLastPageNumber();
81+
};
82+
83+
ctrl.$onChanges = function (changesObj) {
84+
if (changesObj.numTotalItems && !changesObj.numTotalItems.isFirstChange()) {
85+
ctrl.lastPageNumber = getLastPageNumber();
86+
}
87+
};
88+
89+
ctrl.onPageSizeChange = function (newPageSize) {
90+
ctrl.pageSize = newPageSize;
91+
ctrl.lastPageNumber = getLastPageNumber();
92+
ctrl.gotoFirstPage();
93+
};
94+
95+
ctrl.onPageNumberChange = function () {
96+
ctrl.pageNumber = parseInt(ctrl.pageNumber, 10);
97+
if (ctrl.pageNumber > ctrl.lastPageNumber) {
98+
ctrl.pageNumber = ctrl.lastPageNumber;
99+
} else if (ctrl.pageNumber < 1 || isNaN(ctrl.pageNumber)) {
100+
ctrl.pageNumber = 1;
101+
}
102+
};
103+
104+
ctrl.gotoFirstPage = function () {
105+
if (ctrl.pageNumber !== 1) {
106+
ctrl.pageNumber = 1;
107+
}
108+
};
109+
110+
ctrl.gotoPreviousPage = function () {
111+
if (ctrl.pageNumber !== 1) {
112+
ctrl.pageNumber--;
113+
}
114+
};
115+
116+
ctrl.gotoNextPage = function () {
117+
if (ctrl.pageNumber < ctrl.lastPageNumber) {
118+
ctrl.pageNumber++;
119+
}
120+
};
121+
122+
ctrl.gotoLastPage = function () {
123+
if (ctrl.pageNumber < ctrl.lastPageNumber) {
124+
ctrl.pageNumber = ctrl.lastPageNumber;
125+
}
126+
};
127+
128+
ctrl.getStartIndex = function () {
129+
return ctrl.pageSize * (ctrl.pageNumber - 1) + 1;
130+
};
131+
132+
ctrl.getEndIndex = function () {
133+
var numFullPages = Math.floor(ctrl.numTotalItems / ctrl.pageSize);
134+
var numItemsOnLastPage = ctrl.numTotalItems - (numFullPages * ctrl.pageSize);
135+
var numItemsOnPage = isLastPage() ? numItemsOnLastPage : ctrl.pageSize;
136+
return ctrl.getStartIndex() + numItemsOnPage - 1;
137+
};
138+
139+
function getLastPageNumber () {
140+
return Math.ceil(ctrl.numTotalItems / ctrl.pageSize);
141+
}
142+
143+
function isLastPage () {
144+
return ctrl.pageNumber === ctrl.lastPageNumber;
145+
}
146+
}
147+
});

src/pagination/pagination.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<form class="content-view-pf-pagination list-view-pf-pagination clearfix" id="form1">
2+
<div class="form-group">
3+
<div uib-dropdown class="btn-group">
4+
<button uib-dropdown-toggle type="button" class="btn btn-default">
5+
{{$ctrl.pageSize}}
6+
<span class="caret"></span>
7+
</button>
8+
<ul uib-dropdown-menu class="dropdown-menu">
9+
<li ng-repeat="increment in $ctrl.pageSizeIncrements track by $index"
10+
ng-class="{'selected': increment === $ctrl.pageSize}"
11+
class="display-length-increment">
12+
<a role="menuitem" ng-click="$ctrl.onPageSizeChange(increment)">{{increment}}</a></li>
13+
</ul>
14+
</div>
15+
<span class="per-page-label">per page</span>
16+
</div>
17+
<div class="form-group">
18+
<span><span class="pagination-pf-items-current">{{$ctrl.getStartIndex()}}-{{$ctrl.getEndIndex()}}</span> of <span class="pagination-pf-items-total">{{$ctrl.numTotalItems}}</span></span>
19+
<ul class="pagination pagination-pf-back">
20+
<li ng-class="{'disabled': $ctrl.pageNumber === 1}"><a title="First Page" ng-click="$ctrl.gotoFirstPage()" class="goto-first-page"><span class="i fa fa-angle-double-left"></span></a></li>
21+
<li ng-class="{'disabled': $ctrl.pageNumber === 1}"><a title="Previous Page" ng-click="$ctrl.gotoPreviousPage()" class="goto-prev-page"><span class="i fa fa-angle-left"></span></a></li>
22+
</ul>
23+
<input class="pagination-pf-page" type="text" ng-model="$ctrl.pageNumber" ng-model-options="{ updateOn: 'blur' }" ng-change="$ctrl.onPageNumberChange()"/>
24+
<span>of <span class="pagination-pf-pages">{{$ctrl.lastPageNumber}}</span></span>
25+
<ul class="pagination pagination-pf-forward">
26+
<li ng-class="{'disabled': $ctrl.pageNumber === $ctrl.lastPageNumber}"><a title="Next Page" ng-click="$ctrl.gotoNextPage()" class="goto-next-page"><span class="i fa fa-angle-right"></span></a></li>
27+
<li ng-class="{'disabled': $ctrl.pageNumber === $ctrl.lastPageNumber}"><a title="Last Page" ng-click="$ctrl.gotoLastPage()" class="goto-last-page"><span class="i fa fa-angle-double-right"></span></a></li>
28+
</ul>
29+
</div>
30+
</form>

src/pagination/pagination.less

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.content-view-pf-pagination .form-group {
2+
align-items: center;
3+
.per-page-label {
4+
padding-left: 10px;
5+
}
6+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @name patternfly pagination
3+
*
4+
* @description
5+
* Pagination module for patternfly.
6+
*
7+
*/
8+
angular.module('patternfly.pagination', ['ui.bootstrap'])
9+
.filter('startFrom', function () {
10+
'use strict';
11+
return function (input, start) {
12+
start = parseInt(start, 10);
13+
return input.slice(start);
14+
};
15+
});

src/patternfly.module.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ angular.module('patternfly', [
1212
'patternfly.modals',
1313
'patternfly.navigation',
1414
'patternfly.notification',
15+
'patternfly.pagination',
1516
'patternfly.select',
1617
'patternfly.sort',
1718
'patternfly.toolbars',

styles/angular-patternfly.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
@import "../src/wizard/wizard.less";
1313
@import "../src/canvas-view/canvas/canvas.less";
1414
@import "../src/canvas-view/canvas-editor/canvas-editor.less";
15+
@import "../src/pagination/pagination.less";
1516

test/pagination/pagination.spec.js

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
describe('Component: pfPagination', function () {
2+
var $scope;
3+
var $compile;
4+
var element;
5+
var performedAction;
6+
var updateCount;
7+
8+
// load the controller's module
9+
beforeEach(function () {
10+
module('patternfly.pagination', 'pagination/pagination.html', 'patternfly.table', 'table/tableview/table-view.html', 'views/empty-state.html');
11+
});
12+
13+
beforeEach(inject(function (_$compile_, _$rootScope_, _$timeout_) {
14+
$compile = _$compile_;
15+
$scope = _$rootScope_;
16+
$timeout = _$timeout_;
17+
}));
18+
19+
var compileHTML = function (markup, scope) {
20+
element = angular.element(markup);
21+
$compile(element)(scope);
22+
23+
scope.$digest();
24+
};
25+
26+
it('should show the default page size values', function () {
27+
var htmlTmp = '<pf-pagination></pf-pagination>';
28+
compileHTML(htmlTmp, $scope);
29+
30+
var pageSizeIncrements = element.find('.display-length-increment');
31+
expect(pageSizeIncrements.length).toBe(6);
32+
expect(angular.element(pageSizeIncrements[0]).text().trim()).toBe('5');
33+
expect(angular.element(pageSizeIncrements[pageSizeIncrements.length-1]).text().trim()).toBe('100');
34+
35+
expect(angular.element(element.find('.selected')).text().trim()).toBe('5');
36+
});
37+
38+
it('should show the correct page size values when specified', function () {
39+
$scope.pageSizeIncrements = [50, 75];
40+
$scope.pageSize = 75;
41+
var htmlTmp = '<pf-pagination page-size-increments="pageSizeIncrements" page-size="pageSize"></pf-pagination>';
42+
compileHTML(htmlTmp, $scope);
43+
44+
var pageSizeIncrements = element.find('.display-length-increment');
45+
expect(pageSizeIncrements.length).toBe(2);
46+
expect(angular.element(pageSizeIncrements[0]).text().trim()).toBe('50');
47+
expect(angular.element(pageSizeIncrements[pageSizeIncrements.length-1]).text().trim()).toBe('75');
48+
49+
expect(angular.element(element.find('.selected')).text().trim()).toBe('75');
50+
});
51+
52+
it('should show the correct page item ranges, page number, and last page number', function () {
53+
$scope.pageSize = 10;
54+
$scope.pageNumber = 1;
55+
$scope.numTotalItems = 126;
56+
var htmlTmp = '<pf-pagination page-size="pageSize" page-number="pageNumber" num-total-items="numTotalItems"></pf-pagination>';
57+
compileHTML(htmlTmp, $scope);
58+
59+
expect(angular.element(element.find('.pagination-pf-items-current')).text().trim()).toBe('1-10');
60+
expect(angular.element(element.find('.pagination-pf-items-total')).text().trim()).toBe('126');
61+
expect(angular.element(element.find('.pagination-pf-page')).val().trim()).toBe('1');
62+
expect(angular.element(element.find('.pagination-pf-pages')).text().trim()).toBe('13');
63+
});
64+
65+
it('should goto the next and previous pages', function () {
66+
$scope.pageSize = 10;
67+
$scope.pageNumber = 1;
68+
$scope.numTotalItems = 126;
69+
var htmlTmp = '<pf-pagination page-size="pageSize" page-number="pageNumber" num-total-items="numTotalItems"></pf-pagination>';
70+
compileHTML(htmlTmp, $scope);
71+
72+
// On first page, goto prev and first page buttons should be disabled
73+
expect(element.find('.disabled').length).toBe(2);
74+
75+
eventFire(element.find('.goto-next-page')[0], 'click');
76+
$scope.$digest();
77+
78+
expect(angular.element(element.find('.pagination-pf-items-current')).text().trim()).toBe('11-20');
79+
expect(angular.element(element.find('.pagination-pf-items-total')).text().trim()).toBe('126');
80+
expect(angular.element(element.find('.pagination-pf-page')).val().trim()).toBe('2');
81+
expect(angular.element(element.find('.pagination-pf-pages')).text().trim()).toBe('13');
82+
// On second page, no buttons should be disabled
83+
expect(element.find('.disabled').length).toBe(0);
84+
85+
eventFire(element.find('.goto-prev-page')[0], 'click');
86+
$scope.$digest();
87+
88+
expect(angular.element(element.find('.pagination-pf-items-current')).text().trim()).toBe('1-10');
89+
expect(angular.element(element.find('.pagination-pf-items-total')).text().trim()).toBe('126');
90+
expect(angular.element(element.find('.pagination-pf-page')).val().trim()).toBe('1');
91+
expect(angular.element(element.find('.pagination-pf-pages')).text().trim()).toBe('13');
92+
93+
// On first page, goto prev and first page buttons should be disabled
94+
expect(element.find('.disabled').length).toBe(2);
95+
});
96+
97+
it('should goto the last and first pages', function () {
98+
$scope.pageSize = 10;
99+
$scope.pageNumber = 1;
100+
$scope.numTotalItems = 126;
101+
var htmlTmp = '<pf-pagination page-size="pageSize" page-number="pageNumber" num-total-items="numTotalItems"></pf-pagination>';
102+
compileHTML(htmlTmp, $scope);
103+
104+
// On first page, goto prev and first page buttons should be disabled
105+
expect(element.find('.disabled').length).toBe(2);
106+
107+
eventFire(element.find('.goto-last-page')[0], 'click');
108+
$scope.$digest();
109+
110+
expect(angular.element(element.find('.pagination-pf-items-current')).text().trim()).toBe('121-126');
111+
expect(angular.element(element.find('.pagination-pf-items-total')).text().trim()).toBe('126');
112+
expect(angular.element(element.find('.pagination-pf-page')).val().trim()).toBe('13');
113+
expect(angular.element(element.find('.pagination-pf-pages')).text().trim()).toBe('13');
114+
// On last page, next and buttons should be disabled
115+
expect(element.find('.disabled').length).toBe(2);
116+
117+
eventFire(element.find('.goto-first-page')[0], 'click');
118+
$scope.$digest();
119+
120+
expect(angular.element(element.find('.pagination-pf-items-current')).text().trim()).toBe('1-10');
121+
expect(angular.element(element.find('.pagination-pf-items-total')).text().trim()).toBe('126');
122+
expect(angular.element(element.find('.pagination-pf-page')).val().trim()).toBe('1');
123+
expect(angular.element(element.find('.pagination-pf-pages')).text().trim()).toBe('13');
124+
125+
// On first page, goto prev and first page buttons should be disabled
126+
expect(element.find('.disabled').length).toBe(2);
127+
});
128+
129+
it('should goto a specific page when inputted', function () {
130+
$scope.pageSize = 10;
131+
$scope.pageNumber = 1;
132+
$scope.numTotalItems = 126;
133+
var htmlTmp = '<pf-pagination page-size="pageSize" page-number="pageNumber" num-total-items="numTotalItems"></pf-pagination>';
134+
compileHTML(htmlTmp, $scope);
135+
136+
// On first page, goto prev and first page buttons should be disabled
137+
expect(element.find('.disabled').length).toBe(2);
138+
139+
angular.element(element.find('.pagination-pf-page ')).val('7').trigger('input').blur();
140+
$scope.$digest();
141+
142+
expect(angular.element(element.find('.pagination-pf-items-current')).text().trim()).toBe('61-70');
143+
expect(angular.element(element.find('.pagination-pf-items-total')).text().trim()).toBe('126');
144+
expect(angular.element(element.find('.pagination-pf-page')).val().trim()).toBe('7');
145+
expect(angular.element(element.find('.pagination-pf-pages')).text().trim()).toBe('13');
146+
});
147+
});

0 commit comments

Comments
 (0)