Skip to content

Commit 00ccebd

Browse files
authored
fix(react-grid): correct order of fixed right columns (#1533)
* fix(grid): order of fixed right columns * refactor(grid): fixed columns plugin * refactor(grid): move fixed column props calculation to grid-core
1 parent 5686c54 commit 00ccebd

File tree

4 files changed

+244
-64
lines changed

4 files changed

+244
-64
lines changed

packages/dx-grid-core/src/plugins/table-fixed-columns/helpers.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { TABLE_DATA_TYPE } from '../table/constants';
2-
import { TABLE_FIXED_TYPE } from './constants';
2+
import {
3+
FIXED_COLUMN_LEFT_SIDE, FIXED_COLUMN_RIGHT_SIDE, TABLE_FIXED_TYPE,
4+
} from './constants';
35

46
export const getFixedColumnKeys = (tableColumns, fixedNames) => tableColumns
57
.filter(tableColumn => (
@@ -9,3 +11,48 @@ export const getFixedColumnKeys = (tableColumns, fixedNames) => tableColumns
911
.map(({ key }) => key);
1012

1113
export const isFixedTableRow = tableRow => tableRow.type === TABLE_FIXED_TYPE;
14+
15+
const calculatePosition = (array, index, tableColumnDimensions) => (
16+
index === 0
17+
? 0
18+
: array
19+
.slice(0, index)
20+
.reduce((acc, target) => acc + tableColumnDimensions[target] || 0, 0)
21+
);
22+
23+
export const calculateFixedColumnProps = (
24+
{ tableColumn },
25+
{ leftColumns, rightColumns },
26+
tableColumns,
27+
tableColumnDimensions,
28+
) => {
29+
const { fixed: side } = tableColumn;
30+
const targetArray = side === FIXED_COLUMN_LEFT_SIDE
31+
? getFixedColumnKeys(tableColumns, leftColumns)
32+
: getFixedColumnKeys(tableColumns, rightColumns).reverse();
33+
34+
const fixedIndex = targetArray.indexOf(tableColumn.key);
35+
const index = tableColumns.findIndex(({ key }) => key === tableColumn.key);
36+
37+
const isBoundary = fixedSide => (
38+
fixedIndex === targetArray.length - 1 && fixedSide === side
39+
);
40+
const isStandAlone = (shift) => {
41+
const neighborTableColumn = tableColumns[index + shift];
42+
return neighborTableColumn && targetArray.indexOf(neighborTableColumn.key) === -1;
43+
};
44+
45+
const showRightDivider = isBoundary(FIXED_COLUMN_LEFT_SIDE)
46+
|| (index !== tableColumns.length - 1 && isStandAlone(1));
47+
const showLeftDivider = isBoundary(FIXED_COLUMN_RIGHT_SIDE)
48+
|| (index !== 0 && isStandAlone(-1));
49+
50+
const position = calculatePosition(targetArray, fixedIndex, tableColumnDimensions);
51+
52+
return {
53+
showRightDivider,
54+
showLeftDivider,
55+
position,
56+
side,
57+
};
58+
};
Lines changed: 135 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11
import { TABLE_DATA_TYPE } from '../table/constants';
2-
import { TABLE_FIXED_TYPE } from './constants';
3-
import { getFixedColumnKeys, isFixedTableRow } from './helpers';
2+
import { FIXED_COLUMN_LEFT_SIDE, FIXED_COLUMN_RIGHT_SIDE, TABLE_FIXED_TYPE } from './constants';
3+
import { getFixedColumnKeys, isFixedTableRow, calculateFixedColumnProps } from './helpers';
44

55
describe('TableFixedColumns Plugin helpers', () => {
6+
const sampleType = Symbol('sample');
7+
const createColumn = (name, fixed) => ({
8+
key: `key_${name}`, type: TABLE_DATA_TYPE, column: { name }, ...fixed && { fixed },
9+
});
10+
const tableColumns = [
11+
createColumn('a', FIXED_COLUMN_LEFT_SIDE),
12+
createColumn('b', FIXED_COLUMN_LEFT_SIDE),
13+
{
14+
key: 'key_type1', type: sampleType,
15+
},
16+
createColumn('c', FIXED_COLUMN_RIGHT_SIDE),
17+
createColumn('d', FIXED_COLUMN_RIGHT_SIDE),
18+
];
19+
const findColumnByNameCore = (name, columns) => (
20+
columns.find(c => c.column && c.column.name === name)
21+
);
22+
const tableColumnDimensions = {
23+
key_a: 20,
24+
key_b: 30,
25+
key_type1: 40,
26+
key_c: 70,
27+
key_d: 150,
28+
};
29+
630
describe('#getFixedColumnKeys', () => {
731
it('should return the correct array of column keys', () => {
8-
const sampleType = Symbol('sample');
9-
10-
const tableColumns = [
11-
{ key: 'key_a', type: TABLE_DATA_TYPE, column: { name: 'a' } },
12-
{ key: 'key_b', type: TABLE_DATA_TYPE, column: { name: 'b' } },
13-
{ key: 'key_type1', type: sampleType },
14-
{ key: 'key_c', type: TABLE_DATA_TYPE, column: { name: 'c' } },
15-
{ key: 'key_d', type: TABLE_DATA_TYPE, column: { name: 'd' } },
16-
];
1732
const fixedNames = ['a', 'd', sampleType];
1833

1934
expect(getFixedColumnKeys(tableColumns, fixedNames))
@@ -27,4 +42,113 @@ describe('TableFixedColumns Plugin helpers', () => {
2742
expect(isFixedTableRow({ type: 'undefined' })).toBeFalsy();
2843
});
2944
});
45+
46+
describe('#calculateFixedColumnProps', () => {
47+
describe('position', () => {
48+
const calculatePosition = (fixedColumns, column) => {
49+
const { position } = calculateFixedColumnProps(
50+
{ tableColumn: column },
51+
fixedColumns,
52+
tableColumns,
53+
tableColumnDimensions,
54+
);
55+
return position;
56+
};
57+
const findColumnByName = name => findColumnByNameCore(name, tableColumns);
58+
59+
it('should calculate position of columns fixed at the right side', () => {
60+
const fixedColumns = { leftColumns: ['a'], rightColumns: ['c', 'd'] };
61+
62+
expect(calculatePosition(fixedColumns, findColumnByName('c'))).toBe(150);
63+
expect(calculatePosition(fixedColumns, findColumnByName('d'))).toBe(0);
64+
});
65+
66+
it('should calculate position of columns fixed at the left side', () => {
67+
const fixedColumns = { leftColumns: ['a', 'b'], rightColumns: ['c'] };
68+
69+
expect(calculatePosition(fixedColumns, findColumnByName('a'))).toBe(0);
70+
expect(calculatePosition(fixedColumns, findColumnByName('b'))).toBe(20);
71+
});
72+
});
73+
74+
describe('dividers visibility', () => {
75+
const extendedTableColumns = [
76+
createColumn('a', FIXED_COLUMN_LEFT_SIDE),
77+
createColumn('col0'),
78+
createColumn('b0', FIXED_COLUMN_LEFT_SIDE),
79+
createColumn('b1', FIXED_COLUMN_LEFT_SIDE),
80+
createColumn('b2', FIXED_COLUMN_LEFT_SIDE),
81+
createColumn('col1'),
82+
createColumn('c', FIXED_COLUMN_LEFT_SIDE),
83+
createColumn('col2'),
84+
createColumn('d', FIXED_COLUMN_RIGHT_SIDE),
85+
createColumn('col2'),
86+
createColumn('e0', FIXED_COLUMN_RIGHT_SIDE),
87+
createColumn('e1', FIXED_COLUMN_RIGHT_SIDE),
88+
createColumn('e2', FIXED_COLUMN_RIGHT_SIDE),
89+
createColumn('col3'),
90+
createColumn('f', FIXED_COLUMN_RIGHT_SIDE),
91+
];
92+
const findColumnByName = name => findColumnByNameCore(name, extendedTableColumns);
93+
94+
const calculateDividers = column => (calculateFixedColumnProps(
95+
{ tableColumn: column },
96+
{ leftColumns: ['a', 'b0', 'b1', 'b2', 'c'], rightColumns: ['d', 'e0', 'e1', 'e2', 'f'] },
97+
extendedTableColumns,
98+
{},
99+
));
100+
101+
it('should be visible for standalone left column', () => {
102+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('c'));
103+
104+
expect(showLeftDivider).toBeTruthy();
105+
expect(showRightDivider).toBeTruthy();
106+
});
107+
it('should be visible for standalone right column', () => {
108+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('d'));
109+
110+
expect(showLeftDivider).toBeTruthy();
111+
expect(showRightDivider).toBeTruthy();
112+
});
113+
114+
it('should show only right divider for standalone leftmost column', () => {
115+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('a'));
116+
117+
expect(showLeftDivider).toBeFalsy();
118+
expect(showRightDivider).toBeTruthy();
119+
});
120+
it('should show only left divider for standalone rightmost column', () => {
121+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('f'));
122+
123+
expect(showLeftDivider).toBeTruthy();
124+
expect(showRightDivider).toBeFalsy();
125+
});
126+
127+
it('should show only left divider for first left column in a group', () => {
128+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('b0'));
129+
130+
expect(showLeftDivider).toBeTruthy();
131+
expect(showRightDivider).toBeFalsy();
132+
});
133+
it('should show only right divider for first right column in a group', () => {
134+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('e2'));
135+
136+
expect(showLeftDivider).toBeFalsy();
137+
expect(showRightDivider).toBeTruthy();
138+
});
139+
140+
it('should not be visible for consecutive left columns', () => {
141+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('b1'));
142+
143+
expect(showLeftDivider).toBeFalsy();
144+
expect(showRightDivider).toBeFalsy();
145+
});
146+
it('should not be visible for consecutive right columns', () => {
147+
const { showLeftDivider, showRightDivider } = calculateDividers(findColumnByName('e1'));
148+
149+
expect(showLeftDivider).toBeFalsy();
150+
expect(showRightDivider).toBeFalsy();
151+
});
152+
});
153+
});
30154
});

packages/dx-react-grid/src/plugins/table-fixed-columns.jsx

Lines changed: 9 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ import {
88
TemplateConnector,
99
} from '@devexpress/dx-react-core';
1010
import {
11-
FIXED_COLUMN_LEFT_SIDE,
12-
FIXED_COLUMN_RIGHT_SIDE,
1311
isFixedTableRow,
14-
getFixedColumnKeys,
1512
tableColumnsWithFixed,
1613
tableHeaderRowsWithFixed,
14+
calculateFixedColumnProps,
1715
} from '@devexpress/dx-grid-core';
1816

1917
const tableHeaderRowsComputed = ({ tableHeaderRows }) => tableHeaderRowsWithFixed(tableHeaderRows);
@@ -42,14 +40,6 @@ export class TableFixedColumns extends React.PureComponent {
4240
}));
4341
}
4442

45-
calculatePosition(array, index) {
46-
return index === 0
47-
? 0
48-
: array
49-
.slice(0, index)
50-
.reduce((acc, target) => acc + this.tableColumnDimensions[target] || 0, 0);
51-
}
52-
5343
render() {
5444
const {
5545
leftColumns,
@@ -58,9 +48,6 @@ export class TableFixedColumns extends React.PureComponent {
5848
listenerRowComponent: ListenerRow,
5949
listenerCellComponent: ListenerCell,
6050
} = this.props;
61-
const {
62-
tableColumnDimensions,
63-
} = this.state;
6451

6552
const tableColumnsComputed = ({ tableColumns }) => tableColumnsWithFixed(
6653
tableColumns,
@@ -82,45 +69,19 @@ export class TableFixedColumns extends React.PureComponent {
8269
{params => (
8370
<TemplateConnector>
8471
{({ tableColumns }) => {
85-
const { tableColumn } = params;
86-
const { fixed: side } = tableColumn;
87-
const targetArray = side === FIXED_COLUMN_LEFT_SIDE
88-
? getFixedColumnKeys(tableColumns, leftColumns)
89-
: getFixedColumnKeys(tableColumns, rightColumns);
90-
91-
const fixedIndex = targetArray.indexOf(tableColumn.key);
92-
const index = tableColumns.findIndex(({ key }) => key === tableColumn.key);
93-
94-
const isBoundary = fixedSide => (fixedIndex === targetArray.length - 1
95-
&& fixedSide === side);
96-
const isStandAlone = (shift) => {
97-
const neighborTableColumn = tableColumns[index + shift];
98-
if (!neighborTableColumn) return false;
99-
if (targetArray.indexOf(neighborTableColumn.key) === -1) {
100-
return true;
101-
}
102-
return false;
103-
};
104-
105-
const showRightDivider = isBoundary(FIXED_COLUMN_LEFT_SIDE)
106-
|| (index !== tableColumns.length - 1 && isStandAlone(1));
107-
const showLeftDivider = isBoundary(FIXED_COLUMN_RIGHT_SIDE)
108-
|| (index !== 0 && isStandAlone(-1));
109-
110-
const position = fixedIndex === 0
111-
? 0
112-
: targetArray
113-
.slice(0, fixedIndex)
114-
.reduce((acc, target) => acc + tableColumnDimensions[target] || 0, 0);
72+
const { tableColumnDimensions } = this.state;
73+
const fixedColumnProps = calculateFixedColumnProps(
74+
params,
75+
{ leftColumns, rightColumns },
76+
tableColumns,
77+
tableColumnDimensions,
78+
);
11579

11680
return (
11781
<Cell
11882
{...params}
119-
side={side}
83+
{...fixedColumnProps}
12084
component={CellPlaceholder}
121-
showLeftDivider={showLeftDivider}
122-
showRightDivider={showRightDivider}
123-
position={position}
12485
/>
12586
);
12687
}}

packages/dx-react-grid/src/plugins/table-fixed-columns.test.jsx

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { pluginDepsToComponents, getComputedState } from '@devexpress/dx-react-c
55
import { PluginHost, Template, TemplatePlaceholder } from '@devexpress/dx-react-core';
66
import {
77
FIXED_COLUMN_LEFT_SIDE,
8+
FIXED_COLUMN_RIGHT_SIDE,
9+
TABLE_DATA_TYPE,
810
getFixedColumnKeys,
911
tableColumnsWithFixed,
1012
tableHeaderRowsWithFixed,
@@ -13,7 +15,7 @@ import {
1315
import { TableFixedColumns } from './table-fixed-columns';
1416

1517
jest.mock('@devexpress/dx-grid-core', () => ({
16-
FIXED_COLUMN_LEFT_SIDE: 'LEFT',
18+
...require.requireActual('@devexpress/dx-grid-core'),
1719
getFixedColumnKeys: jest.fn(),
1820
tableColumnsWithFixed: jest.fn(),
1921
tableHeaderRowsWithFixed: jest.fn(),
@@ -92,7 +94,7 @@ describe('TableFixedColumns', () => {
9294

9395
it('can render fixed cells', () => {
9496
tableColumnsWithFixed.mockImplementation(() => [
95-
{ column: { name: 'a' }, fixed: FIXED_COLUMN_LEFT_SIDE },
97+
{ column: { name: 'a', xx: 'yyy' }, fixed: FIXED_COLUMN_LEFT_SIDE },
9698
]);
9799
const leftColumns = ['a'];
98100
const deps = {
@@ -153,8 +155,12 @@ describe('TableFixedColumns', () => {
153155
it('takes column widths into account', () => {
154156
getFixedColumnKeys.mockImplementation(() => ['a', 'b']);
155157
tableColumnsWithFixed.mockImplementation(() => [
156-
{ key: 'a', column: { name: 'a' }, fixed: FIXED_COLUMN_LEFT_SIDE },
157-
{ key: 'b', column: { name: 'b' }, fixed: FIXED_COLUMN_LEFT_SIDE },
158+
{
159+
key: 'a', column: { name: 'a' }, type: TABLE_DATA_TYPE, fixed: FIXED_COLUMN_LEFT_SIDE,
160+
},
161+
{
162+
key: 'b', column: { name: 'b' }, type: TABLE_DATA_TYPE, fixed: FIXED_COLUMN_LEFT_SIDE,
163+
},
158164
]);
159165
isFixedTableRow.mockImplementation(tableRow => tableRow.type === 'fixed');
160166
const leftColumns = ['a', 'b'];
@@ -186,4 +192,46 @@ describe('TableFixedColumns', () => {
186192
position: 200,
187193
});
188194
});
195+
196+
it('should render right columns in correct order', () => {
197+
getFixedColumnKeys.mockImplementation(() => ['a', 'b']);
198+
tableColumnsWithFixed.mockImplementation(() => [
199+
{
200+
key: 'a', column: { name: 'a' }, type: TABLE_DATA_TYPE, fixed: FIXED_COLUMN_RIGHT_SIDE,
201+
},
202+
{
203+
key: 'b', column: { name: 'b' }, type: TABLE_DATA_TYPE, fixed: FIXED_COLUMN_RIGHT_SIDE,
204+
},
205+
]);
206+
isFixedTableRow.mockImplementation(tableRow => tableRow.type === 'fixed');
207+
const rightColumns = ['a', 'b'];
208+
209+
const tree = mount((
210+
<PluginHost>
211+
{pluginDepsToComponents(defaultDeps)}
212+
<Template name="root">
213+
<TemplatePlaceholder
214+
name="tableCell"
215+
params={{ tableColumn: { key: 'a', column: { name: 'a' }, fixed: FIXED_COLUMN_RIGHT_SIDE }, tableRow: { type: 'row' } }}
216+
/>
217+
<TemplatePlaceholder
218+
name="tableCell"
219+
params={{ tableColumn: { key: 'b', column: { name: 'b' }, fixed: FIXED_COLUMN_RIGHT_SIDE }, tableRow: { type: 'fixed' } }}
220+
/>
221+
</Template>
222+
<TableFixedColumns
223+
{...defaultProps}
224+
rightColumns={rightColumns}
225+
/>
226+
</PluginHost>
227+
));
228+
229+
tree.find(defaultProps.listenerCellComponent).prop('onSizeChange')({ width: 200 });
230+
tree.update();
231+
232+
expect(tree.find(defaultProps.cellComponent).props())
233+
.toMatchObject({
234+
position: 200,
235+
});
236+
});
189237
});

0 commit comments

Comments
 (0)