Skip to content

Commit

Permalink
feat(design,design-land): add link support to paginator
Browse files Browse the repository at this point in the history
  • Loading branch information
griest024 committed Oct 25, 2023
1 parent c379833 commit 93267b6
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 59 deletions.
6 changes: 6 additions & 0 deletions apps/design-land/src/app/paginator/paginator.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ <h2>Usage</h2>
<design-land-article-encapsulated>
<design-land-example-viewer-container example="basic-paginator"></design-land-example-viewer-container>
</design-land-article-encapsulated>

<h3>Link Mode</h3>
<p>The paginator can be used in link mode which replaces the buttons with anchors. In link mode, pass the URL of the current collection page and optionally the name of the query param that will set the current page.</p>
<design-land-article-encapsulated>
<design-land-example-viewer-container example="link-paginator"></design-land-example-viewer-container>
</design-land-article-encapsulated>
2 changes: 2 additions & 0 deletions libs/design/paginator/examples/src/examples.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BasicPaginatorComponent } from './basic-paginator/basic-paginator.component';
import { LinkPaginatorComponent } from './link-paginator/link-paginator.component';

export const PAGINATOR_EXAMPLES = [
BasicPaginatorComponent,
LinkPaginatorComponent,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<daff-paginator aria-label="Search results page" [numberOfPages]="numberOfPages" [currentPage]="currentPage$ | async" [linkMode]="true" [url]="url" [queryParam]="queryParam"></daff-paginator>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
ChangeDetectionStrategy,
Component,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import {
Observable,
map,
} from 'rxjs';

@Component({
// eslint-disable-next-line @angular-eslint/component-selector
selector: 'link-paginator',
templateUrl: './link-paginator.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LinkPaginatorComponent {
numberOfPages = 15;
// TODO: don't hardcode this, pass it in design land
url = '/paginator';
queryParam = 'currentPage';

get currentPage$(): Observable<number> {
return this.route.queryParamMap.pipe(
map((qps) => Number(qps.get(this.queryParam))),
);
}

constructor(
private route: ActivatedRoute,
) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';

import { DaffPaginatorModule } from '@daffodil/design';

import { LinkPaginatorComponent } from './link-paginator.component';

@NgModule({
declarations: [
LinkPaginatorComponent,
],
exports: [
LinkPaginatorComponent,
],
imports: [
DaffPaginatorModule,
],
providers: [],
})
export class LinkPaginatorModule { }
1 change: 1 addition & 0 deletions libs/design/paginator/examples/src/public_api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { BasicPaginatorComponent } from './basic-paginator/basic-paginator.component';
export { LinkPaginatorComponent } from './link-paginator/link-paginator.component';

export { PaginatorExamplesModule } from './paginator-examples.module';
export { PAGINATOR_EXAMPLES } from './examples';
106 changes: 82 additions & 24 deletions libs/design/src/molecules/paginator/paginator.component.html
Original file line number Diff line number Diff line change
@@ -1,48 +1,106 @@
<button type="button" class="daff-paginator__previous"
[disabled]="_disablePrev()"
<button *ngIf="!linkMode" type="button" class="daff-paginator__previous"
[disabled]="_disablePrev"
tabindex="0"
attr.aria-label="Go to Previous Page of {{_paginatorId}} Paginator"
(click)="_onNotifyPrevPageChange()">
<fa-icon [icon]="faChevronLeft" size="sm"></fa-icon> Previous
</button>
<ng-container *ngIf="linkMode">
<a class="daff-paginator__previous"
*ngIf="!_disablePrev"
attr.aria-label="Go to Previous Page of {{_paginatorId}} Paginator"
[routerLink]="url"
queryParamsHandling="merge"
[queryParams]="_buildPageQueryParams(currentPage - 1)">
<fa-icon [icon]="faChevronLeft" size="sm"></fa-icon><span>Previous</span>
</a>
<span class="daff-paginator__previous disabled"
*ngIf="_disablePrev"
attr.aria-label="Go to Previous Page of {{_paginatorId}} Paginator"
[attr.disabled]="true">
<fa-icon [icon]="faChevronLeft" size="sm"></fa-icon><span>Previous</span>
</span>
</ng-container>

<button type="button" class="daff-paginator__page-link"
<button *ngIf="!linkMode" type="button" class="daff-paginator__page-link"
[class.selected]="_isSelected(1)"
tabindex="0"
attr.aria-label="Go to Page 1 of {{_paginatorId}} Paginator"
(click)="_onNotifyPageChange(1)">
<span>1</span>
</button>
<a *ngIf="linkMode" class="daff-paginator__page-link"
[routerLink]="url"
[queryParams]="_buildPageQueryParams(1)"
queryParamsHandling="merge"
[class.selected]="_isSelected(1)"
attr.aria-label="Go to Page 1 of {{_paginatorId}} Paginator"
><span>1</span></a>

<span class="daff-paginator__ellipsis" *ngIf="_showFirstEllipsis()">...</span>
<span class="daff-paginator__ellipsis" *ngIf="_showFirstEllipsis">...</span>

<ng-container *ngFor="let pageNumber of _numberOfPagesArray">
<button type="button" class="daff-paginator__page-link"
[class.selected]="_isSelected(pageNumber)"
tabindex="0"
attr.aria-label="Go to Page {{pageNumber}} of {{_paginatorId}} Paginator"
aria-current="_isSelected(pageNumber)"
*ngIf="_showNumber(pageNumber)"
(click)="_onNotifyPageChange(pageNumber)">
<span>{{ pageNumber }}</span>
</button>
<ng-container *ngIf="_showNumber(pageNumber)">
<button *ngIf="!linkMode" type="button" class="daff-paginator__page-link"
[class.selected]="_isSelected(pageNumber)"
[attr.data-page-number]="pageNumber"
tabindex="0"
attr.aria-label="Go to Page {{pageNumber}} of {{_paginatorId}} Paginator"
aria-current="_isSelected(pageNumber)"
(click)="_onNotifyPageChange(pageNumber)">
<span>{{ pageNumber }}</span>
</button>
<a *ngIf="linkMode" class="daff-paginator__page-link"
[attr.data-page-number]="pageNumber"
[routerLink]="url"
[queryParams]="_buildPageQueryParams(pageNumber)"
queryParamsHandling="merge"
[class.selected]="_isSelected(pageNumber)"
attr.aria-label="Go to Page {{pageNumber}} of {{_paginatorId}} Paginator"
><span>{{ pageNumber }}</span></a>
</ng-container>
</ng-container>

<span class="daff-paginator__ellipsis" *ngIf="_showLastEllipsis()">...</span>
<span class="daff-paginator__ellipsis" *ngIf="_showLastEllipsis">...</span>

<button type="button" class="daff-paginator__page-link"
[class.selected]="_isSelected(numberOfPages)"
tabindex="0"
attr.aria-label="Go To Page {{numberOfPages}} of {{_paginatorId}} Paginator"
(click)="_onNotifyPageChange(numberOfPages)"
*ngIf="!(numberOfPages < 2)">
<span>{{ numberOfPages }}</span>
</button>
<ng-container *ngIf="!(numberOfPages < 2)">
<button *ngIf="!linkMode" type="button" class="daff-paginator__page-link"
[class.selected]="_isSelected(numberOfPages)"
tabindex="0"
attr.aria-label="Go To Page {{numberOfPages}} of {{_paginatorId}} Paginator"
(click)="_onNotifyPageChange(numberOfPages)"
>
<span>{{ numberOfPages }}</span>
</button>
<a *ngIf="linkMode" class="daff-paginator__page-link"
[routerLink]="url"
[queryParams]="_buildPageQueryParams(numberOfPages)"
queryParamsHandling="merge"
[class.selected]="_isSelected(numberOfPages)"
attr.aria-label="Go to Page {{numberOfPages}} of {{_paginatorId}} Paginator"
><span>{{ numberOfPages }}</span></a>
</ng-container>

<button class="daff-paginator__next"
[disabled]="_disableNext()"
<button *ngIf="!linkMode" class="daff-paginator__next"
[disabled]="_disableNext"
tabindex="0"
attr.aria-label="Go to Next Page of {{_paginatorId}} Paginator"
(click)="_onNotifyNextPageChange()">
Next <fa-icon [icon]="faChevronRight" size="sm"></fa-icon>
</button>
<ng-container *ngIf="linkMode">
<a class="daff-paginator__next"
*ngIf="!_disableNext"
[routerLink]="url"
attr.aria-label="Go to Next Page of {{_paginatorId}} Paginator"
queryParamsHandling="merge"
[queryParams]="_buildPageQueryParams(currentPage + 1)">
<span>Next</span><fa-icon [icon]="faChevronRight" size="sm"></fa-icon>
</a>
<span class="daff-paginator__next disabled"
*ngIf="_disableNext"
attr.aria-label="Go to Next Page of {{_paginatorId}} Paginator"
[attr.disabled]="true">
<span>Next</span><fa-icon [icon]="faChevronRight" size="sm"></fa-icon>
</span>
</ng-container>
110 changes: 109 additions & 1 deletion libs/design/src/molecules/paginator/paginator.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Location } from '@angular/common';
import {
Component,
DebugElement,
Expand All @@ -6,8 +7,12 @@ import {
waitForAsync,
ComponentFixture,
TestBed,
fakeAsync,
tick,
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';

import {
DaffPaginatorNumberOfPagesErrorMessage,
Expand All @@ -16,23 +21,52 @@ import {
import { DaffPaginatorComponent } from './paginator.component';
import { DaffPaginatorModule } from './paginator.module';

@Component({ template: '<daff-paginator aria-label="id" [numberOfPages]="numberOfPagesValue" [currentPage]="currentPageValue"></daff-paginator>' })

@Component({ template: '' })
class TestComponent {}

@Component({ template: `
<daff-paginator
aria-label="id"
[numberOfPages]="numberOfPagesValue"
[currentPage]="currentPageValue"
[linkMode]="linkModeValue"
[url]="urlValue"
[queryParam]="queryParamValue"
></daff-paginator>
` })

class WrapperComponent {
numberOfPagesValue = 20;
currentPageValue = 2;
linkModeValue = false;
urlValue = '';
queryParamValue = '';
}

describe('DaffPaginatorComponent', () => {
let wrapper: WrapperComponent;
let fixture: ComponentFixture<WrapperComponent>;
let de: DebugElement;
let component: DaffPaginatorComponent;
let route: ActivatedRoute;
let location: Location;
const testUrl = 'test';

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
DaffPaginatorModule,
RouterTestingModule.withRoutes([
{
path: '',
component: TestComponent,
},
{
path: testUrl,
component: TestComponent,
},
]),
],
declarations: [
WrapperComponent,
Expand All @@ -42,6 +76,9 @@ describe('DaffPaginatorComponent', () => {
}));

beforeEach(() => {
route = TestBed.inject(ActivatedRoute);
location = TestBed.inject(Location);

fixture = TestBed.createComponent(WrapperComponent);
wrapper = fixture.componentInstance;
de = fixture.debugElement.query(By.css('daff-paginator'));
Expand All @@ -64,6 +101,18 @@ describe('DaffPaginatorComponent', () => {
expect(component.currentPage).toEqual(wrapper.currentPageValue);
});

it('should be able to take linkMode as input', () => {
expect(component.linkMode).toEqual(wrapper.linkModeValue);
});

it('should be able to take url as input', () => {
expect(component.url).toEqual(wrapper.urlValue);
});

it('should be able to take queryParam as input', () => {
expect(component.queryParam).toEqual(wrapper.queryParamValue);
});

it('should be able to take numberOfPages as input', () => {
expect(component.numberOfPages).toEqual(wrapper.numberOfPagesValue);
});
Expand All @@ -81,6 +130,65 @@ describe('DaffPaginatorComponent', () => {
expect(paginatorText.includes(greaterPage)).toBeTruthy();
});

describe('when the component is in link mode', () => {
beforeEach(() => {
wrapper.linkModeValue = true;
wrapper.urlValue = testUrl;
wrapper.queryParamValue = 'queryParam';
fixture.detectChanges();
});

it('should render the buttons as anchors', () => {
expect(fixture.debugElement.queryAll(By.css('button')).length).toEqual(0);
expect(fixture.debugElement.queryAll(By.css('a')).length).toBeGreaterThan(0);
});

describe('when previous link is clicked', () => {
beforeEach(fakeAsync(() => {
fixture.debugElement.query(By.css('.daff-paginator__previous')).nativeElement.click();
tick();
}));

it('should set the query param value', () => {
expect(Number(route.snapshot.queryParamMap.get(wrapper.queryParamValue))).toEqual(wrapper.currentPageValue - 1);
});

it('should navigate to the set url', () => {
expect(location.path()).toContain(wrapper.urlValue);
});
});

describe('when next link is clicked', () => {
beforeEach(fakeAsync(() => {
fixture.debugElement.query(By.css('.daff-paginator__next')).nativeElement.click();
tick();
}));

it('should set the query param value', () => {
expect(Number(route.snapshot.queryParamMap.get(wrapper.queryParamValue))).toEqual(wrapper.currentPageValue + 1);
});

it('should navigate to the set url', () => {
expect(location.path()).toContain(wrapper.urlValue);
});
});

describe('when a page number link is clicked', () => {
beforeEach(fakeAsync(() => {
fixture.debugElement.query(By.css('.daff-paginator__page-link[data-page-number="3"]')).nativeElement.click();
tick();
}));

it('should set the query param value', () => {
expect(Number(route.snapshot.queryParamMap.get(wrapper.queryParamValue))).toEqual(3);
});

it('should navigate to the set url', () => {
expect(location.path()).toContain(wrapper.urlValue);
});
});
});

describe('when the numberOfPages is less than 2', () => {

it('should only render one .daff-paginator__page-link', () => {
Expand Down
Loading

0 comments on commit 93267b6

Please sign in to comment.