Skip to content

Commit f00e956

Browse files
dmirgaevRaushen
andauthored
T1311329 - DataGrid - Column chooser hides a banded column on using search and recursive selection (#32186) (#32237)
Co-authored-by: Andrey Dolzhikov <16618553+Raushen@users.noreply.github.com>
1 parent d16d74c commit f00e956

File tree

7 files changed

+490
-25
lines changed

7 files changed

+490
-25
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { TreeViewModel } from '@ts/ui/__tests__/__mock__/model/tree_view';
2+
3+
const CLASSES = {
4+
columnChooser: 'dx-datagrid-column-chooser',
5+
columnChooserList: 'dx-datagrid-column-chooser-list',
6+
popupWrapper: 'dx-popup-wrapper',
7+
};
8+
9+
export class ColumnChooserModel {
10+
constructor(protected readonly root: HTMLElement) {}
11+
12+
private getPopupWrapper(): HTMLElement | null {
13+
return document.body.querySelector(`.${CLASSES.popupWrapper}.${CLASSES.columnChooser}`);
14+
}
15+
16+
private getOverlay(): HTMLElement | null {
17+
const wrapper = this.getPopupWrapper();
18+
return wrapper?.querySelector('.dx-overlay-content') ?? null;
19+
}
20+
21+
private getTreeView(): TreeViewModel | null {
22+
const overlay = this.getOverlay();
23+
if (!overlay) return null;
24+
25+
const treeViewElement = overlay.querySelector(`.${CLASSES.columnChooserList}`) as HTMLElement;
26+
return treeViewElement ? new TreeViewModel(treeViewElement) : null;
27+
}
28+
29+
public isVisible(): boolean {
30+
return this.getOverlay() !== null;
31+
}
32+
33+
public searchColumn(text: string): void {
34+
const treeView = this.getTreeView();
35+
treeView?.setSearchValue(text);
36+
}
37+
38+
public toggleColumn(columnText: string): void {
39+
const treeView = this.getTreeView();
40+
const checkBox = treeView?.getCheckboxByText(columnText);
41+
checkBox?.toggle();
42+
}
43+
}

packages/devextreme/js/__internal/grids/grid_core/__tests__/__mock__/model/grid_core.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { AIPromptEditorModel } from './ai_prompt_editor';
1010
import { AIHeaderCellModel } from './cell/ai_header_cell';
1111
import { DataCellModel } from './cell/data_cell';
1212
import { HeaderCellModel } from './cell/header_cell';
13+
import { ColumnChooserModel } from './column_chooser';
1314
import { EditFormModel } from './edit_form';
1415
import { DataRowModel } from './row/data_row';
1516

@@ -134,5 +135,9 @@ export abstract class GridCoreModel<TInstance extends GridBase = GridBase> {
134135
return new EditFormModel(this.root.querySelector(`.${this.addWidgetPrefix(SELECTORS.editForm)}`));
135136
}
136137

138+
public getColumnChooser(): ColumnChooserModel {
139+
return new ColumnChooserModel(this.root);
140+
}
141+
137142
public abstract getInstance(): TInstance;
138143
}
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
import {
2+
afterEach, beforeEach, describe, expect, it, jest,
3+
} from '@jest/globals';
4+
import type { dxElementWrapper } from '@js/core/renderer';
5+
import $ from '@js/core/renderer';
6+
import type { Properties as DataGridProperties } from '@js/ui/data_grid';
7+
import DataGrid from '@js/ui/data_grid';
8+
import errors from '@js/ui/widget/ui.errors';
9+
import { DataGridModel } from '@ts/grids/data_grid/__tests__/__mock__/model/data_grid';
10+
11+
const SELECTORS = {
12+
gridContainer: '#gridContainer',
13+
};
14+
15+
const GRID_CONTAINER_ID = 'gridContainer';
16+
17+
const createDataGrid = async (
18+
options: DataGridProperties = {},
19+
): Promise<{
20+
$container: dxElementWrapper;
21+
component: DataGridModel;
22+
instance: DataGrid;
23+
}> => new Promise((resolve) => {
24+
const $container = $('<div>')
25+
.attr('id', GRID_CONTAINER_ID)
26+
.appendTo(document.body);
27+
28+
const dataGridOptions: DataGridProperties = {
29+
keyExpr: 'id',
30+
...options,
31+
};
32+
33+
const instance = new DataGrid($container.get(0) as HTMLDivElement, dataGridOptions);
34+
const component = new DataGridModel($container.get(0) as HTMLElement);
35+
36+
jest.runAllTimers();
37+
38+
resolve({
39+
$container,
40+
component,
41+
instance,
42+
});
43+
});
44+
45+
const beforeTest = (): void => {
46+
jest.useFakeTimers();
47+
jest.spyOn(errors, 'log').mockImplementation(jest.fn());
48+
jest.spyOn(errors, 'Error').mockImplementation(() => ({}));
49+
};
50+
51+
const afterTest = (): void => {
52+
const $container = $(SELECTORS.gridContainer);
53+
const dataGrid = ($container as any).dxDataGrid('instance') as DataGrid;
54+
55+
dataGrid.dispose();
56+
$container.remove();
57+
jest.clearAllMocks();
58+
jest.useRealTimers();
59+
};
60+
61+
describe('Bugs', () => {
62+
beforeEach(beforeTest);
63+
afterEach(afterTest);
64+
65+
describe('T1311329 - DataGrid - Column chooser hides a banded column on using search and recursive selection', () => {
66+
it('should not hide banded column when using search (two levels)', async () => {
67+
const { instance, component } = await createDataGrid({
68+
dataSource: [
69+
{
70+
id: 1,
71+
name: 'Name 1',
72+
value: 10,
73+
phone: 'Banded 1',
74+
email: 'Banded 2',
75+
skype: 'Banded 3',
76+
},
77+
],
78+
columnChooser: {
79+
enabled: true,
80+
search: {
81+
enabled: true,
82+
},
83+
mode: 'select',
84+
selection: {
85+
recursive: true,
86+
selectByClick: true,
87+
allowSelectAll: true,
88+
},
89+
},
90+
columns: [
91+
{ dataField: 'id', caption: 'ID' },
92+
{ dataField: 'name', caption: 'Name' },
93+
{ dataField: 'value', caption: 'Value' },
94+
{
95+
caption: 'Contacts',
96+
columns: [
97+
{
98+
dataField: 'phone',
99+
visible: false,
100+
},
101+
{
102+
dataField: 'email',
103+
},
104+
{
105+
dataField: 'skype',
106+
},
107+
],
108+
},
109+
],
110+
});
111+
112+
let visibleColumnsLevel0 = instance.getVisibleColumns(0);
113+
let visibleColumnsLevel1 = instance.getVisibleColumns(1);
114+
115+
expect(visibleColumnsLevel0.find((col) => col.caption === 'Contacts')).toBeDefined();
116+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'phone')).toBeUndefined();
117+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'email')).toBeDefined();
118+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'skype')).toBeDefined();
119+
expect(visibleColumnsLevel0.find((col) => col.dataField === 'name')).toBeDefined();
120+
121+
instance.showColumnChooser();
122+
jest.runAllTimers();
123+
124+
const columnChooser = component.getColumnChooser();
125+
expect(columnChooser.isVisible()).toBe(true);
126+
127+
columnChooser.searchColumn('n');
128+
jest.runAllTimers();
129+
130+
columnChooser.toggleColumn('Name');
131+
jest.runAllTimers();
132+
133+
visibleColumnsLevel0 = instance.getVisibleColumns(0);
134+
visibleColumnsLevel1 = instance.getVisibleColumns(1);
135+
136+
expect(visibleColumnsLevel0.find((col) => col.caption === 'Contacts')).toBeDefined();
137+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'phone')).toBeUndefined();
138+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'email')).toBeDefined();
139+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'skype')).toBeDefined();
140+
expect(visibleColumnsLevel0.find((col) => col.dataField === 'name')).toBeUndefined();
141+
});
142+
143+
it('should not hide banded column when using search (three levels)', async () => {
144+
const { instance, component } = await createDataGrid({
145+
dataSource: [],
146+
columnChooser: {
147+
enabled: true,
148+
search: {
149+
enabled: true,
150+
},
151+
mode: 'select',
152+
selection: {
153+
recursive: true,
154+
selectByClick: true,
155+
allowSelectAll: true,
156+
},
157+
},
158+
columns: [
159+
{
160+
caption: 'band_level1',
161+
columns: [
162+
{
163+
caption: 'band_level2',
164+
columns: [
165+
{
166+
dataField: 'data1_level3',
167+
visible: false,
168+
},
169+
{
170+
dataField: 'data2_level3',
171+
},
172+
],
173+
},
174+
{
175+
dataField: 'data1_level2',
176+
},
177+
{
178+
dataField: 'data2_level2',
179+
},
180+
],
181+
},
182+
{
183+
dataField: 'data1_level1',
184+
},
185+
],
186+
});
187+
188+
let visibleColumnsLevel0 = instance.getVisibleColumns(0);
189+
let visibleColumnsLevel1 = instance.getVisibleColumns(1);
190+
let visibleColumnsLevel2 = instance.getVisibleColumns(2);
191+
192+
expect(visibleColumnsLevel0.find((col) => col.caption === 'band_level1')).toBeDefined();
193+
expect(visibleColumnsLevel0.find((col) => col.dataField === 'data1_level1')).toBeDefined();
194+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'data1_level2')).toBeDefined();
195+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'data2_level2')).toBeDefined();
196+
expect(visibleColumnsLevel1.find((col) => col.caption === 'band_level2')).toBeDefined();
197+
expect(visibleColumnsLevel2.find((col) => col.dataField === 'data1_level3')).toBeUndefined();
198+
expect(visibleColumnsLevel2.find((col) => col.dataField === 'data2_level3')).toBeDefined();
199+
200+
instance.showColumnChooser();
201+
jest.runAllTimers();
202+
203+
const columnChooser = component.getColumnChooser();
204+
expect(columnChooser.isVisible()).toBe(true);
205+
206+
columnChooser.searchColumn('1');
207+
jest.runAllTimers();
208+
209+
columnChooser.toggleColumn('Data 1 level 1');
210+
jest.runAllTimers();
211+
212+
visibleColumnsLevel0 = instance.getVisibleColumns(0);
213+
visibleColumnsLevel1 = instance.getVisibleColumns(1);
214+
visibleColumnsLevel2 = instance.getVisibleColumns(2);
215+
216+
expect(visibleColumnsLevel0.find((col) => col.caption === 'band_level1')).toBeDefined();
217+
expect(visibleColumnsLevel0.find((col) => col.dataField === 'data1_level1')).toBeUndefined();
218+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'data1_level2')).toBeDefined();
219+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'data2_level2')).toBeDefined();
220+
expect(visibleColumnsLevel1.find((col) => col.caption === 'band_level2')).toBeDefined();
221+
expect(visibleColumnsLevel2.find((col) => col.dataField === 'data1_level3')).toBeUndefined();
222+
expect(visibleColumnsLevel2.find((col) => col.dataField === 'data2_level3')).toBeDefined();
223+
});
224+
225+
it('should hide banded column by click', async () => {
226+
const { instance, component } = await createDataGrid({
227+
dataSource: [
228+
{
229+
id: 1,
230+
name: 'Name 1',
231+
value: 10,
232+
phone: 'Banded 1',
233+
email: 'Banded 2',
234+
skype: 'Banded 3',
235+
},
236+
],
237+
columnChooser: {
238+
enabled: true,
239+
search: {
240+
enabled: true,
241+
},
242+
mode: 'select',
243+
selection: {
244+
recursive: true,
245+
selectByClick: true,
246+
allowSelectAll: true,
247+
},
248+
},
249+
columns: [
250+
{ dataField: 'id', caption: 'ID' },
251+
{ dataField: 'name', caption: 'Name' },
252+
{ dataField: 'value', caption: 'Value' },
253+
{
254+
caption: 'Contacts',
255+
columns: [
256+
{
257+
dataField: 'phone',
258+
visible: false,
259+
},
260+
{
261+
dataField: 'email',
262+
},
263+
{
264+
dataField: 'skype',
265+
},
266+
],
267+
},
268+
],
269+
});
270+
let visibleColumnsLevel0 = instance.getVisibleColumns(0);
271+
let visibleColumnsLevel1 = instance.getVisibleColumns(1);
272+
273+
expect(visibleColumnsLevel0.find((col) => col.caption === 'Contacts')).toBeDefined();
274+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'phone')).toBeUndefined();
275+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'email')).toBeDefined();
276+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'skype')).toBeDefined();
277+
278+
instance.showColumnChooser();
279+
jest.runAllTimers();
280+
281+
const columnChooser = component.getColumnChooser();
282+
expect(columnChooser.isVisible()).toBe(true);
283+
284+
columnChooser.toggleColumn('Contacts');
285+
jest.runAllTimers();
286+
287+
visibleColumnsLevel0 = instance.getVisibleColumns(0);
288+
visibleColumnsLevel1 = instance.getVisibleColumns(1);
289+
290+
expect(visibleColumnsLevel0.find((col) => col.caption === 'Contacts')).toBeDefined();
291+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'phone')).toBeDefined();
292+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'email')).toBeDefined();
293+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'skype')).toBeDefined();
294+
295+
columnChooser.toggleColumn('Contacts');
296+
jest.runAllTimers();
297+
298+
visibleColumnsLevel0 = instance.getVisibleColumns(0);
299+
visibleColumnsLevel1 = instance.getVisibleColumns(1);
300+
301+
expect(visibleColumnsLevel0.find((col) => col.caption === 'Contacts')).toBeUndefined();
302+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'phone')).toBeUndefined();
303+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'email')).toBeUndefined();
304+
expect(visibleColumnsLevel1.find((col) => col.dataField === 'skype')).toBeUndefined();
305+
});
306+
});
307+
});

0 commit comments

Comments
 (0)