Repository: gregnb/mui-datatables
Pull request #179 (2018-08-31 12:37:37)

Add options to CSV download
This PR adds the ability to customize the download option with:
* filename changing
* separator changing
* hide columns in download file

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

Coverage decreased (-0.02%) to 78.141% when pulling **172331b0c443119575b544837106ecc692d5f04d on tyrsensei:master** into **7b7f1606835230a111ea1f74fb6ac48037b08baf on gregnb:master**.

gregnb: Hey @tyrsensei thanks for the PR. Allow me some time to think about adding this feature in. I'm not sure I'm 100% for everything being offered so far but I'll review some more in the coming week
DanielGarciaGuillen: Would love to see this feature!
tyrsensei: Hi @gregnb.

No problem for me. I implemented these features because I was asked to for un professional project. I thought it would be a better idea to make these available to the community.

I tried to stick at the way things were developped, but I might have failed, so feel free to tell me if some things need corrections.
gregnb: @tyrsensei It looks good to me. I just needed some time to look at it. Can you fix the merge conflicts? I'll approve it after
tyrsensei: @gregnb Conflicts resolved, feel free to come back to me if needed.

Patch:
diff --git a/README.md b/README.md
index 841dfbb..5bc0b6d 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 [![dependencies Status](https://david-dm.org/gregnb/mui-datatables/status.svg)](https://david-dm.org/gregnb/mui-datatables)
 [![npm version](https://badge.fury.io/js/mui-datatables.svg)](https://badge.fury.io/js/mui-datatables)
 
-MUI-Datatables is a data tables component built on [Material-UI V1](https://www.material-ui-next.com).  It comes with features like filtering, resizable + view/hide columns, search, export to CSV download, printing, selectable rows, pagination, and sorting. On top of the ability to customize styling on most views, there are two responsive modes "stacked" and "scroll" for mobile/tablet devices. 
+MUI-Datatables is a data tables component built on [Material-UI V1](https://www.material-ui-next.com).  It comes with features like filtering, resizable + view/hide columns, search, export to CSV download, printing, selectable rows, pagination, and sorting. On top of the ability to customize styling on most views, there are two responsive modes "stacked" and "scroll" for mobile/tablet devices.
 
 <div align="center">
 	<img src="https://user-images.githubusercontent.com/19170080/38026128-eac9d506-3258-11e8-92a7-b0d06e5faa82.gif" />
@@ -44,11 +44,11 @@ const options = {
   filterType: 'checkbox',
 };
 
-<MUIDataTable 
-  title={"Employee List"} 
-  data={data} 
-  columns={columns} 
-  options={options} 
+<MUIDataTable
+  title={"Employee List"}
+  data={data}
+  columns={columns}
+  options={options}
 />
 
 ```
@@ -101,11 +101,11 @@ const options = {
   filterType: 'checkbox',
 };
 
-<MUIDataTable 
-  title={"Employee List"} 
-  data={data} 
-  columns={columns} 
-  options={options} 
+<MUIDataTable
+  title={"Employee List"}
+  data={data}
+  columns={columns}
+  options={options}
 />
 
 ```
@@ -151,6 +151,7 @@ The component accepts the following props:
 |**`search`**|boolean|true|Show/hide search icon from toolbar
 |**`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: ','}`
 |**`viewColumns`**|boolean|true|Show/hide viewColumns icon from toolbar
 |**`onRowsSelect`**|function||Callback function that triggers when row(s) are selected. `function(currentRowsSelected: array, rowsSelected: array) => void`
 |**`onRowsDelete`**|function||Callback function that triggers when row(s) are deleted. `function(rowsDeleted: array) => void`
@@ -195,6 +196,7 @@ const columns = [
 |**`display`**|string|'true'|Display column in table. `enum('true', 'false', 'excluded')`
 |**`filter`**|boolean|true|Display column in filter list
 |**`sort`**|boolean|true|Enable/disable sorting on column
+|**`download`**|boolean|true|Display column in CSV download file
 |**`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)
 
@@ -256,7 +258,7 @@ class BodyCellExample extends React.Component {
   })
 
   render() {
-  
+
     return (
       <MuiThemeProvider theme={this.getMuiTheme()}>
         <MUIDataTable title={"ACME Employee list"} data={data} columns={columns} options={options} />
@@ -268,7 +270,7 @@ class BodyCellExample extends React.Component {
 
 ```
 
-## Remote Data 
+## Remote Data
 
 If you are looking to work with remote data sets or handle pagination, filtering, and sorting on a remote server you can do that with the following options:
 
@@ -325,7 +327,7 @@ const options = {
       deleteAria: "Delete Selected Rows",
     },
   }
-  ... 
+  ...
 }
 ```
 
diff --git a/examples/csv-export/index.js b/examples/csv-export/index.js
new file mode 100644
index 0000000..1be1059
--- /dev/null
+++ b/examples/csv-export/index.js
@@ -0,0 +1,101 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import MUIDataTable from "../../src/";
+
+class Example extends React.Component {
+
+  render() {
+
+    const columns = [
+      "Name",
+      "Title",
+      "Location",
+      "Age",
+      {
+          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],
+    ];
+
+    const options = {
+      filter: true,
+      selectableRows: true,
+      filterType: 'dropdown',
+      responsive: 'stacked',
+      rowsPerPage: 10,
+      downloadOptions: {
+          filename: 'excel-format.csv',
+          separator: ';',
+      },
+      onRowsSelect: (rowsSelected, allRows) => {
+        console.log(rowsSelected, allRows);
+      },
+      onRowsDelete: (rowsDeleted) => {
+        console.log(rowsDeleted, "were deleted!");
+      },
+      onChangePage: (numberRows) => {
+        console.log(numberRows);
+      },
+      onSearchChange: (searchText) => {
+        console.log(searchText);
+      },
+      onColumnSortChange: (column, direction) => {
+        console.log(column, direction);
+      },
+      onColumnViewChange: (column, action) => {
+        console.log(column, action);
+      },
+      onFilterChange: (column, filters) => {
+        console.log(column, filters);
+      },
+      onCellClick: (cellIndex, rowIndex) => {
+        console.log(cellIndex, rowIndex);
+      },
+      onRowClick: (rowData, rowState) => {
+        console.log(rowData, rowState);
+      }
+    };
+
+    return (
+      <MUIDataTable title={"ACME Employee list CSV"} data={data} columns={columns} options={options} />
+    );
+
+  }
+}
+
+ReactDOM.render(<Example />, document.getElementById("app-root"));
diff --git a/package-lock.json b/package-lock.json
index 7150c87..db4ca0f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.0.0-beta-24",
+  "version": "2.0.0-beta-27",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index 66e21ac..f8fe6c6 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -56,6 +56,7 @@ class MUIDataTable extends React.Component {
             display: PropTypes.string, // enum('true', 'false', 'excluded')
             filter: PropTypes.bool,
             sort: PropTypes.bool,
+            download: PropTypes.bool,
             customHeadRender: PropTypes.func,
             customBodyRender: PropTypes.func,
           }),
@@ -89,6 +90,10 @@ class MUIDataTable extends React.Component {
       print: PropTypes.bool,
       viewColumns: PropTypes.bool,
       download: PropTypes.bool,
+      downloadOptions: PropTypes.shape({
+        filename: PropTypes.string,
+        separator: PropTypes.string,
+      }),
     }),
     /** Pass and use className to style MUIDataTable as desired */
     className: PropTypes.string,
@@ -169,6 +174,10 @@ class MUIDataTable extends React.Component {
       print: true,
       viewColumns: true,
       download: true,
+      downloadOptions: {
+        filename: 'tableDownload.csv',
+        separator: ',',
+      },
     };
 
     this.options = merge(defaultOptions, props.options);
@@ -220,6 +229,7 @@ class MUIDataTable extends React.Component {
         display: "true",
         filter: true,
         sort: true,
+        download: true,
         sortDirection: null,
       };
 
diff --git a/src/MUIDataTableToolbar.js b/src/MUIDataTableToolbar.js
index be9cc9d..4d4fc7f 100644
--- a/src/MUIDataTableToolbar.js
+++ b/src/MUIDataTableToolbar.js
@@ -84,10 +84,28 @@ class MUIDataTableToolbar extends React.Component {
   };
 
   handleCSVDownload = () => {
-    const { data, columns } = this.props;
+    const { data, columns, options } = this.props;
 
-    const CSVHead = columns.reduce((soFar, column) => soFar + '"' + column.name + '",', "").slice(0, -1) + "\r\n";
-    const CSVBody = data.reduce((soFar, row) => soFar + '"' + row.data.join('","') + '"\r\n', []).trim();
+    const CSVHead =
+      columns
+        .reduce(
+          (soFar, column) =>
+            column.download ? soFar + '"' + column.name + '"' + options.downloadOptions.separator : soFar,
+          "",
+        )
+        .slice(0, -1) + "\r\n";
+    const CSVBody = data
+      .reduce(
+        (soFar, row) =>
+          soFar +
+          '"' +
+          row.data
+            .filter((field, index) => columns[index].download)
+            .join('"' + options.downloadOptions.separator + '"') +
+          '"\r\n',
+        [],
+      )
+      .trim();
 
     /* taken from react-csv */
     const csv = `${CSVHead}${CSVBody}`;
@@ -99,7 +117,7 @@ class MUIDataTableToolbar extends React.Component {
 
     let link = document.createElement("a");
     link.setAttribute("href", downloadURI);
-    link.setAttribute("download", "tableDownload.csv");
+    link.setAttribute("download", options.downloadOptions.filename);
     document.body.appendChild(link);
     link.click();
     document.body.removeChild(link);


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 929133a..95625fd 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -77,17 +77,18 @@ describe("<MUIDataTable />", function() {
     const actualResult = shallowWrapper.dive().state().columns;
 
     const expectedResult = [
-      { display: "true", name: "Name", sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
-      { display: "true", name: "Company", sort: true, filter: true, sortDirection: null },
+      { display: "true", name: "Name", sort: true, filter: true, download: true, sortDirection: null, customBodyRender: renderName },
+      { display: "true", name: "Company", sort: true, filter: true, download: true, sortDirection: null },
       {
         display: "true",
         name: "City",
         sort: true,
         filter: true,
+        download: true,
         sortDirection: null,
         customBodyRender: renderCities,
       },
-      { display: "true", name: "State", sort: true, filter: true, sortDirection: null },
+      { display: "true", name: "State", sort: true, filter: true, download: true, sortDirection: null },
     ];
 
     assert.deepEqual(actualResult, expectedResult);
@@ -327,17 +328,18 @@ describe("<MUIDataTable />", function() {
     const state = shallowWrapper.state();
 
     const expectedResult = [
-      { name: "Name", display: "false", sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
-      { name: "Company", display: "true", sort: true, filter: true, sortDirection: null },
+      { name: "Name", display: "false", sort: true, filter: true, download: true, sortDirection: null, customBodyRender: renderName },
+      { name: "Company", display: "true", sort: true, filter: true, download: true, sortDirection: null },
       {
         name: "City",
         display: "true",
         sort: true,
         filter: true,
+        download: true,
         sortDirection: null,
         customBodyRender: renderCities,
       },
-      { name: "State", display: "true", sort: true, filter: true, sortDirection: null },
+      { name: "State", display: "true", sort: true, filter: true, download: true, sortDirection: null },
     ];
 
     assert.deepEqual(state.columns, expectedResult);
diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js
index 668b309..aec137d 100644
--- a/test/MUIDataTableToolbar.test.js
+++ b/test/MUIDataTableToolbar.test.js
@@ -26,6 +26,10 @@ describe("<MUIDataTableToolbar />", function() {
       filter: true,
       viewColumns: true,
       textLabels,
+      downloadOptions: {
+          separator: ',',
+          filename: 'tableDownload.csv',
+      }
     };
     columns = ["First Name", "Company", "City", "State"];
     data = [