Skip to content

Commit

Permalink
fix(react-grid): prevent animation flicker on column visibility change (
Browse files Browse the repository at this point in the history
  • Loading branch information
ushkal authored Dec 19, 2018
1 parent b09e439 commit 2cbb72a
Show file tree
Hide file tree
Showing 14 changed files with 229 additions and 48 deletions.
2 changes: 2 additions & 0 deletions packages/dx-react-core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ export { connectProps } from './utils/connect-props';

export { createStateHelper } from './utils/state-helper';
export { withComponents } from './utils/with-components';

export { RefType } from './utils/ref-type';
6 changes: 6 additions & 0 deletions packages/dx-react-core/src/utils/ref-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* globals Element */
import * as PropTypes from 'prop-types';

export const RefType = PropTypes.shape({
current: PropTypes.instanceOf((typeof Element !== 'undefined') ? Element : Object),
});
9 changes: 7 additions & 2 deletions packages/dx-react-grid-bootstrap3/src/templates/table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import { RefType } from '@devexpress/dx-react-core';
import { ThemeColors } from './layout';

let globalStickyProp;
Expand Down Expand Up @@ -43,13 +44,16 @@ export class Table extends React.Component {

render() {
const {
children, use, style, className, ...restProps
children, use, style, className, tableRef, ...restProps
} = this.props;
const { stickyProp } = this.state;
const { backgroundColor } = this.context;
return (
<table
ref={this.tableRef}
ref={(ref) => {
this.tableRef.current = ref;
tableRef.current = ref;
}}
className={classNames('table', className)}
style={{
tableLayout: 'fixed',
Expand Down Expand Up @@ -83,6 +87,7 @@ Table.propTypes = {
children: PropTypes.node.isRequired,
style: PropTypes.object,
className: PropTypes.string,
tableRef: RefType.isRequired,
};

Table.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { Table } from './table';
import { ThemeColors } from './layout';

describe('Table', () => {
const tableRef = React.createRef();

it('should pass class to the root element', () => {
const tree = mount((
<ThemeColors.Provider value={{ backgroundColor: 'white' }}>
<Table className="custom-class">
<Table className="custom-class" tableRef={tableRef}>
<tbody />
</Table>
</ThemeColors.Provider>
Expand All @@ -20,7 +22,7 @@ describe('Table', () => {
it('should pass rest props to the root element', () => {
const tree = mount((
<ThemeColors.Provider value={{ backgroundColor: 'white' }}>
<Table data={{ a: 1 }}>
<Table data={{ a: 1 }} tableRef={tableRef}>
<tbody />
</Table>
</ThemeColors.Provider>
Expand Down
5 changes: 4 additions & 1 deletion packages/dx-react-grid-bootstrap4/src/templates/table.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import { RefType } from '@devexpress/dx-react-core';
import { BodyColorContext } from './layout';

// eslint-disable-next-line react/prefer-stateless-function
export class Table extends React.Component {
render() {
const {
children, use, style, className, ...restProps
children, use, style, className, tableRef, ...restProps
} = this.props;
const backgroundColor = this.context;

return (
<table
ref={tableRef}
className={classNames({
'table dx-g-bs4-table': true,
'dx-g-bs4-table-sticky': !!use,
Expand Down Expand Up @@ -43,6 +45,7 @@ Table.propTypes = {
]).isRequired,
style: PropTypes.object,
className: PropTypes.string,
tableRef: RefType.isRequired,
};

Table.defaultProps = {
Expand Down
29 changes: 17 additions & 12 deletions packages/dx-react-grid-material-ui/src/templates/table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import * as React from 'react';
import * as PropTypes from 'prop-types';
import classNames from 'classnames';
import TableMUI from '@material-ui/core/Table';
import RootRef from '@material-ui/core/RootRef';
import { withStyles } from '@material-ui/core/styles';
import { RefType } from '@devexpress/dx-react-core';
import { getBorder } from './utils';

const styles = theme => ({
Expand All @@ -29,27 +31,30 @@ const styles = theme => ({
});

const TableBase = ({
children, classes, className, use,
children, classes, className, use, tableRef,
...restProps
}) => (
<TableMUI
className={classNames({
[classes.table]: true,
[classes.stickyTable]: !!use,
[classes.headTable]: use === 'head',
[classes.footTable]: use === 'foot',
}, className)}
{...restProps}
>
{children}
</TableMUI>
<RootRef rootRef={tableRef}>
<TableMUI
className={classNames({
[classes.table]: true,
[classes.stickyTable]: !!use,
[classes.headTable]: use === 'head',
[classes.footTable]: use === 'foot',
}, className)}
{...restProps}
>
{children}
</TableMUI>
</RootRef>
);

TableBase.propTypes = {
use: PropTypes.oneOf(['head', 'foot']),
children: PropTypes.node.isRequired,
classes: PropTypes.object.isRequired,
className: PropTypes.string,
tableRef: RefType.isRequired,
};

TableBase.defaultProps = {
Expand Down
23 changes: 17 additions & 6 deletions packages/dx-react-grid-material-ui/src/templates/table.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,50 @@ import { createShallow, getClasses } from '@material-ui/core/test-utils';
import { Table } from './table';

describe('Table', () => {
const defaultProps = {
tableRef: React.createRef(),
};
let shallow;
let classes;
beforeAll(() => {
shallow = createShallow({ dive: true });
classes = getClasses(
<Table>
<Table {...defaultProps}>
<tbody />
</Table>,
);
});

it('should pass the className prop to the root element', () => {
const tree = shallow((
<Table className="custom-class">
<Table
{...defaultProps}
className="custom-class"
>
<tbody />
</Table>
));

expect(tree.is(`.${classes.table}`))
const table = tree.childAt(0);
expect(table.is(`.${classes.table}`))
.toBeTruthy();

expect(tree.is('.custom-class'))
expect(table.is('.custom-class'))
.toBeTruthy();
});

it('should pass rest props to the root element', () => {
const tree = shallow((
<Table data={{ a: 1 }}>
<Table
{...defaultProps}
data={{ a: 1 }}
>
<tbody />
</Table>
));

expect(tree.props().data)
const table = tree.childAt(0);
expect(table.props().data)
.toMatchObject({ a: 1 });
});
});
25 changes: 22 additions & 3 deletions packages/dx-react-grid/src/components/table-layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import * as React from 'react';
import * as PropTypes from 'prop-types';
import { findDOMNode } from 'react-dom';
import {
getAnimations,
filterActiveAnimations,
Expand All @@ -19,19 +18,38 @@ export class TableLayout extends React.PureComponent {
};

this.animations = new Map();
this.savedScrolldWidth = {};
this.tableRef = React.createRef();
}

componentDidUpdate(prevProps) {
const { columns } = this.props;
const { columns: prevColumns } = prevProps;

// eslint-disable-next-line react/no-find-dom-node
const tableWidth = findDOMNode(this).scrollWidth;
const tableWidth = this.getTableWidth(prevColumns, columns);
this.animations = getAnimations(prevColumns, columns, tableWidth, this.animations);

cancelAnimationFrame(this.raf);
this.raf = requestAnimationFrame(this.processAnimationFrame.bind(this));
}

getTableWidth(prevColumns, columns) {
const { offsetWidth, scrollWidth } = this.tableRef.current;
const { animationState } = this.state;

const widthChanged = this.savedOffsetWidth !== offsetWidth
|| !this.savedScrolldWidth[columns.length];
const columnCountChanged = columns.length !== prevColumns.length;

if (columnCountChanged || (widthChanged && !animationState.size)) {
this.savedScrolldWidth = {};
this.savedScrolldWidth[columns.length] = scrollWidth;
this.savedOffsetWidth = offsetWidth;
}

return this.savedScrolldWidth[columns.length];
}

getColumns() {
const { columns } = this.props;
const { animationState } = this.state;
Expand Down Expand Up @@ -83,6 +101,7 @@ export class TableLayout extends React.PureComponent {
return (
<Layout
{...restProps}
tableRef={this.tableRef}
columns={columns}
minWidth={minWidth}
minColumnWidth={minColumnWidth}
Expand Down
Loading

0 comments on commit 2cbb72a

Please sign in to comment.