From 3e3a12fca0f4098fe835739c20550c0e98f50163 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Tue, 3 Mar 2026 17:59:54 +0800 Subject: [PATCH 01/18] test --- .../dataGrid/common/markup/noDataText.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts index b0114896f015..d6bf94b8062e 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts @@ -1,5 +1,6 @@ import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; import DataGrid from 'devextreme-testcafe-models/dataGrid'; +import List from 'devextreme-testcafe-models/list'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; import { testScreenshot } from '../../../../helpers/themeUtils'; @@ -8,6 +9,7 @@ fixture.disablePageReloads`No Data` .page(url(__dirname, '../../../container.html')); const GRID_CONTAINER = '#container'; +const OVERLAY_SELECTOR = '.dx-overlay-wrapper'; test('The noDataText element should be centered (T1178289)', async (t) => { const { takeScreenshot, compareResults } = createScreenshotsComparer(t); @@ -31,3 +33,50 @@ test('The noDataText element should be centered (T1178289)', async (t) => { }, }); }); + +test('The noDataText element should be rendered when a lookup column is filtered (T1293839)', async (t) => { + // arrange + const { takeScreenshot, compareResults } = createScreenshotsComparer(t); + const dataGrid = new DataGrid(GRID_CONTAINER); + const filterRow = dataGrid.getHeaders().getFilterRow(); + const nameFilterCell = filterRow.getFilterCell(0); + const nameFilterEditor = nameFilterCell.getEditorInput(); + const lookupFilterCell = filterRow.getFilterCell(1); + + // act + await t.click(lookupFilterCell.element); + const lookupList = new List(OVERLAY_SELECTOR); + const lookupItem = lookupList.getItem(1); + await t.click(lookupItem.element); + + await t.typeText(nameFilterEditor.element, 'test'); + + await dataGrid.isReady(); + + // assert + await testScreenshot(t, takeScreenshot, 'T1293839-grid-no-data-text-rendered.png', { element: dataGrid.element }); + await t + .expect(compareResults.isValid()) + .ok(compareResults.errorMessages()); +}).before(async () => { + await createWidget('dxDataGrid', { + dataSource: [ + { ID: 1, Name: 'John', Lookup: 1 }, + { ID: 2, Name: 'Jane', Lookup: 2 }, + ], + keyExpr: 'ID', + columns: ['Name', { + dataField: 'Lookup', + lookup: { + dataSource: [ + { ID: 1, Text: 'Item 1' }, + { ID: 2, Text: 'Item 2' }, + ], + valueExpr: 'ID', + displayExpr: 'Text', + }, + }], + showBorders: true, + filterRow: { visible: true }, + }); +}); From 1c7ae24636ac0b21d771f5fd2f05aba8dc117c9c Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Tue, 3 Mar 2026 18:22:47 +0800 Subject: [PATCH 02/18] update test --- .../dataGrid/common/markup/noDataText.ts | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts index d6bf94b8062e..7c0325815f8e 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts @@ -11,29 +11,6 @@ fixture.disablePageReloads`No Data` const GRID_CONTAINER = '#container'; const OVERLAY_SELECTOR = '.dx-overlay-wrapper'; -test('The noDataText element should be centered (T1178289)', async (t) => { - const { takeScreenshot, compareResults } = createScreenshotsComparer(t); - const dataGrid = new DataGrid(GRID_CONTAINER); - - await dataGrid.option('dataSource', []); - - await testScreenshot(t, takeScreenshot, 'grid-no-data-text-position.png', { element: dataGrid.element }); - await t - .expect(compareResults.isValid()) - .ok(compareResults.errorMessages()); -}).before(async () => { - await createWidget('dxDataGrid', { - columns: ['column1', 'column2', 'column3', 'column4', 'column5'], - showBorders: true, - columnMinWidth: 200, - width: 600, - stateStoring: { - enabled: true, - storageKey: 'testStorageKey', - }, - }); -}); - test('The noDataText element should be rendered when a lookup column is filtered (T1293839)', async (t) => { // arrange const { takeScreenshot, compareResults } = createScreenshotsComparer(t); @@ -48,12 +25,13 @@ test('The noDataText element should be rendered when a lookup column is filtered const lookupList = new List(OVERLAY_SELECTOR); const lookupItem = lookupList.getItem(1); await t.click(lookupItem.element); - await t.typeText(nameFilterEditor.element, 'test'); - await dataGrid.isReady(); - // assert + await t + .expect(dataGrid.isReady()) + .ok(); + await testScreenshot(t, takeScreenshot, 'T1293839-grid-no-data-text-rendered.png', { element: dataGrid.element }); await t .expect(compareResults.isValid()) @@ -80,3 +58,26 @@ test('The noDataText element should be rendered when a lookup column is filtered filterRow: { visible: true }, }); }); + +test('The noDataText element should be centered (T1178289)', async (t) => { + const { takeScreenshot, compareResults } = createScreenshotsComparer(t); + const dataGrid = new DataGrid(GRID_CONTAINER); + + await dataGrid.option('dataSource', []); + + await testScreenshot(t, takeScreenshot, 'grid-no-data-text-position.png', { element: dataGrid.element }); + await t + .expect(compareResults.isValid()) + .ok(compareResults.errorMessages()); +}).before(async () => { + await createWidget('dxDataGrid', { + columns: ['column1', 'column2', 'column3', 'column4', 'column5'], + showBorders: true, + columnMinWidth: 200, + width: 600, + stateStoring: { + enabled: true, + storageKey: 'testStorageKey', + }, + }); +}); From 22c79eef20e3bffcac23f439ea6bbea62f8d5e20 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Tue, 3 Mar 2026 18:23:08 +0800 Subject: [PATCH 03/18] fix --- .../js/__internal/grids/grid_core/m_utils.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index f48397602623..bb772a1eb8a0 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -211,8 +211,6 @@ export default { const noDataClass = that.addWidgetPrefix(NO_DATA_CLASS); let noDataElement = $element.find(`.${noDataClass}`).last(); - const isVisible = this._dataController.isEmpty(); - const isLoading = this._dataController.isLoading(); if (!noDataElement.length) { noDataElement = $('') @@ -223,14 +221,17 @@ export default { noDataElement.appendTo($element); } - if (isVisible && !isLoading) { - noDataElement - .removeClass('dx-hidden') - .text(that._getNoDataText()); - } else { - noDataElement - .addClass('dx-hidden'); - } + this._dataController.waitReady().done(() => { + const isVisible = this._dataController.isEmpty(); + if (isVisible) { + noDataElement + .removeClass('dx-hidden') + .text(that._getNoDataText()); + } else { + noDataElement + .addClass('dx-hidden'); + } + }); }, renderLoadPanel($element, $container, isLocalStore) { From d54ed8ccfd52247df89a004d46feb2b6f5bff9f7 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Tue, 3 Mar 2026 19:53:15 +0800 Subject: [PATCH 04/18] update test --- .../tests/dataGrid/common/markup/noDataText.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts index 7c0325815f8e..85171e6c6bf7 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts @@ -56,6 +56,9 @@ test('The noDataText element should be rendered when a lookup column is filtered }], showBorders: true, filterRow: { visible: true }, + onEditorPreparing(e) { + e.updateValueTimeout = 0; + }, }); }); From 80d1c85e9f121048af39c8903feb18c3306e66ae Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Tue, 3 Mar 2026 20:21:59 +0800 Subject: [PATCH 05/18] etalon --- ...no-data-text-rendered (fluent.blue.light).png | Bin 0 -> 6552 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 e2e/testcafe-devextreme/tests/dataGrid/common/markup/etalons/T1293839-grid-no-data-text-rendered (fluent.blue.light).png diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/etalons/T1293839-grid-no-data-text-rendered (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/etalons/T1293839-grid-no-data-text-rendered (fluent.blue.light).png new file mode 100644 index 0000000000000000000000000000000000000000..6b8fd751c6005f5ff50de06856966c65ef14e1bd GIT binary patch literal 6552 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 v?zs=2{=fO3guvp^5gnbqKP;eu Date: Tue, 3 Mar 2026 20:22:31 +0800 Subject: [PATCH 06/18] update fix --- .../js/__internal/grids/grid_core/m_utils.ts | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index bb772a1eb8a0..1fcd89383ef5 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -211,6 +211,7 @@ export default { const noDataClass = that.addWidgetPrefix(NO_DATA_CLASS); let noDataElement = $element.find(`.${noDataClass}`).last(); + const isVisible = this._dataController.isEmpty(); if (!noDataElement.length) { noDataElement = $('') @@ -221,17 +222,14 @@ export default { noDataElement.appendTo($element); } - this._dataController.waitReady().done(() => { - const isVisible = this._dataController.isEmpty(); - if (isVisible) { - noDataElement - .removeClass('dx-hidden') - .text(that._getNoDataText()); - } else { - noDataElement - .addClass('dx-hidden'); - } - }); + if (isVisible) { + noDataElement + .removeClass('dx-hidden') + .text(that._getNoDataText()); + } else { + noDataElement + .addClass('dx-hidden'); + } }, renderLoadPanel($element, $container, isLocalStore) { From 72fd464385cf9e304af23b54ef93f22854bdb7fe Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Wed, 4 Mar 2026 20:30:13 +0800 Subject: [PATCH 07/18] Revert "update fix" This reverts commit 9ffb359f30c42ec99fc47742a6ed2b97d75a8dfb. --- .../js/__internal/grids/grid_core/m_utils.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index 1fcd89383ef5..bb772a1eb8a0 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -211,7 +211,6 @@ export default { const noDataClass = that.addWidgetPrefix(NO_DATA_CLASS); let noDataElement = $element.find(`.${noDataClass}`).last(); - const isVisible = this._dataController.isEmpty(); if (!noDataElement.length) { noDataElement = $('') @@ -222,14 +221,17 @@ export default { noDataElement.appendTo($element); } - if (isVisible) { - noDataElement - .removeClass('dx-hidden') - .text(that._getNoDataText()); - } else { - noDataElement - .addClass('dx-hidden'); - } + this._dataController.waitReady().done(() => { + const isVisible = this._dataController.isEmpty(); + if (isVisible) { + noDataElement + .removeClass('dx-hidden') + .text(that._getNoDataText()); + } else { + noDataElement + .addClass('dx-hidden'); + } + }); }, renderLoadPanel($element, $container, isLocalStore) { From 58ec07be0f2828504a5f09593f9e4c8efe259c12 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Wed, 4 Mar 2026 20:31:55 +0800 Subject: [PATCH 08/18] fix --- .../js/__internal/grids/grid_core/filter/m_filter_row.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts b/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts index 9d6e2ffd439d..a819f471e4cc 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts @@ -729,7 +729,9 @@ const columnHeadersView = (Base: ModuleType) => class ColumnH super._handleDataChanged.apply(this, arguments); if (e.operationTypes?.filtering || e.operationTypes?.fullReload) { - this.updateLookupDataSource(e.operationTypes?.filtering || lastLoadOptions?.filter); + this._dataController.waitReady().then(() => { + this.updateLookupDataSource(e.operationTypes?.filtering || lastLoadOptions?.filter); + }); } } From 9b955ef8f367c48c13df61f21d7768a0253da9d7 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Wed, 4 Mar 2026 20:36:43 +0800 Subject: [PATCH 09/18] Revert "fix" This reverts commit b7836c119f300d388caacadc81441d0fadbecd7c. --- .../js/__internal/grids/grid_core/m_utils.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index bb772a1eb8a0..f48397602623 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -211,6 +211,8 @@ export default { const noDataClass = that.addWidgetPrefix(NO_DATA_CLASS); let noDataElement = $element.find(`.${noDataClass}`).last(); + const isVisible = this._dataController.isEmpty(); + const isLoading = this._dataController.isLoading(); if (!noDataElement.length) { noDataElement = $('') @@ -221,17 +223,14 @@ export default { noDataElement.appendTo($element); } - this._dataController.waitReady().done(() => { - const isVisible = this._dataController.isEmpty(); - if (isVisible) { - noDataElement - .removeClass('dx-hidden') - .text(that._getNoDataText()); - } else { - noDataElement - .addClass('dx-hidden'); - } - }); + if (isVisible && !isLoading) { + noDataElement + .removeClass('dx-hidden') + .text(that._getNoDataText()); + } else { + noDataElement + .addClass('dx-hidden'); + } }, renderLoadPanel($element, $container, isLocalStore) { From 2dd19463ba508b1bf431c80e618937cbd4244463 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Thu, 5 Mar 2026 15:11:42 +0800 Subject: [PATCH 10/18] Revert "fix" This reverts commit 9a233a74cb6e9aad336b0c89895502398d072a2f. --- .../js/__internal/grids/grid_core/filter/m_filter_row.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts b/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts index a819f471e4cc..9d6e2ffd439d 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts @@ -729,9 +729,7 @@ const columnHeadersView = (Base: ModuleType) => class ColumnH super._handleDataChanged.apply(this, arguments); if (e.operationTypes?.filtering || e.operationTypes?.fullReload) { - this._dataController.waitReady().then(() => { - this.updateLookupDataSource(e.operationTypes?.filtering || lastLoadOptions?.filter); - }); + this.updateLookupDataSource(e.operationTypes?.filtering || lastLoadOptions?.filter); } } From 41add3326598d9237dd5227e4c8df3c7e2ace0a5 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Thu, 5 Mar 2026 15:12:02 +0800 Subject: [PATCH 11/18] fix --- .../js/__internal/grids/grid_core/views/m_rows_view.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts index 90320c2c1edd..41d02b517cbe 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts @@ -148,6 +148,9 @@ export class RowsView extends ColumnsView { this._contentChanges = []; this._dataController.loadingChanged.add((isLoading, messageText) => { this.setLoading(isLoading, messageText); + if (!isLoading) { + this.renderNoDataText(); + } }); this._dataController.dataSourceChanged.add(() => { From 824fa7a924ad55158411e587c631d145b5179604 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Fri, 6 Mar 2026 19:22:44 +0800 Subject: [PATCH 12/18] Revert "fix" This reverts commit e66ecca9bbe095442d3e8e18c36d1a5e6c8e089e. --- .../js/__internal/grids/grid_core/views/m_rows_view.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts index 41d02b517cbe..90320c2c1edd 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/views/m_rows_view.ts @@ -148,9 +148,6 @@ export class RowsView extends ColumnsView { this._contentChanges = []; this._dataController.loadingChanged.add((isLoading, messageText) => { this.setLoading(isLoading, messageText); - if (!isLoading) { - this.renderNoDataText(); - } }); this._dataController.dataSourceChanged.add(() => { From c27a8f387051826ae0f579810f9c8dcfe17e1802 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Fri, 6 Mar 2026 19:31:18 +0800 Subject: [PATCH 13/18] fix --- .../grids/grid_core/data_controller/m_data_controller.ts | 2 +- packages/devextreme/js/__internal/grids/grid_core/m_utils.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 64f8a1f49c34..54d8693d62c7 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 @@ -1633,7 +1633,7 @@ export class DataController extends DataHelperMixin(modules.Controller) { } public isCustomLoading() { - return this._isCustomLoading; + return this._isCustomLoading || this._dataSource?.isCustomLoading(); } public beginCustomLoading(messageText?: string) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index f48397602623..dd3bc28f83a6 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -212,7 +212,7 @@ export default { const noDataClass = that.addWidgetPrefix(NO_DATA_CLASS); let noDataElement = $element.find(`.${noDataClass}`).last(); const isVisible = this._dataController.isEmpty(); - const isLoading = this._dataController.isLoading(); + const isDefaultLoading = this._dataController.isLoading() && !this._dataController.isCustomLoading(); if (!noDataElement.length) { noDataElement = $('') @@ -223,7 +223,7 @@ export default { noDataElement.appendTo($element); } - if (isVisible && !isLoading) { + if (isVisible && !isDefaultLoading) { noDataElement .removeClass('dx-hidden') .text(that._getNoDataText()); From 696541a4cdce95fef46a09b3f385898aa1480c80 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Fri, 6 Mar 2026 20:01:54 +0800 Subject: [PATCH 14/18] fix --- packages/devextreme/js/__internal/grids/grid_core/m_utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts index dd3bc28f83a6..c62fe4ba1d6d 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -212,7 +212,7 @@ export default { const noDataClass = that.addWidgetPrefix(NO_DATA_CLASS); let noDataElement = $element.find(`.${noDataClass}`).last(); const isVisible = this._dataController.isEmpty(); - const isDefaultLoading = this._dataController.isLoading() && !this._dataController.isCustomLoading(); + const isDefaultLoading = this._dataController.isLoading() && !this._dataController.isCustomLoading?.(); if (!noDataElement.length) { noDataElement = $('') From ffc61a50d673199042bae6c2c6b4dbb74d8ab081 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Mon, 9 Mar 2026 13:53:30 +0800 Subject: [PATCH 15/18] test await grid ready --- .../tests/dataGrid/common/markup/noDataText.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts index 85171e6c6bf7..db70e862f37d 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts @@ -20,6 +20,11 @@ test('The noDataText element should be rendered when a lookup column is filtered const nameFilterEditor = nameFilterCell.getEditorInput(); const lookupFilterCell = filterRow.getFilterCell(1); + // assert + await t + .expect(dataGrid.isReady()) + .ok(); + // act await t.click(lookupFilterCell.element); const lookupList = new List(OVERLAY_SELECTOR); From e5d448bf9f26d8560999afdc7a4f297964962993 Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Mon, 9 Mar 2026 17:12:51 +0800 Subject: [PATCH 16/18] refactor --- .../tests/accessibility/dataGrid/common.ts | 7 ++-- .../tests/accessibility/dataGrid/status.ts | 4 +-- .../dataGrid/common/accessibility/common.ts | 7 ++-- .../dataGrid/common/accessibility/contrast.ts | 9 +++--- .../dataGrid/common/filterPanel/functional.ts | 3 +- ...T1163100_changeFIlterIcon.visual_matrix.ts | 7 ++-- .../dataGrid/common/filterRow/functional.ts | 32 +++++++++---------- .../tests/dataGrid/common/filterRow/visual.ts | 16 +++++----- .../tests/dataGrid/common/focus/focus.ts | 4 +-- .../dataGrid/editors/filterTextBox.ts | 25 --------------- .../testcafe-models/dataGrid/filter/cell.ts | 17 ++++++++-- packages/testcafe-models/dataGrid/index.ts | 12 ++++--- 12 files changed, 63 insertions(+), 80 deletions(-) delete mode 100644 packages/testcafe-models/dataGrid/editors/filterTextBox.ts diff --git a/e2e/testcafe-devextreme/tests/accessibility/dataGrid/common.ts b/e2e/testcafe-devextreme/tests/accessibility/dataGrid/common.ts index 2159b791977c..3e99dd821a93 100644 --- a/e2e/testcafe-devextreme/tests/accessibility/dataGrid/common.ts +++ b/e2e/testcafe-devextreme/tests/accessibility/dataGrid/common.ts @@ -1,5 +1,4 @@ import DataGrid from 'devextreme-testcafe-models/dataGrid'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; import HeaderFilter from 'devextreme-testcafe-models/dataGrid/headers/headerFilter'; import { a11yCheck } from '../../../helpers/accessibility/utils'; import url from '../../../helpers/getPageUrl'; @@ -183,16 +182,16 @@ test('Grouping and Summary', async (t) => { test('Filter row - filter menu', async (t) => { const dataGrid = new DataGrid(DATA_GRID_SELECTOR); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); + const filterCell = dataGrid.getFilterCell(0); await t .expect(dataGrid.isReady()) .ok(); - await t.click(filterEditor.menuButton); + await t.click(filterCell.menuButton); await t - .expect(filterEditor.menu.isOpened) + .expect(filterCell.menu.isOpened) .ok(); await a11yCheck(t, { diff --git a/e2e/testcafe-devextreme/tests/accessibility/dataGrid/status.ts b/e2e/testcafe-devextreme/tests/accessibility/dataGrid/status.ts index 21936dfeaba4..a6c5f87ccb2d 100644 --- a/e2e/testcafe-devextreme/tests/accessibility/dataGrid/status.ts +++ b/e2e/testcafe-devextreme/tests/accessibility/dataGrid/status.ts @@ -1,6 +1,6 @@ import DataGrid from 'devextreme-testcafe-models/dataGrid'; import HeaderFilter from 'devextreme-testcafe-models/dataGrid/headers/headerFilter'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; +import TextBox from 'devextreme-testcafe-models/textBox'; import { a11yCheck } from '../../../helpers/accessibility/utils'; import url from '../../../helpers/getPageUrl'; import { createWidget } from '../../../helpers/createWidget'; @@ -73,7 +73,7 @@ test('Accessibility: DataGrid general status should contains correct text after test('Accessibility: DataGrid general status should contains correct text after filter row interaction', async (t) => { const expectedStatusText = 'Data grid with 1 rows and 2 columns'; const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); + const filterEditor = dataGrid.getFilterEditor(0, TextBox); const applyFilterBtn = dataGrid.getHeaderPanel().getApplyFilterButton(); await t.typeText(filterEditor.input(), 'A') diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/common.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/common.ts index 5daf6dd1d704..626b2baa75f0 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/common.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/common.ts @@ -1,6 +1,5 @@ import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; import DataGrid from 'devextreme-testcafe-models/dataGrid'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; import HeaderFilter from 'devextreme-testcafe-models/dataGrid/headers/headerFilter'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; @@ -182,16 +181,16 @@ test('Grouping and Summary', async (t) => { test('Filter row - filter menu', async (t) => { const dataGrid = new DataGrid(DATA_GRID_SELECTOR); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); + const filterCell = dataGrid.getFilterCell(0); await t .expect(dataGrid.isReady()) .ok(); - await t.click(filterEditor.menuButton); + await t.click(filterCell.menuButton); await t - .expect(filterEditor.menu.isOpened) + .expect(filterCell.menu.isOpened) .ok(); await screenshotCheck(t, 'filter-row-menu'); diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/contrast.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/contrast.ts index 6a2f7b6d5f95..3f0dc0a5952c 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/contrast.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/accessibility/contrast.ts @@ -1,6 +1,5 @@ import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; import DataGrid from 'devextreme-testcafe-models/dataGrid'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; import { getData } from '../../helpers/generateDataSourceData'; @@ -15,9 +14,9 @@ fixture.disablePageReloads`DataGrid - contrast` // visual: material.blue.light test('DataGrid - Contrast between icons in the Filter Row menu and their background doesn\'t comply with WCAG accessibility standards', async (t) => { const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); - const searchButton = filterEditor.menuButton; - const filterMenu = filterEditor.menu; + const filterCell = dataGrid.getFilterCell(0); + const searchButton = filterCell.menuButton; + const filterMenu = filterCell.menu; const { takeScreenshot, compareResults } = createScreenshotsComparer(t); await t @@ -63,7 +62,7 @@ test('DataGrid - Filter icon should remain visible when it\'s focused', async (t .element; await t - .click(dataGrid.getFilterCell(0)) + .click(dataGrid.getFilterCell(0).element) .pressKey('tab') .expect(searchIconContainer.focused) .ok(); diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/filterPanel/functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/filterPanel/functional.ts index 62ca30bb4aa0..1f91e5bc6eaa 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/filterPanel/functional.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/filterPanel/functional.ts @@ -1,7 +1,6 @@ import DataGrid from 'devextreme-testcafe-models/dataGrid'; import Popup from 'devextreme-testcafe-models/popup'; import FilterBuilder from 'devextreme-testcafe-models/filterBuilder'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; @@ -52,7 +51,7 @@ test('Proper handle custom filter operations for dates with non-date values', as .expect(filterPanel.getFilterText().element.innerText) .eql('[Order Date] Weekends'); - const dateFilterCell = dataGrid.getFilterEditor(1, FilterTextBox); + const dateFilterCell = dataGrid.getFilterCell(1); await t .click(dateFilterCell.menuButton) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/T1163100_changeFIlterIcon.visual_matrix.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/T1163100_changeFIlterIcon.visual_matrix.ts index 6b39fdb6c9ad..d28c43f7698c 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/T1163100_changeFIlterIcon.visual_matrix.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/T1163100_changeFIlterIcon.visual_matrix.ts @@ -1,6 +1,5 @@ import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; import DataGrid from 'devextreme-testcafe-models/dataGrid'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; import { createWidget } from '../../../../helpers/createWidget'; import url from '../../../../helpers/getPageUrl'; import { testScreenshot } from '../../../../helpers/themeUtils'; @@ -36,10 +35,10 @@ const generateTestData = (rowCount: number) => new Array(rowCount) const dataGrid = new DataGrid(GRID_SELECTOR); for (let columnIdx = 0; columnIdx < 4; columnIdx += 1) { - const filterMenu = dataGrid.getFilterEditor(columnIdx, FilterTextBox); + const filterCell = dataGrid.getFilterCell(columnIdx); await t - .click(filterMenu.menuButton()) - .click(filterMenu.menu.getItemByText('Starts with')); + .click(filterCell.menuButton()) + .click(filterCell.menu.getItemByText('Starts with')); } await testScreenshot( diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/functional.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/functional.ts index cce7108c020d..a012b41c15a7 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/functional.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/functional.ts @@ -1,5 +1,5 @@ import DataGrid from 'devextreme-testcafe-models/dataGrid'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; +import TextBox from 'devextreme-testcafe-models/textBox'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; import { getData } from '../../helpers/generateDataSourceData'; @@ -9,7 +9,7 @@ fixture`FilterRow` test('Filter should reset if the filter row editor text is cleared (T1257261)', async (t) => { const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(1, FilterTextBox); + const filterEditor = dataGrid.getFilterEditor(1, TextBox); const filterPanelText = dataGrid.getFilterPanel().getFilterText(); await t @@ -53,7 +53,7 @@ test('Filter should reset if the filter row editor text is cleared (T1257261)', // T1267481 test('Filter Row\'s Reset button does not work after a custom filter is set in Filter Builder', async (t) => { const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); + const filterCell = dataGrid.getFilterCell(0); await dataGrid.isReady(); @@ -62,8 +62,8 @@ test('Filter Row\'s Reset button does not work after a custom filter is set in F .eql(0); await t - .click(filterEditor.menuButton) - .click(filterEditor.menu.getItemByText('Reset')); + .click(filterCell.menuButton) + .click(filterCell.menu.getItemByText('Reset')); await t .expect(dataGrid.dataRows.count) @@ -104,11 +104,11 @@ test('Filter Row\'s Reset button does not work after a custom filter is set in F // T1290381 test('DataGrid - filter row\'s search-box\'s aria-label should be customizable via localization', async (t) => { const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); + const filterCell = dataGrid.getFilterCell(0); await dataGrid.isReady(); - const ariaLabel = await filterEditor.menuButton.getAttribute('aria-label'); + const ariaLabel = await filterCell.menuButton.getAttribute('aria-label'); await t .expect(ariaLabel) @@ -135,27 +135,27 @@ test('DataGrid - filter row\'s search-box\'s aria-label should be customizable v test('DataGrid - NVDA reads filter menu items as "Search box 1 of 8" (T1290386)', async (t) => { const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); + const filterCell = dataGrid.getFilterCell(0); await dataGrid.isReady(); await t - .expect(filterEditor.menuButton.getAttribute('aria-label')) + .expect(filterCell.menuButton.getAttribute('aria-label')) .eql('Search box'); await t - .click(filterEditor.menuButton); + .click(filterCell.menuButton); - const itemCount = await filterEditor.menu.getItemCount(); + const itemCount = await filterCell.menu.getItemCount(); for (let i = 0; i < itemCount; i += 1) { - const item = filterEditor.menu.getItemByIndex(i); + const item = filterCell.menu.getItemByIndex(i); await t.expect(item.getAttribute('aria-label')).eql(null); } await t - .click(filterEditor.menu.getItemByText('Equals')) - .expect(filterEditor.menuButton.getAttribute('aria-label')) + .click(filterCell.menu.getItemByText('Equals')) + .expect(filterCell.menuButton.getAttribute('aria-label')) .eql('Equals'); }).before(async () => createWidget('dxDataGrid', { dataSource: getData(5, 1), @@ -173,7 +173,7 @@ test('DataGrid - NVDA reads filter menu items as "Search box 1 of 8" (T1290386)' const expectedFocusedElement = grouped ? dataGrid.getGroupRow(0) : dataGrid.getDataCell(0, 0); await t - .click(filterCell) + .click(filterCell.element) .expect(dataGrid.getFilterRangeOverlay().exists) .ok('Filter range overlay is shown') .pressKey('tab') @@ -209,7 +209,7 @@ test('DataGrid - filter range overlay in last column on Tab pressed moves focus const filterCell = dataGrid.getFilterCell(2); await t - .click(filterCell) + .click(filterCell.element) .expect(dataGrid.getFilterRangeOverlay().exists) .ok('Filter range overlay is shown') .pressKey('tab') diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/visual.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/visual.ts index 5060033d7b36..cdd90ab7d7e6 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/visual.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/filterRow/visual.ts @@ -1,6 +1,6 @@ import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; import DataGrid from 'devextreme-testcafe-models/dataGrid'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; +import TextBox from 'devextreme-testcafe-models/textBox'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; import { getNumberData } from '../../helpers/generateDataSourceData'; @@ -35,12 +35,12 @@ test.meta({ test('FilterRow range overlay screenshot', async (t) => { const { takeScreenshot, compareResults } = createScreenshotsComparer(t); const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(1, FilterTextBox); + const filterCell = dataGrid.getFilterCell(1); await t - .click(filterEditor.menuButton); + .click(filterCell.menuButton); await t - .click(filterEditor.menu.getItemByText('Between')); + .click(filterCell.menu.getItemByText('Between')); // act await testScreenshot(t, takeScreenshot, 'filter-row-overlay.png'); await t @@ -68,7 +68,7 @@ test('FilterRow range overlay screenshot', async (t) => { test('Focus overlay should be visible in filter row when focusedRowEnabled is enabled', async (t) => { const { takeScreenshot, compareResults } = createScreenshotsComparer(t); const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(1, FilterTextBox); + const filterEditor = dataGrid.getFilterEditor(1, TextBox); await t .click(dataGrid.getDataCell(0, 0).element) @@ -96,13 +96,13 @@ test('Focus overlay should be visible in filter row when focusedRowEnabled is en test('DataGrid - The `between` filter dropdown sticks to the viewport edge during horizontal scrolling (T1280071)', async (t) => { const { takeScreenshot, compareResults } = createScreenshotsComparer(t); const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox); + const filterCell = dataGrid.getFilterCell(0); await dataGrid.isReady(); await t - .click(filterEditor.menuButton) - .click(filterEditor.menu.getItemByText('Between')); + .click(filterCell.menuButton) + .click(filterCell.menu.getItemByText('Between')); await dataGrid.scrollBy(t, { x: 999 }); await testScreenshot(t, takeScreenshot, 'filter-row-filter-range-hide-on-scroll.png'); diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/focus/focus.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/focus/focus.ts index 2cc4beefe97f..4b30c707b9eb 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/focus/focus.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/focus/focus.ts @@ -1,6 +1,6 @@ import DataGrid from 'devextreme-testcafe-models/dataGrid'; import { ClientFunction } from 'testcafe'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; +import TextBox from 'devextreme-testcafe-models/textBox'; import { createWidget } from '../../../../helpers/createWidget'; import url from '../../../../helpers/getPageUrl'; @@ -152,7 +152,7 @@ test('Should remove dx-focused class on blur event from the cell', async (t) => test('DataGrid - FilterRow cell loses focus when focusedRowEnabled is true and editing is in batch mode (T1246926)', async (t) => { const dataGrid = new DataGrid('#container'); - const filterEditor = dataGrid.getFilterEditor(0, FilterTextBox).getInput(); + const filterEditor = dataGrid.getFilterEditor(0, TextBox).getInput(); await t .click(dataGrid.getDataCell(0, 0).element) diff --git a/packages/testcafe-models/dataGrid/editors/filterTextBox.ts b/packages/testcafe-models/dataGrid/editors/filterTextBox.ts deleted file mode 100644 index 7e273e3552e3..000000000000 --- a/packages/testcafe-models/dataGrid/editors/filterTextBox.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Selector } from 'testcafe'; -import ContextMenu from '../../contextMenu'; -import TextBox from '../../textBox'; - -const CLASS = { - menuButton: 'dx-menu-item', - filterMenu: 'dx-context-menu', - gridMarker: 'dx-datagrid', -}; - -export default class FilterTextBox extends TextBox { - body: Selector; - - menuButton: Selector; - - menu: ContextMenu; - - constructor(selector: Selector) { - super(selector); - - this.body = Selector('body'); - this.menuButton = this.element.find(`.${CLASS.menuButton}`).nth(0); - this.menu = new ContextMenu(this.body.find(`.${CLASS.gridMarker}.${CLASS.filterMenu}`)); - } -} diff --git a/packages/testcafe-models/dataGrid/filter/cell.ts b/packages/testcafe-models/dataGrid/filter/cell.ts index 78550e4eea0e..4bb42a56ba50 100644 --- a/packages/testcafe-models/dataGrid/filter/cell.ts +++ b/packages/testcafe-models/dataGrid/filter/cell.ts @@ -1,18 +1,29 @@ +import { Selector } from 'testcafe'; +import ContextMenu from '../../contextMenu'; import FocusableElement from '../../internal/focusable'; const CLASS = { filterMenu: 'dx-filter-menu', editorInput: 'dx-texteditor-input', - filterEditor: 'dx-editor-with-menu', + filterEditor: 'dx-widget', focused: 'dx-focused', + menuButton: 'dx-menu-item', + contextMenu: 'dx-context-menu', + gridMarker: 'dx-datagrid', }; export default class FilterCell extends FocusableElement { isFocused: Promise; + + menuButton: Selector; + + menu: ContextMenu; constructor(element: Selector) { super(element); this.isFocused = this.element.hasClass(CLASS.focused); + this.menuButton = this.element.find(`.${CLASS.menuButton}`).nth(0); + this.menu = new ContextMenu(Selector('body').find(`.${CLASS.gridMarker}.${CLASS.contextMenu}`)); } getSearchIcon(): FocusableElement { @@ -23,7 +34,7 @@ export default class FilterCell extends FocusableElement { return new FocusableElement(this.element.find(`.${CLASS.editorInput}`)); } - getEditor(): Selector { - return this.element.find(`.${CLASS.filterEditor}`); + getEditor(EditorType: new (mainElement: Selector) => T): T { + return new EditorType(this.element.find(`.${CLASS.filterEditor}`)); } } diff --git a/packages/testcafe-models/dataGrid/index.ts b/packages/testcafe-models/dataGrid/index.ts index d97dee718e82..081a5b388722 100644 --- a/packages/testcafe-models/dataGrid/index.ts +++ b/packages/testcafe-models/dataGrid/index.ts @@ -4,6 +4,8 @@ import type { SelectionSensitivity } from 'devextreme/ui/data_grid'; import Toolbar from '../toolbar'; import DataRow from './data/row'; import GroupRow from './groupRow'; +import FilterRow from './filter/row'; +import FilterCell from './filter/cell'; import EditForm from './editForm'; import HeaderPanel from './headers/panel'; import DataCell from './data/cell'; @@ -240,12 +242,12 @@ export default class DataGrid extends GridCore { return this.element.find(`.${CLASS.errorRow}`); } - getFilterRow(): Selector { - return this.element.find(`.${this.addWidgetPrefix(CLASS.filterRow)}`); + getFilterRow(): FilterRow { + return this.getHeaders().getFilterRow(); } - getFilterCell(columnIndex: number): Selector { - return this.getFilterRow().find(`[aria-colindex='${columnIndex + 1}']`); + getFilterCell(columnIndex: number): FilterCell { + return this.getHeaders().getFilterRow().getFilterCell(columnIndex); } getFilterRangeOverlay(): Selector { @@ -272,7 +274,7 @@ export default class DataGrid extends GridCore { columnIndex: number, EditorType: new (mainElement: Selector) => T, ): T { - return new EditorType(this.getHeaders().getFilterRow().getFilterCell(columnIndex).getEditor()); + return this.getHeaders().getFilterRow().getFilterCell(columnIndex).getEditor(EditorType); } getSearchBox(): TextBox { From 65697b026d82242d6f765a482c4db85635872caf Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Mon, 9 Mar 2026 17:21:43 +0800 Subject: [PATCH 17/18] refactor noData test --- .../dataGrid/common/markup/noDataText.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts index db70e862f37d..f5c1d8d190d7 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/markup/noDataText.ts @@ -1,6 +1,6 @@ import { createScreenshotsComparer } from 'devextreme-screenshot-comparer'; import DataGrid from 'devextreme-testcafe-models/dataGrid'; -import List from 'devextreme-testcafe-models/list'; +import SelectBox from 'devextreme-testcafe-models/selectBox'; import url from '../../../../helpers/getPageUrl'; import { createWidget } from '../../../../helpers/createWidget'; import { testScreenshot } from '../../../../helpers/themeUtils'; @@ -9,16 +9,13 @@ fixture.disablePageReloads`No Data` .page(url(__dirname, '../../../container.html')); const GRID_CONTAINER = '#container'; -const OVERLAY_SELECTOR = '.dx-overlay-wrapper'; test('The noDataText element should be rendered when a lookup column is filtered (T1293839)', async (t) => { // arrange const { takeScreenshot, compareResults } = createScreenshotsComparer(t); const dataGrid = new DataGrid(GRID_CONTAINER); - const filterRow = dataGrid.getHeaders().getFilterRow(); - const nameFilterCell = filterRow.getFilterCell(0); - const nameFilterEditor = nameFilterCell.getEditorInput(); - const lookupFilterCell = filterRow.getFilterCell(1); + const nameFilterInput = dataGrid.getFilterCell(0).getEditorInput().element; + const lookupFilterEditor = dataGrid.getFilterEditor(1, SelectBox); // assert await t @@ -26,11 +23,16 @@ test('The noDataText element should be rendered when a lookup column is filtered .ok(); // act - await t.click(lookupFilterCell.element); - const lookupList = new List(OVERLAY_SELECTOR); + await t.click(lookupFilterEditor.element); + + // assert + await t.expect(lookupFilterEditor.isOpened()).ok(); + + // act + const lookupList = await lookupFilterEditor.getList(); const lookupItem = lookupList.getItem(1); await t.click(lookupItem.element); - await t.typeText(nameFilterEditor.element, 'test'); + await t.typeText(nameFilterInput, 'test'); // assert await t From 879e6f072e043e85838f421200e281afceaacd0d Mon Sep 17 00:00:00 2001 From: Mark Allen Ramirez Date: Mon, 9 Mar 2026 17:34:58 +0800 Subject: [PATCH 18/18] refactor treelist --- .../tests/accessibility/treeList/status.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/accessibility/treeList/status.ts b/e2e/testcafe-devextreme/tests/accessibility/treeList/status.ts index c32631e1956f..11e2dfea6423 100644 --- a/e2e/testcafe-devextreme/tests/accessibility/treeList/status.ts +++ b/e2e/testcafe-devextreme/tests/accessibility/treeList/status.ts @@ -1,5 +1,5 @@ import HeaderFilter from 'devextreme-testcafe-models/dataGrid/headers/headerFilter'; -import FilterTextBox from 'devextreme-testcafe-models/dataGrid/editors/filterTextBox'; +import TextBox from 'devextreme-testcafe-models/textBox'; import TreeList from 'devextreme-testcafe-models/treeList'; import { a11yCheck } from '../../../helpers/accessibility/utils'; import url from '../../../helpers/getPageUrl'; @@ -96,7 +96,7 @@ test('Accessibility: TreeList general status should contains correct text after test('Accessibility: TreeList general status should contains correct text after filter row interaction', async (t) => { const expectedStatusText = 'Tree list with 2 rows and 2 columns'; const treeList = new TreeList('#container'); - const filterEditor = treeList.getFilterEditor(0, FilterTextBox); + const filterEditor = treeList.getFilterEditor(0, TextBox); const applyFilterBtn = treeList.getHeaderPanel().getApplyFilterButton(); await t.typeText(filterEditor.input(), 'B')