Skip to content

Commit 77ad730

Browse files
author
Alyar
committed
DataGrid: Fix the column separator position when resizing a column next to a fixed one (T1335911)
1 parent 56687c2 commit 77ad730

7 files changed

Lines changed: 313 additions & 7 deletions

File tree

‎e2e/testcafe-devextreme/tests/dataGrid/sticky/common/stickyColumnResizing.ts‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,63 @@ fixture.disablePageReloads`Resize columns - nextColumn mode`
9393
}));
9494
});
9595

96+
test.meta({ browserSize: [900, 800] })('The separator should have correct position when resizing fixed column on the right (rtlEnabled = false) (T1335911)', async (t) => {
97+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
98+
const separator = dataGrid.getColumnsSeparator().element;
99+
const fixedHeaderCell = dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(23).element;
100+
101+
await t.expect(dataGrid.isReady()).ok();
102+
103+
await dataGrid.resizeHeader(23, -100, false);
104+
105+
const fixedCellLeft = await fixedHeaderCell.getBoundingClientRectProperty('left');
106+
107+
await t
108+
.expect(separator.getBoundingClientRectProperty('left'))
109+
.within(fixedCellLeft - 1, fixedCellLeft + 1);
110+
}).before(async () => createWidget('dxDataGrid', {
111+
dataSource: getData(5, 25),
112+
columnAutoWidth: true,
113+
allowColumnResizing: true,
114+
columnWidth: 200,
115+
columnResizingMode: 'nextColumn',
116+
customizeColumns: (columns) => {
117+
columns[5].fixed = true;
118+
columns[5].fixedPosition = 'right';
119+
columns[6].fixed = true;
120+
columns[6].fixedPosition = 'right';
121+
},
122+
}));
123+
124+
test.meta({ browserSize: [900, 800] })('The separator should have correct position when resizing fixed column on the left (rtlEnabled = true) (T1335911)', async (t) => {
125+
const dataGrid = new DataGrid(DATA_GRID_SELECTOR);
126+
const separator = dataGrid.getColumnsSeparator().element;
127+
const fixedHeaderCell = dataGrid.getHeaders().getHeaderRow(0).getHeaderCell(23).element;
128+
129+
await t.expect(dataGrid.isReady()).ok();
130+
131+
await dataGrid.resizeHeader(23, -100, false, 'right');
132+
133+
const fixedCellRight = await fixedHeaderCell.getBoundingClientRectProperty('right');
134+
135+
await t
136+
.expect(separator.getBoundingClientRectProperty('left'))
137+
.within(fixedCellRight - 1, fixedCellRight + 1);
138+
}).before(async () => createWidget('dxDataGrid', {
139+
dataSource: getData(5, 25),
140+
rtlEnabled: true,
141+
columnAutoWidth: true,
142+
allowColumnResizing: true,
143+
columnWidth: 200,
144+
columnResizingMode: 'nextColumn',
145+
customizeColumns: (columns) => {
146+
columns[5].fixed = true;
147+
columns[5].fixedPosition = 'left';
148+
columns[6].fixed = true;
149+
columns[6].fixedPosition = 'left';
150+
},
151+
}));
152+
96153
fixture.disablePageReloads`Resize columns - widget mode`
97154
.page(url(__dirname, '../../../container.html'));
98155

‎packages/devextreme/js/__internal/grids/grid_core/columns_resizing_reordering/m_columns_resizing_reordering.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,8 @@ export class ColumnsResizerViewController extends modules.ViewController {
805805
return null;
806806
}
807807

808-
protected getSeparatorOffsetX($cell: dxElementWrapper): number {
808+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
809+
protected getSeparatorOffsetX($cell: dxElementWrapper, $nextCell: dxElementWrapper): number {
809810
const isNextColumnMode = isNextColumnResizingMode(this);
810811
const rtlEnabled = this.option('rtlEnabled');
811812
const isRtlParentStyle = this._isRtlParentStyle();
@@ -830,10 +831,12 @@ export class ColumnsResizerViewController extends modules.ViewController {
830831
if (that._isResizing && that._resizingInfo) {
831832
if ((parentOffsetLeft <= eventData.x || !isNextColumnMode && isRtlParentStyle) && (!isNextColumnMode || eventData.x <= parentOffsetLeft + getWidth(that._$parentContainer))) {
832833
if (that._updateColumnsWidthIfNeeded(eventData.x)) {
833-
const $cell = that._columnHeadersView.getColumnElements().eq(that._resizingInfo.currentColumnIndex);
834+
const $cells = that._columnHeadersView.getColumnElements();
835+
const $cell = $cells.eq(that._resizingInfo.currentColumnIndex);
836+
const $nextCell = $cells.eq(that._resizingInfo.nextColumnIndex);
834837

835838
if ($cell.length) {
836-
const offsetX = this.getSeparatorOffsetX($cell);
839+
const offsetX = this.getSeparatorOffsetX($cell, $nextCell);
837840

838841
that._columnsSeparatorView.moveByX(offsetX);
839842
that._tablePositionController.update(that._targetPoint.y);
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import {
2+
afterEach,
3+
beforeEach,
4+
describe,
5+
expect,
6+
it,
7+
} from '@jest/globals';
8+
import type { dxElementWrapper } from '@js/core/renderer';
9+
import $ from '@js/core/renderer';
10+
import type { Properties as DataGridProperties } from '@js/ui/data_grid';
11+
12+
import {
13+
afterTest,
14+
beforeTest,
15+
createDataGrid,
16+
} from '../../__tests__/__mock__/helpers/utils';
17+
18+
const CURRENT_CELL_RECT = { left: 100, width: 300 };
19+
const NEXT_CELL_RECT = { left: 250, width: 150 };
20+
21+
const mockCellRect = (
22+
$cell: dxElementWrapper,
23+
{ left, width }: { left: number; width: number },
24+
): dxElementWrapper => {
25+
const element = $cell.get(0) as HTMLElement;
26+
const rect = {
27+
left, right: left + width, width, top: 0, bottom: 0, height: 0,
28+
} as DOMRect;
29+
30+
element.getBoundingClientRect = (): DOMRect => rect;
31+
element.getClientRects = (): DOMRectList => [rect] as unknown as DOMRectList;
32+
33+
return $cell;
34+
};
35+
36+
const getSeparatorOffsetX = async (options: DataGridProperties): Promise<number> => {
37+
const { instance } = await createDataGrid({
38+
dataSource: [{
39+
id: 1, a: 'a', b: 'b', c: 'c',
40+
}],
41+
allowColumnResizing: true,
42+
...options,
43+
});
44+
const columnsResizer = instance.getController('columnsResizer');
45+
const $cells = $(instance.getView('columnHeadersView').getColumnElements());
46+
const $cell = mockCellRect($cells.eq(1), CURRENT_CELL_RECT);
47+
const $nextCell = mockCellRect($cells.eq(2), NEXT_CELL_RECT);
48+
49+
// @ts-expect-error getSeparatorOffsetX is protected
50+
return columnsResizer.getSeparatorOffsetX($cell, $nextCell);
51+
};
52+
53+
describe('ColumnsResizer separator position with sticky columns (T1335911)', () => {
54+
beforeEach(beforeTest);
55+
afterEach(afterTest);
56+
57+
describe('when the next cell is fixed to the right', () => {
58+
it('should return the left edge of the next cell', async () => {
59+
const offsetX = await getSeparatorOffsetX({
60+
columnResizingMode: 'nextColumn',
61+
columns: ['a', 'b', { dataField: 'c', fixed: true, fixedPosition: 'right' }],
62+
});
63+
64+
expect(offsetX).toBe(NEXT_CELL_RECT.left);
65+
});
66+
});
67+
68+
describe('when the next cell is fixed to the left in RTL', () => {
69+
it('should return the right edge of the next cell', async () => {
70+
const offsetX = await getSeparatorOffsetX({
71+
columnResizingMode: 'nextColumn',
72+
rtlEnabled: true,
73+
columns: ['a', 'b', { dataField: 'c', fixed: true, fixedPosition: 'left' }],
74+
});
75+
76+
expect(offsetX).toBe(NEXT_CELL_RECT.left + NEXT_CELL_RECT.width);
77+
});
78+
});
79+
80+
describe('when the next cell is not fixed', () => {
81+
it('should return the right edge of the current cell', async () => {
82+
const offsetX = await getSeparatorOffsetX({
83+
columnResizingMode: 'nextColumn',
84+
columns: [{ dataField: 'a', fixed: true }, 'b', 'c'],
85+
});
86+
87+
expect(offsetX).toBe(CURRENT_CELL_RECT.left + CURRENT_CELL_RECT.width);
88+
});
89+
});
90+
91+
describe('when the resizing mode is widget', () => {
92+
it('should not take the next fixed cell into account', async () => {
93+
const offsetX = await getSeparatorOffsetX({
94+
columnResizingMode: 'widget',
95+
columns: ['a', 'b', { dataField: 'c', fixed: true, fixedPosition: 'right' }],
96+
});
97+
98+
expect(offsetX).toBe(CURRENT_CELL_RECT.left + CURRENT_CELL_RECT.width);
99+
});
100+
});
101+
});
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import {
2+
describe,
3+
expect,
4+
it,
5+
} from '@jest/globals';
6+
import type { dxElementWrapper } from '@js/core/renderer';
7+
import $ from '@js/core/renderer';
8+
9+
import { GridCoreStickyColumnsDom } from '../dom';
10+
11+
const addWidgetPrefix = (className: string): string => `dx-datagrid-${className}`;
12+
13+
const createElement = (
14+
rect: { left: number; right: number },
15+
className = '',
16+
style: { left?: string; right?: string } = {},
17+
): dxElementWrapper => {
18+
const element = document.createElement('td');
19+
20+
element.className = className;
21+
Object.assign(element.style, style);
22+
element.getBoundingClientRect = (): DOMRect => ({
23+
...rect,
24+
width: rect.right - rect.left,
25+
}) as DOMRect;
26+
27+
return $(element);
28+
};
29+
30+
const $container = createElement({ left: 0, right: 600 });
31+
32+
describe('GridCoreStickyColumnsDom.isFixedCellPinnedToLeft', () => {
33+
describe('when the cell is fixed to the left', () => {
34+
it('should return true', () => {
35+
const $cell = createElement({ left: 0, right: 100 }, 'dx-datagrid-sticky-column-left');
36+
37+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToLeft($cell, $container, addWidgetPrefix))
38+
.toBe(true);
39+
});
40+
});
41+
42+
describe('when the sticky cell is pinned to the left', () => {
43+
it('should return true', () => {
44+
const $cell = createElement({ left: 0, right: 100 }, 'dx-datagrid-sticky-column', { left: '0px' });
45+
46+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToLeft($cell, $container, addWidgetPrefix))
47+
.toBe(true);
48+
});
49+
});
50+
51+
describe('when the sticky cell is not pinned', () => {
52+
it('should return false', () => {
53+
const $cell = createElement({ left: 200, right: 300 }, 'dx-datagrid-sticky-column', { left: '0px' });
54+
55+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToLeft($cell, $container, addWidgetPrefix))
56+
.toBe(false);
57+
});
58+
});
59+
60+
describe('when the cell is fixed to the right', () => {
61+
it('should return false', () => {
62+
const $cell = createElement({ left: 500, right: 600 }, 'dx-datagrid-sticky-column-right');
63+
64+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToLeft($cell, $container, addWidgetPrefix))
65+
.toBe(false);
66+
});
67+
});
68+
69+
describe('when the cell is not fixed', () => {
70+
it('should return false', () => {
71+
const $cell = createElement({ left: 0, right: 100 });
72+
73+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToLeft($cell, $container, addWidgetPrefix))
74+
.toBe(false);
75+
});
76+
});
77+
});
78+
79+
describe('GridCoreStickyColumnsDom.isFixedCellPinnedToRight', () => {
80+
describe('when the cell is fixed to the right', () => {
81+
it('should return true', () => {
82+
const $cell = createElement({ left: 500, right: 600 }, 'dx-datagrid-sticky-column-right');
83+
84+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToRight($cell, $container, addWidgetPrefix))
85+
.toBe(true);
86+
});
87+
});
88+
89+
describe('when the sticky cell is pinned to the right', () => {
90+
it('should return true', () => {
91+
const $cell = createElement({ left: 500, right: 600 }, 'dx-datagrid-sticky-column', { right: '0px' });
92+
93+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToRight($cell, $container, addWidgetPrefix))
94+
.toBe(true);
95+
});
96+
});
97+
98+
describe('when the sticky cell is not pinned', () => {
99+
it('should return false', () => {
100+
const $cell = createElement({ left: 200, right: 300 }, 'dx-datagrid-sticky-column', { right: '0px' });
101+
102+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToRight($cell, $container, addWidgetPrefix))
103+
.toBe(false);
104+
});
105+
});
106+
107+
describe('when the cell is fixed to the left', () => {
108+
it('should return false', () => {
109+
const $cell = createElement({ left: 0, right: 100 }, 'dx-datagrid-sticky-column-left');
110+
111+
expect(GridCoreStickyColumnsDom.isFixedCellPinnedToRight($cell, $container, addWidgetPrefix))
112+
.toBe(false);
113+
});
114+
});
115+
});

‎packages/devextreme/js/__internal/grids/grid_core/sticky_columns/dom.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ const isFixedCellPinnedToRight = (
8989
): boolean => $cell.hasClass(addWidgetPrefix(CLASSES.stickyColumnRight))
9090
|| isStickyCellPinnedToRight($cell, $container, addWidgetPrefix);
9191

92+
const isFixedCellPinnedToLeft = (
93+
$cell: dxElementWrapper,
94+
$container: dxElementWrapper,
95+
addWidgetPrefix,
96+
): boolean => $cell.hasClass(addWidgetPrefix(CLASSES.stickyColumnLeft))
97+
|| isStickyCellPinnedToLeft($cell, $container, addWidgetPrefix);
98+
9299
const isLastLeftFixedCell = (
93100
$cell: dxElementWrapper,
94101
addWidgetPrefix,
@@ -359,6 +366,7 @@ export const GridCoreStickyColumnsDom = {
359366
getNextHeaderCell,
360367
noNeedToCreateResizingPoint,
361368
isFixedCellPinnedToRight,
369+
isFixedCellPinnedToLeft,
362370
noNeedToCreateReorderingPoint,
363371
isFixedCell,
364372
isStickyCell,

‎packages/devextreme/js/__internal/grids/grid_core/sticky_columns/m_sticky_columns.ts‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,15 @@ const footerView = (
531531
) => class FooterViewStickyColumnsExtender extends baseStickyColumns(Base) {};
532532

533533
const columnsResizer = (Base: ModuleType<ColumnsResizerViewController>) => class ColumnResizerStickyColumnsExtender extends Base {
534-
protected getSeparatorOffsetX($cell: dxElementWrapper): number {
534+
private isNextCellPinned($nextCell: dxElementWrapper, $container: dxElementWrapper): boolean {
535+
const addWidgetPrefix = this.addWidgetPrefix.bind(this);
536+
537+
return this.option('rtlEnabled')
538+
? GridCoreStickyColumnsDom.isFixedCellPinnedToLeft($nextCell, $container, addWidgetPrefix)
539+
: GridCoreStickyColumnsDom.isFixedCellPinnedToRight($nextCell, $container, addWidgetPrefix);
540+
}
541+
542+
protected getSeparatorOffsetX($cell: dxElementWrapper, $nextCell: dxElementWrapper): number {
535543
// @ts-expect-error
536544
const hasStickyColumns = this._columnHeadersView?.hasStickyColumns();
537545

@@ -547,9 +555,16 @@ const columnsResizer = (Base: ModuleType<ColumnsResizerViewController>) => class
547555
if (isWidgetResizingMode && isFixedCellPinnedToRight) {
548556
return $cell.offset()?.left ?? 0;
549557
}
558+
559+
if (!isWidgetResizingMode && this.isNextCellPinned($nextCell, $container)) {
560+
const nextCellOffsetX: number = $nextCell.offset()?.left ?? 0;
561+
const nextCellWidth: number = $nextCell[0].getBoundingClientRect().width;
562+
563+
return this.option('rtlEnabled') ? nextCellOffsetX + nextCellWidth : nextCellOffsetX;
564+
}
550565
}
551566

552-
return super.getSeparatorOffsetX($cell);
567+
return super.getSeparatorOffsetX($cell, $nextCell);
553568
}
554569

555570
protected _correctColumnIndexForPoint(point, correctionValue: number, columns): void {

‎packages/testcafe-models/dataGrid/index.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -869,7 +869,12 @@ export default class DataGrid extends GridCore {
869869
)(CLASS.sortableDragging);
870870
}
871871

872-
resizeHeader(columnIndex: number, offset: number, needToTriggerPointerUp = true): Promise<void> {
872+
resizeHeader(
873+
columnIndex: number,
874+
offset: number,
875+
needToTriggerPointerUp = true,
876+
edge: 'left' | 'right' = 'left',
877+
): Promise<void> {
873878
const { getInstance } = this;
874879

875880
return ClientFunction(
@@ -879,7 +884,8 @@ export default class DataGrid extends GridCore {
879884
const columnHeadersView = gridInstance.getView('columnHeadersView');
880885
const $header = $(columnHeadersView.getHeaderElement(columnIndex));
881886
const headerOffset = $header.offset();
882-
const offsetX = headerOffset.left;
887+
const headerWidth = edge === 'right' ? $header[0].getBoundingClientRect().width : 0;
888+
const offsetX = headerOffset.left + headerWidth;
883889

884890
triggerPointerMove($(document), offsetX, headerOffset.top + 1);
885891
triggerPointerDown($gridElement, offsetX, headerOffset.top + 1);
@@ -898,6 +904,7 @@ export default class DataGrid extends GridCore {
898904
columnIndex,
899905
offset,
900906
needToTriggerPointerUp,
907+
edge,
901908
},
902909
},
903910
)();

0 commit comments

Comments
 (0)