Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1495,6 +1495,54 @@ test.meta({ runInTheme: Themes.genericLight })('New virtual mode. Navigation to
});
});

// T1284002
test('Last group should not disappear after collapsing another subgroup with virtual scrolling, local grouping and remote operations (T1284002)', async (t) => {
const dataGrid = new DataGrid('#container');
await t.expect(dataGrid.isReady()).ok();

await dataGrid.apiCollapseRow(['Cell phones', 'Touch Screen Phones']);

const visibleRows = await dataGrid.apiGetVisibleRows();
const dataRows = visibleRows.filter((r) => r.rowType === 'data');

await t.expect(dataRows.length).eql(2, 'Computers category should still show 2 data rows');
}).before(async () => createWidget('dxDataGrid', {
height: 500,
dataSource: [
{
Id: 1, Category: 'Cell phones', Subcategory: 'Touch Screen Phones', Store: 'Europe Online Store', Date: '2024-01-10',
},
{
Id: 2, Category: 'Cell phones', Subcategory: 'Touch Screen Phones', Store: 'Europe Online Store', Date: '2024-02-15',
},
{
Id: 3, Category: 'Computers', Subcategory: 'Computers Accessories', Store: 'North America Reseller', Date: '2024-03-20',
},
{
Id: 4, Category: 'Computers', Subcategory: 'Computers Accessories', Store: 'North America Online Store', Date: '2024-04-25',
},
],
keyExpr: 'Id',
remoteOperations: {
filtering: true,
sorting: true,
paging: true,
},
scrolling: {
mode: 'virtual',
},
grouping: {
autoExpandAll: true,
},
columns: [
{ dataField: 'Id', dataType: 'number' },
{ dataField: 'Category', dataType: 'string', groupIndex: 0 },
{ dataField: 'Subcategory', dataType: 'string', groupIndex: 1 },
{ dataField: 'Store', dataType: 'string', groupIndex: 2 },
{ dataField: 'Date', dataType: 'date', format: 'yyyy-MM-dd' },
],
}));

// T1152498
// TODO: fix unstable tests
// ['infinite', 'virtual'].forEach((scrollingMode) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { GroupingHelper, updateGroupOffsets } from '../m_grouping_expanded';
import type { DataItem, GroupInfoData, GroupItemData } from '../types';

export interface GroupConfig {
key: string;
count: number;
serverCount?: number;
}

/**
* Test helper that simulates the grid's expand/collapse flow for grouped data.
*
* Constructed from a configuration array — each entry generates a group
* with `count` leaf items (ids like `"A0"`, `"A1"`, …).
* When `serverCount` is provided it is used as the group's total instead of
* the visible leaf count, mirroring the real server-side paging scenario.
*
* - `collapse(path)` — nullifies `group.items` and registers the group as collapsed.
* - `expand(path)` — restores the saved children and marks the group as expanded.
*
* After each operation all group offsets are recalculated via `updateGroupOffsets`,
* exactly matching the `changeRowExpand` flow in the production code.
*/

export class GroupingTestHelper {
public readonly grouping: GroupingHelper;

public readonly items: DataItem[];

private readonly groupsByKey: Map<string, GroupItemData>;

private readonly leafCounts: Map<string, number>;

/** Saved children snapshots so expand can restore them. */
private readonly savedChildren = new Map<string, GroupItemData[] | null>();

constructor(groups: GroupConfig[]) {
this.grouping = new GroupingHelper({ option: (): undefined => undefined });
this.groupsByKey = new Map();
this.leafCounts = new Map();

// Initialize group items
this.items = groups.map(({ key, count, serverCount }) => {
const items = Array.from({ length: count }, (_, i) => ({ id: `${key}${i}` }));
const group = { key, items } as unknown as GroupItemData;
this.groupsByKey.set(key, group);
this.leafCounts.set(key, serverCount ?? count);

return group;
});
}

public collapse(path: string[]): void {
const key = path[0];
const group = this.groupsByKey.get(key);
const count = this.leafCounts.get(key) ?? 0;

this.simulateChangeRowExpand(path, count);

if (group) {
this.savedChildren.set(key, group.items);
group.items = null;
}
}

public expand(path: string[]): void {
const groupInfo = this.grouping.findGroupInfo(path);
const count = groupInfo ? groupInfo.count : 0;

this.simulateChangeRowExpand(path, count);

const key = path[0];
const group = this.groupsByKey.get(key);
if (group) {
group.items = this.savedChildren.get(key) ?? [];
}
}

private simulateChangeRowExpand(
path: unknown[],
count: number,
): void {
const groupInfo = this.grouping.findGroupInfo(path);

const pendingGroupInfo: GroupInfoData = {
offset: groupInfo ? groupInfo.offset : -1,
path: groupInfo ? groupInfo.path : path,
isExpanded: groupInfo ? !groupInfo.isExpanded : false,
count,
};

updateGroupOffsets(this.grouping, this.items, [], 0, pendingGroupInfo);

if (groupInfo) {
groupInfo.isExpanded = !groupInfo.isExpanded;
groupInfo.count = count;
} else if (pendingGroupInfo.offset >= 0) {
this.grouping.addGroupInfo(pendingGroupInfo);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { GroupingHelper } from '../m_grouping_expanded';

/** Subclass that exposes the protected handleDataLoading method for testing. */
export class GroupingHelperMock extends GroupingHelper {
public testHandleDataLoading(options: unknown): void {
this.handleDataLoading(options);
}
}
Loading
Loading