[ADF-2157] Infinite pagination target supporting (#2856)

* Infinite pagination target supporting

* Updating documentation
This commit is contained in:
Popovics András
2018-01-22 10:59:05 +00:00
committed by Eugenio Romano
parent d189567853
commit 89a7b0c4b0
7 changed files with 158 additions and 68 deletions

View File

@@ -291,10 +291,8 @@
</adf-pagination> </adf-pagination>
<adf-infinite-pagination <adf-infinite-pagination
*ngIf="infiniteScrolling" *ngIf="infiniteScrolling"
[pagination]="pagination" [target]="documentList"
[loading]="documentList.infiniteLoading" [loading]="documentList.infiniteLoading">
[pageSize]="currentMaxItems"
(loadMore)="loadNextBatch($event)">
{{ 'ADF-DOCUMENT-LIST.LAYOUT.LOAD_MORE' | translate }} {{ 'ADF-DOCUMENT-LIST.LAYOUT.LOAD_MORE' | translate }}
</adf-infinite-pagination> </adf-infinite-pagination>
</adf-upload-drag-area> </adf-upload-drag-area>

View File

@@ -14,6 +14,17 @@ Adds "infinite" pagination to the component it is used with.
</adf-infinite-pagination> </adf-infinite-pagination>
``` ```
## Integrating with Document List
```html
<adf-document-list #documentList ...></adf-document-list>
<adf-infinite-pagination
[target]="documentList"
[loading="documentList.infiniteLoading">
</adf-infinite-pagination>
```
### Properties ### Properties
| Name | Type | Default | Description | | Name | Type | Default | Description |
@@ -21,6 +32,8 @@ Adds "infinite" pagination to the component it is used with.
| pagination | Pagination | `InfinitePaginationComponent.DEFAULT_PAGINATION` | Pagination object | | pagination | Pagination | `InfinitePaginationComponent.DEFAULT_PAGINATION` | Pagination object |
| pageSize | number | `InfinitePaginationComponent.DEFAULT_PAGE_SIZE` | Number of items that are added with each "load more" event | | pageSize | number | `InfinitePaginationComponent.DEFAULT_PAGE_SIZE` | Number of items that are added with each "load more" event |
| loading | boolean | false | | | loading | boolean | false | |
| target | PaginatedComponent | | Component that provides custom pagination support |
### Events ### Events

View File

@@ -41,6 +41,7 @@ import {
} from 'alfresco-js-api'; } from 'alfresco-js-api';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject'; import { Subject } from 'rxjs/Subject';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { presetsDefaultModel } from '../models/preset.model'; import { presetsDefaultModel } from '../models/preset.model';
import { ShareDataRow } from './../data/share-data-row.model'; import { ShareDataRow } from './../data/share-data-row.model';
import { ShareDataTableAdapter } from './../data/share-datatable-adapter'; import { ShareDataTableAdapter } from './../data/share-datatable-adapter';
@@ -220,7 +221,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
noPermission: boolean = false; noPermission: boolean = false;
selection = new Array<MinimalNodeEntity>(); selection = new Array<MinimalNodeEntity>();
pagination = new Subject<Pagination>(); pagination: BehaviorSubject<Pagination>;
private layoutPresets = {}; private layoutPresets = {};
private currentNodeAllowableOperations: string[] = []; private currentNodeAllowableOperations: string[] = [];
@@ -236,7 +237,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte
private preferences: UserPreferencesService) { private preferences: UserPreferencesService) {
this.maxItems = this.preferences.paginationSize; this.maxItems = this.preferences.paginationSize;
this.pagination.next(<Pagination> { this.pagination = new BehaviorSubject<Pagination>(<Pagination> {
maxItems: this.preferences.paginationSize, maxItems: this.preferences.paginationSize,
skipCount: 0, skipCount: 0,
totalItems: 0, totalItems: 0,

View File

@@ -21,6 +21,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Pagination } from 'alfresco-js-api'; import { Pagination } from 'alfresco-js-api';
import { MaterialModule } from '../material.module'; import { MaterialModule } from '../material.module';
import { InfinitePaginationComponent } from './infinite-pagination.component'; import { InfinitePaginationComponent } from './infinite-pagination.component';
import { PaginatedComponent } from './paginated-component.interface';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
describe('InfinitePaginationComponent', () => { describe('InfinitePaginationComponent', () => {
@@ -55,62 +57,111 @@ describe('InfinitePaginationComponent', () => {
TestBed.resetTestingModule(); TestBed.resetTestingModule();
}); });
it('should show the loading spinner if loading', () => { describe('Standalone', () => {
pagination.hasMoreItems = true;
component.pagination = pagination;
component.isLoading = true;
fixture.detectChanges();
let loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]')); it('should show the loading spinner if loading', () => {
expect(loadingSpinner).not.toBeNull(); pagination.hasMoreItems = true;
}); component.pagination = pagination;
component.isLoading = true;
fixture.detectChanges();
it('should NOT show the loading spinner if NOT loading', () => { let loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]'));
pagination.hasMoreItems = true; expect(loadingSpinner).not.toBeNull();
component.pagination = pagination;
component.isLoading = false;
fixture.detectChanges();
let loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]'));
expect(loadingSpinner).toBeNull();
});
it('should show the load more button if NOT loading and has more items', () => {
pagination.hasMoreItems = true;
component.pagination = pagination;
component.isLoading = false;
fixture.detectChanges();
let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
expect(loadMoreButton).not.toBeNull();
});
it('should NOT show anything if pagination has NO more items', () => {
pagination.hasMoreItems = false;
component.pagination = pagination;
fixture.detectChanges();
let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
expect(loadMoreButton).toBeNull();
let loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]'));
expect(loadingSpinner).toBeNull();
});
it('should trigger the loadMore event with the proper pagination object', (done) => {
pagination.hasMoreItems = true;
pagination.skipCount = 5;
component.pagination = pagination;
component.isLoading = false;
component.pageSize = 5;
fixture.detectChanges();
component.loadMore.subscribe((newPagination: Pagination) => {
expect(newPagination.skipCount).toBe(10);
done();
}); });
let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]')); it('should NOT show the loading spinner if NOT loading', () => {
loadMoreButton.triggerEventHandler('click', {}); pagination.hasMoreItems = true;
component.pagination = pagination;
component.isLoading = false;
fixture.detectChanges();
let loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]'));
expect(loadingSpinner).toBeNull();
});
it('should show the load more button if NOT loading and has more items', () => {
pagination.hasMoreItems = true;
component.pagination = pagination;
component.isLoading = false;
fixture.detectChanges();
let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
expect(loadMoreButton).not.toBeNull();
});
it('should NOT show anything if pagination has NO more items', () => {
pagination.hasMoreItems = false;
component.pagination = pagination;
fixture.detectChanges();
let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
expect(loadMoreButton).toBeNull();
let loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]'));
expect(loadingSpinner).toBeNull();
});
it('should trigger the loadMore event with the proper pagination object', (done) => {
pagination.hasMoreItems = true;
pagination.skipCount = 5;
component.pagination = pagination;
component.isLoading = false;
component.pageSize = 5;
fixture.detectChanges();
component.loadMore.subscribe((newPagination: Pagination) => {
expect(newPagination.skipCount).toBe(10);
done();
});
let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
loadMoreButton.triggerEventHandler('click', {});
});
}); });
describe('Target', () => {
let testTarget: PaginatedComponent;
beforeEach(() => {
pagination = { maxItems: 444, skipCount: 0, totalItems: 888, hasMoreItems: true };
testTarget = {
pagination: new BehaviorSubject<Pagination>(pagination),
supportedPageSizes: [],
updatePagination() {}
};
spyOn(testTarget, 'updatePagination');
});
it('should subscribe to target\'s pagination observable to update pagination and pagesize correctly', () => {
component.target = testTarget;
fixture.detectChanges();
expect(component.pagination).toBe(pagination);
expect(component.pageSize).toBe(pagination.maxItems);
});
it('should call the target\'s updatePagination on invoking the onLoadMore', () => {
component.target = testTarget;
fixture.detectChanges();
component.onLoadMore();
expect(testTarget.updatePagination).toHaveBeenCalledWith({ maxItems: 444, skipCount: 444, totalItems: 888, hasMoreItems: true });
});
it('should unsubscribe from the target\'s pagination on onDestroy', () => {
component.target = testTarget;
fixture.detectChanges();
fixture.destroy();
const emitNewPaginationEvent = () => {
let newPagination = { maxItems: 1, skipCount: 0, totalItems: 2, hasMoreItems: true };
testTarget.pagination.next(newPagination);
};
expect(emitNewPaginationEvent).not.toThrow();
});
});
}); });

View File

@@ -19,15 +19,19 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
EventEmitter, EventEmitter,
Input, Input,
OnInit, OnInit,
Output, Output,
OnDestroy,
ViewEncapsulation ViewEncapsulation
} from '@angular/core'; } from '@angular/core';
import { PaginatedComponent } from './paginated-component.interface';
import { PaginationQueryParams } from './pagination-query-params.interface';
import { Pagination } from 'alfresco-js-api'; import { Pagination } from 'alfresco-js-api';
import { Subscription } from 'rxjs/Subscription';
@Component({ @Component({
selector: 'adf-infinite-pagination', selector: 'adf-infinite-pagination',
@@ -37,7 +41,7 @@ import { Pagination } from 'alfresco-js-api';
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class InfinitePaginationComponent implements OnInit { export class InfinitePaginationComponent implements OnInit, OnDestroy {
static DEFAULT_PAGE_SIZE: number = 25; static DEFAULT_PAGE_SIZE: number = 25;
@@ -49,6 +53,9 @@ export class InfinitePaginationComponent implements OnInit {
@Input() @Input()
pagination: Pagination; pagination: Pagination;
@Input()
target: PaginatedComponent;
@Input() @Input()
pageSize: number = InfinitePaginationComponent.DEFAULT_PAGE_SIZE; pageSize: number = InfinitePaginationComponent.DEFAULT_PAGE_SIZE;
@@ -58,7 +65,19 @@ export class InfinitePaginationComponent implements OnInit {
@Output() @Output()
loadMore: EventEmitter<Pagination> = new EventEmitter<Pagination>(); loadMore: EventEmitter<Pagination> = new EventEmitter<Pagination>();
private paginationSubscription: Subscription;
constructor(private cdr: ChangeDetectorRef) {}
ngOnInit() { ngOnInit() {
if (this.target) {
this.paginationSubscription = this.target.pagination.subscribe(page => {
this.pagination = page;
this.pageSize = page.maxItems;
this.cdr.detectChanges();
});
}
if (!this.pagination) { if (!this.pagination) {
this.pagination = InfinitePaginationComponent.DEFAULT_PAGINATION; this.pagination = InfinitePaginationComponent.DEFAULT_PAGINATION;
} }
@@ -67,5 +86,15 @@ export class InfinitePaginationComponent implements OnInit {
onLoadMore() { onLoadMore() {
this.pagination.skipCount += this.pageSize; this.pagination.skipCount += this.pageSize;
this.loadMore.next(this.pagination); this.loadMore.next(this.pagination);
if (this.target) {
this.target.updatePagination(<PaginationQueryParams> this.pagination);
}
}
ngOnDestroy() {
if (this.paginationSubscription) {
this.paginationSubscription.unsubscribe();
}
} }
} }

View File

@@ -16,14 +16,12 @@
*/ */
import { Pagination } from 'alfresco-js-api'; import { Pagination } from 'alfresco-js-api';
import { Subject } from 'rxjs/Subject'; import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { PaginationQueryParams } from './pagination-query-params.interface'; import { PaginationQueryParams } from './pagination-query-params.interface';
export interface PaginatedComponent { export interface PaginatedComponent {
pagination: BehaviorSubject<Pagination>;
pagination: Subject<Pagination>;
supportedPageSizes: number[]; supportedPageSizes: number[];
updatePagination(params: PaginationQueryParams); updatePagination(params: PaginationQueryParams);
} }

View File

@@ -27,7 +27,7 @@ import { TranslateLoaderService } from '../services/translate-loader.service';
import { TranslationService } from '../services/translation.service'; import { TranslationService } from '../services/translation.service';
import { PaginationComponent } from './pagination.component'; import { PaginationComponent } from './pagination.component';
import { PaginatedComponent } from './public-api'; import { PaginatedComponent } from './public-api';
import { Subject } from 'rxjs/Subject'; import { BehaviorSubject } from 'rxjs/BehaviorSubject';
class FakePaginationInput implements Pagination { class FakePaginationInput implements Pagination {
count: number = 25; count: number = 25;
@@ -279,7 +279,7 @@ describe('PaginationComponent', () => {
const pagination: Pagination = {}; const pagination: Pagination = {};
const customComponent = <PaginatedComponent> { const customComponent = <PaginatedComponent> {
pagination: new Subject<Pagination>() pagination: new BehaviorSubject<Pagination>({})
}; };
component.target = customComponent; component.target = customComponent;
@@ -294,7 +294,7 @@ describe('PaginationComponent', () => {
const pagination2: Pagination = {}; const pagination2: Pagination = {};
const customComponent = <PaginatedComponent> { const customComponent = <PaginatedComponent> {
pagination: new Subject<Pagination>() pagination: new BehaviorSubject<Pagination>({})
}; };
component.target = customComponent; component.target = customComponent;
@@ -309,7 +309,7 @@ describe('PaginationComponent', () => {
it('should send pagination event to paginated component', () => { it('should send pagination event to paginated component', () => {
const customComponent = <PaginatedComponent> { const customComponent = <PaginatedComponent> {
pagination: new Subject<Pagination>(), pagination: new BehaviorSubject<Pagination>({}),
updatePagination() {}, updatePagination() {},
supportedPageSizes: [] supportedPageSizes: []
}; };