Repository: gregnb/mui-datatables
Pull request #892 (2019-09-03 10:01:04)

Add failing test for sorting columns with undefined data
Supplement to https://github.com/gregnb/mui-datatables/pull/877.


Patch:
diff --git a/README.md b/README.md
index 68f284f..a99b7b7 100644
--- a/README.md
+++ b/README.md
@@ -142,7 +142,8 @@ 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: number, selectedRows: object(lookup: {[dataIndex]: boolean}, data: arrayOfObjects: {index: number, dataIndex: number})) => boolean`.
+|**`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: number, dataIndex: number})) => boolean`.
+|**`selectableRowsHeader`**|boolean|true|Show/hide the select all/deselect all checkbox header for selectable rows
 |**`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`
@@ -180,6 +181,7 @@ The component accepts the following props:
 |**`onChangeRowsPerPage`**|function||Callback function that triggers when the number of rows per page has changed. `function(numberOfRows: number) => void`
 |**`onSearchChange`**|function||Callback function that triggers when the search text value has changed. `function(searchText: string) => void`
 |**`onSearchOpen`**|function||Callback function that triggers when the searchbox opens. `function() => void`
+|**`onSearchClose`**|function||Callback function that triggers when the searchbox closes. `function() => void`
 |**`onFilterChange`**|function||Callback function that triggers when filters have changed. `function(changedColumn: string, filterList: array) => void`
 |**`onColumnSortChange`**|function||Callback function that triggers when a column has been sorted. `function(changedColumn: string, direction: string) => void`
 |**`onColumnViewChange`**|function||Callback function that triggers when a column view has been changed. `function(changedColumn: string, action: string) => void`
@@ -340,6 +342,7 @@ const options = {
     body: {
       noMatch: "Sorry, no matching records found",
       toolTip: "Sort",
+      columnHeaderTooltip: column => `Sort for ${column.label}`
     },
     pagination: {
       next: "Next Page",
diff --git a/examples/Router/ExamplesGrid.js b/examples/Router/ExamplesGrid.js
new file mode 100644
index 0000000..e90f149
--- /dev/null
+++ b/examples/Router/ExamplesGrid.js
@@ -0,0 +1,63 @@
+import {Link} from "react-router-dom";
+import {Card, CardContent, Grid, Typography} from "@material-ui/core";
+import React from "react";
+import examples from "../examples";
+import {withStyles} from "@material-ui/core/styles/index";
+
+const styles = {
+    container: {
+        flexGrow: 1,
+        justifyContent: 'center',
+        alignItems: 'center',
+        marginTop: 16,
+    },
+    card: {
+        '&:hover': {
+            background: 'lightgrey',
+            fontWeight: 500,
+        }
+    },
+    cardContent: {
+        '&:last-child': {
+            padding: 8,
+        }
+    },
+    link: {
+        textDecoration: 'none',
+    },
+    label: {
+        fontWeight: 'inherit'
+    }
+};
+
+function ExamplesGrid(props) {
+    const {classes} = props;
+
+    // Sort Examples alphabetically
+    const examplesSorted = {};
+    Object.keys(examples).sort().forEach(function (key) {
+        examplesSorted[key] = examples[key];
+    });
+
+    const examplesSortedKeys = Object.keys(examplesSorted);
+
+    return <React.Fragment>
+        <Typography variant="h5" align="center">Choose an Example</Typography>
+        <Typography variant="subtitle2" align="center">({examplesSortedKeys.length}) Examples</Typography>
+        <Grid container className={classes.container} spacing={16}>
+            {examplesSortedKeys.map((label, index) => (
+                <Grid key={index} item md={2}>
+                    <Link className={classes.link} to={`/${label.replace(/\s+/g, '-').toLowerCase()}`}>
+                        <Card className={classes.card}>
+                            <CardContent className={classes.cardContent}>
+                                <Typography variant="subtitle1" className={classes.label} align="center">{label}</Typography>
+                            </CardContent>
+                        </Card>
+                    </Link>
+                </Grid>
+            ))}
+        </Grid>
+    </React.Fragment>;
+}
+
+export default withStyles(styles)(ExamplesGrid);
diff --git a/examples/Router/index.js b/examples/Router/index.js
new file mode 100644
index 0000000..1fa75b8
--- /dev/null
+++ b/examples/Router/index.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import {HashRouter as Router, Route, Switch} from 'react-router-dom';
+import {withStyles} from "@material-ui/core/styles/index";
+import ExamplesGrid from "./ExamplesGrid";
+import examples from "../examples";
+
+const styles = {
+    root: {
+        display: 'flex',
+        justifyContent: 'center',
+    },
+    contentWrapper: {
+        width: '100%',
+    },
+};
+
+function Examples(props) {
+    const {classes} = props;
+    return <main className={classes.root}>
+        <div className={classes.contentWrapper}>
+            <Router hashType="noslash">
+                <Switch>
+                    <Route path="/" exact render={() => <ExamplesGrid examples={examples}/>}/>
+                    {Object.keys(examples).map((label, index) => (
+                        <Route key={index} path={`/${label.replace(/\s+/g, '-').toLowerCase()}`} exact component={examples[label]}/>
+                    ))}
+                </Switch>
+            </Router>
+        </div>
+    </main>;
+}
+
+const StyledExamples = withStyles(styles)(Examples);
+
+ReactDOM.render(<StyledExamples/>, document.getElementById('app-root'));
diff --git a/examples/array-value-columns/index.js b/examples/array-value-columns/index.js
index d7a70a4..c771030 100644
--- a/examples/array-value-columns/index.js
+++ b/examples/array-value-columns/index.js
@@ -104,4 +104,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/column-filters/index.js b/examples/column-filters/index.js
index f300690..c1fd4f9 100644
--- a/examples/column-filters/index.js
+++ b/examples/column-filters/index.js
@@ -101,4 +101,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/column-options-update/index.js b/examples/column-options-update/index.js
index adfc9d0..9930923 100644
--- a/examples/column-options-update/index.js
+++ b/examples/column-options-update/index.js
@@ -154,4 +154,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/component/index.js b/examples/component/index.js
index 72e1d78..8780253 100644
--- a/examples/component/index.js
+++ b/examples/component/index.js
@@ -136,4 +136,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/csv-export/index.js b/examples/csv-export/index.js
index 40d9b04..b8fcb99 100644
--- a/examples/csv-export/index.js
+++ b/examples/csv-export/index.js
@@ -135,4 +135,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById('app-root'));
+export default Example;
diff --git a/examples/custom-action-columns/index.js b/examples/custom-action-columns/index.js
index 7253004..7606476 100644
--- a/examples/custom-action-columns/index.js
+++ b/examples/custom-action-columns/index.js
@@ -181,4 +181,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/customize-columns/index.js b/examples/customize-columns/index.js
index 04c11a0..157fd2b 100644
--- a/examples/customize-columns/index.js
+++ b/examples/customize-columns/index.js
@@ -95,4 +95,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/customize-filter/index.js b/examples/customize-filter/index.js
index 3d8ef34..a9ecf30 100644
--- a/examples/customize-filter/index.js
+++ b/examples/customize-filter/index.js
@@ -1,6 +1,5 @@
 import { FormGroup, FormLabel, TextField } from '@material-ui/core';
 import React from 'react';
-import ReactDOM from 'react-dom';
 import MUIDataTable from '../../src';
 
 class Example extends React.Component {
@@ -150,4 +149,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById('app-root'));
+export default Example;
diff --git a/examples/customize-footer/index.js b/examples/customize-footer/index.js
index 0ee9de4..1367dd3 100644
--- a/examples/customize-footer/index.js
+++ b/examples/customize-footer/index.js
@@ -1,5 +1,4 @@
 import React from "react";
-import ReactDOM from "react-dom";
 import MUIDataTable from "../../src/";
 import CustomFooter from "./CustomFooter";
 
@@ -67,4 +66,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
\ No newline at end of file
+export default Example;
diff --git a/examples/customize-rows/index.js b/examples/customize-rows/index.js
index d2b12ed..f2893be 100644
--- a/examples/customize-rows/index.js
+++ b/examples/customize-rows/index.js
@@ -93,4 +93,4 @@ function Example() {
   );
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/customize-search-render/CustomSearchRender.js b/examples/customize-search-render/CustomSearchRender.js
index 0fa6589..3bdd5e2 100644
--- a/examples/customize-search-render/CustomSearchRender.js
+++ b/examples/customize-search-render/CustomSearchRender.js
@@ -22,12 +22,6 @@ const defaultSearchStyles = theme => ({
 
 class CustomSearchRender extends React.Component {
   handleTextChange = event => {
-    const { onSearchChange } = this.props.options;
-
-    if (onSearchChange) {
-      onSearchChange(event.target.value);
-    }
-
     this.props.onSearch(event.target.value);
   };
 
diff --git a/examples/customize-search-render/index.js b/examples/customize-search-render/index.js
index db816c1..2ff9eef 100644
--- a/examples/customize-search-render/index.js
+++ b/examples/customize-search-render/index.js
@@ -67,4 +67,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById('app-root'));
+export default Example;
diff --git a/examples/customize-search/index.js b/examples/customize-search/index.js
index a958358..7ef8830 100644
--- a/examples/customize-search/index.js
+++ b/examples/customize-search/index.js
@@ -104,4 +104,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById('app-root'));
+export default Example;
diff --git a/examples/customize-sorting/index.js b/examples/customize-sorting/index.js
index e876249..f97e27c 100644
--- a/examples/customize-sorting/index.js
+++ b/examples/customize-sorting/index.js
@@ -34,4 +34,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
\ No newline at end of file
+export default Example;
diff --git a/examples/customize-styling/index.js b/examples/customize-styling/index.js
index e849100..5166866 100644
--- a/examples/customize-styling/index.js
+++ b/examples/customize-styling/index.js
@@ -1,5 +1,4 @@
 import React from "react";
-import ReactDOM from "react-dom";
 import MUIDataTable from "../../src/";
 import {createMuiTheme, MuiThemeProvider, withStyles} from '@material-ui/core/styles';
 import classnames from 'classnames';
@@ -134,6 +133,4 @@ class Example extends React.Component {
   }
 }
 
-const ExampleWithStyles = withStyles(customStyles, {name: "Example"})(Example);
-
-ReactDOM.render(<ExampleWithStyles/>, document.getElementById("app-root"));
+export default withStyles(customStyles, {name: "ExampleCard.js"})(Example);
diff --git a/examples/customize-toolbar/index.js b/examples/customize-toolbar/index.js
index 0571684..74834a3 100644
--- a/examples/customize-toolbar/index.js
+++ b/examples/customize-toolbar/index.js
@@ -62,4 +62,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/customize-toolbarselect/index.js b/examples/customize-toolbarselect/index.js
index 7dd528c..906c4d5 100644
--- a/examples/customize-toolbarselect/index.js
+++ b/examples/customize-toolbarselect/index.js
@@ -55,4 +55,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/data-as-objects/index.js b/examples/data-as-objects/index.js
index e4c9031..e04b4e5 100644
--- a/examples/data-as-objects/index.js
+++ b/examples/data-as-objects/index.js
@@ -79,4 +79,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/examples.js b/examples/examples.js
new file mode 100644
index 0000000..59972ed
--- /dev/null
+++ b/examples/examples.js
@@ -0,0 +1,66 @@
+import ArrayValueColumns from "./array-value-columns";
+import ColumnFilters from "./column-filters";
+import ColumnOptionsUpdate from "./column-options-update";
+import Component from "./component";
+import CSVExport from "./csv-export";
+import CustomActionColumns from "./custom-action-columns";
+import CustomizeColumns from "./customize-columns";
+import CustomizeFilter from "./customize-filter";
+import CustomizeFooter from "./customize-footer";
+import CustomizeRows from "./customize-rows";
+import CustomizeSearch from "./customize-search";
+import CustomizeSearchRender from "./customize-search-render";
+import CustomizeSorting from "./customize-sorting";
+import CustomizeStyling from "./customize-styling";
+import CustomizeToolbar from "./customize-toolbar";
+import CustomizeToolbarSelect from "./customize-toolbarselect";
+import DataAsObjects from "./data-as-objects";
+import ExpandableRows from "./expandable-rows";
+import FixedHeader from "./fixed-header";
+import HideColumnsPrint from "./hide-columns-print";
+import OnDownload from "./on-download";
+import OnTableInit from "./on-table-init";
+import ResizableColumns from "./resizable-columns";
+import SelectableRows from "./selectable-rows";
+import ServerSideOptions from "./serverside-options";
+import ServerSidePagination from "./serverside-pagination";
+import Simple from "./simple";
+import SimpleNoToolbar from "./simple-no-toolbar";
+import TextLocalization from "./text-localization";
+import Themes from "./themes";
+
+/**
+ * Here you can add any extra examples with the Card label as the key, and the component to render as the value
+ */
+export default {
+    'Array Value Columns': ArrayValueColumns,
+    'Column Filters': ColumnFilters,
+    'Column Option Update': ColumnOptionsUpdate,
+    'Component': Component,
+    'CSV Export': CSVExport,
+    'Custom ActionColumns': CustomActionColumns,
+    'Customize columns': CustomizeColumns,
+    'Customize Filter': CustomizeFilter,
+    'Customize Footer': CustomizeFooter,
+    'Customize Rows': CustomizeRows,
+    'Customize Search': CustomizeSearch,
+    'Customize Search Render': CustomizeSearchRender,
+    'Customize Sorting': CustomizeSorting,
+    'Customize Styling': CustomizeStyling,
+    'Customize Toolbar': CustomizeToolbar,
+    'Customize Toolbar Select': CustomizeToolbarSelect,
+    'Data As Objects': DataAsObjects,
+    'expandableRows': ExpandableRows,
+    'fixedHeader': FixedHeader,
+    'Hide Columns Print': HideColumnsPrint,
+    'OnDownload': OnDownload,
+    'OnTableInit': OnTableInit,
+    'resizableColumns': ResizableColumns,
+    'selectableRows': SelectableRows,
+    'serverSide Options': ServerSideOptions,
+    'serverSide Pagination': ServerSidePagination,
+    'Simple': Simple,
+    'Simple No Toolbar': SimpleNoToolbar,
+    'Text Localization': TextLocalization,
+    'Themes': Themes,
+};
diff --git a/examples/expandable-rows/index.js b/examples/expandable-rows/index.js
index 0d38bea..ba67d66 100644
--- a/examples/expandable-rows/index.js
+++ b/examples/expandable-rows/index.js
@@ -102,4 +102,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/fixed-header/index.js b/examples/fixed-header/index.js
index c561b80..efe2b64 100644
--- a/examples/fixed-header/index.js
+++ b/examples/fixed-header/index.js
@@ -89,4 +89,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/hide-columns-print/index.js b/examples/hide-columns-print/index.js
index 87b5ebb..e514ea4 100644
--- a/examples/hide-columns-print/index.js
+++ b/examples/hide-columns-print/index.js
@@ -91,4 +91,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
\ No newline at end of file
+export default Example;
diff --git a/examples/on-download/index.js b/examples/on-download/index.js
index f2e9af4..a9d0677 100644
--- a/examples/on-download/index.js
+++ b/examples/on-download/index.js
@@ -118,4 +118,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById('app-root'));
+export default Example;
diff --git a/examples/on-table-init/index.js b/examples/on-table-init/index.js
index aece80f..b178d00 100644
--- a/examples/on-table-init/index.js
+++ b/examples/on-table-init/index.js
@@ -79,4 +79,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById('app-root'));
+export default Example;
diff --git a/examples/resizable-columns/index.js b/examples/resizable-columns/index.js
index ab5a891..bda5842 100644
--- a/examples/resizable-columns/index.js
+++ b/examples/resizable-columns/index.js
@@ -67,4 +67,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/selectable-rows/index.js b/examples/selectable-rows/index.js
index 139e803..5d17d37 100644
--- a/examples/selectable-rows/index.js
+++ b/examples/selectable-rows/index.js
@@ -95,7 +95,8 @@ class Example extends React.Component {
         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";
-      }
+      },
+      selectableRowsHeader: false
     };
 
     return (
@@ -105,4 +106,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/serverside-options/index.js b/examples/serverside-options/index.js
index e148d17..791fa6a 100644
--- a/examples/serverside-options/index.js
+++ b/examples/serverside-options/index.js
@@ -65,4 +65,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/serverside-pagination/index.js b/examples/serverside-pagination/index.js
index 9b74cfc..730fc88 100644
--- a/examples/serverside-pagination/index.js
+++ b/examples/serverside-pagination/index.js
@@ -103,4 +103,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/simple-no-toolbar/index.js b/examples/simple-no-toolbar/index.js
index d8f3cbf..1ff5e8b 100644
--- a/examples/simple-no-toolbar/index.js
+++ b/examples/simple-no-toolbar/index.js
@@ -30,4 +30,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/simple/index.js b/examples/simple/index.js
index 349d663..1c4c8fd 100644
--- a/examples/simple/index.js
+++ b/examples/simple/index.js
@@ -30,4 +30,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/text-localization/index.js b/examples/text-localization/index.js
index 7ede47b..c115f29 100644
--- a/examples/text-localization/index.js
+++ b/examples/text-localization/index.js
@@ -69,4 +69,4 @@ class Example extends React.Component {
   }
 }
 
-ReactDOM.render(<Example />, document.getElementById("app-root"));
+export default Example;
diff --git a/examples/themes/index.js b/examples/themes/index.js
index 4174c6e..c187e8a 100644
--- a/examples/themes/index.js
+++ b/examples/themes/index.js
@@ -1,100 +1,95 @@
 import React from 'react';
-import ReactDOM from 'react-dom';
 import MUIDataTable from '../../src/';
-import { MuiThemeProvider, createMuiTheme } from '@material-ui/core';
+import {MuiThemeProvider, createMuiTheme} from '@material-ui/core';
 
 class Example extends React.Component {
-  render() {
-    const columns = [
-      {
-        name: 'Name',
-        options: {
-          filter: true,
-          filterList: ['Franky Miles'],
-          filterOptions: ['a', 'b', 'c', 'Business Analyst'],
-        },
-      },
-      {
-        name: 'Title',
-        options: {
-          filter: true,
-        },
-      },
-      {
-        name: 'Location',
-        options: {
-          filter: false,
-        },
-      },
-      {
-        name: 'Age',
-        options: {
-          filter: true,
-        },
-      },
-      {
-        name: 'Salary',
-        options: {
-          filter: true,
-          sort: false,
-        },
-      },
-    ];
-    const 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],
-    ];
+    render() {
+        const columns = [
+            {
+                name: 'Name',
+                options: {
+                    filter: true,
+                    filterList: ['Franky Miles'],
+                    filterOptions: ['a', 'b', 'c', 'Business Analyst'],
+                },
+            },
+            {
+                name: 'Title',
+                options: {
+                    filter: true,
+                },
+            },
+            {
+                name: 'Location',
+                options: {
+                    filter: false,
+                },
+            },
+            {
+                name: 'Age',
+                options: {
+                    filter: true,
+                },
+            },
+            {
+                name: 'Salary',
+                options: {
+                    filter: true,
+                    sort: false,
+                },
+            },
+        ];
+        const 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],
+        ];
 
-    const options = {
-      filter: true,
-      selectableRows: true,
-      filterType: 'dropdown',
-      responsive: 'stacked',
-      rowsPerPage: 10,
-      page: 1,
-    };
+        const options = {
+            filter: true,
+            selectableRows: true,
+            filterType: 'dropdown',
+            responsive: 'stacked',
+            rowsPerPage: 10,
+            page: 1,
+        };
 
-    return <MUIDataTable title={'ACME Employee list'} data={data} columns={columns} options={options} />;
-  }
-}
-
-const theme = createMuiTheme({
-  palette: { type: 'dark' },
-  typography: { useNextVariants: true },
-});
+        const theme = createMuiTheme({
+            palette: {type: 'dark'},
+            typography: {useNextVariants: true},
+        });
 
-const element = (
-  <MuiThemeProvider theme={theme}>
-    <Example />
-  </MuiThemeProvider>
-);
+        return <MuiThemeProvider theme={theme}>
+            <MUIDataTable title={'ACME Employee list'} data={data} columns={columns} options={options}/>;
+        </MuiThemeProvider>;
+    }
+}
 
-ReactDOM.render(element, document.getElementById('app-root'));
+export default Example;
diff --git a/package-lock.json b/package-lock.json
index bd13a0a..f4ff3ce 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.10.1",
+  "version": "2.10.3",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -5259,6 +5259,12 @@
       "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
       "dev": true
     },
+    "gud": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
+      "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==",
+      "dev": true
+    },
     "handle-thing": {
       "version": "1.2.5",
       "resolved": "http://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz",
@@ -5389,6 +5395,20 @@
       "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
       "dev": true
     },
+    "history": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmjs.org/history/-/history-4.9.0.tgz",
+      "integrity": "sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.1.2",
+        "loose-envify": "^1.2.0",
+        "resolve-pathname": "^2.2.0",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0",
+        "value-equal": "^0.4.0"
+      }
+    },
     "hmac-drbg": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -6914,6 +6934,34 @@
         "dom-walk": "^0.1.0"
       }
     },
+    "mini-create-react-context": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.3.2.tgz",
+      "integrity": "sha512-2v+OeetEyliMt5VHMXsBhABoJ0/M4RCe7fatd/fBy6SMiKazUSEt3gxxypfnk2SHMkdBYvorHRoQxuGoiwbzAw==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.4.0",
+        "gud": "^1.0.0",
+        "tiny-warning": "^1.0.2"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.5.5",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz",
+          "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==",
+          "dev": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.2"
+          }
+        },
+        "regenerator-runtime": {
+          "version": "0.13.3",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+          "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
+          "dev": true
+        }
+      }
+    },
     "minimalistic-assert": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -11624,6 +11672,56 @@
       "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
       "dev": true
     },
+    "react-router": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz",
+      "integrity": "sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.1.2",
+        "history": "^4.9.0",
+        "hoist-non-react-statics": "^3.1.0",
+        "loose-envify": "^1.3.1",
+        "mini-create-react-context": "^0.3.0",
+        "path-to-regexp": "^1.7.0",
+        "prop-types": "^15.6.2",
+        "react-is": "^16.6.0",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+          "dev": true
+        },
+        "path-to-regexp": {
+          "version": "1.7.0",
+          "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+          "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+          "dev": true,
+          "requires": {
+            "isarray": "0.0.1"
+          }
+        }
+      }
+    },
+    "react-router-dom": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.1.tgz",
+      "integrity": "sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.1.2",
+        "history": "^4.9.0",
+        "loose-envify": "^1.3.1",
+        "prop-types": "^15.6.2",
+        "react-router": "5.0.1",
+        "tiny-invariant": "^1.0.2",
+        "tiny-warning": "^1.0.0"
+      }
+    },
     "react-test-renderer": {
       "version": "16.6.3",
       "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.6.3.tgz",
@@ -12317,6 +12415,12 @@
       "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
       "dev": true
     },
+    "resolve-pathname": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
+      "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg==",
+      "dev": true
+    },
     "resolve-url": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -13552,6 +13656,18 @@
       "dev": true,
       "optional": true
     },
+    "tiny-invariant": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
+      "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==",
+      "dev": true
+    },
+    "tiny-warning": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+      "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
+      "dev": true
+    },
     "tmp": {
       "version": "0.0.33",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
@@ -14066,6 +14182,12 @@
         "spdx-expression-parse": "^3.0.0"
       }
     },
+    "value-equal": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
+      "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw==",
+      "dev": true
+    },
     "vary": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
diff --git a/package.json b/package.json
index 231aeb6..92dbdc6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.10.1",
+  "version": "2.10.3",
   "description": "Datatables for React using Material-UI",
   "main": "dist/index.js",
   "files": [
@@ -74,6 +74,7 @@
     "raw-loader": "^0.5.1",
     "react": "^16.2.0",
     "react-dom": "^16.2.0",
+    "react-router-dom": "^5.0.1",
     "rollup": "^0.52.0",
     "rollup-plugin-babel": "^3.0.2",
     "rollup-plugin-commonjs": "^8.2.6",
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index 00e4b92..e156cca 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -2,11 +2,11 @@ import Paper from '@material-ui/core/Paper';
 import { withStyles } from '@material-ui/core/styles';
 import MuiTable from '@material-ui/core/Table';
 import classnames from 'classnames';
+import assignwith from 'lodash.assignwith';
 import cloneDeep from 'lodash.clonedeep';
 import find from 'lodash.find';
 import isUndefined from 'lodash.isundefined';
 import merge from 'lodash.merge';
-import assignwith from 'lodash.assignwith';
 import PropTypes from 'prop-types';
 import React from 'react';
 import TableBody from './components/TableBody';
@@ -148,6 +148,7 @@ class MUIDataTable extends React.Component {
       selectableRows: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['none', 'single', 'multiple'])]),
       selectableRowsOnClick: PropTypes.bool,
       isRowSelectable: PropTypes.func,
+      selectableRowsHeader: PropTypes.bool,
       serverSide: PropTypes.bool,
       onTableChange: PropTypes.func,
       onTableInit: PropTypes.func,
@@ -237,7 +238,7 @@ class MUIDataTable extends React.Component {
 
   componentDidUpdate(prevProps) {
     if (this.props.data !== prevProps.data || this.props.columns !== prevProps.columns) {
-      this.updateOptions(this.props);
+      this.updateOptions(this.options, this.props);
       this.setTableData(this.props, TABLE_LOAD.INITIAL, () => {
         this.setTableAction('propsUpdate');
       });
@@ -254,8 +255,8 @@ class MUIDataTable extends React.Component {
     }
   }
 
-  updateOptions(props) {
-    this.options = assignwith(this.options, props.options, (objValue, srcValue, key) => {
+  updateOptions(options, props) {
+    this.options = assignwith(options, props.options, (objValue, srcValue, key) => {
       // Merge any default options that are objects, as they will be overwritten otherwise
       if (key === 'textLabels' || key === 'downloadOptions') return merge(objValue, srcValue);
       return;
@@ -282,6 +283,7 @@ class MUIDataTable extends React.Component {
     resizableColumns: false,
     selectableRows: 'multiple',
     selectableRowsOnClick: false,
+    selectableRowsHeader: true,
     caseSensitive: false,
     serverSide: false,
     rowHover: true,
@@ -325,9 +327,7 @@ class MUIDataTable extends React.Component {
   mergeDefaultOptions(props) {
     const defaultOptions = this.getDefaultOptions();
 
-    this.options = merge(defaultOptions, props.options);
-
-    this.handleOptionDeprecation(props);
+    this.updateOptions(defaultOptions, this.props);
   }
 
   validateOptions(options) {
@@ -921,6 +921,9 @@ class MUIDataTable extends React.Component {
       }),
       () => {
         this.setTableAction('search');
+        if (this.options.onSearchChange) {
+          this.options.onSearchChange(this.state.searchText);
+        }
       },
     );
   };
@@ -959,6 +962,9 @@ class MUIDataTable extends React.Component {
           case 'multiselect':
             filterList[index] = value === '' ? [] : value;
             break;
+          case 'dropdown':
+            filterList[index] = value;
+            break;
           case 'custom':
             filterList[index] = value;
             break;
@@ -1231,7 +1237,7 @@ class MUIDataTable extends React.Component {
         responsiveClass = classes.responsiveScrollMaxHeight;
         break;
       case 'scrollFullHeight':
-        responsiveClass = classes.responsiveFullHeight;
+        responsiveClass = classes.responsiveScrollFullHeight;
         break;
       case 'stacked':
         responsiveClass = classes.responsiveStacked;
diff --git a/src/components/TableFilter.js b/src/components/TableFilter.js
index 99e3697..b0ed72e 100644
--- a/src/components/TableFilter.js
+++ b/src/components/TableFilter.js
@@ -107,7 +107,7 @@ class TableFilter extends React.Component {
 
   handleDropdownChange = (event, index, column) => {
     const labelFilterAll = this.props.options.textLabels.filter.all;
-    const value = event.target.value === labelFilterAll ? '' : event.target.value;
+    const value = event.target.value === labelFilterAll ? [] : [event.target.value];
     this.props.onFilterUpdate(index, value, column, 'dropdown');
   };
 
@@ -175,7 +175,7 @@ class TableFilter extends React.Component {
           <InputLabel htmlFor={column.name}>{column.label}</InputLabel>
           <Select
             fullWidth
-            value={filterList[index].toString() || textLabels.all}
+            value={filterList[index].length ? filterList[index].toString() : textLabels.all}
             name={column.name}
             onChange={event => this.handleDropdownChange(event, index, column.name)}
             input={<Input name={column.name} id={column.name} />}>
diff --git a/src/components/TableHead.js b/src/components/TableHead.js
index 36e390d..fd1a53c 100644
--- a/src/components/TableHead.js
+++ b/src/components/TableHead.js
@@ -1,11 +1,11 @@
+import { withStyles } from '@material-ui/core/styles';
+import MuiTableHead from '@material-ui/core/TableHead';
+import classNames from 'classnames';
 import React from 'react';
 import { findDOMNode } from 'react-dom';
-import classNames from 'classnames';
-import MuiTableHead from '@material-ui/core/TableHead';
-import TableHeadRow from './TableHeadRow';
 import TableHeadCell from './TableHeadCell';
+import TableHeadRow from './TableHeadRow';
 import TableSelectCell from './TableSelectCell';
-import { withStyles } from '@material-ui/core/styles';
 
 const defaultHeadStyles = theme => ({
   main: {},
@@ -49,6 +49,7 @@ class TableHead extends React.Component {
             expandableOn={options.expandableRows}
             selectableOn={options.selectableRows}
             fixedHeader={options.fixedHeader}
+            selectableRowsHeader={options.selectableRowsHeader}
             isRowSelectable={true}
           />
           {columns.map(
@@ -67,7 +68,8 @@ class TableHead extends React.Component {
                   toggleSort={this.handleToggleColumn}
                   hint={column.hint}
                   print={column.print}
-                  options={options}>
+                  options={options}
+                  column={column}>
                   {column.label}
                 </TableHeadCell>
               )),
diff --git a/src/components/TableHeadCell.js b/src/components/TableHeadCell.js
index e4c0675..906b7cf 100644
--- a/src/components/TableHeadCell.js
+++ b/src/components/TableHeadCell.js
@@ -1,11 +1,11 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
 import TableCell from '@material-ui/core/TableCell';
 import TableSortLabel from '@material-ui/core/TableSortLabel';
 import Tooltip from '@material-ui/core/Tooltip';
-import { withStyles } from '@material-ui/core/styles';
 import HelpIcon from '@material-ui/icons/Help';
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import React from 'react';
 
 const defaultHeadCellStyles = theme => ({
   root: {},
@@ -68,6 +68,8 @@ class TableHeadCell extends React.Component {
     hint: PropTypes.string,
     /** Column displayed in print */
     print: PropTypes.bool.isRequired,
+    /** Optional to be used with `textLabels.body.columnHeaderTooltip` */
+    column: PropTypes.object,
   };
 
   state = {
@@ -89,7 +91,7 @@ class TableHeadCell extends React.Component {
 
   render() {
     const { isSortTooltipOpen, isHintTooltipOpen } = this.state;
-    const { children, classes, options, sortDirection, sort, hint, print } = this.props;
+    const { children, classes, options, sortDirection, sort, hint, print, column } = this.props;
     const sortActive = sortDirection !== 'none' && sortDirection !== undefined ? true : false;
     const ariaSortDirection = sortDirection === 'none' ? false : sortDirection;
 
@@ -110,7 +112,11 @@ class TableHeadCell extends React.Component {
       <TableCell className={cellClass} scope={'col'} sortDirection={ariaSortDirection}>
         {options.sort && sort ? (
           <Tooltip
-            title={options.textLabels.body.toolTip}
+            title={
+              options.textLabels.body.columnHeaderTooltip
+                ? options.textLabels.body.columnHeaderTooltip(column)
+                : options.textLabels.body.toolTip
+            }
             placement={'bottom-start'}
             classes={{
               tooltip: classes.tooltip,
diff --git a/src/components/TableSearch.js b/src/components/TableSearch.js
index 1d88da3..7d20d2e 100644
--- a/src/components/TableSearch.js
+++ b/src/components/TableSearch.js
@@ -28,12 +28,6 @@ const defaultSearchStyles = theme => ({
 
 class TableSearch extends React.Component {
   handleTextChange = event => {
-    const { onSearchChange } = this.props.options;
-
-    if (onSearchChange) {
-      onSearchChange(event.target.value);
-    }
-
     this.props.onSearch(event.target.value);
   };
 
diff --git a/src/components/TableSelectCell.js b/src/components/TableSelectCell.js
index ac03c96..d044dc6 100644
--- a/src/components/TableSelectCell.js
+++ b/src/components/TableSelectCell.js
@@ -67,6 +67,7 @@ class TableSelectCell extends React.Component {
       isRowExpanded,
       onExpand,
       isRowSelectable,
+      selectableRowsHeader,
       ...otherProps
     } = this.props;
 
@@ -85,7 +86,7 @@ class TableSelectCell extends React.Component {
     });
 
     const renderCheckBox = () => {
-      if (isHeaderCell && selectableOn !== 'multiple') {
+      if (isHeaderCell && (selectableOn !== 'multiple' || selectableRowsHeader === false)) {
         // only display the header checkbox for multiple selection.
         return null;
       }
diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js
index 6711eca..1f264de 100644
--- a/src/components/TableToolbar.js
+++ b/src/components/TableToolbar.js
@@ -139,6 +139,7 @@ class TableToolbar extends React.Component {
         nextVal = true;
       } else {
         const { onSearchClose } = this.props.options;
+        this.props.setTableAction('onSearchClose');
         if (onSearchClose) onSearchClose();
         nextVal = false;
       }
@@ -153,14 +154,15 @@ class TableToolbar extends React.Component {
   };
 
   showSearch = () => {
-    !!this.props.options.onSearchOpen && this.props.options.onSearchOpen();
     this.props.setTableAction('onSearchOpen');
+    !!this.props.options.onSearchOpen && this.props.options.onSearchOpen();
     return true;
   };
 
   hideSearch = () => {
     const { onSearchClose } = this.props.options;
 
+    this.props.setTableAction('onSearchClose');
     if (onSearchClose) onSearchClose();
     this.props.searchTextUpdate(null);
 
diff --git a/src/utils.js b/src/utils.js
index ed93628..94d3f69 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -17,10 +17,10 @@ function getCollatorComparator() {
 
 function sortCompare(order) {
   return (a, b) => {
-    if (a.data === null) a.data = '';
-    if (b.data === null) b.data = '';
+    var aData = (a.data === null || typeof a.data === 'undefined') ? '' : a.data;
+    var bData = (b.data === null || typeof b.data === 'undefined') ? '' : b.data;
     return (
-      (typeof a.data.localeCompare === 'function' ? a.data.localeCompare(b.data) : a.data - b.data) *
+      (typeof aData.localeCompare === 'function' ? aData.localeCompare(bData) : aData - bData) *
       (order === 'asc' ? 1 : -1)
     );
   };
diff --git a/webpack.config.js b/webpack.config.js
index e507b2a..bc69e1d 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,7 +3,7 @@ const webpack = require('webpack');
 
 module.exports = {
   entry: {
-    app: "./examples/customize-search/index.js"
+    app: "./examples/Router/index.js"
   },
   stats: "verbose",
   context: __dirname,


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 6d157be..c6265ec 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -496,6 +496,16 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(state.rowsPerPageOptions, [5, 10, 15]);
   });
 
+  it('should allow empty array rowsPerPageOptions when provided in options', () => {
+    const options = {
+      rowsPerPageOptions: [],
+    };
+
+    const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />);
+    const state = shallowWrapper.dive().state();
+    assert.deepEqual(state.rowsPerPageOptions, []);
+  });
+
   it('should render pagination when enabled in options', () => {
     const options = {
       pagination: true,
@@ -561,7 +571,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', 'Name', 'dropdown');
+    instance.filterUpdate(0, ['Joe James'], 'Name', 'dropdown');
     table.update();
 
     const state = table.state();
@@ -628,17 +638,17 @@ describe('<MUIDataTable />', function() {
     assert.strictEqual(actualResult.prop('label'), 'Name: Joe James');
   });
 
-  it('should remove entry from filterList when calling filterUpdate method with type=dropdown and same arguments a second time', () => {
+  it('should remove entry from filterList when calling filterUpdate method with type=dropdown and an empty array', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, 'Joe James', 'Name', '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', 'Name', 'dropdown');
+    instance.filterUpdate(0, [], 'Name', 'dropdown');
     table.update();
 
     state = table.state();
@@ -679,7 +689,7 @@ describe('<MUIDataTable />', function() {
     assert.strictEqual(changedColumn, 'Name');
 
     // test dropdown filter
-    instance.filterUpdate(0, 'Test Corp', 'Company', 'dropdown');
+    instance.filterUpdate(0, ['Test Corp'], 'Company', 'dropdown');
     table.update();
     assert.strictEqual(changedColumn, 'Company');
 
@@ -729,7 +739,7 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(JSON.stringify(state.displayData), expectedResult);
   });
 
-  it('should sort provided column in descending order when calling toggleSortColum method for the first time', () => {
+  it('should sort provided column with defined data, in descending order when calling toggleSortColum method for the first time', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
@@ -747,6 +757,24 @@ describe('<MUIDataTable />', function() {
     assert.deepEqual(JSON.stringify(state.displayData), expectedResult);
   });
 
+  it('should sort provided column with undefined data as though it were an empty string, in descending order when calling toggleSortColum method for the first time', () => {
+    const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
+    const instance = shallowWrapper.instance();
+
+    instance.toggleSortColumn(4);
+    shallowWrapper.update();
+    const state = shallowWrapper.state();
+
+    const expectedResult = JSON.stringify([
+      { data: ['James, Joe', 'Test Corp', renderCities('Yonkers', { rowIndex: 0 }), 'NY', undefined], dataIndex: 0 },
+      { data: ['Walsh, John', 'Test Corp', renderCities('Hartford', { rowIndex: 1 }), null, undefined], dataIndex: 1 },
+      { data: ['Herm, Bob', 'Test Corp', renderCities('Tampa', { rowIndex: 2 }), 'FL', undefined], dataIndex: 2 },
+      { data: ['Houston, James', 'Test Corp', renderCities('Dallas', { rowIndex: 3 }), 'TX', undefined], dataIndex: 3 },
+    ]);
+
+    assert.deepEqual(JSON.stringify(state.displayData), expectedResult);
+  });
+
   it('should sort provided column in ascending order when calling toggleSortColum method twice', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
@@ -1215,7 +1243,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', 'Name', 'dropdown');
+    instance.filterUpdate(0, ['Joe James'], 'Name', 'dropdown');
     table.update();
     const state = table.state();
 
@@ -1337,7 +1365,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', 'Name', 'dropdown');
+      instance.filterUpdate(1, ['b'], 'Name', 'dropdown');
       table.update();
       const { displayData } = table.state();
 
@@ -1385,7 +1413,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', 'Name', 'dropdown');
+      instance.filterUpdate(1, ['b'], 'Name', 'dropdown');
       table.update();
       const { displayData } = table.state();