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 @@ -59,6 +59,68 @@ test('DataGrid – Resize indicator is moved when resizing a grouped column if s
});
});

// T1329677
test('DataGrid - column width changed via columnOption should be applied immediately, without a repaint (T1329677)', async (t) => {
const dataGrid = new DataGrid('#container');

await t.expect(dataGrid.isReady()).ok();

await dataGrid.apiColumnOption('Task_Assigned_Employee_ID', 'width', 700);

const assignedColumnWidth = await dataGrid.getHeaders().getHeaderRow(0)
.getHeaderCell(1).element.clientWidth;

await t
.expect(assignedColumnWidth)
.within(
700 - 1,
700 + 1,
'columnOption width should be applied immediately, without an explicit repaint',
);
}).before(async () => {
await createWidget('dxDataGrid', {
dataSource: [{ Task_Subject: 'Test' }],
columnAutoWidth: true,
columns: [
{ dataField: 'Task_Subject' },
{ dataField: 'Task_Assigned_Employee_ID', caption: 'Assigned' },
],
});
});

// T1329677
test('DataGrid - other column width should be updated immediately when another column width is changed via columnOption (T1329677)', async (t) => {
const dataGrid = new DataGrid('#container');

await t.expect(dataGrid.isReady()).ok();

const firstColumnOldWidth = await dataGrid.getDataCell(0, 0).element.clientWidth;

await dataGrid.apiColumnOption('Col2', 'width', 200);

const firstColumnNewWidth = await dataGrid.getDataCell(0, 0).element.clientWidth;

await t
.expect(firstColumnOldWidth).notEql(firstColumnNewWidth, 'first column width should be changed');
}).before(async () => {
await createWidget('dxDataGrid', {
dataSource: [{
Col1: 'Test 1',
Col2: 'Test 2',
Col3: 'Test 3',
Col4: 'Test 4',
}],
width: 400,
columnAutoWidth: true,
columns: [
{ dataField: 'Col1' },
{ dataField: 'Col2' },
{ dataField: 'Col3' },
{ dataField: 'Col4' },
],
});
});

const tryResizeHeaderInBandArea = (
dataGrid: DataGrid,
columnIndex: number,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
beforeTest,
createDataGrid,
} from '../../__tests__/__mock__/helpers/utils';
import { HIDDEN_COLUMNS_WIDTH } from '../../adaptivity/const';

describe('getFilteringColumns', () => {
beforeEach(beforeTest);
Expand Down Expand Up @@ -138,4 +139,153 @@ describe('Bugs', () => {
expect(dataCellsArray.length).toBe(1);
});
});

describe('T1329677 - DataGrid - Column width changes are not applied immediately', () => {
it('should invalidate calculated widths when a column width changes through columnOption', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1', field2: 'value 2', field3: 'value 3' }],
columns: ['field1', 'field2', 'field3'],
});
const columnsController = instance.getController('columns');

columnsController.columnOption(0, 'visibleWidth', 100);
columnsController.columnOption(1, 'visibleWidth', 110);
columnsController.columnOption(2, 'visibleWidth', 120);

instance.columnOption(1, 'width', 150);

expect(columnsController.getColumns().map((column) => column.visibleWidth)).toEqual([
null, null, null,
]);
});

it('should preserve an adaptive-hidden marker when a column width changes through columnOption', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1' }],
columns: ['field1'],
});
const columnsController = instance.getController('columns');

columnsController.columnOption(0, 'visibleWidth', HIDDEN_COLUMNS_WIDTH);

instance.columnOption(0, 'width', 150);

expect(columnsController.columnOption(0, 'visibleWidth')).toBe(HIDDEN_COLUMNS_WIDTH);
});

it('should invalidate an auto visible width when a column width changes through columnOption', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1' }],
columns: ['field1'],
});
const columnsController = instance.getController('columns');

columnsController.columnOption(0, 'visibleWidth', 'auto');

instance.columnOption(0, 'width', 150);

expect(columnsController.columnOption(0, 'visibleWidth')).toBeNull();
});

it('should invalidate calculated widths of command columns when another column width changes through columnOption', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1' }],
columns: ['field1'],
});
const columnsController = instance.getController('columns');

columnsController.addCommandColumn({ command: 'test', width: 'auto' });
columnsController.columnOption('command:test', 'visibleWidth', 100);

instance.columnOption('field1', 'width', 150);

expect(columnsController.columnOption('command:test', 'visibleWidth')).toBeNull();
});

it('should preserve calculated widths of unrelated columns when applying resolved dimensions', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1', field2: 'value 2', field3: 'value 3' }],
columns: ['field1', 'field2', 'field3'],
});
const columnsController = instance.getController('columns');

columnsController.columnOption(0, 'visibleWidth', 100);
columnsController.columnOption(1, 'visibleWidth', 110);
columnsController.columnOption(2, 'visibleWidth', 120);

columnsController.updateColumnDimensions([{
columnIndex: 1,
visibleWidth: null,
width: 150,
}]);

expect(columnsController.columnOption(1, 'width')).toBe(150);
expect(columnsController.getColumns().map((column) => column.visibleWidth)).toEqual([
100, null, 120,
]);
});

it('should invalidate a stale visible width when another option changed the same column in the batch', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1', field2: 'value 2', field3: 'value 3' }],
columns: ['field1', 'field2', 'field3'],
});
const columnsController = instance.getController('columns');

columnsController.columnOption(0, 'visibleWidth', 100);
columnsController.columnOption(1, 'visibleWidth', 110);
columnsController.columnOption(2, 'visibleWidth', 120);

columnsController.beginUpdate();
columnsController.columnOption(1, 'caption', 'Updated field 2');
columnsController.columnOption(0, 'visibleWidth', 105);
columnsController.columnOption(1, 'width', 150);
columnsController.endUpdate();

expect(columnsController.getColumns().map((column) => column.visibleWidth)).toEqual([
105, null, null,
]);
});

it('should preserve visible widths that are pending for their respective columns', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1', field2: 'value 2', field3: 'value 3' }],
columns: ['field1', 'field2', 'field3'],
});
const columnsController = instance.getController('columns');

columnsController.columnOption(0, 'visibleWidth', 100);
columnsController.columnOption(1, 'visibleWidth', 110);
columnsController.columnOption(2, 'visibleWidth', 120);

columnsController.beginUpdate();
columnsController.columnOption(0, 'visibleWidth', 105);
columnsController.columnOption(1, 'visibleWidth', 115);
columnsController.columnOption(1, 'width', 150);
columnsController.endUpdate();

expect(columnsController.getColumns().map((column) => column.visibleWidth)).toEqual([
105, 115, null,
]);
});

it('should clear pending visible widths after the update batch completes', async () => {
const { instance } = await createDataGrid({
dataSource: [{ field1: 'value 1', field2: 'value 2', field3: 'value 3' }],
columns: ['field1', 'field2', 'field3'],
});
const columnsController = instance.getController('columns');

columnsController.columnOption(0, 'visibleWidth', 100);

columnsController.beginUpdate();
columnsController.columnOption(0, 'visibleWidth', 105);
columnsController.columnOption(0, 'width', 150);
columnsController.endUpdate();

columnsController.columnOption(0, 'width', 160);

expect(columnsController.columnOption(0, 'visibleWidth')).toBeNull();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ interface IndexedColumns {
negativeIndexedColumns: Record<string, Column[]>[];
}

export interface ColumnDimensionsUpdate {
columnIndex: Column['index'];
visibleWidth?: Column['visibleWidth'] | null;
width: Column['width'];
}

export class ColumnsController extends modules.Controller {
public _skipProcessingColumnsChange: any;

Expand Down Expand Up @@ -133,6 +139,8 @@ export class ColumnsController extends modules.Controller {

public _columnChanges?: ColumnsChanges;

public _pendingVisibleWidthColumnIndices?: Set<number>;

protected _dataController!: DataController;

protected _focusController!: FocusController;
Expand Down Expand Up @@ -1488,6 +1496,41 @@ export class ColumnsController extends modules.Controller {
return this._columns ? this._columns.length : 0;
}

/** Applies dimensions already resolved by an internal layout operation. */
public updateColumnDimensions(updates: ColumnDimensionsUpdate[]): void {
if (!updates.length) {
return;
}

const columnsByIndex = new Map<Column['index'], Column>();

this._columns.concat(this._commandColumns).forEach((column: Column) => {
if (!columnsByIndex.has(column.index)) {
columnsByIndex.set(column.index, column);
}
});

this.beginUpdate();
try {
updates.forEach((dimensions) => {
const column = columnsByIndex.get(dimensions.columnIndex);

if (!column) {
return;
}

if (Object.prototype.hasOwnProperty.call(dimensions, 'visibleWidth')) {
columnOptionCore(this, column, 'visibleWidth', dimensions.visibleWidth);
}
columnOptionCore(this, column, 'width', dimensions.width, {
invalidateVisibleWidths: false,
});
});
} finally {
this.endUpdate();
}
}

public columnOption(identifier, option?, value?, notFireEvent?) {
const that = this;
const columns = that._columns.concat(that._commandColumns);
Expand All @@ -1501,10 +1544,10 @@ export class ColumnsController extends modules.Controller {
if (arguments.length === 2) {
return columnOptionCore(that, column, option);
}
columnOptionCore(that, column, option, value, notFireEvent);
columnOptionCore(that, column, option, value, { notFireEvent });
} else if (isObject(option)) {
each(option, (optionName, optionValue) => {
columnOptionCore(that, column, optionName, optionValue, notFireEvent);
columnOptionCore(that, column, optionName, optionValue, { notFireEvent });
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,10 @@ export const updateColumnChanges = (
};

export const fireColumnsChanged = function (that: ColumnsController) {
if (!that._updateLockCount) {
that._pendingVisibleWidthColumnIndices = undefined;
}

const onColumnsChanging: any = that.option('onColumnsChanging');
const columnChanges = that._columnChanges;
const reinitOptionNames = ['dataField', 'lookup', 'dataType', 'columns'];
Expand Down Expand Up @@ -715,9 +719,54 @@ export const fireOptionChanged = function (that: ColumnsController, options) {
}
};

export const columnOptionCore = function (that: ColumnsController, column, optionName, value?, notFireEvent?) {
const trackPendingVisibleWidthChange = (that: ColumnsController, columnIndex): void => {
if (isDefined(columnIndex)) {
that._pendingVisibleWidthColumnIndices ??= new Set();
that._pendingVisibleWidthColumnIndices.add(columnIndex);
}
};

const isVisibleWidthChangePendingForColumn = (that: ColumnsController, columnIndex): boolean => !!that._pendingVisibleWidthColumnIndices?.has(columnIndex);

const invalidateStaleVisibleWidths = (that: ColumnsController, changedColumn): void => {
const hasCalculatedVisibleWidth = isNumeric(changedColumn.visibleWidth)
|| changedColumn.visibleWidth === 'auto';
const shouldInvalidateChangedColumnVisibleWidth = hasCalculatedVisibleWidth
&& !isVisibleWidthChangePendingForColumn(that, changedColumn.index);

if (shouldInvalidateChangedColumnVisibleWidth) {
changedColumn.visibleWidth = null;
}

that._columns.concat(that._commandColumns).forEach((column) => {
const hasCalculatedVisibleWidth = isNumeric(column.visibleWidth)
&& (!isDefined(column.width) || column.width === 'auto');
const shouldInvalidateVisibleWidth = column !== changedColumn
&& hasCalculatedVisibleWidth
&& !isVisibleWidthChangePendingForColumn(that, column.index);

if (shouldInvalidateVisibleWidth) {
column.visibleWidth = null;
}
});
};

interface ColumnOptionCoreOptions {
invalidateVisibleWidths?: boolean;
notFireEvent?: boolean;
}

export const columnOptionCore = function (
that: ColumnsController,
column,
optionName,
value?,
options: ColumnOptionCoreOptions = {},
) {
const optionGetter = compileGetter(optionName);
const columnIndex = column.index;
const { invalidateVisibleWidths = true } = options;
let { notFireEvent } = options;
let columns;
let changeType;
let initialColumn;
Expand All @@ -740,6 +789,12 @@ export const columnOptionCore = function (that: ColumnsController, column, optio
changeType = 'columns';
}

if (optionName === 'visibleWidth') {
trackPendingVisibleWidthChange(that, columnIndex);
} else if (optionName === 'width' && invalidateVisibleWidths) {
invalidateStaleVisibleWidths(that, column);
}

const optionSetter = compileSetter(optionName);
// @ts-expect-error
optionSetter(column, value, { functionsAsIs: true });
Expand Down
Loading
Loading