Repository: gregnb/mui-datatables
Pull request #110 (2018-07-29 02:29:23)

Feature/update custom render
This will introduce a breaking change by renaming the `customRender` function to `customBodyRender` 

It will also allow custom override of the current table footer (pagination) and also custom column headers. 

When merged will close:
#108 
#96 


Patch:
diff --git a/README.md b/README.md
index 517b4ae..ae5d744 100644
--- a/README.md
+++ b/README.md
@@ -137,6 +137,7 @@ The component accepts the following props:
 |**`selectableRows`**|boolean|true|Enable/disable row selection
 |**`customToolbar`**|function||Render a custom toolbar
 |**`customToolbarSelect`**|function||Render a custom selected rows toolbar
+|**`customFooter`**|function||Render a custom table footer. `function(count, page, rowsPerPage, changeRowsPerPage, changePage) => string`&#124;` React Component`
 |**`caseSensitive `**|boolean|false|Enable/disable case sensitivity for search
 |**`responsive`**|string|'stacked'|Enable/disable responsive table views. Options: 'stacked', 'scroll'
 |**`rowsPerPage`**|number|10|Number of rows allowed per page
@@ -190,12 +191,25 @@ const columns = [
 |**`display`**|boolean|true|Display column in table
 |**`filter`**|boolean|true|Display column in filter list
 |**`sort`**|boolean|true|Enable/disable sorting on column
-|**`customRender`**|function||Function that returns a string or React component. Used as display data within all table cells of a given column. `function(value, tableMeta, updateValue) => string`&#124;` React Component` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/component/index.js)
+|**`customHeadRender`**|function||Function that returns a string or React component. Used as display for column header. `function(value, tableMeta, updateValue) => string`&#124;`
+|**`customBodyRender`**|function||Function that returns a string or React component. Used as display data within all table cells of a given column. `function(value, tableMeta, updateValue) => string`&#124;` React Component` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/component/index.js)
 
-`customRender` is called with these arguments:
+`customHeadRender` is called with these arguments:
 
 ```
-customRender(value: any, tableMeta: {
+function(columnMeta: {
+  display: bool,
+  filter: bool,
+  sort: bool,
+  sortDirection: bool,
+}, updateDirection: function)
+```
+
+
+`customBodyRender` is called with these arguments:
+
+```
+function(value: any, tableMeta: {
   rowIndex: number,
   columnIndex: number,
   columnData: array, // Columns Options object
diff --git a/examples/component/index.js b/examples/component/index.js
index 1de4402..dfe1829 100644
--- a/examples/component/index.js
+++ b/examples/component/index.js
@@ -26,7 +26,7 @@ class Example extends React.Component {
         name: "Location",
         options: {
           filter: true,
-          customRender: (value, tableMeta, updateValue) => {
+          customBodyRender: (value, tableMeta, updateValue) => {
             return (
               <Cities
                 value={value}
@@ -47,7 +47,7 @@ class Example extends React.Component {
         name: "Salary",
         options: {
           filter: true,
-          customRender: (value, tableMeta, updateValue) => {
+          customBodyRender: (value, tableMeta, updateValue) => {
 
             const nf = new Intl.NumberFormat('en-US', {
               style: 'currency',
@@ -64,7 +64,7 @@ class Example extends React.Component {
         name: "Active",
         options: {
           filter: true,
-          customRender: (value, tableMeta, updateValue) => {
+          customBodyRender: (value, tableMeta, updateValue) => {
 
             return (
               <FormControlLabel
diff --git a/examples/customize-footer/CustomFooter.js b/examples/customize-footer/CustomFooter.js
new file mode 100644
index 0000000..b404927
--- /dev/null
+++ b/examples/customize-footer/CustomFooter.js
@@ -0,0 +1,28 @@
+import React from "react";
+import TableFooter from "@material-ui/core/TableFooter";
+import TableRow from "@material-ui/core/TableRow";
+import TableCell from "@material-ui/core/TableCell";
+import { withStyles } from "@material-ui/core/styles";
+
+const defaultFooterStyles = {
+};
+
+class CustomFooter extends React.Component {
+
+  render() {
+    const { classes } = this.props;
+
+    return (
+    <TableFooter>
+      <TableRow>
+        <TableCell>
+          some content
+        </TableCell>
+      </TableRow>
+    </TableFooter>
+    );
+  }
+
+}
+
+export default withStyles(defaultFooterStyles, { name: "CustomFooter" })(CustomFooter);
diff --git a/examples/customize-footer/index.js b/examples/customize-footer/index.js
new file mode 100644
index 0000000..db7e7bf
--- /dev/null
+++ b/examples/customize-footer/index.js
@@ -0,0 +1,65 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import MUIDataTable from "../../src/";
+import CustomFooter from "./CustomFooter";
+
+class Example extends React.Component {
+
+  render() {
+
+    const columns = ["Name", "Title", "Location", "Age", "Salary"];
+
+    let data = [
+      ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
+      ["Aiden Lloyd", "Business Consultant", "Dallas",  55, 200000],
+      ["Jaden Collins", "Attorney", "Santa Ana", 27, 500000],
+      ["Franky Rees", "Business Analyst", "St. Petersburg", 22, 50000],
+      ["Aaren Rose", "Business Consultant", "Toledo", 28, 75000],
+      ["Blake Duncan", "Business Management Analyst", "San Diego", 65, 94000],
+      ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, 210000],
+      ["Lane Wilson", "Commercial Specialist", "Omaha", 19, 65000],
+      ["Robin Duncan", "Business Analyst", "Los Angeles", 20, 77000],
+      ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, 135000],
+      ["Harper White", "Attorney", "Pittsburgh", 52, 420000],
+      ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, 150000],
+      ["Frankie Long", "Industrial Analyst", "Austin", 31, 170000],
+      ["Brynn Robbins", "Business Analyst", "Norfolk", 22, 90000],
+      ["Justice Mann", "Business Consultant", "Chicago", 24, 133000],
+      ["Addison Navarro", "Business Management Analyst", "New York", 50, 295000],
+      ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, 200000],
+      ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, 400000],
+      ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, 110000],
+      ["Danny Leon", "Computer Scientist", "Newark", 60, 220000],
+      ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, 180000],
+      ["Jesse Hall", "Business Analyst", "Baltimore", 44, 99000],
+      ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, 90000],
+      ["Terry Macdonald", "Commercial Specialist", "Miami", 39, 140000],
+      ["Justice Mccarthy", "Attorney", "Tucson", 26, 330000],
+      ["Silver Carey", "Computer Scientist", "Memphis", 47, 250000],
+      ["Franky Miles", "Industrial Analyst", "Buffalo", 49, 190000],
+      ["Glen Nixon", "Corporate Counselor", "Arlington", 44, 80000],
+      ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, 45000],
+      ["Mason Ray", "Computer Scientist", "San Francisco", 39, 142000]
+    ];
+
+    const options = {
+      filter: true,
+      selectableRows: true,
+      filterType: 'dropdown',
+      responsive: 'stacked',
+      rowsPerPage: 10,
+      customFooter: (count, page, rowsPerPage, changeRowsPerPage, changePage) => {
+        return (  
+          <CustomFooter changePage={changePage} count={count} />
+        );
+      }
+    };
+
+    return (
+      <MUIDataTable title={"ACME Employee list"} data={data} columns={columns} options={options} />
+    );
+
+  }
+}
+
+ReactDOM.render(<Example />, document.getElementById("app-root"));
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index 2ece053..e4d4d16 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -55,7 +55,8 @@ class MUIDataTable extends React.Component {
             display: PropTypes.bool,
             filter: PropTypes.bool,
             sort: PropTypes.bool,
-            customRender: PropTypes.func,
+            customHeadRender: PropTypes.func,
+            customBodyRender: PropTypes.func
           }),
         }),
       ]),
@@ -68,6 +69,7 @@ class MUIDataTable extends React.Component {
       pagination: PropTypes.bool,
       customToolbar: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
       customToolbarSelect: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
+      customFooter: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
       selectableRows: PropTypes.bool,
       caseSensitive: PropTypes.bool,
       rowHover: PropTypes.bool,
@@ -217,9 +219,9 @@ class MUIDataTable extends React.Component {
           });
         }
 
-        if (typeof columnOptions.customRender === "function") {
+        if (typeof columnOptions.customBodyRender === "function") {
           const tableMeta = this.getCustomRenderMeta(rowIndex, colIndex, value, [], columnData, this.state);
-          const funcResult = columnOptions.customRender(value, tableMeta);
+          const funcResult = columnOptions.customBodyRender(value, tableMeta);
 
           if (React.isValidElement(funcResult) && funcResult.props.value) {
             value = funcResult.props.value;
@@ -280,14 +282,14 @@ class MUIDataTable extends React.Component {
       let columnDisplay = row[index];
       let columnValue = row[index];
 
-      if (columns[index].customRender) {
+      if (columns[index].customBodyRender) {
         const tableMeta = this.getCustomRenderMeta(rowIndex, index, row, columns[index], this.state.data, {
           ...this.state,
           filterList: filterList,
           searchText: searchText,
         });
 
-        const funcResult = columns[index].customRender(
+        const funcResult = columns[index].customBodyRender(
           columnValue,
           tableMeta,
           this.updateDataCol.bind(null, rowIndex, index),
@@ -326,7 +328,7 @@ class MUIDataTable extends React.Component {
       let filterData = cloneDeep(prevState.filterData);
 
       const tableMeta = this.getCustomRenderMeta(row, index, row, prevState.columns[index], prevState.data, prevState);
-      const funcResult = prevState.columns[index].customRender(value, tableMeta);
+      const funcResult = prevState.columns[index].customBodyRender(value, tableMeta);
 
       const filterValue =
         React.isValidElement(funcResult) && funcResult.props.value
@@ -733,18 +735,20 @@ class MUIDataTable extends React.Component {
           </Table>
         </div>
         <Table>
-          {this.options.pagination ? (
-            <MUIDataTablePagination
-              count={rowCount}
-              page={page}
-              rowsPerPage={rowsPerPage}
-              changeRowsPerPage={this.changeRowsPerPage}
-              changePage={this.changePage}
-              component={"div"}
-              options={this.options}
-            />
-          ) : (
-            false
+          {this.options.customFooter ? (
+            this.options.customFooter(rowCount, page, rowsPerPage, this.changeRowsPerPage, this.changePage)
+          ) : (            
+            this.options.pagination && (
+              <MUIDataTablePagination
+                count={rowCount}
+                page={page}
+                rowsPerPage={rowsPerPage}
+                changeRowsPerPage={this.changeRowsPerPage}
+                changePage={this.changePage}
+                component={"div"}
+                options={this.options}
+              />
+            )
           )}
         </Table>
         <div className={classes.liveAnnounce} aria-live={"polite"} ref={el => (this.announceRef = el)}>
diff --git a/src/MUIDataTableHead.js b/src/MUIDataTableHead.js
index 5816b3d..5334cb8 100644
--- a/src/MUIDataTableHead.js
+++ b/src/MUIDataTableHead.js
@@ -46,30 +46,30 @@ class MUIDataTableHead extends React.Component {
       <TableHead
         className={classNames({ [classes.responsiveStacked]: options.responsive === "stacked", [classes.main]: true })}>
         <MUIDataTableHeadRow>
-          {options.selectableRows ? (
+          {options.selectableRows && (
             <MUIDataTableSelectCell
               onChange={this.handleRowSelect.bind(null)}
               indeterminate={isDeterminate}
               checked={isChecked}
             />
-          ) : (
-            false
           )}
           {columns.map(
             (column, index) =>
-              column.display ? (
-                <MUIDataTableHeadCell
-                  key={index}
-                  index={index}
-                  type={"cell"}
-                  sort={column.sort}
-                  sortDirection={column.sortDirection}
-                  toggleSort={this.handleToggleColumn}
-                  options={options}>
-                  {column.name}
-                </MUIDataTableHeadCell>
-              ) : (
-                false
+              column.display && (
+                column.customHeadRender ? (
+                  column.customHeadRender({ index, ...column }, this.handleToggleColumn)
+                ) : (
+                  <MUIDataTableHeadCell
+                    key={index}
+                    index={index}
+                    type={"cell"}
+                    sort={column.sort}
+                    sortDirection={column.sortDirection}
+                    toggleSort={this.handleToggleColumn}
+                    options={options}>
+                    {column.name}
+                  </MUIDataTableHeadCell>
+                )
               ),
           )}
         </MUIDataTableHeadRow>
diff --git a/webpack.config.js b/webpack.config.js
index f3cd389..b176c22 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,7 +3,7 @@ const webpack = require('webpack');
 
 module.exports = {
   entry: {
-    app: "./examples/selectable-rows/index.js"
+    app: "./examples/component/index.js"
   },
   stats: "verbose",
   context: __dirname,


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 6be507a..08380b3 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -21,9 +21,9 @@ describe("<MUIDataTable />", function() {
 
   before(() => {
     columns = [
-      { name: "Name", options: { customRender: renderName } },
+      { name: "Name", options: { customBodyRender: renderName } },
       "Company",
-      { name: "City", options: { customRender: renderCities } },
+      { name: "City", options: { customBodyRender: renderCities } },
       { name: "State" },
     ];
     data = [
@@ -77,7 +77,7 @@ describe("<MUIDataTable />", function() {
     const actualResult = shallowWrapper.dive().state().columns;
 
     const expectedResult = [
-      { display: true, name: "Name", sort: true, filter: true, sortDirection: null, customRender: renderName },
+      { display: true, name: "Name", sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
       { display: true, name: "Company", sort: true, filter: true, sortDirection: null },
       {
         display: true,
@@ -85,7 +85,7 @@ describe("<MUIDataTable />", function() {
         sort: true,
         filter: true,
         sortDirection: null,
-        customRender: renderCities,
+        customBodyRender: renderCities,
       },
       { display: true, name: "State", sort: true, filter: true, sortDirection: null },
     ];
@@ -327,7 +327,7 @@ describe("<MUIDataTable />", function() {
     const state = shallowWrapper.state();
 
     const expectedResult = [
-      { name: "Name", display: false, sort: true, filter: true, sortDirection: null, customRender: renderName },
+      { name: "Name", display: false, sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
       { name: "Company", display: true, sort: true, filter: true, sortDirection: null },
       {
         name: "City",
@@ -335,7 +335,7 @@ describe("<MUIDataTable />", function() {
         sort: true,
         filter: true,
         sortDirection: null,
-        customRender: renderCities,
+        customBodyRender: renderCities,
       },
       { name: "State", display: true, sort: true, filter: true, sortDirection: null },
     ];
@@ -397,7 +397,7 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.selectedRows.data, [0]);
   });
 
-  it("should update value when calling updateValue method in customRender", () => {
+  it("should update value when calling updateValue method in customBodyRender", () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();