Repository: gregnb/mui-datatables
Pull request #674 (2019-06-06 12:25:04)

Fix for Issue #542
Skip downloading the file if it is downloaded server side.

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

Coverage increased (+0.1%) to 77.333% when pulling **2544398f9283b163e28e0cd1a05d70b6c625da7e on Avd6977:DownloadServerSide** into **2b508722db834843007970c9c8c9c1d9d930cad5 on gregnb:master**.

Avd6977: @gabrielliwerant Any chance this PR could make the version bump with the custom filters?
gabrielliwerant: @Avd6977 I don't think so, but I can do a smaller release after this next one for some quick fixes.
gabrielliwerant: Does this PR actually answer the #542 issue? Looks like that user was referring to an issue where only the current page is downloaded with serverside, not extra downloads.
Avd6977: That'd be awesome, thank you!

It provides one possible answer to it, which is to just download their own file. The other answer is to get all of the data serverside using "onDownload", which is existing functionality.
Avd6977: @gabrielliwerant Just curious when you think that smaller release may be.
gabrielliwerant: This feature probably won't make next release, in case you were wondering. I need some time to think about it, and how it may interact with an earlier PR that is also vying to change download options.  There are also some more critical things that should take priority.
Avd6977: Ok, would you prefer it be in a different manner? Our use case is that the entire CSV is downloaded server-side but we'd like to still use the download button. 
gabrielliwerant: I'm reluctant to extend the API for specialized use cases unless there's a strong case for it and it can't be done in another manner. What if instead the `onDownload` callback were enabled to skip downloading if it returned `false`? This way, we don't need a new option, and it can be utilized in a wider variety of contexts.
Avd6977: I'm good with that. Committed the code change. Let me know if that looks like what you were talking about.

Patch:
diff --git a/README.md b/README.md
index b7e9c9c..45f52be 100644
--- a/README.md
+++ b/README.md
@@ -167,7 +167,7 @@ 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`
+|**`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`. Return `false` to cancel download of file.
 |**`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: object(lookup: {dataindex: boolean}, data: arrayOfObjects: {index, dataIndex})) => void OR false` (Returning `false` prevents row deletion.)
diff --git a/examples/csv-export/index.js b/examples/csv-export/index.js
index 1be1059..6970dca 100644
--- a/examples/csv-export/index.js
+++ b/examples/csv-export/index.js
@@ -1,55 +1,58 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import MUIDataTable from "../../src/";
+import Button from '@material-ui/core/Button';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import MUIDataTable from '../../src/';
 
 class Example extends React.Component {
+  state = {
+    downloadFile: true,
+  };
 
   render() {
-
     const columns = [
-      "Name",
-      "Title",
-      "Location",
-      "Age",
+      'Name',
+      'Title',
+      'Location',
+      'Age',
       {
-          name: "Salary",
-          options: {
-              download: false,
-          }
+        name: 'Salary',
+        options: {
+          download: false,
+        },
       },
     ];
 
     const 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],
+      ['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 = {
@@ -59,19 +62,20 @@ class Example extends React.Component {
       responsive: 'stacked',
       rowsPerPage: 10,
       downloadOptions: {
-          filename: 'excel-format.csv',
-          separator: ';',
+        filename: 'excel-format.csv',
+        separator: ';',
       },
+      onDownload: () => this.state.downloadFile,
       onRowsSelect: (rowsSelected, allRows) => {
         console.log(rowsSelected, allRows);
       },
-      onRowsDelete: (rowsDeleted) => {
-        console.log(rowsDeleted, "were deleted!");
+      onRowsDelete: rowsDeleted => {
+        console.log(rowsDeleted, 'were deleted!');
       },
-      onChangePage: (numberRows) => {
+      onChangePage: numberRows => {
         console.log(numberRows);
       },
-      onSearchChange: (searchText) => {
+      onSearchChange: searchText => {
         console.log(searchText);
       },
       onColumnSortChange: (column, direction) => {
@@ -88,14 +92,27 @@ class Example extends React.Component {
       },
       onRowClick: (rowData, rowState) => {
         console.log(rowData, rowState);
-      }
+      },
     };
 
     return (
-      <MUIDataTable title={"ACME Employee list CSV"} data={data} columns={columns} options={options} />
+      <React.Fragment>
+        <Button
+          style={{
+            right: '17rem',
+            position: 'absolute',
+            top: '1.5rem',
+            background: 'gray',
+            color: 'white',
+            zIndex: 10,
+          }}
+          onClick={() => this.setState(prevState => ({ downloadFile: !prevState.downloadFile }))}>
+          {this.state.downloadFile ? 'Disable' : 'Enable'} Download
+        </Button>
+        <MUIDataTable title={'ACME Employee list CSV'} data={data} columns={columns} options={options} />
+      </React.Fragment>
     );
-
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+ReactDOM.render(<Example />, document.getElementById('app-root'));
diff --git a/examples/customize-toolbarselect/CustomToolbarSelect.js b/examples/customize-toolbarselect/CustomToolbarSelect.js
index 3e7521c..a05f313 100644
--- a/examples/customize-toolbarselect/CustomToolbarSelect.js
+++ b/examples/customize-toolbarselect/CustomToolbarSelect.js
@@ -8,14 +8,9 @@ import { withStyles } from "@material-ui/core/styles";
 
 const defaultToolbarSelectStyles = {
   iconButton: {
-    marginRight: "24px",
-    top: "50%",
-    display: "inline-block",
-    position: "relative",
-    transform: "translateY(-50%)",
   },
-  icon: {
-    color: "#000",
+  iconContainer: {
+    marginRight: "24px",
   },
   inverseIcon: {
     transform: "rotate(90deg)",
@@ -47,7 +42,7 @@ class CustomToolbarSelect extends React.Component {
     const { classes } = this.props;
 
     return (
-      <div className={"custom-toolbar-select"}>
+      <div className={classes.iconContainer}>
         <Tooltip title={"Deselect ALL"}>
           <IconButton className={classes.iconButton} onClick={this.handleClickDeselectAll}>
             <IndeterminateCheckBoxIcon className={classes.icon} />
diff --git a/examples/data-as-objects/index.js b/examples/data-as-objects/index.js
index 255db50..e4c9031 100644
--- a/examples/data-as-objects/index.js
+++ b/examples/data-as-objects/index.js
@@ -43,19 +43,27 @@ class Example extends React.Component {
           filter: true,
           sort: false
         }
-      }      
+      },
+      {
+        name: "phone.home",
+        label: "Home Phone",
+      },
+      {
+        name: "phone.fake",
+        label: "Not a Phone #",
+      },
+      {
+        name: "phone2.home",
+        label: "Not An Attribute",
+      }
     ];
 
 
     const data = [
-      { name: "Gabby George", title: "Business Analyst", location: "Minneapolis", age: 30, salary: "$100,000" },
-      { name: "Aiden Lloyd", title: "Business Consultant", location: "Dallas",  age: 55, salary: "$200,000" },
-      { name: "Jaden Collins", title: "Attorney", location: "Santa Ana", age: 27, salary: "$500,000" },
-      { name: "Franky Rees", title: "Business Analyst", location: "St. Petersburg", age: 22, salary: "$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"]
+      { name: "Gabby George", title: "Business Analyst", location: "Minneapolis", age: 30, salary: "$100,000", phone: { home: '867-5309', cell: '123-4567' } },
+      { name: "Aiden Lloyd", title: "Business Consultant", location: "Dallas",  age: 55, salary: "$200,000", phone: { home: '867-5310', cell: '123-4568' } },
+      { name: "Jaden Collins", title: "Attorney", location: "Santa Ana", age: 27, salary: "$500,000", phone: { home: '867-5311', cell: '123-4569' } },
+      { name: "Franky Rees", title: "Business Analyst", location: "St. Petersburg", age: 22, salary: "$50,000", phone: { home: '867-5312', cell: '123-4569' } }
     ];
 
     const options = {
diff --git a/package-lock.json b/package-lock.json
index 68cb7ae..1867ee3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.4.0",
+  "version": "2.5.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -4522,13 +4522,15 @@
           "version": "1.0.0",
           "resolved": false,
           "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "brace-expansion": {
           "version": "1.1.11",
           "resolved": false,
           "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
           "dev": true,
+          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -4545,19 +4547,22 @@
           "version": "1.1.0",
           "resolved": false,
           "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
           "resolved": false,
           "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "console-control-strings": {
           "version": "1.1.0",
           "resolved": false,
           "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
@@ -4688,7 +4693,8 @@
           "version": "2.0.3",
           "resolved": false,
           "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
@@ -4702,6 +4708,7 @@
           "resolved": false,
           "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
@@ -4718,6 +4725,7 @@
           "resolved": false,
           "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
           "dev": true,
+          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -4726,13 +4734,15 @@
           "version": "0.0.8",
           "resolved": false,
           "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "minipass": {
           "version": "2.2.4",
           "resolved": false,
           "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
           "dev": true,
+          "optional": true,
           "requires": {
             "safe-buffer": "^5.1.1",
             "yallist": "^3.0.0"
@@ -4753,6 +4763,7 @@
           "resolved": false,
           "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
           "dev": true,
+          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
@@ -4841,7 +4852,8 @@
           "version": "1.0.1",
           "resolved": false,
           "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
@@ -4855,6 +4867,7 @@
           "resolved": false,
           "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
           "dev": true,
+          "optional": true,
           "requires": {
             "wrappy": "1"
           }
@@ -4992,6 +5005,7 @@
           "resolved": false,
           "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
diff --git a/package.json b/package.json
index a86b010..23ed267 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.4.0",
+  "version": "2.5.1",
   "description": "Datatables for React using Material-UI",
   "main": "dist/index.js",
   "files": [
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index cbc424a..3f7f1bd 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -1,22 +1,22 @@
-import React from 'react';
-import PropTypes from 'prop-types';
 import Paper from '@material-ui/core/Paper';
+import { withStyles } from '@material-ui/core/styles';
 import MuiTable from '@material-ui/core/Table';
-import TableToolbar from './components/TableToolbar';
-import TableToolbarSelect from './components/TableToolbarSelect';
-import TableFilterList from './components/TableFilterList';
-import TableBody from './components/TableBody';
-import TableResize from './components/TableResize';
-import TableHead from './components/TableHead';
-import TableFooter from './components/TableFooter';
 import classnames from 'classnames';
 import cloneDeep from 'lodash.clonedeep';
-import merge from 'lodash.merge';
-import isEqual from 'lodash.isequal';
 import find from 'lodash.find';
+import isEqual from 'lodash.isequal';
 import isUndefined from 'lodash.isundefined';
+import merge from 'lodash.merge';
+import PropTypes from 'prop-types';
+import React from 'react';
+import TableBody from './components/TableBody';
+import TableFilterList from './components/TableFilterList';
+import TableFooter from './components/TableFooter';
+import TableHead from './components/TableHead';
+import TableResize from './components/TableResize';
+import TableToolbar from './components/TableToolbar';
+import TableToolbarSelect from './components/TableToolbarSelect';
 import textLabels from './textLabels';
-import { withStyles } from '@material-ui/core/styles';
 import { buildMap, getCollatorComparator, sortCompare } from './utils';
 
 const defaultTableStyles = {
@@ -209,6 +209,7 @@ class MUIDataTable extends React.Component {
       this.setTableData(this.props, TABLE_LOAD.INITIAL, () => {
         this.setTableAction('propsUpdate');
       });
+      this.updateOptions(this.props);
     }
 
     if (this.props.options.searchText !== prevProps.options.searchText) {
@@ -222,6 +223,10 @@ class MUIDataTable extends React.Component {
     }
   }
 
+  updateOptions(props) {
+    this.options = merge(this.options, props.options);
+  }
+
   initializeTable(props) {
     this.getDefaultOptions(props);
     this.setTableOptions(props);
@@ -269,6 +274,9 @@ class MUIDataTable extends React.Component {
       extra.selectableRows = props.options.selectableRows ? 'multiple' : 'none';
     }
     this.options = merge(defaultOptions, props.options, extra);
+    if (props.options.rowsPerPageOptions) {
+      this.options.rowsPerPageOptions = props.options.rowsPerPageOptions;
+    }
   }
 
   validateOptions(options) {
@@ -343,11 +351,6 @@ class MUIDataTable extends React.Component {
     let filterData = [];
     let filterList = [];
 
-    if (this.state.columns.length && isEqual(this.rawColumns(newColumns), this.rawColumns(this.props.columns))) {
-      const { columns, filterList, filterData } = this.state;
-      return { columns, filterList, filterData };
-    }
-
     newColumns.forEach((column, colIndex) => {
       let columnOptions = {
         display: 'true',
@@ -386,6 +389,8 @@ class MUIDataTable extends React.Component {
   };
 
   transformData = (columns, data) => {
+    const leaf = (obj, path) => path.split('.').reduce((value, el) => (value ? value[el] : undefined), obj);
+
     return Array.isArray(data[0])
       ? data.map(row => {
           let i = -1;
@@ -395,7 +400,7 @@ class MUIDataTable extends React.Component {
             return col.empty ? undefined : row[i];
           });
         })
-      : data.map(row => columns.map(col => row[col.name]));
+      : data.map(row => columns.map(col => leaf(row, col.name)));
   };
 
   setTableData(props, status, callback = () => {}) {
@@ -421,7 +426,7 @@ class MUIDataTable extends React.Component {
         }
 
         if (typeof column.customBodyRender === 'function') {
-          const tableMeta = this.getTableMeta(rowIndex, colIndex, value, [], column, this.state);
+          const tableMeta = this.getTableMeta(rowIndex, colIndex, value, column, [], this.state);
           const funcResult = column.customBodyRender(value, tableMeta);
 
           if (React.isValidElement(funcResult) && funcResult.props.value) {
@@ -854,6 +859,7 @@ class MUIDataTable extends React.Component {
         }
 
         return {
+          page: 0,
           filterList: filterList,
           displayData: this.options.serverSide
             ? prevState.displayData
@@ -1178,6 +1184,7 @@ class MUIDataTable extends React.Component {
           options={this.options}
           page={page}
           rowCount={rowCount}
+          rowsPerPageOptions={this.options.rowsPerPageOptions}
           rowsPerPage={rowsPerPage}
           changeRowsPerPage={this.changeRowsPerPage}
           changePage={this.changePage}
diff --git a/src/components/TableToolbarSelect.js b/src/components/TableToolbarSelect.js
index ed02d65..05bd1bb 100644
--- a/src/components/TableToolbarSelect.js
+++ b/src/components/TableToolbarSelect.js
@@ -12,26 +12,18 @@ const defaultToolbarSelectStyles = theme => ({
     backgroundColor: theme.palette.background.default,
     flex: '1 1 100%',
     display: 'flex',
-    height: '64px',
     position: 'relative',
     zIndex: 120,
     justifyContent: 'space-between',
+    alignItems: 'center',
     paddingTop: theme.spacing.unit,
     paddingBottom: theme.spacing.unit,
   },
   title: {
     paddingLeft: '26px',
-    top: '50%',
-    position: 'relative',
-    transform: 'translateY(-50%)',
   },
   iconButton: {
     marginRight: '24px',
-    height: '48px',
-    top: '50%',
-    display: 'block',
-    position: 'relative',
-    transform: 'translateY(-50%)',
   },
   deleteIcon: {},
 });
diff --git a/src/utils.js b/src/utils.js
index dac9967..ed93628 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -65,6 +65,11 @@ function createCSVDownload(columns, data, options) {
   const csv = options.onDownload
     ? options.onDownload(buildHead, buildBody, columns, data)
     : `${CSVHead}${CSVBody}`.trim();
+
+  if (options.onDownload && csv === false) {
+    return;
+  }
+
   const blob = new Blob([csv], { type: 'text/csv' });
 
   /* taken from react-csv */


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index f73661e..f4a05f3 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -173,6 +173,32 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(JSON.stringify(state.displayData), displayData);
   });
 
+  it('should correctly build internal table data and displayData structure when using nested data', () => {
+    const columns = [
+      { name: 'Name', options: { customBodyRender: renderName, customFilterListRender: renderCustomFilterList } },
+      'Company',
+      { name: 'Location.City', label: 'City Label' },
+      { name: 'Location.State' },
+      { name: 'Empty', options: { empty: true, filterType: 'checkbox' } },
+    ];
+    const data = [
+      { Name: 'Joe James', Company: 'Test Corp', Location: { City: 'Yonkers', State: 'NY' } },
+      { Name: 'John Walsh', Company: 'Test Corp', Location: { City: 'Hartford', State: null } },
+      { Name: 'Bob Herm', Company: 'Test Corp', Location: { Town: 'Tampa', State: 'FL' } },
+      { Name: 'James Houston', Company: 'Test Corp', Location: { City: 'Dallas', State: 'TX' } },
+    ];
+    const displayData = JSON.stringify([
+      { data: ['James, Joe', 'Test Corp', 'Yonkers', 'NY', undefined], dataIndex: 0 },
+      { data: ['Walsh, John', 'Test Corp', 'Hartford', null, undefined], dataIndex: 1 },
+      { data: ['Herm, Bob', 'Test Corp', undefined, 'FL', undefined], dataIndex: 2 },
+      { data: ['Houston, James', 'Test Corp', 'Dallas', 'TX', undefined], dataIndex: 3 },
+    ]);
+    const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
+    const state = shallowWrapper.dive().state();
+
+    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);
diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js
index 3d6bf24..dd9fcce 100644
--- a/test/MUIDataTableToolbar.test.js
+++ b/test/MUIDataTableToolbar.test.js
@@ -1,16 +1,15 @@
-import React from 'react';
-import { spy, stub } from 'sinon';
-import { mount, shallow } from 'enzyme';
-import { assert, expect, should } from 'chai';
 import IconButton from '@material-ui/core/IconButton';
-import SearchIcon from '@material-ui/icons/Search';
 import DownloadIcon from '@material-ui/icons/CloudDownload';
+import FilterIcon from '@material-ui/icons/FilterList';
 import PrintIcon from '@material-ui/icons/Print';
+import SearchIcon from '@material-ui/icons/Search';
 import ViewColumnIcon from '@material-ui/icons/ViewColumn';
-import ClearIcon from '@material-ui/icons/Clear';
-import FilterIcon from '@material-ui/icons/FilterList';
-import TableToolbar from '../src/components/TableToolbar';
+import { assert } from 'chai';
+import { mount, shallow } from 'enzyme';
+import React from 'react';
+import { spy } from 'sinon';
 import TableSearch from '../src/components/TableSearch';
+import TableToolbar from '../src/components/TableToolbar';
 import textLabels from '../src/textLabels';
 
 describe('<TableToolbar />', function() {