diff --git a/packages/devextreme/js/__internal/grids/grid_core/columns_controller/__tests__/columns_controller.integration.test.ts b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/__tests__/columns_controller.integration.test.ts
new file mode 100644
index 000000000000..49253d562d81
--- /dev/null
+++ b/packages/devextreme/js/__internal/grids/grid_core/columns_controller/__tests__/columns_controller.integration.test.ts
@@ -0,0 +1,148 @@
+import {
+ afterEach, beforeEach, describe, expect, it, jest,
+} from '@jest/globals';
+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 '../../../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 = $('
')
+ .attr('id', GRID_CONTAINER_ID)
+ .appendTo(document.body);
+
+ const dataGridOptions: DataGridProperties = {
+ keyExpr: 'id',
+ ...options,
+ };
+
+ const instance = new DataGrid($container.get(0) as HTMLDivElement, dataGridOptions);
+ 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());
+ jest.spyOn(errors, 'Error').mockImplementation(() => ({}));
+};
+
+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('Bugs', () => {
+ beforeEach(beforeTest);
+ afterEach(afterTest);
+
+ describe('T1319739 - DataGrid - Columns are misaligned after adding a column at runtime', () => {
+ const data = [
+ {
+ id: 1,
+ field_1: 'Value 1',
+ field_2: 'Value 2',
+ },
+ ];
+
+ it('should add column with data cell if repaintChangesOnly=true', async () => {
+ const { instance, component } = await createDataGrid({
+ dataSource: data,
+ repaintChangesOnly: true,
+ columns: [
+ {
+ dataField: 'field_1',
+ },
+ ],
+ });
+
+ let visibleColumns = instance.getVisibleColumns();
+ let headerCellsArray = Array.from(component.getHeaderCells());
+ let dataCellsArray = Array.from(component.getDataCells(0));
+
+ expect(visibleColumns.length).toBe(1);
+ expect(headerCellsArray.length).toBe(1);
+ expect(dataCellsArray.length).toBe(1);
+
+ instance.addColumn({
+ dataField: 'field_2',
+ });
+
+ jest.runAllTimers();
+
+ visibleColumns = instance.getVisibleColumns();
+ headerCellsArray = Array.from(component.getHeaderCells());
+ dataCellsArray = Array.from(component.getDataCells(0));
+
+ expect(visibleColumns.length).toBe(2);
+ expect(visibleColumns[0].dataField).toBe('field_1');
+ expect(visibleColumns[1].dataField).toBe('field_2');
+
+ expect(headerCellsArray.length).toBe(2);
+ expect(dataCellsArray.length).toBe(2);
+ });
+
+ it('should remove column with data cell if repaintChangesOnly=true', async () => {
+ const { instance, component } = await createDataGrid({
+ dataSource: data,
+ repaintChangesOnly: true,
+ columns: [
+ {
+ dataField: 'field_1',
+ },
+ {
+ dataField: 'field_2',
+ },
+ ],
+ });
+
+ let visibleColumns = instance.getVisibleColumns();
+ let headerCellsArray = Array.from(component.getHeaderCells());
+ let dataCellsArray = Array.from(component.getDataCells(0));
+
+ expect(visibleColumns.length).toBe(2);
+ expect(headerCellsArray.length).toBe(2);
+ expect(dataCellsArray.length).toBe(2);
+
+ instance.deleteColumn('field_2');
+ jest.runAllTimers();
+
+ visibleColumns = instance.getVisibleColumns();
+ headerCellsArray = Array.from(component.getHeaderCells());
+ dataCellsArray = Array.from(component.getDataCells(0));
+
+ expect(visibleColumns.length).toBe(1);
+ expect(visibleColumns[0].dataField).toBe('field_1');
+
+ expect(headerCellsArray.length).toBe(1);
+ expect(dataCellsArray.length).toBe(1);
+ });
+ });
+});
diff --git a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts
index 9a5331b2b4fa..66fb8880e39b 100644
--- a/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts
+++ b/packages/devextreme/js/__internal/grids/grid_core/data_controller/m_data_controller.ts
@@ -488,6 +488,20 @@ export class DataController extends DataHelperMixin(modules.Controller) {
e.group = columnsController.getGroupDataSourceParameters(!dataSource.remoteOperations().grouping);
}
+ private needFullRepaint(optionNames, changeTypes) {
+ if (optionNames.visible) {
+ return true;
+ }
+
+ const columnCountChanged = changeTypes.columns && optionNames.all;
+ if (columnCountChanged) {
+ return true;
+ }
+
+ const hasShowEditorAlways = this._columnsController.getVisibleColumns().some((col) => col.showEditorAlways);
+ return hasShowEditorAlways;
+ }
+
private _handleColumnsChanged(e) {
const that = this;
const { changeTypes } = e;
@@ -499,7 +513,8 @@ export class DataController extends DataHelperMixin(modules.Controller) {
// B255430
const updateItemsHandler = function (change) {
that._columnsController.columnsChanged.remove(updateItemsHandler);
- const needsFullRepaint = optionNames.visible || that._columnsController.getVisibleColumns().some((col) => col.showEditorAlways);
+ const needsFullRepaint = that.needFullRepaint(optionNames, changeTypes);
+
that.updateItems({
repaintChangesOnly: needsFullRepaint ? false : that.option('repaintChangesOnly'),
event: change?.changeTypes?.event,