From 89a7b0c4b00736a0ab2f0f82eb3104bde3a0cd35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Popovics=20Andr=C3=A1s?= Date: Mon, 22 Jan 2018 10:59:05 +0000 Subject: [PATCH] [ADF-2157] Infinite pagination target supporting (#2856) * Infinite pagination target supporting * Updating documentation --- .../app/components/files/files.component.html | 6 +- docs/infinite-pagination.component.md | 13 ++ .../components/document-list.component.ts | 5 +- .../infinite-pagination.component.spec.ts | 155 ++++++++++++------ .../infinite-pagination.component.ts | 33 +++- .../paginated-component.interface.ts | 6 +- .../pagination/pagination.component.spec.ts | 8 +- 7 files changed, 158 insertions(+), 68 deletions(-) diff --git a/demo-shell/src/app/components/files/files.component.html b/demo-shell/src/app/components/files/files.component.html index 372589bc94..facb0ca9d7 100644 --- a/demo-shell/src/app/components/files/files.component.html +++ b/demo-shell/src/app/components/files/files.component.html @@ -291,10 +291,8 @@ + [target]="documentList" + [loading]="documentList.infiniteLoading"> {{ 'ADF-DOCUMENT-LIST.LAYOUT.LOAD_MORE' | translate }} diff --git a/docs/infinite-pagination.component.md b/docs/infinite-pagination.component.md index 85880b48c4..0968cdb98b 100644 --- a/docs/infinite-pagination.component.md +++ b/docs/infinite-pagination.component.md @@ -14,6 +14,17 @@ Adds "infinite" pagination to the component it is used with. ``` +## Integrating with Document List + +```html + + + + +``` + ### Properties | 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 | | pageSize | number | `InfinitePaginationComponent.DEFAULT_PAGE_SIZE` | Number of items that are added with each "load more" event | | loading | boolean | false | | +| target | PaginatedComponent | | Component that provides custom pagination support | + ### Events diff --git a/lib/content-services/document-list/components/document-list.component.ts b/lib/content-services/document-list/components/document-list.component.ts index 72a7000374..4c6b380f77 100644 --- a/lib/content-services/document-list/components/document-list.component.ts +++ b/lib/content-services/document-list/components/document-list.component.ts @@ -41,6 +41,7 @@ import { } from 'alfresco-js-api'; import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { presetsDefaultModel } from '../models/preset.model'; import { ShareDataRow } from './../data/share-data-row.model'; import { ShareDataTableAdapter } from './../data/share-datatable-adapter'; @@ -220,7 +221,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte noPermission: boolean = false; selection = new Array(); - pagination = new Subject(); + pagination: BehaviorSubject; private layoutPresets = {}; private currentNodeAllowableOperations: string[] = []; @@ -236,7 +237,7 @@ export class DocumentListComponent implements OnInit, OnChanges, OnDestroy, Afte private preferences: UserPreferencesService) { this.maxItems = this.preferences.paginationSize; - this.pagination.next( { + this.pagination = new BehaviorSubject( { maxItems: this.preferences.paginationSize, skipCount: 0, totalItems: 0, diff --git a/lib/core/pagination/infinite-pagination.component.spec.ts b/lib/core/pagination/infinite-pagination.component.spec.ts index 8243d80cbb..6b5a9c908b 100644 --- a/lib/core/pagination/infinite-pagination.component.spec.ts +++ b/lib/core/pagination/infinite-pagination.component.spec.ts @@ -21,6 +21,8 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Pagination } from 'alfresco-js-api'; import { MaterialModule } from '../material.module'; import { InfinitePaginationComponent } from './infinite-pagination.component'; +import { PaginatedComponent } from './paginated-component.interface'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; describe('InfinitePaginationComponent', () => { @@ -55,62 +57,111 @@ describe('InfinitePaginationComponent', () => { TestBed.resetTestingModule(); }); - it('should show the loading spinner if loading', () => { - pagination.hasMoreItems = true; - component.pagination = pagination; - component.isLoading = true; - fixture.detectChanges(); + describe('Standalone', () => { - let loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]')); - expect(loadingSpinner).not.toBeNull(); - }); + it('should show the loading spinner if loading', () => { + pagination.hasMoreItems = true; + component.pagination = pagination; + component.isLoading = true; + fixture.detectChanges(); - it('should NOT show the loading spinner if NOT loading', () => { - 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 loadingSpinner = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-spinner"]')); + expect(loadingSpinner).not.toBeNull(); }); - let loadMoreButton = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]')); - loadMoreButton.triggerEventHandler('click', {}); + it('should NOT show the loading spinner if NOT loading', () => { + 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), + 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(); + }); + }); }); diff --git a/lib/core/pagination/infinite-pagination.component.ts b/lib/core/pagination/infinite-pagination.component.ts index 912e0e71bd..203b9d8a57 100644 --- a/lib/core/pagination/infinite-pagination.component.ts +++ b/lib/core/pagination/infinite-pagination.component.ts @@ -19,15 +19,19 @@ import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, + OnDestroy, ViewEncapsulation } from '@angular/core'; - +import { PaginatedComponent } from './paginated-component.interface'; +import { PaginationQueryParams } from './pagination-query-params.interface'; import { Pagination } from 'alfresco-js-api'; +import { Subscription } from 'rxjs/Subscription'; @Component({ selector: 'adf-infinite-pagination', @@ -37,7 +41,7 @@ import { Pagination } from 'alfresco-js-api'; changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None }) -export class InfinitePaginationComponent implements OnInit { +export class InfinitePaginationComponent implements OnInit, OnDestroy { static DEFAULT_PAGE_SIZE: number = 25; @@ -49,6 +53,9 @@ export class InfinitePaginationComponent implements OnInit { @Input() pagination: Pagination; + @Input() + target: PaginatedComponent; + @Input() pageSize: number = InfinitePaginationComponent.DEFAULT_PAGE_SIZE; @@ -58,7 +65,19 @@ export class InfinitePaginationComponent implements OnInit { @Output() loadMore: EventEmitter = new EventEmitter(); + private paginationSubscription: Subscription; + + constructor(private cdr: ChangeDetectorRef) {} + ngOnInit() { + if (this.target) { + this.paginationSubscription = this.target.pagination.subscribe(page => { + this.pagination = page; + this.pageSize = page.maxItems; + this.cdr.detectChanges(); + }); + } + if (!this.pagination) { this.pagination = InfinitePaginationComponent.DEFAULT_PAGINATION; } @@ -67,5 +86,15 @@ export class InfinitePaginationComponent implements OnInit { onLoadMore() { this.pagination.skipCount += this.pageSize; this.loadMore.next(this.pagination); + + if (this.target) { + this.target.updatePagination( this.pagination); + } + } + + ngOnDestroy() { + if (this.paginationSubscription) { + this.paginationSubscription.unsubscribe(); + } } } diff --git a/lib/core/pagination/paginated-component.interface.ts b/lib/core/pagination/paginated-component.interface.ts index d745ddda80..34bfd53733 100644 --- a/lib/core/pagination/paginated-component.interface.ts +++ b/lib/core/pagination/paginated-component.interface.ts @@ -16,14 +16,12 @@ */ import { Pagination } from 'alfresco-js-api'; -import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { PaginationQueryParams } from './pagination-query-params.interface'; export interface PaginatedComponent { - - pagination: Subject; + pagination: BehaviorSubject; supportedPageSizes: number[]; updatePagination(params: PaginationQueryParams); - } diff --git a/lib/core/pagination/pagination.component.spec.ts b/lib/core/pagination/pagination.component.spec.ts index b8a22d5515..24c4819f4d 100644 --- a/lib/core/pagination/pagination.component.spec.ts +++ b/lib/core/pagination/pagination.component.spec.ts @@ -27,7 +27,7 @@ import { TranslateLoaderService } from '../services/translate-loader.service'; import { TranslationService } from '../services/translation.service'; import { PaginationComponent } from './pagination.component'; import { PaginatedComponent } from './public-api'; -import { Subject } from 'rxjs/Subject'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; class FakePaginationInput implements Pagination { count: number = 25; @@ -279,7 +279,7 @@ describe('PaginationComponent', () => { const pagination: Pagination = {}; const customComponent = { - pagination: new Subject() + pagination: new BehaviorSubject({}) }; component.target = customComponent; @@ -294,7 +294,7 @@ describe('PaginationComponent', () => { const pagination2: Pagination = {}; const customComponent = { - pagination: new Subject() + pagination: new BehaviorSubject({}) }; component.target = customComponent; @@ -309,7 +309,7 @@ describe('PaginationComponent', () => { it('should send pagination event to paginated component', () => { const customComponent = { - pagination: new Subject(), + pagination: new BehaviorSubject({}), updatePagination() {}, supportedPageSizes: [] };