+ You can create a Custom Pagination that will be appended/prepended (bottom or top) of the grid. In fact we can put these pagination elements
+ anywhere on the page (for example the page size is totally separate in his own corner above).
+
+
+
+
\ No newline at end of file
diff --git a/examples/vite-demo-vanilla-bundle/src/examples/example30.ts b/examples/vite-demo-vanilla-bundle/src/examples/example30.ts
new file mode 100644
index 000000000..9e5ebf553
--- /dev/null
+++ b/examples/vite-demo-vanilla-bundle/src/examples/example30.ts
@@ -0,0 +1,163 @@
+import { Slicker, type SlickVanillaGridBundle } from '@slickgrid-universal/vanilla-bundle';
+
+import {
+ type Column,
+ FieldType,
+ Filters,
+ Formatters,
+ type GridOption,
+ type MultipleSelectOption,
+ OperatorType,
+ type SliderRangeOption,
+} from '@slickgrid-universal/common';
+
+import { ExampleGridOptions } from './example-grid-options';
+import { CustomPager } from './example30-pager';
+
+const NB_ITEMS = 5000;
+
+function randomBetween(min: number, max: number): number {
+ return Math.floor(Math.random() * (max - min + 1) + min);
+}
+
+export default class Example30 {
+ pageSize = 50;
+ columnDefinitions: Column[] = [];
+ gridContainerElm: HTMLDivElement;
+ gridOptions!: GridOption;
+ dataset: any[] = [];
+ paginationPosition: 'bottom' | 'top' = 'top';
+ sgb: SlickVanillaGridBundle;
+
+ attached() {
+ // define the grid options & columns and then create the grid itself
+ this.defineGrid();
+
+ // mock some data (different in each dataset)
+ this.dataset = this.mockData(NB_ITEMS);
+ this.gridContainerElm = document.querySelector('.grid30') as HTMLDivElement;
+ this.sgb = new Slicker.GridBundle(this.gridContainerElm, this.columnDefinitions, { ...ExampleGridOptions, ...this.gridOptions }, this.dataset);
+ document.body.classList.add('material-theme');
+ }
+
+ dispose() {
+ document.body.classList.remove('material-theme');
+ }
+
+ /* Define grid Options and Columns */
+ defineGrid() {
+ this.columnDefinitions = [
+ {
+ id: 'title', name: 'Title', field: 'id', minWidth: 100,
+ sortable: true,
+ filterable: true,
+ formatter: (row, cell, val) => `Task ${val}`,
+ params: { useFormatterOuputToFilter: true }
+ },
+ {
+ id: 'description', name: 'Description', field: 'description', filterable: true, sortable: true, minWidth: 80,
+ type: FieldType.string,
+ },
+ {
+ id: 'percentComplete', name: '% Complete', field: 'percentComplete', nameKey: 'PERCENT_COMPLETE', minWidth: 120,
+ sortable: true,
+ customTooltip: { position: 'center' },
+ formatter: Formatters.progressBar,
+ type: FieldType.number,
+ filterable: true,
+ filter: {
+ model: Filters.sliderRange,
+ maxValue: 100, // or you can use the filterOptions as well
+ operator: OperatorType.rangeInclusive, // defaults to inclusive
+ filterOptions: {
+ hideSliderNumbers: false, // you can hide/show the slider numbers on both side
+ min: 0, step: 5
+ } as SliderRangeOption
+ }
+ },
+ {
+ id: 'start', name: 'Start', field: 'start', formatter: Formatters.dateIso, sortable: true, minWidth: 75, width: 100, exportWithFormatter: true,
+ type: FieldType.date, filterable: true, filter: { model: Filters.compoundDate }
+ },
+ {
+ id: 'finish', name: 'Finish', field: 'finish', formatter: Formatters.dateIso, sortable: true, minWidth: 75, width: 120, exportWithFormatter: true,
+ type: FieldType.date,
+ filterable: true,
+ filter: {
+ model: Filters.dateRange,
+ }
+ },
+ {
+ id: 'duration', field: 'duration', name: 'Duration', maxWidth: 90,
+ type: FieldType.number,
+ sortable: true,
+ filterable: true, filter: {
+ model: Filters.input,
+ operator: OperatorType.rangeExclusive // defaults to exclusive
+ }
+ },
+ {
+ id: 'completed', name: 'Completed', field: 'completed', minWidth: 85, maxWidth: 90,
+ formatter: Formatters.checkmarkMaterial,
+ exportWithFormatter: true, // you can set this property in the column definition OR in the grid options, column def has priority over grid options
+ filterable: true,
+ filter: {
+ collection: [{ value: '', label: '' }, { value: true, label: 'True' }, { value: false, label: 'False' }],
+ model: Filters.singleSelect,
+ filterOptions: { autoAdjustDropHeight: true } as MultipleSelectOption
+ }
+ }
+ ];
+
+ this.gridOptions = {
+ autoResize: {
+ container: '.demo-container',
+ bottomPadding: this.paginationPosition === 'top' ? -10 : 20 // use a negative bottom padding since we've prepended custom pagination
+ },
+ enableExcelCopyBuffer: true,
+ enableFiltering: true,
+ customPaginationComponent: CustomPager,
+ enablePagination: true,
+ rowHeight: 40,
+ pagination: {
+ pageSize: this.pageSize
+ },
+ };
+ }
+
+ setPaginationSize(pageSize: number) {
+ this.sgb.paginationService.changeItemPerPage(pageSize);
+ }
+
+ mockData(itemCount: number, startingIndex = 0): any[] {
+ // mock a dataset
+ const tempDataset: any[] = [];
+ for (let i = startingIndex; i < (startingIndex + itemCount); i++) {
+ const randomDuration = randomBetween(0, 365);
+ const randomYear = randomBetween(new Date().getFullYear(), new Date().getFullYear() + 1);
+ const randomMonth = randomBetween(0, 12);
+ const randomDay = randomBetween(10, 28);
+ const randomPercent = randomBetween(0, 100);
+
+ tempDataset.push({
+ id: i,
+ title: 'Task ' + i,
+ description: (i % 5) ? 'desc ' + i : null, // also add some random to test NULL field
+ duration: randomDuration,
+ percentComplete: randomPercent,
+ percentCompleteNumber: randomPercent,
+ start: (i % 4) ? null : new Date(randomYear, randomMonth, randomDay), // provide a Date format
+ finish: new Date(randomYear, randomMonth, randomDay),
+ completed: (randomPercent === 100) ? true : false,
+ });
+ }
+
+ return tempDataset;
+ }
+
+ togglePaginationPosition() {
+ this.paginationPosition = this.paginationPosition === 'top' ? 'bottom' : 'top';
+ (this.sgb.paginationComponent as CustomPager)?.disposeElement();
+ (this.sgb.paginationComponent as CustomPager)?.render(this.gridContainerElm, this.paginationPosition);
+ }
+}
diff --git a/packages/common/src/interfaces/gridOption.interface.ts b/packages/common/src/interfaces/gridOption.interface.ts
index 3c475a3e5..4a2a70dc7 100644
--- a/packages/common/src/interfaces/gridOption.interface.ts
+++ b/packages/common/src/interfaces/gridOption.interface.ts
@@ -7,6 +7,7 @@ import type {
AutoTooltipOption,
AutocompleterOption,
BackendServiceApi,
+ BasePaginationComponent,
CellMenu,
CheckboxSelectorOption,
Column,
@@ -222,6 +223,9 @@ export interface GridOption {
/** Custom Footer Options */
customFooterOptions?: CustomFooterOption;
+ /** External Custom Pagination Component that can be provided by the user */
+ customPaginationComponent?: typeof BasePaginationComponent;
+
/**
* Custom Tooltip Options, the tooltip could be defined in any of the Column Definition or in the Grid Options,
* it will first try to find it in the Column that the user is hovering over or else (when not found) go and try to find it in the Grid Options
diff --git a/packages/common/src/interfaces/index.ts b/packages/common/src/interfaces/index.ts
index 69e42eb93..1dfbf9657 100644
--- a/packages/common/src/interfaces/index.ts
+++ b/packages/common/src/interfaces/index.ts
@@ -132,7 +132,6 @@ export type * from './rowSelectionModelOption.interface';
export type * from './searchColumnFilter.interface';
export type * from './selectableOverrideCallback.interface';
export type * from './selectOption.interface';
-export type * from './servicePagination.interface';
export type * from './singleColumnSort.interface';
export type * from './slickPlugin.interface';
export type * from './slickRemoteModel.interface';
diff --git a/packages/common/src/interfaces/pagination.interface.ts b/packages/common/src/interfaces/pagination.interface.ts
index 9716158b8..dee65ea39 100644
--- a/packages/common/src/interfaces/pagination.interface.ts
+++ b/packages/common/src/interfaces/pagination.interface.ts
@@ -18,7 +18,7 @@ export interface Pagination {
totalItems?: number;
}
-export abstract class BasePaginationComponent {
+export class BasePaginationComponent {
constructor(
_grid: SlickGrid,
_paginationService: PaginationService,
@@ -29,4 +29,15 @@ export abstract class BasePaginationComponent {
dispose(): void { }
render(_containerElm: HTMLElement): void { }
-}
\ No newline at end of file
+}
+
+export interface PaginationMetadata 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/packages/common/src/interfaces/servicePagination.interface.ts b/packages/common/src/interfaces/servicePagination.interface.ts
deleted file mode 100644
index cf7d9fea3..000000000
--- a/packages/common/src/interfaces/servicePagination.interface.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { 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/packages/common/src/services/pagination.service.ts b/packages/common/src/services/pagination.service.ts
index d07b1eec1..d22a2f1dc 100644
--- a/packages/common/src/services/pagination.service.ts
+++ b/packages/common/src/services/pagination.service.ts
@@ -7,7 +7,7 @@ import type {
CursorPageInfo,
Pagination,
PaginationCursorChangedArgs,
- ServicePagination,
+ PaginationMetadata,
} from '../interfaces/index';
import type { BackendUtilityService } from './backendUtility.service';
import type { SharedService } from './shared.service';
@@ -186,7 +186,7 @@ export class PaginationService {
};
}
- getFullPagination(): ServicePagination {
+ getFullPagination(): PaginationMetadata {
return {
pageCount: this._pageCount,
pageNumber: this._pageNumber,
@@ -206,14 +206,14 @@ export class PaginationService {
return this._itemsPerPage;
}
- changeItemPerPage(itemsPerPage: number, event?: any, triggerChangeEvent = true): Promise {
+ changeItemPerPage(itemsPerPage: number, event?: any, triggerChangeEvent = true): Promise {
this._pageNumber = 1;
this._pageCount = Math.ceil(this._totalItems / itemsPerPage);
this._itemsPerPage = itemsPerPage;
return triggerChangeEvent ? this.processOnPageChanged(this._pageNumber, event) : Promise.resolve(this.getFullPagination());
}
- goToFirstPage(event?: any, triggerChangeEvent = true): Promise {
+ goToFirstPage(event?: any, triggerChangeEvent = true): Promise {
this._pageNumber = 1;
if (triggerChangeEvent) {
return this.isCursorBased && this._cursorPageInfo
@@ -223,7 +223,7 @@ export class PaginationService {
return Promise.resolve(this.getFullPagination());
}
- goToLastPage(event?: any, triggerChangeEvent = true): Promise {
+ goToLastPage(event?: any, triggerChangeEvent = true): Promise {
this._pageNumber = this._pageCount || 1;
if (triggerChangeEvent) {
return this.isCursorBased && this._cursorPageInfo
@@ -233,7 +233,7 @@ export class PaginationService {
return Promise.resolve(this.getFullPagination());
}
- goToNextPage(event?: any, triggerChangeEvent = true): Promise {
+ goToNextPage(event?: any, triggerChangeEvent = true): Promise {
if (this._pageNumber < this._pageCount) {
this._pageNumber++;
if (triggerChangeEvent) {
@@ -247,7 +247,7 @@ export class PaginationService {
return Promise.resolve(false);
}
- goToPageNumber(pageNumber: number, event?: any, triggerChangeEvent = true): Promise {
+ goToPageNumber(pageNumber: number, event?: any, triggerChangeEvent = true): Promise {
if (this.isCursorBased) {
console.assert(true, 'Cursor based navigation cannot navigate to arbitrary page');
return Promise.resolve(false);
@@ -269,7 +269,7 @@ export class PaginationService {
return Promise.resolve(false);
}
- goToPreviousPage(event?: any, triggerChangeEvent = true): Promise {
+ goToPreviousPage(event?: any, triggerChangeEvent = true): Promise {
if (this._pageNumber > 1) {
this._pageNumber--;
if (triggerChangeEvent) {
@@ -383,7 +383,7 @@ export class PaginationService {
}
}
- processOnPageChanged(pageNumber: number, event?: Event | undefined, cursorArgs?: PaginationCursorChangedArgs): Promise {
+ processOnPageChanged(pageNumber: number, event?: Event | undefined, cursorArgs?: PaginationCursorChangedArgs): Promise {
console.assert(!this.isCursorBased || cursorArgs, 'Configured for cursor based pagination - cursorArgs expected');
if (this.pubSubService.publish('onBeforePaginationChange', this.getFullPagination()) === false) {
diff --git a/packages/pagination-component/src/slick-pagination.component.ts b/packages/pagination-component/src/slick-pagination.component.ts
index cf2164631..fb18c4e2a 100644
--- a/packages/pagination-component/src/slick-pagination.component.ts
+++ b/packages/pagination-component/src/slick-pagination.component.ts
@@ -4,7 +4,7 @@ import type {
BasePaginationComponent,
PaginationService,
PubSubService,
- ServicePagination,
+ PaginationMetadata,
SlickGrid,
Subscription,
TranslaterService,
@@ -25,7 +25,7 @@ export class SlickPaginationComponent implements BasePaginationComponent {
protected _seekNextElm!: HTMLLIElement;
protected _seekEndElm!: HTMLLIElement;
protected _subscriptions: Subscription[] = [];
- currentPagination: ServicePagination;
+ currentPagination: PaginationMetadata;
firstButtonClasses = '';
lastButtonClasses = '';
prevButtonClasses = '';
@@ -59,7 +59,7 @@ export class SlickPaginationComponent implements BasePaginationComponent {
// Anytime the pagination is initialized or has changes,
// we'll copy the data into a local object so that we can add binding to this local object
this._subscriptions.push(
- this.pubSubService.subscribe('onPaginationRefreshed', paginationChanges => {
+ this.pubSubService.subscribe('onPaginationRefreshed', paginationChanges => {
for (const key of Object.keys(paginationChanges)) {
(this.currentPagination as any)[key] = (paginationChanges as any)[key];
}
diff --git a/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts b/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts
index b18da7c9a..10330ec67 100644
--- a/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts
+++ b/packages/vanilla-bundle/src/components/__tests__/slick-vanilla-grid.spec.ts
@@ -34,9 +34,9 @@ import {
type OnRowsChangedEventArgs,
type OnSetItemsCalledEventArgs,
type Pagination,
+ type PaginationMetadata,
type PaginationService,
type ResizerService,
- type ServicePagination,
SharedService,
SlickDataView,
type SlickEditorLock,
@@ -1896,20 +1896,20 @@ describe('Slick-Vanilla-Grid-Bundle 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 } as CurrentPagination;
- const mockServicePagination = {
+ const mockPaginationMetadata = {
...mockPagination,
dataFrom: 5,
dataTo: 10,
pageCount: 1,
pageSizes: [5, 10, 15, 20],
- } as ServicePagination;
+ } as PaginationMetadata;
const pluginEaSpy = vi.spyOn(eventPubSubService, 'publish');
vi.spyOn(gridStateServiceStub, 'getCurrentGridState').mockReturnValue({ columns: [], pagination: mockPagination } as GridState);
component.gridOptions.enablePagination = true;
component.initialization(divContainer, slickEventHandler);
component.refreshGridData([{ firstName: 'John', lastName: 'Doe' }]);
- eventPubSubService.publish('onPaginationChanged', mockServicePagination);
+ eventPubSubService.publish('onPaginationChanged', mockPaginationMetadata);
expect(pluginEaSpy).toHaveBeenCalledWith('onGridStateChanged', {
change: { newValues: mockPagination, type: GridStateType.pagination },
@@ -2172,7 +2172,7 @@ describe('Slick-Vanilla-Grid-Bundle Component instantiated via Constructor', ()
component.initialization(divContainer, slickEventHandler);
component.refreshGridData([{ firstName: 'John', lastName: 'Doe' }]);
- const disposeSpy = vi.spyOn(component.slickPagination!, 'dispose');
+ const disposeSpy = vi.spyOn(component.paginationComponent!, 'dispose');
eventPubSubService.publish('onPaginationVisibilityChanged', { visible: false });
expect(component.showPagination).toBeFalsy();
@@ -2193,7 +2193,7 @@ describe('Slick-Vanilla-Grid-Bundle Component instantiated via Constructor', ()
eventPubSubService.publish('onPaginationVisibilityChanged', { visible: true });
expect(backendRefreshSpy).toHaveBeenCalled();
- expect(component.slickPagination).toBeTruthy();
+ expect(component.paginationComponent).toBeTruthy();
expect(component.showPagination).toBeTruthy();
});
});
diff --git a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts
index aaa57a7b6..a4ecfb03e 100644
--- a/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts
+++ b/packages/vanilla-bundle/src/components/slick-vanilla-grid-bundle.ts
@@ -3,6 +3,7 @@ import type {
BackendService,
BackendServiceApi,
BackendServiceOption,
+ BasePaginationComponent,
Column,
DataViewOption,
ExtensionList,
@@ -12,7 +13,7 @@ import type {
Pagination,
RxJsFacade,
SelectEditor,
- ServicePagination,
+ PaginationMetadata,
Subscription,
} from '@slickgrid-universal/common';
@@ -114,6 +115,7 @@ export class SlickVanillaGridBundle {
gridService!: GridService;
gridStateService!: GridStateService;
headerGroupingService!: HeaderGroupingService;
+ paginationComponent: BasePaginationComponent | undefined;
paginationService!: PaginationService;
rxjs?: RxJsFacade;
sharedService!: SharedService;
@@ -125,7 +127,6 @@ export class SlickVanillaGridBundle {
// components
slickEmptyWarning: SlickEmptyWarningComponent | undefined;
slickFooter: SlickFooterComponent | undefined;
- slickPagination: SlickPaginationComponent | undefined;
get backendService(): BackendService | undefined {
return this.gridOptions.backendServiceApi?.service;
@@ -442,7 +443,7 @@ export class SlickVanillaGridBundle {
// dispose the Components
this.slickFooter?.dispose();
this.slickEmptyWarning?.dispose();
- this.slickPagination?.dispose();
+ this.paginationComponent?.dispose();
unsubscribeAll(this.subscriptions);
this._eventPubSubService?.unsubscribeAll();
@@ -1036,7 +1037,7 @@ export class SlickVanillaGridBundle {
* 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 with a Backend Service (Odata, GraphQL), we need to reset any selection
*/
- paginationChanged(pagination: ServicePagination): void {
+ paginationChanged(pagination: PaginationMetadata): void {
const isSyncGridSelectionEnabled = this.gridStateService?.needToPreserveRowSelection() ?? false;
if (this.slickGrid && !isSyncGridSelectionEnabled && this._gridOptions?.backendServiceApi && (this.gridOptions.enableRowSelection || this.gridOptions.enableCheckboxSelector)) {
this.slickGrid.setSelectedRows([]);
@@ -1239,7 +1240,7 @@ export class SlickVanillaGridBundle {
this.paginationService.totalItems = this.totalItems;
this.paginationService.init(this.slickGrid, paginationOptions, this.backendServiceApi);
this.subscriptions.push(
- this._eventPubSubService.subscribe('onPaginationChanged', paginationChanges => this.paginationChanged(paginationChanges)),
+ this._eventPubSubService.subscribe('onPaginationChanged', paginationChanges => this.paginationChanged(paginationChanges)),
this._eventPubSubService.subscribe<{ visible: boolean; }>('onPaginationVisibilityChanged', visibility => {
this.showPagination = visibility?.visible ?? false;
if (this.gridOptions?.backendServiceApi) {
@@ -1263,11 +1264,12 @@ export class SlickVanillaGridBundle {
*/
protected renderPagination(showPagination = true): void {
if (this.slickGrid && this._gridOptions?.enablePagination && !this._isPaginationInitialized && showPagination) {
- this.slickPagination = new SlickPaginationComponent(this.slickGrid, this.paginationService, this._eventPubSubService, this.translaterService);
- this.slickPagination.render(this._gridParentContainerElm);
+ const PaginationClass = this.gridOptions.customPaginationComponent ?? SlickPaginationComponent;
+ this.paginationComponent = new PaginationClass(this.slickGrid, this.paginationService, this._eventPubSubService, this.translaterService);
+ this.paginationComponent!.render(this._gridParentContainerElm);
this._isPaginationInitialized = true;
} else if (!showPagination) {
- this.slickPagination?.dispose();
+ this.paginationComponent?.dispose();
this._isPaginationInitialized = false;
}
}
diff --git a/test/cypress/e2e/example30.cy.ts b/test/cypress/e2e/example30.cy.ts
new file mode 100644
index 000000000..097e5c2d3
--- /dev/null
+++ b/test/cypress/e2e/example30.cy.ts
@@ -0,0 +1,77 @@
+describe('Example 30 - Custom Pagination', () => {
+ const GRID_ROW_HEIGHT = 40;
+ const titles = ['Title', 'Description', '% Complete', 'Start', 'Finish', 'Duration', 'Completed'];
+
+ it('should display Example title', () => {
+ cy.visit(`${Cypress.config('baseUrl')}/example30`);
+ cy.get('h3').should('contain', 'Example 30 - Custom Pagination');
+ });
+
+ it('should have exact Column Titles in the grid', () => {
+ cy.get('.grid30')
+ .find('.slick-header-columns')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(titles[index]));
+ });
+
+ it('should expect first row to be Task 0', () => {
+ cy.get('#pager.top').should('exist');
+ cy.get('.item-from').should('contain', 1);
+ cy.get('.item-to').should('contain', 50);
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 0');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 1');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 2');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 3}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 3');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 4}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 4');
+ });
+
+ it('should click on next page and expect top row to be Task 50', () => {
+ cy.get('.page-item.seek-next').click();
+
+ cy.get('.item-from').should('contain', 51);
+ cy.get('.item-to').should('contain', 100);
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 50');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 51');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 52');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 3}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 53');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 4}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 54');
+ });
+
+ it('should click on goto last page and expect top row to be Task 50', () => {
+ cy.get('.page-item.seek-end').click();
+
+ cy.get('.item-from').should('contain', 4951);
+ cy.get('.item-to').should('contain', 5000);
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 4950');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 4951');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 4952');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 3}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 4953');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 4}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 4954');
+ });
+
+ it('should change page size and expect pagination to be updated', () => {
+ cy.get('[data-test="page-size-input"]').type('{backspace}{backspace}75');
+ cy.get('.item-from').should('contain', 1);
+ cy.get('.item-to').should('contain', 75);
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 0');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 1');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 2');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 3}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 3');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 4}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 4');
+ });
+
+ it('should toggle pagination position to bottom', () => {
+ cy.get('[data-text="toggle-pagination-btn"]').click();
+ cy.get('#pager.bottom').should('exist');
+
+ cy.get('.page-item.seek-next').click();
+
+ cy.get('.item-from').should('contain', 76);
+ cy.get('.item-to').should('contain', 150);
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 0}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 75');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 1}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 76');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 2}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 77');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 3}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 78');
+ cy.get(`[style="top: ${GRID_ROW_HEIGHT * 4}px;"] > .slick-cell:nth(0)`).should('contain', 'Task 79');
+ });
+});