Compare commits

...

42 Commits

Author SHA1 Message Date
Alberto
d2b92b4c9a Release 13.3.0 2017-10-02 14:55:11 +02:00
Sean Landsman
c8267730bf Update code to match latest changes 2017-09-28 13:56:46 +01:00
Alberto
3992e3e928 v 13.2.0 2017-09-19 10:17:51 +01:00
Alberto
8409183d6b v 13.2.0 2017-09-19 09:00:11 +01:00
Sean Landsman
7075f57d88 Tidy examples 2017-09-18 16:03:27 +01:00
Sean Landsman
ad6bc068b2 Improve react examples 2017-09-15 16:56:08 +01:00
seanlandsman
c4310c2768 Merge pull request #32 from unformatt/patch-1
Fixes typo in README
2017-09-13 14:35:08 +01:00
Alberto
2228a8d0c9 Merge branch 'master' into latest 2017-09-08 09:40:53 +01:00
Matt
966fa0fe69 Update README.md 2017-09-07 18:37:15 -04:00
Sean Landsman
9528d5a5bc Move per col icons to grid based 2017-09-04 10:39:17 +01:00
Alberto
3b5eaac21a Merge branch 'latest' into 13.1.0
# Conflicts:
#	aot/dist/agGridColumn.ngfactory.ts
#	aot/dist/agGridColumn.ngsummary.json
#	aot/dist/agGridNg2.ngfactory.ts
#	aot/dist/agGridNg2.ngsummary.json
#	aot/dist/aggrid.module.ngfactory.ts
#	aot/dist/aggrid.module.ngsummary.json
#	aot/dist/baseComponentFactory.ngsummary.json
#	aot/dist/interfaces.ngsummary.json
#	aot/dist/ng2ComponentFactory.ngsummary.json
#	aot/dist/ng2FrameworkComponentWrapper.ngsummary.json
#	aot/dist/ng2FrameworkFactory.ngsummary.json
#	aot/main.ngsummary.json
#	aot/src/agGridColumn.ngfactory.ts
#	aot/src/agGridColumn.ngsummary.json
#	aot/src/agGridNg2.ngfactory.ts
#	aot/src/agGridNg2.ngsummary.json
#	aot/src/aggrid.module.ngfactory.ts
#	aot/src/aggrid.module.ngsummary.json
#	aot/src/baseComponentFactory.ngsummary.json
#	aot/src/interfaces.ngsummary.json
#	aot/src/ng2ComponentFactory.ngsummary.json
#	aot/src/ng2FrameworkComponentWrapper.ngsummary.json
#	aot/src/ng2FrameworkFactory.ngsummary.json
2017-09-01 14:02:24 +01:00
Sean Landsman
73cc989512 AG-730 New example for redux 2017-08-31 17:17:26 +01:00
Alberto
5a45846cb7 Release 13.0.1 2017-08-29 15:27:33 +02:00
Petyo Ivanov
2d8d5b125e Removed legacy padding 2017-08-29 13:34:18 +03:00
Alberto
818b7798ee Merge branch 'latest' into 13.0.0
# Conflicts:
#	package.json
2017-08-25 16:59:25 +02:00
Sean Landsman
15ba8d7e60 Rename cellFormatter to valueFormatter 2017-08-23 14:04:13 +01:00
Alberto
eab958c3e9 Releasing 13.0.0 2017-08-23 11:35:39 +01:00
Sean Landsman
3a17b7f638 AG-668 Update all README.md to point to appropriate plunker/seed 2017-08-21 11:45:09 +01:00
Sean Landsman
734d39e2d7 AG-676 Move framework examples into parent folder for easier exclusion 2017-08-17 11:04:12 +01:00
Sean Landsman
0d7cace354 Add example for keypress in editors
https://github.com/ag-grid/ag-grid-react/issues/60
2017-08-10 16:02:03 +01:00
Sean Landsman
fa0881bfcf Merge remote-tracking branch 'origin/latest' into latest 2017-08-10 15:51:05 +01:00
Sean Landsman
62b9e3582a Add example for keypress in editors
https://github.com/ag-grid/ag-grid-react/issues/60
2017-08-10 15:50:54 +01:00
Petyo Ivanov
949797d4be rename repo from ceolter to ag-grid 2017-08-04 12:23:47 +01:00
Sean Landsman
a869fe3819 CI Work 2017-08-02 13:48:39 +01:00
Sean Landsman
c611af4f39 CI Work 2017-08-02 13:15:55 +01:00
Sean Landsman
7cab3190ae CI Work 2017-08-02 12:19:07 +01:00
Sean Landsman
71fe653fcb CI Work 2017-08-02 11:55:18 +01:00
Sean Landsman
ba1c213ce0 Change selectAll to use refs to expand on example 2017-07-27 08:17:15 +01:00
Sean Landsman
46217c9ad7 Fix sorting issue 2017-07-26 12:10:12 +01:00
Sean Landsman
017642e4d7 AG-643 Tidy React examples 2017-07-25 12:47:47 +01:00
Sean Landsman
79b0eda61b AG-643 Tidy React examples 2017-07-25 12:47:07 +01:00
Sean Landsman
a70f248af5 AG-642 Remove deprecation warning in preparation for 16.x 2017-07-25 12:46:21 +01:00
Alberto
32142b435b v12 Hot fixes 2017-07-21 12:10:00 +02:00
Alberto
d71781899d AG-618 Fixing floating filter issue. Also allowing for gulp watch of Angular and React 2017-07-20 13:59:16 +02:00
Sean Landsman
9560f86bf5 Updated readme 2017-07-19 10:30:28 +01:00
Alberto
9f4b270884 New release branch, everything compiles 2017-07-13 11:25:34 +01:00
John Masterson
e5627d8b9f Added links to ReadMe 2017-07-11 17:07:57 +01:00
Alberto
f8f0c21af2 Version 11.0.0 2017-06-26 16:40:27 +02:00
Sean Landsman
b54d549959 AG-528 Document and use in examples overriding of style in child (react component) divs 2017-06-14 15:32:09 +01:00
Sean Landsman
464a0ea9f4 AG-529 Implement improvements - esp around property changes 2017-06-14 13:15:54 +01:00
Sean Landsman
675cbe497c AG-538 Upgrade to React 15x
AG-539 React rich grid example - fix bugs
2017-06-13 18:15:05 +01:00
Sean Landsman
a4dbbf529d AG-534 Make use of new data retrieval & updating in trader dashboard 2017-06-13 15:17:30 +01:00
57 changed files with 1470 additions and 625 deletions

View File

@@ -4,7 +4,14 @@ ag-Grid React Example
Examples of running ag-Grid inside React application. Examples of running ag-Grid inside React application.
See the [www.ag-grid.com](http://www.ag-grid.com). See [www.ag-grid.com](http://www.ag-grid.com) for an overview and full documentation.
Frameworks Supported
====================
Framework specific Getting Started guides:
[Angular 1](https://www.ag-grid.com/best-angularjs-data-grid/) | [Angular 2](https://www.ag-grid.com/best-angular-2-data-grid/) | [Aurelia](https://www.ag-grid.com/best-aurelia-data-grid/)
[Javascript](https://www.ag-grid.com/best-javascript-data-grid/) | [React](https://www.ag-grid.com/best-react-data-grid/) | [TypeScript](https://www.ag-grid.com/ag-grid-typescript-webpack-2/)
[VueJS](https://www.ag-grid.com/best-vuejs-data-grid/) | [Web Components](https://www.ag-grid.com/best-web-component-data-grid/)
There are two examples: There are two examples:
@@ -18,5 +25,5 @@ Building
To build: To build:
- `npm install` - `npm install`
- `npm install webpack -g` - `npm install webpack -g`
- `npm run standard` or `npm run large` - `npm run examples`, `npm run large` or `npm run trader`
- navigate to localhost:8080 - navigate to localhost:8080

26
gulpfile.js Normal file
View File

@@ -0,0 +1,26 @@
const gulp = require('gulp');
const gulpTypescript = require('gulp-typescript');
const merge = require('merge2');
gulp.task('watch', watchTask);
gulp.task('rebuild-ag-grid-react', rebuildAgGridReact);
const tsConfig = '../ag-grid-react/tsconfig.json';
const tsProject = gulpTypescript.createProject(tsConfig);
function rebuildAgGridReact() {
const tsResult = gulp
.src('../ag-grid-react/src/**/*.ts')
.pipe(tsProject());
return merge([
tsResult.dts
.pipe(gulp.dest('node_modules/ag-grid-react/lib')),
tsResult.js
.pipe(gulp.dest('node_modules/ag-grid-react/lib'))
]);
}
function watchTask() {
gulp.watch(['../ag-grid-react/src/**/*'], rebuildAgGridReact);
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "ag-grid-react-example", "name": "ag-grid-react-example",
"version": "10.0.0", "version": "13.3.0",
"description": "Example Reach applicaiton using ag-Grid.", "description": "Example Reach applicaiton using ag-Grid.",
"main": "dist/ag-grid-react-example.js", "main": "dist/ag-grid-react-example.js",
"scripts": { "scripts": {
@@ -12,16 +12,18 @@
"copy-examples": "ncp images dist/examples/images && ncp src/index.html dist/examples/index.html && ncp dist/react-examples.js dist/examples/dist/react-examples.js && ncp src dist/examples/src", "copy-examples": "ncp images dist/examples/images && ncp src/index.html dist/examples/index.html && ncp dist/react-examples.js dist/examples/dist/react-examples.js && ncp src dist/examples/src",
"copy-trader": "ncp src-trader-dashboard/index.html dist/trader/index.html && ncp dist/react-trader.js dist/trader/dist/react-trader.js", "copy-trader": "ncp src-trader-dashboard/index.html dist/trader/index.html && ncp dist/react-trader.js dist/trader/dist/react-trader.js",
"copy": "npm run copy-examples && npm run copy-trader", "copy": "npm run copy-examples && npm run copy-trader",
"build-large": "webpack --config webpack.config.large.js --progress --profile --bail",
"build-examples": "webpack --config webpack.config.examples.js --progress --profile --bail", "build-examples": "webpack --config webpack.config.examples.js --progress --profile --bail",
"build-dashboard": "webpack --config webpack.config.trader.js --progress --profile --bail", "build-dashboard": "webpack --config webpack.config.trader.js --progress --profile --bail",
"build-all": "npm run build-examples && npm run build-dashboard", "build-all": "npm run build-examples && npm run build-dashboard",
"build": "npm run clean && npm run mkdirs && npm run build-all && npm run copy", "build": "npm run clean && npm run mkdirs && npm run build-all && npm run copy",
"copy-to-docs": "ncp dist/examples ../ag-grid-docs/src/react-examples/examples && ncp dist/trader ../ag-grid-docs/src/react-examples/trader", "copy-to-docs": "ncp dist/examples ../ag-grid-docs/src/framework-examples/react-examples/examples && ncp dist/trader ../ag-grid-docs/src/framework-examples/react-examples/trader",
"build-to-docs": "npm run build && npm run copy-to-docs" "build-to-docs": "npm run build && npm run copy-to-docs",
"start": "npm run examples"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/ceolter/ag-grid-react-example.git" "url": "https://github.com/ag-grid/ag-grid-react-example.git"
}, },
"keywords": [ "keywords": [
"react", "react",
@@ -32,7 +34,7 @@
"author": "Niall Crosby <niall.crosby@gmail.com>", "author": "Niall Crosby <niall.crosby@gmail.com>",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/ceolter/ag-grid-react-example/issues" "url": "https://github.com/ag-grid/ag-grid-react-example/issues"
}, },
"homepage": "http://www.ag-grid.com/", "homepage": "http://www.ag-grid.com/",
"devDependencies": { "devDependencies": {
@@ -45,25 +47,30 @@
"css-loader": "0.23.x", "css-loader": "0.23.x",
"mkdirp": "0.5.1", "mkdirp": "0.5.1",
"ncp": "2.0.0", "ncp": "2.0.0",
"prop-types": "15.5.x",
"rimraf": "2.5.x", "rimraf": "2.5.x",
"style-loader": "0.13.x", "style-loader": "0.13.x",
"webpack": "1.12.x", "webpack": "1.12.x",
"webpack-dev-server": "1.14.x" "webpack-dev-server": "1.14.x",
"gulp": "3.9.x",
"gulp-typescript": "3.1.x",
"merge2": "1.0.x",
"typescript": "2.3.x"
}, },
"dependencies": { "dependencies": {
"ag-grid": "13.3.x",
"ag-grid-enterprise": "13.3.x",
"ag-grid-react": "13.3.x",
"bootstrap": "3.3.7", "bootstrap": "3.3.7",
"d3": "4.9.1", "d3": "4.9.1",
"file-loader": "0.11.1", "file-loader": "0.11.1",
"lodash": "4.17.4", "lodash": "4.17.4",
"react": "15.5.x", "react": "15.6.x",
"react-dom": "15.5.x", "react-dom": "15.6.x",
"react-dom-factories": "1.0.0",
"react-redux": "5.0.x", "react-redux": "5.0.x",
"react-router-dom": "4.2.x",
"redux": "3.6.x", "redux": "3.6.x",
"url-search-params-polyfill": "1.2.0", "url-search-params-polyfill": "1.2.0"
"ag-grid": "10.1.x",
"ag-grid-enterprise": "10.1.x",
"ag-grid-react": "10.1.x"
} }
} }

View File

@@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import * as PropTypes from 'prop-types';
export default class SimpleCellRenderer extends React.Component { export default class SimpleCellRenderer extends React.Component {
render() { render() {
@@ -11,5 +12,5 @@ export default class SimpleCellRenderer extends React.Component {
} }
SimpleCellRenderer.propTypes = { SimpleCellRenderer.propTypes = {
params: React.PropTypes.object params: PropTypes.object
}; };

View File

@@ -16,6 +16,9 @@ class FxQuoteMatrix extends Component {
// grid events // grid events
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
// grid callbacks
this.getRowNodeId = this.getRowNodeId.bind(this);
} }
onGridReady(params) { onGridReady(params) {
@@ -27,34 +30,31 @@ class FxQuoteMatrix extends Component {
} }
} }
getRowNodeId(data) {
return data.symbol;
}
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
const newRowData = nextProps.rowData; const newRowData = nextProps.rowData;
const updatedNodes = []; const updatedRows = [];
const updatedCols = [];
for (let i = 0; i < newRowData.length; i++) { for (let i = 0; i < newRowData.length; i++) {
// note that for this use case we assume the existing and new row data have the same
// row and column order
let node = this.gridApi.getModel().getRow(i);
let newRow = newRowData[i]; let newRow = newRowData[i];
let currentRowNode = this.gridApi.getRowNode(newRow.symbol);
const {data} = node; const {data} = currentRowNode;
let updated = false;
for (const def of this.state.columnDefs) { for (const def of this.state.columnDefs) {
if (data[def.field] !== newRow[def.field]) { if (data[def.field] !== newRow[def.field]) {
updatedCols.push(def.field); updatedRows.push(newRow);
break;
updated = true;
} }
} }
if (updated) {
assign(data, newRow);
updatedNodes.push(node);
}
} }
this.gridApi.refreshCells(updatedNodes, uniq(updatedCols));
this.gridApi.updateRowData({update: updatedRows});
} }
render() { render() {
@@ -67,6 +67,9 @@ class FxQuoteMatrix extends Component {
enableSorting="false" enableSorting="false"
enableFilter="false" enableFilter="false"
// callbacks
getRowNodeId={this.getRowNodeId}
// events // events
onGridReady={this.onGridReady}> onGridReady={this.onGridReady}>
</AgGridReact> </AgGridReact>

View File

@@ -6,7 +6,6 @@ import map from "lodash/map";
import difference from "lodash/difference"; import difference from "lodash/difference";
import forEach from "lodash/forEach"; import forEach from "lodash/forEach";
import includes from "lodash/includes"; import includes from "lodash/includes";
import assign from "lodash/assign";
import ExchangeService from "../services/ExchangeService.jsx"; import ExchangeService from "../services/ExchangeService.jsx";
@@ -24,21 +23,21 @@ export default class extends Component {
{ {
field: 'price', field: 'price',
headerName: 'Price', headerName: 'Price',
cellFormatter: this.numberFormatter, valueFormatter: this.numberFormatter,
cellRenderer: 'animateShowChange', cellRenderer: 'animateShowChange',
cellStyle: {'text-align': 'right'} cellStyle: {'text-align': 'right'}
}, },
{ {
field: 'bid', field: 'bid',
headerName: 'Bid', headerName: 'Bid',
cellFormatter: this.numberFormatter, valueFormatter: this.numberFormatter,
cellRenderer: 'animateShowChange', cellRenderer: 'animateShowChange',
cellStyle: {'text-align': 'right'} cellStyle: {'text-align': 'right'}
}, },
{ {
field: 'ask', field: 'ask',
headerName: 'Ask', headerName: 'Ask',
cellFormatter: this.numberFormatter, valueFormatter: this.numberFormatter,
cellRenderer: 'animateShowChange', cellRenderer: 'animateShowChange',
cellStyle: {'text-align': 'right'} cellStyle: {'text-align': 'right'}
} }
@@ -51,6 +50,9 @@ export default class extends Component {
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
this.onSelectionChanged = this.onSelectionChanged.bind(this); this.onSelectionChanged = this.onSelectionChanged.bind(this);
// grid callbacks
this.getRowNodeId = this.getRowNodeId.bind(this);
// component events // component events
this.updateSymbol = this.updateSymbol.bind(this); this.updateSymbol = this.updateSymbol.bind(this);
} }
@@ -69,7 +71,7 @@ export default class extends Component {
// make realistic - call in a batch // make realistic - call in a batch
let rowData = map(this.props.selectedExchange.supportedStocks, symbol => this.exchangeService.getTicker(symbol)); let rowData = map(this.props.selectedExchange.supportedStocks, symbol => this.exchangeService.getTicker(symbol));
this.gridApi.addItems(rowData); this.gridApi.updateRowData({add: rowData});
// select the first symbol to show the chart // select the first symbol to show the chart
this.gridApi.getModel().getRow(0).setSelected(true); this.gridApi.getModel().getRow(0).setSelected(true);
@@ -77,6 +79,10 @@ export default class extends Component {
this.gridApi.sizeColumnsToFit(); this.gridApi.sizeColumnsToFit();
} }
getRowNodeId(data) {
return data.symbol;
}
onSelectionChanged() { onSelectionChanged() {
let selectedNode = this.gridApi.getSelectedNodes()[0]; let selectedNode = this.gridApi.getSelectedNodes()[0];
this.props.onSelectionChanged(selectedNode ? selectedNode.data.symbol : null); this.props.onSelectionChanged(selectedNode ? selectedNode.data.symbol : null);
@@ -108,25 +114,25 @@ export default class extends Component {
this.exchangeService.removeSubscriber(this.updateSymbol, symbol); this.exchangeService.removeSubscriber(this.updateSymbol, symbol);
}); });
// Remove ag-grid nodes as necessary
const rowsToRemove = [];
this.gridApi.forEachNode(node => {
const {data} = node;
if (includes(symbolsRemoved, data.symbol)) {
rowsToRemove.push(data);
}
});
this.gridApi.updateRowData({remove: rowsToRemove});
// Subscribe to new ones that need to be added // Subscribe to new ones that need to be added
const symbolsAdded = difference(nextSymbols, currentSymbols); const symbolsAdded = difference(nextSymbols, currentSymbols);
forEach(symbolsAdded, symbol => { forEach(symbolsAdded, symbol => {
this.exchangeService.addSubscriber(this.updateSymbol, symbol); this.exchangeService.addSubscriber(this.updateSymbol, symbol);
}); });
// Remove ag-grid nodes as necessary
const nodesToRemove = [];
this.gridApi.forEachNode(node => {
const {data} = node;
if (includes(symbolsRemoved, data.symbol)) {
nodesToRemove.push(node);
}
});
this.gridApi.removeItems(nodesToRemove);
// Insert new ag-grid nodes as necessary // Insert new ag-grid nodes as necessary
let rowData = map(symbolsAdded, symbol => this.exchangeService.getTicker(symbol)); let rowData = map(symbolsAdded, symbol => this.exchangeService.getTicker(symbol));
this.gridApi.addItems(rowData); this.gridApi.updateRowData({add: rowData});
// select the first symbol to show the chart // select the first symbol to show the chart
this.gridApi.getModel().getRow(0).setSelected(true); this.gridApi.getModel().getRow(0).setSelected(true);
@@ -139,24 +145,8 @@ export default class extends Component {
return; return;
} }
const updatedNodes = []; this.gridApi.updateRowData({update: [symbol]});
const updatedCols = [];
this.gridApi.forEachNode(node => {
const {data} = node;
if (data.symbol === symbol.symbol) {
for (const def of this.state.columnDefs) {
if (data[def.field] !== symbol[def.field]) {
updatedCols.push(def.field);
} }
}
assign(data, symbol);
updatedNodes.push(node);
}
});
this.gridApi.refreshCells(updatedNodes, updatedCols);
};
render() { render() {
return ( return (
@@ -168,6 +158,9 @@ export default class extends Component {
enableSorting="true" enableSorting="true"
rowSelection="single" rowSelection="single"
// callbacks
getRowNodeId={this.getRowNodeId}
// events // events
onGridReady={this.onGridReady} onGridReady={this.onGridReady}
onSelectionChanged={this.onSelectionChanged}> onSelectionChanged={this.onSelectionChanged}>

View File

@@ -34,7 +34,7 @@ class TopMoversGrid extends Component {
cellRenderer: 'animateShowChange', cellRenderer: 'animateShowChange',
cellClass: 'align-right', cellClass: 'align-right',
sort: 'desc', sort: 'desc',
cellFormatter(params) { valueFormatter(params) {
return params.value.toFixed(2) return params.value.toFixed(2)
} }
}, },
@@ -43,6 +43,8 @@ class TopMoversGrid extends Component {
// grid events // grid events
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
// grid callbacks
this.getRowNodeId = this.getRowNodeId.bind(this); this.getRowNodeId = this.getRowNodeId.bind(this);
} }

View File

@@ -1,7 +1,16 @@
import React, {Component} from "react"; import React, {Component} from "react";
import * as PropTypes from 'prop-types';
export default class HorizontalBarComponent extends Component { export default class HorizontalBarComponent extends Component {
constructor(props) {
super(props);
this.state = {
value: this.props.value
}
}
render() { render() {
let positiveChange = { let positiveChange = {
fill: "green" fill: "green"
@@ -18,7 +27,7 @@ export default class HorizontalBarComponent extends Component {
textAlign: "right" textAlign: "right"
}; };
let pctNetChange = this.props.value; let pctNetChange = this.state.value;
let pctNetChangeBar = Math.min(Math.abs(pctNetChange) * 100, 100) / 2; let pctNetChangeBar = Math.min(Math.abs(pctNetChange) * 100, 100) / 2;
let barWidth = `${pctNetChangeBar}%`; let barWidth = `${pctNetChangeBar}%`;
@@ -35,8 +44,15 @@ export default class HorizontalBarComponent extends Component {
</div> </div>
) )
} }
refresh(params) {
this.setState({
value: params.value
});
return true;
}
} }
HorizontalBarComponent.propTypes = { HorizontalBarComponent.propTypes = {
params: React.PropTypes.object params: PropTypes.object
}; };

View File

@@ -1,5 +1,4 @@
import concat from "lodash/concat"; import concat from "lodash/concat";
import remove from "lodash/remove";
import uniq from "lodash/uniq"; import uniq from "lodash/uniq";
import find from "lodash/find"; import find from "lodash/find";
import sampleSize from "lodash/sampleSize"; import sampleSize from "lodash/sampleSize";
@@ -26,7 +25,12 @@ export default class {
} }
removeSubscriber(subscriber, symbol) { removeSubscriber(subscriber, symbol) {
remove(this.subscribers[symbol], subscriber); let subscribers = this.subscribers[symbol];
subscribers.splice(subscribers.indexOf(subscriber), 1);
}
removeSubscribers() {
this.subscribers = {};
} }
getTicker(symbol) { getTicker(symbol) {

View File

@@ -1,18 +1,70 @@
import React, {Component} from "react"; import React, {Component} from "react";
import {Redirect, Route, Switch} from "react-router-dom";
import "url-search-params-polyfill"; import NavItem from "./NavItem";
import DynamicComponentsExample from "./dynamicComponentExample/DynamicComponentsExample"; import DynamicComponentsExample from "./dynamicComponentExample/DynamicComponentsExample";
import RichGridExample from "./richGridExample/RichGridExample"; import RichGridExample from "./richGridExample/RichGridExample";
import RichComponentsExample from "./richComponentExample/RichComponentsExample"; import RichComponentsExample from "./richComponentExample/RichComponentsExample";
import EditorComponentsExample from "./editorComponentExample/EditorComponentsExample"; import EditorComponentsExample from "./editorComponentExample/EditorComponentsExample";
import FloatingRowComponentExample from "./floatingRowExample/FloatingRowComponentExample"; import PinnedRowComponentExample from "./pinnedRowExample/PinnedRowComponentExample";
import FullWidthComponentExample from "./fullWidthExample/FullWidthComponentExample"; import FullWidthComponentExample from "./fullWidthExample/FullWidthComponentExample";
import GroupedRowInnerRendererComponentExample from "./groupedRowInnerRendererExample/GroupedRowInnerRendererComponentExample"; import GroupedRowInnerRendererComponentExample from "./groupedRowInnerRendererExample/GroupedRowInnerRendererComponentExample";
import FilterComponentExample from "./filterComponentExample/FilterComponentExample"; import FilterComponentExample from "./filterComponentExample/FilterComponentExample";
import MasterDetailExample from "./masterDetailExample/MasterDetailExample"; import MasterDetailExample from "./masterDetailExample/MasterDetailExample";
import SimpleReduxExample from "./simpleReduxExample/SimpleReduxExample"; import SimpleReduxExample from "./simpleReduxExample/SimpleReduxExample";
import FloatingFilterGridExample from "./floatingFilter/FloatingFilterGridExample";
import SimpleReduxDynamicExample from "./simpleReduxDynamicComponentExample/SimpleReduxExample";
const SideBar = () => (
<div style={{float: "left", width: 335, marginRight: 25}}>
<ul className="nav nav-pills nav-stacked">
<NavItem to='/rich-grid'>Rich Grid Example</NavItem>
<NavItem to='/dynamic'>Dynamic React Component Example</NavItem>
<NavItem to='/rich-dynamic'>Dynamic React Components - Richer Example</NavItem>
<NavItem to='/editor'>Cell Editor Component Example</NavItem>
<NavItem to='/floating-row'>Floating Row Renderer Example</NavItem>
<NavItem to='/full-width'>Full Width Renderer Example</NavItem>
<NavItem to='/group-row'>Grouped Row Inner Renderer Example</NavItem>
<NavItem to='/filter'>Filters Component Example</NavItem>
<NavItem to='/master-detail'>Master Detail Example</NavItem>
<NavItem to='/floating-filter'>Floating Filters</NavItem>
<NavItem to='/simple-redux'>Simple Redux Example</NavItem>
<NavItem to='/simple-redux-dynamic'>Simple Redux Dynamic Component Example</NavItem>
</ul>
</div>
);
class App extends Component {
render() {
return (
<div style={{display: "inline-block", width: "100%"}}>
<SideBar/>
<div style={{float: "left"}}>
<Switch>
<Redirect from="/" exact to="/rich-grid"/>
<Route exact path='/rich-grid' component={RichGridExample}/>
<Route exact path='/dynamic' component={DynamicComponentsExample}/>
<Route exact path='/rich-dynamic' component={RichComponentsExample}/>
<Route exact path='/editor' component={EditorComponentsExample}/>
<Route exact path='/floating-row' component={PinnedRowComponentExample}/>
<Route exact path='/full-width' component={FullWidthComponentExample}/>
<Route exact path='/group-row' component={GroupedRowInnerRendererComponentExample}/>
<Route exact path='/filter' component={FilterComponentExample}/>
<Route exact path='/master-detail' component={MasterDetailExample}/>
<Route exact path='/floating-filter' component={FloatingFilterGridExample}/>
<Route exact path='/simple-redux' component={SimpleReduxExample}/>
<Route exact path='/simple-redux-dynamic' component={SimpleReduxDynamicExample}/>
</Switch>
</div>
</div>
)
}
}
export default App
/*
class App extends Component { class App extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -30,7 +82,6 @@ class App extends Component {
} }
setExample(example) { setExample(example) {
console.log(example);
this.setState({ this.setState({
example example
}) })
@@ -45,12 +96,14 @@ class App extends Component {
<li role="presentation" className={this.state.example === 'dynamic' ? 'active' : null} onClick={() => this.setExample("dynamic")}><a href="#">Dynamic React Component Example</a></li> <li role="presentation" className={this.state.example === 'dynamic' ? 'active' : null} onClick={() => this.setExample("dynamic")}><a href="#">Dynamic React Component Example</a></li>
<li role="presentation" className={this.state.example === 'rich-dynamic' ? 'active' : null} onClick={() => this.setExample("rich-dynamic")}><a href="#">Dynamic React Components - Richer Example</a></li> <li role="presentation" className={this.state.example === 'rich-dynamic' ? 'active' : null} onClick={() => this.setExample("rich-dynamic")}><a href="#">Dynamic React Components - Richer Example</a></li>
<li role="presentation" className={this.state.example === 'editor' ? 'active' : null} onClick={() => this.setExample("editor")}><a href="#">Cell Editor Component Example</a></li> <li role="presentation" className={this.state.example === 'editor' ? 'active' : null} onClick={() => this.setExample("editor")}><a href="#">Cell Editor Component Example</a></li>
<li role="presentation" className={this.state.example === 'floating-row' ? 'active' : null} onClick={() => this.setExample("floating-row")}><a href="#">Floating Row Renderer Example</a></li> <li role="presentation" className={this.state.example === 'pinned-row' ? 'active' : null} onClick={() => this.setExample("pinned-row")}><a href="#">Pinned Row Renderer Example</a></li>
<li role="presentation" className={this.state.example === 'full-width' ? 'active' : null} onClick={() => this.setExample("full-width")}><a href="#">Full Width Renderer Example</a></li> <li role="presentation" className={this.state.example === 'full-width' ? 'active' : null} onClick={() => this.setExample("full-width")}><a href="#">Full Width Renderer Example</a></li>
<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 === '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 === 'filter' ? 'active' : null} onClick={() => this.setExample("filter")}><a href="#">Filters Component Example</a></li>
<li role="presentation" className={this.state.example === 'floating-filter' ? 'active' : null} onClick={() => this.setExample("floating-filter")}><a href="#">Floating Filters</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 === '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> <li role="presentation" className={this.state.example === 'simple-redux' ? 'active' : null} onClick={() => this.setExample("simple-redux")}><a href="#">Simple Redux Example</a></li>
<li role="presentation" className={this.state.example === 'simple-redux-dynamic' ? 'active' : null} onClick={() => this.setExample("simple-redux-dynamic")}><a href="#">Simple Redux Dynamic Component Example</a></li>
</ul>) </ul>)
} }
@@ -65,8 +118,8 @@ class App extends Component {
case 'editor': case 'editor':
example = <EditorComponentsExample/>; example = <EditorComponentsExample/>;
break; break;
case 'floating-row': case 'pinned-row':
example = <FloatingRowComponentExample/>; example = <PinnedRowComponentExample/>;
break; break;
case 'full-width': case 'full-width':
example = <FullWidthComponentExample/>; example = <FullWidthComponentExample/>;
@@ -83,6 +136,12 @@ class App extends Component {
case 'simple-redux': case 'simple-redux':
example = <SimpleReduxExample/>; example = <SimpleReduxExample/>;
break; break;
case 'floating-filter':
example = <FloatingFilterGridExample/>;
break;
case 'simple-redux-dynamic':
example = <SimpleReduxDynamicExample/>;
break;
default: default:
example = <RichGridExample/>; example = <RichGridExample/>;
} }
@@ -97,3 +156,4 @@ class App extends Component {
} }
export default App export default App
*/

19
src/NavItem.jsx Normal file
View File

@@ -0,0 +1,19 @@
import React, {PropTypes} from 'react'
import {Route, Link} from 'react-router-dom'
// for bootstrap li active functionality
export default function NavItem({children, to, exact}) {
return (
<Route path={to} exact={exact} children={({match}) => (
<li className={match ? 'active' : null}>
<Link to={to}>{children}</Link>
</li>
)}/>
)
}
NavItem.propTypes = {
to: PropTypes.string.isRequired,
exact: PropTypes.bool,
children: PropTypes.node.isRequired,
};

View File

@@ -13,7 +13,7 @@ export default class ChildMessageRenderer extends Component {
render() { render() {
return ( return (
<span><button style={{height: 20}} onClick={this.invokeParentMethod}>Invoke Parent</button></span> <span><button style={{height: 20, lineHeight: 0.5}} onClick={this.invokeParentMethod} className="btn btn-info">Invoke Parent</button></span>
); );
} }
}; };

View File

@@ -3,15 +3,29 @@ import React, {Component} from "react";
export default class CurrencyRenderer extends Component { export default class CurrencyRenderer extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
value: props.value
}
} }
formatValueToCurrency(currency, value) { formatValueToCurrency(currency, value) {
return `${currency}${value}` return `${currency}${value}`
} }
// noinspection JSUnusedGlobalSymbols
refresh(params) {
if(params.value !== this.state.value) {
this.setState({
value: params.value.toFixed(2)
})
}
return true;
}
render() { render() {
return ( return (
<span>{this.formatValueToCurrency('EUR', this.props.value)}</span> <span>{this.formatValueToCurrency('EUR', this.state.value)}</span>
); );
} }
}; };

View File

@@ -18,13 +18,13 @@ export default class DynamicComponentsExample extends Component {
} }
}, },
rowData: this.createRowData(), rowData: DynamicComponentsExample.createRowData(),
columnDefs: this.createColumnDefs() columnDefs: DynamicComponentsExample.createColumnDefs()
}; };
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
this.refreshRowData = this.refreshRowData.bind(this); this.refreshEvenRowsCurrencyData = this.refreshEvenRowsCurrencyData.bind(this);
} }
onGridReady(params) { onGridReady(params) {
@@ -34,22 +34,30 @@ export default class DynamicComponentsExample extends Component {
this.gridApi.sizeColumnsToFit(); this.gridApi.sizeColumnsToFit();
} }
onCellValueChanged($event) {
this.gridApi.refreshCells([$event.node],["cube"]);
}
methodFromParent(cell) { methodFromParent(cell) {
alert(`Parent Component Method from ${cell}!`); alert(`Parent Component Method from ${cell}!`);
} }
createColumnDefs() { refreshEvenRowsCurrencyData() {
this.gridApi.forEachNode(rowNode => {
if (rowNode.data.value % 2 === 0) {
rowNode.setDataValue('currency', rowNode.data.value + Number(Math.random().toFixed(2)))
}
});
this.gridApi.refreshCells({
columns: ['currency']
})
}
static createColumnDefs() {
return [ return [
{headerName: "Row", field: "row", width: 100}, {headerName: "Row", field: "row", width: 100},
{ {
headerName: "Square", headerName: "Square",
field: "value", field: "value",
cellRendererFramework: SquareRenderer, cellRendererFramework: SquareRenderer,
editable:true, editable: true,
colId: "square", colId: "square",
width: 100 width: 100
}, },
@@ -71,7 +79,7 @@ export default class DynamicComponentsExample extends Component {
headerName: "Currency", headerName: "Currency",
field: "currency", field: "currency",
cellRendererFramework: CurrencyRenderer, cellRendererFramework: CurrencyRenderer,
colId: "params", colId: "currency",
width: 135 width: 135
}, },
{ {
@@ -84,12 +92,7 @@ export default class DynamicComponentsExample extends Component {
]; ];
} }
refreshRowData() { static createRowData() {
let rowData = this.createRowData();
this.gridApi.setRowData(rowData);
}
createRowData() {
let rowData = []; let rowData = [];
for (let i = 0; i < 15; i++) { for (let i = 0; i < 15; i++) {
@@ -102,12 +105,13 @@ export default class DynamicComponentsExample extends Component {
return rowData; return rowData;
} }
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 400, width: 900}}
className="ag-fresh"> className="ag-fresh">
<h1>Dynamic React Component Example</h1> <h1>Dynamic React Component Example</h1>
<button onClick={this.refreshRowData}>Refresh Data</button> <button onClick={this.refreshEvenRowsCurrencyData} style={{marginBottom: 10}} className="btn btn-primary">Refresh Even Row Currency Data</button>
<AgGridReact <AgGridReact
// properties // properties
columnDefs={this.state.columnDefs} columnDefs={this.state.columnDefs}
@@ -117,6 +121,32 @@ export default class DynamicComponentsExample extends Component {
// events // events
onGridReady={this.onGridReady}> onGridReady={this.onGridReady}>
</AgGridReact> </AgGridReact>
<div className="row">
<div className="col-sm-12"><h1>Dynamic React Component Example</h1></div>
</div>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates Dynamic React Components with ag-Grid, Parent/Child Communication (cell component to parent grid component), as well as
dynamic data updates of the <code>Currency</code> column.</h5>
<p><span style={{fontWeight: "bold"}}>Square, Cube, Row Params, Currency and Child/Parent</span>: React Components within the Grid</p>
<p><span style={{fontWeight: "bold"}}>Currency (Pipe)</span>: An React Component mimicking a Currency Pipe, dynamically updated with the button above.</p>
<p><span style={{fontWeight: "bold"}}>Child/Parent</span>: Demonstrates the Child Cell Component communicating with the Parent Grid Component.</p>
<p><span style={{fontWeight: "bold"}}>Refresh Even Row Currency Data</span>: Dynamically Updates Event Rows Currency Value. Only the Currency column will be re-rendered.</p>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<h4 className="card-title">React Functionality</h4>
<p className="card-text">Utilise React Components within ag-Grid</p>
<a target="_blank" href="https://www.ag-grid.com/best-react-data-grid/?framework=react" className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-rendering-components/?framework=react" className="btn btn-primary">React Renderers</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-data-update/" className="btn btn-primary">Updating Data</a>
</div>
</div>
</div>
</div>
</div> </div>
); );
} }

View File

@@ -10,8 +10,8 @@ export default class EditorComponentsExample extends Component {
super(props); super(props);
this.state = { this.state = {
rowData: this.createRowData(), rowData: EditorComponentsExample.createRowData(),
columnDefs: this.createColumnDefs() columnDefs: EditorComponentsExample.createColumnDefs()
}; };
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
@@ -24,9 +24,32 @@ export default class EditorComponentsExample extends Component {
this.gridApi.sizeColumnsToFit(); this.gridApi.sizeColumnsToFit();
} }
createColumnDefs() { static createColumnDefs() {
return [ return [
{headerName: "Name", field: "name", width: 300}, {
headerName: "Name",
field: "name",
width: 300,
editable: true,
cellEditor: 'richSelect',
cellEditorParams: {
values: [
"Bob",
"Harry",
"Sally",
"Mary",
"John",
"Jack",
"Sue",
"Sean",
"Niall",
"Albert",
"Fred",
"Jenny",
"Larry"
]
}
},
{ {
headerName: "Mood", headerName: "Mood",
field: "mood", field: "mood",
@@ -45,7 +68,7 @@ export default class EditorComponentsExample extends Component {
]; ];
} }
createRowData() { static createRowData() {
return [ return [
{name: "Bob", mood: "Happy", number: 10}, {name: "Bob", mood: "Happy", number: 10},
{name: "Harry", mood: "Sad", number: 3}, {name: "Harry", mood: "Sad", number: 3},
@@ -65,7 +88,7 @@ export default class EditorComponentsExample extends Component {
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 370, width: 900}}
className="ag-fresh"> className="ag-fresh">
<h1>Cell Editor Component Example</h1> <h1>Cell Editor Component Example</h1>
<AgGridReact <AgGridReact
@@ -76,7 +99,34 @@ export default class EditorComponentsExample extends Component {
// events // events
onGridReady={this.onGridReady}> onGridReady={this.onGridReady}>
</AgGridReact> </AgGridReact>
<div className="row">
<div className="col-sm-12"><h1>Cell Editor Component Example</h1></div>
</div> </div>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates React Editor Components within ag-Grid, as well as an example using the built in Rich Select editor.</h5>
<p><span style={{fontWeight: "bold"}}>Name</span>: Utilises the built in <code>RichSelect</code> editor</p>
<p><span style={{fontWeight: "bold"}}>Mood</span>: A Custom React Editor demonstrating popup functionality, with full keyboard control.</p>
<p><span style={{fontWeight: "bold"}}>Numeric</span>: A Custom React Editor demonstrating pre & post validation. Only numeric characters are allowed,
and numbers greater than 1000000 will be rejected.</p>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<h4 className="card-title">React Functionality</h4>
<p className="card-text">Utilise React Components within ag-Grid</p>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-editing/?framework=react" className="btn btn-primary">Cell Editing</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-editor/?framework=react" className="btn btn-primary">Editor Components</a>
<a target="_blank" href="https://www.ag-grid.com/best-react-data-grid/?framework=react" className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-editor/?framework=react#reactCellEditing" className="btn btn-primary">React Editor Components</a>
</div>
</div>
</div>
</div>
</div>
); );
} }
}; };

View File

@@ -18,9 +18,21 @@ export default class MoodEditor extends Component {
} }
componentDidMount() { componentDidMount() {
this.refs.container.addEventListener('keydown', this.checkAndToggleMoodIfLeftRight);
this.focus(); this.focus();
} }
componentWillUnmount() {
this.refs.container.removeEventListener('keydown', this.checkAndToggleMoodIfLeftRight);
}
checkAndToggleMoodIfLeftRight = (event) => {
if ([37, 39].indexOf(event.keyCode) > -1) { // left and right
this.toggleMood();
event.stopPropagation();
}
}
componentDidUpdate() { componentDidUpdate() {
this.focus(); this.focus();
} }
@@ -75,12 +87,11 @@ export default class MoodEditor extends Component {
background: "#e6e6e6", background: "#e6e6e6",
padding: 15, padding: 15,
textAlign: "center", textAlign: "center",
display: "inline-block", display: "inline-block"
// outline: "none"
}; };
let unselected = { let unselected = {
paddingLleft: 10, paddingLeft: 10,
paddingRight: 10, paddingRight: 10,
border: "1px solid transparent", border: "1px solid transparent",
padding: 4 padding: 4
@@ -93,12 +104,14 @@ export default class MoodEditor extends Component {
padding: 4 padding: 4
}; };
let happyStyle = this.state.value === 'Happy' ? selected : unselected; let happyStyle = this.state.happy ? selected : unselected;
let sadStyle = this.state.value !== 'Happy' ? selected : unselected; let sadStyle = !this.state.happy ? selected : unselected;
return ( return (
<div ref="container" <div ref="container"
style={mood}> style={mood}
tabIndex={1} // important - without this the keypresses wont be caught
>
<img src="images/smiley.png" onClick={this.onHappyClick} style={happyStyle}/> <img src="images/smiley.png" onClick={this.onHappyClick} style={happyStyle}/>
<img src="images/smiley-sad.png" onClick={this.onSadClick} style={sadStyle}/> <img src="images/smiley-sad.png" onClick={this.onSadClick} style={sadStyle}/>
</div> </div>

View File

@@ -1,21 +1,25 @@
import React, {Component} from "react"; import React, {Component} from "react";
import ReactDOM from "react-dom";
export default class MoodEditor extends Component { export default class MoodEditor extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
value: this.props.value
};
this.cancelBeforeStart = this.props.charPress && ('1234567890'.indexOf(this.props.charPress) < 0); this.cancelBeforeStart = this.props.charPress && ('1234567890'.indexOf(this.props.charPress) < 0);
let value = this.props.value;
if (!this.cancelBeforeStart && this.props.charPress) {
value = value + this.props.charPress;
}
this.state = {
value
};
this.onKeyDown = this.onKeyDown.bind(this); this.onKeyDown = this.onKeyDown.bind(this);
this.handleChange = this.handleChange.bind(this); this.handleChange = this.handleChange.bind(this);
} }
componentDidMount() { componentDidMount() {
this.refs.input.addEventListener('keydown', this.onKeyDown);
this.focus(); this.focus();
} }
@@ -23,12 +27,15 @@ export default class MoodEditor extends Component {
this.focus(); this.focus();
} }
componentWillUnmount() {
this.refs.input.removeEventListener('keydown', this.onKeyDown);
}
focus() { focus() {
setTimeout(() => { setTimeout(() => {
let container = ReactDOM.findDOMNode(this.refs.input); this.refs.input.focus();
if (container) { this.refs.input.setSelectionRange(this.state.value.length, this.state.value.length);
container.focus();
}
}) })
} }
@@ -47,11 +54,20 @@ export default class MoodEditor extends Component {
}; };
onKeyDown(event) { onKeyDown(event) {
if (this.isLeftOrRight(event)) {
event.stopPropagation();
return;
}
if (!this.isKeyPressedNumeric(event)) { if (!this.isKeyPressedNumeric(event)) {
if (event.preventDefault) event.preventDefault(); if (event.preventDefault) event.preventDefault();
} }
} }
isLeftOrRight(event) {
return [37, 39].indexOf(event.keyCode) > -1;
}
handleChange(event) { handleChange(event) {
this.setState({value: event.target.value}); this.setState({value: event.target.value});
} }
@@ -75,8 +91,8 @@ export default class MoodEditor extends Component {
return ( return (
<input ref="input" <input ref="input"
value={this.state.value} value={this.state.value}
onKeyDown={this.onKeyDown}
onChange={this.handleChange} onChange={this.handleChange}
style={{width: "100%"}}
/> />
); );
} }

View File

@@ -13,8 +13,8 @@ export default class FilterComponentExample extends Component {
this.state = { this.state = {
gridOptions: {}, gridOptions: {},
rowData: this.createRowData(), rowData: FilterComponentExample.createRowData(),
columnDefs: this.createColumnDefs(), columnDefs: FilterComponentExample.createColumnDefs(),
}; };
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
@@ -32,19 +32,20 @@ export default class FilterComponentExample extends Component {
this.gridApi.getFilterInstance("name").getFrameworkComponentInstance().componentMethod("Hello World!"); this.gridApi.getFilterInstance("name").getFrameworkComponentInstance().componentMethod("Hello World!");
} }
createColumnDefs() { static createColumnDefs() {
return [ return [
{headerName: "Row", field: "row", width: 400}, {headerName: "Row", field: "row", width: 400},
{ {
headerName: "Filter Component", headerName: "Filter Component",
field: "name", field: "name",
filterFramework: PartialMatchFilter, filterFramework: PartialMatchFilter,
width: 400 width: 400,
menuTabs:['filterMenuTab']
} }
]; ];
} }
createRowData() { static createRowData() {
return [ return [
{"row": "Row 1", "name": "Michael Phelps"}, {"row": "Row 1", "name": "Michael Phelps"},
{"row": "Row 2", "name": "Natalie Coughlin"}, {"row": "Row 2", "name": "Natalie Coughlin"},
@@ -65,18 +66,16 @@ export default class FilterComponentExample extends Component {
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 400, width: 900}}
className="ag-fresh"> className="ag-fresh">
<h1>Filter Component Example</h1> <h1>Filter Component Example</h1>
<button style={{marginBottom: 10}} onClick={this.onClicked}>Filter Instance Method</button> <button style={{marginBottom: 10}} onClick={this.onClicked} className="btn btn-primary">Filter Instance Method</button>
<AgGridReact <AgGridReact
// properties // properties
columnDefs={this.state.columnDefs} columnDefs={this.state.columnDefs}
rowData={this.state.rowData} rowData={this.state.rowData}
enableFilter enableFilter
suppressMenuColumnPanel // ag-enterprise only
suppressMenuMainPanel // ag-enterprise only
// events // events
onGridReady={this.onGridReady}> onGridReady={this.onGridReady}>

View File

@@ -7,7 +7,8 @@ export default class PartialMatchFilter extends Component {
this.state = { this.state = {
text: '' text: ''
} };
this.valueGetter = this.props.valueGetter; this.valueGetter = this.props.valueGetter;
this.onChange = this.onChange.bind(this); this.onChange = this.onChange.bind(this);
@@ -63,8 +64,17 @@ export default class PartialMatchFilter extends Component {
} }
render() { render() {
let style = {
border: "2px solid #22ff22",
borderRadius: "5px",
backgroundColor: "#bbffbb",
width: "200px",
height: "50px"
};
return ( return (
<span>Filter: <input style={{height: "20px"}} ref="input" value={this.state.text} onChange={this.onChange}/></span> <div style={style}>Filter: <input style={{height: "20px"}} ref="input" value={this.state.text}
onChange={this.onChange} className="form-control"/></div>
); );
} }
}; };

View File

@@ -0,0 +1,69 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import FloatingFilter from "./floatingFilter";
import overrideStyle from "./floatingFilter.css";
export default class FloatingFilterGridExample extends Component {
constructor(props) {
super(props);
this.state = {
columnDefs: this.createColumnDefs(),
rowData: this.createRowData()
}
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
createColumnDefs() {
return [
{
headerName: "Make",
field: "make",
floatingFilterComponentFramework: FloatingFilter,
filter: 'set'
},
{headerName: "Model", field: "model"},
{headerName: "Price", field: "price"}
];
}
createRowData() {
return [
{make: "Toyota", model: "Celica", price: 35000},
{make: "Ford", model: "Mondeo", price: 32000},
{make: "Porsche", model: "Boxter", price: 72000}
];
}
render() {
let divStyle = {
height: 400,
width: 900
};
// combine the styles
const style = Object.assign({}, divStyle, overrideStyle);
return (
<div style={style} className="ag-fresh">
<h1>Floating Filter Example</h1>
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.state.rowData}
floatingFilter={true}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
)
}
};

View File

@@ -0,0 +1,3 @@
.ag-floating-filter-button button {
margin: 0
}

View File

@@ -0,0 +1,39 @@
import React, {Component} from "react";
export default class FloatingFilter extends Component {
constructor(props) {
super(props);
this.state = {
parentModel: null
}
}
// when does this get called? how to test this?
onParentModelChanged(parentModel) {
this.setState({
parentModel: parentModel
})
}
remove(item) {
this.props.onFloatingFilterChanged({
model: this.state.parentModel.filter(it => it !== item)
});
}
render() {
if (!this.state.parentModel) return null;
// as the backing filter is a set filter what we're doing here is rendering the list in the set
// and when the [x] removing the selected item, thereby effectively hiding it
let options = this.state.parentModel.map((item, i) => {
let removeMeListener = () => {
this.remove(item)
};
let removeMeElement = <a onClick={removeMeListener}>[x]</a>;
return <span key={i}>{item}{removeMeElement}</span>;
});
return <div>{options}</div>;
}
}

View File

@@ -10,8 +10,8 @@ export default class FullWidthComponentExample extends Component {
this.state = { this.state = {
gridOptions: {}, gridOptions: {},
rowData: this.createRowData(), rowData: FullWidthComponentExample.createRowData(),
columnDefs: this.createColumnDefs() columnDefs: FullWidthComponentExample.createColumnDefs()
}; };
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
@@ -28,7 +28,7 @@ export default class FullWidthComponentExample extends Component {
return (rowNode.id === "0") || (parseInt(rowNode.id) % 2 === 0); return (rowNode.id === "0") || (parseInt(rowNode.id) % 2 === 0);
} }
createColumnDefs() { static createColumnDefs() {
return [ return [
{ {
headerName: "Name", headerName: "Name",
@@ -43,7 +43,7 @@ export default class FullWidthComponentExample extends Component {
]; ];
} }
createRowData() { static createRowData() {
return [ return [
{name: "Bob", age: 10}, {name: "Bob", age: 10},
{name: "Harry", age: 3}, {name: "Harry", age: 3},
@@ -68,7 +68,7 @@ export default class FullWidthComponentExample extends Component {
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 400, width: 900}}
className="ag-fresh"> className="ag-fresh">
<h1>Full Width Renderer Example</h1> <h1>Full Width Renderer Example</h1>
<AgGridReact <AgGridReact

View File

@@ -8,8 +8,14 @@ export default class NameAndAgeRenderer extends Component {
} }
render() { render() {
let style = {
border: "2px solid #22ff22",
borderRadius: "5px",
backgroundColor: "#bbffbb"
};
return ( return (
<span>Full Width Column! { this.values }</span> <div style={style}>Full Width Column! { this.values }</div>
); );
} }
}; };

View File

@@ -1,3 +0,0 @@
.ag-group-value .ag-react-container {
display: inline-block;
}

View File

@@ -3,8 +3,6 @@ import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react"; import {AgGridReact} from "ag-grid-react";
import MedalRenderer from "./MedalRenderer"; import MedalRenderer from "./MedalRenderer";
import "./GroupedRowInnerRendererComponentExample.css";
import "ag-grid-enterprise"; import "ag-grid-enterprise";
export default class GroupedRowInnerRendererComponentExample extends Component { export default class GroupedRowInnerRendererComponentExample extends Component {
@@ -108,7 +106,7 @@ export default class GroupedRowInnerRendererComponentExample extends Component {
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 400, width: 900}}
className="ag-fresh"> className="ag-fresh">
<h1>Group Row Renderer Example</h1> <h1>Group Row Renderer Example</h1>
<AgGridReact <AgGridReact

View File

@@ -5,9 +5,12 @@ export default class MedalRenderer extends Component {
super(props); super(props);
this.country = this.props.node.key; this.country = this.props.node.key;
this.gold = this.props.data.gold; this.gold = this.props.node.aggData.gold;
this.silver = this.props.data.silver; this.silver = this.props.node.aggData.silver;
this.bronze = this.props.data.bronze; this.bronze = this.props.node.aggData.bronze;
// override the containing div so that the +/- and label are inline
this.props.reactContainer.style.display = "inline-block";
} }
render() { render() {

View File

@@ -12,40 +12,51 @@
height: 100%; height: 100%;
} }
body {
line-height: 1
}
div.card-body > a {
margin-top: 5px
}
.btn-primary {
margin-right: 5px;
}
button { button {
margin-left: 4px; margin-left: 4px;
margin-right: 4px; margin-right: 4px;
} }
.align-right {
text-align: right
}
.customHeaderMenuButton { .customHeaderMenuButton {
margin-top: 5px;
margin-left: 4px; margin-left: 4px;
float: left; float: left;
} }
.customHeaderLabel { .customHeaderLabel {
margin-left: 5px; margin-left: 5px;
margin-top: 3px;
float: left; float: left;
} }
.customSortDownLabel { .customSortDownLabel {
float: left; float: left;
margin-left: 10px; margin-left: 10px;
margin-top: 5px;
} }
.customSortUpLabel { .customSortUpLabel {
float: left; float: left;
margin-left: 3px; margin-left: 3px;
margin-top: 4px;
} }
.customSortRemoveLabel { .customSortRemoveLabel {
float: left; float: left;
font-size: 11px; font-size: 11px;
margin-left: 3px; margin-left: 3px;
margin-top: 6px;
} }
.active { .active {
@@ -58,13 +69,11 @@
.customHeaderLabel { .customHeaderLabel {
margin-left: 5px; margin-left: 5px;
margin-top: 3px;
float: left; float: left;
} }
.customExpandButton { .customExpandButton {
float: right; float: right;
margin-top: 5px;
margin-left: 3px; margin-left: 3px;
} }

View File

@@ -2,6 +2,7 @@
import React from "react"; import React from "react";
import {render} from "react-dom"; import {render} from "react-dom";
import {BrowserRouter} from "react-router-dom";
import "ag-grid-root/dist/styles/ag-grid.css"; import "ag-grid-root/dist/styles/ag-grid.css";
import "ag-grid-root/dist/styles/theme-fresh.css"; import "ag-grid-root/dist/styles/theme-fresh.css";
@@ -9,9 +10,15 @@ import "../node_modules/bootstrap/dist/css/bootstrap.css";
import App from "./App"; import App from "./App";
// only required when using enterprise features
// import {LicenseManager} from "ag-grid-enterprise/main";
// LicenseManager.setLicenseKey("<your license key>");
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
render( render(
<App/>, <BrowserRouter>
<App/>
</BrowserRouter>,
document.querySelector('#app') document.querySelector('#app')
); );
}); });

View File

@@ -1,7 +1,3 @@
.ag-full-width-row .ag-react-container {
height: 100%
}
.full-width-panel { .full-width-panel {
position: relative; position: relative;
background: #fafafa; background: #fafafa;

View File

@@ -16,6 +16,9 @@ export default class DetailPanelComponent extends Component {
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
this.onSearchTextChange = this.onSearchTextChange.bind(this); this.onSearchTextChange = this.onSearchTextChange.bind(this);
// override the containing div so that the child grid fills the row height
this.props.reactContainer.style.height = "100%";
} }
onGridReady(params) { onGridReady(params) {
@@ -41,7 +44,7 @@ export default class DetailPanelComponent extends Component {
headerName: 'Duration', headerName: 'Duration',
field: 'duration', field: 'duration',
cellClass: 'call-record-cell', cellClass: 'call-record-cell',
cellFormatter: this.secondCellFormatter valueFormatter: this.secondCellFormatter
}, },
{headerName: 'Switch', field: 'switchCode', cellClass: 'call-record-cell'}]; {headerName: 'Switch', field: 'switchCode', cellClass: 'call-record-cell'}];

View File

@@ -35,7 +35,7 @@ export default class MasterDetailExample extends Component {
}, },
{headerName: 'Account', field: 'account'}, {headerName: 'Account', field: 'account'},
{headerName: 'Calls', field: 'totalCalls'}, {headerName: 'Calls', field: 'totalCalls'},
{headerName: 'Minutes', field: 'totalMinutes', cellFormatter: this.minuteCellFormatter} {headerName: 'Minutes', field: 'totalMinutes', valueFormatter: this.minuteCellFormatter}
]; ];
} }
@@ -116,7 +116,7 @@ export default class MasterDetailExample extends Component {
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 400, width: 900}}
className="ag-fresh"> className="ag-fresh">
<h1>Master-Detail Example</h1> <h1>Master-Detail Example</h1>
<AgGridReact <AgGridReact
@@ -131,7 +131,6 @@ export default class MasterDetailExample extends Component {
enableSorting enableSorting
enableColResize enableColResize
suppressMenuFilterPanel
// events // events
onGridReady={this.onGridReady}> onGridReady={this.onGridReady}>

View File

@@ -3,7 +3,7 @@ import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react"; import {AgGridReact} from "ag-grid-react";
import StyledRenderer from "./StyledRenderer"; import StyledRenderer from "./StyledRenderer";
export default class FloatingRowComponentExample extends Component { export default class PinnedRowComponentExample extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -13,8 +13,8 @@ export default class FloatingRowComponentExample extends Component {
rowData: this.createRowData(), rowData: this.createRowData(),
columnDefs: this.createColumnDefs(), columnDefs: this.createColumnDefs(),
topFloatingRowData: [{row: "Top Row", number: "Top Number"}], pinnedTopRowData: [{row: "Top Row", number: "Top Number"}],
bottomFloatingRowData: [{row: "Bottom Row", number: "Bottom Number"}] pinnedBottomRowData: [{row: "Bottom Row", number: "Bottom Number"}]
}; };
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
@@ -33,8 +33,8 @@ export default class FloatingRowComponentExample extends Component {
headerName: "Row", headerName: "Row",
field: "row", field: "row",
width: 400, width: 400,
floatingCellRendererFramework: StyledRenderer, pinnedRowCellRendererFramework: StyledRenderer,
floatingCellRendererParams: { pinnedRowCellRendererParams: {
style: {'fontWeight': 'bold'} style: {'fontWeight': 'bold'}
} }
}, },
@@ -42,8 +42,8 @@ export default class FloatingRowComponentExample extends Component {
headerName: "Number", headerName: "Number",
field: "number", field: "number",
width: 399, width: 399,
floatingCellRendererFramework: StyledRenderer, pinnedRowCellRendererFramework: StyledRenderer,
floatingCellRendererParams: { pinnedRowCellRendererParams: {
style: {'fontStyle': 'italic'} style: {'fontStyle': 'italic'}
} }
}, },
@@ -65,16 +65,16 @@ export default class FloatingRowComponentExample extends Component {
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 400, width: 900}}
className="ag-fresh"> className="ag-fresh">
<h1>Floating Row Renderer Example</h1> <h1>Pinned Row Renderer Example</h1>
<AgGridReact <AgGridReact
// properties // properties
columnDefs={this.state.columnDefs} columnDefs={this.state.columnDefs}
rowData={this.state.rowData} rowData={this.state.rowData}
floatingTopRowData={this.state.topFloatingRowData} pinnedTopRowData={this.state.pinnedTopRowData}
floatingBottomRowData={this.state.bottomFloatingRowData} pinnedBottomRowData={this.state.pinnedBottomRowData}
// events // events
onGridReady={this.onGridReady}> onGridReady={this.onGridReady}>

View File

@@ -20,7 +20,7 @@ export default class ClickableRenderer extends Component {
render() { render() {
return ( return (
<button style={{height: 21}} onClick={this.clicked} >Click Me</button> <button style={{lineHeight: 0.5, width: "98%"}} onClick={this.clicked} className="btn btn-info">Click Me</button>
); );
} }
} }

View File

@@ -9,8 +9,8 @@ export default class RichComponentsExample extends Component {
super(props); super(props);
this.state = { this.state = {
rowData: this.createRowData(), rowData: RichComponentsExample.createRowData(),
columnDefs: this.createColumnDefs() columnDefs: RichComponentsExample.createColumnDefs()
}; };
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
@@ -23,9 +23,13 @@ export default class RichComponentsExample extends Component {
this.gridApi.sizeColumnsToFit(); this.gridApi.sizeColumnsToFit();
} }
createColumnDefs() { static createColumnDefs() {
return [ return [
{headerName: "Name", field: "name", width: 200}, {
headerName: "Name",
field: "name",
width: 200
},
{ {
headerName: "Ratio Component", headerName: "Ratio Component",
field: "ratios", field: "ratios",
@@ -40,7 +44,7 @@ export default class RichComponentsExample extends Component {
} }
]; } ]; }
createRowData() { static createRowData() {
return [ return [
{name: 'Homer Simpson', ratios: {top: 0.25, bottom: 0.75}}, {name: 'Homer Simpson', ratios: {top: 0.25, bottom: 0.75}},
{name: 'Marge Simpson', ratios: {top: 0.67, bottom: 0.39}}, {name: 'Marge Simpson', ratios: {top: 0.67, bottom: 0.39}},
@@ -60,8 +64,7 @@ export default class RichComponentsExample extends Component {
render() { render() {
return ( return (
<div style={{height: 400, width: 945}} <div style={{height: 370, width: 900}} className="ag-fresh">
className="ag-fresh">
<h1>Dynamic React Components - Richer Example</h1> <h1>Dynamic React Components - Richer Example</h1>
<AgGridReact <AgGridReact
// properties // properties
@@ -72,6 +75,24 @@ export default class RichComponentsExample extends Component {
// events // events
onGridReady={this.onGridReady}> onGridReady={this.onGridReady}>
</AgGridReact> </AgGridReact>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates Dynamic React Components with ag-Grid. Functionally similar
to the <a href="/dynamic">Dynamic React Components Example</a> but with slightly richer components.</h5>
</div>
</div>
<div className="row">
<div className="col-sm-12">
<div className="card">
<div className="card-body">
<h4 className="card-title">React Functionality</h4>
<p className="card-text">Utilise React Components within ag-Grid</p>
<a target="_blank" href="https://www.ag-grid.com/best-react-data-grid/?framework=react" className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-cell-rendering-components/?framework=react" className="btn btn-primary">React Renderers</a>
</div>
</div>
</div>
</div>
</div> </div>
); );
} }

View File

@@ -4,51 +4,87 @@ import ProficiencyCellRenderer from './ProficiencyCellRenderer.jsx';
import RefData from './RefData'; import RefData from './RefData';
import SkillsFilter from './SkillsFilter.jsx'; import SkillsFilter from './SkillsFilter.jsx';
import ProficiencyFilter from './ProficiencyFilter.jsx'; import ProficiencyFilter from './ProficiencyFilter.jsx';
import MyReactHeaderGroupComponent from './MyReactHeaderGroupComponent.jsx'; import HeaderGroupComponent from './HeaderGroupComponent.jsx';
export default class ColDefFactory { export default class ColDefFactory {
createColDefs() { createColDefs() {
return [
var columnDefs = [ {
{headerName: '#', width: 30, checkboxSelection: true, suppressSorting: true, headerName: '#',
suppressMenu: true, pinned: true}, width: 30,
checkboxSelection: true,
suppressSorting: true,
suppressMenu: true,
suppressFilter: true,
pinned: true
},
{ {
headerName: 'Employee', headerName: 'Employee',
headerGroupComponentFramework: MyReactHeaderGroupComponent, headerGroupComponentFramework: HeaderGroupComponent,
children: [ children: [
{ {
headerName: "Name", field: "name", enableRowGroup: true, enablePivot: true, headerName: "Name",
width: 150, pinned: true, editable: true, field: "name",
enableRowGroup: true,
enablePivot: true,
width: 150,
pinned: true,
editable: true,
// use a React cellEditor // use a React cellEditor
cellEditorFramework: NameCellEditor cellEditorFramework: NameCellEditor
}, { },
headerName: "Country", field: "country", width: 150, enableRowGroup: true, enablePivot: true, {
headerName: "Country",
field: "country",
width: 150,
enableRowGroup: true,
enablePivot: true,
// an example of using a non-React cell renderer // an example of using a non-React cell renderer
cellRenderer: countryCellRenderer, pinned: true, cellRenderer: countryCellRenderer,
pinned: true,
filterParams: { filterParams: {
cellRenderer: countryCellRenderer, cellHeight: 20 cellRenderer: countryCellRenderer,
}, columnGroupShow: 'open' cellHeight: 20
}, { }
headerName: "DOB", field: "dob", width: 110, enableRowGroup: true, enablePivot: true, filter:'date', },
pinned: true, cellRenderer: function(params) { {
headerName: "DOB",
field: "dob",
width: 110,
enableRowGroup: true,
enablePivot: true,
filter: 'date',
pinned: true,
cellRenderer: function (params) {
return pad(params.value.getDate(), 2) + '/' + return pad(params.value.getDate(), 2) + '/' +
pad(params.value.getMonth() + 1, 2)+ '/' + pad(params.value.getMonth() + 1, 2) + '/' +
params.value.getFullYear(); params.value.getFullYear();
}, columnGroupShow: 'open' },
columnGroupShow: 'open'
} }
] ]
}, },
{ {
headerName: 'IT Skills', headerName: 'IT Skills',
children: [ children: [
{headerName: "Skills", width: 125, suppressSorting: true, field: 'skills', enableRowGroup: true, enablePivot: true, {
headerName: "Skills",
width: 125,
suppressSorting: true,
field: 'skills',
enableRowGroup: true,
enablePivot: true,
// supply a React component // supply a React component
cellRendererFramework: SkillsCellRenderer, cellRendererFramework: SkillsCellRenderer,
// supply a React component // supply a React component
filterFramework: SkillsFilter filterFramework: SkillsFilter
}, },
{headerName: "Proficiency", field: "proficiency", width: 135, enableValue: true, {
headerName: "Proficiency",
field: "proficiency",
width: 135,
enableValue: true,
// supply a React component // supply a React component
cellRendererFramework: ProficiencyCellRenderer, cellRendererFramework: ProficiencyCellRenderer,
// supply a React component // supply a React component
@@ -65,7 +101,6 @@ export default class ColDefFactory {
] ]
} }
]; ];
return columnDefs;
} }
} }
@@ -73,7 +108,7 @@ export default class ColDefFactory {
// need to use React for it. // need to use React for it.
function countryCellRenderer(params) { function countryCellRenderer(params) {
if (params.value) { if (params.value) {
var flag = "<img border='0' width='15' height='10' " + const flag = "<img border='0' width='15' height='10' " +
"style='margin-bottom: 2px' src='http://flags.fmcdn.net/data/flags/mini/" "style='margin-bottom: 2px' src='http://flags.fmcdn.net/data/flags/mini/"
+ RefData.COUNTRY_CODES[params.value] + ".png'>"; + RefData.COUNTRY_CODES[params.value] + ".png'>";
return flag + " " + params.value; return flag + " " + params.value;

View File

@@ -1,10 +1,11 @@
import React from 'react'; import React from "react";
import * as PropTypes from "prop-types";
// Date Component to be used in the date filter. // Date Component to be used in the date filter.
// This is a very simple example of how a React component can be plugged as a DateComponentFramework // This is a very simple example of how a React component can be plugged as a DateComponentFramework
// as you can see, the only requirement is that the React component implements the required methods // as you can see, the only requirement is that the React component implements the required methods
// getDate and setDate and that it calls back into props.onDateChanged every time that the date changes. // getDate and setDate and that it calls back into props.onDateChanged every time that the date changes.
export default class MyReactDateComponent extends React.Component { export default class DateComponent extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@@ -27,7 +28,7 @@ export default class MyReactDateComponent extends React.Component {
render() { render() {
//Inlining styles to make simpler the component //Inlining styles to make simpler the component
let filterStyle = { let filterStyle = {
margin:'2px' margin: '2px'
}; };
let ddStyle = { let ddStyle = {
width: '30px' width: '30px'
@@ -50,9 +51,12 @@ export default class MyReactDateComponent extends React.Component {
return ( return (
<div style={filterStyle}> <div style={filterStyle}>
<span style={resetStyle} onClick={this.resetDate.bind(this)}>x</span> <span style={resetStyle} onClick={this.resetDate.bind(this)}>x</span>
<input onInput = {this.onDateChanged.bind(this)} ref="dd" placeholder="dd" style={ddStyle} value={this.state.textBoxes.dd} maxLength="2"/>/ <input onInput={this.onDateChanged.bind(this)} ref="dd" placeholder="dd" style={ddStyle}
<input onInput = {this.onDateChanged.bind(this)} ref="mm" placeholder="mm" style={mmStyle} value={this.state.textBoxes.mm} maxLength="2"/>/ value={this.state.textBoxes.dd} maxLength="2"/>/
<input onInput = {this.onDateChanged.bind(this)} ref="yyyy" placeholder="yyyy" style={yyyyStyle} value={this.state.textBoxes.yyyy} maxLength="4"/> <input onInput={this.onDateChanged.bind(this)} ref="mm" placeholder="mm" style={mmStyle}
value={this.state.textBoxes.mm} maxLength="2"/>/
<input onInput={this.onDateChanged.bind(this)} ref="yyyy" placeholder="yyyy" style={yyyyStyle}
value={this.state.textBoxes.yyyy} maxLength="4"/>
</div> </div>
); );
} }
@@ -61,20 +65,20 @@ export default class MyReactDateComponent extends React.Component {
// METHODS REQUIRED BY AG-GRID // METHODS REQUIRED BY AG-GRID
//********************************************************************************* //*********************************************************************************
getDate (){ getDate() {
//ag-grid will call us here when in need to check what the current date value is hold by this //ag-grid will call us here when in need to check what the current date value is hold by this
//component. //component.
return this.state.date; return this.state.date;
} }
setDate (date){ setDate(date) {
//ag-grid will call us here when it needs this component to update the date that it holds. //ag-grid will call us here when it needs this component to update the date that it holds.
this.setState({ this.setState({
date:date, date,
textBoxes:{ textBoxes: {
dd: date.getDate(), dd: date ? date.getDate() : '',
mm: date.getMonth() + 1, mm: date ? date.getMonth() + 1 : '',
yyyy: date.getFullYear() yyyy: date ? date.getFullYear() : ''
} }
}) })
} }
@@ -83,10 +87,10 @@ export default class MyReactDateComponent extends React.Component {
// LINKS THE INTERNAL STATE AND AG-GRID // LINKS THE INTERNAL STATE AND AG-GRID
//********************************************************************************* //*********************************************************************************
updateAndNotifyAgGrid (date, textBoxes){ updateAndNotifyAgGrid(date, textBoxes) {
this.setState ({ this.setState({
date: date, date: date,
textBoxes:textBoxes textBoxes: textBoxes
}, },
//Callback after the state is set. This is where we tell ag-grid that the date has changed so //Callback after the state is set. This is where we tell ag-grid that the date has changed so
//it will proceed with the filtering and we can then expect ag-Grid to call us back to getDate //it will proceed with the filtering and we can then expect ag-Grid to call us back to getDate
@@ -99,23 +103,23 @@ export default class MyReactDateComponent extends React.Component {
// LINKING THE UI, THE STATE AND AG-GRID // LINKING THE UI, THE STATE AND AG-GRID
//********************************************************************************* //*********************************************************************************
resetDate (){ resetDate() {
let date = null; let date = null;
let textBoxes = { let textBoxes = {
dd : '', dd: '',
mm : '', mm: '',
yyyy : '', yyyy: '',
}; };
this.updateAndNotifyAgGrid(date, textBoxes) this.updateAndNotifyAgGrid(date, textBoxes)
} }
onDateChanged () { onDateChanged() {
let date = this.parseDate(this.refs.dd.value, this.refs.mm.value, this.refs.yyyy.value); let date = this.parseDate(this.refs.dd.value, this.refs.mm.value, this.refs.yyyy.value);
let textBoxes = { let textBoxes = {
dd : this.refs.dd.value, dd: this.refs.dd.value,
mm : this.refs.mm.value, mm: this.refs.mm.value,
yyyy : this.refs.yyyy.value, yyyy: this.refs.yyyy.value,
}; };
this.updateAndNotifyAgGrid(date, textBoxes) this.updateAndNotifyAgGrid(date, textBoxes)
@@ -125,7 +129,7 @@ export default class MyReactDateComponent extends React.Component {
// INTERNAL LOGIC // INTERNAL LOGIC
//********************************************************************************* //*********************************************************************************
parseDate (dd, mm, yyyy){ parseDate(dd, mm, yyyy) {
//If any of the three input date fields are empty, stop and return null //If any of the three input date fields are empty, stop and return null
if (dd.trim() === '' || mm.trim() === '' || yyyy.trim() === '') { if (dd.trim() === '' || mm.trim() === '' || yyyy.trim() === '') {
return null; return null;
@@ -138,7 +142,7 @@ export default class MyReactDateComponent extends React.Component {
let date = new Date(year, month - 1, day); let date = new Date(year, month - 1, day);
//If the date is not valid //If the date is not valid
if (isNaN(date.getTime())){ if (isNaN(date.getTime())) {
return null; return null;
} }
@@ -150,7 +154,7 @@ export default class MyReactDateComponent extends React.Component {
//If the javascript date parts don't match the provided fields, we assume that the input is non //If the javascript date parts don't match the provided fields, we assume that the input is non
//sensical... ie: Day=-1 or month=14, if this is the case, we return null //sensical... ie: Day=-1 or month=14, if this is the case, we return null
//This also protects us from non sensical dates like dd=31, mm=2 of any year //This also protects us from non sensical dates like dd=31, mm=2 of any year
if (date.getDate() != day || date.getMonth() + 1 != month || date.getFullYear() != year){ if (date.getDate() != day || date.getMonth() + 1 != month || date.getFullYear() != year) {
return null; return null;
} }
@@ -162,6 +166,6 @@ export default class MyReactDateComponent extends React.Component {
// which is the grid passing you the params for the cellRenderer. // which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params' // this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data. // props, so little need for adding this validation meta-data.
MyReactDateComponent.propTypes = { DateComponent.propTypes = {
params: React.PropTypes.object params: PropTypes.object
}; };

View File

@@ -1,31 +1,36 @@
import React from 'react'; import React from 'react';
import * as PropTypes from 'prop-types';
// Header component to be used as default for all the columns. // Header component to be used as default for all the columns.
export default class MyReactHeaderGroupComponent extends React.Component { export default class HeaderGroupComponent extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.props.columnGroup.getOriginalColumnGroup().addEventListener('expandedChanged', this.onExpandChanged.bind(this)); this.props.columnGroup.getOriginalColumnGroup().addEventListener('expandedChanged', this.onExpandChanged.bind(this));
this.state = { this.state = {
expanded:null expanded: null
};
} }
componentDidMount() {
this.onExpandChanged(); this.onExpandChanged();
} }
render() { render() {
let arrowClassName = "customExpandButton " + (this.state.expanded ? " expanded": " collapsed"); let arrowClassName = "customExpandButton " + (this.state.expanded ? " expanded" : " collapsed");
return <div> return <div>
<div className="customHeaderLabel"> {this.props.displayName}</div> <div className="customHeaderLabel"> {this.props.displayName}</div>
<div onClick={this.expandOrCollapse.bind(this)} className={arrowClassName}><i className="fa fa-arrow-right" /></div> <div onClick={this.expandOrCollapse.bind(this)} className={arrowClassName}><i
className="fa fa-arrow-right"/></div>
</div> </div>
} }
expandOrCollapse (){ expandOrCollapse() {
this.props.setExpanded(!this.state.expanded); this.props.setExpanded(!this.state.expanded);
}; };
onExpandChanged (){ onExpandChanged() {
this.setState({ this.setState({
expanded: this.props.columnGroup.getOriginalColumnGroup().isExpanded() expanded: this.props.columnGroup.getOriginalColumnGroup().isExpanded()
}) })
@@ -36,6 +41,6 @@ export default class MyReactHeaderGroupComponent extends React.Component {
// which is the grid passing you the params for the cellRenderer. // which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params' // this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data. // props, so little need for adding this validation meta-data.
MyReactHeaderGroupComponent.propTypes = { HeaderGroupComponent.propTypes = {
params: React.PropTypes.object params: PropTypes.object
}; };

View File

@@ -1,76 +0,0 @@
import React from 'react';
// Header component to be used as default for all the columns.
export default class MyReactHeaderComponent extends React.Component {
constructor(props) {
super(props);
this.props.column.addEventListener('sortChanged', this.onSortChanged.bind(this));
//The state of this component contains the current sort state of this column
//The possible values are: 'asc', 'desc' and ''
this.state = {
sorted: ''
}
}
render() {
let sortElements = [];
if (this.props.enableSorting){
let downArrowClass = "customSortDownLabel " + (this.state.sorted === 'desc' ? " active" : "");
let upArrowClass = "customSortUpLabel " + (this.state.sorted === 'asc' ? " active" : "");
let removeArrowClass = "customSortRemoveLabel " + (this.state.sorted === '' ? " active" : "");
sortElements.push(<div className={downArrowClass} onClick={this.onSortRequested.bind(this, 'desc')}><i className="fa fa-long-arrow-down"/></div>)
sortElements.push(<div className={upArrowClass} onClick={this.onSortRequested.bind(this, 'asc')}><i className="fa fa-long-arrow-up"/></div>)
sortElements.push(<div className={removeArrowClass} onClick={this.onSortRequested.bind(this, '')}><i className="fa fa-times"/></div>)
}
let menuButton = null;
if (this.props.enableMenu){
menuButton = <div ref="menuButton" className="customHeaderMenuButton" onClick={this.onMenuClick.bind(this)}><i className={"fa " + this.props.menuIcon}/></div>
}
return <div>
{menuButton}
<div className="customHeaderLabel">{this.props.displayName}</div>
{sortElements}
</div>
}
onSortRequested (order, event) {
this.props.setSort (order, event.shiftKey);
};
onSortChanged (){
if (this.props.column.isSortAscending()){
this.setState({
sorted: 'asc'
})
} else if (this.props.column.isSortDescending()){
this.setState({
sorted: 'desc'
})
} else {
this.setState({
sorted: ''
})
}
};
onMenuClick (){
this.props.showColumnMenu (this.refs.menuButton);
};
}
// the grid will always pass in one props called 'params',
// which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
MyReactHeaderComponent.propTypes = {
params: React.PropTypes.object
};

View File

@@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import RefData from './RefData'; import * as PropTypes from 'prop-types';
var KEY_BACKSPACE = 8; const KEY_BACKSPACE = 8;
var KEY_DELETE = 46; const KEY_DELETE = 46;
var KEY_F2 = 113; const KEY_F2 = 113;
// cell renderer for the proficiency column. this is a very basic cell editor, // cell renderer for the proficiency column. this is a very basic cell editor,
export default class NameCellEditor extends React.Component { export default class NameCellEditor extends React.Component {
@@ -19,9 +19,9 @@ export default class NameCellEditor extends React.Component {
// experience is similar to Excel // experience is similar to Excel
createInitialState(props) { createInitialState(props) {
var startValue; let startValue;
var putCursorAtEndOnFocus = false; const putCursorAtEndOnFocus = false;
var highlightAllOnFocus = false; const highlightAllOnFocus = false;
if (props.keyPress === KEY_BACKSPACE || props.keyPress === KEY_DELETE) { if (props.keyPress === KEY_BACKSPACE || props.keyPress === KEY_DELETE) {
// if backspace or delete pressed, we clear the cell // if backspace or delete pressed, we clear the cell
@@ -55,7 +55,7 @@ export default class NameCellEditor extends React.Component {
onChangeListener(event) { onChangeListener(event) {
// if doing React, you will probably be using a library for managing immutable // if doing React, you will probably be using a library for managing immutable
// objects better. to keep this example simple, we don't use one. // objects better. to keep this example simple, we don't use one.
var newState = { const newState = {
value: event.target.value, value: event.target.value,
putCursorAtEndOnFocus: this.state.putCursorAtEndOnFocus, putCursorAtEndOnFocus: this.state.putCursorAtEndOnFocus,
highlightAllOnFocus: this.state.highlightAllOnFocus highlightAllOnFocus: this.state.highlightAllOnFocus
@@ -72,7 +72,7 @@ export default class NameCellEditor extends React.Component {
// view, it may not yet be in the browser (put in by ag-Grid) so focus will not work // view, it may not yet be in the browser (put in by ag-Grid) so focus will not work
afterGuiAttached() { afterGuiAttached() {
// get ref from React component // get ref from React component
var eInput = this.refs.textField; const eInput = this.refs.textField;
eInput.focus(); eInput.focus();
if (this.highlightAllOnFocus) { if (this.highlightAllOnFocus) {
eInput.select(); eInput.select();
@@ -81,7 +81,7 @@ export default class NameCellEditor extends React.Component {
// this comes into play in two scenarios: a) when user hits F2 and b) // this comes into play in two scenarios: a) when user hits F2 and b)
// when user hits a printable character, then on IE (and only IE) the carot // when user hits a printable character, then on IE (and only IE) the carot
// was placed after the first character, thus 'apply' would end up as 'pplea' // was placed after the first character, thus 'apply' would end up as 'pplea'
var length = eInput.value ? eInput.value.length : 0; const length = eInput.value ? eInput.value.length : 0;
if (length > 0) { if (length > 0) {
eInput.setSelectionRange(length, length); eInput.setSelectionRange(length, length);
} }
@@ -100,7 +100,7 @@ export default class NameCellEditor extends React.Component {
// just to demonstrate, if you type in 'cancel' then the edit will not take effect // just to demonstrate, if you type in 'cancel' then the edit will not take effect
isCancelAfterEnd() { isCancelAfterEnd() {
if (this.state.value && this.state.value.toUpperCase()==='CANCEL') { if (this.state.value && this.state.value.toUpperCase() === 'CANCEL') {
return true; return true;
} else { } else {
return false; return false;
@@ -113,5 +113,5 @@ export default class NameCellEditor extends React.Component {
// this piece is optional. the grid will always pass the 'params' // this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data. // props, so little need for adding this validation meta-data.
NameCellEditor.propTypes = { NameCellEditor.propTypes = {
params: React.PropTypes.object params: PropTypes.object
}; };

View File

@@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import RefData from './RefData';
// cell renderer for the proficiency column. this is a very basic cell renderer, // cell renderer for the proficiency column. this is a very basic cell renderer,
// it is arguable that we should not of used React and just returned a string of // it is arguable that we should not of used React and just returned a string of
@@ -7,7 +6,7 @@ import RefData from './RefData';
export default class ProficiencyCellRenderer extends React.Component { export default class ProficiencyCellRenderer extends React.Component {
render() { render() {
var backgroundColor; let backgroundColor;
if (this.props.value < 20) { if (this.props.value < 20) {
backgroundColor = 'red'; backgroundColor = 'red';
} else if (this.props.value < 60) { } else if (this.props.value < 60) {
@@ -17,7 +16,7 @@ export default class ProficiencyCellRenderer extends React.Component {
} }
return ( return (
<div className="div-percent-bar" style={{ width: this.props.value + '%', backgroundColor: backgroundColor }}> <div className="div-percent-bar" style={{width: this.props.value + '%', backgroundColor: backgroundColor}}>
<div className="div-percent-value">{this.props.value}%</div> <div className="div-percent-value">{this.props.value}%</div>
</div> </div>
); );

View File

@@ -1,13 +1,13 @@
import React from 'react'; import React from 'react';
var PROFICIENCY_NAMES = ['No Filter', 'Above 40%', 'Above 60%', 'Above 80%']; const PROFICIENCY_NAMES = ['No Filter', 'Above 40%', 'Above 60%', 'Above 80%'];
// the proficiency filter component. this demonstrates how to integrate // the proficiency filter component. this demonstrates how to integrate
// a React filter component with ag-Grid. // a React filter component with ag-Grid.
export default class ProficiencyFilter extends React.Component { export default class ProficiencyFilter extends React.Component {
constructor(props) { constructor(props) {
super(); super(props);
this.state = { this.state = {
selected: PROFICIENCY_NAMES[0] selected: PROFICIENCY_NAMES[0]
}; };
@@ -15,14 +15,18 @@ export default class ProficiencyFilter extends React.Component {
// called by agGrid // called by agGrid
doesFilterPass(params) { doesFilterPass(params) {
var value = this.props.valueGetter(params); const value = this.props.valueGetter(params);
var valueAsNumber = parseFloat(value); const valueAsNumber = parseFloat(value);
switch (this.state.selected) { switch (this.state.selected) {
case PROFICIENCY_NAMES[1] : return valueAsNumber >= 40; case PROFICIENCY_NAMES[1] :
case PROFICIENCY_NAMES[2] : return valueAsNumber >= 60; return valueAsNumber >= 40;
case PROFICIENCY_NAMES[3] : return valueAsNumber >= 80; case PROFICIENCY_NAMES[2] :
default : return true; return valueAsNumber >= 60;
case PROFICIENCY_NAMES[3] :
return valueAsNumber >= 80;
default :
return true;
} }
}; };
@@ -32,21 +36,24 @@ export default class ProficiencyFilter extends React.Component {
}; };
onButtonPressed(name) { onButtonPressed(name) {
console.log(name); const newState = {selected: name};
var newState = {selected: name};
// set the state, and once it is done, then call filterChangedCallback // set the state, and once it is done, then call filterChangedCallback
this.setState(newState, this.props.filterChangedCallback); this.setState(newState, this.props.filterChangedCallback);
console.log(name); }
getModel() {
return ''
} }
render() { render() {
var rows = []; const rows = [];
PROFICIENCY_NAMES.forEach( (name)=> { PROFICIENCY_NAMES.forEach((name) => {
var selected = this.state.selected === name; const selected = this.state.selected === name;
rows.push( rows.push(
<div key={name}> <div key={name}>
<label style={{paddingLeft: 4}}> <label style={{paddingLeft: 4}}>
<input type="radio" checked={selected} name={Math.random()} onChange={this.onButtonPressed.bind(this, name)}/> <input type="radio" checked={selected} name={Math.random()}
onChange={this.onButtonPressed.bind(this, name)}/>
{name} {name}
</label> </label>
</div> </div>
@@ -55,7 +62,13 @@ export default class ProficiencyFilter extends React.Component {
return ( return (
<div> <div>
<div style={{textAlign: 'center', background: 'lightgray', width: '100%', display: 'block', borderBottom: '1px solid grey'}}> <div style={{
textAlign: 'center',
background: 'lightgray',
width: '100%',
display: 'block',
borderBottom: '1px solid grey'
}}>
<b>Custom Proficiency Filter</b> <b>Custom Proficiency Filter</b>
</div> </div>
{rows} {rows}

View File

@@ -1,10 +1,11 @@
export default class RefData {} export default class RefData {
}
RefData.FIRST_NAMES = [ RefData.FIRST_NAMES = [
"Sophie", "Isabelle", "Emily", "Olivia", "Lily", "Chloe", "Isabella", "Sophie", "Isabelle", "Emily", "Olivia", "Lily", "Chloe", "Isabella",
"Amelia", "Jessica", "Sophia", "Ava", "Charlotte", "Mia", "Lucy", "Grace", "Ruby", "Amelia", "Jessica", "Sophia", "Ava", "Charlotte", "Mia", "Lucy", "Grace", "Ruby",
"Ella", "Evie", "Freya", "Isla", "Poppy", "Daisy", "Layla" "Ella", "Evie", "Freya", "Isla", "Poppy", "Daisy", "Layla"
]; ];
RefData.LAST_NAMES = [ RefData.LAST_NAMES = [
"Beckham", "Black", "Braxton", "Brennan", "Brock", "Bryson", "Cadwell", "Beckham", "Black", "Braxton", "Brennan", "Brock", "Bryson", "Cadwell",
@@ -12,7 +13,7 @@ RefData.LAST_NAMES = [
"Donovan", "Easton", "Fisher", "Fletcher", "Grady", "Greyson", "Griffin", "Gunner", "Donovan", "Easton", "Fisher", "Fletcher", "Grady", "Greyson", "Griffin", "Gunner",
"Hayden", "Hudson", "Hunter", "Jacoby", "Jagger", "Jaxon", "Jett", "Kade", "Kane", "Hayden", "Hudson", "Hunter", "Jacoby", "Jagger", "Jaxon", "Jett", "Kade", "Kane",
"Keating", "Keegan", "Kingston", "Kobe" "Keating", "Keegan", "Kingston", "Kobe"
]; ];
RefData.COUNTRY_CODES = { RefData.COUNTRY_CODES = {
Ireland: "ie", Ireland: "ie",
@@ -33,7 +34,7 @@ RefData.COUNTRY_CODES = {
Peru: "pe", Peru: "pe",
Venezuela: "ve", Venezuela: "ve",
Uruguay: "uy" Uruguay: "uy"
}; };
RefData.COUNTRIES = [ RefData.COUNTRIES = [
{country: "Ireland", continent: "Europe", language: "English"}, {country: "Ireland", continent: "Europe", language: "English"},
@@ -54,21 +55,21 @@ RefData.COUNTRIES = [
{country: "Peru", continent: "South America", language: "Spanish"}, {country: "Peru", continent: "South America", language: "Spanish"},
{country: "Venezuela", continent: "South America", language: "Spanish"}, {country: "Venezuela", continent: "South America", language: "Spanish"},
{country: "Uruguay", continent: "South America", language: "Spanish"} {country: "Uruguay", continent: "South America", language: "Spanish"}
]; ];
RefData.DOB = [ RefData.DOB = [
new Date(2000, 0, 1 ), new Date(2000, 0, 1),
new Date(2001, 1, 2 ), new Date(2001, 1, 2),
new Date(2002, 2, 3 ), new Date(2002, 2, 3),
new Date(2003, 3, 4 ), new Date(2003, 3, 4),
new Date(2004, 4, 5 ), new Date(2004, 4, 5),
new Date(2005, 5, 6 ), new Date(2005, 5, 6),
new Date(2006, 6, 7 ), new Date(2006, 6, 7),
new Date(2007, 7, 8 ), new Date(2007, 7, 8),
new Date(2008, 8, 9 ), new Date(2008, 8, 9),
new Date(2009, 9, 10 ), new Date(2009, 9, 10),
new Date(2010, 10, 11 ), new Date(2010, 10, 11),
new Date(2011, 11, 12 ) new Date(2011, 11, 12)
]; ];
RefData.ADDRESSES = [ RefData.ADDRESSES = [

View File

@@ -1,7 +1,3 @@
.ag-react-container {
display: block;
}
.ag-cell { .ag-cell {
padding-top: 2px !important; padding-top: 2px !important;
padding-bottom: 2px !important; padding-bottom: 2px !important;

View File

@@ -2,11 +2,10 @@ import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react"; import {AgGridReact} from "ag-grid-react";
import RowDataFactory from "./RowDataFactory"; import RowDataFactory from "./RowDataFactory";
import ColDefFactory from "./ColDefFactory.jsx"; import ColDefFactory from "./ColDefFactory.jsx";
import MyReactDateComponent from "./MyReactDateComponent.jsx"; import DateComponent from "./DateComponent.jsx";
import MyReactHeaderComponent from "./MyReactHeaderComponent.jsx"; import SortableHeaderComponent from "./SortableHeaderComponent";
import "./RichGridExample.css"; import "./RichGridExample.css";
// take this line out if you do not want to use ag-Grid-Enterprise // take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise"; import "ag-grid-enterprise";
@@ -17,7 +16,6 @@ export default class RichGridExample extends Component {
this.state = { this.state = {
quickFilterText: null, quickFilterText: null,
showGrid: true,
showToolPanel: false, showToolPanel: false,
columnDefs: new ColDefFactory().createColDefs(), columnDefs: new ColDefFactory().createColDefs(),
rowData: new RowDataFactory().createRowData(), rowData: new RowDataFactory().createRowData(),
@@ -41,19 +39,20 @@ export default class RichGridExample extends Component {
// what you want! // what you want!
this.gridOptions = { this.gridOptions = {
//We register the react date component that ag-grid will use to render //We register the react date component that ag-grid will use to render
dateComponentFramework: MyReactDateComponent, dateComponentFramework: DateComponent,
// this is how you listen for events using gridOptions // this is how you listen for events using gridOptions
onModelUpdated: function () { onModelUpdated: function () {
console.log('event onModelUpdated received'); console.log('event onModelUpdated received');
}, },
defaultColDef: { defaultColDef: {
headerComponentFramework: MyReactHeaderComponent, headerComponentFramework: SortableHeaderComponent,
headerComponentParams: { headerComponentParams: {
menuIcon: 'fa-bars' menuIcon: 'fa-bars'
} }
}, },
// this is a simple property // this is a simple property
rowBuffer: 10 // no need to set this, the default is fine for almost all scenarios rowBuffer: 10, // no need to set this, the default is fine for almost all scenarios,
floatingFilter: true
}; };
this.onGridReady = this.onGridReady.bind(this); this.onGridReady = this.onGridReady.bind(this);
@@ -61,12 +60,6 @@ export default class RichGridExample extends Component {
this.onCellClicked = this.onCellClicked.bind(this); this.onCellClicked = this.onCellClicked.bind(this);
} }
onShowGrid(show) {
this.setState({
showGrid: show
});
}
onToggleToolPanel(event) { onToggleToolPanel(event) {
this.setState({showToolPanel: event.target.checked}); this.setState({showToolPanel: event.target.checked});
} }
@@ -76,10 +69,6 @@ export default class RichGridExample extends Component {
this.columnApi = params.columnApi; this.columnApi = params.columnApi;
} }
selectAll() {
this.api.selectAll();
}
deselectAll() { deselectAll() {
this.api.deselectAll(); this.api.deselectAll();
} }
@@ -101,15 +90,15 @@ export default class RichGridExample extends Component {
} }
onRefreshData() { onRefreshData() {
var newRowData = new RowDataFactory().createRowData(); let newRowData = new RowDataFactory().createRowData();
this.setState({ this.setState({
rowData: newRowData rowData: newRowData
}); });
} }
invokeSkillsFilterMethod() { invokeSkillsFilterMethod() {
var skillsFilter = this.api.getFilterInstance('skills'); let skillsFilter = this.api.getFilterInstance('skills');
var componentInstance = skillsFilter.getFrameworkComponentInstance(); let componentInstance = skillsFilter.getFrameworkComponentInstance();
componentInstance.helloFromSkillsFilter(); componentInstance.helloFromSkillsFilter();
} }
@@ -117,70 +106,69 @@ export default class RichGridExample extends Component {
let dateFilterComponent = this.gridOptions.api.getFilterInstance('dob'); let dateFilterComponent = this.gridOptions.api.getFilterInstance('dob');
dateFilterComponent.setFilterType('equals'); dateFilterComponent.setFilterType('equals');
dateFilterComponent.setDateFrom('2000-01-01'); dateFilterComponent.setDateFrom('2000-01-01');
this.gridOptions.api.onFilterChanged();
// as the date filter is a React component, and its using setState internally, we need
// to allow time for the state to be set (as setState is an async operation)
// simply wait for the next tick
setTimeout(() => {
this.gridOptions.api.onFilterChanged();
}, 0)
} }
render() { render() {
var gridTemplate; return (
var bottomHeaderTemplate; <div style={{width: '900px'}}>
var topHeaderTemplate; <h1>Rich Grid Example</h1>
<div style={{display: "inline-block", width: "100%"}}>
topHeaderTemplate = ( <div style={{float: "left"}}>
<b>Employees Skills and Contact Details</b><span id="rowCount"/>
</div>
</div>
<div style={{marginTop: 10}}>
<div> <div>
<div style={{float: 'right'}}>
<input type="text" onChange={this.onQuickFilterText.bind(this)}
placeholder="Type text to filter..."/>
<button id="btDestroyGrid" disabled={!this.state.showGrid}
onClick={this.onShowGrid.bind(this, false)}>Destroy Grid
</button>
<button id="btCreateGrid" disabled={this.state.showGrid} onClick={this.onShowGrid.bind(this, true)}>
Create Grid
</button>
</div>
<div style={{padding: '4px'}}>
<b>Employees Skills and Contact Details</b> <span id="rowCount"/>
</div>
</div>
);
// showing the bottom header and grid is optional, so we put in a switch
if (this.state.showGrid) {
bottomHeaderTemplate = (
<div>
<div style={{padding: 4}} className={'toolbar'}>
<span> <span>
Grid API: Grid API:
<button onClick={this.selectAll.bind(this)}>Select All</button> <button onClick={() => {
<button onClick={this.deselectAll.bind(this)}>Clear Selection</button> this.refs.myGrid.api.selectAll()
}} className="btn btn-primary">Select All</button>
<button onClick={this.deselectAll.bind(this)}
className="btn btn-primary">Clear Selection</button>
</span> </span>
<span style={{marginLeft: 20}}> <span style={{float: "right"}}>
Column API: Column API:
<button onClick={this.setCountryVisible.bind(this, false)}>Hide Country Column</button> <button onClick={this.setCountryVisible.bind(this, false)} className="btn btn-primary">Hide Country Column</button>
<button onClick={this.setCountryVisible.bind(this, true)}>Show Country Column</button> <button onClick={this.setCountryVisible.bind(this, true)} className="btn btn-primary">Show Country Column</button>
</span> </span>
</div> </div>
<div style={{clear: 'both'}}></div> <div style={{display: "inline-block", width: "100%", marginTop: 10, marginBottom: 10}}>
<div style={{padding: 4}} className={'toolbar'}> <div style={{float: "left"}}>
<span>
<label> <label>
<input type="checkbox" onChange={this.onToggleToolPanel.bind(this)}/> <input type="checkbox" onChange={this.onToggleToolPanel.bind(this)}
style={{marginRight: 5}}/>
Show Tool Panel Show Tool Panel
</label> </label>
<button onClick={this.onRefreshData.bind(this)}>Refresh Data</button> </div>
</span> <div style={{float: "left", marginLeft: 20}}>
<span style={{marginLeft: 20}}> <button onClick={this.onRefreshData.bind(this)} className="btn btn-primary">Refresh Data
</button>
</div>
<div style={{float: "left", marginLeft: 20}}>
<input type="text" onChange={this.onQuickFilterText.bind(this)}
placeholder="Type text to filter..."/>
</div>
<div style={{float: "right"}}>
Filter API: Filter API:
<button onClick={this.invokeSkillsFilterMethod.bind(this, false)}>Invoke Skills Filter Method</button> <button onClick={this.invokeSkillsFilterMethod.bind(this, false)}
<button onClick={this.dobFilter.bind(this)}>DOB equals to 01/01/2000</button> className="btn btn-primary">Invoke Skills Filter Method
</span> </button>
<button onClick={this.dobFilter.bind(this)} className="btn btn-primary">DOB equals to
01/01/2000
</button>
</div> </div>
<div style={{clear: 'both'}}></div>
</div> </div>
); <div style={{height: 400, width: 900}} className="ag-fresh">
gridTemplate = (
<div style={{height: 400, width: 945}} className="ag-fresh">
<AgGridReact <AgGridReact
ref="myGrid"
// gridOptions is optional - it's possible to provide // gridOptions is optional - it's possible to provide
// all values as React props // all values as React props
gridOptions={this.gridOptions} gridOptions={this.gridOptions}
@@ -211,16 +199,132 @@ export default class RichGridExample extends Component {
rowHeight="22" rowHeight="22"
/> />
</div> </div>
<div>
<div className="row">
<div className="col-sm-12"><h1>Rich Grid Example</h1></div>
</div>
<div className="row">
<div className="col-sm-12">
<h5>This example demonstrates many features of ag-Grid.</h5>
<p><span style={{fontWeight: 500}}>Select All/Clear Selection</span>: Select or Deselect
All
Rows</p>
<p><span style={{fontWeight: 500}}>Hide/Show Country Column</span>: Select or Deselect
All
Rows
(expand the Employee column to show the Country column first)</p>
<p><span style={{fontWeight: 500}}>Toggle The Tool Panel</span>: Let your users Pivot,
Group
and
Aggregate using the Tool Panel</p>
<p><span style={{fontWeight: 500}}>Refresh Data</span>: Dynamically Update Grid Data</p>
<p><span style={{fontWeight: 500}}>Quick Filter</span>: Perform Quick Grid Wide
Filtering
with
the Quick Filter</p>
<p><span style={{fontWeight: 500}}>DOB Filter</span>: Set the DOB filter to 01/01/2000
using
the
Filter API (expand the Employee column to show the DOB column)</p>
<p><span style={{fontWeight: 500}}>Custom Headers</span>: Sort, Filter and Render
Headers
using
Header Components</p>
</div>
</div>
<div className="row">
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Grid & Column API</h4>
<p className="card-text">Utilise Grid Features Programmatically Using the APIs
Available</p>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-api/"
className="btn btn-primary">Grid API</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-column-api/"
className="btn btn-primary">Column API</a>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Header Components</h4>
<p className="card-text">Customer the Header with React Components</p>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-header-rendering/#headerComponent"
className="btn btn-primary">Header Component</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-header-rendering/#headerGroupComponent"
className="btn btn-primary">Header Group Component</a>
</div>
</div>
</div>
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Filters</h4>
<p className="card-text">Filter with Quick Filters, Floating Filters, Built In
Filters
or Using the
Filter API</p>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-filter-quick/"
className="btn btn-primary">Quick
Filter</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-floating-filter-component/"
className="btn btn-primary">Floating Filters</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-filtering/"
className="btn btn-primary">Built in
Filters</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-filtering/#accessing-filter-component-instances"
className="btn btn-primary">Filter API</a>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-sm-4">
<div className="card">
<div className="card-body">
<h4 className="card-title">Tool Panel</h4>
<p className="card-text">Let your users Pivot, Group and Aggregate using the
Tool
Panel</p>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-tool-panel/"
className="btn btn-primary">Tool Panel</a>
</div>
</div>
</div>
<div className="col-sm-8">
<div className="card">
<div className="card-body">
<h4 className="card-title">The Rest</h4>
<p className="card-text">Pinned Columns, Checkbox Selection, Customer Renderers,
Data
Updates</p>
<a target="_blank"
href="https://www.ag-grid.com/best-react-data-grid/?framework=react"
className="btn btn-primary">React with ag-Grid</a>
<a target="_blank" href="https://www.ag-grid.com//javascript-grid-pinning/"
className="btn btn-primary">Pinned Column</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-selection/#checkboxSelection"
className="btn btn-primary">Checkbox Selection</a>
<a target="_blank"
href="https://www.ag-grid.com//javascript-grid-cell-rendering/"
className="btn btn-primary">Cell
Renderers</a>
<a target="_blank" href="https://www.ag-grid.com/javascript-grid-data-update/"
className="btn btn-primary">Updating Data</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
); );
} }
return <div style={{width: '950px'}}>
<div style={{padding: '4px'}}>
{topHeaderTemplate}
{bottomHeaderTemplate}
{gridTemplate}
</div>
</div>;
}
} }

View File

@@ -3,10 +3,10 @@ import RefData from './RefData';
export default class RowDataFactory { export default class RowDataFactory {
createRowData() { createRowData() {
var rowData = []; const rowData = [];
for (var i = 0; i < 200; i++) { for (let i = 0; i < 200; i++) {
var countryData = RefData.COUNTRIES[i % RefData.COUNTRIES.length]; const countryData = RefData.COUNTRIES[i % RefData.COUNTRIES.length];
rowData.push({ rowData.push({
name: RefData.FIRST_NAMES[i % RefData.FIRST_NAMES.length] + ' ' + RefData.LAST_NAMES[i % RefData.LAST_NAMES.length], name: RefData.FIRST_NAMES[i % RefData.FIRST_NAMES.length] + ' ' + RefData.LAST_NAMES[i % RefData.LAST_NAMES.length],
skills: { skills: {
@@ -32,8 +32,8 @@ export default class RowDataFactory {
} }
createRandomPhoneNumber() { createRandomPhoneNumber() {
var result = '+'; let result = '+';
for (var i = 0; i < 12; i++) { for (let i = 0; i < 12; i++) {
result += Math.round(Math.random() * 10); result += Math.round(Math.random() * 10);
if (i === 2 || i === 5 || i === 8) { if (i === 2 || i === 5 || i === 8) {
result += ' '; result += ' ';

View File

@@ -1,14 +1,15 @@
import React from 'react'; import React from 'react';
import * as PropTypes from 'prop-types';
import RefData from './RefData'; import RefData from './RefData';
export default class SkillsCellRenderer extends React.Component { export default class SkillsCellRenderer extends React.Component {
render() { render() {
var skills = []; const skills = [];
var rowData = this.props.data; const rowData = this.props.data;
RefData.IT_SKILLS.forEach( (skill) => { RefData.IT_SKILLS.forEach((skill) => {
if (rowData && rowData.skills && rowData.skills[skill]) { if (rowData && rowData.skills && rowData.skills[skill]) {
skills.push(<img key={skill} src={'images/skills/' + skill + '.png'} width={16} title={skill} />); skills.push(<img key={skill} src={'images/skills/' + skill + '.png'} width={16} title={skill}/>);
} }
}); });
@@ -22,5 +23,5 @@ export default class SkillsCellRenderer extends React.Component {
// this piece is optional. the grid will always pass the 'params' // this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data. // props, so little need for adding this validation meta-data.
SkillsCellRenderer.propTypes = { SkillsCellRenderer.propTypes = {
params: React.PropTypes.object params: PropTypes.object
}; };

View File

@@ -31,21 +31,21 @@ export default class SkillsFilter extends React.Component {
setModel(model) { setModel(model) {
this.setState({ this.setState({
android: model.android, android: model ? model.android : null,
css: model.css, css: model ? model.css : null,
html5: model.html5, html5: model ? model.html5 : null,
mac: model.mac, mac: model ? model.mac : null,
windows: model.windows windows: model ? model.windows : null
}); });
} }
// called by agGrid // called by agGrid
doesFilterPass(params) { doesFilterPass(params) {
var rowSkills = params.data.skills; const rowSkills = params.data.skills;
var passed = true; let passed = true;
RefData.IT_SKILLS.forEach( (skill) => { RefData.IT_SKILLS.forEach((skill) => {
if (this.state[skill]) { if (this.state[skill]) {
if (!rowSkills[skill]) { if (!rowSkills[skill]) {
passed = false; passed = false;
@@ -56,19 +56,23 @@ export default class SkillsFilter extends React.Component {
return passed; return passed;
}; };
getModel() {
return ''
}
// called by agGrid // called by agGrid
isFilterActive() { isFilterActive() {
var somethingSelected = this.state.android || this.state.css || const somethingSelected = this.state.android || this.state.css ||
this.state.html5 || this.state.mac || this.state.windows; this.state.html5 || this.state.mac || this.state.windows;
return somethingSelected; return somethingSelected;
}; };
onSkillChanged(skill, event) { onSkillChanged(skill, event) {
var newValue = event.target.checked; const newValue = event.target.checked;
var newModel = {}; const newModel = {};
newModel[skill] = newValue; newModel[skill] = newValue;
// set the state, and once it is done, then call filterChangedCallback // set the state, and once it is done, then call filterChangedCallback
this.setState(newModel, this.props.filterChangedCallback ); this.setState(newModel, this.props.filterChangedCallback);
} }
helloFromSkillsFilter() { helloFromSkillsFilter() {
@@ -77,17 +81,18 @@ export default class SkillsFilter extends React.Component {
render() { render() {
var skillsTemplates = []; const skillsTemplates = [];
RefData.IT_SKILLS.forEach( (skill, index) => { RefData.IT_SKILLS.forEach((skill, index) => {
var skillName = RefData.IT_SKILLS_NAMES[index]; const skillName = RefData.IT_SKILLS_NAMES[index];
var template = ( const template = (
<label key={skill} style={{border: '1px solid lightgrey', margin: 4, padding: 4, display: 'inline-block'}}> <label key={skill}
style={{border: '1px solid lightgrey', margin: 4, padding: 4, display: 'inline-block'}}>
<span> <span>
<div style={{textAlign: 'center'}}>{skillName}</div> <div style={{textAlign: 'center'}}>{skillName}</div>
<div> <div>
<input type="checkbox" onClick={this.onSkillChanged.bind(this, skill)}/> <input type="checkbox" onClick={this.onSkillChanged.bind(this, skill)}/>
<img src={'images/skills/'+skill+'.png'} width={30}/> <img src={'images/skills/' + skill + '.png'} width={30}/>
</div> </div>
</span> </span>
</label> </label>
@@ -98,7 +103,13 @@ export default class SkillsFilter extends React.Component {
return ( return (
<div style={{width: 380}}> <div style={{width: 380}}>
<div style={{textAlign: 'center', background: 'lightgray', width: '100%', display: 'block', borderBottom: '1px solid grey'}}> <div style={{
textAlign: 'center',
background: 'lightgray',
width: '100%',
display: 'block',
borderBottom: '1px solid grey'
}}>
<b>Custom Skills Filter</b> <b>Custom Skills Filter</b>
</div> </div>
{skillsTemplates} {skillsTemplates}

View File

@@ -0,0 +1,89 @@
import React from "react";
import * as PropTypes from "prop-types";
// Header component to be used as default for all the columns.
export default class SortableHeaderComponent extends React.Component {
constructor(props) {
super(props);
// this.sortChanged = this.onSortChanged.bind(this);
this.props.column.addEventListener('sortChanged', this.onSortChanged);
//The state of this component contains the current sort state of this column
//The possible values are: 'asc', 'desc' and ''
this.state = {
sorted: ''
}
}
componentWillUnmount() {
this.props.column.removeEventListener('sortChanged', this.onSortChanged);
}
render() {
let sortElements = [];
if (this.props.enableSorting) {
let downArrowClass = "customSortDownLabel " + (this.state.sorted === 'desc' ? " active" : "");
let upArrowClass = "customSortUpLabel " + (this.state.sorted === 'asc' ? " active" : "");
let removeArrowClass = "customSortRemoveLabel " + (this.state.sorted === '' ? " active" : "");
sortElements.push(<div key={`up${this.props.displayName}`} className={downArrowClass}
onClick={this.onSortRequested.bind(this, 'desc')}><i
className="fa fa-long-arrow-down"/></div>);
sortElements.push(<div key={`down${this.props.displayName}`} className={upArrowClass}
onClick={this.onSortRequested.bind(this, 'asc')}><i
className="fa fa-long-arrow-up"/></div>);
sortElements.push(<div key={`minus${this.props.displayName}`} className={removeArrowClass}
onClick={this.onSortRequested.bind(this, '')}><i
className="fa fa-times"/></div>)
}
let menuButton = null;
if (this.props.enableMenu) {
menuButton =
<div ref="menuButton" className="customHeaderMenuButton" onClick={this.onMenuClick.bind(this)}><i
className={"fa " + this.props.menuIcon}/></div>
}
return <div>
{menuButton}
<div className="customHeaderLabel">{this.props.displayName}</div>
{sortElements}
</div>
}
onSortRequested(order, event) {
this.props.setSort(order, event.shiftKey);
};
onSortChanged = () => {
if (this.props.column.isSortAscending()) {
this.setState({
sorted: 'asc'
})
} else if (this.props.column.isSortDescending()) {
this.setState({
sorted: 'desc'
})
} else {
this.setState({
sorted: ''
})
}
};
onMenuClick() {
this.props.showColumnMenu(this.refs.menuButton);
};
}
// the grid will always pass in one props called 'params',
// which is the grid passing you the params for the cellRenderer.
// this piece is optional. the grid will always pass the 'params'
// props, so little need for adding this validation meta-data.
SortableHeaderComponent.propTypes = {
params: PropTypes.object
};

View File

@@ -0,0 +1,57 @@
import React, {Component} from "react";
import {AgGridReact} from "ag-grid-react";
import {connect} from "react-redux";
import PriceRenderer from "./PriceRenderer";
/*
* 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', cellClass: 'align-right', cellRendererFramework: PriceRenderer}
]
};
this.onGridReady = this.onGridReady.bind(this);
}
onGridReady(params) {
this.gridApi = params.api;
this.columnApi = params.columnApi;
this.gridApi.sizeColumnsToFit();
}
// row data will be provided via redux on this.props.rowData
render() {
return (
<div style={{height: 400, width: 900, marginTop: 15}}
className="ag-fresh">
<AgGridReact
// properties
columnDefs={this.state.columnDefs}
rowData={this.props.rowData}
// events
onGridReady={this.onGridReady}>
</AgGridReact>
</div>
)
}
}
// pull off row data changes
export default connect(
(state) => {
return {
rowData: state.rowData
}
}
)(GridComponent);

View File

@@ -0,0 +1,85 @@
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 {setCurrency, 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);
}
componentDidMount() {
// provide the initial data to the store (which in turn will populate the grid)
this.props.dispatch(updateRowData(this.createRowData()));
}
setCurrency(currencySymbol, exchangeRate) {
this.props.dispatch(setCurrency(currencySymbol, exchangeRate));
}
render() {
return (
<div style={{marginTop: 15}}>
<button onClick={this.setCurrency.bind(this, '£', 1)}>Set Currency to GBP</button>
<button onClick={this.setCurrency.bind(this, '$', 1.29)}>Set Currency to USD</button>
</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 {
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
export default connect(
(state) => {
return {
rowData: state.rowData
}
}
)(HeaderComponent);

View File

@@ -0,0 +1,37 @@
import React, {Component} from "react";
import {connect} from "react-redux";
class PriceRenderer extends Component {
constructor(props) {
super(props);
this.state = {
convertedValue: this.applyExchangeRate(props.exchangeRate, props.value)
};
}
componentWillReceiveProps(nextProps) {
this.setState({
convertedValue: this.applyExchangeRate(nextProps.exchangeRate, nextProps.value)
})
}
render() {
return (
<span>{this.props.currencySymbol}{this.state.convertedValue}</span>
);
}
applyExchangeRate = (exchangeRate, value) => {
return parseFloat(value * exchangeRate).toFixed(2); // simplified/naive exchange rate implementation!
}
}
export default connect(
(state) => {
return {
currencySymbol: state.currencySymbol,
exchangeRate: state.exchangeRate
}
}
)(PriceRenderer);

View File

@@ -0,0 +1,34 @@
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);
}
render() {
return (
<Provider store={store}>
<div>
<h1>Simple Redux Example using Connected React Components</h1>
<HeaderComponent />
<GridComponent />
</div>
</Provider>
)
}
};

View File

@@ -0,0 +1,13 @@
export function updateRowData(rowData) {
return {
type: 'ROW_DATA_CHANGED',
rowData
}
}
export function setCurrency(currencySymbol, exchangeRate) {
return {
type: 'CURRENCY_CHANGED',
currencySymbol,
exchangeRate
}
}

View File

@@ -0,0 +1,17 @@
export default (state = {rowData: [], currencySymbol: '£', exchangeRate: 1}, action) => {
switch (action.type) {
case 'ROW_DATA_CHANGED':
return {
...state,
rowData: action.rowData,
};
case 'CURRENCY_CHANGED':
return {
...state,
currencySymbol: action.currencySymbol,
exchangeRate: action.exchangeRate
};
default:
return state;
}
};

View File

@@ -62,7 +62,7 @@ class GridComponent extends Component {
// this requires each row to have a uniquely identifying property - in this case the row data "symbol" (see getRowNodeId) // this requires each row to have a uniquely identifying property - in this case the row data "symbol" (see getRowNodeId)
render() { render() {
return ( return (
<div style={{height: 400, width: 945, marginTop: 15}} <div style={{height: 400, width: 900, marginTop: 15}}
className="ag-fresh"> className="ag-fresh">
<AgGridReact <AgGridReact
// properties // properties
@@ -75,7 +75,7 @@ class GridComponent extends Component {
enableColResize enableColResize
rowSelection="multiple" rowSelection="multiple"
enableRangeSelection enableRangeSelection
groupColumnDef={{ autoColumnGroupDef={{
headerName: 'Symbol', headerName: 'Symbol',
cellRenderer: 'group', cellRenderer: 'group',
field: 'symbol' field: 'symbol'