Skip to content
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

feat(paging): ability to jump to page n with page links (closes #496) #544

Merged
merged 11 commits into from
May 4, 2017
Merged
6 changes: 3 additions & 3 deletions src/app/components/components/paging/paging.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<p>Total: {{event?.total || '1345'}}</p>
</md-card-content>
<md-divider></md-divider>
<td-paging-bar #pagingBar pageSizeAllText="All" [pageSizeAll]="true" [pageSizes]="[50,100,200,500,1000,2000]"
<td-paging-bar #pagingBar pageSizeAllText="All" [pageSizeAll]="true" [pageSizes]="[50,100,200,500,1000,2000]" pageLinkCount="5"
[initialPage]="1" [firstLast]="firstLast" [pageSize]="100" [total]="1345" (change)="change($event)">
<span td-paging-bar-label hide-xs>Row per page:</span>
{{pagingBar.range}} <span hide-xs>of {{pagingBar.total}}</span>
Expand All @@ -27,7 +27,7 @@
<p>HTML:</p>
<td-highlight lang="html">
<![CDATA[
<td-paging-bar #pagingBar pageSizeAllText="All" [pageSizeAll]="true" [pageSizes]="[50,100,200,500,1000,2000]"
<td-paging-bar #pagingBar pageSizeAllText="All" [pageSizeAll]="true" [pageSizes]="[50,100,200,500,1000,2000]" pageLinkCount="5"
[initialPage]="1" [firstLast]="firstLast" [pageSize]="100" [total]="1345" (change)="change($event)">
<span td-paging-bar-label hide-xs>Row per page:</span>
{ {pagingBar.range} } <span hide-xs>of { {pagingBar.total} }</span>
Expand Down Expand Up @@ -101,7 +101,7 @@ <h3>Example:</h3>
<td-highlight lang="html">
<![CDATA[
<td-paging-bar #pagingBar pageSizeAllText="allText" [firstLast]="true|false" [pageSizeAll]="true|false" [pageSizes]="[100,200,500,1000,2000]"
[initialPage]="1" [pageSize]="100" [total]="1345" (change)="change($event)">
pageLinkCount="5" [initialPage]="1" [pageSize]="100" [total]="1345" (change)="change($event)">
<span td-paging-bar-label hide-xs>Row per page:</span>
{ {pagingBar.range} } <span hide-xs>of { {pagingBar.total} }</span>
</td-paging-bar>
Expand Down
4 changes: 4 additions & 0 deletions src/app/components/components/paging/paging.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export class PagingDemoComponent {
description: `Selected page size for the pagination. Defaults to first element of the [pageSizes] array.`,
name: 'pageSize?',
type: 'number',
}, {
description: `Defines the number of PageLinks to display. PageLinks are used to jump to a specific page, default is 0.`,
name: 'pageLinkCount?',
type: 'number',
}, {
description: `ets starting page for the paging bar. Defaults to '1'`,
name: 'initialPage?',
Expand Down
7 changes: 5 additions & 2 deletions src/platform/core/paging/paging-bar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@
<ng-content></ng-content>
</div>
<div class="td-paging-bar-navigation">
<button md-icon-button type="button" *ngIf="firstLast" [disabled]="isMinPage()" (click)="firstPage()">
<button id="firstPageButton" md-icon-button type="button" *ngIf="firstLast" [disabled]="isMinPage()" (click)="firstPage()">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when using id's, it can get tricky since there will be id duplication when using 2 td-paging-bar components on screen, so we have to dynamically create an id (could be a number) and increase it on every constructor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like [id]="'td-paging-bar-' + id + '-first-page'"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code updated

<md-icon>{{ isRTL ? 'skip_next' : 'skip_previous' }}</md-icon>
</button>
<button md-icon-button type="button" [disabled]="isMinPage()" (click)="prevPage()">
<md-icon>{{ isRTL ? 'navigate_next' : 'navigate_before' }}</md-icon>
</button>
<ng-template *ngIf="pageLinkCount > 0" let-link let-index="index" ngFor [ngForOf]="pageLinks">
<button id="{{'pageLinkCount' + index}}" md-icon-button type="button" [color]="page === link ? 'accent' : ''" (click)="navigateToPage(link)">{{link}}</button>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, and we can probably do [id]="'td-paging-bar-' + id + '-page-link-' + index"

</ng-template>
<button md-icon-button type="button" [disabled]="isMaxPage()" (click)="nextPage()">
<md-icon>{{ isRTL ? 'navigate_before' : 'navigate_next' }}</md-icon>
</button>
<button md-icon-button type="button" *ngIf="firstLast" [disabled]="isMaxPage()" (click)="lastPage()">
<button id="lastPageButton" md-icon-button type="button" *ngIf="firstLast" [disabled]="isMaxPage()" (click)="lastPage()">
<md-icon>{{ isRTL ? 'skip_previous' : 'skip_next' }}</md-icon>
</button>
</div>
Expand Down
172 changes: 172 additions & 0 deletions src/platform/core/paging/paging-bar.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import {
TestBed,
inject,
async,
ComponentFixture,
} from '@angular/core/testing';
import 'hammerjs';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { TdPagingBarComponent } from './paging-bar.component';
import { CovalentPagingModule } from './paging.module';
import { NgModule, DebugElement } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

describe('Component: TdPagingBarComponent', () => {

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
CovalentPagingModule,
],
declarations: [
TestpageSizeAllTextComponent,
TestInitialPageComponent,
TestPageSizesComponent,
TestFirstLastComponent,
TestPageLinkCountComponent,
],
});
TestBed.compileComponents();
}));

it('should create the component', (done: DoneFn) => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TestpageSizeAllTextComponent);
let component: TestpageSizeAllTextComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component).toBeTruthy();
done();
});
});

it('should set pageSizeAllText, pageSizeAll and see it in markup', (done: DoneFn) => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TestpageSizeAllTextComponent);
let component: TestpageSizeAllTextComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
let pageSizeAllText: string = fixture.debugElement.query(By.directive(TdPagingBarComponent)).componentInstance.pageSizeAllText;
expect(pageSizeAllText).toBe('SomeOtherText');

let pageSizeAll: boolean = fixture.debugElement.query(By.directive(TdPagingBarComponent)).componentInstance.pageSizeAll;
expect(pageSizeAll).toBe(true);
done();
});
});
});

it('should set pageSizes and then component instantiate with that pageSize', (done: DoneFn) => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TestPageSizesComponent);
let component: TestPageSizesComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
let pageSize: number = fixture.debugElement.query(By.directive(TdPagingBarComponent)).componentInstance.pageSize;
expect(pageSize).toBe(37);
done();
});
});
});

it('should set intialPage and then component instantiate with that page', (done: DoneFn) => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TestInitialPageComponent);
let component: TestInitialPageComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
let page: number = fixture.debugElement.query(By.directive(TdPagingBarComponent)).componentInstance.page;
expect(page).toBe(3);
done();
});
});
});

it('should set firstLast and then see buttons in markup', (done: DoneFn) => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TestFirstLastComponent);
let component: TestFirstLastComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.query(By.css('#firstPageButton'))).toBeTruthy();
expect(fixture.debugElement.query(By.css('#lastPageButton'))).toBeTruthy();
done();
});
});
});

it('should set pageLinkCount and then see buttons in markup', (done: DoneFn) => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TestPageLinkCountComponent);
let component: TestPageLinkCountComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.query(By.css('#pageLinkCount0'))).toBeTruthy();
expect(fixture.debugElement.query(By.css('#pageLinkCount1'))).toBeTruthy();
expect(fixture.debugElement.query(By.css('#pageLinkCount2'))).toBeTruthy();
expect(fixture.debugElement.query(By.css('#pageLinkCount3'))).toBeTruthy();
expect(fixture.debugElement.query(By.css('#pageLinkCount4'))).toBeTruthy();
expect(fixture.debugElement.query(By.css('#pageLinkCount5'))).toBeTruthy();
expect(fixture.debugElement.query(By.css('#pageLinkCount6'))).toBeTruthy();
done();
});
});
});
});

@Component({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the most part, its good to separate test cases like you did. But we can make the inputs dynamic so we change them in the unit test on the fly and see if what we expected happens.

e.g.

What should happen if i change pageSizeAll to false on the fly and then back

or

What should happen if i change the pageLinks

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code checked in for this

template: `
<td-paging-bar pageSizeAllText="SomeOtherText" [pageSizeAll]="true"
[pageSizes]="[50,100,200,500,1000,2000]" [total]="9215"></td-paging-bar>`,
})
class TestpageSizeAllTextComponent {
content: string;
}

@Component({
template: `
<td-paging-bar pageSizeAllText="SomeOtherText" [pageSizeAll]="true"
[pageSizes]="[37,48]" [total]="9215"></td-paging-bar>`,
})
class TestPageSizesComponent {
content: string;
}

@Component({
template: `
<td-paging-bar pageSizeAllText="SomeOtherText" [pageSizeAll]="true"
[initialPage]="3" [pageSizes]="[50,100,200,500,1000,2000]" [total]="9215"></td-paging-bar>`,
})
class TestInitialPageComponent {
content: string;
}

@Component({
template: `
<td-paging-bar pageSizeAllText="All" [pageSizeAll]="true" [pageSizes]="[50,100,200,500,1000,2000]"
[initialPage]="1" [firstLast]="true" [pageSize]="100" [total]="9333"></td-paging-bar>`,
})
class TestFirstLastComponent {
content: string;
}

@Component({
template: `
<td-paging-bar pageSizeAllText="All" [pageSizeAll]="true" [pageSizes]="[50,100,200,500,1000,2000]" pageLinkCount="7"
[initialPage]="1" [firstLast]="true" [pageSize]="100" [total]="1345"></td-paging-bar>`,
})
class TestPageLinkCountComponent {
content: string;
}
43 changes: 43 additions & 0 deletions src/platform/core/paging/paging-bar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class TdPagingBarComponent implements OnInit {
private _fromRow: number = 1;
private _toRow: number = 1;
private _initialized: boolean = false;
private _pageLinks: number[] = [];

/**
* pageSizeAll?: boolean
Expand All @@ -49,6 +50,12 @@ export class TdPagingBarComponent implements OnInit {
*/
@Input('initialPage') initialPage: number = 1;

/**
* pageLinkCount?: number
* Amount of page jump to links for the paging bar. Defaults to '0'
*/
@Input('pageLinkCount') pageLinkCount: number = 0;

/**
* pageSizes?: number[]
* Array that populates page size menu. Defaults to [50, 100, 200, 500, 1000]
Expand Down Expand Up @@ -96,6 +103,14 @@ export class TdPagingBarComponent implements OnInit {
return this._total;
}

/**
* pageLinks: number[]
* Returns the pageLinks in an array
*/
get pageLinks(): number[] {
return this._pageLinks;
}

/**
* range: string
* Returns the range of the rows.
Expand Down Expand Up @@ -139,6 +154,7 @@ export class TdPagingBarComponent implements OnInit {
ngOnInit(): void {
this._page = this.initialPage;
this._calculateRows();
this._calculatePageLinks();
this._initialized = true;
}

Expand Down Expand Up @@ -201,8 +217,35 @@ export class TdPagingBarComponent implements OnInit {
this._toRow = this._total > top ? top : this._total;
}

/**
* _calculatePageLinks?: function
* Calculates the page links that should be shown to the user based on the current state of the paginator
*/
private _calculatePageLinks(): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fancee 🍷

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎩

// reset the pageLinks array
this._pageLinks = [];
// fill in the array with the pageLinks based on the current selected page
let middlePageLinks: number = Math.floor(this.pageLinkCount / 2);
for (let x: number = 0; x < this.pageLinkCount; x++) {
// don't go past the maxPage in the pageLinks
if ((this.page + middlePageLinks) >= this.maxPage) {
this._pageLinks[x] = this.maxPage - (this.pageLinkCount - (x + 1));
// if the selected page is after the middle then set that page as middle and get the correct balance on left and right
} else if ((this.page - middlePageLinks) > 0) {
this._pageLinks[x] = (this.page - middlePageLinks) + x;
// if the selected page is before the middle then set the pages based on the x index leading up to and after selected page
} else if ((this.page - middlePageLinks) <= 0) {
this._pageLinks[x] = x + 1;
// other wise just set the array in order starting from the selected page
} else {
this._pageLinks[x] = this.page + x;
}
}
}

private _handleOnChange(): void {
this._calculateRows();
this._calculatePageLinks();
let event: IPageChangeEvent = {
page: this._page,
maxPage: this.maxPage,
Expand Down