Skip to content

Commit

Permalink
POC: use react portals for cell editors (#1369)
Browse files Browse the repository at this point in the history
* POC: use react portals for cell editors

* Delete obsolete tests

* Fix selection/copy/drag masks positions on frozen columns

* Simplify selectionMask position logic
Remove unused props

* Fix unit tests

* Remove getSelectedRowTop

* Fix unit tests

* Fix unit tests in FF

* Remove position prop in favor of left/top props

* MInor cleanup

* Remove fdescribe and fix unit tests

* Remove empty event handlers

* Add some comments

* - Fix row heights for copy and drag masks
- Add back dynamic row logic
  • Loading branch information
amanmahajan7 authored Nov 20, 2018
1 parent c2cfdf8 commit 264045f
Show file tree
Hide file tree
Showing 25 changed files with 299 additions and 273 deletions.
72 changes: 37 additions & 35 deletions packages/common/editors/EditorContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';
import joinClasses from 'classnames';
import SimpleTextEditor from './SimpleTextEditor';
import {isFunction} from 'common/utils';
import { isFunction } from 'common/utils';
import { isKeyPrintable, isCtrlKeyHeldDown } from 'common/utils/keyboardUtils';
import zIndexes from 'common/constants/zIndexes';
import EditorPortal from './EditorPortal';

require('../../../themes/react-data-grid-core.css');

const isFrozen = column => column.frozen === true || column.locked === true;
Expand All @@ -17,19 +19,19 @@ class EditorContainer extends React.Component {
rowData: PropTypes.object.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object, PropTypes.bool]).isRequired,
column: PropTypes.object.isRequired,
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
left: PropTypes.number.isRequired,
top: PropTypes.number.isRequired,
onGridKeyDown: PropTypes.func,
onCommit: PropTypes.func,
onCommitCancel: PropTypes.func,
firstEditorKeyPress: PropTypes.string,
width: PropTypes.number,
top: PropTypes.number,
left: PropTypes.number,
scrollLeft: PropTypes.number,
scrollTop: PropTypes.number
};

state = {isInvalid: false};
state = { isInvalid: false };
changeCommitted = false;
changeCanceled = false;

Expand All @@ -42,7 +44,6 @@ class EditorContainer extends React.Component {
inputNode.style.height = this.props.height - 1 + 'px';
}
}
window.addEventListener('scroll', this.setContainerPosition);
}

componentDidUpdate(prevProps) {
Expand All @@ -53,24 +54,10 @@ class EditorContainer extends React.Component {

componentWillUnmount() {
if (!this.changeCommitted && !this.changeCanceled) {
this.commit({key: 'Enter'});
}
window.removeEventListener('scroll', this.setContainerPosition);
}

setContainerPosition = () => {
if (this.container) {
this.container.style.transform = this.calculateTransform();
this.commit({ key: 'Enter' });
}
}

calculateTransform = () => {
const { column, left, scrollLeft, top, scrollTop } = this.props;
const editorLeft = isFrozen(column) ? left : left - scrollLeft;
const editorTop = top - scrollTop - window.pageYOffset;
return `translate(${editorLeft}px, ${editorTop}px)`;
}

isKeyExplicitlyHandled = (key) => {
return isFunction(this['onPress' + key]);
};
Expand Down Expand Up @@ -132,15 +119,23 @@ class EditorContainer extends React.Component {
return <CustomEditor ref={this.setEditorRef} {...editorProps} />;
}

return <SimpleTextEditor ref={this.setEditorRef} column={this.props.column} value={this.getInitialValue()} onBlur={this.commit} rowMetaData={this.getRowMetaData()} onKeyDown={() => {}} commit={() => {}}/>;
return (
<SimpleTextEditor
ref={this.setEditorRef}
column={this.props.column}
value={this.getInitialValue()}
onBlur={this.commit}
rowMetaData={this.getRowMetaData()}
/>
);
};

onPressEnter = () => {
this.commit({key: 'Enter'});
this.commit({ key: 'Enter' });
};

onPressTab = () => {
this.commit({key: 'Tab'});
this.commit({ key: 'Tab' });
};

onPressEscape = (e) => {
Expand Down Expand Up @@ -239,13 +234,13 @@ class EditorContainer extends React.Component {
};

commit = (args) => {
const {onCommit} = this.props;
const { onCommit } = this.props;
let opts = args || {};
let updated = this.getEditor().getValue();
if (this.isNewValueValid(updated)) {
this.changeCommitted = true;
let cellKey = this.props.column.key;
onCommit({cellKey: cellKey, rowIdx: this.props.rowIdx, updated: updated, key: opts.key});
onCommit({ cellKey: cellKey, rowIdx: this.props.rowIdx, updated: updated, key: opts.key });
}
};

Expand All @@ -257,7 +252,7 @@ class EditorContainer extends React.Component {
isNewValueValid = (value) => {
if (isFunction(this.getEditor().validate)) {
let isValid = this.getEditor().validate(value);
this.setState({isInvalid: !isValid});
this.setState({ isInvalid: !isValid });
return isValid;
}

Expand Down Expand Up @@ -306,8 +301,8 @@ class EditorContainer extends React.Component {

getRelatedTarget = (e) => {
return e.relatedTarget ||
e.explicitOriginalTarget ||
document.activeElement; // IE11
e.explicitOriginalTarget ||
document.activeElement; // IE11
};

handleRightClick = (e) => {
Expand All @@ -321,7 +316,7 @@ class EditorContainer extends React.Component {
}

if (!this.isBodyClicked(e)) {
// prevent null reference
// prevent null reference
if (this.isViewportClicked(e) || !this.isClickInsideEditor(e)) {
this.commit(e);
}
Expand Down Expand Up @@ -349,15 +344,22 @@ class EditorContainer extends React.Component {
};

render() {
const { width, height, column } = this.props;
const zIndex = isFrozen(column) ? zIndexes.FROZEN_EDITOR_CONTAINER : zIndexes.EDITOR_CONTAINER;
const style = { position: 'fixed', height, width, zIndex, transform: this.calculateTransform() };
const { width, height, left, top, column } = this.props;
const zIndex = isFrozen(column) ? zIndexes.FROZEN_EDITOR_CONTAINER : zIndexes.EDITOR_CONTAINER;
const style = { position: 'absolute', height, width, left, top, zIndex };
return (
<div ref={this.setContainerRef} style={style} className={this.getContainerClass()} onBlur={this.handleBlur} onKeyDown={this.onKeyDown} onContextMenu={this.handleRightClick}>
<EditorPortal>
<div style={style}
className={this.getContainerClass()}
onBlur={this.handleBlur}
onKeyDown={this.onKeyDown}
onContextMenu={this.handleRightClick}
>
{this.createEditor()}
{this.renderStatusIcon()}
</div>
);
</EditorPortal>
);
}
}

Expand Down
28 changes: 28 additions & 0 deletions packages/common/editors/EditorPortal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';

const editorRoot = document.body;

export default class EditorPortal extends React.Component {
static propTypes = {
children: PropTypes.node
};

el = document.createElement('div');

componentDidMount() {
editorRoot.appendChild(this.el);
}

componentWillUnmount() {
editorRoot.removeChild(this.el);
}

render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}
37 changes: 7 additions & 30 deletions packages/common/editors/__tests__/EditorContainer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ const defaultProps = {
},
column: fakeColumn,
value: 'Adwolf',
height: 50
height: 50,
onCommit: jasmine.createSpy(),
onCommitCancel: jasmine.createSpy()
};

const getComponent = (props) => {
Expand Down Expand Up @@ -76,31 +78,6 @@ describe('Editor Container Tests', () => {
expect(editorDiv.props().onKeyDown).toBeDefined();
expect(editorDiv.props().children).toBeDefined();
});

describe('Frozen columns', () => {
const frozenProps = {
column: { ...fakeColumn, frozen: true },
left: 60,
top: 0,
scrollTop: 0,
scrollLeft: 250
};

it('should not subtract scrollLeft value from editors left position when column is frozen', () => {
const { shallowWrapper } = getComponent(frozenProps);
const editorDiv = shallowWrapper.find('div').at(0);
expect(editorDiv.props().style.transform).toBe('translate(60px, 0px)');
});

it('should subtract scrollLeft value from editors left position when column is not frozen', () => {
const unfrozenProps = { ...frozenProps };
unfrozenProps.column.frozen = false;

const { shallowWrapper } = getComponent(unfrozenProps);
const editorDiv = shallowWrapper.find('div').at(0);
expect(editorDiv.props().style.transform).toBe('translate(-190px, 0px)');
});
});
});

describe('Custom Editors', () => {
Expand Down Expand Up @@ -189,12 +166,11 @@ describe('Editor Container Tests', () => {
let container;

beforeEach(() => {
defaultProps.onCommit.calls.reset();
defaultProps.onCommitCancel.calls.reset();
container = document.createElement('div');
document.body.appendChild(container);
component = mount(<EditorContainer
onCommit={jasmine.createSpy()}
onCommitCancel={jasmine.createSpy()}
{ ...defaultProps }/>, { attachTo: container });
component = mount(<EditorContainer { ...defaultProps }/>, { attachTo: container });
});

afterEach(() => {
Expand All @@ -214,6 +190,7 @@ describe('Editor Container Tests', () => {
const editor = component.find(SimpleTextEditor);

editor.simulate('keydown', { key: 'Escape' });
component.detach();

expect(component.props().onCommitCancel).toHaveBeenCalled();
expect(component.props().onCommitCancel.calls.count()).toEqual(1);
Expand Down
4 changes: 2 additions & 2 deletions packages/react-data-grid-addons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"react-data-grid": "^5.0.4"
},
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0"
"react": "^16.0.0",
"react-dom": "^16.0.0"
}
}
4 changes: 2 additions & 2 deletions packages/react-data-grid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"author": "Adazzle",
"license": "MIT",
"peerDependencies": {
"react": "^15.0.0 || ^16.0.0",
"react-dom": "^15.0.0 || ^16.0.0"
"react": "^16.0.0",
"react-dom": "^16.0.0"
}
}
Loading

0 comments on commit 264045f

Please sign in to comment.