diff --git a/lib/core/pagination/pagination.component.spec.ts b/lib/core/pagination/pagination.component.spec.ts index 010d1e5de4..7f046a70b3 100644 --- a/lib/core/pagination/pagination.component.spec.ts +++ b/lib/core/pagination/pagination.component.spec.ts @@ -26,11 +26,11 @@ import { CoreTestingModule } from '../testing/core.testing.module'; import { TranslateModule } from '@ngx-translate/core'; class FakePaginationInput implements Pagination { - count: number = 25; - hasMoreItems: boolean; - totalItems: number = null; - skipCount: number = null; - maxItems: number = 25; + count = 25; + hasMoreItems = false; + totalItems = 0; + skipCount = 0; + maxItems = 25; constructor(pagesCount: number, currentPage: number, lastPageItems: number) { this.totalItems = ((pagesCount - 1) * this.maxItems) + lastPageItems; @@ -54,19 +54,23 @@ describe('PaginationComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(PaginationComponent); component = fixture.componentInstance; + fixture.detectChanges(); }); afterEach(() => { fixture.destroy(); }); - it('should have an "empty" class if no items present', () => { + it('should have an "empty" class if no items present', async (done) => { fixture.detectChanges(); + await fixture.whenStable(); + expect(fixture.nativeElement.classList.contains('adf-pagination__empty')).toBeTruthy(); + done(); }); describe('Single page', () => { - beforeEach(() => { + beforeEach(async () => { component.pagination = new FakePaginationInput(1, 1, 10); }); @@ -238,21 +242,19 @@ describe('PaginationComponent', () => { it('has defaults', () => { component.ngOnInit(); - const { - current, lastPage, isFirstPage, isLastPage, - next, previous, range, pages - } = component; + expect(component.lastPage).toBe(1, 'lastPage'); + expect(component.previous).toBe(1, 'previous'); + expect(component.current).toBe(1, 'current'); + expect(component.next).toBe(1, 'next'); - expect(lastPage).toBe(1, 'lastPage'); - expect(previous).toBe(1, 'previous'); - expect(current).toBe(1, 'current'); - expect(next).toBe(1, 'next'); + expect(component.isFirstPage).toBe(true, 'isFirstPage'); + expect(component.isLastPage).toBe(true, 'isLastPage'); - expect(isFirstPage).toBe(true, 'isFirstPage'); - expect(isLastPage).toBe(true, 'isLastPage'); + // tslint:disable-next-line: no-console + console.log(JSON.stringify(component.pagination)); - expect(range).toEqual([ 0, 0 ], 'range'); - expect(pages).toEqual([ 1 ], 'pages'); + expect(component.range).toEqual([ 0, 0 ], 'range'); + expect(component.pages).toEqual([ 1 ], 'pages'); }); }); diff --git a/lib/core/pagination/pagination.component.ts b/lib/core/pagination/pagination.component.ts index 499eb2b1a5..ec6c75ad83 100644 --- a/lib/core/pagination/pagination.component.ts +++ b/lib/core/pagination/pagination.component.ts @@ -15,12 +15,7 @@ * limitations under the License. */ -import { - ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, - ChangeDetectorRef, OnDestroy, HostBinding -} from '@angular/core'; - -import { Pagination } from '@alfresco/js-api'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy, ElementRef, ChangeDetectionStrategy, ChangeDetectorRef, Renderer2 } from '@angular/core'; import { PaginatedComponent } from './paginated-component.interface'; import { PaginationComponentInterface } from './pagination-component.interface'; import { Subject } from 'rxjs'; @@ -29,6 +24,12 @@ import { UserPreferencesService, UserPreferenceValues } from '../services/user-p import { takeUntil } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; +export type PaginationAction = + | 'NEXT_PAGE' + | 'PREV_PAGE' + | 'CHANGE_PAGE_SIZE' + | 'CHANGE_PAGE_NUMBER'; + @Component({ selector: 'adf-pagination', host: { 'class': 'adf-pagination' }, @@ -38,20 +39,18 @@ import { TranslateService } from '@ngx-translate/core'; encapsulation: ViewEncapsulation.None }) export class PaginationComponent implements OnInit, OnDestroy, PaginationComponentInterface { - - static DEFAULT_PAGINATION: Pagination = new Pagination({ + static DEFAULT_PAGINATION: PaginationModel = { skipCount: 0, maxItems: 25, - totalItems: 0 - }); - - static ACTIONS = { - NEXT_PAGE: 'NEXT_PAGE', - PREV_PAGE: 'PREV_PAGE', - CHANGE_PAGE_SIZE: 'CHANGE_PAGE_SIZE', - CHANGE_PAGE_NUMBER: 'CHANGE_PAGE_NUMBER' + totalItems: 0, + count: 0, + hasMoreItems: false }; + private _pagination: PaginationModel; + private _isEmpty = true; + private _hasItems = false; + /** Component that provides custom pagination support. */ @Input() target: PaginatedComponent; @@ -60,40 +59,70 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone @Input() supportedPageSizes: number[]; + get pagination(): PaginationModel { + return this._pagination; + } + /** Pagination object. */ @Input() - pagination: PaginationModel = PaginationComponent.DEFAULT_PAGINATION; + set pagination(value: PaginationModel) { + value = value || PaginationComponent.DEFAULT_PAGINATION; + + this._pagination = value; + this._hasItems = value && value.count > 0; + this._isEmpty = !this.hasItems; + + // TODO: Angular 10 workaround for HostBinding bug + if (this._isEmpty) { + this.renderer.addClass(this.elementRef.nativeElement, 'adf-pagination__empty'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'adf-pagination__empty'); + } + + this.cdr.detectChanges(); + } /** Emitted when pagination changes in any way. */ @Output() - change: EventEmitter = new EventEmitter(); + change = new EventEmitter(); /** Emitted when the page number changes. */ @Output() - changePageNumber: EventEmitter = new EventEmitter(); + changePageNumber = new EventEmitter(); /** Emitted when the page size changes. */ @Output() - changePageSize: EventEmitter = new EventEmitter(); + changePageSize = new EventEmitter(); /** Emitted when the next page is requested. */ @Output() - nextPage: EventEmitter = new EventEmitter(); + nextPage = new EventEmitter(); /** Emitted when the previous page is requested. */ @Output() - prevPage: EventEmitter = new EventEmitter(); + prevPage = new EventEmitter(); private onDestroy$ = new Subject(); - constructor(private cdr: ChangeDetectorRef, private userPreferencesService: UserPreferencesService, private translate: TranslateService) { + constructor( + private elementRef: ElementRef, + private renderer: Renderer2, + private cdr: ChangeDetectorRef, + private userPreferencesService: UserPreferencesService, + private translate: TranslateService) { } ngOnInit() { this.userPreferencesService .select(UserPreferenceValues.PaginationSize) .pipe(takeUntil(this.onDestroy$)) - .subscribe(pagSize => this.pagination.maxItems = pagSize); + .subscribe(maxItems => { + this.pagination = { + ...PaginationComponent.DEFAULT_PAGINATION, + ...this.pagination, + maxItems + }; + }); if (!this.supportedPageSizes) { this.supportedPageSizes = this.userPreferencesService.supportedPageSizes; @@ -107,13 +136,16 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone this.goPrevious(); } - this.pagination = pagination; - this.cdr.detectChanges(); + this.pagination = { + ...pagination + }; }); } if (!this.pagination) { - this.pagination = PaginationComponent.DEFAULT_PAGINATION; + this.pagination = { + ...PaginationComponent.DEFAULT_PAGINATION + }; } } @@ -153,24 +185,24 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone } get hasItems(): boolean { - return this.pagination && this.pagination.count > 0; + return this._hasItems; } - @HostBinding('class.adf-pagination__empty') + // TODO: not working correctly in Angular 10 + // @HostBinding('class.adf-pagination__empty') get isEmpty(): boolean { - return !this.hasItems; + return this._isEmpty; } get range(): number[] { const { skipCount, maxItems, totalItems } = this.pagination; - const { isLastPage } = this; let start = 0; if (totalItems || totalItems !== 0) { start = skipCount + 1; } - const end = isLastPage ? totalItems : skipCount + maxItems; + const end = this.isLastPage ? totalItems : skipCount + maxItems; return [start, end]; } @@ -183,13 +215,16 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone get itemRangeText(): string { const rangeString = this.range.join('-'); + let translation = this.translate.instant('CORE.PAGINATION.ITEMS_RANGE', { range: rangeString, total: this.pagination.totalItems }); + if (!this.pagination.totalItems) { translation = translation.substr(0, translation.indexOf(rangeString) + rangeString.length); } + return translation; } @@ -197,12 +232,13 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone if (this.hasItems) { const maxItems = this.pagination.maxItems; const skipCount = (this.next - 1) * maxItems; - this.pagination.skipCount = skipCount; - this.handlePaginationEvent(PaginationComponent.ACTIONS.NEXT_PAGE, { - skipCount, - maxItems - }); + this.pagination = { + ...this.pagination, + skipCount + }; + + this.handlePaginationEvent('NEXT_PAGE'); } } @@ -210,12 +246,13 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone if (this.hasItems) { const maxItems = this.pagination.maxItems; const skipCount = (this.previous - 1) * maxItems; - this.pagination.skipCount = skipCount; - this.handlePaginationEvent(PaginationComponent.ACTIONS.PREV_PAGE, { - skipCount, - maxItems - }); + this.pagination = { + ...this.pagination, + skipCount + }; + + this.handlePaginationEvent('PREV_PAGE'); } } @@ -223,22 +260,25 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone if (this.hasItems) { const maxItems = this.pagination.maxItems; const skipCount = (pageNumber - 1) * maxItems; - this.pagination.skipCount = skipCount; - this.handlePaginationEvent(PaginationComponent.ACTIONS.CHANGE_PAGE_NUMBER, { - skipCount, - maxItems - }); + this.pagination = { + ...this.pagination, + skipCount + }; + + this.handlePaginationEvent('CHANGE_PAGE_NUMBER'); } } onChangePageSize(maxItems: number) { - this.pagination.skipCount = 0; - this.userPreferencesService.paginationSize = maxItems; - this.handlePaginationEvent(PaginationComponent.ACTIONS.CHANGE_PAGE_SIZE, { + this.pagination = { + ...this.pagination, skipCount: 0, maxItems - }); + }; + + this.userPreferencesService.paginationSize = maxItems; + this.handlePaginationEvent('CHANGE_PAGE_SIZE'); } ngOnDestroy() { @@ -246,45 +286,29 @@ export class PaginationComponent implements OnInit, OnDestroy, PaginationCompone this.onDestroy$.complete(); } - handlePaginationEvent(action: string, params: PaginationModel) { - const { - NEXT_PAGE, - PREV_PAGE, - CHANGE_PAGE_NUMBER, - CHANGE_PAGE_SIZE - } = PaginationComponent.ACTIONS; + handlePaginationEvent(action: PaginationAction) { + const paginationModel = { ...this.pagination }; - const { - change, - changePageNumber, - changePageSize, - nextPage, - prevPage, - pagination - } = this; - - const paginationModel: PaginationModel = Object.assign({}, pagination, params); - - if (action === NEXT_PAGE) { - nextPage.emit(paginationModel); + if (action === 'NEXT_PAGE') { + this.nextPage.emit(paginationModel); } - if (action === PREV_PAGE) { - prevPage.emit(paginationModel); + if (action === 'PREV_PAGE') { + this.prevPage.emit(paginationModel); } - if (action === CHANGE_PAGE_NUMBER) { - changePageNumber.emit(paginationModel); + if (action === 'CHANGE_PAGE_NUMBER') { + this.changePageNumber.emit(paginationModel); } - if (action === CHANGE_PAGE_SIZE) { - changePageSize.emit(paginationModel); + if (action === 'CHANGE_PAGE_SIZE') { + this.changePageSize.emit(paginationModel); } - change.emit(params); + this.change.emit(paginationModel); if (this.target) { - this.target.updatePagination(params); + this.target.updatePagination(paginationModel); } } }