From 6b48cab1089de97b833cf26764707a95ce058e4b Mon Sep 17 00:00:00 2001 From: "anna.shakhova" Date: Fri, 6 Mar 2026 10:24:05 +0100 Subject: [PATCH] GridCore: make expand column header rowspan equal to header rows count (T1317623) GridCore: extract Column type to types file --- ...-with-band-columns (fluent.blue.light).png | Bin 0 -> 42942 bytes ...fixed-band-columns (fluent.blue.light).png | Bin 0 -> 42618 bytes .../dataGrid/common/fixedColumns/visual.ts | 70 ++++++ .../m_column_keyboard_navigation_mixin.ts | 2 +- .../m_group_panel_keyboard_navigation.ts | 2 +- .../m_headers_keyboard_navigation.ts | 2 +- .../grids/data_grid/summary/utils.ts | 2 +- .../grid_core/adaptivity/m_adaptivity.ts | 3 +- .../grids/grid_core/adaptivity/utils.ts | 3 +- .../controllers/m_ai_column_controller.ts | 3 +- .../m_ai_prompt_editor_view_controller.ts | 3 +- .../grids/grid_core/ai_column/utils.test.ts | 2 +- .../grids/grid_core/ai_column/utils.ts | 2 +- .../ai_column/views/m_ai_column_view.ts | 2 +- .../views/m_ai_prompt_editor_view.test.ts | 2 +- .../views/m_ai_prompt_editor_view.ts | 3 +- .../column_headers/m_column_headers.ts | 2 +- .../m_columns_controller.test.ts | 207 ++++++++++++++++++ .../m_columns_controller.ts | 118 +++++----- .../m_columns_controller_utils.ts | 43 ++-- .../grid_core/columns_controller/types.ts | 21 +- .../grids/grid_core/editing/types.ts | 3 +- .../m_column_keyboard_navigation_core.ts | 2 +- .../m_headers_keyboard_navigation.ts | 2 +- .../m_keyboard_navigation.ts | 2 +- .../js/__internal/grids/grid_core/m_utils.ts | 2 +- .../grids/grid_core/search/m_search.ts | 5 +- .../grids/grid_core/selection/m_selection.ts | 3 +- .../grids/grid_core/views/m_columns_view.ts | 3 +- .../__internal/grids/grid_core/views/utils.ts | 3 +- .../columnsController.tests.js | 142 ------------ .../columnsHeadersView.tests.js | 8 +- 32 files changed, 414 insertions(+), 253 deletions(-) create mode 100644 e2e/testcafe-devextreme/tests/dataGrid/common/fixedColumns/etalons/T1317623-expand-columns-with-band-columns (fluent.blue.light).png create mode 100644 e2e/testcafe-devextreme/tests/dataGrid/common/fixedColumns/etalons/T1317623-horizontal-scroll-with-fixed-band-columns (fluent.blue.light).png diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/fixedColumns/etalons/T1317623-expand-columns-with-band-columns (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/fixedColumns/etalons/T1317623-expand-columns-with-band-columns (fluent.blue.light).png new file mode 100644 index 0000000000000000000000000000000000000000..bdf57d5b899b2cabf5d3e13c547f642e2a90071f GIT binary patch literal 42942 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 literal 0 HcmV?d00001 diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/fixedColumns/etalons/T1317623-horizontal-scroll-with-fixed-band-columns (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/dataGrid/common/fixedColumns/etalons/T1317623-horizontal-scroll-with-fixed-band-columns (fluent.blue.light).png new file mode 100644 index 0000000000000000000000000000000000000000..a2ec21a333cd70a2bc1553820ecabd2aa717285a GIT binary patch literal 42618 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 { + const dataGrid = new DataGrid(DATA_GRID_SELECTOR); + const { takeScreenshot, compareResults } = createScreenshotsComparer(t); + + await t.expect(dataGrid.isReady()).ok(); + + await testScreenshot(t, takeScreenshot, 'T1317623-expand-columns-with-band-columns.png', { element: dataGrid.element }); + + await dataGrid.scrollTo(t, { x: 5000 }); + + await testScreenshot(t, takeScreenshot, 'T1317623-horizontal-scroll-with-fixed-band-columns.png', { element: dataGrid.element }); + + await t + .expect(compareResults.isValid()) + .ok(compareResults.errorMessages()); +}).before(async () => createWidget('dxDataGrid', { + dataSource: [ + { + ID: 1, + CompanyName: 'Super Mart of the West', + Address: '702 SW 8th Street', + City: 'Bentonville', + State: 'Arkansas', + Zipcode: 72716, + Phone: '(800) 555-2797', + Fax: '(800) 555-2171', + }, + { + ID: 2, + CompanyName: 'K&S Music', + Address: '1000 Nicllet Mall', + City: 'Minneapolis', + State: 'Minnesota', + Zipcode: 55403, + Phone: '(612) 304-6073', + Fax: '(612) 304-6074', + }, + ], + keyExpr: 'ID', + width: '100%', + showBorders: true, + columnWidth: 200, + columnFixing: { enabled: true }, + selection: { mode: 'multiple' }, + grouping: { autoExpandAll: true }, + masterDetail: { + enabled: true, + }, + columns: [ + { + caption: 'Company Info', + fixed: true, + fixedPosition: 'left', + columns: [ + { dataField: 'CompanyName', groupIndex: 1, showWhenGrouped: true }, + { dataField: 'Phone' }, + { dataField: 'Fax' }, + ], + }, + 'City', + { + dataField: 'State', + groupIndex: 0, + }, + 'Address', + 'Zipcode', + ], +})); diff --git a/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_column_keyboard_navigation_mixin.ts b/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_column_keyboard_navigation_mixin.ts index b814293b8540..0a04c139361f 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_column_keyboard_navigation_mixin.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_column_keyboard_navigation_mixin.ts @@ -1,6 +1,6 @@ import { isCommandKeyPressed } from '@js/common/core/events/utils'; import { isDefined } from '@js/core/utils/type'; -import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { FocusedCellPosition } from '@ts/grids/grid_core/keyboard_navigation/const'; import { KEY_CODES } from '@ts/grids/grid_core/keyboard_navigation/const'; import type { ColumnKeyboardNavigationController } from '@ts/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core'; diff --git a/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_group_panel_keyboard_navigation.ts b/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_group_panel_keyboard_navigation.ts index b4ae24f34f2a..c5af64ed3ebc 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_group_panel_keyboard_navigation.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_group_panel_keyboard_navigation.ts @@ -5,7 +5,7 @@ import { } from '@js/common/core/events/utils/index'; import $ from '@js/core/renderer'; import { hiddenFocus } from '@js/ui/shared/accessibility'; -import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import { Direction } from '@ts/grids/grid_core/keyboard_navigation/const'; import { ColumnKeyboardNavigationController } from '@ts/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core'; import type { Views } from '@ts/grids/grid_core/m_types'; diff --git a/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_headers_keyboard_navigation.ts b/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_headers_keyboard_navigation.ts index d4d757fb7ec1..47748edce42e 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_headers_keyboard_navigation.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_headers_keyboard_navigation.ts @@ -1,7 +1,7 @@ import { isCommandKeyPressed } from '@js/common/core/events/utils'; import $ from '@js/core/renderer'; import { isDefined } from '@js/core/utils/type'; -import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import { KEY_CODES } from '@ts/grids/grid_core/keyboard_navigation/const'; import type { HeadersKeyboardNavigationController } from '@ts/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation'; import { headersKeyboardNavigationModule } from '@ts/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation'; diff --git a/packages/devextreme/js/__internal/grids/data_grid/summary/utils.ts b/packages/devextreme/js/__internal/grids/data_grid/summary/utils.ts index 88662edc0fce..a6065e3b565e 100644 --- a/packages/devextreme/js/__internal/grids/data_grid/summary/utils.ts +++ b/packages/devextreme/js/__internal/grids/data_grid/summary/utils.ts @@ -1,5 +1,5 @@ import { isDefined } from '@js/core/utils/type'; -import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; export function getSummaryCellIndex( column: Column, diff --git a/packages/devextreme/js/__internal/grids/grid_core/adaptivity/m_adaptivity.ts b/packages/devextreme/js/__internal/grids/grid_core/adaptivity/m_adaptivity.ts index ec350d4f3ccf..254218475ba8 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/adaptivity/m_adaptivity.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/adaptivity/m_adaptivity.ts @@ -16,11 +16,12 @@ import { getWidth } from '@js/core/utils/size'; import { isDefined, isString } from '@js/core/utils/type'; import Form from '@js/ui/form'; import { isMaterial } from '@js/ui/themes'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ResizingController } from '@ts/grids/grid_core/views/m_grid_view'; import type { ExportController } from '../../data_grid/export/m_export'; import { AI_COLUMN_NAME } from '../ai_column/const'; -import type { Column, ColumnsController } from '../columns_controller/m_columns_controller'; +import type { ColumnsController } from '../columns_controller/m_columns_controller'; import type { ColumnsResizerViewController, DraggingHeaderViewController } from '../columns_resizing_reordering/m_columns_resizing_reordering'; import type { DataController } from '../data_controller/m_data_controller'; import type { EditingController } from '../editing/m_editing'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/adaptivity/utils.ts b/packages/devextreme/js/__internal/grids/grid_core/adaptivity/utils.ts index 57b26320ce84..a03d6024efb6 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/adaptivity/utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/adaptivity/utils.ts @@ -1,6 +1,5 @@ import { isDefined } from '@js/core/utils/type'; - -import type { Column } from '../columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; const HIDEABLE_COMMAND_COLUMNS = ['ai']; diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts index 79bfea4ac143..0157a4227a33 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_column_controller.ts @@ -1,8 +1,9 @@ import type { DataChange } from '@js/common/grids'; import type { Callback } from '@js/core/utils/callbacks'; import { isDefined } from '@ts/core/utils/m_type'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; -import type { Column, ColumnsController } from '../../columns_controller/m_columns_controller'; +import type { ColumnsController } from '../../columns_controller/m_columns_controller'; import type { DataController, HandleDataChangedArguments, UserData } from '../../data_controller/m_data_controller'; import { Controller } from '../../m_modules'; import gridCoreUtils from '../../m_utils'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_prompt_editor_view_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_prompt_editor_view_controller.ts index de8052547a2e..917232df34c0 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_prompt_editor_view_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/controllers/m_ai_prompt_editor_view_controller.ts @@ -1,4 +1,5 @@ -import type { Column } from '../../columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; + import { ViewController } from '../../m_modules'; import type { AIPromptEditorView } from '../views/m_ai_prompt_editor_view'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.test.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.test.ts index 6cb2bdd42a92..d50f38d9a60e 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.test.ts @@ -1,8 +1,8 @@ import { describe, expect, it, } from '@jest/globals'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; -import type { Column } from '../columns_controller/m_columns_controller'; import type { Item } from '../data_controller/m_data_controller'; import { getDataFromRowItems, diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.ts index f862f70b6506..ce5ffb741080 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/utils.ts @@ -1,6 +1,6 @@ import { isDefined } from '@ts/core/utils/m_type'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; -import type { Column } from '../columns_controller/m_columns_controller'; import type { Item, UserData } from '../data_controller/m_data_controller'; import { AI_COLUMN_NAME, CLASSES } from './const'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts index 1ef7d9b84edb..54d4a0983e35 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_column_view.ts @@ -3,9 +3,9 @@ import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import type { Item, ItemClickEvent, Properties as DropDownProperties } from '@js/ui/drop_down_button'; import DropDownButton from '@js/ui/drop_down_button'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ColumnHeadersView } from '../../column_headers/m_column_headers'; -import type { Column } from '../../columns_controller/m_columns_controller'; import type { ColumnsResizerViewController } from '../../columns_resizing_reordering/m_columns_resizing_reordering'; import type { ModuleType } from '../../m_types'; import { AI_COLUMN_NAME, CLASSES, ICON_NAMES } from '../const'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.test.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.test.ts index 57f4972ab726..545a6e9038f3 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.test.ts @@ -13,9 +13,9 @@ import $ from '@js/core/renderer'; import Callbacks from '@js/core/utils/callbacks'; import wrapInstanceWithMocks from '@ts/grids/grid_core/__tests__/__mock__/helpers/wrapInstance'; import { AIPromptEditorModel } from '@ts/grids/grid_core/__tests__/__mock__/model/ai_prompt_editor'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import { AIPromptEditor } from '../../ai_prompt_editor/ai_prompt_editor'; -import type { Column } from '../../columns_controller/m_columns_controller'; import { AIPromptEditorView } from './m_ai_prompt_editor_view'; jest.mock('../../ai_prompt_editor/ai_prompt_editor', (): any => { diff --git a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts index 1ff42da2aac7..831cb6f106da 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/ai_column/views/m_ai_prompt_editor_view.ts @@ -1,9 +1,10 @@ import $ from '@js/core/renderer'; import domAdapter from '@ts/core/m_dom_adapter'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import { AIPromptEditor } from '../../ai_prompt_editor/ai_prompt_editor'; import type { AIPromptEditorOptions } from '../../ai_prompt_editor/types'; -import type { Column, ColumnsController } from '../../columns_controller/m_columns_controller'; +import type { ColumnsController } from '../../columns_controller/m_columns_controller'; import { getColumnHeaderCellSelector } from '../../columns_controller/m_columns_controller_utils'; import { View } from '../../m_modules'; import { AI_COLUMN_NAME } from '../const'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/column_headers/m_column_headers.ts b/packages/devextreme/js/__internal/grids/grid_core/column_headers/m_column_headers.ts index 71174c016b90..ac89dd174d34 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/column_headers/m_column_headers.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/column_headers/m_column_headers.ts @@ -7,11 +7,11 @@ import { Deferred } from '@js/core/utils/deferred'; import { extend } from '@js/core/utils/extend'; import { getHeight } from '@js/core/utils/size'; import { isDefined } from '@js/core/utils/type'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import { ColumnContextMenuMixin } from '@ts/grids/grid_core/context_menu/m_column_context_menu_mixin'; import type { HeaderFilterController } from '@ts/grids/grid_core/header_filter/m_header_filter'; import type { HeaderPanel } from '@ts/grids/grid_core/header_panel/m_header_panel'; -import type { Column } from '../columns_controller/m_columns_controller'; import { CLASSES as REORDERING_CLASSES } from '../columns_resizing_reordering/const'; import type { HeadersKeyboardNavigationController } from '../keyboard_navigation/m_headers_keyboard_navigation'; import { registerKeyboardAction } from '../m_accessibility'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.test.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.test.ts index 234ef1a94e38..126b03a7fb28 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.test.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.test.ts @@ -62,4 +62,211 @@ describe('Column Controller', () => { }); }); }); + + describe('Expand columns in band columns layout', () => { + it('detail expand column header should have rowspan equal to header row count when band columns are used', async () => { + const { instance } = await createDataGrid({ + dataSource, + columns: [ + { caption: 'Band column 1', columns: ['id', 'name'] }, + { dataField: 'name', caption: 'Column 3', name: 'Column3' }, + ], + masterDetail: { + enabled: true, + }, + }); + + const columnsController = instance.getController('columns'); + const rowCount = columnsController.getRowCount(); + const firstRowColumns = columnsController.getVisibleColumns(0); + + expect(rowCount).toBe(2); + + const expandColumn = firstRowColumns.find((col) => col.command === 'expand'); + expect(expandColumn).toBeDefined(); + expect(expandColumn.rowspan).toBe(2); + }); + + it('should place expand columns only in the first header row with grouped columns', async () => { + const { instance } = await createDataGrid({ + dataSource: [ + { + TestField1: 'group1', TestField2: 'group2', TestField3: 'val3', TestField4: 'val4', + }, + ], + columns: [ + { dataField: 'TestField1', caption: 'Column 1', groupIndex: 0 }, + { + caption: 'Band Column 1', + columns: [ + { dataField: 'TestField2', caption: 'Column 2', groupIndex: 1 }, + { dataField: 'TestField3', caption: 'Column 3' }, + { caption: 'Band Column 2', columns: [{ dataField: 'TestField4', caption: 'Column 4' }] }, + ], + }, + ], + }); + + const columnsController = instance.getController('columns'); + const rowCount = columnsController.getRowCount(); + + // Row 0: expand columns + Band Column 1 + const firstRowColumns = columnsController.getVisibleColumns(0); + const expandColumnsInFirstRow = firstRowColumns.filter((col) => col.command === 'expand'); + + expect(rowCount).toBe(3); + expect(firstRowColumns.length).toBe(3); + expect(expandColumnsInFirstRow.length).toBe(2); + + expandColumnsInFirstRow.forEach((col) => { + expect(col.rowspan).toBe(3); + }); + + const bandColumn = firstRowColumns.find((col) => col.caption === 'Band Column 1'); + + expect(bandColumn).toBeDefined(); + expect(bandColumn.isBand).toBe(true); + expect(bandColumn.rowspan).toBeUndefined(); + + // Row 1: Column 3 + Band Column 2 + const secondRowColumns = columnsController.getVisibleColumns(1); + const expandColumnsInSecondRow = secondRowColumns.filter((col) => col.command === 'expand'); + + expect(secondRowColumns.length).toBe(2); + expect(expandColumnsInSecondRow.length).toBe(0); + + const column3 = secondRowColumns.find((col) => col.caption === 'Column 3'); + + expect(column3).toBeDefined(); + expect(column3.rowspan).toBe(2); + + const bandColumn2 = secondRowColumns.find((col) => col.caption === 'Band Column 2'); + + expect(bandColumn2).toBeDefined(); + expect(bandColumn2.isBand).toBe(true); + expect(bandColumn2.rowspan).toBeUndefined(); + + // Row 2: Column 4 + const thirdRowColumns = columnsController.getVisibleColumns(2); + const expandColumnsInThirdRow = thirdRowColumns.filter((col) => col.command === 'expand'); + const column4 = thirdRowColumns.find((col) => col.caption === 'Column 4'); + + expect(expandColumnsInThirdRow.length).toBe(0); + expect(column4).toBeDefined(); + expect(column4.rowspan).toBeUndefined(); + }); + + it('should place expand columns only in the first header row with showWhenGrouped', async () => { + const { instance } = await createDataGrid({ + dataSource: [ + { field1: 'g1', field2: 'g2', field3: 'g3' }, + ], + columns: [{ + dataField: 'field1', + showWhenGrouped: true, + groupIndex: 0, + }, { + caption: 'band2', + columns: [{ + dataField: 'field2', + showWhenGrouped: true, + groupIndex: 1, + }, { + caption: 'band3', + columns: [{ + dataField: 'field3', + showWhenGrouped: true, + groupIndex: 2, + }], + }], + }], + }); + + const columnsController = instance.getController('columns'); + const rowCount = columnsController.getRowCount(); + + expect(rowCount).toBe(3); + + // Row 0: expand columns with rowspan=3, data columns with rowspan=3, band column + const firstRowColumns = columnsController.getVisibleColumns(0); + const expandColumnsRow0 = firstRowColumns.filter((col) => col.command === 'expand'); + + expect(expandColumnsRow0.length).toBe(3); + expandColumnsRow0.forEach((col) => { + expect(col.rowspan).toBe(3); + }); + + // showWhenGrouped data columns should be in the first row with rowspan=3 + const field1Col = firstRowColumns.find((col) => col.caption === 'Field 1' && !col.command); + expect(field1Col).toBeDefined(); + expect(field1Col.rowspan).toBe(3); + + // band2 should be in the first row without rowspan (it has children) + const band2Col = firstRowColumns.find((col) => col.caption === 'band2'); + expect(band2Col).toBeDefined(); + expect(band2Col.rowspan).toBeUndefined(); + + // Row 1: no expand columns + const secondRowColumns = columnsController.getVisibleColumns(1); + const expandColumnsRow1 = secondRowColumns.filter((col) => col.command === 'expand'); + + expect(expandColumnsRow1.length).toBe(0); + + // band3 should be in the second row + const band3Col = secondRowColumns.find((col) => col.caption === 'band3'); + expect(band3Col).toBeDefined(); + + // Row 2: no expand columns + const thirdRowColumns = columnsController.getVisibleColumns(2); + const expandColumnsRow2 = thirdRowColumns.filter((col) => col.command === 'expand'); + + expect(expandColumnsRow2.length).toBe(0); + }); + + it('should not set rowspan on expand columns when there is only one header row with grouped showWhenGrouped columns', async () => { + const { instance } = await createDataGrid({ + dataSource: [ + { + field1: 'val1', field2: 'val2', field3: 'g1', field4: 'val4', + }, + ], + columns: [ + 'field1', + 'field2', + { dataField: 'field3', showWhenGrouped: true, groupIndex: 0 }, + ], + }); + + const columnsController = instance.getController('columns'); + const rowCount = columnsController.getRowCount(); + + expect(rowCount).toBe(1); + + const visibleColumns = columnsController.getVisibleColumns(0); + const expandColumn = visibleColumns.find((col) => col.command === 'expand' || col.type === 'groupExpand'); + + expect(expandColumn).toBeDefined(); + expect(expandColumn.rowspan).toBeUndefined(); + }); + + it('should not set rowspan on expand column when there is a single header row', async () => { + const { instance } = await createDataGrid({ + dataSource, + columns: ['id', 'name'], + masterDetail: { + enabled: true, + }, + }); + + const columnsController = instance.getController('columns'); + const rowCount = columnsController.getRowCount(); + const firstRowColumns = columnsController.getVisibleColumns(0); + + const expandColumn = firstRowColumns.find((col) => col.command === 'expand'); + + expect(rowCount).toBe(1); + expect(expandColumn).toBeDefined(); + expect(expandColumn.rowspan).toBeUndefined(); + }); + }); }); diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts index e6811452bf48..e015309d51b1 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts @@ -3,7 +3,6 @@ import dateLocalization from '@js/common/core/localization/date'; import messageLocalization from '@js/common/core/localization/message'; import { DataSource } from '@js/common/data/data_source/data_source'; import { normalizeDataSourceOptions } from '@js/common/data/data_source/utils'; -import type { ColumnAIOptions, ColumnBase } from '@js/common/grids'; import config from '@js/core/config'; import $ from '@js/core/renderer'; import Callbacks from '@js/core/utils/callbacks'; @@ -13,14 +12,14 @@ import { extend } from '@js/core/utils/extend'; import { each, map } from '@js/core/utils/iterator'; import { orderEach } from '@js/core/utils/object'; import { - isDefined, isFunction, isNumeric, isObject, isPlainObject, - isString, + isDefined, isFunction, isNumeric, isObject, isPlainObject, isString, } from '@js/core/utils/type'; import variableWrapper from '@js/core/utils/variable_wrapper'; import Store from '@js/data/abstract_store'; import filterUtils from '@js/ui/shared/filtering'; import errors from '@js/ui/widget/ui.errors'; import inflector from '@ts/core/utils/m_inflector'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { DataController } from '@ts/grids/grid_core/data_controller/m_data_controller'; import type { FocusController } from '@ts/grids/grid_core/focus/m_focus'; import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; @@ -71,7 +70,8 @@ import { isSortOrderValid, mergeColumns, moveColumnToGroup, - numberToString, processBandColumns, + numberToString, + processBandColumns, processExpandColumns, resetBandColumnsCache, resetColumnsCache, @@ -84,15 +84,9 @@ import { updateSerializers, } from './m_columns_controller_utils'; -export interface Column extends ColumnBase { - parseValue: (text: string) => unknown; - index?: number; - groupIndex?: number; - type?: string; - visibleWidth?: string | number; - hidingPriority?: number; - ai?: ColumnAIOptions; - command?: string; +interface IndexedColumns { + positiveIndexedColumns: Record[][]; + negativeIndexedColumns: Record[]; } export class ColumnsController extends modules.Controller { @@ -739,9 +733,9 @@ export class ColumnsController extends modules.Controller { }); } - private _compileVisibleColumnsCore() { + private _compileVisibleColumnsCore(): Column[][] { const bandColumnsCache = this.getBandColumnsCache(); - const columns = mergeColumns(this, this._columns, this._commandColumns, true); + const columns: Column[] = mergeColumns(this, this._columns, this._commandColumns, true); processBandColumns(this, columns, bandColumnsCache); @@ -758,18 +752,18 @@ export class ColumnsController extends modules.Controller { return visibleColumns; } - private _getIndexedColumns(columns) { + private _getIndexedColumns(columns: Column[]): IndexedColumns { const rtlEnabled = this.option('rtlEnabled'); const rowCount = this.getRowCount(); const columnDigitsCount = digitsCount(columns.length); const bandColumnsCache = this.getBandColumnsCache(); - const positiveIndexedColumns: any = []; - const negativeIndexedColumns: any = []; + const positiveIndexedColumns: IndexedColumns['positiveIndexedColumns'] = []; + const negativeIndexedColumns: IndexedColumns['negativeIndexedColumns'] = []; for (let i = 0; i < rowCount; i += 1) { - negativeIndexedColumns[i] = [{}]; + negativeIndexedColumns[i] = {}; // 0 - fixed columns on the left side // 1 - not fixed columns @@ -778,26 +772,30 @@ export class ColumnsController extends modules.Controller { } columns.forEach((column) => { - let { visibleIndex } = column; - let indexedColumns; - - const parentBandColumns = getParentBandColumns(column.index, bandColumnsCache.columnParentByIndex); - + const { visibleIndex } = column; const isVisible = this._isColumnVisible(column); const isInGroupPanel = this._isColumnInGroupPanel(column); if (isVisible && !isInGroupPanel) { + const parentBandColumns = getParentBandColumns( + column.index, + bandColumnsCache.columnParentByIndex, + ); const rowIndex = parentBandColumns.length; + let targetIndex: string | number = visibleIndex ?? 'undefined'; + // eslint-disable-next-line @typescript-eslint/init-declarations + let indexedColumns: Record; - if (visibleIndex < 0) { - visibleIndex = -visibleIndex; + if (isDefined(visibleIndex) && visibleIndex < 0) { + targetIndex = -visibleIndex; indexedColumns = negativeIndexedColumns[rowIndex]; } else { column.fixed = parentBandColumns[0]?.fixed ?? column.fixed; column.fixedPosition = parentBandColumns[0]?.fixedPosition ?? column.fixedPosition; if (column.fixed && column.fixedPosition !== StickyPosition.Sticky) { - const isDefaultCommandColumn = !!column.command && !gridCoreUtils.isCustomCommandColumn(this._columns, column); + const isDefaultCommandColumn = !!column.command + && !gridCoreUtils.isCustomCommandColumn(this._columns, column); let isFixedToEnd = column.fixedPosition === 'right'; @@ -814,15 +812,16 @@ export class ColumnsController extends modules.Controller { } if (parentBandColumns.length) { - visibleIndex = numberToString(visibleIndex, columnDigitsCount); + targetIndex = numberToString(targetIndex, columnDigitsCount); for (let i = parentBandColumns.length - 1; i >= 0; i -= 1) { - visibleIndex = numberToString(parentBandColumns[i].visibleIndex, columnDigitsCount) + visibleIndex; + const { visibleIndex: parentVisibleIndex } = parentBandColumns[i]; + targetIndex = `${numberToString(parentVisibleIndex, columnDigitsCount)}${targetIndex}`; } } - indexedColumns[visibleIndex] = indexedColumns[visibleIndex] || []; - indexedColumns[visibleIndex].push(column); + indexedColumns[targetIndex] = indexedColumns[targetIndex] || []; + indexedColumns[targetIndex].push(column); } }); @@ -831,41 +830,50 @@ export class ColumnsController extends modules.Controller { }; } - private _getVisibleColumnsFromIndexed({ positiveIndexedColumns, negativeIndexedColumns }) { - const result: any = []; - - const rowCount = this.getRowCount(); - const expandColumns = mergeColumns(this, this.getExpandColumns(), this._columns); - - let rowspanGroupColumns = 0; - let rowspanExpandColumns = 0; + private _getVisibleColumnsFromIndexed({ + positiveIndexedColumns, + negativeIndexedColumns, + }: IndexedColumns): Column[][] { + const result: Column[][] = []; + const rowCount: number = this.getRowCount(); + const expandColumns: Column[] = mergeColumns(this, this.getExpandColumns(), this._columns); + // Process header rows columns for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) { result.push([]); - orderEach(negativeIndexedColumns[rowIndex], (_, columns) => { - result[rowIndex].unshift.apply(result[rowIndex], columns); + orderEach(negativeIndexedColumns[rowIndex], (_: string, columns: Column[]) => { + result[rowIndex].unshift(...columns); }); + } - const firstPositiveIndexColumn = result[rowIndex].length; - const positiveIndexedRowColumns = positiveIndexedColumns[rowIndex]; + const firstExpandColumnIndex = result[0].length; - positiveIndexedRowColumns.forEach((columnsByFixing) => { - orderEach(columnsByFixing, (_, columnsByVisibleIndex) => { - result[rowIndex].push.apply(result[rowIndex], columnsByVisibleIndex); + for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) { + positiveIndexedColumns[rowIndex].forEach((columnsByFixing) => { + orderEach(columnsByFixing, (_: string, columnsByVisibleIndex: Column[]) => { + result[rowIndex].push(...columnsByVisibleIndex); }); }); - - // The order of processing is important - if (rowspanExpandColumns <= rowIndex) { - rowspanExpandColumns += processExpandColumns.call(this, result[rowIndex], expandColumns, DETAIL_COMMAND_COLUMN_NAME, firstPositiveIndexColumn); - } - - if (rowspanGroupColumns <= rowIndex) { - rowspanGroupColumns += processExpandColumns.call(this, result[rowIndex], expandColumns, GROUP_COMMAND_COLUMN_NAME, firstPositiveIndexColumn); - } } + // The order of processing is important + processExpandColumns( + result[0], + expandColumns, + DETAIL_COMMAND_COLUMN_NAME, + firstExpandColumnIndex, + rowCount, + ); + processExpandColumns( + result[0], + expandColumns, + GROUP_COMMAND_COLUMN_NAME, + firstExpandColumnIndex, + rowCount, + ); + + // Process table body columns result.push(getDataColumns(result)); return result; diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts index 1f0267472113..36fe855a3c56 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts @@ -5,13 +5,14 @@ import { equalByValue } from '@js/core/utils/common'; import { compileGetter, compileSetter } from '@js/core/utils/data'; import dateSerialization from '@js/core/utils/date_serialization'; import { extend } from '@js/core/utils/extend'; -import { each, map } from '@js/core/utils/iterator'; +import { each } from '@js/core/utils/iterator'; import { deepExtendArraySafe } from '@js/core/utils/object'; import { getDefaultAlignment } from '@js/core/utils/position'; import { isDefined, isFunction, isNumeric, isObject, isString, type, } from '@js/core/utils/type'; import variableWrapper from '@js/core/utils/variable_wrapper'; +import type { DataGridCommandColumnType } from '@js/ui/data_grid'; import errors from '@js/ui/widget/ui.errors'; import { HIDDEN_COLUMNS_WIDTH } from '../adaptivity/const'; @@ -32,8 +33,8 @@ import { USER_STATE_FIELD_NAMES_15_1, VIRTUAL_COMMAND_COLUMN_NAME, } from './const'; -import type { Column, ColumnsController } from './m_columns_controller'; -import type { ColumnIndex, DropLocationNames } from './types'; +import type { ColumnsController } from './m_columns_controller'; +import type { Column, ColumnIndex, DropLocationNames } from './types'; const warnFixedInChildColumnsOnce = (controller: ColumnsController, childColumns: any[]): void => { if (controller?._isWarnedAboutUnsupportedProperties) return; @@ -859,26 +860,22 @@ export const getFixedPosition = function (that: ColumnsController, column) { return column.fixedPosition; }; -export const processExpandColumns = function (columns, expandColumns, type, columnIndex) { - let customColumnIndex; - const rowCount = this.getRowCount(); - let rowspan = columns[columnIndex] && columns[columnIndex].rowspan; - let expandColumnsByType = expandColumns.filter((column) => column.type === type); - - columns.forEach((column, index) => { - if (column.type === type) { - customColumnIndex = index; - rowspan = columns[index + 1] ? columns[index + 1].rowspan : rowCount; - } - }); - - if (rowspan > 1) { - expandColumnsByType = map(expandColumnsByType, (expandColumn) => extend({}, expandColumn, { rowspan })); - } - expandColumnsByType.unshift.apply(expandColumnsByType, isDefined(customColumnIndex) ? [customColumnIndex, 1] : [columnIndex, 0]); - columns.splice.apply(columns, expandColumnsByType); - - return rowspan || 1; +export const processExpandColumns = ( + columns: Column[], + expandColumns: Column[], + commandType: DataGridCommandColumnType, + columnIndex: number, + rowspan: number, +): void => { + const expandColumnsByType = expandColumns + .filter((column) => column.type === commandType) + .map((column): Column => (rowspan > 1 ? { ...column, rowspan } : column)); + + const customExpandColumnIndex = columns.findIndex((column) => column.type === commandType); + const targetIndex = customExpandColumnIndex >= 0 ? customExpandColumnIndex : columnIndex; + const deleteCount = customExpandColumnIndex >= 0 ? 1 : 0; + + columns.splice(targetIndex, deleteCount, ...expandColumnsByType); }; export const digitsCount = function (number) { diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts index 90dd5a1e741e..2b9864d5a68d 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/types.ts @@ -1,8 +1,6 @@ -import type { - COLUMN_CHOOSER_LOCATION, - GROUP_LOCATION, - HEADERS_LOCATION, -} from './const'; +import type { ColumnAIOptions, ColumnBase } from '@js/common/grids'; + +import type { COLUMN_CHOOSER_LOCATION, GROUP_LOCATION, HEADERS_LOCATION } from './const'; export type DropLocationNames = typeof GROUP_LOCATION | typeof COLUMN_CHOOSER_LOCATION @@ -12,3 +10,16 @@ export type ColumnIndex = number | { rowIndex: number; columnIndex: number; }; + +export interface Column extends ColumnBase { + parseValue?: (text: string) => unknown; + index?: number; + groupIndex?: number; + type?: string; + visibleWidth?: string | number; + hidingPriority?: number; + ai?: ColumnAIOptions; + command?: string; + rowspan?: number; + colspan?: number; +} diff --git a/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts b/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts index e3360e331618..7eb05c693db1 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/editing/types.ts @@ -1,4 +1,5 @@ -import type { Column } from '../columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; + import type { Item, UserData } from '../data_controller/m_data_controller'; import type { RowKey } from '../m_types'; import type { INSERT_INDEX } from './const'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core.ts index 4de2724c236e..8d02ff6c4998 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core.ts @@ -1,6 +1,6 @@ import { isDefined, isEmptyObject } from '@js/core/utils/type'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; -import type { Column } from '../columns_controller/m_columns_controller'; import { Direction } from './const'; import type { ColumnFocusDispatcher } from './m_column_focus_dispatcher'; import { KeyboardNavigationController as KeyboardNavigationControllerCore } from './m_keyboard_navigation_core'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation.ts index 0ff0e50ef9c7..9ceff9618e61 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation.ts @@ -5,8 +5,8 @@ import type { dxElementWrapper } from '@js/core/renderer'; import $ from '@js/core/renderer'; import { getBoundingRect } from '@js/core/utils/position'; import { isDefined } from '@js/core/utils/type'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; -import type { Column } from '../columns_controller/m_columns_controller'; import type { Views } from '../m_types'; import { StickyPosition } from '../sticky_columns/const'; import { GridCoreStickyColumnsDom } from '../sticky_columns/dom'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts index 52cec10e8e0e..fbb694f5e8bb 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/keyboard_navigation/m_keyboard_navigation.ts @@ -26,12 +26,12 @@ import * as accessibility from '@js/ui/shared/accessibility'; import { isElementInDom } from '@ts/core/utils/m_dom'; import { focused } from '@ts/core/utils/m_selectors'; import type { AdaptiveColumnsController } from '@ts/grids/grid_core/adaptivity/m_adaptivity'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { DataController } from '@ts/grids/grid_core/data_controller/m_data_controller'; import type { EditingController } from '@ts/grids/grid_core/editing/m_editing'; import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view'; import { memoize } from '@ts/utils/memoize'; -import type { Column } from '../columns_controller/m_columns_controller'; import { EDIT_FORM_CLASS, EDIT_MODE_BATCH, 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..105dc0a84584 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/m_utils.ts @@ -20,10 +20,10 @@ import formatHelper from '@js/format_helper'; import LoadPanel from '@js/ui/load_panel'; import sharedFiltering from '@js/ui/shared/filtering'; import { isNumeric } from '@ts/core/utils/m_type'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ColumnPoint } from '@ts/grids/grid_core/m_types'; import { AI_COLUMN_NAME } from './ai_column/const'; -import type { Column } from './columns_controller/m_columns_controller'; import { isEqualSelectors, isSelectorEqualWithCallback } from './utils/index'; const BASE_LOAD_PANEL_Z_INDEX = 1000; diff --git a/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts b/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts index 30d0a86685fb..95af6a7fc774 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/search/m_search.ts @@ -1,13 +1,13 @@ /* eslint-disable max-classes-per-file */ -/* eslint-disable @typescript-eslint/method-signature-style */ + import messageLocalization from '@js/common/core/localization/message'; import type { LangParams } from '@js/common/data'; import dataQuery from '@js/common/data/query'; import domAdapter from '@js/core/dom_adapter'; import $ from '@js/core/renderer'; import { compileGetter, toComparable } from '@js/core/utils/data'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; -import type { Column } from '../columns_controller/m_columns_controller'; import type { DataController, Filter } from '../data_controller/m_data_controller'; import type { HeaderPanel } from '../header_panel/m_header_panel'; import type { ModuleType } from '../m_types'; @@ -270,7 +270,6 @@ const rowsView = ( for (let i = 0; i < $contents.length; i++) { const node = $contents.get(i); if (node.nodeType === 3) { - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const normalizedText = stringNormalizer(node.textContent ?? node.nodeValue ?? ''); if (normalizedText.includes(normalizedSearchText)) { resultTextNodes.push(node); diff --git a/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts b/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts index 503d34b73238..d8c8bdd6ae8e 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/selection/m_selection.ts @@ -16,7 +16,8 @@ import { isDefined, isObject } from '@js/core/utils/type'; import errors from '@js/ui/widget/ui.errors'; import supportUtils from '@ts/core/utils/m_support'; import type { ColumnHeadersView } from '@ts/grids/grid_core/column_headers/m_column_headers'; -import type { Column, ColumnsController } from '@ts/grids/grid_core/columns_controller/m_columns_controller'; +import type { ColumnsController } from '@ts/grids/grid_core/columns_controller/m_columns_controller'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { ContextMenuController } from '@ts/grids/grid_core/context_menu/m_context_menu'; import type { ModuleType } from '@ts/grids/grid_core/m_types'; import type { StateStoringController } from '@ts/grids/grid_core/state_storing/m_state_storing_core'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/views/m_columns_view.ts b/packages/devextreme/js/__internal/grids/grid_core/views/m_columns_view.ts index de48318c3aea..1a03c37e6126 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/views/m_columns_view.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/views/m_columns_view.ts @@ -31,10 +31,11 @@ import supportUtils from '@ts/core/utils/m_support'; import type { AdaptiveColumnsController } from '@ts/grids/grid_core/adaptivity/m_adaptivity'; import type { ColumnChooserController, ColumnChooserView } from '@ts/grids/grid_core/column_chooser/m_column_chooser'; import { ColumnStateMixin } from '@ts/grids/grid_core/column_state_mixin/m_column_state_mixin'; +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; import type { EditorFactory } from '@ts/grids/grid_core/editor_factory/m_editor_factory'; import type { SelectionController } from '@ts/grids/grid_core/selection/m_selection'; -import type { Column, ColumnsController } from '../columns_controller/m_columns_controller'; +import type { ColumnsController } from '../columns_controller/m_columns_controller'; import type { DataController } from '../data_controller/m_data_controller'; import modules from '../m_modules'; import gridCoreUtils from '../m_utils'; diff --git a/packages/devextreme/js/__internal/grids/grid_core/views/utils.ts b/packages/devextreme/js/__internal/grids/grid_core/views/utils.ts index 36311a9b1ec2..8044b9662d49 100644 --- a/packages/devextreme/js/__internal/grids/grid_core/views/utils.ts +++ b/packages/devextreme/js/__internal/grids/grid_core/views/utils.ts @@ -1,5 +1,6 @@ +import type { Column } from '@ts/grids/grid_core/columns_controller/types'; + import { AI_COLUMN_NAME } from '../ai_column/const'; -import type { Column } from '../columns_controller/m_columns_controller'; import gridCoreUtils from '../m_utils'; export const getCellText = ( diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js index 448e2d2ee2a8..622149cb8e07 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsController.tests.js @@ -7787,83 +7787,6 @@ QUnit.module('Band columns', { beforeEach: setupModule, afterEach: teardownModul assert.ok(!visibleColumns[1].isBand, 'data column'); }); - QUnit.test('getVisibleColumns with rowIndex and grouped columns', function(assert) { - // arrange - this.applyOptions({ - columns: [ - { dataField: 'TestField1', caption: 'Column 1', groupIndex: 0 }, - { - caption: 'Band Column 1', columns: [ - { dataField: 'TestField2', caption: 'Column 2', groupIndex: 1 }, - { dataField: 'TestField3', caption: 'Column 3' }, - { caption: 'Band Column 2', columns: [{ dataField: 'TestField4', caption: 'Column 4' }] } - ] - } - ] - }); - - // assert - assert.ok(this.columnsController.isInitialized()); - - // act - let visibleColumns = this.columnsController.getVisibleColumns(0); - - // assert - assert.equal(visibleColumns.length, 3, 'count column'); - - // first column - assert.strictEqual(visibleColumns[0].caption, 'Column 1', 'caption of the first column of the first row'); - assert.strictEqual(visibleColumns[0].command, 'expand', 'command column'); - assert.ok(!visibleColumns[0].rowspan, 'rowspan of the first column of the first row'); - - // second column - assert.strictEqual(visibleColumns[1].caption, 'Column 2', 'caption of the second column of the first row'); - assert.strictEqual(visibleColumns[1].command, 'expand', 'command column'); - assert.ok(!visibleColumns[1].rowspan, 'rowspan of the second column of the first row'); - - // third column - assert.strictEqual(visibleColumns[2].caption, 'Band Column 1', 'caption of the second column of the first row'); - assert.equal(visibleColumns[2].colspan, 2, 'colspan of the second column of the first row'); - assert.ok(visibleColumns[2].isBand, 'band column'); - - // act - visibleColumns = this.columnsController.getVisibleColumns(1); - - // assert - assert.equal(visibleColumns.length, 4, 'count column'); - - // first column - assert.strictEqual(visibleColumns[0].caption, 'Column 1', 'caption of the first column of the second row'); - assert.strictEqual(visibleColumns[0].command, 'expand', 'command column'); - assert.equal(visibleColumns[0].rowspan, 2, 'rowspan of the first column of the second row'); - - // second column - assert.strictEqual(visibleColumns[1].caption, 'Column 2', 'caption of the second column of the second row'); - assert.strictEqual(visibleColumns[1].command, 'expand', 'command column'); - assert.equal(visibleColumns[1].rowspan, 2, 'rowspan of the second column of the second row'); - - // third column - assert.strictEqual(visibleColumns[2].caption, 'Column 3', 'caption of the third column of the second row'); - assert.equal(visibleColumns[2].rowspan, 2, 'rowspan of the third column of the second row'); - assert.ok(!visibleColumns[2].isBand, 'data column'); - - // fourth column - assert.strictEqual(visibleColumns[3].caption, 'Band Column 2', 'caption of the fourth column of the second row'); - assert.equal(visibleColumns[3].colspan, 1, 'colspan of the fourth column of the second row'); - assert.ok(visibleColumns[3].isBand, 'band column'); - - // act - visibleColumns = this.columnsController.getVisibleColumns(2); - - // assert - assert.equal(visibleColumns.length, 1, 'count column'); - - // first column - assert.strictEqual(visibleColumns[0].caption, 'Column 4', 'caption of the first column of the third row'); - assert.ok(!visibleColumns[0].rowspan, 'rowspan of the first column of the third row'); - assert.ok(!visibleColumns[0].isBand, 'data column'); - }); - QUnit.test('getVisibleColumnIndex with rowIndex', function(assert) { // arrange this.applyOptions({ @@ -7991,46 +7914,6 @@ QUnit.module('Band columns', { beforeEach: setupModule, afterEach: teardownModul assert.notOk(thirdRowColumns[0].rowspan, 'rowspan of the first column of the third row'); }); - // T895529 - QUnit.test('getVisibleColumns when there are grouped columns with showWhenGrouped', function(assert) { - // arrange - this.applyOptions({ - columns: [ - { - caption: 'Band 1', - columns: ['field1', 'field2'] - }, - { - caption: 'Band 2', - columns: [{ dataField: 'field3', showWhenGrouped: true, groupIndex: 0 }, 'field4'] - } - ] - }); - - // assert - assert.ok(this.columnsController.isInitialized()); - - // act - const visibleColumns = this.columnsController.getVisibleColumns(); - - assert.equal(visibleColumns.length, 5, 'column count in second row'); - assert.equal(visibleColumns[0].type, 'groupExpand', 'type of the first column'); - assert.equal(visibleColumns[0].colspan, undefined, 'colspan of the first column'); - assert.equal(visibleColumns[0].rowspan, undefined, 'rowspan of the first column'); - assert.equal(visibleColumns[1].caption, 'Field 1', 'caption of the second column'); - assert.equal(visibleColumns[1].colspan, undefined, 'colspan of the second column'); - assert.equal(visibleColumns[1].rowspan, undefined, 'rowspan of the second column'); - assert.equal(visibleColumns[2].caption, 'Field 2', 'caption of the third column'); - assert.equal(visibleColumns[2].colspan, undefined, 'colspan of the third column'); - assert.equal(visibleColumns[2].rowspan, undefined, 'rowspan of the third column'); - assert.equal(visibleColumns[3].caption, 'Field 3', 'caption of the fourth column'); - assert.equal(visibleColumns[3].colspan, undefined, 'colspan of the fourth column'); - assert.equal(visibleColumns[3].rowspan, undefined, 'rowspan of the fourth column'); - assert.equal(visibleColumns[4].caption, 'Field 4', 'caption of the fifth column'); - assert.equal(visibleColumns[4].colspan, undefined, 'colspan of the fifth column'); - assert.equal(visibleColumns[4].rowspan, undefined, 'rowspan of the fifth column'); - }); - QUnit.test('getFixedColumns for data columns', function(assert) { // arrange this.applyOptions({ @@ -8950,31 +8833,6 @@ QUnit.module('Band columns', { beforeEach: setupModule, afterEach: teardownModul assert.notOk(this.columnsController.isBandColumnsUsed(), 'band column is not used'); }); - // T647024 - QUnit.test('Expand column must have the right rowspan', function(assert) { - // arrange - - this.applyOptions({ - columns: [ - { caption: 'Band column 1', columns: ['Column1', 'Column2'] }, - { dataField: 'Column3', caption: 'Column 3' } - ], - masterDetail: { - enabled: true - } - }); - - // act - this.columnsController.getVisibleColumns(); - this.columnsController.resetColumnsCache(); - const visibleColumns = this.columnsController.getVisibleColumns(); - - // assert - assert.strictEqual(visibleColumns.length, 4, 'column count'); - assert.strictEqual(visibleColumns[0].command, 'expand', 'expand column'); - assert.ok(!visibleColumns[0].rowspan, 'rowspan of the expand column'); - }); - // T670211 QUnit.test('Delete band column via API', function(assert) { // arrange diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js index 1ca0fb9cd380..fa396d44cd8f 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets.dataGrid/columnsHeadersView.tests.js @@ -2795,11 +2795,15 @@ QUnit.module('Headers with band columns', { // act const $headerCells = $testElement.find('.dx-row.dx-column-lines.dx-header-row').children(); + const $expandHeaders = $headerCells.filter('.dx-command-expand'); + const $dataColumnHeaders = $headerCells.filter(':not(.dx-command-expand)'); // assert - assert.equal($headerCells.length, 4, 'header cell count'); + assert.equal($headerCells.length, 3, 'header cell count'); + assert.equal($expandHeaders.length, 1, 'single expand header cell for 2 rows'); - $headerCells.each((_, headerCellElement) => { + assert.strictEqual($expandHeaders.eq(0).attr('rowspan'), '2', 'expand header cell has correct rowspan'); + $dataColumnHeaders.each((_, headerCellElement) => { assert.strictEqual($(headerCellElement).attr('rowspan'), undefined); }); });