Skip to content

feat(paginator): Add functionality to jump to first and last page #9603

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 31, 2018
Merged
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
18 changes: 18 additions & 0 deletions src/demo-app/table/table-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,24 @@ <h3>MatTable Example</h3>
</mat-paginator>
</div>

<h3>MatTable with First/Last Buttons Example</h3>

<div class="demo-table-container demo-mat-table-example mat-elevation-z4">

<table-header-demo>
</table-header-demo>

<h4 class="paginator-output">{{paginatorOutput | json}}</h4>

<mat-paginator #paginator
[length]="_peopleDatabase.data.length"
[pageSize]="10"
[pageSizeOptions]="[5, 10, 25, 100]"
showFirstLastButtons
(page)="handlePaginator($event)">
</mat-paginator>
</div>

<mat-card class="demo-table-card">
<h3> MatTable Using 'When' Rows for Interactive Details</h3>

Expand Down
4 changes: 4 additions & 0 deletions src/demo-app/table/table-demo.scss
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,7 @@
background: #f5f5f5;
}
}

.paginator-output {
margin-left: 20px;
}
8 changes: 7 additions & 1 deletion src/demo-app/table/table-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import {Component, ElementRef, ViewChild} from '@angular/core';
import {PeopleDatabase, UserData} from './people-database';
import {PersonDataSource} from './person-data-source';
import {MatPaginator, MatSort, MatTableDataSource} from '@angular/material';
import {MatPaginator, MatSort, MatTableDataSource, PageEvent} from '@angular/material';
import {DetailRow, PersonDetailDataSource} from './person-detail-data-source';
import {animate, state, style, transition, trigger} from '@angular/animations';
import {SelectionModel} from '@angular/cdk/collections';
Expand Down Expand Up @@ -65,6 +65,8 @@ export class TableDemo {
@ViewChild('paginatorForDataSource') paginatorForDataSource: MatPaginator;
@ViewChild('sortForDataSource') sortForDataSource: MatSort;

paginatorOutput: PageEvent;

constructor(public _peopleDatabase: PeopleDatabase) {
this.matTableDataSource.sortingDataAccessor = (data: UserData, property: string) => {
switch (property) {
Expand Down Expand Up @@ -183,4 +185,8 @@ export class TableDemo {
toggleHighlight(property: string, enable: boolean) {
enable ? this.highlights.add(property) : this.highlights.delete(property);
}

handlePaginator(pageEvent: PageEvent) {
this.paginatorOutput = pageEvent;
}
}
13 changes: 10 additions & 3 deletions src/lib/paginator/_paginator-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@
color: mat-color($foreground, secondary-text);
}

.mat-paginator-increment,
.mat-paginator-decrement {
.mat-paginator-decrement,
.mat-paginator-increment {
border-top: 2px solid mat-color($foreground, 'icon');
border-right: 2px solid mat-color($foreground, 'icon');
}

.mat-paginator-first,
.mat-paginator-last {
border-top: 2px solid mat-color($foreground, 'icon');
}

.mat-icon-button[disabled] {
.mat-paginator-decrement,
.mat-paginator-increment,
.mat-paginator-decrement {
.mat-paginator-first,
.mat-paginator-last {
border-color: mat-color($foreground, 'disabled');
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/lib/paginator/paginator-intl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export class MatPaginatorIntl {
/** A label for the button that decrements the current page. */
previousPageLabel: string = 'Previous page';

/** A label for the button that moves to the first page. */
firstPageLabel: string = 'First page';

/** A label for the button that moves to the last page. */
lastPageLabel: string = 'Last page';

/** A label for the range of items within the current page and the length of the whole list. */
getRangeLabel = (page: number, pageSize: number, length: number) => {
if (length == 0 || pageSize == 0) { return `0 of ${length}`; }
Expand Down
26 changes: 24 additions & 2 deletions src/lib/paginator/paginator.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,25 @@
{{_intl.getRangeLabel(pageIndex, pageSize, length)}}
</div>

<button mat-icon-button type="button"
class="mat-paginator-navigation-first"
(click)="firstPage()"
[attr.aria-label]="_intl.firstPageLabel"
[matTooltip]="_intl.firstPageLabel"
[matTooltipPosition]="'above'"
[disabled]="!hasPreviousPage()"
*ngIf="showFirstLastButtons">
<div class="mat-paginator-first"></div>
<div class="mat-paginator-decrement"></div>
</button>
<button mat-icon-button type="button"
class="mat-paginator-navigation-previous"
(click)="previousPage()"
[attr.aria-label]="_intl.previousPageLabel"
[matTooltip]="_intl.previousPageLabel"
[matTooltipPosition]="'above'"
[disabled]="!hasPreviousPage()">
<div class="mat-paginator-increment"></div>
<div class="mat-paginator-decrement"></div>
</button>
<button mat-icon-button type="button"
class="mat-paginator-navigation-next"
Expand All @@ -41,7 +52,18 @@
[matTooltip]="_intl.nextPageLabel"
[matTooltipPosition]="'above'"
[disabled]="!hasNextPage()">
<div class="mat-paginator-decrement"></div>
<div class="mat-paginator-increment"></div>
</button>
<button mat-icon-button type="button"
class="mat-paginator-navigation-last"
(click)="lastPage()"
[attr.aria-label]="_intl.lastPageLabel"
[matTooltip]="_intl.lastPageLabel"
[matTooltipPosition]="'above'"
[disabled]="!hasNextPage()"
*ngIf="showFirstLastButtons">
<div class="mat-paginator-increment"></div>
<div class="mat-paginator-last"></div>
</button>
</div>
</div>
2 changes: 1 addition & 1 deletion src/lib/paginator/paginator.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ This will allow you to change the following:
3. The tooltip messages on the navigation buttons.

### Accessibility
The `aria-label`s for next page and previous page buttons can be set in `MatPaginatorIntl`.
The `aria-label`s for next page, previous page, first page and last page buttons can be set in `MatPaginatorIntl`.
64 changes: 51 additions & 13 deletions src/lib/paginator/paginator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,18 @@ $mat-paginator-button-margin: 8px;
$mat-paginator-button-icon-height: 8px;
$mat-paginator-button-icon-width: 8px;

$mat-paginator-button-decrement-icon-margin: 12px;
$mat-paginator-button-increment-icon-margin: 16px;
$mat-paginator-button-increment-icon-margin: 12px;
$mat-paginator-button-decrement-icon-margin: 16px;

$mat-paginator-button-first-last-icon-width: 14px;

$mat-paginator-button-first-icon-margin: 3px;
$mat-paginator-button-last-icon-margin: 15px;


$mat-paginator-button-first-decrement-icon-margin: 21px;
$mat-paginator-button-last-increment-icon-margin: 9px;


.mat-paginator {
display: block;
Expand Down Expand Up @@ -50,43 +60,71 @@ $mat-paginator-button-increment-icon-margin: 16px;
margin: $mat-paginator-range-label-margin;
}

.mat-paginator-increment-button + .mat-paginator-increment-button {
.mat-paginator-decrement-button + .mat-paginator-decrement-button {
margin: 0 0 0 $mat-paginator-button-margin;

[dir='rtl'] & {
margin: 0 $mat-paginator-button-margin 0 0;
}
}

.mat-paginator-increment,
.mat-paginator-decrement {
.mat-paginator-decrement,
.mat-paginator-increment {
width: $mat-paginator-button-icon-width;
height: $mat-paginator-button-icon-height;
}

.mat-paginator-decrement,
[dir='rtl'] .mat-paginator-increment {
transform: rotate(45deg);
}
.mat-paginator-increment,
[dir='rtl'] .mat-paginator-decrement {
transform: rotate(45deg);
}
.mat-paginator-decrement,
[dir='rtl'] .mat-paginator-increment {
transform: rotate(225deg);
}

.mat-paginator-increment {
margin-left: $mat-paginator-button-increment-icon-margin;
[dir='rtl'] & {
margin-right: $mat-paginator-button-increment-icon-margin;
}
}

.mat-paginator-decrement {
margin-left: $mat-paginator-button-decrement-icon-margin;
[dir='rtl'] & {
margin-right: $mat-paginator-button-decrement-icon-margin;
}
}

.mat-paginator-increment {
margin-left: $mat-paginator-button-increment-icon-margin;
[dir='rtl'] & {
margin-right: $mat-paginator-button-increment-icon-margin;
.mat-paginator-first {
transform: rotate(90deg);
width: $mat-paginator-button-first-last-icon-width;
height: $mat-paginator-button-icon-height;
float:left;
margin-left: $mat-paginator-button-first-icon-margin;
}

.mat-paginator-navigation-first {
.mat-paginator-decrement {
margin-left: $mat-paginator-button-first-decrement-icon-margin;
}
}

.mat-paginator-navigation-last {
.mat-paginator-increment {
float: left;
margin-left: $mat-paginator-button-last-increment-icon-margin;
}
}

.mat-paginator-last {
transform: rotate(90deg);
width: $mat-paginator-button-first-last-icon-width;
height: $mat-paginator-button-icon-height;
margin-left: $mat-paginator-button-last-icon-margin;
}

.mat-paginator-range-actions {
display: flex;
align-items: center;
Expand Down
68 changes: 66 additions & 2 deletions src/lib/paginator/paginator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('MatPaginator', () => {
}));
});

describe('when navigating with the navigation buttons', () => {
describe('when navigating with the next and previous buttons', () => {
it('should be able to go to the next page', () => {
expect(paginator.pageIndex).toBe(0);

Expand All @@ -125,6 +125,58 @@ describe('MatPaginator', () => {
expect(component.latestPageEvent ? component.latestPageEvent.pageIndex : null).toBe(0);
});

});

it('should be able to show the first/last buttons', () => {
expect(getFirstButton(fixture))
.toBeNull('Expected first button to not exist.');

expect(getLastButton(fixture))
.toBeNull('Expected last button to not exist.');

fixture.componentInstance.showFirstLastButtons = true;
fixture.detectChanges();

expect(getFirstButton(fixture))
.toBeTruthy('Expected first button to be rendered.');

expect(getLastButton(fixture))
.toBeTruthy('Expected last button to be rendered.');

});

describe('when showing the first and last button', () => {

beforeEach(() => {
component.showFirstLastButtons = true;
fixture.detectChanges();
});

it('should show right aria-labels for first/last buttons', () => {
expect(getFirstButton(fixture).getAttribute('aria-label')).toBe('First page');
expect(getLastButton(fixture).getAttribute('aria-label')).toBe('Last page');
});

it('should be able to go to the last page via the last page button', () => {
expect(paginator.pageIndex).toBe(0);

dispatchMouseEvent(getLastButton(fixture), 'click');

expect(paginator.pageIndex).toBe(9);
expect(component.latestPageEvent ? component.latestPageEvent.pageIndex : null).toBe(9);
});

it('should be able to go to the first page via the first page button', () => {
paginator.pageIndex = 3;
fixture.detectChanges();
expect(paginator.pageIndex).toBe(3);

dispatchMouseEvent(getFirstButton(fixture), 'click');

expect(paginator.pageIndex).toBe(0);
expect(component.latestPageEvent ? component.latestPageEvent.pageIndex : null).toBe(0);
});

it('should disable navigating to the next page if at last page', () => {
component.goToLastPage();
fixture.detectChanges();
Expand All @@ -148,6 +200,7 @@ describe('MatPaginator', () => {
expect(component.latestPageEvent).toBe(null);
expect(paginator.pageIndex).toBe(0);
});

});

it('should mark for check when inputs are changed directly', () => {
Expand Down Expand Up @@ -253,7 +306,7 @@ describe('MatPaginator', () => {
expect(fixture.nativeElement.querySelector('.mat-select')).toBeNull();
});

it('should handle the number inputs being passed in as strings', () => {
it('should handle the number inputs being passed in as strings', () => {
const withStringFixture = TestBed.createComponent(MatPaginatorWithStringValues);
const withStringPaginator = withStringFixture.componentInstance.paginator;

Expand All @@ -277,6 +330,7 @@ describe('MatPaginator', () => {
expect(element.querySelector('.mat-paginator-page-size'))
.toBeNull('Expected select to be removed.');
});

});

function getPreviousButton(fixture: ComponentFixture<any>) {
Expand All @@ -287,12 +341,21 @@ function getNextButton(fixture: ComponentFixture<any>) {
return fixture.nativeElement.querySelector('.mat-paginator-navigation-next');
}

function getFirstButton(fixture: ComponentFixture<any>) {
return fixture.nativeElement.querySelector('.mat-paginator-navigation-first');
}

function getLastButton(fixture: ComponentFixture<any>) {
return fixture.nativeElement.querySelector('.mat-paginator-navigation-last');
}

@Component({
template: `
<mat-paginator [pageIndex]="pageIndex"
[pageSize]="pageSize"
[pageSizeOptions]="pageSizeOptions"
[hidePageSize]="hidePageSize"
[showFirstLastButtons]="showFirstLastButtons"
[length]="length"
(page)="latestPageEvent = $event">
</mat-paginator>
Expand All @@ -303,6 +366,7 @@ class MatPaginatorApp {
pageSize = 10;
pageSizeOptions = [5, 10, 25, 100];
hidePageSize = false;
showFirstLastButtons = false;
length = 100;

latestPageEvent: PageEvent | null;
Expand Down
Loading