AG-420 Improve React implementation

This commit is contained in:
Sean Landsman
2017-06-06 08:52:37 +01:00
parent ef44ebc166
commit 7564f1597a
43 changed files with 358 additions and 4 deletions

View File

@@ -65,10 +65,10 @@ class TopMoversGrid extends Component {
// properties
columnDefs={this.state.columnDefs}
rowData={this.props.rowData}
enableSorting="true"
enableSorting
enableFilter="false"
animateRows="true"
enableImmutableMode="true"
animateRows
deltaRowDataMode
getRowNodeId={this.getRowNodeId}
// events

View File

@@ -11,6 +11,7 @@ import FullWidthComponentExample from "./fullWidthExample/FullWidthComponentExam
import GroupedRowInnerRendererComponentExample from "./groupedRowInnerRendererExample/GroupedRowInnerRendererComponentExample";
import FilterComponentExample from "./filterComponentExample/FilterComponentExample";
import MasterDetailExample from "./masterDetailExample/MasterDetailExample";
import SimpleReduxExample from "./simpleReduxExample/SimpleReduxExample";
class App extends Component {
constructor(props) {
@@ -49,6 +50,7 @@ class App extends Component {
<li role="presentation" className={this.state.example === 'group-row' ? 'active' : null} onClick={() => this.setExample("group-row")}><a href="#">Grouped Row Inner Renderer Example</a></li>
<li role="presentation" className={this.state.example === 'filter' ? 'active' : null} onClick={() => this.setExample("filter")}><a href="#">Filters Component Example</a></li>
<li role="presentation" className={this.state.example === 'master-detail' ? 'active' : null} onClick={() => this.setExample("master-detail")}><a href="#">Master Detail Example</a></li>
<li role="presentation" className={this.state.example === 'simple-redux' ? 'active' : null} onClick={() => this.setExample("simple-redux")}><a href="#">Simple Redux Example</a></li>
</ul>)
}
@@ -78,6 +80,9 @@ class App extends Component {
case 'master-detail':
example = <MasterDetailExample/>;
break;
case 'simple-redux':
example = <SimpleReduxExample/>;
break;
default:
example = <RichGridExample/>;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 739 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 883 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 902 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 953 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 B

View File

@@ -67,7 +67,7 @@ export default class FilterComponentExample extends Component {
return (
<div style={{height: 400, width: 945}}
className="ag-fresh">
<h1>Group Row Renderer Example</h1>
<h1>Filter Component Example</h1>
<button style={{marginBottom: 10}} onClick={this.onClicked}>Filter Instance Method</button>
<AgGridReact
// properties

View File

@@ -0,0 +1,106 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import {connect} from "react-redux";
// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";
import {updateRowSelection} from "./gridDataActions";
/*
* This component serves to display the row data (provided by redux)
*/
class GridComponent extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: [
{headerName: 'Symbol', field: 'symbol'},
{headerName: 'Price', field: 'price'},
{headerName: 'Group', field: 'group'}
]
};
this.onGridReady = this.onGridReady.bind(this);
this.onSelectionChanged = this.onSelectionChanged.bind(this);
this.setGroupingEnabled = this.setGroupingEnabled.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
// set the initial group state
this.setGroupingEnabled(false);
}
// on selection publish selected row ids
onSelectionChanged() {
let selectedRowNodes = this.gridApi.getSelectedNodes();
let selectedIds = selectedRowNodes.map((rowNode) => rowNode.id);
this.props.dispatch(updateRowSelection(selectedIds));
}
setGroupingEnabled(enabled) {
if (enabled) {
this.columnApi.addRowGroupColumn('group');
this.columnApi.setColumnVisible('group', false);
this.columnApi.setColumnVisible('symbol', false);
} else {
this.columnApi.removeRowGroupColumn('group');
this.columnApi.setColumnVisible('group', true);
this.columnApi.setColumnVisible('symbol', true);
}
}
// row data will be provided via redux on this.props.rowData
// we bind to this and using "deltaRowDataMode" the grid will only re-render rows that have changed
// this requires each row to have a uniquely identifying property - in this case the row data "symbol" (see getRowNodeId)
render() {
return (
<div style={{height: 400, width: 945, marginTop: 15}}
className="ag-fresh">
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.props.rowData}
deltaRowDataMode
enableStatusBar
animateRows
enableColResize
rowSelection="multiple"
enableRangeSelection
groupColumnDef={{
headerName: 'Symbol',
cellRenderer: 'group',
field: 'symbol'
}}
groupDefaultExpanded="1"
enableSorting
getRowNodeId={(data) => data.symbol}
// events
onGridReady={this.onGridReady}
onSelectionChanged={this.onSelectionChanged}>
</AgGridReact>
</div>
)
}
}
// pull off row data changes
export default connect(
(state) => {
return {
rowData: state.rowData
}
},
null,
null,
{withRef: true}
)(GridComponent);

View File

@@ -0,0 +1,174 @@
import React, {Component} from "react";
import {connect} from "react-redux";
// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";
import {updateRowData} from "./gridDataActions";
/*
* This component serves both to host the demo controls, which in turn will drive row data state changes
*/
class HeaderComponent extends Component {
constructor(props) {
super(props);
this.addFiveItems = this.addFiveItems.bind(this);
this.removeSelected = this.removeSelected.bind(this);
this.updatePrices = this.updatePrices.bind(this);
this.setGroupingEnabled = this.setGroupingEnabled.bind(this);
this.setItemVisible = this.setItemVisible.bind(this);
this.setSelectedToGroup = this.setSelectedToGroup.bind(this);
}
componentDidMount() {
// provide the initial data to the store (which in turn will populate the grid)
this.props.dispatch(updateRowData(this.createRowData()));
}
// add five new items to the row data and publish the change
addFiveItems() {
let newRowData = this.props.rowData.slice();
for (let i = 0; i < 5; i++) {
let newItem = this.createItem();
newRowData.push(newItem);
}
this.props.dispatch(updateRowData(newRowData));
}
// remove selected rows from the data set and publish the change
removeSelected() {
let newRowData = this.props.rowData.filter((dataItem) => (this.props.rowSelection.indexOf(dataItem.symbol) < 0));
this.props.dispatch(updateRowData(newRowData));
}
// group data based on selection and publish the change
setSelectedToGroup(newGroup) {
let selectedIds = this.props.rowSelection;
let newRowData = this.props.rowData.map((dataItem) => {
let itemSelected = selectedIds.indexOf(dataItem.symbol) >= 0;
if (itemSelected) {
return {
// symbol and price stay the same
symbol: dataItem.symbol,
price: dataItem.price,
// group gets the group
group: newGroup
};
} else {
return dataItem;
}
});
this.props.dispatch(updateRowData(newRowData));
}
// randomly update prices in the row data and publish the change
updatePrices() {
let newRowData = [];
this.props.rowData.forEach(function (item) {
newRowData.push({
// use same symbol as last time, this is the unique id
symbol: item.symbol,
// group also stays the same
group: item.group,
// add random price
price: Math.floor(Math.random() * 100)
});
});
this.props.dispatch(updateRowData(newRowData));
}
setGroupingEnabled(enabled) {
// let the parent (and the grid in turn) know about the grouping state change
this.props.setGroupingEnabled(enabled);
// toggle the grouping buttons visibility
this.setItemVisible('groupingOn', !enabled);
this.setItemVisible('groupingOff', enabled);
}
setItemVisible(id, visible) {
let element = document.querySelector('#' + id);
element.style.display = visible ? null : 'none';
}
render() {
return (
<div style={{marginTop: 15}}>
<button onClick={this.addFiveItems}>Add Five Items</button>
<button onClick={this.removeSelected}>Remove Selected</button>
<button onClick={this.updatePrices}>Update Prices</button>
<span style={{padding: 10}}/>
<button id="groupingOn" onClick={() => this.setGroupingEnabled(true)}>Turn Grouping On</button>
<button id="groupingOff" style={{display: "none"}} onClick={() => this.setGroupingEnabled(false)}>Turn
Grouping Off
</button>
<span style={{padding: 10}}/>
<span style={{border: "1px solid lightgrey", padding: 4}}>
Group Selected:
<button onClick={() => this.setSelectedToGroup('A')}>A</button>
<button onClick={() => this.setSelectedToGroup('B')}>B</button>
<button onClick={() => this.setSelectedToGroup('C')}>C</button>
</span>
</div>
)
}
// the following methods are for creating dummy row data
createRowData() {
let rowData = [];
for (let i = 0; i < 14; i++) {
let newItem = this.createItem();
rowData.push(newItem);
}
return rowData;
}
createItem() {
return {
group: 'A',
symbol: this.createUniqueRandomSymbol(),
price: Math.floor(Math.random() * 100)
};
}
// creates a unique symbol, eg 'ADG' or 'ZJD'
createUniqueRandomSymbol() {
let symbol;
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let isUnique = false;
while (!isUnique) {
symbol = '';
// create symbol
for (let i = 0; i < 3; i++) {
symbol += possible.charAt(Math.floor(Math.random() * possible.length));
}
// check uniqueness
isUnique = true;
this.props.rowData.forEach(function (oldItem) {
if (oldItem.symbol === symbol) {
isUnique = false;
}
});
}
return symbol;
}
}
// pull off row data and selected row changes
export default connect(
(state) => {
return {
rowData: state.rowData,
rowSelection: state.rowSelection
}
}
)(HeaderComponent);

View File

@@ -0,0 +1,40 @@
import React, {Component} from "react";
import {Provider} from "react-redux";
import {createStore} from "redux";
// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";
import HeaderComponent from "./HeaderComponent";
import GridComponent from "./GridComponent";
import gridData from "./gridDataReducer";
let store = createStore(gridData);
/*
* This component serves as a container for both the header and grid components. It's primarily here to act as a container
* for the redux Provider
*/
export default class SimpleReduxExample extends Component {
constructor(props) {
super(props);
this.setGroupingEnabled = this.setGroupingEnabled.bind(this);
}
setGroupingEnabled(enabled) {
this.grid.setGroupingEnabled(enabled);
}
render() {
return (
<Provider store={store}>
<div>
<h1>Simple Redux Example using ag-Grid's deltaRowMode</h1>
<HeaderComponent setGroupingEnabled={this.setGroupingEnabled} />
<GridComponent ref={ grid => { this.grid = grid ? grid.getWrappedInstance() : null }} />
</div>
</Provider>
)
}
};

View File

@@ -0,0 +1,13 @@
export function updateRowData(rowData) {
return {
type: 'ROW_DATA_CHANGED',
rowData
}
}
export function updateRowSelection(rowSelection) {
return {
type: 'ROW_SELECTION_CHANGED',
rowSelection
}
}

View File

@@ -0,0 +1,16 @@
export default (state = {rowData: [], rowSelection: []}, action) => {
switch (action.type) {
case 'ROW_DATA_CHANGED':
return {
...state,
rowData: action.rowData,
};
case 'ROW_SELECTION_CHANGED':
return {
...state,
rowSelection: action.rowSelection,
};
default:
return state;
}
};