A Grid and Tree Component written in React using the Redux Pattern with plenty of open source examples, and an interesting backstory.
- Flat List or Tree Structure ➖ 🌲
- Local and/or Remote Data Source
- Local and/or Remote Pagination
- Extensive Column Definitions 💪
- Draggable Column Width/Resizing
- Draggable Column Ordering
- Sortable Columns
- Grid Action Menus
- Bulk Action Toolbar
- Selection Model (Single, MultiSelect, Checkbox)
- Event Handling for all kinds of DOM Events (List Below)
- Extendable and Modular Style Built with JavaScript
- Loading Mask
- Built-in Error Handling Module
- Handles Huge amount of Records (1000000+) ⭐
$ npm install react-redux-grid --save
If you would like to build and run the demo in your browser:
$ git clone https://github.com/bencripps/react-redux-grid.git
$ cd react-redux-grid
$ npm install
$ npm run start
Open your browser to: http://localhost:3000
import React from 'react';
import { render } from 'react-dom';
import { Grid } from 'react-redux-grid';
render(
<Grid
data={data}
stateKey={stateKey}
/>,
document.getElementById('grid-mount')
);
- Grid
- Columns
- Editor
- Column Manager
- Pagination
- Row Actions
- Actions
- Bulk Selection
- Types
- Error Handling
- Loading Mask
- Bulk Actions
- Eventing
- Style
- How do I create a Tree Grid
- How do I import Grid Reducers
- Is there more information on Column Renderers
- Is there more information on Column Editors
- How do I enable the Sticky Header/Footer
- How do I create Custom Pagination
- How is Font Awesome Incorporated
Prop | Type | Description |
---|---|---|
stateful | bool | the grid will store column configuration in browser local storage (based off of stateKey , so the key must be unique across all grids in a single application) |
height | oneOfType([number, string, bool]) | the height of the grid container, if false , then no height will be set |
stateKey | string | unique id for grid, more information available |
showTreeRootNode | bool | used with tree-grid, to determine if root node should be displayed |
classNames | array | a list of strings to be applied to the grid container as classes |
events | object | grid event object, more information below |
reducerKeys | object | object describing custom named reducers, more information below |
pageSize | int | number of records to shown on a single grid page |
emptyDataMessage | any | can be a string or a react component, which will be displayed if no grid data is available |
dragAndDrop | bool | whether drag and drop of rows should be enabled |
gridType | oneOf(['grid', 'tree']) | whether the grid will be a flat list or a tree view |
data | arrayOf(object) | local data for grid to display, more information available |
dataSource | func | function which returns data to display, more information available |
filterFields | object | optional object describing additional values to filter grid data |
export const columns = [
{
name: 'Name',
dataIndex: 'name',
editor: '<input type="text" required />',
width: '10%',
className: 'additional-class',
renderer: ({ column, value, row }) => (
<span> Name: { value } </span>
),
hidden: false,
placeholder: 'Name',
validator: ({ value, values }) => value.length > 0,
change: ({ values }) => ({
otherColDataIndex: 'newValue'
}),
editable: ({ value, values }) => {
if (value === 'ShouldDisabled') {
return true;
}
return false;
},
hideable: false,
resizable: false,
moveable: false,
HANDLE_CLICK: () => { console.log('Header Click'); },
createKeyFrom: true
}
];
Prop | Type | Description |
---|---|---|
name | string | title of column to be displayed |
dataIndex | oneOfType([string, array]) | the key accessor for the column value (required parameter). more information available |
editor | jsx | when an editor is used, this element will be rendered in place of the edited cell, more information available |
width | int | width of column (if none is provided, a default width will be applied) |
className | array | additional class names to apply to header of this column |
renderer | func | a function which returns the cell contents for this column, more information available |
hidden | bool | whether the column is hidden or visible |
hideable | bool | whether the column can be hidden |
moveable | bool | whether this column can be moved |
placeholder | string | the placeholder that will be used for the editor input |
validator | func | a func that should return a boolean, to determine if the newly input value is valid |
change | func | a func that should return an object where keys are the dataIndex of affected columns, and the values will be the new values associated with that dataIndex. |
editable | oneOfType([func, bool]) | whether the field should be disabled while in edit mode. |
createKeyFrom | bool | see full documentation on createKeyFrom |
sortFn | func | when a local sort action occurs, you can provide a method that will be passed to sort |
export const plugins = {
EDITOR: {
type: 'inline',
enabled: true,
focusOnEdit: true
}
}
Prop | Type | Description |
---|---|---|
type | oneOf(['inline', 'grid']) | two editors are available by default. in grid mode, all fields are editable. in inline mode, only a single line is editable at a time |
enabled | bool | if true, the grid will have an editor available |
focusOnEdit | bool | focus the first editable input when an edit event occurs (defaults to true) |
export const plugins = {
COLUMN_MANAGER: {
resizable: false
defaultColumnWidth: `${100 / columns.length}%`,
minColumnWidth: 10,
moveable: true,
headerActionItemBuilder: () => {},
sortable: {
enabled: true,
method: 'local',
sortingSource: 'http://url/to/sortingSource'
}
}
}
Prop | Type | Description |
---|---|---|
resizable | bool | will set all columns to resizable. This parameter will not override columns that have declared they are not resizable from the columns array |
defaultColumnWidth | int | if no column width is provided, columns will be divided equally. this can be overwritten by providing a new string template |
minColumnWidth | int | the minimum width a column can be dragged to |
moveable | bool | whether the columns can be reordered by drag |
headerActionItemBuilder | func | build a custom jsx component to be used as the header action items |
sortable | object | an object that describes whether columns can be sorted |
sortable.enabled | bool | an object that describes whether columns can be sorted |
sortable.method | oneOf(['local', 'remote']) | whether sorting will execute locally, or remotely |
sortable.sortingSource | string | where sorting data will be retrieved (a required parameter for remote sorting) |
export const plugins = {
PAGER: {
enabled: true,
pagingType: 'remote',
toolbarRenderer: (pageIndex, pageSize, total, currentRecords, recordType) => {
return `${pageIndex * pageSize} through ${pageIndex * pageSize + currentRecords} of ${total} ${recordType} Displayed`;
},
pagerComponent: false
}
};
Prop | Type | Description |
---|---|---|
enabled | bool | whether a pager will be used, defaults to true |
pagingType | oneOf(['local', 'remote']) | defaults to local |
toolbarRenderer | func | a function which which returns the description of the current pager state, ex: 'Viewing Records 10 of 100' |
pagerComponent | jsx | if you'd like to pass your own pager in, you can supply a jsx element which will replace the pager entirely |
export const plugins = {
GRID_ACTIONS: {
iconCls: 'action-icon',
onMenuShow: ({ columns, rowData }) => {
console.log('This event fires before menushow');
if (rowData.isDisabled) {
return ['menu-item-key'] // this field will now be disabled
}
},
menu: [
{
text: 'Menu Item',
key: 'menu-item-key',
EVENT_HANDLER: () => {
alert('Im a menu Item Action');
}
}
]
}
};
Prop | Type | Description |
---|---|---|
iconCls | string | class to be used for the action icon |
menu | arrayOf(object) | menuItems, with text , key , EVENT_HANDLER properties. each object must contain a unique key relative to it's parent array. These keys will be used as the JSX element key. |
onMenuShow | func | a method that fires upon menu action click. @return an array of keys to disable menu items that correspond with these keys. |
export const plugins = {
SELECTION_MODEL: {
mode: 'single',
enabled: true,
editEvent: 'singleclick',
allowDeselect: true,
activeCls: 'active-class',
selectionEvent: 'singleclick'
}
};
Prop | Type | Description |
---|---|---|
mode | oneOf(['single', 'multi', 'checkbox-single', 'checkbox-multi']) | determines whether a single value, or multiple values can be selected |
editEvent | oneOf(['singleclick', 'doubleclick', 'none']) | what type of mouse event will trigger the editor |
enabled | bool | whether the selection model class is initialized |
allowDeselect | bool | whether a value can be deselected |
activeCls | string | the class applied to active rows upon selection |
selectionEvent | oneOf(['singleclick', 'doubleclick']) | the browser event which triggers the selection event |
export const plugins = {
ERROR_HANDLER: {
defaultErrorMessage: 'AN ERROR OCURRED',
enabled: true
}
};
Prop | Type | Description |
---|---|---|
defaultErrorMessage | string | the default error message to display when no error information is available |
enabled | bool | whether the error handler should be initialized |
export const plugins = {
LOADER: {
enabled: true
}
};
Prop | Type | Description |
---|---|---|
enabled | bool | whether the loading mask should be initialized |
export const plugins = {
BULK_ACTIONS: {
enabled: true,
actions: [
{
text: 'Bulk Action Button',
EVENT_HANDLER: () => {
console.log('Doing a bulk action');
}
}
]
}
};
Prop | Type | Description |
---|---|---|
enabled | bool | whether the bulk action toolbar should be used |
actions | arrayOf(object) | the actions (including button text, and event handler) that will be displayed in the bar |
export const plugins = {
ROW: {
enabled: true,
renderer: ({rowProps, cells, row}) => {
return (
<tr { ...rowProps }>
{ cells }
</tr>
);
}
}
};
Prop | Type | Description |
---|---|---|
enabled | bool | whether the bulk action toolbar should be used |
renderer | func | function which returns the row contents for this row |
All grid events are passed in as a single object.
export const events = {
HANDLE_CELL_CLICK: () => {},
HANDLE_CELL_DOUBLE_CLICK: () => {},
HANDLE_BEFORE_ROW_CLICK: () => {},
HANDLE_ROW_CLICK: () => {},
HANDLE_ROW_DOUBLE_CLICK: () => {},
HANDLE_BEFORE_SELECTION: () => {},
HANDLE_AFTER_SELECTION: () => {},
HANDLE_BEFORE_INLINE_EDITOR_SAVE: () => {},
HANDLE_AFTER_INLINE_EDITOR_SAVE: () => {},
HANDLE_BEFORE_BULKACTION_SHOW: () => {},
HANDLE_AFTER_BULKACTION_SHOW: () => {},
HANDLE_BEFORE_SORT: () => {},
HANLE_BEFORE_EDIT: () => {},
HANDLE_AFTER_SELECT_ALL: () => {},
HANDLE_AFTER_DESELECT_ALL: () => {},
HANDLE_AFTER_ROW_DROP: () => {},
HANDLE_BEFORE_TREE_CHILD_CREATE: () => {},
HANDLE_EDITOR_FOCUS: () => {},
HANDLE_EDITOR_BLUR: () => {}
};
Each function is passed two arguments, the first is a context object which will contain metadata about the event, and the second argument is the browser event if applicable.
HANDLE_CELL_CLICK = ({ row, rowId, rowIndex }, e) => {}
All core components and plugins have corresponding .styl
files that can be extended or overwritten. Class names have also been modularized and are available to modify or extend within src/constants/gridConstants.js
To update CLASS_NAMES
or the CSS_PREFIX
dynamically, you can use the applyGridConfig
function. More information is available here.
export const CSS_PREFIX = 'react-grid';
export const CLASS_NAMES = {
ACTIVE_CLASS: 'active',
DRAG_HANDLE: 'drag-handle',
SORT_HANDLE: 'sort-handle',
SECONDARY_CLASS: 'secondary',
CONTAINER: 'container',
TABLE: 'table',
HEADER: 'header',
ROW: 'row',
CELL: 'cell',
PAGERTOOLBAR: 'pager-toolbar',
EMPTY_ROW: 'empty-row',
LOADING_BAR: 'loading-bar',
DRAGGABLE_COLUMN: 'draggable-column',
COLUMN: 'column',
SORT_HANDLE_VISIBLE: 'sort-handle-visible',
BUTTONS: {
PAGER: 'page-buttons'
},
SELECTION_MODEL: {
CHECKBOX: 'checkbox',
CHECKBOX_CONTAINER: 'checkbox-container'
},
ERROR_HANDLER: {
CONTAINER: 'error-container',
MESSAGE: 'error-message'
},
EDITOR: {
INLINE: {
CONTAINER: 'inline-editor',
SHOWN: 'shown',
HIDDEN: 'hidden',
SAVE_BUTTON: 'save-button',
CANCEL_BUTTON: 'cancel-button',
BUTTON_CONTAINER: 'button-container'
}
},
GRID_ACTIONS: {
CONTAINER: 'action-container',
SELECTED_CLASS: 'action-menu-selected',
MENU: {
CONTAINER: 'action-menu-container',
ITEM: 'action-menu-item'
}
},
BULK_ACTIONS: {
CONTAINER: 'bulkaction-container',
DESCRIPTION: 'bulkaction-description',
SHOWN: 'shown',
HIDDEN: 'hidden'
}
};