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",
},
};