From 664a12999a09887f29237a93659d1a5c430548b2 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Mon, 27 Apr 2026 18:05:22 +0800 Subject: [PATCH 1/8] fix --- .../devextreme/js/__internal/grids/grid_core/pager/m_pager.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts b/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts index ad7118d25190..fdf9ce3e4533 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts @@ -9,6 +9,10 @@ const PAGER_CLASS = 'pager'; export const MAX_PAGES_COUNT = 10; const getPageIndex = function (dataController) { + if (dataController.pageSize() === 0) { + return dataController.pageCount(); + } + // eslint-disable-next-line radix return 1 + (parseInt(dataController.pageIndex()) || 0); }; From 29f7c0253554cb28a61676be9aca1104eb5ee922 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Tue, 28 Apr 2026 18:37:00 +0800 Subject: [PATCH 2/8] implement test --- .../tests/dataGrid/common/pager.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts index 9b7973b975dd..c776369d4d95 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts @@ -238,6 +238,41 @@ test('Page index should not reset when scrolling while the grid is being refresh height: 440, })); +test('Pager info should show page 1 of 1 after changing pageSize to \'all\' with virtual scrolling', async (t) => { + const dataGrid = new DataGrid('#container'); + const pager = dataGrid.getPager(); + + await t + .click(pager.getNavPage('5').element) + .expect(pager.getNavPage('5').selected) + .ok() + .expect(pager.getInfoText().textContent) + .eql('Page 5 of 10 (50 items)'); + + await t + .click(pager.getPageSize(1).element) + .expect(pager.getInfoText().textContent) + .eql('Page 1 of 1 (50 items)'); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [...new Array(50).keys()].map((i) => ({ id: i })), + keyExpr: 'id', + showBorders: true, + scrolling: { + mode: 'virtual', + }, + paging: { + pageSize: 5, + }, + pager: { + visible: true, + allowedPageSizes: [5, 'all'], + showPageSizeSelector: true, + showInfo: true, + showNavigationButtons: true, + }, + height: 400, +})); + test('No error should occur if dataSource is not defined and pageIndex is promise chained (T1256070)', async (t) => { const dataGrid = new DataGrid('#container'); From eb5a17dadff99504cc2e8aa1b0b8f18747e2edc3 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Wed, 6 May 2026 18:24:56 +0800 Subject: [PATCH 3/8] refix --- .../tests/dataGrid/common/pager.ts | 13 +++--- .../data_controller/m_data_controller.ts | 43 ++++++++----------- .../grids/grid_core/pager/m_pager.ts | 4 -- .../virtual_scrolling/m_virtual_scrolling.ts | 11 ++--- ...eyboardNavigation.realControllers.tests.js | 2 +- 5 files changed, 31 insertions(+), 42 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts index c776369d4d95..245f9d76163e 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts @@ -238,30 +238,31 @@ test('Page index should not reset when scrolling while the grid is being refresh height: 440, })); -test('Pager info should show page 1 of 1 after changing pageSize to \'all\' with virtual scrolling', async (t) => { +test('Pager info should show page 1 of 1 after changing pageSize to \'all\' with virtual scrolling (T1327238)', async (t) => { const dataGrid = new DataGrid('#container'); const pager = dataGrid.getPager(); - + await t .click(pager.getNavPage('5').element) .expect(pager.getNavPage('5').selected) .ok() .expect(pager.getInfoText().textContent) - .eql('Page 5 of 10 (50 items)'); + .eql('Page 5 of 10 (100 items)'); await t .click(pager.getPageSize(1).element) .expect(pager.getInfoText().textContent) - .eql('Page 1 of 1 (50 items)'); + .eql('Page 1 of 1 (100 items)'); }).before(async () => createWidget('dxDataGrid', { - dataSource: [...new Array(50).keys()].map((i) => ({ id: i })), + dataSource: [...new Array(100).keys()].map((i) => ({ id: i })), keyExpr: 'id', showBorders: true, scrolling: { mode: 'virtual', }, paging: { - pageSize: 5, + pageSize: 10, + pageIndex: 4, }, pager: { visible: true, diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts index e8fe7487c881..83a6ed50739e 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts @@ -213,11 +213,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { this._isPaging = false; this._currentOperationTypes = null; - this._dataChangedHandler = (e) => { - this._currentOperationTypes = this._dataSource.operationTypes(); - this._handleDataChanged(e); - this._currentOperationTypes = null; - }; + this._dataChangedHandler = this._handleDataChanged.bind(this); this._columnsChangedHandler = this._handleColumnsChanged.bind(this); this._loadingChangedHandler = this._handleLoadingChanged.bind(this); this._loadErrorHandler = this._handleLoadError.bind(this); @@ -588,6 +584,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { errors.log('W1005', that.component.NAME); that._applyFilter(); } else { + this._currentOperationTypes = dataSource.operationTypes(); that.updateItems(e, true); } }).fail(() => { @@ -1170,6 +1167,8 @@ export class DataController extends DataHelperMixin(modules.Controller) { const changeType = change.changeType || 'refresh'; change.changeType = changeType; + change.operationTypes = this._currentOperationTypes; + this._currentOperationTypes = null; if (dataSource) { const cachedProcessedItems = this._cachedProcessedItems; @@ -1231,36 +1230,32 @@ export class DataController extends DataHelperMixin(modules.Controller) { } } - public updateItems(change?, isDataChanged?) { - change = change || {}; - const that = this; - change.isFirstRender = !that.changed.fired(); + public updateItems(change: any = {}, isDataChanged?: boolean) { + change.isFirstRender = !this.changed.fired(); - if (that._repaintChangesOnly !== undefined) { - change.repaintChangesOnly = change.repaintChangesOnly ?? that._repaintChangesOnly; - change.needUpdateDimensions = change.needUpdateDimensions || that._needUpdateDimensions; + if (this._repaintChangesOnly !== undefined) { + change.repaintChangesOnly ??= this._repaintChangesOnly; + change.needUpdateDimensions ??= this._needUpdateDimensions; } else if (change.changes) { - change.repaintChangesOnly = that.option('repaintChangesOnly'); + change.repaintChangesOnly = this.option('repaintChangesOnly'); } else if (isDataChanged) { - const operationTypes = that.dataSource().operationTypes(); + const operationTypes = this.dataSource().operationTypes(); - change.repaintChangesOnly = operationTypes && !operationTypes.grouping && !operationTypes.filtering && that.option('repaintChangesOnly'); change.isDataChanged = true; - if (operationTypes && (operationTypes.reload || operationTypes.paging || operationTypes.groupExpanding)) { - change.needUpdateDimensions = true; - } + change.repaintChangesOnly = operationTypes && !operationTypes.grouping && !operationTypes.filtering && this.option('repaintChangesOnly'); + change.needUpdateDimensions = operationTypes && (operationTypes.reload || operationTypes.paging || operationTypes.groupExpanding); } - if (that._updateLockCount && !change.cancel) { - that._changes.push(change); + if (this._updateLockCount && !change.cancel) { + this._changes.push(change); return; } - that._updateItemsCore(change); + this._updateItemsCore(change); if (change.cancel) return; - that._fireChanged(change); + this._fireChanged(change); } public loadingOperationTypes() { @@ -1273,10 +1268,6 @@ export class DataController extends DataHelperMixin(modules.Controller) { * @extended: virtual_scrolling, focus */ protected _fireChanged(change) { - if (this._currentOperationTypes) { - change.operationTypes = this._currentOperationTypes; - this._currentOperationTypes = null; - } deferRender(() => { this.changed.fire(change); }); diff --git a/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts b/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts index fdf9ce3e4533..ad7118d25190 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/pager/m_pager.ts @@ -9,10 +9,6 @@ const PAGER_CLASS = 'pager'; export const MAX_PAGES_COUNT = 10; const getPageIndex = function (dataController) { - if (dataController.pageSize() === 0) { - return dataController.pageCount(); - } - // eslint-disable-next-line radix return 1 + (parseInt(dataController.pageIndex()) || 0); }; diff --git a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts index c2717eba9ca3..5a30993d5fa6 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/virtual_scrolling/m_virtual_scrolling.ts @@ -1004,18 +1004,19 @@ export const data = (Base: ModuleType) => class VirtualScrolling }; } - private _updateVisiblePageIndex(currentPageIndex?) { + private _updateVisiblePageIndex(value?: number): void { if (!this._rowsScrollController) { return; } - if (isDefined(currentPageIndex)) { - this._silentOption(VISIBLE_PAGE_INDEX, currentPageIndex); + + if (isDefined(value)) { + this._silentOption(VISIBLE_PAGE_INDEX, value); this.pageChanged.fire(); return; } - const viewPortItemIndex = this._rowsScrollController.getViewportItemIndex(); - const newPageIndex = Math.floor(viewPortItemIndex / this.pageSize()); + const viewportItemIndex = this._rowsScrollController.getViewportItemIndex(); + const newPageIndex = Math.floor(viewportItemIndex / this.pageSize()); if (this.pageIndex() !== newPageIndex) { this._silentOption(VISIBLE_PAGE_INDEX, newPageIndex); diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js index 531e3b09673c..b8d81a00877f 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/keyboardNavigation.realControllers.tests.js @@ -671,7 +671,7 @@ QUnit.module('Real DataController and ColumnsController', { this.cellValue(0, 2, '5'); this.saveEditData(); // assert - assert.equal(focusedRowChangedFiresCount, 2, 'onFocusedRowChanged fires count'); + assert.equal(focusedRowChangedFiresCount, 1, 'onFocusedRowChanged fires count'); // act this.refresh(); From 36a2cce471d8ecaffb9304d39cb9b9acc4816692 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Fri, 8 May 2026 17:01:23 +0800 Subject: [PATCH 4/8] fix test --- e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts index 245f9d76163e..c17b027e51d5 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts @@ -241,11 +241,8 @@ test('Page index should not reset when scrolling while the grid is being refresh test('Pager info should show page 1 of 1 after changing pageSize to \'all\' with virtual scrolling (T1327238)', async (t) => { const dataGrid = new DataGrid('#container'); const pager = dataGrid.getPager(); - + await t - .click(pager.getNavPage('5').element) - .expect(pager.getNavPage('5').selected) - .ok() .expect(pager.getInfoText().textContent) .eql('Page 5 of 10 (100 items)'); @@ -266,7 +263,7 @@ test('Pager info should show page 1 of 1 after changing pageSize to \'all\' with }, pager: { visible: true, - allowedPageSizes: [5, 'all'], + allowedPageSizes: [10, 'all'], showPageSizeSelector: true, showInfo: true, showNavigationButtons: true, From 36e42977e8dc76719982bc52d15910c9b4ae3a8a Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Sat, 9 May 2026 00:19:17 +0800 Subject: [PATCH 5/8] add another test + cover it --- .../tests/dataGrid/common/pager.ts | 46 +++++++++++++++++++ .../data_controller/m_data_controller.ts | 3 ++ 2 files changed, 49 insertions(+) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts index c17b027e51d5..d48d5adb7ee0 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts @@ -271,6 +271,52 @@ test('Pager info should show page 1 of 1 after changing pageSize to \'all\' with height: 400, })); +test('Pager info should show page 1 of 1 after changing pageSize to \'all\' and enabling virtual scrolling (T1327238)', async (t) => { + const dataGrid = new DataGrid('#container'); + const pager = dataGrid.getPager(); + + await t + .expect(pager.getInfoText().textContent) + .eql('Page 5 of 10 (100 items)'); + + await t + .click(pager.getPageSize(1).element) + .expect(pager.getInfoText().textContent) + .eql('Page 1 of 1 (100 items)'); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [...new Array(100).keys()].map((i) => ({ id: i })), + keyExpr: 'id', + showBorders: true, + scrolling: { + mode: 'standard', + }, + paging: { + pageSize: 10, + pageIndex: 4, + }, + pager: { + visible: true, + allowedPageSizes: [10, 'all'], + showPageSizeSelector: true, + showInfo: true, + showNavigationButtons: true, + }, + height: 400, + onOptionChanged(e){ + if (e.fullName === "paging.pageSize") { + const setVirtual = e.value === 0; + const targetRenderingMode = setVirtual ? "virtual" : "standard"; + const currentRenderingMode = e.component.option("scrolling.mode"); + if (currentRenderingMode !== targetRenderingMode) { + e.component.beginUpdate(); + e.component.option("scrolling.mode", targetRenderingMode); + e.component.repaint(); + e.component.endUpdate(); + } + } + }, +})); + test('No error should occur if dataSource is not defined and pageIndex is promise chained (T1256070)', async (t) => { const dataGrid = new DataGrid('#container'); diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts index 83a6ed50739e..d85ccd929d3f 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts @@ -43,6 +43,9 @@ const changePaging = function (that, optionName, value) { that._skipProcessingPagingChange = true; that.option(`paging.${optionName}`, value); + if (optionName === 'pageSize') { + that.option('paging.pageIndex', 0); + } that._skipProcessingPagingChange = false; const pageIndex = dataSource.pageIndex(); that._isPaging = optionName === 'pageIndex'; From bd3a6daae2dd52f629dcd7c8c01263f71e36df9f Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Mon, 11 May 2026 01:00:30 +0800 Subject: [PATCH 6/8] fix --- .../grids/grid_core/data_controller/m_data_controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts index d85ccd929d3f..01a945a94879 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts @@ -43,7 +43,7 @@ const changePaging = function (that, optionName, value) { that._skipProcessingPagingChange = true; that.option(`paging.${optionName}`, value); - if (optionName === 'pageSize') { + if (optionName === 'pageSize' && value === 0) { that.option('paging.pageIndex', 0); } that._skipProcessingPagingChange = false; From c1657fa6e253dae463bce681aff13e28da78a06e Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Mon, 11 May 2026 01:02:20 +0800 Subject: [PATCH 7/8] fix lint --- .../tests/dataGrid/common/pager.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts index d48d5adb7ee0..39e2bf4dc996 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts @@ -302,16 +302,16 @@ test('Pager info should show page 1 of 1 after changing pageSize to \'all\' and showNavigationButtons: true, }, height: 400, - onOptionChanged(e){ - if (e.fullName === "paging.pageSize") { + onOptionChanged: (e) => { + if (e.fullName === 'paging.pageSize') { const setVirtual = e.value === 0; - const targetRenderingMode = setVirtual ? "virtual" : "standard"; - const currentRenderingMode = e.component.option("scrolling.mode"); + const targetRenderingMode = setVirtual ? 'virtual' : 'standard'; + const currentRenderingMode = e.component.option('scrolling.mode'); if (currentRenderingMode !== targetRenderingMode) { - e.component.beginUpdate(); - e.component.option("scrolling.mode", targetRenderingMode); - e.component.repaint(); - e.component.endUpdate(); + e.component.beginUpdate(); + e.component.option('scrolling.mode', targetRenderingMode); + e.component.repaint(); + e.component.endUpdate(); } } }, From 65b83c760ab6f0da7db759d24d11d4674d1b1fc5 Mon Sep 17 00:00:00 2001 From: Eldar Iusupzhanov Date: Mon, 11 May 2026 01:12:31 +0800 Subject: [PATCH 8/8] fix lint --- e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts index 39e2bf4dc996..665d10e0c810 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/pager.ts @@ -312,7 +312,7 @@ test('Pager info should show page 1 of 1 after changing pageSize to \'all\' and e.component.option('scrolling.mode', targetRenderingMode); e.component.repaint(); e.component.endUpdate(); - } + } } }, }));