Skip to content

Commit 38878ef

Browse files
GridCore: make expand column header rowspan equal to rows count (T1317623) (#32847)
1 parent 18d1f57 commit 38878ef

22 files changed

Lines changed: 419 additions & 234 deletions
Loading
Loading

e2e/testcafe-devextreme/tests/dataGrid/common/fixedColumns/visual.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,73 @@ test('The grid layout should be correct after unfixing a column via the context
314314
{ dataField: 'State' },
315315
],
316316
}));
317+
318+
// T1317623
319+
test('Expand columns headers offsets should be correct with fixed band columns and fixed command columns (T1317623)', async (t) => {
320+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
321+
const { takeScreenshot, compareResults } = createScreenshotsComparer(t);
322+
323+
await t.expect(dataGrid.isReady()).ok();
324+
325+
await testScreenshot(t, takeScreenshot, 'T1317623-expand-columns-with-band-columns.png', { element: dataGrid.element });
326+
327+
await dataGrid.scrollTo(t, { x: 5000 });
328+
329+
await testScreenshot(t, takeScreenshot, 'T1317623-horizontal-scroll-with-fixed-band-columns.png', { element: dataGrid.element });
330+
331+
await t
332+
.expect(compareResults.isValid())
333+
.ok(compareResults.errorMessages());
334+
}).before(async () => createWidget('dxDataGrid', {
335+
dataSource: [
336+
{
337+
ID: 1,
338+
CompanyName: 'Super Mart of the West',
339+
Address: '702 SW 8th Street',
340+
City: 'Bentonville',
341+
State: 'Arkansas',
342+
Zipcode: 72716,
343+
Phone: '(800) 555-2797',
344+
Fax: '(800) 555-2171',
345+
},
346+
{
347+
ID: 2,
348+
CompanyName: 'K&S Music',
349+
Address: '1000 Nicllet Mall',
350+
City: 'Minneapolis',
351+
State: 'Minnesota',
352+
Zipcode: 55403,
353+
Phone: '(612) 304-6073',
354+
Fax: '(612) 304-6074',
355+
},
356+
],
357+
keyExpr: 'ID',
358+
width: '100%',
359+
showBorders: true,
360+
columnWidth: 200,
361+
columnFixing: { enabled: true },
362+
selection: { mode: 'multiple' },
363+
grouping: { autoExpandAll: true },
364+
masterDetail: {
365+
enabled: true,
366+
},
367+
columns: [
368+
{
369+
caption: 'Company Info',
370+
fixed: true,
371+
fixedPosition: 'left',
372+
columns: [
373+
{ dataField: 'CompanyName', groupIndex: 1, showWhenGrouped: true },
374+
{ dataField: 'Phone' },
375+
{ dataField: 'Fax' },
376+
],
377+
},
378+
'City',
379+
{
380+
dataField: 'State',
381+
groupIndex: 0,
382+
},
383+
'Address',
384+
'Zipcode',
385+
],
386+
}));

packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_column_keyboard_navigation_mixin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { isCommandKeyPressed } from '@js/common/core/events/utils';
22
import { isDefined } from '@js/core/utils/type';
3-
import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller';
3+
import type { Column } from '@ts/grids/grid_core/columns_controller/types';
44
import type { FocusedCellPosition } from '@ts/grids/grid_core/keyboard_navigation/const';
55
import { KEY_CODES } from '@ts/grids/grid_core/keyboard_navigation/const';
66
import type { ColumnKeyboardNavigationController } from '@ts/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core';

packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_group_panel_keyboard_navigation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from '@js/common/core/events/utils/index';
66
import $ from '@js/core/renderer';
77
import { hiddenFocus } from '@js/ui/shared/accessibility';
8-
import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller';
8+
import type { Column } from '@ts/grids/grid_core/columns_controller/types';
99
import { Direction } from '@ts/grids/grid_core/keyboard_navigation/const';
1010
import { ColumnKeyboardNavigationController } from '@ts/grids/grid_core/keyboard_navigation/m_column_keyboard_navigation_core';
1111
import type { Views } from '@ts/grids/grid_core/m_types';

packages/devextreme/js/__internal/grids/data_grid/keyboard_navigation/m_headers_keyboard_navigation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { isCommandKeyPressed } from '@js/common/core/events/utils';
22
import $ from '@js/core/renderer';
33
import { isDefined } from '@js/core/utils/type';
4-
import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller';
4+
import type { Column } from '@ts/grids/grid_core/columns_controller/types';
55
import { KEY_CODES } from '@ts/grids/grid_core/keyboard_navigation/const';
66
import type { HeadersKeyboardNavigationController } from '@ts/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation';
77
import { headersKeyboardNavigationModule } from '@ts/grids/grid_core/keyboard_navigation/m_headers_keyboard_navigation';

packages/devextreme/js/__internal/grids/data_grid/summary/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isDefined } from '@js/core/utils/type';
2-
import type { Column } from '@ts/grids/grid_core/columns_controller/m_columns_controller';
2+
import type { Column } from '@ts/grids/grid_core/columns_controller/types';
33

44
export function getSummaryCellIndex(
55
column: Column,

packages/devextreme/js/__internal/grids/grid_core/adaptivity/m_adaptivity.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import { isMaterial } from '@js/ui/themes';
1919
import type { ResizingController } from '@ts/grids/grid_core/views/m_grid_view';
2020

2121
import type { ExportController } from '../../data_grid/export/m_export';
22-
import type { Column, ColumnsController } from '../columns_controller/m_columns_controller';
22+
import type { ColumnsController } from '../columns_controller/m_columns_controller';
23+
import type { Column } from '../columns_controller/types';
2324
import type { ColumnsResizerViewController, DraggingHeaderViewController } from '../columns_resizing_reordering/m_columns_resizing_reordering';
2425
import type { DataController } from '../data_controller/m_data_controller';
2526
import type { EditingController } from '../editing/m_editing';

packages/devextreme/js/__internal/grids/grid_core/column_headers/m_column_headers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { ColumnContextMenuMixin } from '@ts/grids/grid_core/context_menu/m_colum
1111
import type { HeaderFilterController } from '@ts/grids/grid_core/header_filter/m_header_filter';
1212
import type { HeaderPanel } from '@ts/grids/grid_core/header_panel/m_header_panel';
1313

14-
import type { Column } from '../columns_controller/m_columns_controller';
14+
import type { Column } from '../columns_controller/types';
1515
import { CLASSES as REORDERING_CLASSES } from '../columns_resizing_reordering/const';
1616
import type { HeadersKeyboardNavigationController } from '../keyboard_navigation/m_headers_keyboard_navigation';
1717
import { registerKeyboardAction } from '../m_accessibility';
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import {
2+
afterEach, beforeEach, describe, expect, it,
3+
} from '@jest/globals';
4+
5+
import {
6+
afterTest,
7+
beforeTest,
8+
createDataGrid,
9+
} from '../__tests__/__mock__/helpers/utils';
10+
11+
const dataSource = [
12+
{ id: 1, name: 'Item 1' },
13+
{ id: 2, name: 'Item 2' },
14+
{ id: 3, name: 'Item 3' },
15+
];
16+
17+
describe('Column Controller', () => {
18+
beforeEach(beforeTest);
19+
afterEach(afterTest);
20+
21+
describe('Expand columns in band columns layout', () => {
22+
it('detail expand column header should have rowspan equal to header row count when band columns are used', async () => {
23+
const { instance } = await createDataGrid({
24+
dataSource,
25+
columns: [
26+
{ caption: 'Band column 1', columns: ['id', 'name'] },
27+
{ dataField: 'name', caption: 'Column 3', name: 'Column3' },
28+
],
29+
masterDetail: {
30+
enabled: true,
31+
},
32+
});
33+
34+
const columnsController = instance.getController('columns');
35+
const rowCount = columnsController.getRowCount();
36+
const firstRowColumns = columnsController.getVisibleColumns(0);
37+
38+
expect(rowCount).toBe(2);
39+
40+
const expandColumn = firstRowColumns.find((col) => col.command === 'expand');
41+
expect(expandColumn).toBeDefined();
42+
expect(expandColumn.rowspan).toBe(2);
43+
});
44+
45+
it('should place expand columns only in the first header row with grouped columns', async () => {
46+
const { instance } = await createDataGrid({
47+
dataSource: [
48+
{
49+
TestField1: 'group1', TestField2: 'group2', TestField3: 'val3', TestField4: 'val4',
50+
},
51+
],
52+
columns: [
53+
{ dataField: 'TestField1', caption: 'Column 1', groupIndex: 0 },
54+
{
55+
caption: 'Band Column 1',
56+
columns: [
57+
{ dataField: 'TestField2', caption: 'Column 2', groupIndex: 1 },
58+
{ dataField: 'TestField3', caption: 'Column 3' },
59+
{ caption: 'Band Column 2', columns: [{ dataField: 'TestField4', caption: 'Column 4' }] },
60+
],
61+
},
62+
],
63+
});
64+
65+
const columnsController = instance.getController('columns');
66+
const rowCount = columnsController.getRowCount();
67+
68+
// Row 0: expand columns + Band Column 1
69+
const firstRowColumns = columnsController.getVisibleColumns(0);
70+
const expandColumnsInFirstRow = firstRowColumns.filter((col) => col.command === 'expand');
71+
72+
expect(rowCount).toBe(3);
73+
expect(firstRowColumns.length).toBe(3);
74+
expect(expandColumnsInFirstRow.length).toBe(2);
75+
76+
expandColumnsInFirstRow.forEach((col) => {
77+
expect(col.rowspan).toBe(3);
78+
});
79+
80+
const bandColumn = firstRowColumns.find((col) => col.caption === 'Band Column 1');
81+
82+
expect(bandColumn).toBeDefined();
83+
expect(bandColumn.isBand).toBe(true);
84+
expect(bandColumn.rowspan).toBeUndefined();
85+
86+
// Row 1: Column 3 + Band Column 2
87+
const secondRowColumns = columnsController.getVisibleColumns(1);
88+
const expandColumnsInSecondRow = secondRowColumns.filter((col) => col.command === 'expand');
89+
90+
expect(secondRowColumns.length).toBe(2);
91+
expect(expandColumnsInSecondRow.length).toBe(0);
92+
93+
const column3 = secondRowColumns.find((col) => col.caption === 'Column 3');
94+
95+
expect(column3).toBeDefined();
96+
expect(column3.rowspan).toBe(2);
97+
98+
const bandColumn2 = secondRowColumns.find((col) => col.caption === 'Band Column 2');
99+
100+
expect(bandColumn2).toBeDefined();
101+
expect(bandColumn2.isBand).toBe(true);
102+
expect(bandColumn2.rowspan).toBeUndefined();
103+
104+
// Row 2: Column 4
105+
const thirdRowColumns = columnsController.getVisibleColumns(2);
106+
const expandColumnsInThirdRow = thirdRowColumns.filter((col) => col.command === 'expand');
107+
const column4 = thirdRowColumns.find((col) => col.caption === 'Column 4');
108+
109+
expect(expandColumnsInThirdRow.length).toBe(0);
110+
expect(column4).toBeDefined();
111+
expect(column4.rowspan).toBeUndefined();
112+
});
113+
114+
it('should place expand columns only in the first header row with showWhenGrouped', async () => {
115+
const { instance } = await createDataGrid({
116+
dataSource: [
117+
{ field1: 'g1', field2: 'g2', field3: 'g3' },
118+
],
119+
columns: [{
120+
dataField: 'field1',
121+
showWhenGrouped: true,
122+
groupIndex: 0,
123+
}, {
124+
caption: 'band2',
125+
columns: [{
126+
dataField: 'field2',
127+
showWhenGrouped: true,
128+
groupIndex: 1,
129+
}, {
130+
caption: 'band3',
131+
columns: [{
132+
dataField: 'field3',
133+
showWhenGrouped: true,
134+
groupIndex: 2,
135+
}],
136+
}],
137+
}],
138+
});
139+
140+
const columnsController = instance.getController('columns');
141+
const rowCount = columnsController.getRowCount();
142+
143+
expect(rowCount).toBe(3);
144+
145+
// Row 0: expand columns with rowspan=3, data columns with rowspan=3, band column
146+
const firstRowColumns = columnsController.getVisibleColumns(0);
147+
const expandColumnsRow0 = firstRowColumns.filter((col) => col.command === 'expand');
148+
149+
expect(expandColumnsRow0.length).toBe(3);
150+
expandColumnsRow0.forEach((col) => {
151+
expect(col.rowspan).toBe(3);
152+
});
153+
154+
// showWhenGrouped data columns should be in the first row with rowspan=3
155+
const field1Col = firstRowColumns.find((col) => col.caption === 'Field 1' && !col.command);
156+
expect(field1Col).toBeDefined();
157+
expect(field1Col.rowspan).toBe(3);
158+
159+
// band2 should be in the first row without rowspan (it has children)
160+
const band2Col = firstRowColumns.find((col) => col.caption === 'band2');
161+
expect(band2Col).toBeDefined();
162+
expect(band2Col.rowspan).toBeUndefined();
163+
164+
// Row 1: no expand columns
165+
const secondRowColumns = columnsController.getVisibleColumns(1);
166+
const expandColumnsRow1 = secondRowColumns.filter((col) => col.command === 'expand');
167+
168+
expect(expandColumnsRow1.length).toBe(0);
169+
170+
// band3 should be in the second row
171+
const band3Col = secondRowColumns.find((col) => col.caption === 'band3');
172+
expect(band3Col).toBeDefined();
173+
174+
// Row 2: no expand columns
175+
const thirdRowColumns = columnsController.getVisibleColumns(2);
176+
const expandColumnsRow2 = thirdRowColumns.filter((col) => col.command === 'expand');
177+
178+
expect(expandColumnsRow2.length).toBe(0);
179+
});
180+
181+
it('should not set rowspan on expand columns when there is only one header row with grouped showWhenGrouped columns', async () => {
182+
const { instance } = await createDataGrid({
183+
dataSource: [
184+
{
185+
field1: 'val1', field2: 'val2', field3: 'g1', field4: 'val4',
186+
},
187+
],
188+
columns: [
189+
'field1',
190+
'field2',
191+
{ dataField: 'field3', showWhenGrouped: true, groupIndex: 0 },
192+
],
193+
});
194+
195+
const columnsController = instance.getController('columns');
196+
const rowCount = columnsController.getRowCount();
197+
198+
expect(rowCount).toBe(1);
199+
200+
const visibleColumns = columnsController.getVisibleColumns(0);
201+
const expandColumn = visibleColumns.find((col) => col.command === 'expand' || col.type === 'groupExpand');
202+
203+
expect(expandColumn).toBeDefined();
204+
expect(expandColumn.rowspan).toBeUndefined();
205+
});
206+
207+
it('should not set rowspan on expand column when there is a single header row', async () => {
208+
const { instance } = await createDataGrid({
209+
dataSource,
210+
columns: ['id', 'name'],
211+
masterDetail: {
212+
enabled: true,
213+
},
214+
});
215+
216+
const columnsController = instance.getController('columns');
217+
const rowCount = columnsController.getRowCount();
218+
const firstRowColumns = columnsController.getVisibleColumns(0);
219+
220+
const expandColumn = firstRowColumns.find((col) => col.command === 'expand');
221+
222+
expect(rowCount).toBe(1);
223+
expect(expandColumn).toBeDefined();
224+
expect(expandColumn.rowspan).toBeUndefined();
225+
});
226+
});
227+
});

0 commit comments

Comments
 (0)