Skip to content

Commit 2f2cb57

Browse files
authored
DataGrid(T1281192 & T1298901) - Error occurs on an attempt to group custom columns at runtime (#30448) (#30490)
1 parent b09f20f commit 2f2cb57

File tree

9 files changed

+666
-19
lines changed

9 files changed

+666
-19
lines changed
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import DataGrid from 'devextreme-testcafe-models/dataGrid';
2+
import url from '../../../../helpers/getPageUrl';
3+
import { createWidget } from '../../../../helpers/createWidget';
4+
5+
fixture.disablePageReloads`Grouping API - calculateGroupValue runtime changes`
6+
.page(url(__dirname, '../../../container.html'));
7+
8+
const DATA_GRID_SELECTOR = '#container';
9+
10+
test(
11+
'One group: should expand grouped section after calculateGroupValue update',
12+
async (t) => {
13+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
14+
15+
await dataGrid.isReady();
16+
await dataGrid.apiColumnOption('group', 'calculateGroupValue', () => 'ALL');
17+
18+
await t
19+
.expect(await dataGrid.getGroupRow(0).isExpanded)
20+
.notOk()
21+
.expect(dataGrid.getGroupRowSelector().count)
22+
.eql(1)
23+
.expect(dataGrid.dataRows.count)
24+
.eql(0);
25+
26+
await t.click(dataGrid
27+
.getGroupRow(0)
28+
.getExpandCell());
29+
30+
await t
31+
.expect(await dataGrid.getGroupRow(0).isExpanded)
32+
.ok()
33+
.expect(dataGrid.getGroupRowSelector().count)
34+
.eql(1)
35+
.expect(dataGrid.dataRows.count)
36+
.eql(4);
37+
},
38+
).before(async () => createWidget('dxDataGrid', {
39+
dataSource: [
40+
{ id: 0, A: 'A_0', group: 'A' },
41+
{ id: 1, A: 'A_1', group: 'A' },
42+
{ id: 2, A: 'A_2', group: 'B' },
43+
{ id: 3, A: 'A_3', group: 'B' },
44+
],
45+
keyExpr: 'id',
46+
columns: [
47+
{ dataField: 'group', groupIndex: 0 },
48+
'A',
49+
],
50+
grouping: { autoExpandAll: false },
51+
}));
52+
53+
// NOTE: Intersection with "column configuration from first data source item" feature
54+
// Because one of first item's fields is null and different logic is applied
55+
test(
56+
'One group: should expand grouped section after calculateGroupValue update if first record contains null value',
57+
async (t) => {
58+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
59+
60+
await dataGrid.isReady();
61+
await dataGrid.apiColumnOption('group', 'calculateGroupValue', () => 'ALL');
62+
63+
await t
64+
.expect(await dataGrid.getGroupRow(0).isExpanded)
65+
.notOk()
66+
.expect(dataGrid.getGroupRowSelector().count)
67+
.eql(1)
68+
.expect(dataGrid.dataRows.count)
69+
.eql(0);
70+
71+
await t.click(dataGrid
72+
.getGroupRow(0)
73+
.getExpandCell());
74+
75+
await t
76+
.expect(await dataGrid.getGroupRow(0).isExpanded)
77+
.ok()
78+
.expect(dataGrid.getGroupRowSelector().count)
79+
.eql(1)
80+
.expect(dataGrid.dataRows.count)
81+
.eql(4);
82+
},
83+
).before(async () => createWidget('dxDataGrid', {
84+
dataSource: [
85+
{ id: 0, A: 'A_0', group: 'A' },
86+
{ id: 1, A: 'A_1', group: 'A' },
87+
{ id: 2, A: 'A_2', group: 'B' },
88+
{ id: 3, A: 'A_3', group: 'B' },
89+
],
90+
keyExpr: 'id',
91+
columns: [
92+
{ dataField: 'group', groupIndex: 0 },
93+
'A',
94+
],
95+
grouping: { autoExpandAll: false },
96+
}));
97+
98+
test(
99+
'Multiple groups: should expand grouped section after calculateGroupValue update',
100+
async (t) => {
101+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
102+
103+
await dataGrid.isReady();
104+
await dataGrid.apiColumnOption('group', 'calculateGroupValue', () => 'ALL');
105+
106+
await t
107+
.expect(await dataGrid.getGroupRow(0).isExpanded)
108+
.notOk()
109+
.expect(dataGrid.getGroupRowSelector().count)
110+
.eql(1)
111+
.expect(dataGrid.dataRows.count)
112+
.eql(0);
113+
114+
await t.click(dataGrid
115+
.getGroupRow(0)
116+
.getExpandCell());
117+
118+
await t
119+
.expect(await dataGrid.getGroupRow(0).isExpanded)
120+
.ok()
121+
.expect(dataGrid.getGroupRowSelector().count)
122+
.eql(5)
123+
.expect(dataGrid.dataRows.count)
124+
.eql(0);
125+
},
126+
).before(async () => createWidget('dxDataGrid', {
127+
dataSource: [
128+
{
129+
id: 0, A: 'A_0', B: 'B_0', group: 'A',
130+
},
131+
{
132+
id: 1, A: 'A_1', B: 'B_1', group: 'A',
133+
},
134+
{
135+
id: 2, A: 'A_2', B: 'B_2', group: 'B',
136+
},
137+
{
138+
id: 3, A: 'A_3', B: 'B_3', group: 'B',
139+
},
140+
],
141+
keyExpr: 'id',
142+
columns: [
143+
{ dataField: 'group', groupIndex: 0 },
144+
{ dataField: 'A', groupIndex: 1 },
145+
'B',
146+
],
147+
grouping: { autoExpandAll: false },
148+
}));
149+
150+
// NOTE: Intersection with "column configuration from first data source item" feature
151+
// Because one of first item's fields is null and different logic is applied
152+
test(
153+
'Multiple groups: should expand grouped section after calculateGroupValue update if first record contains null value [T1281192]',
154+
async (t) => {
155+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
156+
157+
await dataGrid.isReady();
158+
await dataGrid.apiColumnOption('group', 'calculateGroupValue', () => 'ALL');
159+
160+
await t
161+
.expect(await dataGrid.getGroupRow(0).isExpanded)
162+
.notOk()
163+
.expect(dataGrid.getGroupRowSelector().count)
164+
.eql(1)
165+
.expect(dataGrid.dataRows.count)
166+
.eql(0);
167+
168+
await t.click(dataGrid
169+
.getGroupRow(0)
170+
.getExpandCell());
171+
172+
await t
173+
.expect(await dataGrid.getGroupRow(0).isExpanded)
174+
.ok()
175+
.expect(dataGrid.getGroupRowSelector().count)
176+
.eql(5)
177+
.expect(dataGrid.dataRows.count)
178+
.eql(0);
179+
},
180+
).before(async () => createWidget('dxDataGrid', {
181+
dataSource: [
182+
{
183+
id: 0, A: 'A_0', B: null, group: 'A',
184+
},
185+
{
186+
id: 1, A: 'A_1', B: 'B_1', group: 'A',
187+
},
188+
{
189+
id: 2, A: 'A_2', B: 'B_2', group: 'B',
190+
},
191+
{
192+
id: 3, A: 'A_3', B: 'B_3', group: 'B',
193+
},
194+
],
195+
keyExpr: 'id',
196+
columns: [
197+
{ dataField: 'group', groupIndex: 0 },
198+
{ dataField: 'A', groupIndex: 1 },
199+
'B',
200+
],
201+
grouping: { autoExpandAll: false },
202+
}));
203+
204+
test('Should not reset sorting parameters after calculateGroupValue update [T1298901]', async (t) => {
205+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
206+
207+
await dataGrid.isReady();
208+
209+
await t
210+
.expect(await dataGrid.apiColumnOption('A', 'sortOrder'))
211+
.eql('desc')
212+
.expect(await dataGrid.apiColumnOption('A', 'sortIndex'))
213+
.eql(0);
214+
215+
await dataGrid.apiColumnOption('A', 'calculateGroupValue', () => 'ALL');
216+
217+
await t
218+
.expect(await dataGrid.apiColumnOption('A', 'sortOrder'))
219+
.eql('desc')
220+
.expect(await dataGrid.apiColumnOption('A', 'sortIndex'))
221+
.eql(0);
222+
}).before(async () => createWidget('dxDataGrid', {
223+
dataSource: [
224+
{ id: 0, A: 0, B: 'B_0' },
225+
{ id: 1, A: 1, B: 'B_1' },
226+
{ id: 2, A: 2, B: 'B_2' },
227+
{ id: 3, A: 3, B: 'B_3' },
228+
],
229+
keyExpr: 'id',
230+
columns: [
231+
{ dataField: 'A', sortOrder: 'desc' },
232+
'B',
233+
],
234+
sorting: { mode: 'single' },
235+
}));
236+
237+
test('Should not reset multiple sorting parameters after calculateGroupValue update [T1298901]', async (t) => {
238+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
239+
240+
await dataGrid.isReady();
241+
242+
await t
243+
.expect(await dataGrid.apiColumnOption('A', 'sortOrder'))
244+
.eql('desc')
245+
.expect(await dataGrid.apiColumnOption('A', 'sortIndex'))
246+
.eql(1)
247+
.expect(await dataGrid.apiColumnOption('B', 'sortOrder'))
248+
.eql('asc')
249+
.expect(await dataGrid.apiColumnOption('B', 'sortIndex'))
250+
.eql(0);
251+
252+
await dataGrid.apiColumnOption('A', 'calculateGroupValue', () => 'ALL');
253+
254+
await t
255+
.expect(await dataGrid.apiColumnOption('A', 'sortOrder'))
256+
.eql('desc')
257+
.expect(await dataGrid.apiColumnOption('A', 'sortIndex'))
258+
.eql(1)
259+
.expect(await dataGrid.apiColumnOption('B', 'sortOrder'))
260+
.eql('asc')
261+
.expect(await dataGrid.apiColumnOption('B', 'sortIndex'))
262+
.eql(0);
263+
}).before(async () => createWidget('dxDataGrid', {
264+
dataSource: [
265+
{ id: 0, A: 0, B: 'B_0' },
266+
{ id: 1, A: 1, B: 'B_1' },
267+
{ id: 2, A: 2, B: 'B_2' },
268+
{ id: 3, A: 3, B: 'B_3' },
269+
],
270+
keyExpr: 'id',
271+
columns: [
272+
{ dataField: 'A', sortOrder: 'desc', sortIndex: 1 },
273+
{ dataField: 'B', sortOrder: 'asc', sortIndex: 0 },
274+
],
275+
sorting: { mode: 'multiple' },
276+
}));

packages/devextreme/js/__internal/grids/data_grid/grouping/m_grouping_core.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { normalizeSortingInfo } from '@js/common/data/utils';
22
import $ from '@js/core/renderer';
33
import { when } from '@js/core/utils/deferred';
4+
import gridCoreUtils from '@ts/grids/grid_core/m_utils';
45

56
import gridCore from '../m_core';
67

@@ -262,7 +263,7 @@ export class GroupingHelper {
262263
that._group = storeLoadOptions.group;
263264

264265
for (let groupIndex = 0; groupIndex < groupsCount; groupIndex++) {
265-
if (oldGroups[groupIndex].selector !== groups[groupIndex].selector) {
266+
if (!gridCoreUtils.isEqualSelectors(oldGroups[groupIndex].selector, groups[groupIndex].selector)) {
266267
groupsCount = groupIndex;
267268
break;
268269
}

packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,10 +1282,10 @@ export class ColumnsController extends modules.Controller {
12821282
if (selector === column.dataField
12831283
|| selector === column.name
12841284
|| selector === column.displayField
1285-
|| selector === column.selector
1286-
|| selector === column.calculateCellValue
1287-
|| selector === column.calculateGroupValue
1288-
|| selector === column.calculateDisplayValue
1285+
|| gridCoreUtils.isEqualSelectors(selector, column.selector)
1286+
|| gridCoreUtils.isSelectorEqualWithCallback(selector, column.calculateCellValue)
1287+
|| gridCoreUtils.isSelectorEqualWithCallback(selector, column.calculateGroupValue)
1288+
|| gridCoreUtils.isSelectorEqualWithCallback(selector, column.calculateDisplayValue)
12891289
) {
12901290
if (fromDataSource) {
12911291
column.sortOrder = 'sortOrder' in column ? column.sortOrder : sortParameters[i].desc ? 'desc' : 'asc';

packages/devextreme/js/__internal/grids/grid_core/columns_controller/m_columns_controller_utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,6 @@ export const createColumnsFromDataSource = function (that: ColumnsController, da
311311

312312
for (let i = 0; i < firstItems.length; i++) {
313313
if (firstItems[i]) {
314-
// eslint-disable-next-line no-restricted-syntax
315314
for (fieldName in firstItems[i]) {
316315
if (!isFunction(firstItems[i][fieldName]) || variableWrapper.isWrapped(firstItems[i][fieldName])) {
317316
processedFields[fieldName] = true;
@@ -320,7 +319,6 @@ export const createColumnsFromDataSource = function (that: ColumnsController, da
320319
}
321320
}
322321

323-
// eslint-disable-next-line no-restricted-syntax
324322
for (fieldName in processedFields) {
325323
if (fieldName.indexOf('__') !== 0) {
326324
const column = createColumn(that, fieldName);
@@ -657,9 +655,11 @@ export const columnOptionCore = function (that: ColumnsController, column, optio
657655
// @ts-expect-error
658656
const prevValue = optionGetter(column, { functionsAsIs: true });
659657
if (!equalByValue(prevValue, value, { maxDepth: 5 })) {
660-
if (optionName === 'groupIndex' || optionName === 'calculateGroupValue') {
658+
if (optionName === 'groupIndex') {
661659
changeType = 'grouping';
662660
updateSortOrderWhenGrouping(that, column, value, prevValue);
661+
} else if (optionName === 'calculateGroupValue') {
662+
changeType = 'grouping';
663663
} else if (optionName === 'sortIndex' || optionName === 'sortOrder' || optionName === 'calculateSortValue') {
664664
changeType = 'sorting';
665665
} else {

packages/devextreme/js/__internal/grids/grid_core/m_utils.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import LoadPanel from '@js/ui/load_panel';
2121
import sharedFiltering from '@js/ui/shared/filtering';
2222
import type { ColumnPoint } from '@ts/grids/grid_core/m_types';
2323

24+
import { isEqualSelectors, isSelectorEqualWithCallback } from './utils/index';
25+
2426
const DATAGRID_SELECTION_DISABLED_CLASS = 'dx-selection-disabled';
2527
const DATAGRID_GROUP_OPENED_CLASS = 'dx-datagrid-group-opened';
2628
const DATAGRID_GROUP_CLOSED_CLASS = 'dx-datagrid-group-closed';
@@ -70,16 +72,6 @@ const getIntervalSelector = function () {
7072
}
7173
};
7274

73-
const equalSelectors = function (selector1, selector2) {
74-
if (isFunction(selector1) && isFunction(selector2)) {
75-
if (selector1.originalCallback && selector2.originalCallback) {
76-
return selector1.originalCallback === selector2.originalCallback && selector1.columnIndex === selector2.columnIndex;
77-
}
78-
}
79-
80-
return selector1 === selector2;
81-
};
82-
8375
function isDateType(dataType) {
8476
return dataType === 'date' || dataType === 'datetime';
8577
}
@@ -427,7 +419,7 @@ export default {
427419
return false;
428420
}
429421
for (let i = 0; i < sortParameters1.length; i++) {
430-
if (!equalSelectors(sortParameters1[i].selector, sortParameters2[i].selector) || sortParameters1[i].desc !== sortParameters2[i].desc || sortParameters1[i].groupInterval !== sortParameters2[i].groupInterval || (!ignoreIsExpanded && Boolean(sortParameters1[i].isExpanded) !== Boolean(sortParameters2[i].isExpanded))) {
422+
if (!isEqualSelectors(sortParameters1[i].selector, sortParameters2[i].selector) || sortParameters1[i].desc !== sortParameters2[i].desc || sortParameters1[i].groupInterval !== sortParameters2[i].groupInterval || (!ignoreIsExpanded && Boolean(sortParameters1[i].isExpanded) !== Boolean(sortParameters2[i].isExpanded))) {
431423
return false;
432424
}
433425
}
@@ -797,4 +789,8 @@ export default {
797789

798790
return !!customCommandColumns.length;
799791
},
792+
793+
// New utils
794+
isEqualSelectors,
795+
isSelectorEqualWithCallback,
800796
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export {
2+
isEqualSelectors,
3+
isSelectorEqualWithCallback,
4+
} from './selector_comparison';

0 commit comments

Comments
 (0)