Repository: gregnb/mui-datatables
Pull request #848 (2019-08-20 22:56:02)

Enhancement/pr 687 updated
Continuation of https://github.com/gregnb/mui-datatables/pull/687.

Strips out the changes to the core library, but preserves the test and example, with some improvements to the example.

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

Coverage increased (+0.1%) to 75.494% when pulling **79acdce454b6b05f719639a61d96096fa1ed00b2 on enhancement/pr-687-updated** into **8438fe3a06b3bb570910d5c445c33b4501de3d6e on master**.


Patch:
diff --git a/README.md b/README.md
index fc29dce..f3a7aa9 100644
--- a/README.md
+++ b/README.md
@@ -142,14 +142,14 @@ The component accepts the following props:
 |**`pagination`**|boolean|true|Enable/disable pagination
 |**`selectableRows`**|string|'multiple'|Numbers of rows that can be selected. Options are "multiple", "single", "none".
 |**`selectableRowsOnClick`**|boolean|false|Enable/disable select toggle when row is clicked. When False, only checkbox will trigger this action.
-|**`isRowSelectable`**|function||Enable/disable selection on certain rows with custom function. Returns true if not provided.  `function(dataIndex) => bool`
+|**`isRowSelectable`**|function||Enable/disable selection on certain rows with custom function. Returns true if not provided. `function(dataIndex: number, selectedRows: object(lookup: {dataindex: boolean}, data: arrayOfObjects: {index, dataIndex})) => bool`.
 |**`expandableRows`**|boolean|false|Enable/disable expandable rows
 |**`expandableRowsOnClick`**|boolean|false|Enable/disable expand trigger when row is clicked. When False, only expand icon will trigger this action.
 |**`renderExpandableRow`**|function||Render expandable row. `function(rowData, rowMeta) => React Component`
 |**`resizableColumns`**|boolean|false|Enable/disable resizable columns
 |**`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`
+|**`customFooter`**|function||Render a custom table footer. `function(count, page, rowsPerPage, changeRowsPerPage, changePage, `[`textLabels: object`](https://github.com/gregnb/mui-datatables/blob/master/src/textLabels.js)`) => string`&#124;` React Component` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-footer/index.js)
 |**`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`
@@ -158,7 +158,7 @@ The component accepts the following props:
 |**`caseSensitive `**|boolean|false|Enable/disable case sensitivity for search
 |**`responsive`**|string|'stacked'|Enable/disable responsive table views. Options: 'stacked', 'scroll'
 |**`rowsPerPage`**|number|10|Number of rows allowed per page
-|**`rowsPerPageOptions`**|array|[10,15,20]|Options to provide in pagination for number of rows a user can select
+|**`rowsPerPageOptions`**|array|[10,15,100]|Options to provide in pagination for number of rows a user can select
 |**`rowHover`**|boolean|true|Enable/disable hover style over rows
 |**`fixedHeader`**|boolean|true|Enable/disable fixed header columns
 |**`sortFilterList`**|boolean|true|Enable/disable alphanumeric sorting of filter lists
@@ -168,7 +168,7 @@ The component accepts the following props:
 |**`searchText`**|string||Initial search text
 |**`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: ','}`
+|**`downloadOptions`**|object|`{filename: 'tableDownload.csv', separator: ','}`|Options to change the output of the CSV file: `filename`: string, `separator`: string, `filterOptions`: object(`useDisplayedColumnsOnly`: boolean, `useDisplayedRowsOnly`: boolean)
 |**`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`
diff --git a/docs/utils/withRoot.js b/docs/utils/withRoot.js
index d82df6c..eace25c 100644
--- a/docs/utils/withRoot.js
+++ b/docs/utils/withRoot.js
@@ -5,7 +5,7 @@ import getPageContext from './getPageContext';
 
 function withRoot(Component) {
   class WithRoot extends React.Component {
-    componentWillMount() {
+    UNSAFE_componentWillMount() {
       this.pageContext = this.props.pageContext || getPageContext();
     }
 
diff --git a/examples/column-options-update/index.js b/examples/column-options-update/index.js
new file mode 100644
index 0000000..adfc9d0
--- /dev/null
+++ b/examples/column-options-update/index.js
@@ -0,0 +1,157 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import MUIDataTable from "../../src/";
+
+class Example extends React.Component {
+
+  state = {
+    filterList: [
+      ['Franky Miles'],
+      ['Business Analyst'],
+      [],
+      [],
+      []
+    ],
+    filterOptions: ['this', 'test', 'is', 'working'],
+    display: ['true', 'true', 'true', 'true', 'true'],
+    data: [
+      ["Gabby George", "Business Analyst", "Minneapolis", 30, 100000],
+      ["Business Analyst", "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]
+    ]
+  }
+
+  handleFilterNameChange = (event) => {
+    let string = prompt("Write a semicolon-separated string to change filter names in the first column!");
+    if (string) this.setState({ filterOptions: string.split(';') });
+  }
+
+  handleAddData = (event) => {
+    const string = prompt("Write a semicolon-separated string with values for 'Name', 'Title', 'Location', 'Age' and 'Salary' to add a new row of data!");
+    if (string) this.setState({ data: [string.split(';'), ...this.state.data] });
+  }
+
+  handleChangeDisplay = (event) => {
+    const string = prompt("Write a semicolon-separated string of display options for each of the 5 columns. Options are either 'true', 'false', or 'excluded'");
+    if (string) this.setState({ display: string.split(';') });
+  }
+
+  render() {
+    const { data, filterList, filterOptions } = this.state;
+
+    const columns = [
+      {
+        name: "Name",
+        options: {
+          filter: true,
+          display: this.state.display[0],
+          filterList: filterList[0].length ? filterList[0] : null,
+          customFilterListRender: v => `Name: ${v}`,
+          filterOptions: {
+            names: filterOptions
+          },
+        }
+      },
+      {
+        name: "Title",
+        options: {
+          display: this.state.display[1],
+          filter: true,
+          filterList: filterList[1].length ? filterList[1] : null,
+          customFilterListRender: v => `Title: ${v}`,
+          filterType: 'textField' // set filterType's at the column level
+        }
+      },
+      {
+        name: "Location",
+        options: {
+          display: this.state.display[2],
+          filter: false,
+          filterList: filterList[2].length ? filterList[2] : null,
+        }
+      },
+      {
+        name: "Age",
+        options: {
+          display: this.state.display[3],
+          filter: true,
+          filterList: filterList[3].length ? filterList[3] : null,
+          customFilterListRender: v => `Age: ${v}`,
+        }
+      },
+      {
+        name: "Salary",
+        options: {
+          display: this.state.display[4],
+          filter: true,
+          filterList: filterList[4].length ? filterList[4] : null,
+          customFilterListRender: v => `Salary: ${v}`,
+          sort: false
+        }
+      }
+    ];
+
+    const options = {
+      filter: true,
+      onFilterChange: (changedColumn, newFilterList) => {
+        this.setState({ filterList: newFilterList });
+      },
+      selectableRows: 'multiple',
+      filterType: 'dropdown',
+      responsive: 'stacked',
+      rowsPerPage: 10,
+      page: 1,
+    };
+
+    return (
+      <React.Fragment>
+        <MUIDataTable title={"ACME Employee list"} data={data} columns={columns} options={options} />
+        <button onClick={() => this.setState({
+          filterList: [
+            ['Franky Miles'],
+            ['Business Analyst'],
+            [],
+            [],
+            []
+          ]
+        })}
+        >
+          Set starter filters!
+        </button>
+        <button onClick={this.handleFilterNameChange}>Change filter names for first column!</button>
+        <button onClick={this.handleAddData}>Add row data!</button>
+        <button onClick={this.handleChangeDisplay}>Change which columns are displayed!</button>
+      </React.Fragment>
+    );
+
+  }
+}
+
+ReactDOM.render(<Example />, document.getElementById("app-root"));
diff --git a/examples/customize-footer/CustomFooter.js b/examples/customize-footer/CustomFooter.js
index b404927..c30edca 100644
--- a/examples/customize-footer/CustomFooter.js
+++ b/examples/customize-footer/CustomFooter.js
@@ -2,6 +2,7 @@ import React from "react";
 import TableFooter from "@material-ui/core/TableFooter";
 import TableRow from "@material-ui/core/TableRow";
 import TableCell from "@material-ui/core/TableCell";
+import MuiTablePagination from "@material-ui/core/TablePagination";
 import { withStyles } from "@material-ui/core/styles";
 
 const defaultFooterStyles = {
@@ -9,20 +10,52 @@ const defaultFooterStyles = {
 
 class CustomFooter extends React.Component {
 
+  handleRowChange = event => {
+    this.props.changeRowsPerPage(event.target.value);
+  };
+
+  handlePageChange = (_, page) => {
+    this.props.changePage(page);
+  };
+
   render() {
-    const { classes } = this.props;
+    const { count, classes, textLabels, rowsPerPage, page } = this.props;
+
+    const footerStyle = {
+      display:'flex', 
+      justifyContent: 'flex-end',
+      padding: '0px 24px 0px 24px'
+    };
 
     return (
-    <TableFooter>
-      <TableRow>
-        <TableCell>
-          some content
-        </TableCell>
-      </TableRow>
-    </TableFooter>
+      <TableFooter>
+        <TableRow>
+          <TableCell style={footerStyle} colSpan={1000}>
+            <button>Custom Option</button>
+          
+            <MuiTablePagination
+              component="div"
+              count={count}
+              rowsPerPage={rowsPerPage}
+              page={page}
+              labelRowsPerPage={textLabels.rowsPerPage}
+              labelDisplayedRows={({ from, to, count }) => `${from}-${to} ${textLabels.displayRows} ${count}`}
+              backIconButtonProps={{
+                'aria-label': textLabels.previous,
+              }}
+              nextIconButtonProps={{
+                'aria-label': textLabels.next,
+              }}
+              rowsPerPageOptions={[10,20,100]}
+              onChangePage={this.handlePageChange}
+              onChangeRowsPerPage={this.handleRowChange}
+            />
+          </TableCell>
+        </TableRow>
+      </TableFooter>
     );
   }
 
 }
 
-export default withStyles(defaultFooterStyles, { name: "CustomFooter" })(CustomFooter);
+export default withStyles(defaultFooterStyles, { name: "CustomFooter" })(CustomFooter);
\ No newline at end of file
diff --git a/examples/customize-footer/index.js b/examples/customize-footer/index.js
index db7e7bf..0ee9de4 100644
--- a/examples/customize-footer/index.js
+++ b/examples/customize-footer/index.js
@@ -44,13 +44,18 @@ class Example extends React.Component {
 
     const options = {
       filter: true,
-      selectableRows: true,
       filterType: 'dropdown',
       responsive: 'stacked',
       rowsPerPage: 10,
-      customFooter: (count, page, rowsPerPage, changeRowsPerPage, changePage) => {
+      customFooter: (count, page, rowsPerPage, changeRowsPerPage, changePage, textLabels) => {
         return (  
-          <CustomFooter changePage={changePage} count={count} />
+          <CustomFooter 
+            count={count} 
+            page={page} 
+            rowsPerPage={rowsPerPage} 
+            changeRowsPerPage={changeRowsPerPage} 
+            changePage={changePage} 
+            textLabels={textLabels} />
         );
       }
     };
@@ -62,4 +67,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+ReactDOM.render(<Example />, document.getElementById("app-root"));
\ No newline at end of file
diff --git a/examples/selectable-rows/index.js b/examples/selectable-rows/index.js
index 4323549..139e803 100644
--- a/examples/selectable-rows/index.js
+++ b/examples/selectable-rows/index.js
@@ -90,7 +90,9 @@ class Example extends React.Component {
       onRowClick: (rowData, rowState) => {
         console.log(rowData, rowState);
       },
-      isRowSelectable: (dataIndex) => {
+      isRowSelectable: (dataIndex, selectedRows) => {
+        //prevents selection of any additional row after the third
+        if (selectedRows.data.length > 2 && selectedRows.data.filter(d => d.dataIndex === dataIndex).length === 0) return false;
         //prevents selection of row with title "Attorney"
         return data[dataIndex][1] != "Attorney";
       }
diff --git a/package-lock.json b/package-lock.json
index 2e4c77d..9bff58a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4498,7 +4498,8 @@
           "version": "2.1.1",
           "resolved": "",
           "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "aproba": {
           "version": "1.2.0",
@@ -4522,13 +4523,15 @@
           "version": "1.0.0",
           "resolved": "",
           "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "brace-expansion": {
           "version": "1.1.11",
           "resolved": "",
           "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
           "dev": true,
+          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -4545,19 +4548,22 @@
           "version": "1.1.0",
           "resolved": "",
           "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
           "resolved": "",
           "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "console-control-strings": {
           "version": "1.1.0",
           "resolved": "",
           "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
@@ -4688,7 +4694,8 @@
           "version": "2.0.3",
           "resolved": "",
           "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
@@ -4702,6 +4709,7 @@
           "resolved": "",
           "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
@@ -4718,6 +4726,7 @@
           "resolved": "",
           "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
           "dev": true,
+          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -4726,13 +4735,15 @@
           "version": "0.0.8",
           "resolved": "",
           "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "minipass": {
           "version": "2.2.4",
           "resolved": "",
           "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==",
           "dev": true,
+          "optional": true,
           "requires": {
             "safe-buffer": "^5.1.1",
             "yallist": "^3.0.0"
@@ -4753,6 +4764,7 @@
           "resolved": "",
           "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
           "dev": true,
+          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
@@ -4841,7 +4853,8 @@
           "version": "1.0.1",
           "resolved": "",
           "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
@@ -4855,6 +4868,7 @@
           "resolved": "",
           "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
           "dev": true,
+          "optional": true,
           "requires": {
             "wrappy": "1"
           }
@@ -4950,7 +4964,8 @@
           "version": "5.1.1",
           "resolved": "",
           "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "safer-buffer": {
           "version": "2.1.2",
@@ -4992,6 +5007,7 @@
           "resolved": "",
           "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -5013,6 +5029,7 @@
           "resolved": "",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
+          "optional": true,
           "requires": {
             "ansi-regex": "^2.0.0"
           }
@@ -5061,13 +5078,15 @@
           "version": "1.0.2",
           "resolved": "",
           "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "yallist": {
           "version": "3.0.2",
           "resolved": "",
           "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
-          "dev": true
+          "dev": true,
+          "optional": true
         }
       }
     },
@@ -7780,6 +7799,7 @@
           "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
           "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
           "dev": true,
+          "optional": true,
           "requires": {
             "kind-of": "^3.0.2",
             "longest": "^1.0.1",
@@ -9113,7 +9133,8 @@
           "version": "1.0.1",
           "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
           "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=",
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "loose-envify": {
           "version": "1.3.1",
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index 73b2d5a..1bce47c 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -208,7 +208,7 @@ class MUIDataTable extends React.Component {
     this.updateDividers = () => {};
   }
 
-  componentWillMount() {
+  UNSAFE_componentWillMount() {
     this.initializeTable(this.props);
   }
 
@@ -295,6 +295,9 @@ class MUIDataTable extends React.Component {
     if (props.options.rowsPerPageOptions) {
       this.options.rowsPerPageOptions = props.options.rowsPerPageOptions;
     }
+    if (['scroll', 'stacked'].indexOf(this.options.responsive) === -1) {
+      console.error('Invalid option value for responsive. Please use string option: stacked | scroll');
+    }
   }
 
   validateOptions(options) {
@@ -1023,7 +1026,7 @@ class MUIDataTable extends React.Component {
       const { isRowSelectable } = this.options;
       this.setState(
         prevState => {
-          const { displayData } = prevState;
+          const { displayData, selectedRows: prevSelectedRows } = prevState;
           const selectedRowsLen = prevState.selectedRows.data.length;
           const isDeselect =
             selectedRowsLen === displayData.length || (selectedRowsLen < displayData.length && selectedRowsLen > 0)
@@ -1031,7 +1034,7 @@ class MUIDataTable extends React.Component {
               : false;
 
           let selectedRows = displayData.reduce((arr, d, i) => {
-            const selected = isRowSelectable ? isRowSelectable(displayData[i].dataIndex) : true;
+            const selected = isRowSelectable ? isRowSelectable(displayData[i].dataIndex, prevSelectedRows) : true;
             selected && arr.push({ index: i, dataIndex: displayData[i].dataIndex });
             return arr;
           }, []);
diff --git a/src/components/Popover.js b/src/components/Popover.js
index 29bc6fa..a912316 100644
--- a/src/components/Popover.js
+++ b/src/components/Popover.js
@@ -8,7 +8,7 @@ class Popover extends React.Component {
     open: false,
   };
 
-  componentWillMount() {
+  UNSAFE_componentWillMount() {
     this.anchorEl = null;
   }
 
diff --git a/src/components/TableBody.js b/src/components/TableBody.js
index 8207b4f..dedb641 100644
--- a/src/components/TableBody.js
+++ b/src/components/TableBody.js
@@ -87,11 +87,12 @@ class TableBody extends React.Component {
   }
 
   isRowSelectable(dataIndex) {
-    const { options } = this.props;
+    const { options, selectedRows } = this.props;
     if (options.isRowSelectable) {
-      return options.isRowSelectable(dataIndex);
+      return options.isRowSelectable(dataIndex, selectedRows);
+    } else {
+      return true;
     }
-    return true;
   }
 
   handleRowSelect = data => {
diff --git a/src/components/TableFooter.js b/src/components/TableFooter.js
index c5c33a7..56ff689 100644
--- a/src/components/TableFooter.js
+++ b/src/components/TableFooter.js
@@ -16,7 +16,7 @@ class TableFooter extends React.Component {
     return (
       <MuiTable>
         {options.customFooter
-          ? options.customFooter(rowCount, page, rowsPerPage, changeRowsPerPage, changePage)
+          ? options.customFooter(rowCount, page, rowsPerPage, changeRowsPerPage, changePage, options.textLabels.pagination)
           : options.pagination && (
               <TablePagination
                 count={rowCount}
diff --git a/src/components/TablePagination.js b/src/components/TablePagination.js
index fcc6768..cc20ea1 100644
--- a/src/components/TablePagination.js
+++ b/src/components/TablePagination.js
@@ -44,7 +44,6 @@ class TablePagination extends React.Component {
   };
 
   handlePageChange = (_, page) => {
-    const { options } = this.props;
     this.props.changePage(page);
   };
 
diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js
index 41bc119..c25125c 100644
--- a/src/components/TableToolbar.js
+++ b/src/components/TableToolbar.js
@@ -13,10 +13,10 @@ import PrintIcon from '@material-ui/icons/Print';
 import ViewColumnIcon from '@material-ui/icons/ViewColumn';
 import FilterIcon from '@material-ui/icons/FilterList';
 import ReactToPrint from 'react-to-print';
-import styled from '../styled';
+import { withStyles } from '@material-ui/core/styles';
 import { createCSVDownload } from '../utils';
 
-export const defaultToolbarStyles = (theme, props) => ({
+export const defaultToolbarStyles = theme => ({
   root: {},
   left: {
     flex: '1 1 auto',
@@ -43,10 +43,6 @@ export const defaultToolbarStyles = (theme, props) => ({
     marginTop: '10px',
     marginRight: '8px',
   },
-  ...(props.options.responsive ? { ...responsiveToolbarStyles(theme) } : {}),
-});
-
-export const responsiveToolbarStyles = theme => ({
   [theme.breakpoints.down('sm')]: {
     titleRoot: {},
     titleText: {
@@ -315,4 +311,4 @@ class TableToolbar extends React.Component {
   }
 }
 
-export default styled(TableToolbar)(defaultToolbarStyles, { name: 'MUIDataTableToolbar' });
+export default withStyles(defaultToolbarStyles, { name: 'MUIDataTableToolbar' })(TableToolbar);
diff --git a/src/styled.js b/src/styled.js
deleted file mode 100644
index 543db2f..0000000
--- a/src/styled.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import merge from 'lodash.merge';
-import { withStyles } from '@material-ui/core/styles';
-
-/*
- *  Material-UI does not yet support ability to grab props within style()
- *  https://github.com/mui-org/material-ui/issues/7633
- *
- *  This is a workaround provided from the thread
- */
-
-const styles = (theme, props, style) => {
-  return typeof style === 'function' ? style(theme, props) : style;
-};
-
-class StyledComponent extends React.Component {
-  static propTypes = {
-    classes: PropTypes.object.isRequired,
-    className: PropTypes.string,
-  };
-
-  render() {
-    const { classes, className = '', WrappedComponent, ...passThroughProps } = this.props;
-
-    return <WrappedComponent classes={classes} className={className} {...passThroughProps} />;
-  }
-}
-
-const styled = (WrappedComponent, customProps = {}) => {
-  return (style, options = {}) => {
-    const HOCProps = WrappedComponent => {
-      return class _HOCProps extends React.Component {
-        constructor(props) {
-          super(props);
-          this.FinalComponent = withStyles(theme => {
-            const defaultStyles = styles(theme, props, style);
-            const mergedStyles = merge(defaultStyles, props.styles ? props.styles : {});
-            return mergedStyles;
-          }, options)(StyledComponent);
-        }
-
-        render() {
-          const { styles, ...otherProps } = this.props;
-          return <this.FinalComponent {...customProps} {...otherProps} WrappedComponent={WrappedComponent} />;
-        }
-      };
-    };
-    return HOCProps(WrappedComponent);
-  };
-};
-
-export default styled;


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 1fb54de..7b45011 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -167,6 +167,103 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(actualResult, expectedResult);
   });
 
+  it('should correctly rebuild internal columns data structure', () => {
+    const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
+
+    shallowWrapper.setProps({
+      columns: [
+        { name: 'Test Name', options: { filter: false, display: 'excluded', 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: 'Empty', options: { empty: true, filterType: 'checkbox' } },
+      ]
+    })
+
+    const actualResult = shallowWrapper.dive().state().columns;
+
+    const expectedResult = [
+      {
+        display: 'excluded',
+        empty: false,
+        print: true,
+        name: 'Test Name',
+        sort: true,
+        filter: false,
+        label: 'Test Name',
+        download: true,
+        searchable: true,
+        sortDirection: null,
+        viewColumns: true,
+        customFilterListRender: renderCustomFilterList,
+        customBodyRender: renderName,
+      },
+      {
+        display: 'true',
+        empty: false,
+        print: true,
+        name: 'Company',
+        sort: true,
+        filter: true,
+        label: 'Company',
+        download: true,
+        searchable: true,
+        viewColumns: true,
+        sortDirection: null,
+      },
+      {
+        display: 'true',
+        empty: false,
+        print: true,
+        name: 'City',
+        sort: true,
+        filter: true,
+        filterType: 'textField',
+        label: 'City Label',
+        download: true,
+        searchable: true,
+        viewColumns: true,
+        sortDirection: null,
+        customBodyRender: renderCities,
+      },
+      {
+        display: 'true',
+        empty: false,
+        print: true,
+        name: 'State',
+        sort: true,
+        filter: true,
+        filterType: 'multiselect',
+        label: 'State',
+        download: true,
+        searchable: true,
+        viewColumns: true,
+        sortDirection: null,
+        customBodyRender: renderState,
+        customHeadRender: renderHead,
+      },
+      {
+        display: 'true',
+        empty: true,
+        print: true,
+        name: 'Empty',
+        sort: true,
+        filter: true,
+        filterType: 'checkbox',
+        label: 'Empty',
+        download: true,
+        searchable: true,
+        viewColumns: true,
+        sortDirection: null,
+      },
+    ];
+
+    assert.deepEqual(actualResult, expectedResult);
+  });
+
   it('should correctly build internal table data and displayData structure', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const state = shallowWrapper.dive().state();
diff --git a/test/MUIDataTableBody.test.js b/test/MUIDataTableBody.test.js
index 1fb866e..bb4613f 100644
--- a/test/MUIDataTableBody.test.js
+++ b/test/MUIDataTableBody.test.js
@@ -235,14 +235,14 @@ describe('<TableBody />', function() {
     assert.strictEqual(toggleExpandRow.callCount, 0);
   });
 
-  it('should not gather selected row data when clicking row with selectableRowsOnClick=true when it is disabled with isRowSelectable.', () => {
+  it('should not gather selected row data when clicking row with selectableRowsOnClick=true when it is disabled with isRowSelectable via index.', () => {
     let selectedRowData;
     const options = {
       selectableRows: true,
       selectableRowsOnClick: true,
       isRowSelectable: dataIndex => (dataIndex === 2 ? false : true),
     };
-    const selectRowUpdate = (type, data) => (selectedRowData = data);
+    const selectRowUpdate = (_, data) => (selectedRowData = data);
     const toggleExpandRow = spy();
 
     const mountWrapper = mount(
@@ -271,6 +271,86 @@ describe('<TableBody />', function() {
     assert.strictEqual(toggleExpandRow.callCount, 0);
   });
 
+  it('should not gather selected row data when clicking row with selectableRowsOnClick=true when it is disabled with isRowSelectable via selectedRows.', () => {
+    let selectedRowData;
+    const options = {
+      selectableRows: true,
+      selectableRowsOnClick: true,
+      isRowSelectable: (index, selectedRows) => selectedRows.lookup[index] || selectedRows.data.length < 1,
+    };
+    const selectRowUpdate = (_, data) => (selectedRowData = data);
+    const toggleExpandRow = spy();
+    const initialSelectedRows = {
+      data: [{ index: 1, dataIndex: 1 }],
+      lookup: { 1: true },
+    };
+
+    const mountWrapper = mount(
+      <TableBody
+        data={displayData}
+        count={displayData.length}
+        columns={columns}
+        page={0}
+        rowsPerPage={10}
+        selectedRows={initialSelectedRows}
+        selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
+        options={options}
+        searchText={''}
+        filterList={[]}
+      />,
+    );
+
+    mountWrapper
+      .find('#MUIDataTableBodyRow-2')
+      .first()
+      .simulate('click');
+
+    assert.isUndefined(selectedRowData);
+    assert.strictEqual(toggleExpandRow.callCount, 0);
+  });
+
+  it('should gather selected row data when clicking row with selectableRowsOnClick=true when it is enabled with isRowSelectable via index.', () => {
+    let selectedRowData;
+    const options = {
+      selectableRows: true,
+      selectableRowsOnClick: true,
+      isRowSelectable: (index, selectedRows) => selectedRows.lookup[index] || selectedRows.data.length < 1,
+    };
+    const selectRowUpdate = (_, data) => (selectedRowData = data);
+    const toggleExpandRow = spy();
+    const initialSelectedRows = {
+      data: [{ index: 1, dataIndex: 1 }],
+      lookup: { 1: true },
+    };
+
+    const mountWrapper = mount(
+      <TableBody
+        data={displayData}
+        count={displayData.length}
+        columns={columns}
+        page={0}
+        rowsPerPage={10}
+        selectedRows={initialSelectedRows}
+        selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
+        options={options}
+        searchText={''}
+        filterList={[]}
+      />,
+    );
+
+    mountWrapper
+      .find('#MUIDataTableBodyRow-1')
+      .first()
+      .simulate('click');
+
+    assert.isDefined(selectedRowData);
+    assert.strictEqual(toggleExpandRow.callCount, 0);
+  });
+
   it('should gather expanded row data when clicking row with expandableRows=true and expandableRowsOnClick=true.', () => {
     let expandedRowData;
     const options = { selectableRows: true, expandableRows: true, expandableRowsOnClick: true };
@@ -493,4 +573,35 @@ describe('<TableBody />', function() {
 
     expect(html).to.contain('Test_Text');
   });
+
+  it('should pass in selectedRows to isRowSelectable', () => {
+    const selectedIndex = 2;
+    const originalSelectedRows = {
+      data: [{ index: selectedIndex, dataIndex: selectedIndex }],
+      lookup: { [selectedIndex]: true },
+    };
+    const isRowSelectable = spy((_, selectedRows) => {
+      assert.deepEqual(selectedRows, originalSelectedRows);
+      return true;
+    });
+
+    const options = { selectableRows: true, isRowSelectable };
+
+    mount(
+      <TableBody
+        data={displayData}
+        count={displayData.length}
+        columns={columns}
+        page={0}
+        selectedRows={originalSelectedRows}
+        rowsPerPage={10}
+        expandedRows={[]}
+        options={options}
+        searchText={''}
+        filterList={[]}
+      />,
+    );
+
+    assert.equal(isRowSelectable.callCount, displayData.length);
+  });
 });
diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js
index dd00ccf..f44a294 100644
--- a/test/MUIDataTableToolbar.test.js
+++ b/test/MUIDataTableToolbar.test.js
@@ -130,8 +130,6 @@ describe('<TableToolbar />', function() {
     const shallowWrapper = shallow(
       <TableToolbar columns={columns} data={data} options={newOptions} setTableAction={setTableAction} />,
     )
-      .dive()
-      .dive()
       .dive();
     const instance = shallowWrapper.instance();
     instance.setActiveIcon('search');
@@ -144,8 +142,6 @@ describe('<TableToolbar />', function() {
     const shallowWrapper = shallow(
       <TableToolbar columns={columns} data={data} options={options} setTableAction={setTableAction} />,
     )
-      .dive()
-      .dive()
       .dive();
     const instance = shallowWrapper.instance();
 
@@ -167,8 +163,6 @@ describe('<TableToolbar />', function() {
         setTableAction={setTableAction}
       />,
     )
-      .dive()
-      .dive()
       .dive();
     const instance = shallowWrapper.instance();
 
@@ -195,8 +189,6 @@ describe('<TableToolbar />', function() {
     const shallowWrapper = shallow(
       <TableToolbar columns={columns} data={data} options={options} setTableAction={setTableAction} />,
     )
-      .dive()
-      .dive()
       .dive();
     const instance = shallowWrapper.instance();
 
@@ -218,8 +210,6 @@ describe('<TableToolbar />', function() {
       />,
     );
     const instance = shallowWrapper
-      .dive()
-      .dive()
       .dive()
       .instance();
 
@@ -247,8 +237,6 @@ describe('<TableToolbar />', function() {
     );
 
     const instance = shallowWrapper
-      .dive()
-      .dive()
       .dive()
       .instance();