Skip to content

Commit

Permalink
perf(react-grid): optimize column resizing (#878)
Browse files Browse the repository at this point in the history
related to #843
  • Loading branch information
kvet committed Mar 23, 2018
1 parent 10b53a0 commit b3f264b
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 21 deletions.
Expand Up @@ -3,21 +3,31 @@ const UNSET_COLUMN_WIDTH_ERROR = [
'The TableColumnResizing plugin requires that all columns have the specified width.',
].join('\n');

export const tableColumnsWithWidths = (tableColumns, columnWidths, draftColumnWidths) =>
tableColumns
const specifyWidths = (tableColumns, widths, onAbsence) => {
if (!widths.length) return tableColumns;
return tableColumns
.reduce((acc, tableColumn) => {
if (tableColumn.type === 'data') {
const columnName = tableColumn.column.name;
const isCurrentColumn = elem => elem.columnName === columnName;
const column = draftColumnWidths.find(isCurrentColumn)
|| columnWidths.find(isCurrentColumn);
const column = widths.find(el => el.columnName === columnName);
const width = column && column.width;
if (width === undefined) {
throw new Error(UNSET_COLUMN_WIDTH_ERROR.replace('$1', columnName));
onAbsence(columnName);
acc.push(tableColumn);
} else {
acc.push({ ...tableColumn, width });
}
acc.push({ ...tableColumn, width });
} else {
acc.push(tableColumn);
}
return acc;
}, []);
};

export const tableColumnsWithWidths = (tableColumns, columnWidths) =>
specifyWidths(tableColumns, columnWidths, (columnName) => {
throw new Error(UNSET_COLUMN_WIDTH_ERROR.replace('$1', columnName));
});

export const tableColumnsWithDraftWidths = (tableColumns, draftColumnWidths) =>
specifyWidths(tableColumns, draftColumnWidths, () => {});
@@ -1,5 +1,8 @@
import { TABLE_DATA_TYPE } from '../table/constants';
import { tableColumnsWithWidths } from './computeds';
import {
tableColumnsWithWidths,
tableColumnsWithDraftWidths,
} from './computeds';

describe('TableColumnResizing Plugin computeds', () => {
describe('#tableColumnsWithWidths', () => {
Expand All @@ -17,10 +20,9 @@ describe('TableColumnResizing Plugin computeds', () => {
{ columnName: 'b', width: 20 },
{ columnName: 'c', width: 15 },
],
[{ columnName: 'a', width: 15 }],
))
.toEqual([
{ type: TABLE_DATA_TYPE, width: 15, column: { name: 'a' } },
{ type: TABLE_DATA_TYPE, width: 10, column: { name: 'a' } },
{ type: TABLE_DATA_TYPE, width: 20, column: { name: 'b' } },
{ type: TABLE_DATA_TYPE, width: 15, column: { name: 'c' } },
]);
Expand All @@ -41,4 +43,24 @@ describe('TableColumnResizing Plugin computeds', () => {
.toThrow(/"b".*width/);
});
});

describe('#tableColumnsWithDraftWidths', () => {
it('should work', () => {
const tableColumns = [
{ type: TABLE_DATA_TYPE, column: { name: 'a' } },
{ type: TABLE_DATA_TYPE, column: { name: 'b' } },
{ type: TABLE_DATA_TYPE, column: { name: 'c' } },
];

expect(tableColumnsWithDraftWidths(
tableColumns,
[{ columnName: 'a', width: 15 }],
))
.toEqual([
{ type: TABLE_DATA_TYPE, width: 15, column: { name: 'a' } },
{ type: TABLE_DATA_TYPE, column: { name: 'b' } },
{ type: TABLE_DATA_TYPE, column: { name: 'c' } },
]);
});
});
});
4 changes: 4 additions & 0 deletions packages/dx-grid-core/src/utils/table.js
Expand Up @@ -55,6 +55,10 @@ export const getAnimations = (
tableWidth,
prevAnimations,
) => {
if (prevColumns.map(c => c.key).join('') === nextColumns.map(c => c.key).join('')) {
return new Map();
}

const prevColumnGeometries = new Map(getTableColumnGeometries(prevColumns, tableWidth)
.map((geometry, index) => [prevColumns[index].key, geometry])
.map(([key, geometry]) => {
Expand Down
12 changes: 12 additions & 0 deletions packages/dx-grid-core/src/utils/table.test.js
Expand Up @@ -2,6 +2,7 @@ import {
getTableColumnGeometries,
getTableTargetColumnIndex,
getTableRowColumnsWithColSpan,
getAnimations,
} from './table';

describe('table utils', () => {
Expand Down Expand Up @@ -67,4 +68,15 @@ describe('table utils', () => {
expect(getTableTargetColumnIndex(columnGeometries, 1, 260)).toEqual(3);
});
});

describe('#getAnimations', () => {
it('should not return animations if columns are the same', () => {
expect(getAnimations(
[{ key: 'a', width: 100 }, { key: 'b' }],
[{ key: 'a', width: 200 }, { key: 'b' }],
1000,
new Map(),
)).toEqual(new Map());
});
});
});
24 changes: 19 additions & 5 deletions packages/dx-react-grid/src/plugins/table-column-resizing.jsx
@@ -1,8 +1,10 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { memoize } from '@devexpress/dx-core';
import { Plugin, Getter, Action } from '@devexpress/dx-react-core';
import {
tableColumnsWithWidths,
tableColumnsWithDraftWidths,
changeTableColumnWidth,
draftTableColumnWidth,
cancelTableColumnWidthDraft,
Expand All @@ -28,13 +30,24 @@ export class TableColumnResizing extends React.PureComponent {
columnWidths: () => this.props.onColumnWidthsChange,
},
);
const { minColumnWidth } = props;

this.tableColumnsComputed = memoize(columnWidths =>
({ tableColumns }) => tableColumnsWithWidths(tableColumns, columnWidths));
this.tableColumnsDraftComputed = memoize(draftColumnWidths =>
({ tableColumns }) => tableColumnsWithDraftWidths(tableColumns, draftColumnWidths));

this.changeTableColumnWidth =
stateHelper.applyReducer.bind(stateHelper, (prevState, payload) =>
changeTableColumnWidth(prevState, { ...payload, minColumnWidth }));
changeTableColumnWidth(
prevState,
{ ...payload, minColumnWidth: this.props.minColumnWidth },
));
this.draftTableColumnWidth =
stateHelper.applyReducer.bind(stateHelper, (prevState, payload) =>
draftTableColumnWidth(prevState, { ...payload, minColumnWidth }));
draftTableColumnWidth(
prevState,
{ ...payload, minColumnWidth: this.props.minColumnWidth },
));
this.cancelTableColumnWidthDraft =
stateHelper.applyReducer.bind(stateHelper, cancelTableColumnWidthDraft);
}
Expand All @@ -49,8 +62,8 @@ export class TableColumnResizing extends React.PureComponent {
render() {
const { columnWidths, draftColumnWidths } = this.state;

const tableColumnsComputed = ({ tableColumns }) =>
tableColumnsWithWidths(tableColumns, columnWidths, draftColumnWidths);
const tableColumnsComputed = this.tableColumnsComputed(columnWidths);
const tableColumnsDraftComputed = this.tableColumnsDraftComputed(draftColumnWidths);

return (
<Plugin
Expand All @@ -59,6 +72,7 @@ export class TableColumnResizing extends React.PureComponent {
>
<Getter name="tableColumnResizingEnabled" value />
<Getter name="tableColumns" computed={tableColumnsComputed} />
<Getter name="tableColumns" computed={tableColumnsDraftComputed} />
<Action name="changeTableColumnWidth" action={this.changeTableColumnWidth} />
<Action name="draftTableColumnWidth" action={this.draftTableColumnWidth} />
<Action name="cancelTableColumnWidthDraft" action={this.cancelTableColumnWidthDraft} />
Expand Down
17 changes: 11 additions & 6 deletions packages/dx-react-grid/src/plugins/table-column-resizing.test.jsx
Expand Up @@ -4,6 +4,7 @@ import { setupConsole } from '@devexpress/dx-testing';
import { PluginHost } from '@devexpress/dx-react-core';
import {
tableColumnsWithWidths,
tableColumnsWithDraftWidths,
changeTableColumnWidth,
draftTableColumnWidth,
cancelTableColumnWidthDraft,
Expand All @@ -14,6 +15,7 @@ import { testStatePluginField } from '../utils/state-helper.test-utils';

jest.mock('@devexpress/dx-grid-core', () => ({
tableColumnsWithWidths: jest.fn(),
tableColumnsWithDraftWidths: jest.fn(),
changeTableColumnWidth: jest.fn(),
draftTableColumnWidth: jest.fn(),
cancelTableColumnWidthDraft: jest.fn(),
Expand Down Expand Up @@ -41,6 +43,7 @@ describe('TableColumnResizing', () => {

beforeEach(() => {
tableColumnsWithWidths.mockImplementation(() => 'tableColumnsWithWidths');
tableColumnsWithDraftWidths.mockImplementation(() => 'tableColumnsWithDraftWidths');
changeTableColumnWidth.mockImplementation(() => ([]));
draftTableColumnWidth.mockImplementation(() => ([]));
cancelTableColumnWidthDraft.mockImplementation(() => ([]));
Expand Down Expand Up @@ -82,9 +85,11 @@ describe('TableColumnResizing', () => {
));

expect(getComputedState(tree).tableColumns)
.toBe('tableColumnsWithWidths');
.toBe('tableColumnsWithDraftWidths');
expect(tableColumnsWithWidths)
.toBeCalledWith(defaultDeps.getter.tableColumns, [{ columnName: 'a', width: 100 }], []);
.toBeCalledWith(defaultDeps.getter.tableColumns, [{ columnName: 'a', width: 100 }]);
expect(tableColumnsWithDraftWidths)
.toBeCalledWith('tableColumnsWithWidths', []);
});
});

Expand All @@ -107,8 +112,8 @@ describe('TableColumnResizing', () => {
expect(draftTableColumnWidth)
.toBeCalledWith(expect.objectContaining({ draftColumnWidths: [] }), payload);

expect(tableColumnsWithWidths)
.toBeCalledWith(defaultDeps.getter.tableColumns, [{ columnName: 'a', width: 100 }], [{ columnName: 'a', width: 150 }]);
expect(tableColumnsWithDraftWidths)
.toBeCalledWith('tableColumnsWithWidths', [{ columnName: 'a', width: 150 }]);
});

it('should correctly update column widths after the "cancelTableColumnWidthDraft" action is fired', () => {
Expand All @@ -130,7 +135,7 @@ describe('TableColumnResizing', () => {
expect(cancelTableColumnWidthDraft)
.toBeCalledWith(expect.objectContaining({ draftColumnWidths: [] }), payload);

expect(tableColumnsWithWidths)
.toBeCalledWith(defaultDeps.getter.tableColumns, [{ columnName: 'a', width: 100 }], [{ columnName: 'a', width: 150 }]);
expect(tableColumnsWithDraftWidths)
.toBeCalledWith('tableColumnsWithWidths', [{ columnName: 'a', width: 150 }]);
});
});

0 comments on commit b3f264b

Please sign in to comment.