Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -996,8 +996,8 @@ const exportExtender = (
const columnsResizer = (
Base: ModuleType<ColumnsResizerViewController>,
) => class AdaptivityColumnsResizerExtender extends Base {
protected _pointCreated(point, cellsLength, columns) {
const result = super._pointCreated(point, cellsLength, columns);
protected _pointCreated(point, columns, cells?: dxElementWrapper) {
const result = super._pointCreated(point, columns, cells);
const currentColumn = columns[point.columnIndex] || {};
const nextColumnIndex = this._getNextColumnIndex(point.columnIndex);
const nextColumn = columns[nextColumnIndex] || {};
Expand All @@ -1022,8 +1022,12 @@ const columnsResizer = (
const draggingHeader = (
Base: ModuleType<DraggingHeaderViewController>,
) => class AdaptivityDraggingHeaderExtender extends Base {
protected _pointCreated(point, columns, location, sourceColumn) {
const result = super._pointCreated(point, columns, location, sourceColumn);
protected _pointCreated({
point, columns, location, sourceColumn, cells,
}) {
const result = super._pointCreated({
point, columns, location, sourceColumn, cells,
});
const column = columns[point.columnIndex - 1] || {};
const hasAdaptiveHiddenWidth = column.visibleWidth === HIDDEN_COLUMNS_WIDTH;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,6 @@ const baseFixedColumns = <T extends ModuleType<ColumnsView>>(Base: T) => class B
return cellElements;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getColumnWidths(fixedTableElement?: any, rowIndex?: number) {
const result = super.getColumnWidths(fixedTableElement, rowIndex);
const fixedColumns = this.getFixedColumns();
Expand Down Expand Up @@ -1077,8 +1076,12 @@ const draggingHeader = (Base: ModuleType<DraggingHeaderViewController>) => class
return super._generatePointsByColumns(options, needToCheckPrevPoint);
}

protected _pointCreated(point, columns, location, sourceColumn) {
const result = super._pointCreated.apply(this, arguments as any);
protected _pointCreated({
point, columns, location, sourceColumn, cells,
}) {
const result = super._pointCreated({
point, columns, location, sourceColumn, cells,
});
const targetColumn = columns[point.columnIndex];
// @ts-expect-error
const $transparentColumn = this._columnHeadersView.getTransparentColumnElement();
Expand Down Expand Up @@ -1122,7 +1125,7 @@ const columnsResizer = (Base: ModuleType<ColumnsResizerViewController>) => class
point.index += correctIndex;
}

return that._pointCreated(point, columns.length, columns);
return that._pointCreated(point, columns);
});
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import {
afterEach, beforeEach, describe, expect, it, jest,
} from '@jest/globals';
import {
end as dragEventEnd,
move as dragEventMove,
start as dragEventStart,
} from '@js/common/core/events/drag';
import type { dxElementWrapper } from '@js/core/renderer';
import $ from '@js/core/renderer';
import type { Properties as DataGridProperties } from '@js/ui/data_grid';
import DataGrid from '@js/ui/data_grid';
import errors from '@js/ui/widget/ui.errors';
import { DataGridModel } from '@ts/grids/data_grid/__tests__/__mock__/model/data_grid';

const SELECTORS = {
gridContainer: '#gridContainer',
};

const GRID_CONTAINER_ID = 'gridContainer';

const createDataGrid = async (
options: DataGridProperties = {},
): Promise<{
$container: dxElementWrapper;
component: DataGridModel;
instance: DataGrid;
}> => new Promise((resolve) => {
const $container = $('<div>')
.attr('id', GRID_CONTAINER_ID)
.appendTo(document.body);

const instance = new DataGrid($container.get(0) as HTMLDivElement, options);
const component = new DataGridModel($container.get(0) as HTMLElement);

jest.runAllTimers();

resolve({
$container,
component,
instance,
});
});

const beforeTest = (): void => {
jest.useFakeTimers();
jest.spyOn(errors, 'log').mockImplementation(jest.fn());
};

const afterTest = (): void => {
const $container = $(SELECTORS.gridContainer);
const dataGrid = ($container as any).dxDataGrid('instance') as DataGrid;

dataGrid.dispose();
$container.remove();
jest.clearAllMocks();
jest.useRealTimers();
};

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

const createGridWith200Columns = async (): Promise<{
$container: dxElementWrapper;
component: DataGridModel;
instance: DataGrid;
}> => {
const columns = [
{
dataField: 'id', caption: 'ID', width: '100px', fixed: true,
},
{
caption: 'Name',
columns: [
{ dataField: 'name.first', caption: 'First name', width: '150px' },
{ dataField: 'name.last', caption: 'Last name', width: '150px' },
],
},
...Array.from({ length: 198 }, (_, index) => ({
dataField: `values.${index}`,
caption: `Value ${index + 1}`,
width: '100px',
})),
];

const dataSource = [
{
id: 1,
name: { first: 'John', last: 'Doe' },
values: Array.from({ length: 198 }, (_, index) => index + 1),
},
];

return createDataGrid({
dataSource,
columns,
width: '100%',
showBorders: true,
showColumnLines: true,
allowColumnResizing: true,
allowColumnReordering: true,
});
};

describe('ColumnsResizerViewController', () => {
it('should call "_pointCreated" 202 times when generating points by columns (1 fixed + 1 group + 2 group children + 198 regular)', async () => {
const { instance } = await createGridWith200Columns();
const columnsResizerController = (instance as any).getController('columnsResizer');

const pointCreatedSpy = jest.spyOn(columnsResizerController, '_pointCreated');

columnsResizerController.pointsByColumns();

expect(pointCreatedSpy).toHaveBeenCalledTimes(202);
});

it('should call "getColumnElements" as many times as there are head rows', async () => {
const { instance } = await createGridWith200Columns();
const columnsResizerController = (instance as any).getController('columnsResizer');
const columnHeadersView = (instance as any).getView('columnHeadersView');

const columnHeadersViewSpy = jest.spyOn(columnHeadersView, 'getColumnElements');

columnsResizerController.pointsByColumns();

expect(columnHeadersViewSpy).toHaveBeenCalledTimes(2);
});
});

describe('DraggingHeaderViewController', () => {
const getDragEvent = (
eventName: string,
headerOffset: { left: number; top: number },
dragOffset: { left: number; top: number },
) => {
const dragEndEvent = document.createEvent('CustomEvent') as any;

dragEndEvent.initCustomEvent(eventName, true, true);
dragEndEvent.pageX = headerOffset.left + dragOffset.left;
dragEndEvent.pageY = headerOffset.top + dragOffset.top;
dragEndEvent.pointerType = 'mouse';

return dragEndEvent;
};

it('should call "getBoundingRect" once for each dragging panel view', async () => {
const { instance } = await createGridWith200Columns();
const columnHeadersView = (instance as any).getView('columnHeadersView');
const columnChooserView = (instance as any).getView('columnChooserView');
const headerPanelView = (instance as any).getView('headerPanel');

const getBoundingViewMocks = [
jest.spyOn(columnHeadersView, 'getBoundingRect'),
jest.spyOn(columnChooserView, 'getBoundingRect'),
jest.spyOn(headerPanelView, 'getBoundingRect'),
];

const $headerCell = $(columnHeadersView.element()).find('.dx-header-row td').eq(5);
const headerOffset = $headerCell.offset();

if (!headerOffset) {
throw new Error('Header cell not found');
}

const dragStartOffset = { left: 10, top: 10 };
const dragStartEvent = getDragEvent(dragEventStart, headerOffset, dragStartOffset);
$headerCell.get(0)?.dispatchEvent(dragStartEvent);

const dragMoveOffset = { left: 500, top: 10 };
const dragMoveEvent = getDragEvent(dragEventMove, headerOffset, dragMoveOffset);
$headerCell.get(0)?.dispatchEvent(dragMoveEvent);

const dragEndOffset = { left: 500, top: 10 };
const dragEndEvent = getDragEvent(dragEventEnd, headerOffset, dragEndOffset);
$headerCell.get(0)?.dispatchEvent(dragEndEvent);

getBoundingViewMocks.forEach((getBoundingViewMock) => {
expect(getBoundingViewMock).toHaveBeenCalledTimes(1);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
} from '@js/core/utils/size';
import { isDefined, isObject, isString } from '@js/core/utils/type';
import swatchContainer from '@ts/core/utils/swatch_container';
import { getDraggingPanelBoundingRects } from '@ts/grids/grid_core/columns_resizing_reordering/utils';
import type { EditorFactory } from '@ts/grids/grid_core/editor_factory/m_editor_factory';
import type { ColumnPoint, ModuleType } from '@ts/grids/grid_core/m_types';
import type { RowsView } from '@ts/grids/grid_core/views/m_rows_view';
Expand Down Expand Up @@ -467,14 +468,15 @@ export class DraggingHeaderView extends modules.View {
const that = this;
let result;

each(that._dragOptions.draggingPanels, (index, draggingPanel) => {
if (draggingPanel) {
const boundingRect = draggingPanel.getBoundingRect();
if (boundingRect && (boundingRect.bottom === undefined || pos.y < boundingRect.bottom) && (boundingRect.top === undefined || pos.y > boundingRect.top)
&& (boundingRect.left === undefined || pos.x > boundingRect.left) && (boundingRect.right === undefined || pos.x < boundingRect.right)) {
result = draggingPanel;
return false;
}
each(that._dragOptions.draggingPanelBoundingRects, (_, { draggingPanel, boundingRect }) => {
if (boundingRect
&& (boundingRect.bottom === undefined || pos.y < boundingRect.bottom)
&& (boundingRect.top === undefined || pos.y > boundingRect.top)
&& (boundingRect.left === undefined || pos.x > boundingRect.left)
&& (boundingRect.right === undefined || pos.x < boundingRect.right)
) {
result = draggingPanel;
return false;
}

return undefined;
Expand Down Expand Up @@ -525,9 +527,13 @@ export class DraggingHeaderView extends modules.View {

public dragHeader(options) {
const { columnElement } = options;
const dragOptions = {
...options,
draggingPanelBoundingRects: getDraggingPanelBoundingRects(options.draggingPanels),
};

this._isDragging = true;
this._dragOptions = options;
this._dragOptions = dragOptions;
this._dropOptions = {
sourceIndex: options.index,
sourceColumnIndex: this._getVisibleIndexObject(options.rowIndex, options.columnIndex),
Expand Down Expand Up @@ -748,11 +754,12 @@ export class ColumnsResizerViewController extends modules.ViewController {
/**
* @extended: adaptivity
*/
protected _pointCreated(point, cellsLength, columns) {
protected _pointCreated(point, columns, cells?: dxElementWrapper) {
const isNextColumnMode = isNextColumnResizingMode(this);
const rtlEnabled = this.option('rtlEnabled');
const isRtlParentStyle = this._isRtlParentStyle();
const firstPointColumnIndex = !isNextColumnMode && rtlEnabled && !isRtlParentStyle ? 0 : 1;
const cellsLength = cells?.length ?? columns.length;

if (point.index >= firstPointColumnIndex && point.index < cellsLength + (!isNextColumnMode && (!rtlEnabled || isRtlParentStyle) ? 1 : 0)) {
this._correctColumnIndexForPoint(point, firstPointColumnIndex, columns);
Expand Down Expand Up @@ -989,7 +996,7 @@ export class ColumnsResizerViewController extends modules.ViewController {
if (cells && cells.length > 0) {
that._pointsByColumns = gridCoreUtils.getPointsByColumns(
cells,
(point) => that._pointCreated(correctColumnY(point), cells.length, columns),
(point) => that._pointCreated(correctColumnY(point), columns, cells),
false,
0,
needToCheckPrevPoint,
Expand Down Expand Up @@ -1385,10 +1392,18 @@ export class DraggingHeaderViewController extends modules.ViewController {
* @extended: column_fixing
*/
public _generatePointsByColumns(options, needToCheckPrevPoint = false) {
const cells = this._columnHeadersView.getColumnElements();
this.isCustomGroupColumnPosition = this.checkIsCustomGroupColumnPosition(options);

const points = gridCoreUtils.getPointsByColumns(
options.columnElements,
(point) => this._pointCreated(point, options.columns, options.targetDraggingPanel.getName(), options.sourceColumn),
(point) => this._pointCreated({
point,
columns: options.columns,
location: options.targetDraggingPanel.getName(),
sourceColumn: options.sourceColumn,
cells,
}),
options.isVerticalOrientation,
options.startColumnIndex,
needToCheckPrevPoint,
Expand Down Expand Up @@ -1416,14 +1431,24 @@ export class DraggingHeaderViewController extends modules.ViewController {

/**
* @extended: adaptivity, column_fixing
* Function that is used to filter column points, it's called for each point
* @description Function used to filter column points, it's called for each point
* @param point Point that we are checking
* @param columns All columns in the given location
* @param location Location where we move column (headers, group, column chooser etc)
* @param location Location where we move column (headers, group, column chooser, etc.)
* @param sourceColumn Column that is dragging
* @param cells JQuery-wrapped collection of header cell elements
* @returns whether to filter current point (true - remove point, false - keep it)
*/
protected _pointCreated(point, columns, location, sourceColumn): boolean {

protected _pointCreated({
point, columns, location, sourceColumn,
}: {
point: ColumnPoint;
columns: any[];
location?: string;
sourceColumn?: any;
cells?: dxElementWrapper;
}): boolean {
const targetColumn = columns[point.columnIndex];
const prevColumn = columns[point.columnIndex - 1];

Expand Down
Loading
Loading