Repository: gregnb/mui-datatables
Pull request #589 (2019-05-02 16:54:34)

customRowRender
I have a use-case in my project where I need a non-column based layout for smaller screens. This is to prevent horizontal scrolling, which is not a very good UX on mobile.
I still wanted to make use of the (great) filtering and sorting system of mui-datatables.

This PR adds a new `customRowRender` option to mui-datatables, that can be used like this:

```
customRowRender: ({ data }, rowIndex) => (
  <div>
    My Important data field: {data[0]}
  </div>
)
```

coveralls: 
[![Coverage Status](https://coveralls.io/builds/23153462/badge)](https://coveralls.io/builds/23153462)

Coverage decreased (-0.06%) to 74.61% when pulling **51922a21884c25ce837d7697060fca672e83555c on Skn0tt:master** into **d6ec8a3b1da48bbefda60f9112a590942bc06bf5 on gregnb:master**.

gabrielliwerant: @Skn0tt Have you had a look at the `responsize: 'stacked'` option that provides a non-column based layout already?
Skn0tt: I did, but it didn't match the asthethics I was looking for.

Patch:
diff --git a/README.md b/README.md
index 05aab14..fcd6f02 100644
--- a/README.md
+++ b/README.md
@@ -147,6 +147,7 @@ The component accepts the following props:
 |**`customToolbar`**|function||Render a custom toolbar
 |**`customToolbarSelect`**|function||Render a custom selected rows toolbar. `function(selectedRows, displayData, setSelectedRows) => void`
 |**`customFooter`**|function||Render a custom table footer. `function(count, page, rowsPerPage, changeRowsPerPage, changePage) => string`&#124;` React Component`
+|**`customRowRender `**|function||Override default row rendering with custom function. `customRowRender(data, dataIndex, rowIndex) => React Component`
 |**`customSort`**|function||Override default sorting with custom function. `function(data: array, colIndex: number, order: string) => array`
 |**`customSearch `**|function||Override default search with custom function. `customSearch(searchQuery: string, currentRow: array, columns: array) => boolean`
 |**`elevation`**|number|4|Shadow depth applied to Paper component
@@ -163,9 +164,10 @@ The component accepts the following props:
 |**`print`**|boolean|true|Show/hide print	 icon from toolbar
 |**`download`**|boolean|true|Show/hide download icon from toolbar
 |**`downloadOptions`**|object||Options to change the output of the CSV file. Default options: `{filename: 'tableDownload.csv', separator: ','}`
+|**`onDownload`**|function||A callback function that triggers when the user downloads the CSV file. In the callback, you can control what is written to the CSV file. `function(buildHead: (columns) => string, buildBody: (data) => string, columns, data) => string`
 |**`viewColumns`**|boolean|true|Show/hide viewColumns icon from toolbar
 |**`onRowsSelect`**|function||Callback function that triggers when row(s) are selected. `function(currentRowsSelected: array, allRowsSelected: array) => void`
-|**`onRowsDelete`**|function||Callback function that triggers when row(s) are deleted. `function(rowsDeleted: array) => void`
+|**`onRowsDelete`**|function||Callback function that triggers when row(s) are deleted. `function(rowsDeleted: object(lookup: {dataindex: boolean}, data: arrayOfObjects: {index, dataIndex})) => void OR false` (Returning `false` prevents row deletion.)
 |**`onRowClick`**|function||Callback function that triggers when a row is clicked. `function(rowData: string[], rowMeta: { dataIndex: number, rowIndex: number }) => void`
 |**`onCellClick`**|function||Callback function that triggers when a cell is clicked. `function(colData: any, cellMeta: { colIndex: number, rowIndex: number, dataIndex: number }) => void`
 |**`onChangePage`**|function||Callback function that triggers when a page has changed. `function(currentPage: number) => void`
@@ -363,10 +365,22 @@ const options = {
 }
 ```
 
+## Contributing
+Thanks for taking an interest in the library and the github community!
+
+The following commands should get you started:
+
+```sh
+npm i
+npm run dev
+```
+open  http://localhost:5050/ in browser
+
+After you make your changes locally, you can run the test suite with `npm test`. 
+
 ## License
 The files included in this repository are licensed under the MIT license.
 
-
 ## Thanks
 
 [<img src="https://www.browserstack.com/images/mail/browserstack-logo-footer.png" width="120">](https://www.browserstack.com/)
diff --git a/examples/column-filters/index.js b/examples/column-filters/index.js
index e6368b2..02c9d5b 100644
--- a/examples/column-filters/index.js
+++ b/examples/column-filters/index.js
@@ -15,11 +15,12 @@ class Example extends React.Component {
           customFilterListRender: v => `Name: ${v}`,
           filterOptions: ['a', 'b', 'c', 'Business Analyst']
         }
-      },      
+      },
       {
         name: "Title",
         options: {
           filter: true,
+          filterList: ['Business Analyst'],
           customFilterListRender: v => `Title: ${v}`,
           filterType: 'textField' // set filterType's at the column level
         }
@@ -44,7 +45,7 @@ class Example extends React.Component {
           customFilterListRender: v => `Salary: ${v}`,
           sort: false
         }
-      }      
+      }
     ];
     const data = [
       ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
@@ -81,6 +82,9 @@ class Example extends React.Component {
 
     const options = {
       filter: true,
+      onFilterChange: (changedColumn, filterList) => {
+        console.log(changedColumn, filterList);
+      },
       selectableRows: true,
       filterType: 'dropdown',
       responsive: 'stacked',
diff --git a/examples/component/index.js b/examples/component/index.js
index 0098f1f..82f155a 100644
--- a/examples/component/index.js
+++ b/examples/component/index.js
@@ -1,6 +1,7 @@
 import React from "react";
 import ReactDOM from "react-dom";
 import FormControlLabel from '@material-ui/core/FormControlLabel';
+import TextField from '@material-ui/core/TextField';
 import Switch from '@material-ui/core/Switch';
 import MUIDataTable from "../../src/";
 import Cities from "./cities";
@@ -14,8 +15,15 @@ class Example extends React.Component {
         name: "Name",
         options: {
           filter: false,
+          customBodyRender: (value, tableMeta, updateValue) => (
+            <FormControlLabel
+              value={value}
+              control={<TextField value={value} />}
+              onChange={event => updateValue(event.target.value)}
+            />
+          )
         }
-      },      
+      },
       {
         name: "Title",
         options: {
@@ -34,14 +42,19 @@ class Example extends React.Component {
                 change={event => updateValue(event)}
               />
             );
-          },         
+          },
         }
       },
       {
         name: "Age",
         options: {
           filter: false,
-          customBodyRender: value => value
+          customBodyRender: (value, tableMeta, updateValue) => (
+            <FormControlLabel
+              control={<TextField value={value || ''} type='number' />}
+              onChange={event => updateValue(event.target.value)}
+            />
+          )
         }
       },
       {
diff --git a/examples/customize-rows/index.js b/examples/customize-rows/index.js
new file mode 100644
index 0000000..33bf240
--- /dev/null
+++ b/examples/customize-rows/index.js
@@ -0,0 +1,96 @@
+/*
+  See another example of how to use `customRowRender` at
+  https://github.com/Skn0tt/mui-datatables-responsive-demo
+  https://mui-datatables-responsive-demo.skn0tt.now.sh
+*/
+
+import React from 'react';
+import ReactDOM from "react-dom";
+import MuiDataTable from "../../src/";
+
+function YourCustomRowComponent(props) {
+  const { name, cardNumber, cvc, expiry } = props;
+
+  return (
+    <div>
+      <h1>
+        {name}
+      </h1>
+      <p>
+        Number: {cardNumber} <br/>
+        CVC: {cvc} <br/>
+        expiry: {expiry}
+      </p>
+    </div>
+  );
+}
+
+const creditCards = [
+  {
+    name: "Tom Tallis",
+    cardNumber: "5500005555555559",
+    cvc: "582",
+    expiry: "02/24"
+  },
+  {
+    name: "Rich Harris",
+    cardNumber: "4444444444444448",
+    cvc: "172",
+    expiry: "03/22"
+  },
+  {
+    name: "Moby Dixon",
+    cardNumber: "3566003566003566",
+    cvc: "230",
+    expiry: "12/25"
+  }
+];
+
+function Example() {
+  return (
+    <MuiDataTable
+      title="Cards"
+      data={creditCards}
+      columns={[
+        {
+          name: "name",
+          label: "Name",
+        },
+        {
+          name: "cardNumber",
+          label: "Card Number"
+        },
+        {
+          name: "cvc",
+          label: "CVC"
+        },
+        {
+          name: "expiry",
+          label: "Expiry"
+        },
+      ]}
+      options={{
+        selectableRows: "none",
+        responsive: "scroll",
+        customRowRender: data => {
+          const [ name, cardNumber, cvc, expiry ] = data;
+          
+          return (
+            <tr key={cardNumber}>
+              <td colSpan={4} style={{ paddingTop: "10px"}}>
+                <YourCustomRowComponent
+                  name={name}
+                  cardNumber={cardNumber}
+                  cvc={cvc}
+                  expiry={expiry}
+                />
+              </td>
+            </tr>
+          );
+        },
+      }}
+    />
+  );
+}
+
+ReactDOM.render(<Example />, document.getElementById("app-root"));
diff --git a/examples/on-download/index.js b/examples/on-download/index.js
new file mode 100644
index 0000000..e93ca16
--- /dev/null
+++ b/examples/on-download/index.js
@@ -0,0 +1,121 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import MUIDataTable from '../../src';
+
+class Example extends React.Component {
+  render() {
+    const columns = [
+      {
+        name: 'Name',
+        options: {
+          filter: true,
+          display: 'excluded',
+        },
+      },
+      {
+        label: 'Modified Title Label',
+        name: 'Title',
+        options: {
+          filter: true,
+        },
+      },
+      {
+        name: 'Location',
+        options: {
+          print: false,
+          filter: false,
+        },
+      },
+      {
+        name: 'Age',
+        options: {
+          filter: true,
+          print: false,
+        },
+      },
+      {
+        name: 'Salary',
+        options: {
+          filter: true,
+          sort: false,
+        },
+      },
+    ];
+
+    const data = [
+      ['Gabby George', 'Business Analyst', 'Minneapolis', 30, '$100,000'],
+      ['Aiden Lloyd', 'Business Consultant', 'Dallas', 55, '$200,000'],
+      ['Jaden Collins', 'Attorney', 'Santa Ana', 27, '$500,000'],
+      ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, '$50,000'],
+      ['Aaren Rose', 'Business Consultant', 'Toledo', 28, '$75,000'],
+      ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, '$94,000'],
+      ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, '$210,000'],
+      ['Lane Wilson', 'Commercial Specialist', 'Omaha', 19, '$65,000'],
+      ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, '$77,000'],
+      ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, '$135,000'],
+      ['Harper White', 'Attorney', 'Pittsburgh', 52, '$420,000'],
+      ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, '$150,000'],
+      ['Frankie Long', 'Industrial Analyst', 'Austin', 31, '$170,000'],
+      ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, '$90,000'],
+      ['Justice Mann', 'Business Consultant', 'Chicago', 24, '$133,000'],
+      ['Addison Navarro', 'Business Management Analyst', 'New York', 50, '$295,000'],
+      ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, '$200,000'],
+      ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, '$400,000'],
+      ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, '$110,000'],
+      ['Danny Leon', 'Computer Scientist', 'Newark', 60, '$220,000'],
+      ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, '$180,000'],
+      ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, '$99,000'],
+      ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, '$90,000'],
+      ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, '$140,000'],
+      ['Justice Mccarthy', 'Attorney', 'Tucson', 26, '$330,000'],
+      ['Silver Carey', 'Computer Scientist', 'Memphis', 47, '$250,000'],
+      ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, '$190,000'],
+      ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, '$80,000'],
+      ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, '$45,000'],
+      ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, '$142,000'],
+    ];
+
+    // Data for building a custom head and body with the onDownload option
+    const headerNames = [
+      {
+        name: 'Given name',
+        download: true,
+      },
+      {
+        name: 'Role',
+        download: true,
+      },
+      {
+        name: 'City',
+        download: true,
+      },
+      {
+        name: 'Years',
+        download: true,
+      },
+      {
+        name: 'Dough',
+        download: true,
+      },
+    ];
+    const footerNames = ['Full Name', 'Job', 'Whereabouts', 'Age', 'Allowance'];
+
+    const options = {
+      filter: true,
+      filterType: 'dropdown',
+      responsive: 'scroll',
+      onDownload: (buildHead, buildBody, columns, data) =>
+        buildHead(headerNames) +
+        buildBody(
+          data.concat({
+            index: data.length,
+            data: footerNames,
+          }),
+        ),
+    };
+
+    return <MUIDataTable title={'ACME Employee list'} data={data} columns={columns} options={options} />;
+  }
+}
+
+ReactDOM.render(<Example />, document.getElementById('app-root'));
diff --git a/examples/selectable-rows/index.js b/examples/selectable-rows/index.js
index a08b586..9860ba9 100644
--- a/examples/selectable-rows/index.js
+++ b/examples/selectable-rows/index.js
@@ -57,6 +57,10 @@ class Example extends React.Component {
         console.log(rowsSelected, allRows);
       },
       onRowsDelete: (rowsDeleted) => {
+        if (rowsDeleted.data[0].dataIndex === 0) {
+          window.alert('Can\'t delete this!');
+          return false;
+        };
         console.log(rowsDeleted, "were deleted!");
       },
       onChangePage: (numberRows) => {
diff --git a/package.json b/package.json
index d8b8596..6b23a1a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.0.0",
+  "version": "2.1.0",
   "description": "Datatables for React using Material-UI",
   "main": "dist/index.js",
   "files": [
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index ba3dee8..131571f 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -111,6 +111,7 @@ class MUIDataTable extends React.Component {
       customToolbar: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
       customToolbarSelect: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
       customFooter: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
+      customRowRender: PropTypes.func,
       onRowClick: PropTypes.func,
       resizableColumns: PropTypes.bool,
       selectableRows: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['none', 'single', 'multiple'])]),
@@ -137,6 +138,7 @@ class MUIDataTable extends React.Component {
         filename: PropTypes.string,
         separator: PropTypes.string,
       }),
+      onDownload: PropTypes.func,
     }),
     /** Pass and use className to style MUIDataTable as desired */
     className: PropTypes.string,
@@ -190,7 +192,7 @@ class MUIDataTable extends React.Component {
 
   componentDidUpdate(prevProps) {
     if (this.props.data !== prevProps.data || this.props.columns !== prevProps.columns) {
-      this.setTableData(prevProps, TABLE_LOAD.INITIAL);
+      this.setTableData(this.props, TABLE_LOAD.INITIAL);
     }
 
     if (this.options.resizableColumns) {
@@ -791,21 +793,21 @@ class MUIDataTable extends React.Component {
     );
   };
 
-  filterUpdate = (index, column, type) => {
+  filterUpdate = (index, value, column, type) => {
     this.setState(
       prevState => {
         const filterList = cloneDeep(prevState.filterList);
-        const filterPos = filterList[index].indexOf(column);
+        const filterPos = filterList[index].indexOf(value);
 
         switch (type) {
           case 'checkbox':
-            filterPos >= 0 ? filterList[index].splice(filterPos, 1) : filterList[index].push(column);
+            filterPos >= 0 ? filterList[index].splice(filterPos, 1) : filterList[index].push(value);
             break;
           case 'multiselect':
-            filterList[index] = column === '' ? [] : column;
+            filterList[index] = value === '' ? [] : value;
             break;
           default:
-            filterList[index] = filterPos >= 0 || column === '' ? [] : [column];
+            filterList[index] = filterPos >= 0 || value === '' ? [] : [value];
         }
 
         return {
@@ -831,6 +833,7 @@ class MUIDataTable extends React.Component {
     const cleanRows = data.filter(({ index }) => !selectedMap[index]);
 
     if (this.options.onRowsDelete) {
+      if (this.options.onRowsDelete(selectedRows) === false) return;
       this.options.onRowsDelete(selectedRows);
     }
 
@@ -1043,6 +1046,7 @@ class MUIDataTable extends React.Component {
     const rowCount = this.options.count || displayData.length;
     const rowsPerPage = this.options.pagination ? this.state.rowsPerPage : displayData.length;
     const showToolbar = hasToolbarItem(this.options, title);
+    const columnNames = columns.map(column => ({ name: column.name }));
 
     return (
       <Paper
@@ -1083,6 +1087,7 @@ class MUIDataTable extends React.Component {
           })}
           filterList={filterList}
           filterUpdate={this.filterUpdate}
+          columnNames={columnNames}
         />
         <div
           style={{ position: 'relative' }}
diff --git a/src/components/TableBody.js b/src/components/TableBody.js
index 58924a3..6a0e9df 100644
--- a/src/components/TableBody.js
+++ b/src/components/TableBody.js
@@ -120,53 +120,61 @@ class TableBody extends React.Component {
     return (
       <MuiTableBody>
         {tableRows && tableRows.length > 0 ? (
-          tableRows.map(({ data: row, dataIndex }, rowIndex) => (
-            <React.Fragment key={rowIndex}>
-              <TableBodyRow
-                {...(options.setRowProps ? options.setRowProps(row, dataIndex) : {})}
-                options={options}
-                rowSelected={options.selectableRows !== 'none' ? this.isRowSelected(dataIndex) : false}
-                onClick={this.handleRowClick.bind(null, row, { rowIndex, dataIndex })}
-                id={'MUIDataTableBodyRow-' + dataIndex}>
-                <TableSelectCell
-                  onChange={this.handleRowSelect.bind(null, {
-                    index: this.getRowIndex(rowIndex),
-                    dataIndex: dataIndex,
-                  })}
-                  onExpand={toggleExpandRow.bind(null, {
-                    index: this.getRowIndex(rowIndex),
-                    dataIndex: dataIndex,
-                  })}
-                  fixedHeader={options.fixedHeader}
-                  checked={this.isRowSelected(dataIndex)}
-                  expandableOn={options.expandableRows}
-                  selectableOn={options.selectableRows}
-                  isRowExpanded={this.isRowExpanded(dataIndex)}
-                  isRowSelectable={this.isRowSelectable(dataIndex)}
-                  id={'MUIDataTableSelectCell-' + dataIndex}
-                />
-                {row.map(
-                  (column, columnIndex) =>
-                    columns[columnIndex].display === 'true' && (
-                      <TableBodyCell
-                        {...(columns[columnIndex].setCellProps
-                          ? columns[columnIndex].setCellProps(column, dataIndex, columnIndex)
-                          : {})}
-                        dataIndex={dataIndex}
-                        rowIndex={rowIndex}
-                        colIndex={columnIndex}
-                        columnHeader={columns[columnIndex].label}
-                        print={columns[columnIndex].print}
-                        options={options}
-                        key={columnIndex}>
-                        {column}
-                      </TableBodyCell>
-                    ),
-                )}
-              </TableBodyRow>
-              {this.isRowExpanded(dataIndex) && options.renderExpandableRow(row, { rowIndex, dataIndex })}
-            </React.Fragment>
-          ))
+          tableRows.map((data, rowIndex) => {
+            const { data: row, dataIndex } = data;
+
+            if (options.customRowRender) {
+              return options.customRowRender(row, dataIndex, rowIndex);
+            }
+
+            return (
+              <React.Fragment key={rowIndex}>
+                <TableBodyRow
+                  {...(options.setRowProps ? options.setRowProps(row, dataIndex) : {})}
+                  options={options}
+                  rowSelected={options.selectableRows !== 'none' ? this.isRowSelected(dataIndex) : false}
+                  onClick={this.handleRowClick.bind(null, row, { rowIndex, dataIndex })}
+                  id={'MUIDataTableBodyRow-' + dataIndex}>
+                  <TableSelectCell
+                    onChange={this.handleRowSelect.bind(null, {
+                      index: this.getRowIndex(rowIndex),
+                      dataIndex: dataIndex,
+                    })}
+                    onExpand={toggleExpandRow.bind(null, {
+                      index: this.getRowIndex(rowIndex),
+                      dataIndex: dataIndex,
+                    })}
+                    fixedHeader={options.fixedHeader}
+                    checked={this.isRowSelected(dataIndex)}
+                    expandableOn={options.expandableRows}
+                    selectableOn={options.selectableRows}
+                    isRowExpanded={this.isRowExpanded(dataIndex)}
+                    isRowSelectable={this.isRowSelectable(dataIndex)}
+                    id={'MUIDataTableSelectCell-' + dataIndex}
+                  />
+                  {row.map(
+                    (column, columnIndex) =>
+                      columns[columnIndex].display === 'true' && (
+                        <TableBodyCell
+                          {...(columns[columnIndex].setCellProps
+                            ? columns[columnIndex].setCellProps(column, dataIndex, columnIndex)
+                            : {})}
+                          dataIndex={dataIndex}
+                          rowIndex={rowIndex}
+                          colIndex={columnIndex}
+                          columnHeader={columns[columnIndex].label}
+                          print={columns[columnIndex].print}
+                          options={options}
+                          key={columnIndex}>
+                          {column}
+                        </TableBodyCell>
+                      ),
+                  )}
+                </TableBodyRow>
+                {this.isRowExpanded(dataIndex) && options.renderExpandableRow(row, { rowIndex, dataIndex })}
+              </React.Fragment>
+            );
+          })
         ) : (
           <TableBodyRow options={options}>
             <TableBodyCell
diff --git a/src/components/TableFilter.js b/src/components/TableFilter.js
index ea975fa..758ffde 100644
--- a/src/components/TableFilter.js
+++ b/src/components/TableFilter.js
@@ -125,22 +125,22 @@ class TableFilter extends React.Component {
     classes: PropTypes.object,
   };
 
-  handleCheckboxChange = (index, column) => {
-    this.props.onFilterUpdate(index, column, 'checkbox');
+  handleCheckboxChange = (index, value, column) => {
+    this.props.onFilterUpdate(index, value, column, 'checkbox');
   };
 
-  handleDropdownChange = (event, index) => {
+  handleDropdownChange = (event, index, column) => {
     const labelFilterAll = this.props.options.textLabels.filter.all;
     const value = event.target.value === labelFilterAll ? '' : event.target.value;
-    this.props.onFilterUpdate(index, value, 'dropdown');
+    this.props.onFilterUpdate(index, value, column, 'dropdown');
   };
 
-  handleMultiselectChange = (index, column) => {
-    this.props.onFilterUpdate(index, column, 'multiselect');
+  handleMultiselectChange = (index, value, column) => {
+    this.props.onFilterUpdate(index, value, column, 'multiselect');
   };
 
-  handleTextFieldChange = (event, index) => {
-    this.props.onFilterUpdate(index, event.target.value, 'textField');
+  handleTextFieldChange = (event, index, column) => {
+    this.props.onFilterUpdate(index, event.target.value, column, 'textField');
   };
 
   renderCheckbox(column, index) {
@@ -155,7 +155,7 @@ class TableFilter extends React.Component {
             </Typography>
           </Grid>
           <Grid container>
-            {filterData[index].map((filterColumn, filterIndex) => (
+            {filterData[index].map((filterValue, filterIndex) => (
               <Grid item key={filterIndex}>
                 <FormControlLabel
                   key={filterIndex}
@@ -166,16 +166,16 @@ class TableFilter extends React.Component {
                   control={
                     <Checkbox
                       className={classes.checkboxIcon}
-                      onChange={this.handleCheckboxChange.bind(null, index, filterColumn)}
-                      checked={filterList[index].indexOf(filterColumn) >= 0 ? true : false}
+                      onChange={this.handleCheckboxChange.bind(null, index, filterValue, column.name)}
+                      checked={filterList[index].indexOf(filterValue) >= 0 ? true : false}
                       classes={{
                         root: classes.checkbox,
                         checked: classes.checked,
                       }}
-                      value={filterColumn != null ? filterColumn.toString() : ''}
+                      value={filterValue != null ? filterValue.toString() : ''}
                     />
                   }
-                  label={filterColumn}
+                  label={filterValue}
                 />
               </Grid>
             ))}
@@ -197,14 +197,14 @@ class TableFilter extends React.Component {
             <Select
               value={filterList[index].toString() || textLabels.all}
               name={column.name}
-              onChange={event => this.handleDropdownChange(event, index)}
+              onChange={event => this.handleDropdownChange(event, index, column.name)}
               input={<Input name={column.name} id={column.name} />}>
               <MenuItem value={textLabels.all} key={0}>
                 {textLabels.all}
               </MenuItem>
-              {filterData[index].map((filterColumn, filterIndex) => (
-                <MenuItem value={filterColumn} key={filterIndex + 1}>
-                  {filterColumn != null ? filterColumn.toString() : ''}
+              {filterData[index].map((filterValue, filterIndex) => (
+                <MenuItem value={filterValue} key={filterIndex + 1}>
+                  {filterValue != null ? filterValue.toString() : ''}
                 </MenuItem>
               ))}
             </Select>
@@ -224,7 +224,7 @@ class TableFilter extends React.Component {
             <TextField
               label={column.label}
               value={filterList[index].toString() || ''}
-              onChange={event => this.handleTextFieldChange(event, index)}
+              onChange={event => this.handleTextFieldChange(event, index, column.name)}
             />
           </FormControl>
         </div>
@@ -245,20 +245,20 @@ class TableFilter extends React.Component {
               value={filterList[index] || []}
               renderValue={selected => selected.join(', ')}
               name={column.name}
-              onChange={event => this.handleMultiselectChange(index, event.target.value)}
+              onChange={event => this.handleMultiselectChange(index, event.target.value, column.name)}
               input={<Input name={column.name} id={column.name} />}>
-              {filterData[index].map((filterColumn, filterIndex) => (
-                <MenuItem value={filterColumn} key={filterIndex + 1}>
+              {filterData[index].map((filterValue, filterIndex) => (
+                <MenuItem value={filterValue} key={filterIndex + 1}>
                   <Checkbox
-                    checked={filterList[index].indexOf(filterColumn) >= 0 ? true : false}
-                    value={filterColumn.toString()}
+                    checked={filterList[index].indexOf(filterValue) >= 0 ? true : false}
+                    value={filterValue.toString()}
                     className={classes.checkboxIcon}
                     classes={{
                       root: classes.checkbox,
                       checked: classes.checked,
                     }}
                   />
-                  <ListItemText primary={filterColumn} />
+                  <ListItemText primary={filterValue} />
                 </MenuItem>
               ))}
             </Select>
diff --git a/src/components/TableFilterList.js b/src/components/TableFilterList.js
index 9729f05..2f4ece6 100644
--- a/src/components/TableFilterList.js
+++ b/src/components/TableFilterList.js
@@ -21,6 +21,13 @@ class TableFilterList extends React.Component {
     filterList: PropTypes.array.isRequired,
     /** Filter List value renderers */
     filterListRenderers: PropTypes.array.isRequired,
+    /** Columns used to describe table */
+    columnNames: PropTypes.PropTypes.arrayOf(
+      PropTypes.oneOfType([
+        PropTypes.string,
+        PropTypes.shape({ name: PropTypes.string.isRequired }),
+      ]),
+    ).isRequired,
     /** Callback to trigger filter update */
     onFilterUpdate: PropTypes.func,
     /** Extend the style applied to components */
@@ -28,7 +35,7 @@ class TableFilterList extends React.Component {
   };
 
   render() {
-    const { classes, filterList, filterUpdate, filterListRenderers } = this.props;
+    const { classes, filterList, filterUpdate, filterListRenderers, columnNames } = this.props;
 
     return (
       <div className={classes.root}>
@@ -37,7 +44,7 @@ class TableFilterList extends React.Component {
             <Chip
               label={filterListRenderers[index](data)}
               key={colIndex}
-              onDelete={filterUpdate.bind(null, index, data, 'checkbox')}
+              onDelete={filterUpdate.bind(null, index, data, columnNames[index].name, 'checkbox')}
               className={classes.chip}
             />
           )),
diff --git a/src/components/TablePagination.js b/src/components/TablePagination.js
index ba42f83..caf6ef6 100644
--- a/src/components/TablePagination.js
+++ b/src/components/TablePagination.js
@@ -75,13 +75,13 @@ class TablePagination extends React.Component {
               id: 'pagination-next',
               'aria-label': textLabels.next,
             }}
-            SelectProps= {{
+            SelectProps={{
               id: 'pagination-input',
               SelectDisplayProps: { id: 'pagination-rows' },
               MenuProps: {
                 id: 'pagination-menu',
-                MenuListProps: { id: 'pagination-menu-list' }
-              }
+                MenuListProps: { id: 'pagination-menu-list' },
+              },
             }}
             rowsPerPageOptions={options.rowsPerPageOptions}
             onChangePage={this.handlePageChange}
diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js
index 2e93d85..59c0829 100644
--- a/src/components/TableToolbar.js
+++ b/src/components/TableToolbar.js
@@ -199,31 +199,31 @@ class TableToolbar extends React.Component {
             </Tooltip>
           )}
           {options.print && (
-            <Tooltip title={print}>
-              <span>
-                <ReactToPrint
-                  trigger={() => (
+            <span>
+              <ReactToPrint
+                trigger={() => (
+                  <Tooltip title={print}>
                     <IconButton aria-label={print} classes={{ root: classes.icon }}>
                       <PrintIcon />
                     </IconButton>
-                  )}
-                  content={() => this.props.tableRef()}
-                />
-              </span>
-            </Tooltip>
+                  </Tooltip>
+                )}
+                content={() => this.props.tableRef()}
+              />
+            </span>
           )}
           {options.viewColumns && (
             <Popover
               refExit={this.setActiveIcon.bind(null)}
               trigger={
-                <IconButton
-                  aria-label={viewColumns}
-                  classes={{ root: this.getActiveIcon(classes, 'viewcolumns') }}
-                  onClick={this.setActiveIcon.bind(null, 'viewcolumns')}>
-                  <Tooltip title={viewColumns}>
+                <Tooltip title={viewColumns}>
+                  <IconButton
+                    aria-label={viewColumns}
+                    classes={{ root: this.getActiveIcon(classes, 'viewcolumns') }}
+                    onClick={this.setActiveIcon.bind(null, 'viewcolumns')}>
                     <ViewColumnIcon />
-                  </Tooltip>
-                </IconButton>
+                  </IconButton>
+                </Tooltip>
               }
               content={
                 <TableViewCol data={data} columns={columns} options={options} onColumnUpdate={toggleViewColumn} />
@@ -235,14 +235,14 @@ class TableToolbar extends React.Component {
               refExit={this.setActiveIcon.bind(null)}
               classes={{ paper: classes.filterPaper }}
               trigger={
-                <IconButton
-                  aria-label={filterTable}
-                  classes={{ root: this.getActiveIcon(classes, 'filter') }}
-                  onClick={this.setActiveIcon.bind(null, 'filter')}>
-                  <Tooltip title={filterTable}>
+                <Tooltip title={filterTable}>
+                  <IconButton
+                    aria-label={filterTable}
+                    classes={{ root: this.getActiveIcon(classes, 'filter') }}
+                    onClick={this.setActiveIcon.bind(null, 'filter')}>
                     <FilterIcon />
-                  </Tooltip>
-                </IconButton>
+                  </IconButton>
+                </Tooltip>
               }
               content={
                 <TableFilter
diff --git a/src/utils.js b/src/utils.js
index c85b0f7..dac9967 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -30,32 +30,41 @@ function createCSVDownload(columns, data, options) {
   const replaceDoubleQuoteInString = columnData =>
     typeof columnData === 'string' ? columnData.replace(/\"/g, '""') : columnData;
 
-  const CSVHead =
-    columns
+  const buildHead = columns => {
+    return (
+      columns
+        .reduce(
+          (soFar, column) =>
+            column.download
+              ? soFar + '"' + replaceDoubleQuoteInString(column.name) + '"' + options.downloadOptions.separator
+              : soFar,
+          '',
+        )
+        .slice(0, -1) + '\r\n'
+    );
+  };
+  const CSVHead = buildHead(columns);
+
+  const buildBody = data => {
+    return data
       .reduce(
-        (soFar, column) =>
-          column.download
-            ? soFar + '"' + replaceDoubleQuoteInString(column.name) + '"' + options.downloadOptions.separator
-            : soFar,
-        '',
+        (soFar, row) =>
+          soFar +
+          '"' +
+          row.data
+            .filter((_, index) => columns[index].download)
+            .map(columnData => replaceDoubleQuoteInString(columnData))
+            .join('"' + options.downloadOptions.separator + '"') +
+          '"\r\n',
+        [],
       )
-      .slice(0, -1) + '\r\n';
-
-  const CSVBody = data
-    .reduce(
-      (soFar, row) =>
-        soFar +
-        '"' +
-        row.data
-          .filter((field, index) => columns[index].download)
-          .map(columnData => replaceDoubleQuoteInString(columnData))
-          .join('"' + options.downloadOptions.separator + '"') +
-        '"\r\n',
-      [],
-    )
-    .trim();
+      .trim();
+  };
+  const CSVBody = buildBody(data);
 
-  const csv = `${CSVHead}${CSVBody}`;
+  const csv = options.onDownload
+    ? options.onDownload(buildHead, buildBody, columns, data)
+    : `${CSVHead}${CSVBody}`.trim();
   const blob = new Blob([csv], { type: 'text/csv' });
 
   /* taken from react-csv */
diff --git a/webpack.config.js b/webpack.config.js
index 43ad278..6a2037e 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,7 +3,7 @@ const webpack = require('webpack');
 
 module.exports = {
   entry: {
-    app: "./examples/custom-action-columns/index.js"
+    app: "./examples/column-filters/index.js"
   },
   stats: "verbose",
   context: __dirname,


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 0bea639..32bf2c5 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -30,7 +30,10 @@ describe('<MUIDataTable />', function() {
       { name: 'Name', options: { customBodyRender: renderName, customFilterListRender: renderCustomFilterList } },
       'Company',
       { name: 'City', label: 'City Label', options: { customBodyRender: renderCities, filterType: 'textField' } },
-      { name: 'State', options: { customBodyRender: renderState, filterType: 'multiselect', customHeadRender: renderHead } },
+      {
+        name: 'State',
+        options: { customBodyRender: renderState, filterType: 'multiselect', customHeadRender: renderHead },
+      },
       { name: 'Empty', options: { empty: true, filterType: 'checkbox' } },
     ];
     data = [
@@ -170,6 +173,21 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(JSON.stringify(state.displayData), displayData);
   });
 
+  it('should correctly re-build display after xhr with serverSide=true', done => {
+    const fullWrapper = mount(<MUIDataTable columns={columns} data={[]} options={{ serverSide: true }} />);
+    assert.strictEqual(fullWrapper.find('tbody tr').length, 1);
+
+    // simulate xhr and test number of displayed rows
+    new Promise((resolve, reject) => {
+      setTimeout(() => {
+        fullWrapper.setProps({ data });
+        fullWrapper.update();
+        assert.strictEqual(fullWrapper.find('tbody tr').length, 4);
+        done();
+      }, 10);
+    });
+  });
+
   it('should correctly re-build internal table data and displayData structure with prop change', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     let state = shallowWrapper.dive().state();
@@ -199,18 +217,27 @@ describe('<MUIDataTable />', function() {
       rowsPerPage: 1,
       rowsPerPageOptions: [1, 2, 4],
       page: 1,
-      onChangePage: current => currentPage = current,
+      onChangePage: current => (currentPage = current),
     };
     const fullWrapper = mount(<MUIDataTable columns={columns} data={data} options={options} />);
 
     // simulate paging backward to set `currentPage`
-    fullWrapper.find('#pagination-back').at(0).simulate('click');
+    fullWrapper
+      .find('#pagination-back')
+      .at(0)
+      .simulate('click');
     assert.strictEqual(currentPage, 0);
 
     // simulate changing pagination to set `rowsPerPage`
     fullWrapper.find('#pagination-rows').simulate('click');
-    fullWrapper.find('#pagination-menu-list li').at(1).simulate('click');
-    let inputValue = fullWrapper.find('#pagination-input').at(0).text();
+    fullWrapper
+      .find('#pagination-menu-list li')
+      .at(1)
+      .simulate('click');
+    let inputValue = fullWrapper
+      .find('#pagination-input')
+      .at(0)
+      .text();
     assert.strictEqual(inputValue, '2');
 
     // add data to simulate state change
@@ -219,11 +246,17 @@ describe('<MUIDataTable />', function() {
     fullWrapper.setProps({ data: newData });
 
     // simulate paging forward to test whether or not the `currentPage` was reset
-    fullWrapper.find('#pagination-next').at(0).simulate('click');
+    fullWrapper
+      .find('#pagination-next')
+      .at(0)
+      .simulate('click');
     assert.strictEqual(currentPage, 1);
 
     // grab pagination value to test whether or not `rowsPerPage` was reset
-    inputValue = fullWrapper.find('#pagination-input').at(0).text();
+    inputValue = fullWrapper
+      .find('#pagination-input')
+      .at(0)
+      .text();
     assert.strictEqual(inputValue, '2');
 
     // test that data updated properly
@@ -336,7 +369,7 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'Joe James', 'checkbox');
+    instance.filterUpdate(0, 'Joe James', 'Name', 'checkbox');
     table.update();
 
     const state = table.state();
@@ -347,7 +380,7 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'Joe James', 'checkbox');
+    instance.filterUpdate(0, 'Joe James', 'Name', 'checkbox');
     table.update();
 
     let state = table.state();
@@ -364,7 +397,7 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'Joe James', 'dropdown');
+    instance.filterUpdate(0, 'Joe James', 'Name', 'dropdown');
     table.update();
 
     const state = table.state();
@@ -380,9 +413,10 @@ describe('<MUIDataTable />', function() {
       defaultRenderCustomFilterList,
       defaultRenderCustomFilterList,
     ];
+    const columnNames = columns.map(column => ({ name: column.name }));
 
     const mountWrapper = mount(
-      <TableFilterList filterList={filterList} filterListRenderers={filterListRenderers} filterUpdate={() => true} />,
+      <TableFilterList filterList={filterList} filterListRenderers={filterListRenderers} filterUpdate={() => true} columnNames={columnNames} />,
     );
     const actualResult = mountWrapper.find(Chip);
     assert.strictEqual(actualResult.length, 1);
@@ -395,9 +429,10 @@ describe('<MUIDataTable />', function() {
         ? c.options.customFilterListRender
         : defaultRenderCustomFilterList;
     });
+    const columnNames = columns.map(column => ({ name: column.name }));
 
     const mountWrapper = mount(
-      <TableFilterList filterList={filterList} filterListRenderers={filterListRenderers} filterUpdate={() => true} />,
+      <TableFilterList filterList={filterList} filterListRenderers={filterListRenderers} filterUpdate={() => true} columnNames={columnNames} />,
     );
     const actualResult = mountWrapper.find(Chip);
     assert.strictEqual(actualResult.length, 1);
@@ -408,13 +443,13 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'Joe James', 'dropdown');
+    instance.filterUpdate(0, 'Joe James', 'Name', 'dropdown');
     table.update();
 
     let state = table.state();
     assert.deepEqual(state.filterList, [['Joe James'], [], [], [], []]);
 
-    instance.filterUpdate(0, 'Joe James', 'dropdown');
+    instance.filterUpdate(0, 'Joe James', 'Name', 'dropdown');
     table.update();
 
     state = table.state();
@@ -426,7 +461,7 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'Joe James', 'checkbox');
+    instance.filterUpdate(0, 'Joe James', 'Name', 'checkbox');
     table.update();
 
     // now remove it
@@ -439,6 +474,42 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(state.filterList, [[], [], [], [], []]);
   });
 
+  it('should have the proper column name in onFilterChange when applying filters', () => {
+    let changedColumn;
+    const options = {
+      onFilterChange: column => changedColumn = column
+    };
+
+    const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />);
+    const table = shallowWrapper.dive();
+    const instance = table.instance();
+
+    // test checkbox filter
+    instance.filterUpdate(0, 'Joe James', 'Name', 'checkbox');
+    table.update();
+    assert.strictEqual(changedColumn, 'Name');
+
+    // test dropdown filter
+    instance.filterUpdate(0, 'Test Corp', 'Company', 'dropdown');
+    table.update();
+    assert.strictEqual(changedColumn, 'Company');
+
+    // test textField filter
+    instance.filterUpdate(0, 'Joe James', 'Name', 'textField');
+    table.update();
+    assert.strictEqual(changedColumn, 'Name');
+
+    // test multiselect filter
+    instance.filterUpdate(0, 'Test Corp', 'Company', 'multiselect');
+    table.update();
+    assert.strictEqual(changedColumn, 'Company');
+
+    // test reset filters
+    instance.resetFilters();
+    table.update();
+    assert.strictEqual(changedColumn, null);
+  });
+
   it('should properly set searchText when calling searchTextUpdate method', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
@@ -679,6 +750,45 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(state.selectedRows.data, expectedResult);
   });
 
+  it('should remove selected data on selectRowDelete when type=cell', () => {
+    const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
+    const instance = shallowWrapper.instance();
+    const onRowsDelete = () => {};
+    const selectedRows = { data: [1] };
+
+    instance.selectRowUpdate('cell', { index: 0, dataIndex: 0 });
+    shallowWrapper.update();
+    instance.selectRowDelete();
+    shallowWrapper.update();
+
+    const newDisplayData = shallowWrapper.state().displayData;
+    const expectedResult = JSON.stringify([
+      { data: ['Walsh, John', 'Test Corp', renderCities('Hartford', { rowIndex: 0 }), null, undefined], dataIndex: 1 },
+      { data: ['Herm, Bob', 'Test Corp', renderCities('Tampa', { rowIndex: 1 }), 'FL', undefined], dataIndex: 2 },
+      { data: ['Houston, James', 'Test Corp', renderCities('Dallas', { rowIndex: 2 }), 'TX', undefined], dataIndex: 3 },
+    ]);
+
+    assert.deepEqual(JSON.stringify(newDisplayData), expectedResult);
+  });
+
+  it('should not remove selected data on selectRowDelete when type=cell when onRowsDelete returns false', () => {
+    const options = {
+      onRowsDelete: () => false,
+    };
+    const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />).dive();
+    const instance = shallowWrapper.instance();
+    const selectedRows = { data: [1] };
+
+    instance.selectRowUpdate('cell', { index: 0, dataIndex: 0 });
+    shallowWrapper.update();
+    instance.selectRowDelete();
+    shallowWrapper.update();
+
+    const myDisplayData = shallowWrapper.state().displayData;
+
+    assert.deepEqual(JSON.stringify(myDisplayData), displayData);
+  });
+
   it('should update value when calling updateValue method in customBodyRender', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
@@ -742,7 +852,7 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'James, Joe', 'checkbox');
+    instance.filterUpdate(0, 'James, Joe', 'Name', 'checkbox');
     table.update();
     const state = table.state();
 
@@ -759,7 +869,7 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'James', 'textField');
+    instance.filterUpdate(0, 'James', 'Name', 'textField');
     table.update();
     const state = table.state();
 
@@ -776,7 +886,7 @@ describe('<MUIDataTable />', function() {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'Joe James', 'dropdown');
+    instance.filterUpdate(0, 'Joe James', 'Name', 'dropdown');
     table.update();
     const state = table.state();
 
@@ -814,7 +924,7 @@ describe('<MUIDataTable />', function() {
       const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />);
       const table = shallowWrapper.dive();
       const instance = table.instance();
-      instance.filterUpdate(1, 'b', 'dropdown');
+      instance.filterUpdate(1, 'b', 'Name', 'dropdown');
       table.update();
       const { displayData } = table.state();
 
@@ -862,7 +972,7 @@ describe('<MUIDataTable />', function() {
       const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />);
       const table = shallowWrapper.dive();
       const instance = table.instance();
-      instance.filterUpdate(1, 'b', 'dropdown');
+      instance.filterUpdate(1, 'b', 'Name', 'dropdown');
       table.update();
       const { displayData } = table.state();
 
diff --git a/test/MUIDataTableBody.test.js b/test/MUIDataTableBody.test.js
index f62cf9d..46458d5 100644
--- a/test/MUIDataTableBody.test.js
+++ b/test/MUIDataTableBody.test.js
@@ -207,7 +207,7 @@ describe('<TableBody />', function() {
     const selectRowUpdate = stub();
     const toggleExpandRow = () => {};
 
-    const t = mount(
+    const mountWrapper = mount(
       <TableBody
         data={displayData}
         count={displayData.length}
@@ -224,7 +224,7 @@ describe('<TableBody />', function() {
       />,
     );
 
-    t.find('#MUIDataTableBodyRow-2')
+    mountWrapper.find('#MUIDataTableBodyRow-2')
       .first()
       .simulate('click');
 
@@ -237,7 +237,7 @@ describe('<TableBody />', function() {
     const selectRowUpdate = stub();
     const toggleExpandRow = () => {};
 
-    const t = mount(
+    const mountWrapper = mount(
       <TableBody
         data={displayData}
         count={displayData.length}
@@ -254,7 +254,7 @@ describe('<TableBody />', function() {
       />,
     );
 
-    const props = t
+    const props = mountWrapper
       .find('#MUIDataTableBodyRow-1')
       .first()
       .props();
@@ -263,4 +263,31 @@ describe('<TableBody />', function() {
     assert.isAtLeast(options.setRowProps.callCount, 1);
     assert(options.setRowProps.calledWith(data[1]));
   });
+
+  it("should use 'customRowRender' when provided", () => {
+    const options = { customRowRender: () => <div>Test_Text</div> };
+    const selectRowUpdate = stub();
+    const toggleExpandRow = () => {};
+
+    const mountWrapper = mount(
+      <TableBody
+        data={displayData}
+        count={displayData.length}
+        columns={columns}
+        page={0}
+        rowsPerPage={10}
+        selectedRows={[]}
+        selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
+        options={options}
+        searchText={''}
+        filterList={[]}
+      />,
+    );
+
+    const html = mountWrapper.html();
+
+    expect(html).to.contain("Test_Text");
+  });
 });
diff --git a/test/MUIDataTableHead.test.js b/test/MUIDataTableHead.test.js
index beff059..7f6573e 100644
--- a/test/MUIDataTableHead.test.js
+++ b/test/MUIDataTableHead.test.js
@@ -21,12 +21,8 @@ describe('<TableHead />', function() {
         label: 'State',
         display: 'true',
         options: { fixedHeader: true },
-        customHeadRender: columnMeta => (
-          <TableHeadCell {...columnMeta}>
-            {columnMeta.name + 's'}
-          </TableHeadCell>
-        ),
-        sort: null
+        customHeadRender: columnMeta => <TableHeadCell {...columnMeta}>{columnMeta.name + 's'}</TableHeadCell>,
+        sort: null,
       },
     ];
 
diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js
index b640108..7e0b7ec 100644
--- a/test/MUIDataTableToolbar.test.js
+++ b/test/MUIDataTableToolbar.test.js
@@ -205,4 +205,30 @@ describe('<TableToolbar />', function() {
     assert.strictEqual(appendSpy.callCount, 1);
     assert.strictEqual(removeSpy.callCount, 1);
   });
+
+  it('should trigger onDownload prop callback when calling method handleCSVDownload', () => {
+    const onDownload = spy();
+
+    const newOptions = { ...options, onDownload };
+
+    const shallowWrapper = shallow(
+      <TableToolbar
+        columns={columns}
+        displayData={data}
+        data={data}
+        options={newOptions}
+        setTableAction={setTableAction}
+      />,
+    );
+
+    const instance = shallowWrapper
+      .dive()
+      .dive()
+      .dive()
+      .instance();
+
+    instance.handleCSVDownload();
+
+    assert.strictEqual(onDownload.callCount, 1);
+  });
 });