diff --git a/src/app/examples/grid-graphql.component.ts b/src/app/examples/grid-graphql.component.ts index a90f7b2a5..f03f6f83e 100644 --- a/src/app/examples/grid-graphql.component.ts +++ b/src/app/examples/grid-graphql.component.ts @@ -243,7 +243,7 @@ export class GridGraphqlComponent implements OnInit, OnDestroy { setTimeout(() => { this.graphqlQuery = this.angularGrid.backendService.buildQuery(); resolve(mockedResult); - }, 250); + }, 100); }); } diff --git a/src/app/examples/grid-odata.component.ts b/src/app/examples/grid-odata.component.ts index 0e9467c8d..43e6c1514 100644 --- a/src/app/examples/grid-odata.component.ts +++ b/src/app/examples/grid-odata.component.ts @@ -291,16 +291,17 @@ export class GridOdataComponent implements OnInit { countPropName = (this.odataVersion === 4) ? '@odata.count' : 'odata.count'; } const backendResult = { items: updatedData, [countPropName]: countTotalItems, query }; - console.log('Backend Result', backendResult); + // console.log('Backend Result', backendResult); resolve(backendResult); - }, 250); + }, 100); }); }); } /** Dispatched event of a Grid State Changed event */ gridStateChanged(gridStateChanges: GridStateChange) { - console.log('Client sample, Grid State changed:: ', gridStateChanges); + // console.log('Client sample, Grid State changed:: ', gridStateChanges); + console.log('Client sample, Grid State changed:: ', gridStateChanges.change); } // THE FOLLOWING METHODS ARE ONLY FOR DEMO PURPOSES DO NOT USE THIS CODE diff --git a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts index 1ba682895..d7d6230e4 100644 --- a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts +++ b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts @@ -20,7 +20,7 @@ import { SharedService, SortService, } from '../../services'; -import { Column, CurrentFilter, CurrentSorter, GraphqlPaginatedResult, GraphqlServiceApi, GraphqlServiceOption, GridOption, GridState, GridStateChange, GridStateType, Pagination } from '../../models'; +import { Column, CurrentFilter, CurrentSorter, GraphqlPaginatedResult, GraphqlServiceApi, GraphqlServiceOption, GridOption, GridState, GridStateChange, GridStateType, Pagination, ServicePagination } from '../../models'; import { Filters } from '../../filters'; import { Editors } from '../../editors'; import * as utilities from '../../services/backend-utilities'; @@ -116,10 +116,17 @@ const gridStateServiceStub = { } as unknown as GridStateService; const paginationServiceStub = { + totalItems: 0, init: jest.fn(), dispose: jest.fn(), + onPaginationChanged: new Subject(), } as unknown as PaginationService; +Object.defineProperty(paginationServiceStub, 'totalItems', { + get: jest.fn(() => 0), + set: jest.fn() +}); + const resizerServiceStub = { init: jest.fn(), dispose: jest.fn(), @@ -676,6 +683,22 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = expect(component.gridOptions.backendServiceApi.internalPostProcess).toEqual(expect.any(Function)); }); + it('should execute the "internalPostProcess" callback and expect totalItems to be updated in the PaginationService when "refreshGridData" is called on the 2nd time', () => { + jest.spyOn(component.gridOptions.backendServiceApi.service, 'getDatasetName').mockReturnValue('users'); + const refreshSpy = jest.spyOn(component, 'refreshGridData'); + const paginationSpy = jest.spyOn(paginationServiceStub, 'totalItems', 'set'); + const mockDataset = [{ firstName: 'John' }, { firstName: 'Jane' }]; + + component.ngAfterViewInit(); + component.gridOptions.backendServiceApi.internalPostProcess({ data: { users: { nodes: mockDataset, totalCount: mockDataset.length } } } as GraphqlPaginatedResult); + component.refreshGridData(mockDataset, 1); + component.refreshGridData(mockDataset, 1); + + expect(refreshSpy).toHaveBeenCalledTimes(3); + expect(paginationSpy).toHaveBeenCalledWith(2); + expect(component.gridOptions.backendServiceApi.internalPostProcess).toEqual(expect.any(Function)); + }); + it('should execute the "internalPostProcess" callback method that was created by "createBackendApiInternalPostProcessCallback" without Pagination (when disabled)', () => { component.gridOptions.enablePagination = false; jest.spyOn(component.gridOptions.backendServiceApi.service, 'getDatasetName').mockReturnValue('users'); @@ -1100,6 +1123,26 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = }); }); + it('should call trigger a gridStage change event when "onPaginationChanged" from the Pagination Service is triggered', () => { + const mockPagination = { pageNumber: 2, pageSize: 20, pageSizes: [5, 10, 15, 20] } as Pagination; + const mockServicePagination = { + ...mockPagination, + dataFrom: 5, + dataTo: 10, + pageCount: 1, + } as ServicePagination; + const spy = jest.spyOn(gridStateServiceStub.onGridStateChanged, 'next'); + jest.spyOn(gridStateServiceStub, 'getCurrentGridState').mockReturnValue({ columns: [], pagination: mockPagination } as GridState); + + component.ngAfterViewInit(); + paginationServiceStub.onPaginationChanged.next(mockServicePagination); + + expect(spy).toHaveBeenCalledWith({ + change: { newValues: mockPagination, type: GridStateType.pagination }, + gridState: { columns: [], pagination: mockPagination } + }); + }); + it('should call trigger a gridStage change and reset selected rows when pagination change is triggered and "enableRowSelection" is set', () => { const mockPagination = { pageNumber: 2, pageSize: 20 } as Pagination; const stateChangedSpy = jest.spyOn(gridStateServiceStub.onGridStateChanged, 'next'); diff --git a/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination-without-translate.component.spec.ts b/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination-without-translate.component.spec.ts index 542f4cd92..6ebda7067 100644 --- a/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination-without-translate.component.spec.ts +++ b/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination-without-translate.component.spec.ts @@ -3,56 +3,19 @@ import { By } from '@angular/platform-browser'; import { Subject } from 'rxjs'; import { SlickPaginationComponent } from '../slick-pagination.component'; -import { Column, GridOption, Locale, Pager } from '../../models'; +import { Locale, ServicePagination } from '../../models'; import { PaginationService } from '../../services'; -const dataviewStub = { - onPagingInfoChanged: jest.fn(), - onRowCountChanged: jest.fn(), - onRowsChanged: jest.fn(), -}; - -const mockBackendService = { - resetPaginationOptions: jest.fn(), - buildQuery: jest.fn(), - updateOptions: jest.fn(), - processOnFilterChanged: jest.fn(), - processOnSortChanged: jest.fn(), - processOnPaginationChanged: jest.fn(), -}; - -const mockGridOption = { - enableAutoResize: true, - enablePagination: true, - backendServiceApi: { - service: mockBackendService, - process: jest.fn(), - options: { - columnDefinitions: [{ id: 'name', field: 'name' }] as Column[], - datasetName: 'user', - } - }, - pagination: { - pageSizes: [10, 15, 20, 25, 30, 40, 50, 75, 100], - pageSize: 25, - totalItems: 85 - } -} as GridOption; - -const gridStub = { - autosizeColumns: jest.fn(), - getColumnIndex: jest.fn(), - getOptions: () => mockGridOption, - getColumns: jest.fn(), - setColumns: jest.fn(), - onColumnsReordered: jest.fn(), - onColumnsResized: jest.fn(), - registerPlugin: jest.fn(), -}; - const paginationServiceStub = { - onPaginationRefreshed: new Subject(), - onPaginationChanged: new Subject(), + dataFrom: 5, + dataTo: 10, + pageNumber: 2, + pageCount: 1, + itemsPerPage: 5, + pageSize: 10, + totalItems: 100, + availablePageSizes: [5, 10, 15, 20], + pageInfoTotalItems: jest.fn(), goToFirstPage: jest.fn(), goToLastPage: jest.fn(), goToNextPage: jest.fn(), @@ -61,12 +24,14 @@ const paginationServiceStub = { changeItemPerPage: jest.fn(), dispose: jest.fn(), init: jest.fn(), + onPaginationRefreshed: new Subject(), + onPaginationChanged: new Subject(), } as unknown as PaginationService; describe('without ngx-translate', () => { let fixture: ComponentFixture; let component: SlickPaginationComponent; - let mockPager: Pager; + let mockServicePagination: ServicePagination; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -82,25 +47,16 @@ describe('without ngx-translate', () => { fixture = TestBed.createComponent(SlickPaginationComponent); component = fixture.debugElement.componentInstance; - mockPager = { - from: 5, - to: 10, - itemsPerPage: 5, + mockServicePagination = { + dataFrom: 5, + dataTo: 10, + pageSize: 5, pageCount: 1, pageNumber: 2, - availablePageSizes: [5, 10, 15, 20], + pageSizes: [5, 10, 15, 20], totalItems: 100, }; component.enableTranslate = false; - component.grid = gridStub; - component.options = { - pageNumber: mockPager.pageNumber, - pageSizes: mockPager.availablePageSizes, - pageSize: mockPager.itemsPerPage, - totalItems: mockPager.totalItems, - }; - component.backendServiceApi = mockGridOption.backendServiceApi; - component.totalItems = mockPager.totalItems; component.locales = { TEXT_ITEMS_PER_PAGE: 'items per page', TEXT_ITEMS: 'items', @@ -108,8 +64,7 @@ describe('without ngx-translate', () => { TEXT_PAGE: 'page' } as Locale; - paginationServiceStub.init(gridStub, dataviewStub, component.options, component.backendServiceApi); - paginationServiceStub.onPaginationChanged.next(mockPager); + paginationServiceStub.onPaginationChanged.next(mockServicePagination); fixture.detectChanges(); })); @@ -137,7 +92,6 @@ describe('without ngx-translate', () => { it('should have defined locale and expect new text in the UI', (done) => { component.enableTranslate = false; fixture.detectChanges(); - paginationServiceStub.onPaginationRefreshed.next(true); setTimeout(() => { fixture.detectChanges(); diff --git a/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination.component.spec.ts b/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination.component.spec.ts index 6f8fc2aeb..4a4dc7618 100644 --- a/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination.component.spec.ts +++ b/src/app/modules/angular-slickgrid/components/__tests__/slick-pagination.component.spec.ts @@ -4,56 +4,19 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { Subject } from 'rxjs'; import { SlickPaginationComponent } from '../slick-pagination.component'; -import { Column, GridOption, Pager } from '../../models'; +import { ServicePagination } from '../../models'; import { PaginationService } from '../../services'; -const dataviewStub = { - onPagingInfoChanged: jest.fn(), - onRowCountChanged: jest.fn(), - onRowsChanged: jest.fn(), -}; - -const mockBackendService = { - resetPaginationOptions: jest.fn(), - buildQuery: jest.fn(), - updateOptions: jest.fn(), - processOnFilterChanged: jest.fn(), - processOnSortChanged: jest.fn(), - processOnPaginationChanged: jest.fn(), -}; - -const mockGridOption = { - enableAutoResize: true, - enablePagination: true, - backendServiceApi: { - service: mockBackendService, - process: jest.fn(), - options: { - columnDefinitions: [{ id: 'name', field: 'name' }] as Column[], - datasetName: 'user', - } - }, - pagination: { - pageSizes: [10, 15, 20, 25, 30, 40, 50, 75, 100], - pageSize: 25, - totalItems: 85 - } -} as GridOption; - -const gridStub = { - autosizeColumns: jest.fn(), - getColumnIndex: jest.fn(), - getOptions: () => mockGridOption, - getColumns: jest.fn(), - setColumns: jest.fn(), - onColumnsReordered: jest.fn(), - onColumnsResized: jest.fn(), - registerPlugin: jest.fn(), -}; - const paginationServiceStub = { - onPaginationRefreshed: new Subject(), - onPaginationChanged: new Subject(), + dataFrom: 5, + dataTo: 10, + pageNumber: 2, + pageCount: 1, + itemsPerPage: 5, + pageSize: 10, + totalItems: 100, + availablePageSizes: [5, 10, 15, 20], + pageInfoTotalItems: jest.fn(), goToFirstPage: jest.fn(), goToLastPage: jest.fn(), goToNextPage: jest.fn(), @@ -62,13 +25,15 @@ const paginationServiceStub = { changeItemPerPage: jest.fn(), dispose: jest.fn(), init: jest.fn(), + onPaginationRefreshed: new Subject(), + onPaginationChanged: new Subject(), } as unknown as PaginationService; describe('App Component', () => { let fixture: ComponentFixture; let component: SlickPaginationComponent; let translate: TranslateService; - let mockPager: Pager; + let mockServicePagination: ServicePagination; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -113,28 +78,17 @@ describe('App Component', () => { describe('Integration Tests', () => { beforeEach(() => { - mockPager = { - from: 5, - to: 10, - itemsPerPage: 5, + mockServicePagination = { + dataFrom: 5, + dataTo: 10, + pageSize: 5, pageCount: 1, pageNumber: 2, - availablePageSizes: [5, 10, 15, 20], + pageSizes: [5, 10, 15, 20], totalItems: 100, }; component.enableTranslate = true; - component.grid = gridStub; - component.options = { - pageNumber: mockPager.pageNumber, - pageSizes: mockPager.availablePageSizes, - pageSize: mockPager.itemsPerPage, - totalItems: mockPager.totalItems, - }; - component.backendServiceApi = mockGridOption.backendServiceApi; - component.totalItems = mockPager.totalItems; - - paginationServiceStub.init(gridStub, dataviewStub, component.options, component.backendServiceApi); - paginationServiceStub.onPaginationChanged.next(mockPager); + paginationServiceStub.onPaginationChanged.next(mockServicePagination); fixture.detectChanges(); }); @@ -145,6 +99,16 @@ describe('App Component', () => { fixture.destroy(); }); + describe('getters & setters', () => { + // the following 2 setters unit test are simply here for code coverage, + // these 2 setters don't actually do anything + it('should use the "itemsPerPage" setter but do nothing with it', () => { + fixture.detectChanges(); + component.pageNumber = 3; + expect(component.pageNumber).toBe(2); + }); + }); + it('should create a the Slick-Pagination component in the DOM', () => { fixture.detectChanges(); diff --git a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.html b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.html index 8496748b6..61dc76c50 100644 --- a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.html +++ b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.html @@ -4,9 +4,7 @@ + [enableTranslate]="gridOptions.enableTranslate" [locales]="locales"> diff --git a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts index 3a3184489..1d058d06a 100644 --- a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts +++ b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts @@ -29,6 +29,7 @@ import { Locale, Metrics, Pagination, + ServicePagination, SlickEventHandler, } from './../models/index'; import { FilterFactory } from '../filters/filterFactory'; @@ -135,6 +136,10 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn showCustomFooter = false; showPagination = false; totalItems = 0; + paginationData: { + enableTranslate: boolean; + locales: Locale; + }; subscriptions: Subscription[] = []; @Output() onAngularGridCreated = new EventEmitter(); @@ -300,18 +305,18 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn * On a Pagination changed, we will trigger a Grid State changed with the new pagination info * Also if we use Row Selection or the Checkbox Selector, we need to reset any selection */ - paginationChanged(pagination: Pagination) { + paginationChanged(pagination: ServicePagination) { if (this.gridOptions.enableRowSelection || this.gridOptions.enableCheckboxSelector) { this.gridService.setSelectedRows([]); } + const { pageNumber, pageSize, pageSizes } = pagination; if (this.sharedService) { - const { pageNumber, pageSize } = pagination; if (pageSize) { - this.sharedService.currentPagination = { pageNumber, pageSize }; + this.sharedService.currentPagination = { pageNumber, pageSize, pageSizes }; } } this.gridStateService.onGridStateChanged.next({ - change: { newValues: pagination, type: GridStateType.pagination }, + change: { newValues: { pageNumber, pageSize, pageSizes }, type: GridStateType.pagination }, gridState: this.gridStateService.getCurrentGridState() }); } @@ -347,6 +352,9 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn // initialize the Pagination Service with new pagination options (which might have presets) if (!this._isPaginationInitialized) { this.initializePaginationService(paginationOptions); + } else { + // update the pagination service with the new total + this.paginationService.totalItems = this.totalItems; } } @@ -643,8 +651,16 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn } private initializePaginationService(paginationOptions: Pagination) { - this.paginationService.init(this.grid, this.dataView, paginationOptions, this.backendServiceApi); - this._isPaginationInitialized = true; + if (this.gridOptions) { + this.paginationData = { + enableTranslate: this.gridOptions.enableTranslate, + locales: this.locales, + }; + this.paginationService.totalItems = this.totalItems; + this.paginationService.onPaginationChanged.subscribe((changes: ServicePagination) => this.paginationChanged(changes)); + this.paginationService.init(this.grid, this.dataView, paginationOptions, this.backendServiceApi); + this._isPaginationInitialized = true; + } this.cd.detectChanges(); } diff --git a/src/app/modules/angular-slickgrid/components/slick-pagination.component.html b/src/app/modules/angular-slickgrid/components/slick-pagination.component.html index d4762b2f7..fcdbc69b7 100644 --- a/src/app/modules/angular-slickgrid/components/slick-pagination.component.html +++ b/src/app/modules/angular-slickgrid/components/slick-pagination.component.html @@ -1,13 +1,13 @@ -
+
- + {{textItemsPerPage}}, - + - {{pager?.from}}-{{pager?.to}} + {{dataFrom}}-{{dataTo}} {{textOf}} - {{pager?.totalItems}} {{textItems}} + {{totalItems}} {{textItems}} diff --git a/src/app/modules/angular-slickgrid/components/slick-pagination.component.ts b/src/app/modules/angular-slickgrid/components/slick-pagination.component.ts index a46793a3b..4d86c75e3 100644 --- a/src/app/modules/angular-slickgrid/components/slick-pagination.component.ts +++ b/src/app/modules/angular-slickgrid/components/slick-pagination.component.ts @@ -1,8 +1,8 @@ -import { Component, EventEmitter, Input, OnDestroy, Optional, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, Optional, Output, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; -import { BackendServiceApi, Locale, Pager, Pagination } from './../models/index'; +import { Locale, Pagination } from './../models/index'; import { PaginationService } from '../services/pagination.service'; import { unsubscribeAllObservables } from '../services/utilities'; @@ -10,33 +10,11 @@ import { unsubscribeAllObservables } from '../services/utilities'; selector: 'slick-pagination', templateUrl: './slick-pagination.component.html' }) -export class SlickPaginationComponent implements OnDestroy { - private _isFirstRender = true; - private _pager: Pager; - private _totalItems: number; +export class SlickPaginationComponent implements OnDestroy, OnInit { private subscriptions: Subscription[] = []; - @Output() onPaginationChanged = new EventEmitter(); @Input() enableTranslate: boolean; - @Input() options: Pagination; - @Input() dataView: any; @Input() locales: Locale; - @Input() backendServiceApi: BackendServiceApi; - @Input() - set totalItems(total: number) { - if (this._isFirstRender || this._totalItems === undefined) { - this._isFirstRender = true; - } - this._totalItems = total; - this._isFirstRender = false; - if (this.paginationService) { - this.paginationService.totalItems = total; - } - } - get totalItems(): number { - return this._totalItems; - } - @Input() grid: any; // text translations (handled by ngx-translate or by custom locale) textItemsPerPage: string; @@ -55,33 +33,47 @@ export class SlickPaginationComponent implements OnDestroy { if (translate && translate.onLangChange) { this.subscriptions.push(this.translate.onLangChange.subscribe(() => this.translateAllUiTexts(this.locales))); } + } - // translate all the text using ngx-translate or custom locales - this.paginationService.onPaginationRefreshed.subscribe(() => this.translateAllUiTexts(this.locales)); + get availablePageSizes(): number[] { + return this.paginationService.availablePageSizes; + } + + get dataFrom(): number { + return this.paginationService.dataFrom; + } - this.paginationService.onPaginationChanged.subscribe(pager => { - this._pager = pager; + get dataTo(): number { + return this.paginationService.dataTo; + } - // emit the changes to the parent component with only necessary properties - if (!this._isFirstRender) { - this.onPaginationChanged.emit({ - pageNumber: this.pager.pageNumber, - pageSizes: this.pager.availablePageSizes, - pageSize: this.pager.itemsPerPage, - totalItems: this.pager.totalItems, - }); - } - }); + get itemsPerPage(): number { + return this.paginationService.itemsPerPage; } - get pager(): Pager { - return this._pager || this.paginationService.pager; + get pageCount(): number { + return this.paginationService.pageCount; + } + + get pageNumber(): number { + return this.paginationService.pageNumber; + } + set pageNumber(page: number) { + // the setter has to be declared but we won't use it, instead we will use the "changeToCurrentPage()" to only update the value after ENTER keydown event + } + + get totalItems() { + return this.paginationService.totalItems; } ngOnDestroy() { this.dispose(); } + ngOnInit() { + this.translateAllUiTexts(this.locales); + } + changeToFirstPage(event: any) { this.paginationService.goToFirstPage(event); } @@ -115,7 +107,6 @@ export class SlickPaginationComponent implements OnDestroy { } dispose() { - this.onPaginationChanged.unsubscribe(); this.paginationService.dispose(); // also unsubscribe all Angular Subscriptions diff --git a/src/app/modules/angular-slickgrid/models/backendService.interface.ts b/src/app/modules/angular-slickgrid/models/backendService.interface.ts index 6e3f87fa2..89a40107a 100644 --- a/src/app/modules/angular-slickgrid/models/backendService.interface.ts +++ b/src/app/modules/angular-slickgrid/models/backendService.interface.ts @@ -46,7 +46,7 @@ export interface BackendService { updateFilters?: (columnFilters: ColumnFilters | CurrentFilter[], isUpdatedByPresetOrDynamically: boolean) => void; /** Update the Pagination component with it's new page number and size */ - updatePagination?: (newPage: number, pageSize: number) => void; + updatePagination?: (newPage: number, pageSize: number, pageSizes?: number[]) => void; /** Update the Sorters options with a set of new options */ updateSorters?: (sortColumns?: ColumnSort[], presetSorters?: CurrentSorter[]) => void; diff --git a/src/app/modules/angular-slickgrid/models/currentPagination.interface.ts b/src/app/modules/angular-slickgrid/models/currentPagination.interface.ts index 8b81f0cd5..88cac7689 100644 --- a/src/app/modules/angular-slickgrid/models/currentPagination.interface.ts +++ b/src/app/modules/angular-slickgrid/models/currentPagination.interface.ts @@ -4,4 +4,7 @@ export interface CurrentPagination { /** Grid page size */ pageSize: number; + + /** The available page sizes */ + pageSizes: number[]; } diff --git a/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts b/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts index 95302280f..4e22f5c2b 100644 --- a/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts +++ b/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts @@ -1,9 +1,9 @@ -import { Column, CurrentFilter, CurrentSorter, GridState, GridStateType, Pagination } from './index'; +import { Column, CurrentFilter, CurrentPagination, CurrentSorter, GridState, GridStateType } from './index'; export interface GridStateChange { /** Changes that were triggered */ change?: { - newValues: Column[] | CurrentFilter[] | CurrentSorter[] | Pagination; + newValues: Column[] | CurrentFilter[] | CurrentSorter[] | CurrentPagination; type: GridStateType; }; diff --git a/src/app/modules/angular-slickgrid/models/index.ts b/src/app/modules/angular-slickgrid/models/index.ts index 7f2e1f85b..95ad22c83 100644 --- a/src/app/modules/angular-slickgrid/models/index.ts +++ b/src/app/modules/angular-slickgrid/models/index.ts @@ -110,7 +110,6 @@ export * from './odataSortingOption.interface'; export * from './onEventArgs.interface'; export * from './operatorString'; export * from './operatorType.enum'; -export * from './pager.interface'; export * from './pagination.interface'; export * from './paginationChangedArgs.interface'; export * from './queryArgument.interface'; @@ -119,6 +118,7 @@ export * from './rowMoveManager.interface'; export * from './searchTerm.type'; export * from './selectedRange.interface'; export * from './selectOption.interface'; +export * from './servicePagination.interface'; export * from './slickEvent.interface'; export * from './slickEventData.interface'; export * from './slickEventHandler.interface'; diff --git a/src/app/modules/angular-slickgrid/models/pager.interface.ts b/src/app/modules/angular-slickgrid/models/pager.interface.ts deleted file mode 100644 index 80398d0fa..000000000 --- a/src/app/modules/angular-slickgrid/models/pager.interface.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface Pager { - from: number; - to: number; - itemsPerPage: number; - pageCount: number; - pageNumber: number; - availablePageSizes: number[]; - totalItems: number; -} diff --git a/src/app/modules/angular-slickgrid/models/pagination.interface.ts b/src/app/modules/angular-slickgrid/models/pagination.interface.ts index 04230fe64..af5b706c3 100644 --- a/src/app/modules/angular-slickgrid/models/pagination.interface.ts +++ b/src/app/modules/angular-slickgrid/models/pagination.interface.ts @@ -1,7 +1,4 @@ export interface Pagination { - /** How many pages do we have in total to display the entire dataset? */ - pageCount?: number; - /** Current page number that we are we currently displaying. */ pageNumber?: number; @@ -13,10 +10,4 @@ export interface Pagination { /** The full total count of items for the entire dataset */ totalItems?: number; - - /** Current From count (which displayed items are we starting from) */ - dataFrom?: number; - - /** Current To count (which displayed items are we ending to) */ - dataTo?: number; } diff --git a/src/app/modules/angular-slickgrid/models/servicePagination.interface.ts b/src/app/modules/angular-slickgrid/models/servicePagination.interface.ts new file mode 100644 index 000000000..802f9b88f --- /dev/null +++ b/src/app/modules/angular-slickgrid/models/servicePagination.interface.ts @@ -0,0 +1,12 @@ +import { Pagination } from './pagination.interface'; + +export interface ServicePagination extends Pagination { + /** How many pages do we have in total to display the entire dataset? */ + pageCount?: number; + + /** Current From count (which displayed items are we starting from) */ + dataFrom?: number; + + /** Current To count (which displayed items are we ending to) */ + dataTo?: number; +} diff --git a/src/app/modules/angular-slickgrid/services/__tests__/pagination.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/pagination.service.spec.ts index 44ef0ad14..2418e2f60 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/pagination.service.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/pagination.service.spec.ts @@ -100,7 +100,6 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); expect(service.paginationOptions).toEqual(mockGridOption.pagination); - expect(service.pager).toBeTruthy(); expect(refreshSpy).toHaveBeenCalled(); expect(service.getCurrentPageNumber()).toBe(2); }); @@ -110,7 +109,6 @@ describe('PaginationService', () => { service.paginationOptions = mockGridOption.pagination; expect(service.paginationOptions).toEqual(mockGridOption.pagination); - expect(service.pager).toBeTruthy(); expect(service.getCurrentPageNumber()).toBe(2); }); @@ -120,7 +118,6 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); expect(service.totalItems).toEqual(125); - expect(service.pager).toBeTruthy(); expect(service.getCurrentPageNumber()).toBe(2); expect(spy).toHaveBeenCalledWith(false, false); }); @@ -131,11 +128,35 @@ describe('PaginationService', () => { service.totalItems = 125; expect(service.totalItems).toEqual(125); - expect(service.pager).toBeTruthy(); expect(service.getCurrentPageNumber()).toBe(2); expect(spy).toHaveBeenCalledTimes(2); // called 2x times inside the init() and SETTER }); + describe('Getters and Setters', () => { + it('should get the availablePageSizes and equal the one defined in the grid options pagination', () => { + mockGridOption.pagination.pageSizes = [5, 10, 15, 20]; + service.init(gridStub, dataviewStub, mockGridOption.pagination); + expect(service.availablePageSizes).toEqual(mockGridOption.pagination.pageSizes); + }); + + it('should get the itemsPerPage and equal the one defined in the grid options pagination', () => { + mockGridOption.pagination.pageSize = 20; + service.init(gridStub, dataviewStub, mockGridOption.pagination); + expect(service.itemsPerPage).toEqual(mockGridOption.pagination.pageSize); + }); + + it('should get the pageCount and equal the one defined in the grid options pagination', () => { + service.init(gridStub, dataviewStub, mockGridOption.pagination); + expect(service.pageCount).toEqual(4); // since totalItems is 85 and our pageSize is 20/page + }); + + it('should get the pageNumber and equal the one defined in the grid options pagination', () => { + mockGridOption.pagination.pageNumber = 3; + service.init(gridStub, dataviewStub, mockGridOption.pagination); + expect(service.pageNumber).toEqual(mockGridOption.pagination.pageNumber); + }); + }); + describe('changeItemPerPage method', () => { it('should be on page 0 when total items is 0', () => { mockGridOption.pagination.totalItems = 0; @@ -177,8 +198,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToFirstPage(); - expect(service.pager.from).toBe(1); - expect(service.pager.to).toBe(25); + expect(service.dataFrom).toBe(1); + expect(service.dataTo).toBe(25); expect(service.getCurrentPageNumber()).toBe(1); expect(spy).toHaveBeenCalledWith(1, undefined); }); @@ -191,8 +212,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToLastPage(); - expect(service.pager.from).toBe(76); - expect(service.pager.to).toBe(85); + expect(service.dataFrom).toBe(76); + expect(service.dataTo).toBe(85); expect(service.getCurrentPageNumber()).toBe(4); expect(spy).toHaveBeenCalledWith(4, undefined); }); @@ -205,8 +226,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToNextPage(); - expect(service.pager.from).toBe(51); - expect(service.pager.to).toBe(75); + expect(service.dataFrom).toBe(51); + expect(service.dataTo).toBe(75); expect(service.getCurrentPageNumber()).toBe(3); expect(spy).toHaveBeenCalledWith(3, undefined); }); @@ -217,8 +238,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToNextPage(); - expect(service.pager.from).toBe(51); - expect(service.pager.to).toBe(75); + expect(service.dataFrom).toBe(51); + expect(service.dataTo).toBe(75); expect(service.getCurrentPageNumber()).toBe(3); expect(spy).toHaveBeenCalledWith(3, undefined); }); @@ -230,8 +251,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToNextPage(); - expect(service.pager.from).toBe(76); - expect(service.pager.to).toBe(85); + expect(service.dataFrom).toBe(76); + expect(service.dataTo).toBe(85); expect(service.getCurrentPageNumber()).toBe(4); expect(spy).not.toHaveBeenCalled(); }); @@ -244,8 +265,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToPreviousPage(); - expect(service.pager.from).toBe(1); - expect(service.pager.to).toBe(25); + expect(service.dataFrom).toBe(1); + expect(service.dataTo).toBe(25); expect(service.getCurrentPageNumber()).toBe(1); expect(spy).toHaveBeenCalledWith(1, undefined); }); @@ -257,8 +278,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToPreviousPage(); - expect(service.pager.from).toBe(1); - expect(service.pager.to).toBe(25); + expect(service.dataFrom).toBe(1); + expect(service.dataTo).toBe(25); expect(service.getCurrentPageNumber()).toBe(1); expect(spy).not.toHaveBeenCalled(); }); @@ -272,8 +293,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToPageNumber(4); - expect(service.pager.from).toBe(76); - expect(service.pager.to).toBe(85); + expect(service.dataFrom).toBe(76); + expect(service.dataTo).toBe(85); expect(service.getCurrentPageNumber()).toBe(4); expect(spy).toHaveBeenCalledWith(4, undefined); }); @@ -284,8 +305,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToPageNumber(0); - expect(service.pager.from).toBe(1); - expect(service.pager.to).toBe(25); + expect(service.dataFrom).toBe(1); + expect(service.dataTo).toBe(25); expect(service.getCurrentPageNumber()).toBe(1); expect(spy).toHaveBeenCalledWith(1, undefined); }); @@ -296,8 +317,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToPageNumber(10); - expect(service.pager.from).toBe(76); - expect(service.pager.to).toBe(85); + expect(service.dataFrom).toBe(76); + expect(service.dataTo).toBe(85); expect(service.getCurrentPageNumber()).toBe(4); expect(spy).toHaveBeenCalledWith(4, undefined); }); @@ -309,8 +330,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.goToPageNumber(2); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50); expect(service.getCurrentPageNumber()).toBe(2); expect(spy).not.toHaveBeenCalled(); }); @@ -416,15 +437,29 @@ describe('PaginationService', () => { expect(setPagingSpy).toHaveBeenCalledWith({ pageSize: 25, pageNum: 0 }); expect(onPaginationSpy).toHaveBeenCalledWith({ - availablePageSizes: mockGridOption.pagination.pageSizes, - from: 26, - itemsPerPage: 25, + pageSizes: mockGridOption.pagination.pageSizes, + dataFrom: 26, + pageSize: 25, pageCount: 4, pageNumber: 2, - to: 50, + dataTo: 50, totalItems: 85, }); }); + + it('should call "setPagingOptions" from the DataView and trigger "onPaginationChanged" when using a Local Grid', () => { + const setPagingSpy = jest.spyOn(dataviewStub, 'setPagingOptions'); + const onPaginationSpy = jest.spyOn(service.onPaginationChanged, 'next'); + + mockGridOption.backendServiceApi = null; + service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); + service.processOnPageChanged(1); + + expect(setPagingSpy).toHaveBeenCalledWith({ pageSize: 25, pageNum: 0 }); + expect(onPaginationSpy).toHaveBeenCalledWith({ + dataFrom: 26, dataTo: 50, pageSize: 25, pageCount: 4, pageNumber: 2, totalItems: 85, pageSizes: mockGridOption.pagination.pageSizes, + }); + }); }); describe('recalculateFromToIndexes method', () => { @@ -436,8 +471,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.recalculateFromToIndexes(); - expect(service.pager.from).toBe(0); - expect(service.pager.to).toBe(0); + expect(service.dataFrom).toBe(0); + expect(service.dataTo).toBe(0); }); it('should recalculate the From/To within range', () => { @@ -448,8 +483,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.recalculateFromToIndexes(); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50); }); it('should recalculate the From/To within range and have the To equal the total items when total items is not a modulo of 1', () => { @@ -460,8 +495,8 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination, mockGridOption.backendServiceApi); service.recalculateFromToIndexes(); - expect(service.pager.from).toBe(76); - expect(service.pager.to).toBe(85); + expect(service.dataFrom).toBe(76); + expect(service.dataTo).toBe(85); }); }); @@ -550,8 +585,8 @@ describe('PaginationService', () => { setTimeout(() => { expect(paginationSpy).toHaveBeenCalledTimes(1); expect(recalculateSpy).toHaveBeenCalledTimes(2); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50 + 1); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50 + 1); done(); }); }); @@ -567,8 +602,8 @@ describe('PaginationService', () => { setTimeout(() => { expect(paginationSpy).toHaveBeenCalledTimes(1); expect(recalculateSpy).toHaveBeenCalledTimes(2); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50 + mockItems.length); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50 + mockItems.length); done(); }); }); @@ -583,8 +618,8 @@ describe('PaginationService', () => { setTimeout(() => { expect(paginationSpy).not.toHaveBeenCalled(); expect(recalculateSpy).toHaveBeenCalledTimes(1); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50); done(); }); }); @@ -600,8 +635,8 @@ describe('PaginationService', () => { setTimeout(() => { expect(paginationSpy).toHaveBeenCalledTimes(1); expect(recalculateSpy).toHaveBeenCalledTimes(2); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50 - 1); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50 - 1); done(); }); }); @@ -617,8 +652,8 @@ describe('PaginationService', () => { setTimeout(() => { expect(paginationSpy).toHaveBeenCalledTimes(1); expect(recalculateSpy).toHaveBeenCalledTimes(2); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50 - mockItems.length); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50 - mockItems.length); done(); }); }); @@ -634,8 +669,8 @@ describe('PaginationService', () => { setTimeout(() => { expect(paginationSpy).not.toHaveBeenCalled(); expect(recalculateSpy).toHaveBeenCalledTimes(1); - expect(service.pager.from).toBe(26); - expect(service.pager.to).toBe(50); + expect(service.dataFrom).toBe(26); + expect(service.dataTo).toBe(50); done(); }); }); @@ -650,8 +685,8 @@ describe('PaginationService', () => { service.changeItemPerPage(200); setTimeout(() => { - expect(service.pager.from).toBe(1); - expect(service.pager.to).toBe(101); + expect(service.dataFrom).toBe(1); + expect(service.dataTo).toBe(101); done(); }); }); @@ -666,8 +701,8 @@ describe('PaginationService', () => { service.changeItemPerPage(100); setTimeout(() => { - expect(service.pager.from).toBe(1); - expect(service.pager.to).toBe(100); + expect(service.dataFrom).toBe(1); + expect(service.dataTo).toBe(100); done(); }); }); @@ -689,7 +724,6 @@ describe('PaginationService', () => { service.init(gridStub, dataviewStub, mockGridOption.pagination); expect(service.paginationOptions).toEqual(mockGridOption.pagination); - expect(service.pager).toBeTruthy(); expect(refreshSpy).toHaveBeenCalled(); expect(onPagingSpy).toHaveBeenCalled(); expect(setRefreshSpy).toHaveBeenCalled(); diff --git a/src/app/modules/angular-slickgrid/services/graphql.service.ts b/src/app/modules/angular-slickgrid/services/graphql.service.ts index 4e6359bf4..7253675da 100644 --- a/src/app/modules/angular-slickgrid/services/graphql.service.ts +++ b/src/app/modules/angular-slickgrid/services/graphql.service.ts @@ -258,7 +258,8 @@ export class GraphqlService implements BackendService { // save current pagination as Page 1 and page size as "first" set size this._currentPagination = { pageNumber: 1, - pageSize: paginationOptions.first || DEFAULT_PAGE_SIZE + pageSize: paginationOptions.first || DEFAULT_PAGE_SIZE, + pageSizes: this._gridOptions && this._gridOptions.pagination && this._gridOptions.pagination.pageSizes, }; this.updateOptions({ paginationOptions }); @@ -455,10 +456,11 @@ export class GraphqlService implements BackendService { * @param newPage * @param pageSize */ - updatePagination(newPage: number, pageSize: number) { + updatePagination(newPage: number, pageSize: number, pageSizes?: number[]) { this._currentPagination = { pageNumber: newPage, - pageSize + pageSize, + pageSizes, }; let paginationOptions; diff --git a/src/app/modules/angular-slickgrid/services/grid-odata.service.ts b/src/app/modules/angular-slickgrid/services/grid-odata.service.ts index 91485f546..899383f2a 100644 --- a/src/app/modules/angular-slickgrid/services/grid-odata.service.ts +++ b/src/app/modules/angular-slickgrid/services/grid-odata.service.ts @@ -78,7 +78,8 @@ export class GridOdataService implements BackendService { this._odataService.options = { ...mergedOptions, top: topOption }; this._currentPagination = { pageNumber: 1, - pageSize: this._odataService.options.top || this.defaultOptions.top || DEFAULT_PAGE_SIZE + pageSize: this._odataService.options.top || this.defaultOptions.top || DEFAULT_PAGE_SIZE, + pageSizes: this._gridOptions && this._gridOptions.pagination && this._gridOptions.pagination.pageSizes, }; } @@ -398,10 +399,11 @@ export class GridOdataService implements BackendService { * @param newPage * @param pageSize */ - updatePagination(newPage: number, pageSize: number) { + updatePagination(newPage: number, pageSize: number, pageSizes?: number[]) { this._currentPagination = { pageNumber: newPage, - pageSize + pageSize, + pageSizes, }; // unless user specifically set "enablePagination" to False, we'll update pagination options in every other cases diff --git a/src/app/modules/angular-slickgrid/services/pagination.service.ts b/src/app/modules/angular-slickgrid/services/pagination.service.ts index 4f4ee10d7..d58bb573b 100644 --- a/src/app/modules/angular-slickgrid/services/pagination.service.ts +++ b/src/app/modules/angular-slickgrid/services/pagination.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Subscription, isObservable, Subject } from 'rxjs'; -import { BackendServiceApi, CurrentPagination, GraphqlResult, GraphqlPaginatedResult, Pager, Pagination } from '../models'; +import { BackendServiceApi, CurrentPagination, GraphqlResult, GraphqlPaginatedResult, Pagination, ServicePagination } from '../models'; import { FilterService } from './filter.service'; import { GridService } from './grid.service'; import { SharedService } from './shared.service'; @@ -27,7 +27,7 @@ export class PaginationService { private _paginationOptions: Pagination; private _subscriptions: Subscription[] = []; onPaginationRefreshed = new Subject(); - onPaginationChanged = new Subject(); + onPaginationChanged = new Subject(); dataView: any; grid: any; @@ -35,30 +35,41 @@ export class PaginationService { /** Constructor */ constructor(private filterService: FilterService, private gridService: GridService, private sharedService: SharedService) { } + get paginationOptions(): Pagination { + return this._paginationOptions; + } set paginationOptions(paginationOptions: Pagination) { this._paginationOptions = paginationOptions; } - get paginationOptions(): Pagination { - return this._paginationOptions; + get availablePageSizes(): number[] { + return this._availablePageSizes; } - get pager(): Pager { - return { - from: this._dataFrom, - to: this._dataTo, - itemsPerPage: this._itemsPerPage, - pageCount: this._pageCount, - pageNumber: this._pageNumber, - availablePageSizes: this._availablePageSizes, - totalItems: this._totalItems, - }; + get dataFrom(): number { + return this._dataFrom; + } + + get dataTo(): number { + return this._dataTo; + } + + get itemsPerPage(): number { + return this._itemsPerPage; + } + + get pageCount(): number { + return this._pageCount; + } + + get pageNumber(): number { + return this._pageNumber; } set totalItems(totalItems: number) { this._totalItems = totalItems; if (this._initialized) { - this.refreshPagination(); + this.refreshPagination(false, false); } } @@ -101,9 +112,8 @@ export class PaginationService { this._subscriptions.push(this.gridService.onItemAdded.subscribe((items: any | any[]) => this.processOnItemAddedOrRemoved(items, true))); this._subscriptions.push(this.gridService.onItemDeleted.subscribe((items: any | any[]) => this.processOnItemAddedOrRemoved(items, false))); } - if (!this._paginationOptions || (this._paginationOptions.totalItems !== this._totalItems)) { - this.refreshPagination(false, false); - } + + this.refreshPagination(false, false); this._initialized = true; } @@ -124,7 +134,20 @@ export class PaginationService { getCurrentPagination(): CurrentPagination { return { pageNumber: this._pageNumber, - pageSize: this._itemsPerPage + pageSize: this._itemsPerPage, + pageSizes: this._availablePageSizes, + }; + } + + getFullPagination(): ServicePagination { + return { + pageCount: this._pageCount, + pageNumber: this._pageNumber, + pageSize: this._itemsPerPage, + pageSizes: this._availablePageSizes, + totalItems: this._totalItems, + dataFrom: this._dataFrom, + dataTo: this._dataTo, }; } @@ -225,7 +248,7 @@ export class PaginationService { } this._pageCount = Math.ceil(this._totalItems / this._itemsPerPage); if (triggerChangedEvent) { - this.onPaginationChanged.next(this.pager); + this.onPaginationChanged.next(this.getFullPagination()); } this.sharedService.currentPagination = this.getCurrentPagination(); } @@ -236,7 +259,7 @@ export class PaginationService { if (this._isLocalGrid && this.dataView) { this.dataView.setPagingOptions({ pageSize: this._itemsPerPage, pageNum: (pageNumber - 1) }); // dataView page starts at 0 instead of 1 - this.onPaginationChanged.next(this.pager); + this.onPaginationChanged.next(this.getFullPagination()); } else { const itemsPerPage = +this._itemsPerPage; @@ -272,7 +295,7 @@ export class PaginationService { } ); } - this.onPaginationChanged.next(this.pager); + this.onPaginationChanged.next(this.getFullPagination()); } }); } @@ -289,6 +312,9 @@ export class PaginationService { this._dataTo = this._totalItems; } } + if (this._totalItems > 0 && this._pageNumber === 0) { + this._pageNumber = 1; + } // do a final check on the From/To and make sure they are not over or below min/max acceptable values if (this._dataTo > this._totalItems) { @@ -327,7 +353,7 @@ export class PaginationService { // finally refresh the "To" count and we know it might be different than the "items per page" count // but this is necessary since we don't want an actual backend refresh this._dataTo = previousDataTo + itemCountWithDirection; - this.onPaginationChanged.next(this.pager); + this.onPaginationChanged.next(this.getFullPagination()); } } } diff --git a/test/cypress/integration/example05.spec.js b/test/cypress/integration/example05.spec.js index 1672db373..39debc9e0 100644 --- a/test/cypress/integration/example05.spec.js +++ b/test/cypress/integration/example05.spec.js @@ -1,6 +1,13 @@ /// describe('Example 5 - OData Grid', () => { + beforeEach(() => { + // create a console.log spy for later use + cy.window().then((win) => { + cy.spy(win.console, 'log'); + }); + }); + it('should display Example title', () => { cy.visit(`${Cypress.config('baseExampleUrl')}/odata`); cy.get('h2').should('contain', 'Example 5: Grid connected to Backend Server with OData'); @@ -46,6 +53,11 @@ describe('Example 5 - OData Grid', () => { .should(($span) => { expect($span.text()).to.eq(`$inlinecount=allpages&$top=20&$skip=40&$orderby=Name asc&$filter=(Gender eq 'male')`); }); + + cy.window().then((win) => { + expect(win.console.log).to.have.callCount(1); + expect(win.console.log).to.be.calledWith("Client sample, Grid State changed:: ", { newValues: { pageNumber: 3, pageSize: 20, pageSizes: [10, 15, 20, 25, 30, 40, 50, 75, 100] }, type: 'pagination' }); + }); }); it('should change Pagination to first page with 10 items', () => { @@ -460,18 +472,28 @@ describe('Example 5 - OData Grid', () => { }); describe('General Pagination Behaviors', () => { - it('should display page 1 of 1 but hide pagination from/to numbers when filtered data returns an empty dataset', () => { + it('should type a filter which returns an empty dataset', () => { cy.get('.search-filter.filter-name') .find('input') .clear() .type('xyz'); + cy.get('[data-test=odata-query-result]') + .should(($span) => { + expect($span.text()).to.eq(`$top=10&$filter=(contains(Name, 'xyz'))`); + }); + + // wait for the query to finish + cy.get('[data-test=status]').should('contain', 'done'); + }); + + it('should display page 0 of 0 but hide pagination from/to numbers when filtered data "xyz" returns an empty dataset', () => { cy.get('[data-test=page-number-input]') .invoke('val') - .then(pageNumber => expect(pageNumber).to.eq('1')); + .then(pageNumber => expect(pageNumber).to.eq('0')); cy.get('[data-test=page-count]') - .contains('1'); + .contains('0'); cy.get('[data-test=item-from]') .should('not.exist'); @@ -487,6 +509,44 @@ describe('Example 5 - OData Grid', () => { expect($span.text()).to.eq(`$top=10&$filter=(contains(Name, 'xyz'))`); }); }); + + it('should erase part of the filter so that it filters with "x"', () => { + cy.get('.search-filter.filter-name') + .find('input') + .type('{backspace}{backspace}'); + + cy.get('[data-test=odata-query-result]') + .should(($span) => { + expect($span.text()).to.eq(`$top=10&$filter=(contains(Name, 'x'))`); + }); + + // wait for the query to finish + cy.get('[data-test=status]').should('contain', 'done'); + + cy.window().then((win) => { + expect(win.console.log).to.have.callCount(2); + expect(win.console.log).to.be.calledWith("Client sample, Grid State changed:: ", { newValues: [{ columnId: 'name', searchTerms: ['x'] }], type: 'filter' }); + expect(win.console.log).to.be.calledWith("Client sample, Grid State changed:: ", { newValues: { pageNumber: 0, pageSize: 10, pageSizes: [10, 15, 20, 25, 30, 40, 50, 75, 100] }, type: 'pagination' }); + }); + }); + + it('should display page 1 of 1 with 2 items after erasing part of the filter to be "x" which should return 1 page', () => { + cy.get('[data-test=page-number-input]') + .invoke('val') + .then(pageNumber => expect(pageNumber).to.eq('1')); + + cy.get('[data-test=page-count]') + .contains('1'); + + cy.get('[data-test=item-from]') + .contains('1'); + + cy.get('[data-test=item-to]') + .contains('2'); + + cy.get('[data-test=total-items]') + .contains('2'); + }); }); describe('Set Dynamic Sorting', () => { @@ -521,8 +581,6 @@ describe('Example 5 - OData Grid', () => { .children('.slick-cell:nth(1)') .should('contain', 'Alexander Foley'); - - cy.get('[data-test=odata-query-result]') .should(($span) => { expect($span.text()).to.eq(`$top=10&$orderby=Name desc&$filter=(startswith(Name, 'A'))`); diff --git a/test/cypress/integration/example06.spec.js b/test/cypress/integration/example06.spec.js index c6a5fb47c..aff5d72d1 100644 --- a/test/cypress/integration/example06.spec.js +++ b/test/cypress/integration/example06.spec.js @@ -338,7 +338,7 @@ describe('Example 6 - GraphQL Grid', () => { .click(); // wait for the query to finish - cy.get('[data-test=status]').should('contain', 'processing'); + // cy.get('[data-test=status]').should('contain', 'processing'); cy.get('[data-test=status]').should('contain', 'done'); }); diff --git a/test/cypress/integration/example22.spec.js b/test/cypress/integration/example22.spec.js index 295f9a04f..cd925c964 100644 --- a/test/cypress/integration/example22.spec.js +++ b/test/cypress/integration/example22.spec.js @@ -24,10 +24,10 @@ describe('Example 22 - Use of Angular Components', () => { cy.get('#grid22') .find('#myDrop-r1-c6') - .click(); + .click({ force: true }); cy.contains('Delete Row') - .trigger('click', { force: true }); + .click({ force: true }); // after deleting the row cy.get('#grid22')