Skip to content

Commit

Permalink
Context menu upgrade from v1.6.2 to v2.9.2 (#1081)
Browse files Browse the repository at this point in the history
* upgrade react context menu to v2.9

* add unit tests

* update unit test

* remove unecessary id set

* remove unnecessary function

* update migration doc
  • Loading branch information
qili26 authored and malonecj committed Feb 8, 2018
1 parent 594681a commit 31c9b1a
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 54 deletions.
7 changes: 6 additions & 1 deletion migrations/v2-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

### Differences
### 2.0.0
- Bundles `create-react-class` package with `react-data-grid`;
- Bundles `create-react-class` package with `react-data-grid`;
- react-contextmenu v1.6.2
- exports ContextMenuLayer

### 3.0.0
- <del>Does not bundle `create-react-class` package with `react-data-grid`. It is specified as a peer dependency now [1065](https://github.com/adazzle/react-data-grid/pull/1065);</del> ReactDataGrid is not longer dependent on `create-react-class` as all the components have been migrated to ES6 Classes ([#1078](https://github.com/adazzle/react-data-grid/pull/1078) and [#1094](https://github.com/adazzle/react-data-grid/pull/1094))
- react-contextmenu v2.9.2
- exports ContextMenuTrigger
- According to react-contextmenu, style css files are not included in react-contextmenu, need to refer to this link https://github.com/vkbansal/react-contextmenu/blob/master/docs/usage.md#styling

### Steps for a sucessfull migration
- <del>Install create-react-class package (```npm install create-react-class```)</del> This is no longer required
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"fs-extra": "^0.30.0",
"object-assign": "^2.0.0",
"prop-types": "^15.5.10",
"react-contextmenu": "1.6.2",
"react-contextmenu": "^2.9.2",
"react-dnd": "^2.1.4",
"react-dnd-html5-backend": "^2.1.2",
"react-input-autosize": "1.1.0",
Expand Down
5 changes: 2 additions & 3 deletions packages/react-data-grid-addons/src/menu/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {MenuItem, monitor, SubMenu, connect, ContextMenuLayer} from 'react-contextmenu';
import ContextMenu from './ContextMenu';
import {ContextMenu, MenuItem, monitor, SubMenu, connect, ContextMenuTrigger} from 'react-contextmenu';
import MenuHeader from './MenuHeader';

export {ContextMenu, MenuHeader, MenuItem, monitor, SubMenu, connect, ContextMenuLayer};
export {ContextMenu, MenuHeader, MenuItem, monitor, SubMenu, connect, ContextMenuTrigger};
Original file line number Diff line number Diff line change
@@ -1,47 +1,78 @@
.react-context-menu {
.react-contextmenu {
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
font-size: 14px;
font-size: 16px;
color: #373a3c;
text-align: left;
background-color: #fff;
-webkit-background-clip: padding-box;
background-clip: padding-box;
border: 1px solid rgba(0,0,0,.15);
border-radius: .25rem;
outline: none;
opacity: 0;
pointer-events: none;
transition: opacity 250ms ease !important;
}

.react-context-menu-link {
display: inline-block;
width: 100%;
.react-contextmenu.react-contextmenu--visible {
opacity: 1;
pointer-events: auto;
}

.react-contextmenu-item {
padding: 3px 20px;
clear: both;
font-weight: 400;
line-height: 1.5;
color: #373a3c;
text-align: inherit;
white-space: nowrap;
background: 0 0;
border: 0;
cursor: pointer;
}

.react-context-menu-link.active,
.react-context-menu-link:hover {
.react-contextmenu-item.react-contextmenu-item--active,
.react-contextmenu-item.react-contextmenu-item--selected {
color: #fff;
background-color: #0275d8;
border-color: #0275d8;
background-color: #20a0ff;
border-color: #20a0ff;
text-decoration: none;
}

.react-context-menu-item.submenu > a {
padding-right: 27px;
.react-contextmenu-item.react-contextmenu-item--disabled,
.react-contextmenu-item.react-contextmenu-item--disabled:hover {
color: #878a8c;
background-color: transparent;
border-color: rgba(0,0,0,.15);
}

.react-contextmenu-item--divider {
margin-bottom: 3px;
padding: 2px 0;
border-bottom: 1px solid rgba(0,0,0,.15);
cursor: inherit;
}
.react-contextmenu-item--divider:hover {
background-color: transparent;
border-color: rgba(0,0,0,.15);
}

.react-context-menu-item.submenu > a:after {
.react-contextmenu-item.react-contextmenu-submenu {
padding: 0;
}

.react-contextmenu-item.react-contextmenu-submenu > .react-contextmenu-item {
}

.react-contextmenu-item.react-contextmenu-submenu > .react-contextmenu-item:after {
content: "▶";
display: inline-block;
position: absolute;
right: 7px;
}

.example-multiple-targets::after {
content: attr(data-count);
display: block;
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Example extends React.Component {
render() {
return (
<ReactDataGrid
contextMenu={<MyContextMenu onRowDelete={this.deleteRow} onRowInsertAbove={this.insertRowAbove} onRowInsertBelow={this.insertRowBelow} />}
contextMenu={<MyContextMenu id="customizedContextMenu" onRowDelete={this.deleteRow} onRowInsertAbove={this.insertRowAbove} onRowInsertBelow={this.insertRowBelow} />}
columns={this._columns}
rowGetter={this.rowGetter}
rowsCount={this.state.rows.length}
Expand All @@ -76,7 +76,8 @@ class MyContextMenu extends React.Component {
onRowInsertAbove: PropTypes.func.isRequired,
onRowInsertBelow: PropTypes.func.isRequired,
rowIdx: PropTypes.string.isRequired,
idx: PropTypes.string.isRequired
idx: PropTypes.string.isRequired,
id: PropTypes.string.isRequired
};

onRowDelete = (e, data) => {
Expand All @@ -98,12 +99,14 @@ class MyContextMenu extends React.Component {
};

render() {
const { idx, id, rowIdx } = this.props;

return (
<ContextMenu>
<MenuItem data={{rowIdx: this.props.rowIdx, idx: this.props.idx}} onClick={this.onRowDelete}>Delete Row</MenuItem>
<ContextMenu id={id}>
<MenuItem data={{ rowIdx, idx }} onClick={this.onRowDelete}>Delete Row</MenuItem>
<SubMenu title="Insert Row">
<MenuItem data={{rowIdx: this.props.rowIdx, idx: this.props.idx}} onClick={this.onRowInsertAbove}>Above</MenuItem>
<MenuItem data={{rowIdx: this.props.rowIdx, idx: this.props.idx}} onClick={this.onRowInsertBelow}>Below</MenuItem>
<MenuItem data={{ rowIdx, idx }} onClick={this.onRowInsertAbove}>Above</MenuItem>
<MenuItem data={{ rowIdx, idx }} onClick={this.onRowInsertBelow}>Below</MenuItem>
</SubMenu>
</ContextMenu>
);
Expand Down
49 changes: 27 additions & 22 deletions packages/react-data-grid/src/RowsContainer.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';

const SimpleRowsContainer = (props) => {
return (
<div key="rows-container">
{props.rows}
</div>
);
};
export const DEFAULT_CONTEXT_MENU_ID = 'rgdContextMenu';

const SimpleRowsContainer = (props) => <div key="rows-container">{props.rows}</div>;

SimpleRowsContainer.propTypes = {
width: PropTypes.number,
rows: PropTypes.array
};

export const getNewContextMenuProps = ({ contextMenu, rowIdx, idx }) => ({
rowIdx, idx, id: contextMenu.props.id || DEFAULT_CONTEXT_MENU_ID
});

class RowsContainer extends React.Component {
constructor(props) {
super(props);
this.plugins = props.window ? props.window.ReactDataGridPlugins : window.ReactDataGridPlugins;
this.hasContextMenu = this.hasContextMenu.bind(this);
this.renderRowsWithContextMenu = this.renderRowsWithContextMenu.bind(this);
this.getContextMenuContainer = this.getContextMenuContainer.bind(this);
this.state = {ContextMenuContainer: this.getContextMenuContainer(props)};
}

getContextMenuContainer() {
if (this.hasContextMenu()) {
if (!this.plugins) {
throw new Error('You need to include ReactDataGrid UiPlugins in order to initialise context menu');
}
return this.plugins.Menu.ContextMenuLayer('reactDataGridContextMenu')(SimpleRowsContainer);
validatePlugin() {
if (!this.plugins) {
throw new Error('You need to include ReactDataGrid UiPlugins in order to initialise context menu');
}
}

Expand All @@ -38,15 +31,27 @@ class RowsContainer extends React.Component {
}

renderRowsWithContextMenu() {
let ContextMenuRowsContainer = this.state.ContextMenuContainer;
let newProps = {rowIdx: this.props.rowIdx, idx: this.props.idx};
let contextMenu = React.cloneElement(this.props.contextMenu, newProps);
const { ContextMenuTrigger } = this.plugins.Menu;
const newProps = getNewContextMenuProps(this.props);
const contextMenu = React.cloneElement(this.props.contextMenu, newProps);
// Initialise the context menu if it is available
return (<div><ContextMenuRowsContainer {...this.props} />{contextMenu}</div>);
return (
<div>
<ContextMenuTrigger id={newProps.id}>
<SimpleRowsContainer {...this.props} />
</ContextMenuTrigger>
{contextMenu}
</div>
);
}

render() {
return this.hasContextMenu() ? this.renderRowsWithContextMenu() : <SimpleRowsContainer {...this.props} />;
if (this.hasContextMenu()) {
this.validatePlugin();
return this.renderRowsWithContextMenu();
}

return <SimpleRowsContainer {...this.props} />;
}
}

Expand Down
59 changes: 52 additions & 7 deletions packages/react-data-grid/src/__tests__/RowsContainer.spec.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,62 @@
import { shallow } from 'enzyme';
import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import RowsContainer from '../RowsContainer';
import RowsContainer, { getNewContextMenuProps, SimpleRowsContainer, DEFAULT_CONTEXT_MENU_ID } from '../RowsContainer';

const FakeContextMenuTrigger = () => <div id="fakeContextMenuTrigger" />;

const FakeContextMenu = () => <div />;

const ReactDataGridPlugins = {
Menu: {
ContextMenuTrigger: FakeContextMenuTrigger
}
};

const contextMenuId = 'fakeContextMenu';
const props = {
contextMenu: <FakeContextMenu id={contextMenuId} />,
rowIdx: 5,
idx: 8,
window: { ReactDataGridPlugins },
rows: [
{ id: 'row_1' },
{ id: 'row_2' }
]
};

describe('Rows Container', () => {
describe('without context menu', () => {
let componentWithoutContextMenu = {};
describe('getNewContextMenuProps()', () => {
it('should populate correct newProps for contextMenu with customized menu id', () => {
const newProps = getNewContextMenuProps(props);
expect(newProps.id).toBe(contextMenuId);
});

beforeEach(() => {
componentWithoutContextMenu = ReactTestUtils.renderIntoDocument(<RowsContainer window={{ ReactDataGridPlugins: undefined }} />);
it('should populate correct newProps for contextMenu with default menu id', () => {
const newProps = getNewContextMenuProps(Object.assign({}, props, { contextMenu: <FakeContextMenu /> }));
expect(newProps.id).toBe(DEFAULT_CONTEXT_MENU_ID);
});
});

describe('with context menu', () => {
it('should create a new RowsContainer instance', () => {
expect(componentWithoutContextMenu).toBeDefined();
const wrapper = shallow(<RowsContainer {...props} />);
expect(wrapper.find(FakeContextMenuTrigger).length).toBe(1);
});

it('should throw exception for no context menu plugin when rendering', () => {
const newProp = Object.assign({}, props, { window: {}});
expect(() => { shallow(<RowsContainer {...newProp} />); }).toThrowError('You need to include ReactDataGrid UiPlugins in order to initialise context menu');
});
});

describe('without context menu', () => {
it('should create a SimpleRowsContainer', () => {
const newProps = Object.assign({}, props, {
contextMenu: undefined
});
const wrapper = shallow(<RowsContainer {...newProps}/>);
expect(wrapper.find(SimpleRowsContainer).length).toBe(1);
expect(wrapper.find(FakeContextMenuTrigger).length).toBe(0);
});
});
});

0 comments on commit 31c9b1a

Please sign in to comment.