Skip to content
Open
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 @@ -5,7 +5,7 @@ import type { ProcessedItem } from '@ts/grids/grid_core/data_controller/types';
import type { RawItemData } from '@ts/grids/grid_core/data_source_adapter/types';

import {
isGroupNode, isGroupRow, isSameContinuationState, isSameExpandedState,
getGroupColumnIndices, isGroupNode, isGroupRow, isSameContinuationState, isSameExpandedState,
} from '../utils';

const groupRow = (partial: Partial<ProcessedItem> = {}): ProcessedItem => ({
Expand Down Expand Up @@ -136,3 +136,39 @@ describe('isSameContinuationState', () => {
)).toBe(true);
});
});

describe('getGroupColumnIndices', () => {
const expandedGroupRow = (partial: Partial<ProcessedItem> = {}): ProcessedItem => groupRow({
isExpanded: true,
data: { isContinuation: false, isContinuationOnNextPage: false },
...partial,
});

it('should skip the group expand cell', () => {
const oldItem = expandedGroupRow({
cells: [{ column: { type: 'groupExpand' } }, {}, { column: { dataField: 'name' } }],
});

expect(getGroupColumnIndices(oldItem, expandedGroupRow())).toEqual([1, 2]);
});

it('should return undefined when the old row has no cells', () => {
expect(getGroupColumnIndices(expandedGroupRow(), expandedGroupRow())).toBeUndefined();
});

it('should return undefined when the expanded state has changed', () => {
const oldItem = expandedGroupRow({ cells: [{}] });

expect(getGroupColumnIndices(oldItem, expandedGroupRow({ isExpanded: false })))
.toBeUndefined();
});

it('should return undefined when the continuation state has changed', () => {
const oldItem = expandedGroupRow({ cells: [{}] });
const newItem = expandedGroupRow({
data: { isContinuation: true, isContinuationOnNextPage: false },
});

expect(getGroupColumnIndices(oldItem, newItem)).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {
afterEach, beforeEach, describe, expect, it,
} from '@jest/globals';
import {
afterTest,
beforeTest,
createDataGrid,
} from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils';
import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller';
import type { ProcessedItem } from '@ts/grids/grid_core/data_controller/types';

declare class ExposedDataController extends DataController {
public _items: ProcessedItem[];

public adjustInsertRowIndex: (visibleRowIndex: number) => number;
}

const row = (rowType: ProcessedItem['rowType']): ProcessedItem => ({
rowType,
key: rowType,
data: {},
values: [],
});

const withVisibleRows = async (
rows: ProcessedItem[],
): Promise<(visibleRowIndex: number) => number> => {
const { instance } = await createDataGrid({
dataSource: [],
columns: ['name', 'age'],
});
const dataController = instance.getController('data') as unknown as ExposedDataController;

dataController._items = rows;

return (visibleRowIndex: number): number => dataController.adjustInsertRowIndex(visibleRowIndex);
};

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

it('should count the group rows along with the data rows', async () => {
const dataRowIndex = await withVisibleRows([
row('data'), row('group'), row('detail'), row('data'),
]);

expect(dataRowIndex(3)).toBe(2);
expect(dataRowIndex(4)).toBe(3);
});

it('should not count the group footer rows', async () => {
const dataRowIndex = await withVisibleRows([row('data'), row('groupFooter'), row('data')]);

expect(dataRowIndex(3)).toBe(2);
});

it('should not count the adaptive detail rows', async () => {
const dataRowIndex = await withVisibleRows([row('data'), row('detailAdaptive'), row('data')]);

expect(dataRowIndex(3)).toBe(2);
});

it('should count only the data rows when there are no group rows', async () => {
const dataRowIndex = await withVisibleRows([row('data'), row('detail'), row('data')]);

expect(dataRowIndex(3)).toBe(2);
});

it('should return zero for the first visible index', async () => {
const dataRowIndex = await withVisibleRows([row('group'), row('data')]);

expect(dataRowIndex(0)).toBe(0);
});

it('should count the rows that are there when the index is out of range', async () => {
const dataRowIndex = await withVisibleRows([row('group'), row('data')]);

expect(dataRowIndex(10)).toBe(2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,31 @@ interface GroupRowState {
isExpanded?: boolean;
isContinuation?: boolean;
isContinuationOnNextPage?: boolean;
values?: unknown[];
cells?: ProcessedItem['cells'];
}

const groupRow = ({
isExpanded = true,
isContinuation = false,
isContinuationOnNextPage = false,
values = [],
cells,
}: GroupRowState = {}): ProcessedItem => ({
rowType: 'group',
key: [1],
data: { isContinuation, isContinuationOnNextPage },
values: [],
values,
isExpanded,
cells,
});

const renderedCells: ProcessedItem['cells'] = [
{ column: { type: 'groupExpand' } },
{},
{ column: { dataField: 'name' } },
];

const dataRow = (partial: Partial<ProcessedItem> = {}): ProcessedItem => ({
rowType: 'data',
key: 1,
Expand All @@ -46,12 +57,13 @@ describe('Grouping data controller row changes', () => {

it('should report a group row when isExpanded changed', async () => {
const change = await refreshRow(
groupRow({ isExpanded: true }),
groupRow({ isExpanded: true, cells: renderedCells }),
groupRow({ isExpanded: false }),
);

expect(change.rowIndices).toEqual([0]);
expect(change.changeTypes).toEqual(['update']);
expect(change.columnIndices).toEqual([undefined]);
});

it('should report a group row when isContinuation changed', async () => {
Expand All @@ -68,6 +80,26 @@ describe('Grouping data controller row changes', () => {
expect(change.changeTypes).toEqual(['update']);
});

it('should diff every group cell but the expand one when values changed', async () => {
const change = await refreshRow(
groupRow({ values: ['Alex'], cells: renderedCells }),
groupRow({ values: ['Bob'] }),
);

expect(change.rowIndices).toEqual([0]);
expect(change.columnIndices).toEqual([[1, 2]]);
});

it('should repaint the whole group row when it was never rendered', async () => {
const change = await refreshRow(
groupRow({ values: ['Alex'] }),
groupRow({ values: ['Bob'] }),
);

expect(change.rowIndices).toEqual([0]);
expect(change.columnIndices).toEqual([undefined]);
});

it('should not report a data row when isExpanded changed (master detail row)', async () => {
const change = await refreshRow(
dataRow({ isExpanded: false }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Deferred, when } from '@js/core/utils/deferred';
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 { RawItemData } from '@ts/grids/grid_core/data_source_adapter/types';
import type {
ModuleType,
Expand All @@ -16,7 +17,7 @@ import type {
ChangeRowExpandArgs, GroupItem, ProcessGroupItemsOptions,
} from '../types';
import {
isGroupNode, isGroupRow, isSameContinuationState, isSameExpandedState,
getGroupColumnIndices, isGroupNode, isGroupRow, isSameContinuationState, isSameExpandedState,
} from '../utils';

export const groupingDataControllerExtender = (
Expand Down Expand Up @@ -134,6 +135,25 @@ export const groupingDataControllerExtender = (
return resultItems;
}

protected adjustInsertRowIndex(visibleRowIndex: number): number {
const groupRowCount = countRowsBefore(this.getVisibleRows(), visibleRowIndex, 'group');

return super.adjustInsertRowIndex(visibleRowIndex) + groupRowCount;
}

protected getChangedColumnIndices(
oldItem: ProcessedItem,
newItem: ProcessedItem,
visibleRowIndex: number,
isLiveUpdate?: boolean,
): number[] | undefined {
if (oldItem.rowType === 'group' && newItem.rowType === 'group') {
return getGroupColumnIndices(oldItem, newItem);
}

return super.getChangedColumnIndices(oldItem, newItem, visibleRowIndex, isLiveUpdate);
}

protected isSameRowState(item1: ProcessedItem, item2: ProcessedItem): boolean {
if (item1.rowType === 'group'
&& (!isSameExpandedState(item1, item2) || !isSameContinuationState(item1, item2))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@ export function isSameContinuationState(item1: ProcessedItem, item2: ProcessedIt
return item1.data?.isContinuation === item2.data?.isContinuation
&& item1.data?.isContinuationOnNextPage === item2.data?.isContinuationOnNextPage;
}

export function getGroupColumnIndices(
oldItem: ProcessedItem,
newItem: ProcessedItem,
): number[] | undefined {
const isSameState = isSameExpandedState(oldItem, newItem)
&& isSameContinuationState(oldItem, newItem);

if (!oldItem.cells || !isSameState) {
return undefined;
}

return oldItem.cells
.map((cell, index) => (cell.column?.type !== 'groupExpand' ? index : -1))
.filter((index) => index >= 0);
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('Summary data controller row changes', () => {

expect(change.rowIndices).toEqual([0]);
expect(change.changeTypes).toEqual(['update']);
expect(change.columnIndices).toEqual([undefined]);
});

it('should report a group footer when isContinuation changed', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,19 @@ export const summaryDataControllerExtender = (
return super.isSameRowState(item1, item2);
}

protected getChangedColumnIndices(
oldItem: ProcessedItem,
newItem: ProcessedItem,
visibleRowIndex: number,
isLiveUpdate?: boolean,
): number[] | undefined {
if (newItem.rowType === DATAGRID_GROUP_FOOTER_ROW_TYPE) {
return undefined;
}

return super.getChangedColumnIndices(oldItem, newItem, visibleRowIndex, isLiveUpdate);
}

protected _updateItemsCore(change: DataChange): void {
const dataSource = this._dataSource;
const summaryTotalItems = this.option('summary.totalItems');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { Properties as DataGridProperties } from '@js/ui/data_grid';
import { createDataGrid } from '@ts/grids/grid_core/__tests__/__mock__/helpers/utils';
import type { DataController } from '@ts/grids/grid_core/data_controller/data_controller';
import type {
Expand All @@ -10,26 +11,37 @@ declare class ExposedDataController extends DataController {
public applyChangesOnly: (change: DataChange) => void;
}

const createRefreshChange = (items: ProcessedItem[]): DataChange => ({
export interface RefreshRowOptions {
gridOptions?: DataGridProperties;
isLiveUpdate?: boolean;
}

const createRefreshChange = (
items: ProcessedItem[],
isLiveUpdate?: boolean,
): DataChange => ({
changeType: 'refresh',
repaintChangesOnly: true,
items,
isLiveUpdate,
});

export const refreshRow = async (
oldItem: ProcessedItem,
newItem: ProcessedItem,
{ gridOptions, isLiveUpdate }: RefreshRowOptions = {},
): Promise<UpdateChange> => {
const { instance } = await createDataGrid({
dataSource: [],
columns: ['name', 'age'],
repaintChangesOnly: true,
...gridOptions,
});
const dataController = instance.getController('data') as unknown as ExposedDataController;

dataController._items = [oldItem];

const change = createRefreshChange([newItem]);
const change = createRefreshChange([newItem], isLiveUpdate);

dataController.applyChangesOnly(change);

Expand Down
Loading
Loading