<html>
<head>
    <title>ag-Grid React Example</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

</head>
<body>
    <div id="root">Loading ag-Grid React example&hellip;</div>
    
    <script>
        var appLocation = '';
        var boilerplatePath = '';
        var systemJsMap = {"ag-grid":"https:\/\/unpkg.com\/ag-grid@13.3.1\/dist\/ag-grid.js","ag-grid\/main":"https:\/\/unpkg.com\/ag-grid@13.3.1\/dist\/ag-grid.js","ag-grid-enterprise":"https:\/\/unpkg.com\/ag-grid-enterprise@13.3.1\/","ag-grid-react":"npm:ag-grid-react@13.3.0\/","ag-grid-angular":"npm:ag-grid-angular@13.3.0\/"}    </script>

    <script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
    <script src="systemjs.config.js"></script>

    <script>
      System.import('index.jsx').catch( function(err) {
        console.error(err);
      })
    </script>
</body>
</html>
import React, {Component} from "react";

import {AgGridReact} from "ag-grid-react";
import {connect} from "react-redux";

// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";

import {updateRowSelection} from "./gridDataActions.jsx";

/*
 * 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', width: 300},
                {headerName: 'Price', field: 'price', width: 300},
                {headerName: 'Group', field: 'group', width: 300}
            ]
        };

        this.onGridReady = this.onGridReady.bind(this);
        this.onSelectionChanged = this.onSelectionChanged.bind(this);
        this.setGroupingEnabled = this.setGroupingEnabled.bind(this);
    }

    onGridReady(params) {
        this.gridApi = params.api;
        this.columnApi = params.columnApi;

        this.gridApi.sizeColumnsToFit();

        // set the initial group state
        this.setGroupingEnabled(false);
    }

    // on selection publish selected row ids
    onSelectionChanged() {
        let selectedRowNodes = this.gridApi.getSelectedNodes();
        let selectedIds = selectedRowNodes.map((rowNode) => rowNode.id);

        this.props.dispatch(updateRowSelection(selectedIds));
    }

    setGroupingEnabled(enabled) {
        if (enabled) {
            this.columnApi.addRowGroupColumn('group');
            this.columnApi.setColumnVisible('group', false);
            this.columnApi.setColumnVisible('symbol', false);
        } else {
            this.columnApi.removeRowGroupColumn('group');
            this.columnApi.setColumnVisible('group', true);
            this.columnApi.setColumnVisible('symbol', true);
        }
    }

    // row data will be provided via redux on this.props.rowData
    // we bind to this and using "deltaRowDataMode" the grid will only re-render rows that have changed
    // this requires each row to have a uniquely identifying property - in this case the row data "symbol" (see getRowNodeId)
    render() {
        return (
            <div style={{height: 400, width: 900, marginTop: 15}} className="ag-fresh">
                <AgGridReact
                    // properties
                    columnDefs={this.state.columnDefs}
                    rowData={this.props.rowData}

                    deltaRowDataMode
                    enableStatusBar
                    animateRows
                    enableColResize
                    rowSelection="multiple"
                    enableRangeSelection
                    autoColumnGroupDef={{
                        headerName: 'Symbol',
                        cellRenderer: 'group',
                        field: 'symbol'
                    }}
                    groupDefaultExpanded="1"
                    enableSorting
                    getRowNodeId={(data) => data.symbol}

                    // events
                    onGridReady={this.onGridReady}
                    onSelectionChanged={this.onSelectionChanged}>
                </AgGridReact>
            </div>
        )
    }
}

// pull off row data changes
export default connect(
    (state) => {
        return {
            rowData: state.rowData
        }
    },
    null,
    null,
    {withRef: true}
)(GridComponent);
import React, {Component} from "react";
import {connect} from "react-redux";

// take this line out if you do not want to use ag-Grid-Enterprise
import "ag-grid-enterprise";

import {updateRowData} from "./gridDataActions.jsx";

/*
 * This component serves both to host the demo controls, which in turn will drive row data state changes
 */
class HeaderComponent extends Component {
    constructor(props) {
        super(props);

        this.addFiveItems = this.addFiveItems.bind(this);
        this.removeSelected = this.removeSelected.bind(this);
        this.updatePrices = this.updatePrices.bind(this);
        this.setGroupingEnabled = this.setGroupingEnabled.bind(this);
        this.setItemVisible = this.setItemVisible.bind(this);
        this.setSelectedToGroup = this.setSelectedToGroup.bind(this);
    }

    componentDidMount() {
        // provide the initial data to the store (which in turn will populate the grid)
        this.props.dispatch(updateRowData(this.createRowData()));
    }

    // add five new items to the row data and publish the change
    addFiveItems() {
        let newRowData = this.props.rowData.slice();
        for (let i = 0; i < 5; i++) {
            let newItem = this.createItem();
            newRowData.push(newItem);
        }

        this.props.dispatch(updateRowData(newRowData));
    }

    // remove selected rows from the data set and publish the change
    removeSelected() {
        let newRowData = this.props.rowData.filter((dataItem) => (this.props.rowSelection.indexOf(dataItem.symbol) < 0));
        this.props.dispatch(updateRowData(newRowData));
    }

    // group data based on selection and publish the change
    setSelectedToGroup(newGroup) {
        let selectedIds = this.props.rowSelection;
        let newRowData = this.props.rowData.map((dataItem) => {
            let itemSelected = selectedIds.indexOf(dataItem.symbol) >= 0;
            if (itemSelected) {
                return {
                    // symbol and price stay the same
                    symbol: dataItem.symbol,
                    price: dataItem.price,
                    // group gets the group
                    group: newGroup
                };
            } else {
                return dataItem;
            }
        });

        this.props.dispatch(updateRowData(newRowData));
    }

    // randomly update prices in the row data and publish the change
    updatePrices() {
        let newRowData = [];
        this.props.rowData.forEach(function (item) {
            newRowData.push({
                // use same symbol as last time, this is the unique id
                symbol: item.symbol,
                // group also stays the same
                group: item.group,
                // add random price
                price: Math.floor(Math.random() * 100)
            });
        });

        this.props.dispatch(updateRowData(newRowData));
    }


    setGroupingEnabled(enabled) {
        // let the parent (and the grid in turn) know about the grouping state change
        this.props.setGroupingEnabled(enabled);

        // toggle the grouping buttons visibility
        this.setItemVisible('groupingOn', !enabled);
        this.setItemVisible('groupingOff', enabled);
    }

    setItemVisible(id, visible) {
        let element = document.querySelector('#' + id);
        element.style.display = visible ? null : 'none';
    }

    render() {
        return (
            <div style={{marginTop: 15}}>
                <button onClick={this.addFiveItems} style={{marginLeft: 5}}>Add Five Items</button>
                <button onClick={this.removeSelected} style={{marginLeft: 5}}>Remove Selected</button>
                <button onClick={this.updatePrices} style={{marginLeft: 5}}>Update Prices</button>

                <span style={{padding: 10}}/>
                <button id="groupingOn" onClick={() => this.setGroupingEnabled(true)}>Turn Grouping On</button>
                <button id="groupingOff" style={{display: "none"}} onClick={() => this.setGroupingEnabled(false)}>Turn
                    Grouping Off
                </button>
                <span style={{padding: 10}}/>
                <span style={{border: "1px  solid lightgrey", padding: 4}}>
                Group Selected:
                    <button onClick={() => this.setSelectedToGroup('A')} style={{marginLeft: 5}}>A</button>
                    <button onClick={() => this.setSelectedToGroup('B')} style={{marginLeft: 5}}>B</button>
                    <button onClick={() => this.setSelectedToGroup('C')} style={{marginLeft: 5}}>C</button>
                </span>
            </div>
        )
    }

    // the following methods are for creating dummy row data
    createRowData() {
        let rowData = [];

        for (let i = 0; i < 14; i++) {
            let newItem = this.createItem();
            rowData.push(newItem);
        }

        return rowData;
    }

    createItem() {
        return {
            group: 'A',
            symbol: this.createUniqueRandomSymbol(),
            price: Math.floor(Math.random() * 100)
        };
    }

    // creates a unique symbol, eg 'ADG' or 'ZJD'
    createUniqueRandomSymbol() {
        let symbol;
        let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        let isUnique = false;
        while (!isUnique) {
            symbol = '';
            // create symbol
            for (let i = 0; i < 3; i++) {
                symbol += possible.charAt(Math.floor(Math.random() * possible.length));
            }
            // check uniqueness
            isUnique = true;
            this.props.rowData.forEach(function (oldItem) {
                if (oldItem.symbol === symbol) {
                    isUnique = false;
                }
            });
        }

        return symbol;
    }
}

// pull off row data and selected row changes
export default connect(
    (state) => {
        return {
            rowData: state.rowData,
            rowSelection: state.rowSelection
        }
    }
)(HeaderComponent);
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.jsx";
import GridComponent from "./GridComponent.jsx";

import gridData from "./gridDataReducer.jsx";

let store = createStore(gridData);

/*
 * This component serves as a container for both the header and grid components. It's primarily here to act as a container
 * for the redux Provider
 */
export default class SimpleReduxExample extends Component {
    constructor(props) {
        super(props);

        this.setGroupingEnabled = this.setGroupingEnabled.bind(this);
    }

    setGroupingEnabled(enabled) {
        this.grid.setGroupingEnabled(enabled);
    }

    render() {
        return (
            <Provider store={store}>
                <div>
                    <HeaderComponent setGroupingEnabled={this.setGroupingEnabled} />
                    <GridComponent ref={ grid => { this.grid = grid ? grid.getWrappedInstance() : null }} />
                </div>
            </Provider>
        )
    }
};
export function updateRowData(rowData) {
    return {
        type: 'ROW_DATA_CHANGED',
        rowData
    }
}

export function updateRowSelection(rowSelection) {
    return {
        type: 'ROW_SELECTION_CHANGED',
        rowSelection
    }
}
export default (state = {rowData: [], rowSelection: []}, action) => {
    switch (action.type) {
        case 'ROW_DATA_CHANGED':
            return {
                ...state,
                rowData: action.rowData,
            };
        case 'ROW_SELECTION_CHANGED':
            return {
                ...state,
                rowSelection: action.rowSelection,
            };
        default:
            return state;
    }
};
'use strict'

import React from "react";
import {render} from "react-dom"

import Grid from './SimpleReduxExample.jsx'

render(
    <Grid></Grid>,
    document.querySelector('#root')
)
(function(global) {
    System.config({
        transpiler: 'plugin-babel',
        defaultExtension: 'js',
        paths: {
            'npm:': 'https://unpkg.com/'
        },
        map: Object.assign(
            {
                // babel transpiler
                'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js',
                'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js',

                // react
                react: 'npm:react@16.0.0',
                'react-dom': 'npm:react-dom@16.0.0',
                'react-dom-factories': 'npm:react-dom-factories',
                redux: 'npm:redux@3.6.0',
                'react-redux': 'npm:react-redux@5.0.6',
                'prop-types': 'npm:prop-types',

                app: appLocation + 'app'
            },
            systemJsMap
        ), // systemJsMap comes from index.html

        packages: {
            react: {
                main: './umd/react.production.min.js'
            },
            'react-dom': {
                main: './umd/react-dom.production.min.js'
            },
            'prop-types': {
                main: './prop-types.min.js',
                defaultExtension: 'js'
            },
            redux: {
                main: './dist/redux.min.js',
                defaultExtension: 'js'
            },
            'react-redux': {
                main: './dist/react-redux.min.js',
                defaultExtension: 'js'
            },
            app: {
                defaultExtension: 'jsx'
            },
            'ag-grid-react': {
                main: './main.js',
                defaultExtension: 'js'
            },
            'ag-grid-enterprise': {
                main: './main.js',
                defaultExtension: 'js'
            }
        },
        meta: {
            '*.jsx': {
                babelOptions: {
                    react: true
                }
            }
        }
    });
})(this);