Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { equalByValue } from '@js/core/utils/common';
import { compileGetter } from '@js/core/utils/data';
import { Deferred } from '@js/core/utils/deferred';
import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller';
import type { DataSourceController } from '@ts/grids/grid_core/data_source/data_source_controller';
import type { FocusDataSourceControllerExtension } from '@ts/grids/grid_core/focus/extenders/focus_data_source_controller';
import { focusModule } from '@ts/grids/grid_core/focus/focus_module';
import type { ModuleType } from '@ts/grids/grid_core/m_types';

Expand All @@ -24,7 +26,8 @@ DataController
& GroupingDataControllerExtension>;

const data = (Base: DataControllerBase) => class FocusDataControllerExtender extends focusModule.extenders.controllers.data(Base) {
public declare _dataSource?: GroupingDataSourceAdapter | null;
protected declare dataSourceController: DataSourceController<GroupingDataSourceAdapter>
& FocusDataSourceControllerExtension;

private changeRowExpand(path, isRowClick) {
// @ts-expect-error
Expand Down Expand Up @@ -89,58 +92,58 @@ const data = (Base: DataControllerBase) => class FocusDataControllerExtender ext
}

private _calculateGlobalRowIndexByGroupedData(key) {
const dataSource = this._dataSource;
const dataSourceAdapter = this.dataSourceController.getAdapter();
const filter = this._generateFilterByKey(key);
// @ts-expect-error
const deferred = new Deferred();
const isGroupKey = Array.isArray(key);

if (isGroupKey || !dataSource) {
if (isGroupKey || !dataSourceAdapter) {
return deferred.resolve(-1).promise();
}

const group = dataSource.group();
const group = dataSourceAdapter.group();

if (!dataSource._grouping._updatePagingOptions) {
if (!dataSourceAdapter._grouping._updatePagingOptions) {
this._calculateGlobalRowIndexByFlatData(key, null, true)
.done(deferred.resolve)
.fail(deferred.reject);
return deferred;
}

dataSource.customLoader.load({
dataSourceAdapter.customLoader.load({
filter: this._concatWithCombinedFilter(filter),
group,
}).done(({ data }) => {
const hasData = Array.isArray(data) && data.length > 0;

if (this._dataSource !== dataSource || !hasData) {
if (this.dataSourceController.getAdapter() !== dataSourceAdapter || !hasData) {
return deferred.resolve(-1).promise();
}

const groupPath = this._getGroupPath(data, gridCore.normalizeSortingInfo(group).length);

this._expandGroupByPath(this, groupPath, 0).done(() => {
this._calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSource);
this._calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSourceAdapter);
}).fail(deferred.reject);
}).fail(deferred.reject);

return deferred.promise();
}

private _calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSource) {
if (this._dataSource !== dataSource) {
private _calculateExpandedRowGlobalIndex(deferred, key, groupPath, group, dataSourceAdapter) {
if (this.dataSourceController.getAdapter() !== dataSourceAdapter) {
deferred.resolve(-1);
return;
}

const groupFilter = createGroupFilter(groupPath, { group });
const scrollingMode = this.option('scrolling.mode');
const isVirtualScrolling = scrollingMode === 'virtual' || scrollingMode === 'infinite';
const pageSize = dataSource.pageSize();
const pageSize = dataSourceAdapter.pageSize();
let groupOffset;

dataSource._grouping._updatePagingOptions({ skip: 0, take: MAX_SAFE_INTEGER }, (groupInfo, totalOffset) => {
dataSourceAdapter._grouping._updatePagingOptions({ skip: 0, take: MAX_SAFE_INTEGER }, (groupInfo, totalOffset) => {
if (equalByValue(groupInfo.path, groupPath)) {
groupOffset = totalOffset;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {
afterEach, beforeEach, describe, expect, it,
} from '@jest/globals';
import {
afterTest,
beforeTest,
createDataGrid,
flushAsync,
} from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils';

const DATA = [
{ id: 1, group: 'A', value: 10 },
{ id: 2, group: 'A', value: 20 },
{ id: 3, group: 'B', value: 30 },
];

const createGroupedGrid = async (
autoExpandAll = true,
): ReturnType<typeof createDataGrid> => createDataGrid({
dataSource: DATA,
columns: [{ dataField: 'group', groupIndex: 0 }, 'value'],
grouping: { autoExpandAll },
paging: { enabled: false },
});

const groupRowKeys = (
instance: Awaited<ReturnType<typeof createDataGrid>>['instance'],
): unknown[] => instance.getVisibleRows()
.filter((row) => row.rowType === 'group')
.map((row) => row.key);

describe('Grouping data controller — expanding', () => {
beforeEach(beforeTest);
afterEach(afterTest);

describe('with a data source', () => {
it('reports every group expanded when autoExpandAll is on', async () => {
const { instance } = await createGroupedGrid();
await flushAsync();

expect(groupRowKeys(instance)).toEqual([['A'], ['B']]);
expect(instance.isRowExpanded(['A'])).toBe(true);
expect(instance.isRowExpanded(['B'])).toBe(true);
});

it('reports every group collapsed when autoExpandAll is off', async () => {
const { instance } = await createGroupedGrid(false);
await flushAsync();

expect(instance.isRowExpanded(['A'])).toBe(false);
expect(instance.isRowExpanded(['B'])).toBe(false);
});

it('collapses one group through collapseRow', async () => {
const { instance } = await createGroupedGrid();
await flushAsync();

const collapsing = instance.collapseRow(['A']);
await flushAsync();
await collapsing;

expect(instance.isRowExpanded(['A'])).toBe(false);
expect(instance.isRowExpanded(['B'])).toBe(true);
});

it('expands one group through expandRow', async () => {
const { instance } = await createGroupedGrid(false);
await flushAsync();

const expanding = instance.expandRow(['A']);
await flushAsync();
await expanding;

expect(instance.isRowExpanded(['A'])).toBe(true);
expect(instance.isRowExpanded(['B'])).toBe(false);
});

it('collapses every group through collapseAll', async () => {
const { instance } = await createGroupedGrid();
await flushAsync();

instance.collapseAll();
await flushAsync();

expect(instance.isRowExpanded(['A'])).toBe(false);
expect(instance.isRowExpanded(['B'])).toBe(false);
});

it('expands every group through expandAll', async () => {
const { instance } = await createGroupedGrid(false);
await flushAsync();

instance.expandAll();
await flushAsync();

expect(instance.isRowExpanded(['A'])).toBe(true);
expect(instance.isRowExpanded(['B'])).toBe(true);
});

it('resets the page index when collapseAll changes the expanded state', async () => {
const { instance } = await createDataGrid({
dataSource: DATA,
columns: [{ dataField: 'group', groupIndex: 0 }, 'value'],
grouping: { autoExpandAll: true },
paging: { pageSize: 2 },
});
await flushAsync();

const paging = instance.pageIndex(1);
await flushAsync();
await paging;
expect(instance.pageIndex()).toBe(1);

instance.collapseAll();
await flushAsync();

expect(instance.pageIndex()).toBe(0);
});
});

describe('with no data source', () => {
it('reports a group as not expanded', async () => {
const { instance } = await createDataGrid({
columns: [{ dataField: 'group', groupIndex: 0 }, 'value'],
});
await flushAsync();

expect(instance.isRowExpanded(['A'])).toBe(false);
});

it('resolves expandRow without reaching a data source', async () => {
const { instance } = await createDataGrid({
columns: [{ dataField: 'group', groupIndex: 0 }, 'value'],
});
await flushAsync();

await instance.expandRow(['A']);

expect(instance.getVisibleRows()).toEqual([]);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Properties } from '@js/ui/data_grid';
import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller';
import type { ItemProcessingOptions, ProcessedItem } from '@ts/grids/grid_core/data_controller/types';
import { countRowsBefore } from '@ts/grids/grid_core/data_controller/utils/row_changes';
import type { DataSourceController } from '@ts/grids/grid_core/data_source/data_source_controller';
import type { RawItemData } from '@ts/grids/grid_core/data_source_adapter/types';
import type {
ModuleType,
Expand All @@ -23,7 +24,7 @@ import {
export const groupingDataControllerExtender = (
Base: ModuleType<DataController>,
): ModuleType<DataController> => class GroupingDataControllerExtender extends Base {
public declare _dataSource?: GroupingDataSourceAdapter | null;
protected declare dataSourceController: DataSourceController<GroupingDataSourceAdapter>;

public init(): void {
super.init();
Expand Down Expand Up @@ -170,18 +171,18 @@ export const groupingDataControllerExtender = (
}

private collapseAll(groupIndex: number): void {
const dataSource = this._dataSource;
if (dataSource?.collapseAll(groupIndex)) {
dataSource?.pageIndex(0);
dataSource?.reload();
const dataSourceAdapter = this.dataSourceController.getAdapter();
if (dataSourceAdapter?.collapseAll(groupIndex)) {
dataSourceAdapter.pageIndex(0);
dataSourceAdapter.reload();
}
}

private expandAll(groupIndex: number): void {
const dataSource = this._dataSource;
if (dataSource?.expandAll(groupIndex)) {
dataSource?.pageIndex(0);
dataSource?.reload();
const dataSourceAdapter = this.dataSourceController.getAdapter();
if (dataSourceAdapter?.expandAll(groupIndex)) {
dataSourceAdapter.pageIndex(0);
dataSourceAdapter.reload();
}
}

Expand All @@ -205,13 +206,13 @@ export const groupingDataControllerExtender = (
}

protected changeRowExpandCore(key: RowKey): DeferredObj<unknown> {
const dataSource = this._dataSource;
const dataSourceAdapter = this.dataSourceController.getAdapter();

const d = Deferred();
if (!dataSource) {
if (!dataSourceAdapter) {
d.resolve();
} else {
when(dataSource.changeRowExpand(key)).done(() => {
when(dataSourceAdapter.changeRowExpand(key)).done(() => {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
this.load().done(d.resolve).fail(d.reject);
// eslint-disable-next-line @typescript-eslint/no-misused-promises
Expand All @@ -222,7 +223,7 @@ export const groupingDataControllerExtender = (
}

private isRowExpanded(key: RowKey): boolean {
return !!this._dataSource?.isRowExpanded(key);
return !!this.dataSourceController.getAdapter()?.isRowExpanded(key);
}

private expandRow(key: RowKey): DeferredObj<unknown> {
Expand Down
Loading
Loading