From 18a629a0ba72ec151c2f1bd64ffdee6747df4fa5 Mon Sep 17 00:00:00 2001
From: Alyar <>
Date: Fri, 10 Apr 2026 14:51:47 +0400
Subject: [PATCH 1/2] DataGrid: Fix detail grid reloading data when collapsed
master row reenters viewport with virtual row rendering (T1326188)
---
.../common/masterDetail/functional.ts | 117 ++++++++++++++++++
.../visual.ts} | 10 +-
.../data_controller/m_data_controller.ts | 4 +
.../master_detail/m_master_detail.ts | 1 +
4 files changed, 127 insertions(+), 5 deletions(-)
create mode 100644 e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/functional.ts
rename e2e/testcafe-devextreme/tests/dataGrid/common/{masterDetail.ts => masterDetail/visual.ts} (96%)
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/functional.ts
new file mode 100644
index 000000000000..92ff0b4f7236
--- /dev/null
+++ b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/functional.ts
@@ -0,0 +1,117 @@
+import { ClientFunction } from 'testcafe';
+import DataGrid from 'devextreme-testcafe-models/dataGrid';
+import url from '../../../../helpers/getPageUrl';
+import { createWidget } from '../../../../helpers/createWidget';
+
+const getDetailGridLoadCount = ClientFunction(() => (window as any).detailGridLoadCount as number);
+
+fixture.disablePageReloads`Master detail.Functional`
+ .page(url(__dirname, '../../../container.html'));
+
+test('Detail grid should not reload data when the collapsed master row reenters the viewport with standard scrolling and virtual row rendering (T1326188)', async (t) => {
+ // arrange
+ const dataGrid = new DataGrid('#container');
+
+ await t.expect(dataGrid.isReady()).ok();
+
+ // act - expand the master row
+ await t.click(dataGrid.getDataRow(0).getCommandCell(0).element);
+
+ let detailGrid = dataGrid.getMasterRow(0).getDataGrid();
+
+ // assert
+ await t
+ .expect(dataGrid.getDataRow(0).isExpanded)
+ .ok()
+ .expect(detailGrid.element.exists)
+ .ok()
+ .expect(getDetailGridLoadCount())
+ .eql(1);
+
+ // act - collapse the master row
+ await t.click(dataGrid.getDataRow(0).getCommandCell(0).element);
+
+ // assert
+ await t
+ .expect(dataGrid.getDataRow(0).isExpanded)
+ .notOk()
+ .expect(detailGrid.element.visible)
+ .notOk()
+ .expect(getDetailGridLoadCount())
+ .eql(1);
+
+ // act
+ await dataGrid.scrollTo(t, { y: 10000 });
+
+ // assert
+ await t
+ .expect(dataGrid.isReady())
+ .ok()
+ .expect(dataGrid.getDataRow(199).element.exists)
+ .ok();
+
+ // act
+ await dataGrid.scrollTo(t, { y: 0 });
+
+ detailGrid = dataGrid.getMasterRow(0).getDataGrid();
+
+ // assert
+ await t
+ .expect(dataGrid.isReady())
+ .ok()
+ .expect(dataGrid.getDataRow(0).element.exists)
+ .ok()
+ .expect(detailGrid.element.exists)
+ .notOk()
+ .expect(getDetailGridLoadCount())
+ .eql(1, 'Detail grid data should not be reloaded after scrolling back to the collapsed master row');
+}).before(async () => {
+ await ClientFunction(() => {
+ (window as any).detailGridLoadCount = 0;
+ })();
+
+ return createWidget('dxDataGrid', {
+ dataSource: [...new Array(200)].map((_, index) => ({
+ id: index,
+ text: `item ${index}`,
+ })),
+ keyExpr: 'id',
+ height: 400,
+ scrolling: {
+ mode: 'standard',
+ rowRenderingMode: 'virtual',
+ // @ts-expect-error private option
+ updateTimeout: 0,
+ useNative: false,
+ },
+ paging: {
+ enabled: false,
+ },
+ columns: ['id', 'text'],
+ masterDetail: {
+ enabled: true,
+ template(container) {
+ ($('
') as any)
+ .dxDataGrid({
+ keyExpr: 'id',
+ height: 180,
+ columns: ['id', 'text'],
+ dataSource: {
+ load() {
+ ((window as any).detailGridLoadCount as number) += 1;
+
+ return [
+ { id: 1, text: 'detail 1' },
+ { id: 2, text: 'detail 2' },
+ ];
+ },
+ key: 'id',
+ } as any,
+ })
+ .appendTo(container);
+ },
+ },
+ });
+}).after(async () => ClientFunction(() => {
+ delete (window as any).detailGridLoadCount;
+})());
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/visual.ts
similarity index 96%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail.ts
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/visual.ts
index 8a4aabdba5b8..2856fe1104aa 100644
--- a/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail.ts
+++ b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/visual.ts
@@ -1,12 +1,12 @@
import { ClientFunction } from 'testcafe';
import { createScreenshotsComparer } from 'devextreme-screenshot-comparer';
import DataGrid from 'devextreme-testcafe-models/dataGrid';
-import url from '../../../helpers/getPageUrl';
-import { createWidget } from '../../../helpers/createWidget';
-import { testScreenshot } from '../../../helpers/themeUtils';
+import url from '../../../../helpers/getPageUrl';
+import { createWidget } from '../../../../helpers/createWidget';
+import { testScreenshot } from '../../../../helpers/themeUtils';
-fixture.disablePageReloads`Master detail`
- .page(url(__dirname, '../../container.html'));
+fixture.disablePageReloads`Master detail.Visual`
+ .page(url(__dirname, '../../../container.html'));
// visual: material.blue.light
// visual: generic.light
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 54d8693d62c7..e8fe7487c881 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
@@ -1791,6 +1791,10 @@ export class DataController extends DataHelperMixin(modules.Controller) {
public isViewportChanging(): boolean {
return false;
}
+
+ public resetCachedProcessedItems(): void {
+ this._cachedProcessedItems = null;
+ }
}
export const dataControllerModule: Module = {
defaultOptions() {
diff --git a/packages/devextreme/js/__internal/grids/grid_core/master_detail/m_master_detail.ts b/packages/devextreme/js/__internal/grids/grid_core/master_detail/m_master_detail.ts
index aa6982bf02cd..9bddb33090bf 100644
--- a/packages/devextreme/js/__internal/grids/grid_core/master_detail/m_master_detail.ts
+++ b/packages/devextreme/js/__internal/grids/grid_core/master_detail/m_master_detail.ts
@@ -118,6 +118,7 @@ export const dataMasterDetailExtenderMixin = (Base: ModuleType)
changeType: 'update',
rowIndices: that._getRowIndicesForExpand(key),
});
+ that.resetCachedProcessedItems();
// @ts-expect-error
result = new Deferred().resolve();
From c8b07d94d65d5225d53776abe3f2f6c940e0194c Mon Sep 17 00:00:00 2001
From: Alyar <>
Date: Fri, 10 Apr 2026 14:56:39 +0400
Subject: [PATCH 2/2] move the etalons into the masterDetail.etalons folder
---
.../etalons/T1045321 (fluent.blue.light).png | Bin
...1113525.page-size-select (fluent.blue.light).png | Bin
...etail-with-renderAsync-1 (fluent.blue.light).png | Bin
...etail-with-renderAsync-2 (fluent.blue.light).png | Bin
...olling.useNative=false-1 (fluent.blue.light).png | Bin
...olling.useNative=false-2 (fluent.blue.light).png | Bin
...olling.useNative=false-3 (fluent.blue.light).png | Bin
...rolling.useNative=true-1 (fluent.blue.light).png | Bin
...rolling.useNative=true-2 (fluent.blue.light).png | Bin
...rolling.useNative=true-3 (fluent.blue.light).png | Bin
10 files changed, 0 insertions(+), 0 deletions(-)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1045321 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1113525.page-size-select (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1159578-master-detail-with-renderAsync-1 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1159578-master-detail-with-renderAsync-2 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1167889-master-detail-with-scrolling.useNative=false-1 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1167889-master-detail-with-scrolling.useNative=false-2 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1167889-master-detail-with-scrolling.useNative=false-3 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1167889-master-detail-with-scrolling.useNative=true-1 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1167889-master-detail-with-scrolling.useNative=true-2 (fluent.blue.light).png (100%)
rename e2e/testcafe-devextreme/tests/dataGrid/common/{ => masterDetail}/etalons/T1167889-master-detail-with-scrolling.useNative=true-3 (fluent.blue.light).png (100%)
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1045321 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1045321 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1045321 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1045321 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1113525.page-size-select (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1113525.page-size-select (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1113525.page-size-select (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1113525.page-size-select (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1159578-master-detail-with-renderAsync-1 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1159578-master-detail-with-renderAsync-1 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1159578-master-detail-with-renderAsync-1 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1159578-master-detail-with-renderAsync-1 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1159578-master-detail-with-renderAsync-2 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1159578-master-detail-with-renderAsync-2 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1159578-master-detail-with-renderAsync-2 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1159578-master-detail-with-renderAsync-2 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=false-1 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=false-1 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=false-1 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=false-1 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=false-2 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=false-2 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=false-2 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=false-2 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=false-3 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=false-3 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=false-3 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=false-3 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=true-1 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=true-1 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=true-1 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=true-1 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=true-2 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=true-2 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=true-2 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=true-2 (fluent.blue.light).png
diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=true-3 (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=true-3 (fluent.blue.light).png
similarity index 100%
rename from e2e/testcafe-devextreme/tests/dataGrid/common/etalons/T1167889-master-detail-with-scrolling.useNative=true-3 (fluent.blue.light).png
rename to e2e/testcafe-devextreme/tests/dataGrid/common/masterDetail/etalons/T1167889-master-detail-with-scrolling.useNative=true-3 (fluent.blue.light).png