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
Original file line number Diff line number Diff line change
Expand Up @@ -611,13 +611,15 @@ export class ExportController extends dataGridCore.ViewController {

private _getSummaryCells(summaryTotalItems, totalAggregates) {
// @ts-expect-error
return this._dataController._calculateSummaryCells(
summaryTotalItems,
totalAggregates,
this._columnsController.getVisibleColumns(null, true),
// @ts-expect-error
(summaryItem, column) => (this._dataController._isDataColumn(column) ? column.index : -1),
);
return this._dataController._calculateSummaryCells({
summaryItems: summaryTotalItems,
aggregates: totalAggregates,
visibleColumns: this._columnsController.getVisibleColumns(null, true),
calculateTargetColumnIndex: (summaryItem, column) => (
// @ts-expect-error
this._dataController._isDataColumn(column) ? column.index : -1
),
});
}

public _getSelectedItems() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import {
afterEach, beforeEach, describe, expect, it, jest,
} from '@jest/globals';

import {
afterTest,
beforeTest,
createDataGrid,
flushAsync,
} from '../../../grid_core/__tests__/__mock__/helpers/utils';

describe('Summary', () => {
beforeEach(beforeTest);
afterEach(afterTest);

describe('column lookup map performance optimization (T1316562)', () => {
const SUMMARY_COUNT = 100;
const GROUP_COUNT = 4;

const dataSource = [
{
id: 1, name: 'Alice', value: 10, category: 'A', region: 'X',
},
{
id: 2, name: 'Bob', value: 20, category: 'A', region: 'Y',
},
{
id: 3, name: 'Carol', value: 30, category: 'B', region: 'X',
},
{
id: 4, name: 'Dave', value: 40, category: 'B', region: 'Y',
},
];

const groupSummaryItems = Array.from(
{ length: SUMMARY_COUNT },
(_, i) => ({
column: i % 2 === 0 ? 'value' : 'id',
summaryType: 'sum' as const,
showInGroupFooter: false,
name: `summary_${i}`,
}),
);

it('should use columnMap optimization and avoid O(n*m) columnOption calls on refresh', async () => {
const { instance } = await createDataGrid({
dataSource,
columns: [
{ dataField: 'id' },
{ dataField: 'name' },
{ dataField: 'value' },
{ dataField: 'category', groupIndex: 0 },
{ dataField: 'region', groupIndex: 1 },
],
summary: { groupItems: groupSummaryItems },
});

await flushAsync();

const columnsController = instance.getController('columns');
const spy = jest.spyOn(columnsController, 'columnOption');

instance.refresh().catch(() => {});
await flushAsync();

const worstCaseMinCalls = SUMMARY_COUNT * GROUP_COUNT;

expect(spy.mock.calls.length).toBeLessThan(worstCaseMinCalls);

spy.mockRestore();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import {
describe, expect, it,
} from '@jest/globals';
import type { Column } from '@ts/grids/grid_core/columns_controller/types';

import { getColumnFromMap, getSummaryCellIndex } from '../utils';

const makeColumn = (overrides: Partial<Column> = {}): Column => ({
...overrides,
});

describe('getSummaryCellIndex', () => {
describe('when isGroupRow is false (default)', () => {
it('should return column.index', () => {
const column = makeColumn({ index: 5 });

expect(getSummaryCellIndex(column)).toBe(5);
});

it('should return -1 when column.index is undefined', () => {
const column = makeColumn();

expect(getSummaryCellIndex(column)).toBe(-1);
});

it('should ignore prevColumn when isGroupRow is false', () => {
const column = makeColumn({ index: 3 });
const prevColumn = makeColumn({ index: 10, type: 'groupExpand' });

expect(getSummaryCellIndex(column, prevColumn, false)).toBe(3);
});
});

describe('when isGroupRow is true', () => {
describe('groupExpand handling', () => {
it('should return prevColumn.index when prevColumn.type is groupExpand', () => {
const column = makeColumn({ index: 5 });
const prevColumn = makeColumn({ index: 2, type: 'groupExpand' });

expect(getSummaryCellIndex(column, prevColumn, true)).toBe(2);
});

it('should return prevColumn.index when column.type is groupExpand', () => {
const column = makeColumn({ index: 5, type: 'groupExpand' });
const prevColumn = makeColumn({ index: 7 });

expect(getSummaryCellIndex(column, prevColumn, true)).toBe(7);
});

it('should return -1 when column.type is groupExpand and prevColumn is undefined', () => {
const column = makeColumn({ index: 5, type: 'groupExpand' });

expect(getSummaryCellIndex(column, undefined, true)).toBe(-1);
});

it('should return -1 when prevColumn.type is groupExpand and prevColumn.index is undefined', () => {
const column = makeColumn({ index: 5 });
const prevColumn = makeColumn({ type: 'groupExpand' });

expect(getSummaryCellIndex(column, prevColumn, true)).toBe(-1);
});
});

describe('groupIndex handling', () => {
it('should return column.index when groupIndex is not defined', () => {
const column = makeColumn({ index: 4 });

expect(getSummaryCellIndex(column, undefined, true)).toBe(4);
});

it('should return -1 when groupIndex is defined', () => {
const column = makeColumn({ index: 4, groupIndex: 0 });

expect(getSummaryCellIndex(column, undefined, true)).toBe(-1);
});

it('should return -1 when groupIndex is 0 (falsy but defined)', () => {
const column = makeColumn({ index: 8, groupIndex: 0 });

expect(getSummaryCellIndex(column, undefined, true)).toBe(-1);
});

it('should return column.index when groupIndex is undefined and prevColumn has no groupExpand type', () => {
const column = makeColumn({ index: 6 });
const prevColumn = makeColumn({ index: 3 });

expect(getSummaryCellIndex(column, prevColumn, true)).toBe(6);
});
});
});
});

describe('getColumnFromMap', () => {
const colA = makeColumn({ index: 0, dataField: 'fieldA' });
const colB = makeColumn({ index: 1, dataField: 'fieldB' });
const getColumnMap = (): Map<string | number, Column> => (
new Map<string | number, Column>([
[0, colA],
['fieldA', colA],
[1, colB],
['fieldB', colB],
])
);

it('should return column by numeric index', () => {
const columnMap = getColumnMap();

expect(getColumnFromMap(0, columnMap)).toBe(colA);
expect(getColumnFromMap(1, columnMap)).toBe(colB);
});

it('should return column by string dataField', () => {
const columnMap = getColumnMap();

expect(getColumnFromMap('fieldA', columnMap)).toBe(colA);
expect(getColumnFromMap('fieldB', columnMap)).toBe(colB);
});

it('should return undefined for undefined identifier', () => {
const columnMap = getColumnMap();

expect(getColumnFromMap(undefined, columnMap)).toBeUndefined();
});

it('should return undefined for identifier not in the map', () => {
const columnMap = getColumnMap();

expect(getColumnFromMap(999, columnMap)).toBeUndefined();
expect(getColumnFromMap('nonExistent', columnMap)).toBeUndefined();
});

it('should work with an empty map', () => {
const emptyMap = new Map<string | number, Column>();

expect(getColumnFromMap(0, emptyMap)).toBeUndefined();
expect(getColumnFromMap('fieldA', emptyMap)).toBeUndefined();
expect(getColumnFromMap(undefined, emptyMap)).toBeUndefined();
});
});
Loading
Loading