Repository: gregnb/mui-datatables
Pull request #329 (2018-12-24 20:10:00)

Project refactoring
This is phase 1 for refactoring of this project.

TODO:
- fix unit tests


Patch:
diff --git a/README.md b/README.md
index 31af8af..7dbf876 100644
--- a/README.md
+++ b/README.md
@@ -137,6 +137,8 @@ The component accepts the following props:
 |**`pagination`**|boolean|true|Enable/disable pagination
 |**`selectableRows`**|boolean|true|Enable/disable row selection
 |**`resizableColumns`**|boolean|false|Enable/disable resizable columns
+|**`expandableRows`**|boolean|false|Enable/disable expandable 
+|**`renderExpandableRow `**|function||Render expandable row. `function(rowData, rowMeta) => React Component`
 |**`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`
diff --git a/docs/icons/GitHub.js b/docs/icons/GitHub.js
index a40a858..0c1dd01 100644
--- a/docs/icons/GitHub.js
+++ b/docs/icons/GitHub.js
@@ -1,7 +1,7 @@
 /* eslint-disable max-len */
 
-import React from "react";
-import SvgIcon from "@material-ui/core/SvgIcon";
+import React from 'react';
+import SvgIcon from '@material-ui/core/SvgIcon';
 
 function GitHub(props) {
   return (
@@ -11,6 +11,6 @@ function GitHub(props) {
   );
 }
 
-GitHub.muiName = "SvgIcon";
+GitHub.muiName = 'SvgIcon';
 
 export default GitHub;
diff --git a/docs/pages/_document.js b/docs/pages/_document.js
index 6eb8bea..7303a53 100644
--- a/docs/pages/_document.js
+++ b/docs/pages/_document.js
@@ -1,7 +1,7 @@
-import React from "react";
-import Document, { Head, Main, NextScript } from "next/document";
-import JssProvider from "react-jss/lib/JssProvider";
-import getPageContext from "../utils/getPageContext";
+import React from 'react';
+import Document, { Head, Main, NextScript } from 'next/document';
+import JssProvider from 'react-jss/lib/JssProvider';
+import getPageContext from '../utils/getPageContext';
 
 class MyDocument extends Document {
   render() {
@@ -15,12 +15,12 @@ class MyDocument extends Document {
 
           <meta
             name="description"
-            content={"MUI-Datatables is a data tables component built for React Material-UI V1"}
+            content={'MUI-Datatables is a data tables component built for React Material-UI V1'}
           />
           <meta
             name="keywords"
             content={
-              "material-ui, data tables, datatables, material-ui, material-ui-datables, react tables, react data tables"
+              'material-ui, data tables, datatables, material-ui, material-ui-datables, react tables, react data tables'
             }
           />
           <meta name="robots" content="index,follow,noodp" />
diff --git a/docs/pages/index.js b/docs/pages/index.js
index 70800a5..d8a566b 100644
--- a/docs/pages/index.js
+++ b/docs/pages/index.js
@@ -1,27 +1,27 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Typography from "@material-ui/core/Typography";
-import IconButton from "@material-ui/core/IconButton";
-import Tooltip from "@material-ui/core/Tooltip";
-import DownloadIcon from "@material-ui/icons/CloudDownload";
-import BuildIcon from "@material-ui/icons/Build"; // eslint-disable-line import/no-unresolved
-import CodeSnippet from "../utils/CodeSnippet";
-import Layout from "../utils/layout";
-import withRoot from "../utils/withRoot";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import Typography from '@material-ui/core/Typography';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+import DownloadIcon from '@material-ui/icons/CloudDownload';
+import BuildIcon from '@material-ui/icons/Build'; // eslint-disable-line import/no-unresolved
+import CodeSnippet from '../utils/CodeSnippet';
+import Layout from '../utils/layout';
+import withRoot from '../utils/withRoot';
+import { withStyles } from '@material-ui/core/styles';
 
 const styles = theme => ({
   stepIcon: {
-    fontSize: "30px",
+    fontSize: '30px',
     marginRight: theme.spacing.unit * 2,
   },
   stepWrapper: {
     marginTop: theme.spacing.unit * 4,
-    display: "flex",
-    alignItems: "center",
+    display: 'flex',
+    alignItems: 'center',
   },
   mainImage: {
-    maxWidth: "100%",
+    maxWidth: '100%',
   },
 });
 
@@ -49,14 +49,14 @@ class Homepage extends React.Component {
             <DownloadIcon className={classes.stepIcon} />
             <Typography variant="h6">Installation</Typography>
           </div>
-          <CodeSnippet withMargin language={"bash"} text={`npm install mui-datatables --save`} />
+          <CodeSnippet withMargin language={'bash'} text={`npm install mui-datatables --save`} />
 
           <div className={classes.stepWrapper}>
             <BuildIcon className={classes.stepIcon} />
             <Typography variant="h6">Usage</Typography>
           </div>
           <CodeSnippet
-            language={"jsx"}
+            language={'jsx'}
             text={`import MUIDataTable from "mui-datatables";
 
 const columns = ["Name", "Company", "City", "State"];
diff --git a/docs/utils/CodeSnippet.js b/docs/utils/CodeSnippet.js
index 61011aa..154436c 100644
--- a/docs/utils/CodeSnippet.js
+++ b/docs/utils/CodeSnippet.js
@@ -1,11 +1,11 @@
-import React from "react";
-import PropTypes from "prop-types";
-import classnames from "classnames";
-import prism from "prismjs";
-import "prismjs/components/prism-jsx";
-import "prismjs/components/prism-bash";
-import Paper from "@material-ui/core/Paper";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import classnames from 'classnames';
+import prism from 'prismjs';
+import 'prismjs/components/prism-jsx';
+import 'prismjs/components/prism-bash';
+import Paper from '@material-ui/core/Paper';
+import { withStyles } from '@material-ui/core/styles';
 
 const styles = theme => ({});
 
@@ -17,7 +17,7 @@ class CodeSnippet extends React.Component {
   };
 
   static defaultProps = {
-    language: "jsx",
+    language: 'jsx',
   };
 
   render() {
diff --git a/docs/utils/Menu.js b/docs/utils/Menu.js
index b59d5c1..9c40b36 100644
--- a/docs/utils/Menu.js
+++ b/docs/utils/Menu.js
@@ -1,11 +1,11 @@
-import React from "react";
-import PropTypes from "prop-types";
-import { withStyles } from "@material-ui/core";
-import Drawer from "@material-ui/core/Drawer";
-import List from "@material-ui/core/List";
-import ListItem from "@material-ui/core/ListItem";
-import ListItemText from "@material-ui/core/ListItemText";
-import ListSubheader from "@material-ui/core/ListSubheader";
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core';
+import Drawer from '@material-ui/core/Drawer';
+import List from '@material-ui/core/List';
+import ListItem from '@material-ui/core/ListItem';
+import ListItemText from '@material-ui/core/ListItemText';
+import ListSubheader from '@material-ui/core/ListSubheader';
 
 const styles = theme => ({
   list: {
@@ -17,18 +17,18 @@ const styles = theme => ({
 });
 
 const sandboxes = [
-  { name: "Custom Component", href: "https://codesandbox.io/embed/xrvrzryjvp?autoresize=1&hidenavigation=1" },
-  { name: "Customize Columns", href: "https://codesandbox.io/embed/xowj5oj8w?autoresize=1&hidenavigation=1" },
-  { name: "Customize Footer", href: "https://codesandbox.io/embed/5z0w0w9jyk?autoresize=1&hidenavigation=1" },
-  { name: "Customize Styling", href: "https://codesandbox.io/embed/0ylq1lqwp0?autoresize=1&hidenavigation=1" },
-  { name: "Customize Toolbar", href: "https://codesandbox.io/embed/wy2rl1nyzl?autoresize=1&hidenavigation=1" },
-  { name: "Customize ToolbarSelect", href: "https://codesandbox.io/embed/545ym5ov6p?autoresize=1&hidenavigation=1" },
-  { name: "Resizable Columns", href: "https://codesandbox.io/embed/q8w3230qpj?autoresize=1&hidenavigation=1" },
+  { name: 'Custom Component', href: 'https://codesandbox.io/embed/xrvrzryjvp?autoresize=1&hidenavigation=1' },
+  { name: 'Customize Columns', href: 'https://codesandbox.io/embed/xowj5oj8w?autoresize=1&hidenavigation=1' },
+  { name: 'Customize Footer', href: 'https://codesandbox.io/embed/5z0w0w9jyk?autoresize=1&hidenavigation=1' },
+  { name: 'Customize Styling', href: 'https://codesandbox.io/embed/0ylq1lqwp0?autoresize=1&hidenavigation=1' },
+  { name: 'Customize Toolbar', href: 'https://codesandbox.io/embed/wy2rl1nyzl?autoresize=1&hidenavigation=1' },
+  { name: 'Customize ToolbarSelect', href: 'https://codesandbox.io/embed/545ym5ov6p?autoresize=1&hidenavigation=1' },
+  { name: 'Resizable Columns', href: 'https://codesandbox.io/embed/q8w3230qpj?autoresize=1&hidenavigation=1' },
 ];
 
 const SandboxItem = props => (
   <ListItem button>
-    <ListItemText onClick={() => window.open(props.href, "_blank")} primary={props.name} />
+    <ListItemText onClick={() => window.open(props.href, '_blank')} primary={props.name} />
   </ListItem>
 );
 
diff --git a/docs/utils/getPageContext.js b/docs/utils/getPageContext.js
index 9bb5e93..8ccf31e 100644
--- a/docs/utils/getPageContext.js
+++ b/docs/utils/getPageContext.js
@@ -1,9 +1,9 @@
 /* eslint-disable no-underscore-dangle */
 
-import { SheetsRegistry } from "jss";
-import { createMuiTheme, createGenerateClassName } from "@material-ui/core/styles";
-import purple from "@material-ui/core/colors/purple";
-import green from "@material-ui/core/colors/green";
+import { SheetsRegistry } from 'jss';
+import { createMuiTheme, createGenerateClassName } from '@material-ui/core/styles';
+import purple from '@material-ui/core/colors/purple';
+import green from '@material-ui/core/colors/green';
 
 // A theme with custom primary and secondary color.
 // It's optional.
diff --git a/docs/utils/layout.js b/docs/utils/layout.js
index 05f1885..fdfc9f0 100644
--- a/docs/utils/layout.js
+++ b/docs/utils/layout.js
@@ -1,47 +1,47 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Head from "next/head";
-import Typography from "@material-ui/core/Typography";
-import AppBar from "@material-ui/core/AppBar";
-import Toolbar from "@material-ui/core/Toolbar";
-import IconButton from "@material-ui/core/IconButton";
-import MenuIcon from "@material-ui/icons/Menu";
-import Tooltip from "@material-ui/core/Tooltip";
-import GitHub from "../icons/GitHub";
-import withRoot from "../utils/withRoot";
-import { withStyles } from "@material-ui/core/styles";
-import Menu from "./Menu";
+import React from 'react';
+import PropTypes from 'prop-types';
+import Head from 'next/head';
+import Typography from '@material-ui/core/Typography';
+import AppBar from '@material-ui/core/AppBar';
+import Toolbar from '@material-ui/core/Toolbar';
+import IconButton from '@material-ui/core/IconButton';
+import MenuIcon from '@material-ui/icons/Menu';
+import Tooltip from '@material-ui/core/Tooltip';
+import GitHub from '../icons/GitHub';
+import withRoot from '../utils/withRoot';
+import { withStyles } from '@material-ui/core/styles';
+import Menu from './Menu';
 
 /* eslint-disable import/no-webpack-loader-syntax  */
-import lightTheme from "!raw-loader!prismjs/themes/prism.css";
+import lightTheme from '!raw-loader!prismjs/themes/prism.css';
 
 const styles = theme => ({
   appBar: {
-    backgroundColor: "#23232f",
+    backgroundColor: '#23232f',
   },
   toolBar: {
-    justifyContent: "space-between",
+    justifyContent: 'space-between',
   },
   logo: {
-    display: "block",
-    height: "56px",
-    position: "relative",
-    top: "5px",
+    display: 'block',
+    height: '56px',
+    position: 'relative',
+    top: '5px',
   },
   wrapper: {
-    flex: "1 0 100%",
+    flex: '1 0 100%',
   },
   content: {
-    flex: "1 0 100%",
-    margin: "0 auto",
-    padding: "16px 16px 0px 16px",
-    marginTop: "64px",
-    minHeight: "600px",
-    maxWidth: "960px",
+    flex: '1 0 100%',
+    margin: '0 auto',
+    padding: '16px 16px 0px 16px',
+    marginTop: '64px',
+    minHeight: '600px',
+    maxWidth: '960px',
   },
   footer: {
-    flex: "1 0 100%",
-    marginTop: "32px",
+    flex: '1 0 100%',
+    marginTop: '32px',
   },
 });
 
@@ -51,8 +51,8 @@ class Layout extends React.Component {
   };
 
   componentDidMount() {
-    const styleNode = document.createElement("style");
-    styleNode.setAttribute("data-prism", "true");
+    const styleNode = document.createElement('style');
+    styleNode.setAttribute('data-prism', 'true');
     if (document.head) {
       document.head.appendChild(styleNode);
     }
diff --git a/docs/utils/withRoot.js b/docs/utils/withRoot.js
index bdb83b9..d82df6c 100644
--- a/docs/utils/withRoot.js
+++ b/docs/utils/withRoot.js
@@ -1,7 +1,7 @@
-import React from "react";
-import PropTypes from "prop-types";
-import { MuiThemeProvider } from "@material-ui/core/styles";
-import getPageContext from "./getPageContext";
+import React from 'react';
+import PropTypes from 'prop-types';
+import { MuiThemeProvider } from '@material-ui/core/styles';
+import getPageContext from './getPageContext';
 
 function withRoot(Component) {
   class WithRoot extends React.Component {
@@ -11,7 +11,7 @@ function withRoot(Component) {
 
     componentDidMount() {
       // Remove the server-side injected CSS.
-      const jssStyles = document.querySelector("#jss-server-side");
+      const jssStyles = document.querySelector('#jss-server-side');
       if (jssStyles && jssStyles.parentNode) {
         jssStyles.parentNode.removeChild(jssStyles);
       }
diff --git a/examples/expandable-rows/index.js b/examples/expandable-rows/index.js
new file mode 100644
index 0000000..7fa6cee
--- /dev/null
+++ b/examples/expandable-rows/index.js
@@ -0,0 +1,102 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import MUIDataTable from "../../src/";
+import TableRow from "@material-ui/core/TableRow";
+import TableCell from "@material-ui/core/TableCell";
+
+class Example extends React.Component {
+
+  render() {
+
+    const columns = [
+      {
+        name: "Name",
+        options: {
+          filter: true,
+        }
+      },      
+      {
+        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, "$100,000"],
+      ["Aiden Lloyd", "Business Consultant", "Dallas",  55, "$200,000"],
+      ["Jaden Collins", "Attorney", "Santa Ana", 27, "$500,000"],
+      ["Franky Rees", "Business Analyst", "St. Petersburg", 22, "$50,000"],
+      ["Aaren Rose", "Business Consultant", "Toledo", 28, "$75,000"],
+      ["Blake Duncan", "Business Management Analyst", "San Diego", 65, "$94,000"],
+      ["Frankie Parry", "Agency Legal Counsel", "Jacksonville", 71, "$210,000"],
+      ["Lane Wilson", "Commercial Specialist", "Omaha", 19, "$65,000"],
+      ["Robin Duncan", "Business Analyst", "Los Angeles", 20, "$77,000"],
+      ["Mel Brooks", "Business Consultant", "Oklahoma City", 37, "$135,000"],
+      ["Harper White", "Attorney", "Pittsburgh", 52, "$420,000"],
+      ["Kris Humphrey", "Agency Legal Counsel", "Laredo", 30, "$150,000"],
+      ["Frankie Long", "Industrial Analyst", "Austin", 31, "$170,000"],
+      ["Brynn Robbins", "Business Analyst", "Norfolk", 22, "$90,000"],
+      ["Justice Mann", "Business Consultant", "Chicago", 24, "$133,000"],
+      ["Addison Navarro", "Business Management Analyst", "New York", 50, "$295,000"],
+      ["Jesse Welch", "Agency Legal Counsel", "Seattle", 28, "$200,000"],
+      ["Eli Mejia", "Commercial Specialist", "Long Beach", 65, "$400,000"],
+      ["Gene Leblanc", "Industrial Analyst", "Hartford", 34, "$110,000"],
+      ["Danny Leon", "Computer Scientist", "Newark", 60, "$220,000"],
+      ["Lane Lee", "Corporate Counselor", "Cincinnati", 52, "$180,000"],
+      ["Jesse Hall", "Business Analyst", "Baltimore", 44, "$99,000"],
+      ["Danni Hudson", "Agency Legal Counsel", "Tampa", 37, "$90,000"],
+      ["Terry Macdonald", "Commercial Specialist", "Miami", 39, "$140,000"],
+      ["Justice Mccarthy", "Attorney", "Tucson", 26, "$330,000"],
+      ["Silver Carey", "Computer Scientist", "Memphis", 47, "$250,000" ],
+      ["Franky Miles", "Industrial Analyst", "Buffalo", 49, "$190,000"],
+      ["Glen Nixon", "Corporate Counselor", "Arlington", 44, "$80,000"],
+      ["Gabby Strickland", "Business Process Consultant", "Scottsdale", 26, "$45,000"],
+      ["Mason Ray", "Computer Scientist", "San Francisco", 39, "$142,000"]
+    ];
+
+    const options = {
+      filter: true,
+      filterType: 'dropdown',
+      responsive: 'scroll',
+      expandableRows: true,
+      renderExpandableRow: (rowData, rowMeta) => {
+        return (
+          <TableRow>
+            <TableCell colSpan={rowData.length}>
+              Custom expandable row option. Data: {JSON.stringify(rowData)}
+            </TableCell>
+          </TableRow>
+        );
+      }
+    };
+
+    return (
+      <MUIDataTable title={"ACME Employee list"} data={data} columns={columns} options={options} />
+    );
+
+  }
+}
+
+ReactDOM.render(<Example />, document.getElementById("app-root"));
diff --git a/prettier.config.js b/prettier.config.js
index 5b7d82a..93f6ee6 100644
--- a/prettier.config.js
+++ b/prettier.config.js
@@ -1,6 +1,6 @@
 module.exports = {
   printWidth: 120,
-  singleQuote: false,
+  singleQuote: true,
   trailingComma: 'all',
   bracketSpacing: true,
   jsxBracketSameLine: true,
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index d756b1d..b0dbb76 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -1,40 +1,43 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Paper from "@material-ui/core/Paper";
-import Table from "@material-ui/core/Table";
-import MUIDataTableToolbar from "./MUIDataTableToolbar";
-import MUIDataTableToolbarSelect from "./MUIDataTableToolbarSelect";
-import MUIDataTableFilterList from "./MUIDataTableFilterList";
-import MUIDataTableBody from "./MUIDataTableBody";
-import MUIDataTableResize from "./MUIDataTableResize";
-import MUIDataTableHead from "./MUIDataTableHead";
-import MUIDataTablePagination from "./MUIDataTablePagination";
-import cloneDeep from "lodash.clonedeep";
-import merge from "lodash.merge";
-import isEqual from "lodash.isequal";
-import textLabels from "./textLabels";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import Paper from '@material-ui/core/Paper';
+import MuiTable from '@material-ui/core/Table';
+import TableToolbar from './components/TableToolbar';
+import TableToolbarSelect from './components/TableToolbarSelect';
+import TableFilterList from './components/TableFilterList';
+import TableBody from './components/TableBody';
+import TableResize from './components/TableResize';
+import TableHead from './components/TableHead';
+import TableFooter from './components/TableFooter';
+import TablePagination from './components/TablePagination';
+import cloneDeep from 'lodash.clonedeep';
+import merge from 'lodash.merge';
+import isEqual from 'lodash.isequal';
+import textLabels from './textLabels';
+import { withStyles } from '@material-ui/core/styles';
+import { buildMap, getCollatorComparator, sortCompare } from './utils';
 
 const defaultTableStyles = {
   root: {},
   responsiveScroll: {
-    overflow: "auto",
-    height: "100%",
-    maxHeight: "499px",
+    overflowX: 'auto',
+    overflow: 'auto',
+    height: '100%',
+    maxHeight: '499px',
   },
   caption: {
-    position: "absolute",
-    left: "-3000px",
+    position: 'absolute',
+    left: '-3000px',
   },
   liveAnnounce: {
-    border: "0",
-    clip: "rect(0 0 0 0)",
-    height: "1px",
-    margin: "-1px",
-    overflow: "hidden",
-    padding: "0",
-    position: "absolute",
-    width: "1px",
+    border: '0',
+    clip: 'rect(0 0 0 0)',
+    height: '1px',
+    margin: '-1px',
+    overflow: 'hidden',
+    padding: '0',
+    position: 'absolute',
+    width: '1px',
   },
 };
 
@@ -68,10 +71,12 @@ class MUIDataTable extends React.Component {
     ).isRequired,
     /** Options used to describe table */
     options: PropTypes.shape({
-      responsive: PropTypes.oneOf(["stacked", "scroll"]),
-      filterType: PropTypes.oneOf(["dropdown", "checkbox", "multiselect"]),
+      responsive: PropTypes.oneOf(['stacked', 'scroll']),
+      filterType: PropTypes.oneOf(['dropdown', 'checkbox', 'multiselect']),
       textLabels: PropTypes.object,
       pagination: PropTypes.bool,
+      expandableRows: PropTypes.bool,
+      renderExpandableRow: PropTypes.func,
       customToolbar: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
       customToolbarSelect: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
       customFooter: PropTypes.oneOfType([PropTypes.func, PropTypes.element]),
@@ -106,7 +111,7 @@ class MUIDataTable extends React.Component {
   };
 
   static defaultProps = {
-    title: "",
+    title: '',
     options: {},
     data: [],
     columns: [],
@@ -126,6 +131,10 @@ class MUIDataTable extends React.Component {
       data: [],
       lookup: {},
     },
+    expandedRows: {
+      data: [],
+      lookup: {},
+    },
     showResponsive: false,
     searchText: null,
   };
@@ -158,26 +167,16 @@ class MUIDataTable extends React.Component {
     this.setTableData(props, TABLE_LOAD.INITIAL);
   }
 
-  static fallbackComparator = (a, b) => a.localeCompare(b);
-
-  static getCollatzComparator = () => {
-    if (!!Intl) {
-      const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" });
-      return collator.compare;
-    }
-
-    return MUIDataTable.fallbackComparator;
-  };
-
   /*
    * React currently does not support deep merge for defaultProps. Objects are overwritten
    */
   getDefaultOptions(props) {
     const defaultOptions = {
-      responsive: "stacked",
-      filterType: "checkbox",
+      responsive: 'stacked',
+      filterType: 'checkbox',
       pagination: true,
       textLabels,
+      expandableRows: false,
       resizableColumns: false,
       selectableRows: true,
       caseSensitive: false,
@@ -194,8 +193,8 @@ class MUIDataTable extends React.Component {
       viewColumns: true,
       download: true,
       downloadOptions: {
-        filename: "tableDownload.csv",
-        separator: ",",
+        filename: 'tableDownload.csv',
+        separator: ',',
       },
     };
 
@@ -204,18 +203,21 @@ class MUIDataTable extends React.Component {
 
   validateOptions(options) {
     if (options.serverSide && options.onTableChange === undefined) {
-      throw Error("onTableChange callback must be provided when using serverSide option");
+      throw Error('onTableChange callback must be provided when using serverSide option');
+    }
+    if (options.expandableRows && options.renderExpandableRow === undefined) {
+      throw Error('renderExpandableRow must be provided when using expandableRows option');
     }
   }
 
   setTableAction = action => {
-    if (typeof this.options.onTableChange === "function") {
+    if (typeof this.options.onTableChange === 'function') {
       this.options.onTableChange(action, this.state);
     }
   };
 
   setTableOptions(props) {
-    const optionNames = ["rowsPerPage", "page", "rowsSelected", "filterList", "rowsPerPageOptions"];
+    const optionNames = ['rowsPerPage', 'page', 'rowsSelected', 'filterList', 'rowsPerPageOptions'];
     const optState = optionNames.reduce((acc, cur) => {
       if (this.options[cur] !== undefined) {
         acc[cur] = this.options[cur];
@@ -231,9 +233,13 @@ class MUIDataTable extends React.Component {
     this.headCellRefs[index] = el;
   };
 
+  getTableContentRef = () => {
+    return this.tableContent.current;
+  };
+
   rawColumns = cols => {
     return cols.map(item => {
-      if (typeof item !== "object") return item;
+      if (typeof item !== 'object') return item;
 
       const { options, ...otherOpts } = item;
       return otherOpts;
@@ -256,14 +262,14 @@ class MUIDataTable extends React.Component {
 
     newColumns.forEach((column, colIndex) => {
       let columnOptions = {
-        display: "true",
+        display: 'true',
         filter: true,
         sort: true,
         download: true,
         sortDirection: null,
       };
 
-      if (typeof column === "object") {
+      if (typeof column === 'object') {
         if (column.options && column.options.display !== undefined) {
           column.options.display = column.options.display.toString();
         }
@@ -298,20 +304,20 @@ class MUIDataTable extends React.Component {
       for (let rowIndex = 0; rowIndex < data.length; rowIndex++) {
         let value = status === TABLE_LOAD.INITIAL ? data[rowIndex][colIndex] : data[rowIndex].data[colIndex];
 
-        if (typeof tableData[rowIndex] === "undefined") {
+        if (typeof tableData[rowIndex] === 'undefined') {
           tableData.push({
             index: status === TABLE_LOAD.INITIAL ? rowIndex : data[rowIndex].index,
             data: status === TABLE_LOAD.INITIAL ? data[rowIndex] : data[rowIndex].data,
           });
         }
 
-        if (typeof column.customBodyRender === "function") {
+        if (typeof column.customBodyRender === 'function') {
           const tableMeta = this.getTableMeta(rowIndex, colIndex, value, [], column, this.state);
           const funcResult = column.customBodyRender(value, tableMeta);
 
           if (React.isValidElement(funcResult) && funcResult.props.value) {
             value = funcResult.props.value;
-          } else if (typeof funcResult === "string") {
+          } else if (typeof funcResult === 'string') {
             value = funcResult;
           }
         }
@@ -320,20 +326,20 @@ class MUIDataTable extends React.Component {
       }
 
       if (this.options.sortFilterList) {
-        const comparator = MUIDataTable.getCollatzComparator();
+        const comparator = getCollatorComparator();
         filterData[colIndex].sort(comparator);
       }
 
       if (column.sortDirection !== null) {
         sortIndex = colIndex;
-        sortDirection = column.sortDirection === "asc" ? "desc" : "asc";
+        sortDirection = column.sortDirection === 'asc' ? 'desc' : 'asc';
       }
     });
 
     if (options.filterList) filterList = options.filterList;
 
     if (filterList.length !== columns.length) {
-      throw new Error("Provided options.filterList does not match the column length");
+      throw new Error('Provided options.filterList does not match the column length');
     }
 
     let selectedRowsData = {
@@ -397,7 +403,7 @@ class MUIDataTable extends React.Component {
 
         /* drill down to get the value of a cell */
         columnValue =
-          typeof funcResult === "string"
+          typeof funcResult === 'string'
             ? funcResult
             : funcResult.props && funcResult.props.value
             ? funcResult.props.value
@@ -410,7 +416,7 @@ class MUIDataTable extends React.Component {
         isFiltered = true;
       }
 
-      const columnVal = columnValue === null ? "" : columnValue.toString();
+      const columnVal = columnValue === null ? '' : columnValue.toString();
 
       if (searchText) {
         let searchNeedle = searchText.toString();
@@ -442,7 +448,7 @@ class MUIDataTable extends React.Component {
       const filterValue =
         React.isValidElement(funcResult) && funcResult.props.value
           ? funcResult.props.value
-          : prevState["data"][row][index];
+          : prevState['data'][row][index];
 
       const prevFilterIndex = filterData[index].indexOf(filterValue);
       filterData[index].splice(prevFilterIndex, 1, filterValue);
@@ -450,7 +456,7 @@ class MUIDataTable extends React.Component {
       changedData[row].data[index] = value;
 
       if (this.options.sortFilterList) {
-        const comparator = MUIDataTable.getCollatzComparator();
+        const comparator = getCollatorComparator();
         filterData[index].sort(comparator);
       }
 
@@ -497,17 +503,17 @@ class MUIDataTable extends React.Component {
     this.setState(
       prevState => {
         const columns = cloneDeep(prevState.columns);
-        columns[index].display = columns[index].display === "true" ? "false" : "true";
+        columns[index].display = columns[index].display === 'true' ? 'false' : 'true';
         return {
           columns: columns,
         };
       },
       () => {
-        this.setTableAction("columnViewChange");
+        this.setTableAction('columnViewChange');
         if (this.options.onColumnViewChange) {
           this.options.onColumnViewChange(
             this.state.columns[index].name,
-            this.state.columns[index].display === "true" ? "add" : "remove",
+            this.state.columns[index].display === 'true' ? 'add' : 'remove',
           );
         }
       },
@@ -515,7 +521,7 @@ class MUIDataTable extends React.Component {
   };
 
   getSortDirection(column) {
-    return column.sortDirection === "asc" ? "ascending" : "descending";
+    return column.sortDirection === 'asc' ? 'ascending' : 'descending';
   }
 
   toggleSortColumn = index => {
@@ -529,7 +535,7 @@ class MUIDataTable extends React.Component {
           if (index !== pos) {
             columns[pos].sortDirection = null;
           } else {
-            columns[pos].sortDirection = columns[pos].sortDirection === "asc" ? "desc" : "asc";
+            columns[pos].sortDirection = columns[pos].sortDirection === 'asc' ? 'desc' : 'asc';
           }
         }
 
@@ -563,7 +569,7 @@ class MUIDataTable extends React.Component {
         return newState;
       },
       () => {
-        this.setTableAction("sort");
+        this.setTableAction('sort');
         if (this.options.onColumnSortChange) {
           this.options.onColumnSortChange(
             this.state.columns[index].name,
@@ -588,7 +594,7 @@ class MUIDataTable extends React.Component {
         page: this.state.page > nextTotalPages ? nextTotalPages : this.state.page,
       }),
       () => {
-        this.setTableAction("changeRowsPerPage");
+        this.setTableAction('changeRowsPerPage');
         if (this.options.onChangeRowsPerPage) {
           this.options.onChangeRowsPerPage(this.state.rowsPerPage);
         }
@@ -602,7 +608,7 @@ class MUIDataTable extends React.Component {
         page: page,
       }),
       () => {
-        this.setTableAction("changePage");
+        this.setTableAction('changePage');
         if (this.options.onChangePage) {
           this.options.onChangePage(this.state.page);
         }
@@ -619,7 +625,7 @@ class MUIDataTable extends React.Component {
           : this.getDisplayData(prevState.columns, prevState.data, prevState.filterList, text),
       }),
       () => {
-        this.setTableAction("search");
+        this.setTableAction('search');
       },
     );
   };
@@ -637,7 +643,7 @@ class MUIDataTable extends React.Component {
         };
       },
       () => {
-        this.setTableAction("resetFilters");
+        this.setTableAction('resetFilters');
         if (this.options.onFilterChange) {
           this.options.onFilterChange(null, this.state.filterList);
         }
@@ -652,14 +658,14 @@ class MUIDataTable extends React.Component {
         const filterPos = filterList[index].indexOf(column);
 
         switch (type) {
-          case "checkbox":
+          case 'checkbox':
             filterPos >= 0 ? filterList[index].splice(filterPos, 1) : filterList[index].push(column);
             break;
-          case "multiselect":
-            filterList[index] = column === "" ? [] : column;
+          case 'multiselect':
+            filterList[index] = column === '' ? [] : column;
             break;
           default:
-            filterList[index] = filterPos >= 0 || column === "" ? [] : [column];
+            filterList[index] = filterPos >= 0 || column === '' ? [] : [column];
         }
 
         return {
@@ -670,7 +676,7 @@ class MUIDataTable extends React.Component {
         };
       },
       () => {
-        this.setTableAction("filterChange");
+        this.setTableAction('filterChange');
         if (this.options.onFilterChange) {
           this.options.onFilterChange(column, this.state.filterList);
         }
@@ -681,7 +687,7 @@ class MUIDataTable extends React.Component {
   selectRowDelete = () => {
     const { selectedRows, data, filterList } = this.state;
 
-    const selectedMap = this.buildSelectedMap(selectedRows.data);
+    const selectedMap = buildMap(selectedRows.data);
     const cleanRows = data.filter(({ index }) => !selectedMap[index]);
 
     if (this.options.onRowsDelete) {
@@ -698,20 +704,44 @@ class MUIDataTable extends React.Component {
       },
       TABLE_LOAD.UPDATE,
       () => {
-        this.setTableAction("rowDelete");
+        this.setTableAction('rowDelete');
       },
     );
   };
 
-  buildSelectedMap = rows => {
-    return rows.reduce((accum, { dataIndex }) => {
-      accum[dataIndex] = true;
-      return accum;
-    }, {});
+  toggleExpandRow = row => {
+    const { index, dataIndex } = row;
+    let expandedRows = [...this.state.expandedRows.data];
+    let rowPos = -1;
+
+    for (let cIndex = 0; cIndex < expandedRows.length; cIndex++) {
+      if (expandedRows[cIndex].index === index) {
+        rowPos = cIndex;
+        break;
+      }
+    }
+
+    if (rowPos >= 0) {
+      expandedRows.splice(rowPos, 1);
+    } else {
+      expandedRows.push(row);
+    }
+
+    this.setState(
+      {
+        expandedRows: {
+          lookup: buildMap(expandedRows),
+          data: expandedRows,
+        },
+      },
+      () => {
+        this.setTableAction('expandRow');
+      },
+    );
   };
 
   selectRowUpdate = (type, value) => {
-    if (type === "head") {
+    if (type === 'head') {
       this.setState(
         prevState => {
           const { displayData } = prevState;
@@ -726,11 +756,11 @@ class MUIDataTable extends React.Component {
             .map((d, i) => ({ index: i, dataIndex: displayData[i].dataIndex }));
 
           let newRows = [...prevState.selectedRows, ...selectedRows];
-          let selectedMap = this.buildSelectedMap(newRows);
+          let selectedMap = buildMap(newRows);
 
           if (isDeselect) {
             newRows = prevState.selectedRows.data.filter(({ dataIndex }) => !selectedMap[dataIndex]);
-            selectedMap = this.buildSelectedMap(newRows);
+            selectedMap = buildMap(newRows);
           }
 
           return {
@@ -742,13 +772,13 @@ class MUIDataTable extends React.Component {
           };
         },
         () => {
-          this.setTableAction("rowsSelect");
+          this.setTableAction('rowsSelect');
           if (this.options.onRowsSelect) {
             this.options.onRowsSelect(this.state.curSelectedRows, this.state.selectedRows.data);
           }
         },
       );
-    } else if (type === "cell") {
+    } else if (type === 'cell') {
       this.setState(
         prevState => {
           const { index, dataIndex } = value;
@@ -770,30 +800,30 @@ class MUIDataTable extends React.Component {
 
           return {
             selectedRows: {
-              lookup: this.buildSelectedMap(selectedRows),
+              lookup: buildMap(selectedRows),
               data: selectedRows,
             },
           };
         },
         () => {
-          this.setTableAction("rowsSelect");
+          this.setTableAction('rowsSelect');
           if (this.options.onRowsSelect) {
             this.options.onRowsSelect([value], this.state.selectedRows.data);
           }
         },
       );
-    } else if (type === "custom") {
+    } else if (type === 'custom') {
       const { displayData } = this.state;
 
       const data = value.map(row => ({ index: row, dataIndex: displayData[row].dataIndex }));
-      const lookup = this.buildSelectedMap(data);
+      const lookup = buildMap(data);
 
       this.setState(
         {
           selectedRows: { data, lookup },
         },
         () => {
-          this.setTableAction("rowsSelect");
+          this.setTableAction('rowsSelect');
           if (this.options.onRowsSelect) {
             this.options.onRowsSelect(this.state.selectedRows.data, this.state.selectedRows.data);
           }
@@ -802,19 +832,8 @@ class MUIDataTable extends React.Component {
     }
   };
 
-  sortCompare(order) {
-    return (a, b) => {
-      if (a.data === null) a.data = "";
-      if (b.data === null) b.data = "";
-      return (
-        (typeof a.data.localeCompare === "function" ? a.data.localeCompare(b.data) : a.data - b.data) *
-        (order === "asc" ? -1 : 1)
-      );
-    };
-  }
-
   sortTable(data, col, order) {
-    let dataSrc = this.options.customSort ? this.options.customSort(data, col, order || "desc") : data;
+    let dataSrc = this.options.customSort ? this.options.customSort(data, col, order || 'desc') : data;
 
     let sortedData = dataSrc.map((row, sIndex) => ({
       data: row.data[col],
@@ -823,7 +842,7 @@ class MUIDataTable extends React.Component {
     }));
 
     if (!this.options.customSort) {
-      sortedData.sort(this.sortCompare(order));
+      sortedData.sort(sortCompare(order));
     }
 
     let tableData = [];
@@ -840,7 +859,7 @@ class MUIDataTable extends React.Component {
     return {
       data: tableData,
       selectedRows: {
-        lookup: this.buildSelectedMap(selectedRows),
+        lookup: buildMap(selectedRows),
         data: selectedRows,
       },
     };
@@ -865,6 +884,7 @@ class MUIDataTable extends React.Component {
       filterList,
       rowsPerPage,
       selectedRows,
+      expandedRows,
       searchText,
     } = this.state;
 
@@ -873,7 +893,7 @@ class MUIDataTable extends React.Component {
     return (
       <Paper elevation={4} ref={this.tableContent} className={classes.paper}>
         {selectedRows.data.length ? (
-          <MUIDataTableToolbarSelect
+          <TableToolbarSelect
             options={this.options}
             selectedRows={selectedRows}
             onRowsDelete={this.selectRowDelete}
@@ -881,7 +901,7 @@ class MUIDataTable extends React.Component {
             selectRowUpdate={this.selectRowUpdate}
           />
         ) : (
-          <MUIDataTableToolbar
+          <TableToolbar
             columns={columns}
             displayData={displayData}
             data={data}
@@ -897,16 +917,16 @@ class MUIDataTable extends React.Component {
             setTableAction={this.setTableAction}
           />
         )}
-        <MUIDataTableFilterList options={this.options} filterList={filterList} filterUpdate={this.filterUpdate} />
+        <TableFilterList options={this.options} filterList={filterList} filterUpdate={this.filterUpdate} />
         <div
-          style={{ position: "relative" }}
-          className={this.options.responsive === "scroll" ? classes.responsiveScroll : null}>
+          style={{ position: 'relative' }}
+          className={this.options.responsive === 'scroll' ? classes.responsiveScroll : null}>
           {this.options.resizableColumns && (
-            <MUIDataTableResize key={rowCount} setResizeable={fn => (this.setHeadResizeable = fn)} />
+            <TableResize key={rowCount} setResizeable={fn => (this.setHeadResizeable = fn)} />
           )}
-          <Table ref={el => (this.tableRef = el)} tabIndex={"0"} role={"grid"}>
+          <MuiTable ref={el => (this.tableRef = el)} tabIndex={'0'} role={'grid'}>
             <caption className={classes.caption}>{title}</caption>
-            <MUIDataTableHead
+            <TableHead
               columns={columns}
               activeColumn={activeColumn}
               data={displayData}
@@ -921,7 +941,7 @@ class MUIDataTable extends React.Component {
               setCellRef={this.setHeadCellRef}
               options={this.options}
             />
-            <MUIDataTableBody
+            <TableBody
               data={displayData}
               count={rowCount}
               columns={columns}
@@ -929,28 +949,23 @@ class MUIDataTable extends React.Component {
               rowsPerPage={rowsPerPage}
               selectedRows={selectedRows}
               selectRowUpdate={this.selectRowUpdate}
+              expandedRows={expandedRows}
+              toggleExpandRow={this.toggleExpandRow}
               options={this.options}
               searchText={searchText}
               filterList={filterList}
             />
-          </Table>
+          </MuiTable>
         </div>
-        <Table>
-          {this.options.customFooter
-            ? this.options.customFooter(rowCount, page, rowsPerPage, this.changeRowsPerPage, this.changePage)
-            : this.options.pagination && (
-                <MUIDataTablePagination
-                  count={rowCount}
-                  page={page}
-                  rowsPerPage={rowsPerPage}
-                  changeRowsPerPage={this.changeRowsPerPage}
-                  changePage={this.changePage}
-                  component={"div"}
-                  options={this.options}
-                />
-              )}
-        </Table>
-        <div className={classes.liveAnnounce} aria-live={"polite"} ref={el => (this.announceRef = el)}>
+        <TableFooter
+          options={this.options}
+          page={page}
+          rowCount={rowCount}
+          rowsPerPage={rowsPerPage}
+          changeRowsPerPage={this.changeRowsPerPage}
+          changePage={this.changePage}
+        />
+        <div className={classes.liveAnnounce} aria-live={'polite'} ref={el => (this.announceRef = el)}>
           {announceText}
         </div>
       </Paper>
@@ -958,4 +973,4 @@ class MUIDataTable extends React.Component {
   }
 }
 
-export default withStyles(defaultTableStyles, { name: "MUIDataTable" })(MUIDataTable);
+export default withStyles(defaultTableStyles, { name: 'MUIDataTable' })(MUIDataTable);
diff --git a/src/MUIDataTableBody.js b/src/MUIDataTableBody.js
deleted file mode 100644
index c1885ed..0000000
--- a/src/MUIDataTableBody.js
+++ /dev/null
@@ -1,156 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Typography from "@material-ui/core/Typography";
-import TableBody from "@material-ui/core/TableBody";
-import MUIDataTableBodyCell from "./MUIDataTableBodyCell";
-import MUIDataTableBodyRow from "./MUIDataTableBodyRow";
-import MUIDataTableSelectCell from "./MUIDataTableSelectCell";
-import { withStyles } from "@material-ui/core/styles";
-
-const defaultBodyStyles = {
-  root: {},
-  emptyTitle: {
-    textAlign: "center",
-  },
-};
-
-class MUIDataTableBody extends React.Component {
-  static propTypes = {
-    /** Data used to describe table */
-    data: PropTypes.array.isRequired,
-    /** Total number of data rows */
-    count: PropTypes.number.isRequired,
-    /** Columns used to describe table */
-    columns: PropTypes.array.isRequired,
-    /** Options used to describe table */
-    options: PropTypes.object.isRequired,
-    /** Data used to filter table against */
-    filterList: PropTypes.array,
-    /** Callback to execute when row is clicked */
-    onRowClick: PropTypes.func,
-    /** Table rows selected */
-    selectedRows: PropTypes.object,
-    /** Callback to trigger table row select */
-    selectRowUpdate: PropTypes.func,
-    /** Data used to search table against */
-    searchText: PropTypes.string,
-    /** Extend the style applied to components */
-    classes: PropTypes.object,
-  };
-
-  buildRows() {
-    const { data, page, rowsPerPage, count } = this.props;
-
-    if (this.props.options.serverSide) return data;
-
-    let rows = [];
-    const totalPages = Math.floor(count / rowsPerPage);
-    const fromIndex = page === 0 ? 0 : page * rowsPerPage;
-    const toIndex = Math.min(count, (page + 1) * rowsPerPage);
-
-    if (page > totalPages && totalPages !== 0) {
-      throw new Error(
-        "Provided options.page of `" +
-          page +
-          "` is greater than the total available page length of `" +
-          totalPages +
-          "`",
-      );
-    }
-
-    for (let rowIndex = fromIndex; rowIndex < count && rowIndex < toIndex; rowIndex++) {
-      if (data[rowIndex] !== undefined) rows.push(data[rowIndex]);
-    }
-
-    return rows.length ? rows : null;
-  }
-
-  getRowIndex(index) {
-    const { page, rowsPerPage, options } = this.props;
-
-    if (options.serverSide) {
-      return index;
-    }
-
-    const startIndex = page === 0 ? 0 : page * rowsPerPage;
-    return startIndex + index;
-  }
-
-  isRowSelected(dataIndex) {
-    const { selectedRows } = this.props;
-    return selectedRows.lookup && selectedRows.lookup[dataIndex] ? true : false;
-  }
-
-  handleRowSelect = data => {
-    this.props.selectRowUpdate("cell", data);
-  };
-
-  render() {
-    const { classes, columns, options } = this.props;
-    const tableRows = this.buildRows();
-
-    if (tableRows) {
-      return (
-        <TableBody>
-          {tableRows.map(({ data: row, dataIndex }, rowIndex) => (
-            <MUIDataTableBodyRow
-              {...(options.setRowProps ? options.setRowProps(row, dataIndex) : {})}
-              options={options}
-              rowSelected={options.selectableRows ? this.isRowSelected(dataIndex) : false}
-              onClick={options.onRowClick ? options.onRowClick.bind(null, row, { rowIndex, dataIndex }) : null}
-              id={"MUIDataTableBodyRow-" + dataIndex}
-              key={rowIndex}>
-              {options.selectableRows && (
-                <MUIDataTableSelectCell
-                  onChange={this.handleRowSelect.bind(null, {
-                    index: this.getRowIndex(rowIndex),
-                    dataIndex: dataIndex,
-                  })}
-                  fixedHeader={options.fixedHeader}
-                  checked={this.isRowSelected(dataIndex)}
-                />
-              )}
-              {row.map(
-                (column, columnIndex) =>
-                  columns[columnIndex].display === "true" && (
-                    <MUIDataTableBodyCell
-                      {...(columns[columnIndex].setCellProps
-                        ? columns[columnIndex].setCellProps(column, dataIndex, columnIndex)
-                        : {})}
-                      dataIndex={dataIndex}
-                      rowIndex={rowIndex}
-                      colIndex={columnIndex}
-                      columnHeader={columns[columnIndex].name}
-                      options={options}
-                      key={columnIndex}>
-                      {column}
-                    </MUIDataTableBodyCell>
-                  ),
-              )}
-            </MUIDataTableBodyRow>
-          ))}
-        </TableBody>
-      );
-    }
-
-    return (
-      <TableBody>
-        {
-          <MUIDataTableBodyRow options={options}>
-            <MUIDataTableBodyCell
-              colSpan={options.selectableRows ? columns.length + 1 : columns.length}
-              options={options}
-              colIndex={0}
-              rowIndex={0}>
-              <Typography variant="subtitle1" className={classes.emptyTitle}>
-                {options.textLabels.body.noMatch}
-              </Typography>
-            </MUIDataTableBodyCell>
-          </MUIDataTableBodyRow>
-        }
-      </TableBody>
-    );
-  }
-}
-
-export default withStyles(defaultBodyStyles, { name: "MUIDataTableBody" })(MUIDataTableBody);
diff --git a/src/MUIDataTableSelectCell.js b/src/MUIDataTableSelectCell.js
deleted file mode 100644
index e19f7cb..0000000
--- a/src/MUIDataTableSelectCell.js
+++ /dev/null
@@ -1,73 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import classNames from "classnames";
-import Checkbox from "@material-ui/core/Checkbox";
-import TableCell from "@material-ui/core/TableCell";
-import { withStyles } from "@material-ui/core/styles";
-
-const defaultSelectCellStyles = theme => ({
-  root: {
-    [theme.breakpoints.down("sm")]: {
-      display: "none",
-    },
-  },
-  fixedHeader: {
-    position: "sticky",
-    top: "0px",
-    left: "0px",
-    zIndex: 100,
-    backgroundColor: "#FFF",
-  },
-  headerCell: {
-    zIndex: 110,
-  },
-  checkboxRoot: {
-    "&$checked": {
-      color: "#027cb5",
-    },
-  },
-  checked: {},
-  disabled: {},
-});
-
-class MUIDataTableSelectCell extends React.Component {
-  static propTypes = {
-    /** Select cell checked on/off */
-    checked: PropTypes.bool.isRequired,
-    /** Select cell part of fixed header */
-    fixedHeader: PropTypes.bool.isRequired,
-    /** Callback to trigger cell update */
-    onChange: PropTypes.func,
-    /** Extend the style applied to components */
-    classes: PropTypes.object,
-  };
-
-  static defaultProps = {
-    isHeaderCell: false,
-  };
-
-  render() {
-    const { classes, fixedHeader, isHeaderCell, ...otherProps } = this.props;
-
-    const cellClass = classNames({
-      [classes.root]: true,
-      [classes.fixedHeader]: fixedHeader,
-      [classes.headerCell]: isHeaderCell,
-    });
-
-    return (
-      <TableCell className={cellClass} padding="checkbox">
-        <Checkbox
-          classes={{
-            root: classes.checkboxRoot,
-            checked: classes.checked,
-            disabled: classes.disabled,
-          }}
-          {...otherProps}
-        />
-      </TableCell>
-    );
-  }
-}
-
-export default withStyles(defaultSelectCellStyles, { name: "MUIDataTableSelectCell" })(MUIDataTableSelectCell);
diff --git a/src/MUIDataTableToolbar.js b/src/MUIDataTableToolbar.js
deleted file mode 100644
index ee36f93..0000000
--- a/src/MUIDataTableToolbar.js
+++ /dev/null
@@ -1,268 +0,0 @@
-import React from "react";
-import Typography from "@material-ui/core/Typography";
-import Toolbar from "@material-ui/core/Toolbar";
-import Tooltip from "@material-ui/core/Tooltip";
-import IconButton from "@material-ui/core/IconButton";
-import MUIDataTableFilter from "./MUIDataTableFilter";
-import MUIDataTableViewCol from "./MUIDataTableViewCol";
-import MUIDataTableSearch from "./MUIDataTableSearch";
-import SearchIcon from "@material-ui/icons/Search";
-import DownloadIcon from "@material-ui/icons/CloudDownload";
-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 MUIDataTablePopoverWrapper from "./MUIPopover/MUIDataTablePopoverWrapper";
-
-export const defaultToolbarStyles = (theme, props) => ({
-  root: {},
-  left: {
-    flex: "1 1 55%",
-  },
-  actions: {
-    flex: "0 0 45%",
-    textAlign: "right",
-  },
-  titleRoot: {},
-  titleText: {},
-  icon: {
-    "&:hover": {
-      color: "#307BB0",
-    },
-  },
-  iconActive: {
-    color: "#307BB0",
-  },
-  searchIcon: {
-    display: "inline-flex",
-    marginTop: "10px",
-    marginRight: "8px",
-  },
-  ...(props.options.responsive ? { ...responsiveToolbarStyles(theme) } : {}),
-});
-
-export const responsiveToolbarStyles = theme => ({
-  [theme.breakpoints.down("sm")]: {
-    titleRoot: {},
-    titleText: {
-      fontSize: "16px",
-    },
-    spacer: {
-      display: "none",
-    },
-    left: {
-      // flex: "1 1 40%",
-      padding: "8px 0px",
-    },
-    actions: {
-      // flex: "1 1 60%",
-      textAlign: "right",
-    },
-  },
-  [theme.breakpoints.down("xs")]: {
-    root: {
-      display: "block",
-    },
-    left: {
-      padding: "8px 0px 0px 0px",
-    },
-    titleText: {
-      textAlign: "center",
-    },
-    actions: {
-      textAlign: "center",
-    },
-  },
-  "@media screen and (max-width: 480px)": {},
-});
-
-class MUIDataTableToolbar extends React.Component {
-  state = {
-    iconActive: null,
-    showSearch: false,
-  };
-
-  handleCSVDownload = () => {
-    const { data, columns, options } = this.props;
-
-    const CSVHead =
-      columns
-        .reduce(
-          (soFar, column) =>
-            column.download ? soFar + '"' + column.name + '"' + options.downloadOptions.separator : soFar,
-          "",
-        )
-        .slice(0, -1) + "\r\n";
-
-    const CSVBody = data
-      .reduce(
-        (soFar, row) =>
-          soFar +
-          '"' +
-          row.data
-            .filter((field, index) => columns[index].download)
-            .join('"' + options.downloadOptions.separator + '"') +
-          '"\r\n',
-        [],
-      )
-      .trim();
-
-    /* taken from react-csv */
-    const csv = `${CSVHead}${CSVBody}`;
-    const blob = new Blob([csv], { type: "text/csv" });
-
-    if (navigator && navigator.msSaveOrOpenBlob) {
-      navigator.msSaveOrOpenBlob(blob, options.downloadOptions.filename);
-    } else {
-      const dataURI = `data:text/csv;charset=utf-8,${csv}`;
-
-      const URL = window.URL || window.webkitURL;
-      const downloadURI = typeof URL.createObjectURL === "undefined" ? dataURI : URL.createObjectURL(blob);
-
-      let link = document.createElement("a");
-      link.setAttribute("href", downloadURI);
-      link.setAttribute("download", options.downloadOptions.filename);
-      document.body.appendChild(link);
-      link.click();
-      document.body.removeChild(link);
-    }
-  };
-
-  setActiveIcon = iconName => {
-    this.setState(() => ({
-      iconActive: iconName,
-      showSearch: iconName === "search" ? this.handleShowSearch() : false,
-    }));
-  };
-
-  getActiveIcon = (styles, iconName) => {
-    return this.state.iconActive !== iconName ? styles.icon : styles.iconActive;
-  };
-
-  handleShowSearch = () => {
-    !!this.props.options.onSearchOpen && this.props.options.onSearchOpen();
-    this.props.setTableAction("onSearchOpen");
-    return true;
-  };
-
-  hideSearch = () => {
-    const { onSearchClose } = this.props.options;
-
-    if (onSearchClose) onSearchClose();
-    this.props.searchTextUpdate(null);
-
-    this.setState(() => ({
-      iconActive: null,
-      showSearch: false,
-    }));
-
-    this.searchButton.focus();
-  };
-
-  render() {
-    const {
-      data,
-      options,
-      classes,
-      columns,
-      filterData,
-      filterList,
-      filterUpdate,
-      resetFilters,
-      searchTextUpdate,
-      toggleViewColumn,
-      title,
-      tableRef,
-    } = this.props;
-
-    const { search, downloadCsv, print, viewColumns, filterTable } = options.textLabels.toolbar;
-    const { showSearch } = this.state;
-
-    return (
-      <Toolbar className={classes.root} role={"toolbar"} aria-label={"Table Toolbar"}>
-        <div className={classes.left}>
-          {showSearch === true ? (
-            <MUIDataTableSearch onSearch={searchTextUpdate} onHide={this.hideSearch} options={options} />
-          ) : (
-            <div className={classes.titleRoot} aria-hidden={"true"}>
-              <Typography variant="h6" className={classes.titleText}>
-                {title}
-              </Typography>
-            </div>
-          )}
-        </div>
-        <div className={classes.actions}>
-          {options.search && (
-            <Tooltip title={search}>
-              <IconButton
-                aria-label={search}
-                buttonRef={el => (this.searchButton = el)}
-                classes={{ root: this.getActiveIcon(classes, "search") }}
-                onClick={this.setActiveIcon.bind(null, "search")}>
-                <SearchIcon />
-              </IconButton>
-            </Tooltip>
-          )}
-
-          {options.download && (
-            <Tooltip title={downloadCsv}>
-              <IconButton aria-label={downloadCsv} classes={{ root: classes.icon }} onClick={this.handleCSVDownload}>
-                <DownloadIcon />
-              </IconButton>
-            </Tooltip>
-          )}
-
-          {options.print && (
-            <Tooltip title={print}>
-              <span>
-                <ReactToPrint
-                  trigger={() => (
-                    <IconButton aria-label={print} classes={{ root: classes.icon }}>
-                      <PrintIcon />
-                    </IconButton>
-                  )}
-                  content={() => this.props.tableRef()}
-                />
-              </span>
-            </Tooltip>
-          )}
-
-          {options.viewColumns && (
-            <MUIDataTablePopoverWrapper
-              label={viewColumns}
-              tableRef={tableRef}
-              onClick={this.setActiveIcon.bind(null, "viewcolumns")}
-              buttonRoot={this.getActiveIcon(classes, "viewcolumns")}
-              icon={<ViewColumnIcon />}
-              classes={classes}>
-              <MUIDataTableViewCol data={data} columns={columns} options={options} onColumnUpdate={toggleViewColumn} />
-            </MUIDataTablePopoverWrapper>
-          )}
-
-          {options.filter && (
-            <MUIDataTablePopoverWrapper
-              label={filterTable}
-              tableRef={tableRef}
-              onClick={this.setActiveIcon.bind(null, "filter")}
-              buttonRoot={this.getActiveIcon(classes, "filter")}
-              icon={<FilterIcon />}
-              classes={classes}>
-              <MUIDataTableFilter
-                columns={columns}
-                options={options}
-                filterList={filterList}
-                filterData={filterData}
-                onFilterUpdate={filterUpdate}
-                onFilterReset={resetFilters}
-              />
-            </MUIDataTablePopoverWrapper>
-          )}
-          {options.customToolbar ? options.customToolbar() : false}
-        </div>
-      </Toolbar>
-    );
-  }
-}
-
-export default styled(MUIDataTableToolbar)(defaultToolbarStyles, { name: "MUIDataTableToolbar" });
diff --git a/src/MUIPopover/MUIDataTablePopoverWrapper.js b/src/MUIPopover/MUIDataTablePopoverWrapper.js
deleted file mode 100644
index b5ac50a..0000000
--- a/src/MUIPopover/MUIDataTablePopoverWrapper.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from "react";
-import Tooltip from "@material-ui/core/Tooltip";
-import IconButton from "@material-ui/core/IconButton";
-import MUIPopover from "./MUIPopover";
-import MUIPopoverTarget from "./MUIPopoverTarget";
-import MUIPopoverContent from "./MUIPopoverContent";
-
-class MUIDataTablePopoverWrapper extends React.PureComponent {
-  constructor(props) {
-    super(props);
-  }
-  render() {
-    const { label, tableRef, onClick, icon, children } = this.props;
-
-    return (
-      <MUIPopover container={tableRef}>
-        <MUIPopoverTarget>
-          <IconButton aria-label={label} onClick={onClick}>
-            <Tooltip title={label}>{icon}</Tooltip>
-          </IconButton>
-        </MUIPopoverTarget>
-        <MUIPopoverContent>{children}</MUIPopoverContent>
-      </MUIPopover>
-    );
-  }
-}
-
-export default MUIDataTablePopoverWrapper;
diff --git a/src/MUIPopover/MUIPopover.js b/src/MUIPopover/MUIPopover.js
deleted file mode 100644
index 9aeed72..0000000
--- a/src/MUIPopover/MUIPopover.js
+++ /dev/null
@@ -1,128 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Popover from "@material-ui/core/Popover";
-import MUIPopoverContent from "./MUIPopoverContent";
-import MUIPopoverTarget from "./MUIPopoverTarget";
-import { findDOMNode } from "react-dom";
-
-class MUIPopover extends React.Component {
-  static propTypes = {
-    /** Show indicating arrow. default: true */
-    arrow: PropTypes.bool,
-    /** Reference callback to handleRequestClose() to trigger manual close of MUIPopover */
-    refClose: PropTypes.func,
-    /** Reference callback to onExited() to trigger manual close of MUIPopover */
-    refExit: PropTypes.func,
-    /** MUIPopoverTarget and MUIPopoverContent are required children */
-    children: (props, propName, componentName) => {
-      let childMatch = true;
-      const expectedComponents = [MUIPopoverContent, MUIPopoverTarget];
-
-      React.Children.map(props.children, (child, index) => {
-        if (expectedComponents.indexOf(child.type) === -1) childMatch = false;
-      });
-
-      if (!childMatch) {
-        return new Error(
-          "`" +
-            componentName +
-            "` " +
-            "should only have children of the following types: `MUIPopoverTarget`, `MUIPopoverContent`.",
-        );
-      }
-    },
-  };
-
-  state = {
-    open: false,
-  };
-
-  componentWillMount() {
-    this.anchorEl = null;
-  }
-
-  componentDidMount() {
-    /*
-     * expose close method to the parent
-     */
-    if (this.props.refClose) {
-      this.props.refClose(this.handleRequestClose);
-    }
-  }
-
-  componentDidUpdate(prevProps, prevState) {
-    /*
-     * Update Popover position if a filter removes data from the table because
-     * it affects the window height which would cause the Popover to in the wrong place
-     */
-    if (this.state.open === true) {
-      this.anchorEl = findDOMNode(this.anchorEl);
-      this.popoverActions.updatePosition();
-    }
-  }
-
-  handleClick = () => {
-    this.anchorEl = findDOMNode(this.anchorEl);
-    this.setState({ open: true });
-  };
-
-  handleRequestClose = cb => {
-    this.setState({ open: false }, cb && typeof cb === "function" ? cb() : () => {});
-  };
-
-  handleOnExit = () => {
-    if (this.props.refExit) {
-      this.props.refExit();
-    }
-  };
-
-  render() {
-    let popoverRender = [];
-
-    const { className, placement, refClose, refExit, children, ...providedProps } = this.props;
-
-    React.Children.map(children, (child, index) => {
-      if (child.type === MUIPopoverContent || child.type === <MUIPopoverContent />.type) {
-        const transformOriginSpecs = {
-          vertical: "top",
-          horizontal: "center",
-        };
-
-        const anchorOriginSpecs = {
-          vertical: "bottom",
-          horizontal: "center",
-        };
-
-        const popoverContent = (
-          <Popover
-            action={actions => (this.popoverActions = actions)}
-            key={index}
-            elevation={2}
-            open={this.state.open}
-            onClose={this.handleRequestClose}
-            onExited={this.handleOnExit}
-            anchorEl={this.anchorEl}
-            anchorOrigin={anchorOriginSpecs}
-            transformOrigin={transformOriginSpecs}
-            {...providedProps}>
-            {child}
-          </Popover>
-        );
-
-        popoverRender.push(popoverContent);
-      } else if (child.type === MUIPopoverTarget || child.type === <MUIPopoverTarget />.type) {
-        const targetContent = React.cloneElement(child, {
-          key: index,
-          targetRef: el => (this.anchorEl = el),
-          onClick: this.handleClick,
-        });
-
-        popoverRender.push(targetContent);
-      }
-    });
-
-    return popoverRender;
-  }
-}
-
-export default MUIPopover;
diff --git a/src/MUIPopover/MUIPopoverContent.js b/src/MUIPopover/MUIPopoverContent.js
deleted file mode 100644
index 00cd90d..0000000
--- a/src/MUIPopover/MUIPopoverContent.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from "react";
-
-class MUIPopoverContent extends React.Component {
-  render() {
-    return this.props.children;
-  }
-}
-
-export default MUIPopoverContent;
diff --git a/src/MUIPopover/MUIPopoverTarget.js b/src/MUIPopover/MUIPopoverTarget.js
deleted file mode 100644
index 5a5d187..0000000
--- a/src/MUIPopover/MUIPopoverTarget.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from "react";
-
-class MUIPopoverTarget extends React.Component {
-  render() {
-    const targetContent = React.Children.map(this.props.children, (child, index) => {
-      return React.cloneElement(child, {
-        key: index,
-        ref: this.props.targetRef,
-        onClick: this.props.onClick,
-      });
-    });
-
-    return targetContent;
-  }
-}
-
-export default MUIPopoverTarget;
diff --git a/src/MUIPopover/index.js b/src/MUIPopover/index.js
deleted file mode 100644
index 71cff31..0000000
--- a/src/MUIPopover/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import MUIPopover from "./MUIPopover";
-import MUIPopoverContent from "./MUIPopoverContent";
-import MUIPopoverTarget from "./MUIPopoverTarget";
-
-export { MUIPopover, MUIPopoverContent, MUIPopoverTarget };
diff --git a/src/components/Popover.js b/src/components/Popover.js
new file mode 100644
index 0000000..923ce42
--- /dev/null
+++ b/src/components/Popover.js
@@ -0,0 +1,90 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import MuiPopover from '@material-ui/core/Popover';
+import { findDOMNode } from 'react-dom';
+
+class Popover extends React.Component {
+  state = {
+    open: false,
+  };
+
+  componentWillMount() {
+    this.anchorEl = null;
+  }
+
+  componentDidMount() {
+    if (this.props.refClose) {
+      this.props.refClose(this.handleRequestClose);
+    }
+  }
+
+  componentDidUpdate(prevProps, prevState) {
+    /*
+     * Update Popover position if a filter removes data from the table because
+     * it affects the window height which would cause the Popover to in the wrong place
+     */
+    if (this.state.open === true) {
+      this.anchorEl = findDOMNode(this.anchorEl);
+      this.popoverActions.updatePosition();
+    }
+  }
+
+  handleClick = () => {
+    this.anchorEl = findDOMNode(this.anchorEl);
+    this.setState({ open: true });
+  };
+
+  handleRequestClose = cb => {
+    this.setState({ open: false }, cb && typeof cb === 'function' ? cb() : () => {});
+  };
+
+  handleOnExit = () => {
+    if (this.props.refExit) {
+      this.props.refExit();
+    }
+  };
+
+  render() {
+    const { className, placement, trigger, refExit, content, ...providedProps } = this.props;
+
+    const transformOriginSpecs = {
+      vertical: 'top',
+      horizontal: 'center',
+    };
+
+    const anchorOriginSpecs = {
+      vertical: 'bottom',
+      horizontal: 'center',
+    };
+
+    const triggerEl = React.cloneElement(trigger, {
+      key: 'content',
+      ref: el => (this.anchorEl = el),
+      onClick: () => {
+        if (trigger.props.onClick) trigger.props.onClick();
+        this.handleClick();
+      },
+    });
+
+    return (
+      <React.Fragment>
+        <MuiPopover
+          action={actions => (this.popoverActions = actions)}
+          elevation={2}
+          open={this.state.open}
+          onClose={this.handleRequestClose}
+          onExited={this.handleOnExit}
+          anchorEl={this.anchorEl}
+          ref={el => this.popoverEl}
+          anchorOrigin={anchorOriginSpecs}
+          transformOrigin={transformOriginSpecs}
+          {...providedProps}>
+          {content}
+        </MuiPopover>
+        {triggerEl}
+      </React.Fragment>
+    );
+  }
+}
+
+export default Popover;
diff --git a/src/components/TableBody.js b/src/components/TableBody.js
new file mode 100644
index 0000000..701d8e5
--- /dev/null
+++ b/src/components/TableBody.js
@@ -0,0 +1,169 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Typography from '@material-ui/core/Typography';
+import MuiTableBody from '@material-ui/core/TableBody';
+import TableBodyCell from './TableBodyCell';
+import TableBodyRow from './TableBodyRow';
+import TableSelectCell from './TableSelectCell';
+import { withStyles } from '@material-ui/core/styles';
+
+const defaultBodyStyles = {
+  root: {},
+  emptyTitle: {
+    textAlign: 'center',
+  },
+};
+
+class TableBody extends React.Component {
+  static propTypes = {
+    /** Data used to describe table */
+    data: PropTypes.array.isRequired,
+    /** Total number of data rows */
+    count: PropTypes.number.isRequired,
+    /** Columns used to describe table */
+    columns: PropTypes.array.isRequired,
+    /** Options used to describe table */
+    options: PropTypes.object.isRequired,
+    /** Data used to filter table against */
+    filterList: PropTypes.array,
+    /** Callback to execute when row is clicked */
+    onRowClick: PropTypes.func,
+    /** Table rows selected */
+    selectedRows: PropTypes.object,
+    /** Callback to trigger table row select */
+    selectRowUpdate: PropTypes.func,
+    /** Data used to search table against */
+    searchText: PropTypes.string,
+    /** Toggle row expandable */
+    toggleExpandRow: PropTypes.func,
+    /** Extend the style applied to components */
+    classes: PropTypes.object,
+  };
+
+  static defaultProps = {
+    toggleExpandRow: () => {},
+  };
+
+  buildRows() {
+    const { data, page, rowsPerPage, count } = this.props;
+
+    if (this.props.options.serverSide) return data;
+
+    let rows = [];
+    const totalPages = Math.floor(count / rowsPerPage);
+    const fromIndex = page === 0 ? 0 : page * rowsPerPage;
+    const toIndex = Math.min(count, (page + 1) * rowsPerPage);
+
+    if (page > totalPages && totalPages !== 0) {
+      throw new Error(
+        'Provided options.page of `' +
+          page +
+          '` is greater than the total available page length of `' +
+          totalPages +
+          '`',
+      );
+    }
+
+    for (let rowIndex = fromIndex; rowIndex < count && rowIndex < toIndex; rowIndex++) {
+      if (data[rowIndex] !== undefined) rows.push(data[rowIndex]);
+    }
+
+    return rows.length ? rows : null;
+  }
+
+  getRowIndex(index) {
+    const { page, rowsPerPage, options } = this.props;
+
+    if (options.serverSide) {
+      return index;
+    }
+
+    const startIndex = page === 0 ? 0 : page * rowsPerPage;
+    return startIndex + index;
+  }
+
+  isRowSelected(dataIndex) {
+    const { selectedRows } = this.props;
+    return selectedRows.lookup && selectedRows.lookup[dataIndex] ? true : false;
+  }
+
+  isRowExpanded(dataIndex) {
+    const { expandedRows } = this.props;
+    return expandedRows.lookup && expandedRows.lookup[dataIndex] ? true : false;
+  }
+
+  handleRowSelect = data => {
+    this.props.selectRowUpdate('cell', data);
+  };
+
+  render() {
+    const { classes, columns, toggleExpandRow, options } = this.props;
+    const tableRows = this.buildRows();
+
+    return (
+      <MuiTableBody>
+        {tableRows ? (
+          tableRows.map(({ data: row, dataIndex }, rowIndex) => (
+            <React.Fragment key={rowIndex}>
+              <TableBodyRow
+                {...(options.setRowProps ? options.setRowProps(row, dataIndex) : {})}
+                options={options}
+                rowSelected={options.selectableRows ? this.isRowSelected(dataIndex) : false}
+                onClick={options.onRowClick ? options.onRowClick.bind(null, row, { rowIndex, dataIndex }) : null}
+                id={'MUIDataTableBodyRow-' + dataIndex}>
+                {options.selectableRows && (
+                  <TableSelectCell
+                    onChange={this.handleRowSelect.bind(null, {
+                      index: this.getRowIndex(rowIndex),
+                      dataIndex: dataIndex,
+                    })}
+                    onExpand={toggleExpandRow.bind(null, {
+                      index: this.getRowIndex(rowIndex),
+                      dataIndex: dataIndex,
+                    })}
+                    fixedHeader={options.fixedHeader}
+                    checked={this.isRowSelected(dataIndex)}
+                    isExpandable={options.expandableRows}
+                    isRowExpanded={this.isRowExpanded(dataIndex)}
+                  />
+                )}
+                {row.map(
+                  (column, columnIndex) =>
+                    columns[columnIndex].display === 'true' && (
+                      <TableBodyCell
+                        {...(columns[columnIndex].setCellProps
+                          ? columns[columnIndex].setCellProps(column, dataIndex, columnIndex)
+                          : {})}
+                        dataIndex={dataIndex}
+                        rowIndex={rowIndex}
+                        colIndex={columnIndex}
+                        columnHeader={columns[columnIndex].name}
+                        options={options}
+                        key={columnIndex}>
+                        {column}
+                      </TableBodyCell>
+                    ),
+                )}
+              </TableBodyRow>
+              {this.isRowExpanded(dataIndex) && options.renderExpandableRow(row, { rowIndex, dataIndex })}
+            </React.Fragment>
+          ))
+        ) : (
+          <TableBodyRow options={options}>
+            <TableBodyCell
+              colSpan={options.selectableRows ? columns.length + 1 : columns.length}
+              options={options}
+              colIndex={0}
+              rowIndex={0}>
+              <Typography variant="subheading" className={classes.emptyTitle}>
+                {options.textLabels.body.noMatch}
+              </Typography>
+            </TableBodyCell>
+          </TableBodyRow>
+        )}
+      </MuiTableBody>
+    );
+  }
+}
+
+export default withStyles(defaultBodyStyles, { name: 'MUIDataTableBody' })(TableBody);
diff --git a/src/MUIDataTableBodyCell.js b/src/components/TableBodyCell.js
similarity index 58%
rename from src/MUIDataTableBodyCell.js
rename to src/components/TableBodyCell.js
index 31d709c..6ee07a3 100644
--- a/src/MUIDataTableBodyCell.js
+++ b/src/components/TableBodyCell.js
@@ -1,35 +1,35 @@
-import React from "react";
-import classNames from "classnames";
-import TableCell from "@material-ui/core/TableCell";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import classNames from 'classnames';
+import TableCell from '@material-ui/core/TableCell';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultBodyCellStyles = theme => ({
   root: {},
   cellHide: {
-    display: "none",
+    display: 'none',
   },
   cellStacked: {
-    [theme.breakpoints.down("sm")]: {
-      display: "inline-block",
-      backgroundColor: "#FFF",
-      fontSize: "16px",
-      height: "24px",
-      width: "calc(50% - 80px)",
-      whiteSpace: "nowrap",
+    [theme.breakpoints.down('sm')]: {
+      display: 'inline-block',
+      backgroundColor: '#FFF',
+      fontSize: '16px',
+      height: '24px',
+      width: 'calc(50% - 80px)',
+      whiteSpace: 'nowrap',
     },
   },
   responsiveStacked: {
-    [theme.breakpoints.down("sm")]: {
-      display: "inline-block",
-      fontSize: "16px",
-      width: "calc(50% - 80px)",
-      whiteSpace: "nowrap",
-      height: "24px",
+    [theme.breakpoints.down('sm')]: {
+      display: 'inline-block',
+      fontSize: '16px',
+      width: 'calc(50% - 80px)',
+      whiteSpace: 'nowrap',
+      height: '24px',
     },
   },
 });
 
-class MUIDataTableBodyCell extends React.Component {
+class TableBodyCell extends React.Component {
   handleClick = () => {
     const { colIndex, options, children, dataIndex, rowIndex } = this.props;
     if (options.onCellClick) {
@@ -57,7 +57,7 @@ class MUIDataTableBodyCell extends React.Component {
           {
             [classes.root]: true,
             [classes.cellHide]: true,
-            [classes.cellStacked]: options.responsive === "stacked",
+            [classes.cellStacked]: options.responsive === 'stacked',
           },
           className,
         )}>
@@ -69,7 +69,7 @@ class MUIDataTableBodyCell extends React.Component {
         className={classNames(
           {
             [classes.root]: true,
-            [classes.responsiveStacked]: options.responsive === "stacked",
+            [classes.responsiveStacked]: options.responsive === 'stacked',
           },
           className,
         )}
@@ -80,4 +80,4 @@ class MUIDataTableBodyCell extends React.Component {
   }
 }
 
-export default withStyles(defaultBodyCellStyles, { name: "MUIDataTableBodyCell" })(MUIDataTableBodyCell);
+export default withStyles(defaultBodyCellStyles, { name: 'MUIDataTableBodyCell' })(TableBodyCell);
diff --git a/src/MUIDataTableBodyRow.js b/src/components/TableBodyRow.js
similarity index 67%
rename from src/MUIDataTableBodyRow.js
rename to src/components/TableBodyRow.js
index d9d8878..fe3307d 100644
--- a/src/MUIDataTableBodyRow.js
+++ b/src/components/TableBodyRow.js
@@ -1,19 +1,19 @@
-import React from "react";
-import PropTypes from "prop-types";
-import classNames from "classnames";
-import TableRow from "@material-ui/core/TableRow";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import TableRow from '@material-ui/core/TableRow';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultBodyRowStyles = theme => ({
   root: {},
   responsiveStacked: {
-    [theme.breakpoints.down("sm")]: {
-      border: "solid 2px rgba(0, 0, 0, 0.15)",
+    [theme.breakpoints.down('sm')]: {
+      border: 'solid 2px rgba(0, 0, 0, 0.15)',
     },
   },
 });
 
-class MUIDataTableBodyRow extends React.Component {
+class TableBodyRow extends React.Component {
   static propTypes = {
     /** Options used to describe table */
     options: PropTypes.object.isRequired,
@@ -35,7 +35,7 @@ class MUIDataTableBodyRow extends React.Component {
         className={classNames(
           {
             [classes.root]: true,
-            [classes.responsiveStacked]: options.responsive === "stacked",
+            [classes.responsiveStacked]: options.responsive === 'stacked',
           },
           className,
         )}
@@ -47,4 +47,4 @@ class MUIDataTableBodyRow extends React.Component {
   }
 }
 
-export default withStyles(defaultBodyRowStyles, { name: "MUIDataTableBodyRow" })(MUIDataTableBodyRow);
+export default withStyles(defaultBodyRowStyles, { name: 'MUIDataTableBodyRow' })(TableBodyRow);
diff --git a/src/MUIDataTableFilter.js b/src/components/TableFilter.js
similarity index 70%
rename from src/MUIDataTableFilter.js
rename to src/components/TableFilter.js
index 75c6980..3bf87c6 100644
--- a/src/MUIDataTableFilter.js
+++ b/src/components/TableFilter.js
@@ -1,112 +1,112 @@
-import React from "react";
-import PropTypes from "prop-types";
-import classNames from "classnames";
-import Typography from "@material-ui/core/Typography";
-import FormControl from "@material-ui/core/FormControl";
-import FormGroup from "@material-ui/core/FormGroup";
-import FormControlLabel from "@material-ui/core/FormControlLabel";
-import InputLabel from "@material-ui/core/InputLabel";
-import Input from "@material-ui/core/Input";
-import MenuItem from "@material-ui/core/MenuItem";
-import Select from "@material-ui/core/Select";
-import Checkbox from "@material-ui/core/Checkbox";
-import ListItemText from "@material-ui/core/ListItemText";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import Typography from '@material-ui/core/Typography';
+import FormControl from '@material-ui/core/FormControl';
+import FormGroup from '@material-ui/core/FormGroup';
+import FormControlLabel from '@material-ui/core/FormControlLabel';
+import InputLabel from '@material-ui/core/InputLabel';
+import Input from '@material-ui/core/Input';
+import MenuItem from '@material-ui/core/MenuItem';
+import Select from '@material-ui/core/Select';
+import Checkbox from '@material-ui/core/Checkbox';
+import ListItemText from '@material-ui/core/ListItemText';
+import { withStyles } from '@material-ui/core/styles';
 
 export const defaultFilterStyles = {
   root: {
-    padding: "16px 24px 16px 24px",
-    fontFamily: "Roboto",
+    padding: '16px 24px 16px 24px',
+    fontFamily: 'Roboto',
   },
   header: {
-    flex: "0 0 auto",
-    marginBottom: "16px",
-    width: "100%",
-    display: "flex",
-    justifyContent: "space-between",
+    flex: '0 0 auto',
+    marginBottom: '16px',
+    width: '100%',
+    display: 'flex',
+    justifyContent: 'space-between',
   },
   title: {
-    display: "inline-block",
-    marginLeft: "7px",
-    color: "#424242",
-    fontSize: "14px",
+    display: 'inline-block',
+    marginLeft: '7px',
+    color: '#424242',
+    fontSize: '14px',
     fontWeight: 500,
   },
   noMargin: {
-    marginLeft: "0px",
+    marginLeft: '0px',
   },
   reset: {
-    alignSelf: "left",
+    alignSelf: 'left',
   },
   resetLink: {
-    color: "#027cb5",
-    backgroundColor: "#FFF",
-    display: "inline-block",
-    marginLeft: "24px",
-    fontSize: "12px",
-    cursor: "pointer",
-    border: "none",
-    "&:hover": {
-      color: "#FF0000",
+    color: '#027cb5',
+    backgroundColor: '#FFF',
+    display: 'inline-block',
+    marginLeft: '24px',
+    fontSize: '12px',
+    cursor: 'pointer',
+    border: 'none',
+    '&:hover': {
+      color: '#FF0000',
     },
   },
   filtersSelected: {
-    alignSelf: "right",
+    alignSelf: 'right',
   },
   /* checkbox */
   checkboxList: {
-    flex: "1 1 100%",
-    display: "inline-flex",
-    marginRight: "24px",
+    flex: '1 1 100%',
+    display: 'inline-flex',
+    marginRight: '24px',
   },
   checkboxListTitle: {
-    marginLeft: "7px",
-    marginBottom: "8px",
-    fontSize: "14px",
-    color: "#424242",
-    textAlign: "left",
+    marginLeft: '7px',
+    marginBottom: '8px',
+    fontSize: '14px',
+    color: '#424242',
+    textAlign: 'left',
     fontWeight: 500,
   },
   checkboxFormGroup: {
-    marginTop: "8px",
+    marginTop: '8px',
   },
   checkboxFormControl: {
-    margin: "0px",
+    margin: '0px',
   },
   checkboxFormControlLabel: {
-    fontSize: "15px",
-    marginLeft: "8px",
-    color: "#4a4a4a",
+    fontSize: '15px',
+    marginLeft: '8px',
+    color: '#4a4a4a',
   },
   checkboxIcon: {
     //color: "#027cb5",
-    width: "32px",
-    height: "32px",
+    width: '32px',
+    height: '32px',
   },
   checkbox: {
-    "&$checked": {
-      color: "#027cB5",
+    '&$checked': {
+      color: '#027cB5',
     },
   },
   checked: {},
   /* selects */
   selectRoot: {
-    display: "flex",
-    marginTop: "16px",
-    flexDirection: "row",
-    flexWrap: "wrap",
-    width: "100%",
-    height: "80%",
-    justifyContent: "space-between",
+    display: 'flex',
+    marginTop: '16px',
+    flexDirection: 'row',
+    flexWrap: 'wrap',
+    width: '100%',
+    height: '80%',
+    justifyContent: 'space-between',
   },
   selectFormControl: {
-    flex: "1 1 calc(50% - 24px)",
-    marginRight: "24px",
-    marginBottom: "24px",
+    flex: '1 1 calc(50% - 24px)',
+    marginRight: '24px',
+    marginBottom: '24px',
   },
 };
 
-class MUIDataTableFilter extends React.Component {
+class TableFilter extends React.Component {
   static propTypes = {
     /** Data used to populate filter dropdown/checkbox */
     filterData: PropTypes.array.isRequired,
@@ -123,16 +123,16 @@ class MUIDataTableFilter extends React.Component {
   };
 
   handleCheckboxChange = (index, column) => {
-    this.props.onFilterUpdate(index, column, "checkbox");
+    this.props.onFilterUpdate(index, column, 'checkbox');
   };
 
   handleDropdownChange = (event, index) => {
-    const value = event.target.value === "All" ? "" : event.target.value;
-    this.props.onFilterUpdate(index, value, "dropdown");
+    const value = event.target.value === 'All' ? '' : event.target.value;
+    this.props.onFilterUpdate(index, value, 'dropdown');
   };
 
   handleMultiselectChange = (index, column) => {
-    this.props.onFilterUpdate(index, column, "multiselect");
+    this.props.onFilterUpdate(index, column, 'multiselect');
   };
 
   renderCheckbox(columns) {
@@ -161,7 +161,7 @@ class MUIDataTableFilter extends React.Component {
                       root: classes.checkbox,
                       checked: classes.checked,
                     }}
-                    value={filterColumn !== null ? filterColumn.toString() : ""}
+                    value={filterColumn !== null ? filterColumn.toString() : ''}
                   />
                 }
                 label={filterColumn}
@@ -195,7 +195,7 @@ class MUIDataTableFilter extends React.Component {
                 </MenuItem>
                 {filterData[index].map((filterColumn, filterIndex) => (
                   <MenuItem value={filterColumn} key={filterIndex + 1}>
-                    {filterColumn !== null ? filterColumn.toString() : ""}
+                    {filterColumn !== null ? filterColumn.toString() : ''}
                   </MenuItem>
                 ))}
               </Select>
@@ -220,7 +220,7 @@ class MUIDataTableFilter extends React.Component {
               <Select
                 multiple
                 value={filterList[index] || []}
-                renderValue={selected => selected.join(", ")}
+                renderValue={selected => selected.join(', ')}
                 name={column.name}
                 onChange={event => this.handleMultiselectChange(index, event.target.value)}
                 input={<Input name={column.name} id={column.name} />}>
@@ -260,7 +260,7 @@ class MUIDataTableFilter extends React.Component {
               variant="caption"
               className={classNames({
                 [classes.title]: true,
-                [classes.noMargin]: options.filterType !== "checkbox" ? true : false,
+                [classes.noMargin]: options.filterType !== 'checkbox' ? true : false,
               })}>
               {textLabels.title}
             </Typography>
@@ -270,9 +270,9 @@ class MUIDataTableFilter extends React.Component {
           </div>
           <div className={classes.filtersSelected} />
         </div>
-        {options.filterType === "checkbox"
+        {options.filterType === 'checkbox'
           ? this.renderCheckbox(columns)
-          : options.filterType === "multiselect"
+          : options.filterType === 'multiselect'
           ? this.renderMultiselect(columns)
           : this.renderSelect(columns)}
       </div>
@@ -280,4 +280,4 @@ class MUIDataTableFilter extends React.Component {
   }
 }
 
-export default withStyles(defaultFilterStyles, { name: "MUIDataTableFilter" })(MUIDataTableFilter);
+export default withStyles(defaultFilterStyles, { name: 'MUIDataTableFilter' })(TableFilter);
diff --git a/src/MUIDataTableFilterList.js b/src/components/TableFilterList.js
similarity index 57%
rename from src/MUIDataTableFilterList.js
rename to src/components/TableFilterList.js
index 8ef1bd9..fbb65ac 100644
--- a/src/MUIDataTableFilterList.js
+++ b/src/components/TableFilterList.js
@@ -1,21 +1,21 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Chip from "@material-ui/core/Chip";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import Chip from '@material-ui/core/Chip';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultFilterListStyles = {
   root: {
-    display: "flex",
-    justifyContent: "left",
-    flexWrap: "wrap",
-    margin: "0px 16px 0px 16px",
+    display: 'flex',
+    justifyContent: 'left',
+    flexWrap: 'wrap',
+    margin: '0px 16px 0px 16px',
   },
   chip: {
-    margin: "8px 8px 0px 0px",
+    margin: '8px 8px 0px 0px',
   },
 };
 
-class MUIDataTableFilterList extends React.Component {
+class TableFilterList extends React.Component {
   static propTypes = {
     /** Data used to filter table against */
     filterList: PropTypes.array.isRequired,
@@ -35,7 +35,7 @@ class MUIDataTableFilterList extends React.Component {
             <Chip
               label={data}
               key={colIndex}
-              onDelete={filterUpdate.bind(null, index, data, "checkbox")}
+              onDelete={filterUpdate.bind(null, index, data, 'checkbox')}
               className={classes.chip}
             />
           )),
@@ -45,4 +45,4 @@ class MUIDataTableFilterList extends React.Component {
   }
 }
 
-export default withStyles(defaultFilterListStyles, { name: "MUIDataTableFilterList" })(MUIDataTableFilterList);
+export default withStyles(defaultFilterListStyles, { name: 'MUIDataTableFilterList' })(TableFilterList);
diff --git a/src/components/TableFooter.js b/src/components/TableFooter.js
new file mode 100644
index 0000000..c5c33a7
--- /dev/null
+++ b/src/components/TableFooter.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import MuiTable from '@material-ui/core/Table';
+import TableHead from './TableHead';
+import TablePagination from './TablePagination';
+import { withStyles } from '@material-ui/core/styles';
+
+export const defaultFooterStyles = {};
+
+class TableFooter extends React.Component {
+  static propTypes = {};
+
+  render() {
+    const { options, rowCount, page, rowsPerPage, changeRowsPerPage, changePage } = this.props;
+
+    return (
+      <MuiTable>
+        {options.customFooter
+          ? options.customFooter(rowCount, page, rowsPerPage, changeRowsPerPage, changePage)
+          : options.pagination && (
+              <TablePagination
+                count={rowCount}
+                page={page}
+                rowsPerPage={rowsPerPage}
+                changeRowsPerPage={changeRowsPerPage}
+                changePage={changePage}
+                component={'div'}
+                options={options}
+              />
+            )}
+      </MuiTable>
+    );
+  }
+}
+
+export default TableFooter;
diff --git a/src/MUIDataTableHead.js b/src/components/TableHead.js
similarity index 62%
rename from src/MUIDataTableHead.js
rename to src/components/TableHead.js
index f4b2bdb..4da159e 100644
--- a/src/MUIDataTableHead.js
+++ b/src/components/TableHead.js
@@ -1,22 +1,22 @@
-import React from "react";
-import { findDOMNode } from "react-dom";
-import classNames from "classnames";
-import TableHead from "@material-ui/core/TableHead";
-import MUIDataTableHeadRow from "./MUIDataTableHeadRow";
-import MUIDataTableHeadCell from "./MUIDataTableHeadCell";
-import MUIDataTableSelectCell from "./MUIDataTableSelectCell";
-import { withStyles } from "@material-ui/core/styles";
+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 TableSelectCell from './TableSelectCell';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultHeadStyles = theme => ({
   main: {},
   responsiveStacked: {
-    [theme.breakpoints.down("sm")]: {
-      display: "none",
+    [theme.breakpoints.down('sm')]: {
+      display: 'none',
     },
   },
 });
 
-class MUIDataTableHead extends React.Component {
+class TableHead extends React.Component {
   componentDidMount() {
     this.props.handleHeadUpdateRef(this.handleUpdateCheck);
   }
@@ -26,7 +26,7 @@ class MUIDataTableHead extends React.Component {
   };
 
   handleRowSelect = () => {
-    this.props.selectRowUpdate("head", null);
+    this.props.selectRowUpdate('head', null);
   };
 
   render() {
@@ -37,29 +37,30 @@ class MUIDataTableHead extends React.Component {
     const isChecked = numSelected === count ? true : false;
 
     return (
-      <TableHead
-        className={classNames({ [classes.responsiveStacked]: options.responsive === "stacked", [classes.main]: true })}>
-        <MUIDataTableHeadRow>
+      <MuiTableHead
+        className={classNames({ [classes.responsiveStacked]: options.responsive === 'stacked', [classes.main]: true })}>
+        <TableHeadRow>
           {options.selectableRows && (
-            <MUIDataTableSelectCell
+            <TableSelectCell
               ref={el => setCellRef(0, findDOMNode(el))}
               onChange={this.handleRowSelect.bind(null)}
               indeterminate={isDeterminate}
               checked={isChecked}
               isHeaderCell={true}
+              isExpandable={options.expandableRows}
               fixedHeader={options.fixedHeader}
             />
           )}
           {columns.map(
             (column, index) =>
-              column.display === "true" &&
+              column.display === 'true' &&
               (column.customHeadRender ? (
                 column.customHeadRender({ index, ...column }, this.handleToggleColumn)
               ) : (
-                <MUIDataTableHeadCell
+                <TableHeadCell
                   key={index}
                   index={index}
-                  type={"cell"}
+                  type={'cell'}
                   ref={el => setCellRef(index + 1, findDOMNode(el))}
                   sort={column.sort}
                   sortDirection={column.sortDirection}
@@ -67,13 +68,13 @@ class MUIDataTableHead extends React.Component {
                   hint={column.hint}
                   options={options}>
                   {column.name}
-                </MUIDataTableHeadCell>
+                </TableHeadCell>
               )),
           )}
-        </MUIDataTableHeadRow>
-      </TableHead>
+        </TableHeadRow>
+      </MuiTableHead>
     );
   }
 }
 
-export default withStyles(defaultHeadStyles, { name: "MUIDataTableHead" })(MUIDataTableHead);
+export default withStyles(defaultHeadStyles, { name: 'MUIDataTableHead' })(TableHead);
diff --git a/src/MUIDataTableHeadCell.js b/src/components/TableHeadCell.js
similarity index 69%
rename from src/MUIDataTableHeadCell.js
rename to src/components/TableHeadCell.js
index 4f25275..832339b 100644
--- a/src/MUIDataTableHeadCell.js
+++ b/src/components/TableHeadCell.js
@@ -1,50 +1,50 @@
-import React from "react";
-import PropTypes from "prop-types";
-import classNames from "classnames";
-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 React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+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';
 
 const defaultHeadCellStyles = {
   root: {},
   fixedHeader: {
-    position: "sticky",
-    top: "0px",
-    left: "0px",
+    position: 'sticky',
+    top: '0px',
+    left: '0px',
     zIndex: 100,
-    backgroundColor: "#FFF",
+    backgroundColor: '#FFF',
   },
   tooltip: {
-    cursor: "pointer",
+    cursor: 'pointer',
   },
   mypopper: {
-    "&[data-x-out-of-boundaries]": {
-      display: "none",
+    '&[data-x-out-of-boundaries]': {
+      display: 'none',
     },
   },
   data: {
-    display: "inline-block",
+    display: 'inline-block',
   },
   sortAction: {
-    display: "inline-block",
-    verticalAlign: "top",
-    cursor: "pointer",
-    paddingLeft: "4px",
-    height: "10px",
+    display: 'inline-block',
+    verticalAlign: 'top',
+    cursor: 'pointer',
+    paddingLeft: '4px',
+    height: '10px',
   },
   sortActive: {
-    color: "rgba(0, 0, 0, 0.87)",
+    color: 'rgba(0, 0, 0, 0.87)',
   },
   toolButton: {
-    height: "10px",
-    outline: "none",
-    cursor: "pointer",
+    height: '10px',
+    outline: 'none',
+    cursor: 'pointer',
   },
 };
 
-class MUIDataTableHeadCell extends React.Component {
+class TableHeadCell extends React.Component {
   static propTypes = {
     /** Extend the style applied to components */
     classes: PropTypes.object,
@@ -79,11 +79,11 @@ class MUIDataTableHeadCell extends React.Component {
     });
 
     return (
-      <TableCell className={cellClass} scope={"col"} sortDirection={sortDirection}>
+      <TableCell className={cellClass} scope={'col'} sortDirection={sortDirection}>
         {options.sort && sort ? (
           <Tooltip
             title={options.textLabels.body.toolTip}
-            placement={"bottom-end"}
+            placement={'bottom-end'}
             classes={{
               tooltip: classes.tooltip,
             }}
@@ -113,7 +113,7 @@ class MUIDataTableHeadCell extends React.Component {
         {hint && (
           <Tooltip
             title={hint}
-            placement={"bottom-end"}
+            placement={'bottom-end'}
             classes={{
               tooltip: classes.tooltip,
             }}
@@ -127,4 +127,4 @@ class MUIDataTableHeadCell extends React.Component {
   }
 }
 
-export default withStyles(defaultHeadCellStyles, { name: "MUIDataTableHeadCell" })(MUIDataTableHeadCell);
+export default withStyles(defaultHeadCellStyles, { name: 'MUIDataTableHeadCell' })(TableHeadCell);
diff --git a/src/MUIDataTableHeadRow.js b/src/components/TableHeadRow.js
similarity index 51%
rename from src/MUIDataTableHeadRow.js
rename to src/components/TableHeadRow.js
index 1bb9329..c8b60e2 100644
--- a/src/MUIDataTableHeadRow.js
+++ b/src/components/TableHeadRow.js
@@ -1,14 +1,14 @@
-import React from "react";
-import PropTypes from "prop-types";
-import classNames from "classnames";
-import TableRow from "@material-ui/core/TableRow";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import TableRow from '@material-ui/core/TableRow';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultHeadRowStyles = {
   root: {},
 };
 
-class MUIDataTableHeadRow extends React.Component {
+class TableHeadRow extends React.Component {
   static propTypes = {
     /** Extend the style applied to components */
     classes: PropTypes.object,
@@ -28,4 +28,4 @@ class MUIDataTableHeadRow extends React.Component {
   }
 }
 
-export default withStyles(defaultHeadRowStyles, { name: "MUIDataTableHeadRow" })(MUIDataTableHeadRow);
+export default withStyles(defaultHeadRowStyles, { name: 'MUIDataTableHeadRow' })(TableHeadRow);
diff --git a/src/MUIDataTablePagination.js b/src/components/TablePagination.js
similarity index 66%
rename from src/MUIDataTablePagination.js
rename to src/components/TablePagination.js
index 731cb3f..870d6fa 100644
--- a/src/MUIDataTablePagination.js
+++ b/src/components/TablePagination.js
@@ -1,31 +1,31 @@
-import React from "react";
-import PropTypes from "prop-types";
-import TableRow from "@material-ui/core/TableRow";
-import TableFooter from "@material-ui/core/TableFooter";
-import TablePagination from "@material-ui/core/TablePagination";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import MuiTableRow from '@material-ui/core/TableRow';
+import MuiTableFooter from '@material-ui/core/TableFooter';
+import MuiTablePagination from '@material-ui/core/TablePagination';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultPaginationStyles = {
   root: {
-    "&:last-child": {
-      padding: "0px 24px 0px 24px",
+    '&:last-child': {
+      padding: '0px 24px 0px 24px',
     },
   },
   toolbar: {},
   selectRoot: {},
-  "@media screen and (max-width: 400px)": {
+  '@media screen and (max-width: 400px)': {
     toolbar: {
-      "& span:nth-child(2)": {
-        display: "none",
+      '& span:nth-child(2)': {
+        display: 'none',
       },
     },
     selectRoot: {
-      marginRight: "8px",
+      marginRight: '8px',
     },
   },
 };
 
-class MUIDataTablePagination extends React.Component {
+class TablePagination extends React.Component {
   static propTypes = {
     /** Total number of table rows */
     count: PropTypes.number.isRequired,
@@ -53,9 +53,9 @@ class MUIDataTablePagination extends React.Component {
     const textLabels = options.textLabels.pagination;
 
     return (
-      <TableFooter>
-        <TableRow>
-          <TablePagination
+      <MuiTableFooter>
+        <MuiTableRow>
+          <MuiTablePagination
             className={classes.root}
             classes={{
               caption: classes.caption,
@@ -68,19 +68,19 @@ class MUIDataTablePagination extends React.Component {
             labelRowsPerPage={textLabels.rowsPerPage}
             labelDisplayedRows={({ from, to, count }) => `${from}-${to} ${textLabels.displayRows} ${count}`}
             backIconButtonProps={{
-              "aria-label": textLabels.previous,
+              'aria-label': textLabels.previous,
             }}
             nextIconButtonProps={{
-              "aria-label": textLabels.next,
+              'aria-label': textLabels.next,
             }}
             rowsPerPageOptions={options.rowsPerPageOptions}
             onChangePage={this.handlePageChange}
             onChangeRowsPerPage={this.handleRowChange}
           />
-        </TableRow>
-      </TableFooter>
+        </MuiTableRow>
+      </MuiTableFooter>
     );
   }
 }
 
-export default withStyles(defaultPaginationStyles, { name: "MUIDataTablePagination" })(MUIDataTablePagination);
+export default withStyles(defaultPaginationStyles, { name: 'MUIDataTablePagination' })(TablePagination);
diff --git a/src/MUIDataTableResize.js b/src/components/TableResize.js
similarity index 78%
rename from src/MUIDataTableResize.js
rename to src/components/TableResize.js
index ae1552f..75e3bc8 100644
--- a/src/MUIDataTableResize.js
+++ b/src/components/TableResize.js
@@ -1,24 +1,24 @@
-import React from "react";
-import PropTypes from "prop-types";
-import classNames from "classnames";
-import { findDOMNode } from "react-dom";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { findDOMNode } from 'react-dom';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultResizeStyles = {
   root: {
-    position: "absolute",
+    position: 'absolute',
   },
   resizer: {
-    position: "absolute",
-    width: "1px",
-    height: "100%",
-    left: "100px",
-    cursor: "ew-resize",
-    border: "0.1px solid rgba(224, 224, 224, 1)",
+    position: 'absolute',
+    width: '1px',
+    height: '100%',
+    left: '100px',
+    cursor: 'ew-resize',
+    border: '0.1px solid rgba(224, 224, 224, 1)',
   },
 };
 
-class MUIDataTableResize extends React.Component {
+class TableResize extends React.Component {
   static propTypes = {
     /** Extend the style applied to components */
     classes: PropTypes.object,
@@ -27,8 +27,8 @@ class MUIDataTableResize extends React.Component {
   state = {
     resizeCoords: {},
     startPosition: 0,
-    tableWidth: "100%",
-    tableHeight: "100%",
+    tableWidth: '100%',
+    tableHeight: '100%',
   };
 
   handleReize = () => {
@@ -41,11 +41,11 @@ class MUIDataTableResize extends React.Component {
   componentDidMount() {
     this.windowWidth = null;
     this.props.setResizeable(this.setCellRefs);
-    window.addEventListener("resize", this.handleReize, false);
+    window.addEventListener('resize', this.handleReize, false);
   }
 
   componentWillUnmount() {
-    window.removeEventListener("resize", this.handleReize, false);
+    window.removeEventListener('resize', this.handleReize, false);
   }
 
   setCellRefs = (cellsRef, tableRef) => {
@@ -83,7 +83,7 @@ class MUIDataTableResize extends React.Component {
       lastPosition = item.left;
 
       const thCell = this.cellsRef[key];
-      thCell.style.width = newWidth + "%";
+      thCell.style.width = newWidth + '%';
     });
   };
 
@@ -122,8 +122,8 @@ class MUIDataTableResize extends React.Component {
               onMouseMove={this.onResizeMove.bind(null, key)}
               onMouseUp={this.onResizeEnd.bind(null, key)}
               style={{
-                width: isResize && id == key ? tableWidth : "auto",
-                position: "absolute",
+                width: isResize && id == key ? tableWidth : 'auto',
+                position: 'absolute',
                 height: tableHeight,
                 zIndex: 1000,
               }}>
@@ -141,4 +141,4 @@ class MUIDataTableResize extends React.Component {
   }
 }
 
-export default withStyles(defaultResizeStyles, { name: "MUIDataTableResize" })(MUIDataTableResize);
+export default withStyles(defaultResizeStyles, { name: 'MUIDataTableResize' })(TableResize);
diff --git a/src/MUIDataTableSearch.js b/src/components/TableSearch.js
similarity index 58%
rename from src/MUIDataTableSearch.js
rename to src/components/TableSearch.js
index 771a99b..72e461f 100644
--- a/src/MUIDataTableSearch.js
+++ b/src/components/TableSearch.js
@@ -1,31 +1,31 @@
-import React from "react";
-import Grow from "@material-ui/core/Grow";
-import TextField from "@material-ui/core/TextField";
-import SearchIcon from "@material-ui/icons/Search";
-import IconButton from "@material-ui/core/IconButton";
-import ClearIcon from "@material-ui/icons/Clear";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import Grow from '@material-ui/core/Grow';
+import TextField from '@material-ui/core/TextField';
+import SearchIcon from '@material-ui/icons/Search';
+import IconButton from '@material-ui/core/IconButton';
+import ClearIcon from '@material-ui/icons/Clear';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultSearchStyles = {
   main: {
-    display: "flex",
-    flex: "1 0 auto",
+    display: 'flex',
+    flex: '1 0 auto',
   },
   searchIcon: {
-    marginTop: "10px",
-    marginRight: "8px",
+    marginTop: '10px',
+    marginRight: '8px',
   },
   searchText: {
-    flex: "0.8 0",
+    flex: '0.8 0',
   },
   clearIcon: {
-    "&:hover": {
-      color: "#FF0000",
+    '&:hover': {
+      color: '#FF0000',
     },
   },
 };
 
-class MUIDataTableSearch extends React.Component {
+class TableSearch extends React.Component {
   handleTextChange = event => {
     const { onSearchChange } = this.props.options;
 
@@ -37,11 +37,11 @@ class MUIDataTableSearch extends React.Component {
   };
 
   componentDidMount() {
-    document.addEventListener("keydown", this.onKeyDown, false);
+    document.addEventListener('keydown', this.onKeyDown, false);
   }
 
   componentWillUnmount() {
-    document.removeEventListener("keydown", this.onKeyDown, false);
+    document.removeEventListener('keydown', this.onKeyDown, false);
   }
 
   onKeyDown = event => {
@@ -61,7 +61,7 @@ class MUIDataTableSearch extends React.Component {
             className={classes.searchText}
             autoFocus={true}
             InputProps={{
-              "aria-label": options.textLabels.toolbar.search,
+              'aria-label': options.textLabels.toolbar.search,
             }}
             onChange={this.handleTextChange}
             fullWidth={true}
@@ -76,4 +76,4 @@ class MUIDataTableSearch extends React.Component {
   }
 }
 
-export default withStyles(defaultSearchStyles, { name: "MUIDataTableSearch" })(MUIDataTableSearch);
+export default withStyles(defaultSearchStyles, { name: 'MUIDataTableSearch' })(TableSearch);
diff --git a/src/components/TableSelectCell.js b/src/components/TableSelectCell.js
new file mode 100644
index 0000000..f64d3f1
--- /dev/null
+++ b/src/components/TableSelectCell.js
@@ -0,0 +1,95 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import Checkbox from '@material-ui/core/Checkbox';
+import TableCell from '@material-ui/core/TableCell';
+import { withStyles } from '@material-ui/core/styles';
+import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight';
+
+const defaultSelectCellStyles = theme => ({
+  root: {
+    [theme.breakpoints.down('sm')]: {
+      display: 'none',
+    },
+  },
+  fixedHeader: {
+    position: 'sticky',
+    top: '0px',
+    left: '0px',
+    zIndex: 100,
+  },
+  icon: {
+    cursor: 'pointer',
+    transition: 'transform 0.25s',
+  },
+  expanded: {
+    transform: 'rotate(90deg)',
+  },
+  hide: {
+    visibility: 'hidden',
+  },
+  headerCell: {
+    zIndex: 110,
+    backgroundColor: '#FFF',
+  },
+  checkboxRoot: {
+    '&$checked': {
+      color: '#027cb5',
+    },
+  },
+  checked: {},
+  disabled: {},
+});
+
+class TableSelectCell extends React.Component {
+  static propTypes = {
+    /** Select cell checked on/off */
+    checked: PropTypes.bool.isRequired,
+    /** Select cell part of fixed header */
+    fixedHeader: PropTypes.bool.isRequired,
+    /** Callback to trigger cell update */
+    onChange: PropTypes.func,
+    /** Extend the style applied to components */
+    classes: PropTypes.object,
+  };
+
+  static defaultProps = {
+    isHeaderCell: false,
+    isExpandable: false,
+    isRowExpanded: false,
+  };
+
+  render() {
+    const { classes, fixedHeader, isHeaderCell, isExpandable, isRowExpanded, onExpand, ...otherProps } = this.props;
+
+    const cellClass = classNames({
+      [classes.root]: true,
+      [classes.fixedHeader]: fixedHeader,
+      [classes.headerCell]: isHeaderCell,
+    });
+
+    const iconClass = classNames({
+      [classes.icon]: true,
+      [classes.hide]: isHeaderCell,
+      [classes.expanded]: isRowExpanded,
+    });
+
+    return (
+      <TableCell className={cellClass} padding="checkbox">
+        <div style={{ display: 'flex', alignItems: 'center' }}>
+          {isExpandable && <KeyboardArrowRight className={iconClass} onClick={onExpand} />}
+          <Checkbox
+            classes={{
+              root: classes.checkboxRoot,
+              checked: classes.checked,
+              disabled: classes.disabled,
+            }}
+            {...otherProps}
+          />
+        </div>
+      </TableCell>
+    );
+  }
+}
+
+export default withStyles(defaultSelectCellStyles, { name: 'MUIDataTableSelectCell' })(TableSelectCell);
diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js
new file mode 100644
index 0000000..c229e26
--- /dev/null
+++ b/src/components/TableToolbar.js
@@ -0,0 +1,240 @@
+import React from 'react';
+import Typography from '@material-ui/core/Typography';
+import Toolbar from '@material-ui/core/Toolbar';
+import Tooltip from '@material-ui/core/Tooltip';
+import IconButton from '@material-ui/core/IconButton';
+import Popover from './Popover';
+import TableFilter from './TableFilter';
+import TableViewCol from './TableViewCol';
+import TableSearch from './TableSearch';
+import SearchIcon from '@material-ui/icons/Search';
+import DownloadIcon from '@material-ui/icons/CloudDownload';
+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 { createCSVDownload } from '../utils';
+
+export const defaultToolbarStyles = (theme, props) => ({
+  root: {},
+  left: {
+    flex: '1 1 55%',
+  },
+  actions: {
+    flex: '0 0 45%',
+    textAlign: 'right',
+  },
+  titleRoot: {},
+  titleText: {},
+  icon: {
+    '&:hover': {
+      color: '#307BB0',
+    },
+  },
+  iconActive: {
+    color: '#307BB0',
+  },
+  searchIcon: {
+    display: 'inline-flex',
+    marginTop: '10px',
+    marginRight: '8px',
+  },
+  ...(props.options.responsive ? { ...responsiveToolbarStyles(theme) } : {}),
+});
+
+export const responsiveToolbarStyles = theme => ({
+  [theme.breakpoints.down('sm')]: {
+    titleRoot: {},
+    titleText: {
+      fontSize: '16px',
+    },
+    spacer: {
+      display: 'none',
+    },
+    left: {
+      // flex: "1 1 40%",
+      padding: '8px 0px',
+    },
+    actions: {
+      // flex: "1 1 60%",
+      textAlign: 'right',
+    },
+  },
+  [theme.breakpoints.down('xs')]: {
+    root: {
+      display: 'block',
+    },
+    left: {
+      padding: '8px 0px 0px 0px',
+    },
+    titleText: {
+      textAlign: 'center',
+    },
+    actions: {
+      textAlign: 'center',
+    },
+  },
+  '@media screen and (max-width: 480px)': {},
+});
+
+class TableToolbar extends React.Component {
+  state = {
+    iconActive: null,
+    showSearch: false,
+  };
+
+  handleCSVDownload = () => {
+    const { data, columns, options } = this.props;
+    createCSVDownload(columns, data, options);
+  };
+
+  setActiveIcon = iconName => {
+    this.setState(() => ({
+      iconActive: iconName,
+      showSearch: iconName === 'search' ? this.showSearch() : false,
+    }));
+  };
+
+  getActiveIcon = (styles, iconName) => {
+    return this.state.iconActive !== iconName ? styles.icon : styles.iconActive;
+  };
+
+  showSearch = () => {
+    !!this.props.options.onSearchOpen && this.props.options.onSearchOpen();
+    this.props.setTableAction('onSearchOpen');
+    return true;
+  };
+
+  hideSearch = () => {
+    const { onSearchClose } = this.props.options;
+
+    if (onSearchClose) onSearchClose();
+    this.props.searchTextUpdate(null);
+
+    this.setState(() => ({
+      iconActive: null,
+      showSearch: false,
+    }));
+
+    this.searchButton.focus();
+  };
+
+  render() {
+    const {
+      data,
+      options,
+      classes,
+      columns,
+      filterData,
+      filterList,
+      filterUpdate,
+      resetFilters,
+      searchTextUpdate,
+      toggleViewColumn,
+      title,
+      tableRef,
+    } = this.props;
+
+    const { search, downloadCsv, print, viewColumns, filterTable } = options.textLabels.toolbar;
+    const { showSearch } = this.state;
+
+    return (
+      <Toolbar className={classes.root} role={'toolbar'} aria-label={'Table Toolbar'}>
+        <div className={classes.left}>
+          {showSearch === true ? (
+            <TableSearch onSearch={searchTextUpdate} onHide={this.hideSearch} options={options} />
+          ) : (
+            <div className={classes.titleRoot} aria-hidden={'true'}>
+              <Typography variant="h6" className={classes.titleText}>
+                {title}
+              </Typography>
+            </div>
+          )}
+        </div>
+        <div className={classes.actions}>
+          {options.search && (
+            <Tooltip title={search}>
+              <IconButton
+                aria-label={search}
+                buttonRef={el => (this.searchButton = el)}
+                classes={{ root: this.getActiveIcon(classes, 'search') }}
+                onClick={this.setActiveIcon.bind(null, 'search')}>
+                <SearchIcon />
+              </IconButton>
+            </Tooltip>
+          )}
+          {options.download && (
+            <Tooltip title={downloadCsv}>
+              <IconButton aria-label={downloadCsv} classes={{ root: classes.icon }} onClick={this.handleCSVDownload}>
+                <DownloadIcon />
+              </IconButton>
+            </Tooltip>
+          )}
+          {options.print && (
+            <Tooltip title={print}>
+              <span>
+                <ReactToPrint
+                  trigger={() => (
+                    <IconButton aria-label={print} classes={{ root: classes.icon }}>
+                      <PrintIcon />
+                    </IconButton>
+                  )}
+                  content={() => this.props.tableRef()}
+                />
+              </span>
+            </Tooltip>
+          )}
+          {options.viewColumns && (
+            <Popover
+              refExit={this.setActiveIcon.bind(null)}
+              container={tableRef}
+              trigger={
+                <IconButton
+                  aria-label={viewColumns}
+                  classes={{ root: this.getActiveIcon(classes, 'viewcolumns') }}
+                  onClick={this.setActiveIcon.bind(null, 'viewcolumns')}>
+                  <Tooltip title={viewColumns}>
+                    <ViewColumnIcon />
+                  </Tooltip>
+                </IconButton>
+              }
+              content={
+                <TableViewCol data={data} columns={columns} options={options} onColumnUpdate={toggleViewColumn} />
+              }
+            />
+          )}
+          {options.filter && (
+            <Popover
+              refExit={this.setActiveIcon.bind(null)}
+              container={tableRef}
+              trigger={
+                <IconButton
+                  aria-label={filterTable}
+                  classes={{ root: this.getActiveIcon(classes, 'filter') }}
+                  onClick={this.setActiveIcon.bind(null, 'filter')}>
+                  <Tooltip title={filterTable}>
+                    <FilterIcon />
+                  </Tooltip>
+                </IconButton>
+              }
+              content={
+                <TableFilter
+                  columns={columns}
+                  options={options}
+                  filterList={filterList}
+                  filterData={filterData}
+                  onFilterUpdate={filterUpdate}
+                  onFilterReset={resetFilters}
+                />
+              }
+            />
+          )}
+          {options.customToolbar && options.customToolbar()}
+        </div>
+      </Toolbar>
+    );
+  }
+}
+
+export default styled(TableToolbar)(defaultToolbarStyles, { name: 'MUIDataTableToolbar' });
diff --git a/src/MUIDataTableToolbarSelect.js b/src/components/TableToolbarSelect.js
similarity index 61%
rename from src/MUIDataTableToolbarSelect.js
rename to src/components/TableToolbarSelect.js
index 7db8d63..536d260 100644
--- a/src/MUIDataTableToolbarSelect.js
+++ b/src/components/TableToolbarSelect.js
@@ -1,39 +1,41 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Paper from "@material-ui/core/Paper";
-import IconButton from "@material-ui/core/IconButton";
-import Tooltip from "@material-ui/core/Tooltip";
-import Typography from "@material-ui/core/Typography";
-import DeleteIcon from "@material-ui/icons/Delete";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import Paper from '@material-ui/core/Paper';
+import IconButton from '@material-ui/core/IconButton';
+import Tooltip from '@material-ui/core/Tooltip';
+import Typography from '@material-ui/core/Typography';
+import DeleteIcon from '@material-ui/icons/Delete';
+import { withStyles } from '@material-ui/core/styles';
 
 const defaultToolbarSelectStyles = {
   root: {
-    backgroundColor: "#f7f7f7",
-    flex: "1 1 100%",
-    display: "flex",
-    height: "64px",
-    justifyContent: "space-between",
+    backgroundColor: '#f7f7f7',
+    flex: '1 1 100%',
+    display: 'flex',
+    height: '64px',
+    position: 'relative',
+    zIndex: 120,
+    justifyContent: 'space-between',
   },
   title: {
-    paddingLeft: "26px",
-    top: "50%",
-    position: "relative",
-    transform: "translateY(-50%)",
+    paddingLeft: '26px',
+    top: '50%',
+    position: 'relative',
+    transform: 'translateY(-50%)',
   },
   iconButton: {
-    marginRight: "24px",
-    top: "50%",
-    display: "block",
-    position: "relative",
-    transform: "translateY(-50%)",
+    marginRight: '24px',
+    top: '50%',
+    display: 'block',
+    position: 'relative',
+    transform: 'translateY(-50%)',
   },
   deleteIcon: {
-    color: "#000",
+    color: '#000',
   },
 };
 
-class MUIDataTableToolbarSelect extends React.Component {
+class TableToolbarSelect extends React.Component {
   static propTypes = {
     /** Options used to describe table */
     options: PropTypes.object.isRequired,
@@ -53,11 +55,11 @@ class MUIDataTableToolbarSelect extends React.Component {
       throw new TypeError(`"selectedRows" must be an "array", but it's "${typeof selectedRows}"`);
     }
 
-    if (selectedRows.some(row => typeof row !== "number")) {
+    if (selectedRows.some(row => typeof row !== 'number')) {
       throw new TypeError(`Array "selectedRows" must contain only numbers`);
     }
 
-    this.props.selectRowUpdate("custom", selectedRows);
+    this.props.selectRowUpdate('custom', selectedRows);
   };
 
   render() {
@@ -85,4 +87,4 @@ class MUIDataTableToolbarSelect extends React.Component {
   }
 }
 
-export default withStyles(defaultToolbarSelectStyles, { name: "MUIDataTableToolbarSelect" })(MUIDataTableToolbarSelect);
+export default withStyles(defaultToolbarSelectStyles, { name: 'MUIDataTableToolbarSelect' })(TableToolbarSelect);
diff --git a/src/MUIDataTableViewCol.js b/src/components/TableViewCol.js
similarity index 63%
rename from src/MUIDataTableViewCol.js
rename to src/components/TableViewCol.js
index 1b57267..5d72339 100644
--- a/src/MUIDataTableViewCol.js
+++ b/src/components/TableViewCol.js
@@ -1,47 +1,47 @@
-import React from "react";
-import PropTypes from "prop-types";
-import Checkbox from "@material-ui/core/Checkbox";
-import Typography from "@material-ui/core/Typography";
-import FormControl from "@material-ui/core/FormControl";
-import FormGroup from "@material-ui/core/FormGroup";
-import FormControlLabel from "@material-ui/core/FormControlLabel";
-import { withStyles } from "@material-ui/core/styles";
+import React from 'react';
+import PropTypes from 'prop-types';
+import Checkbox from '@material-ui/core/Checkbox';
+import Typography from '@material-ui/core/Typography';
+import FormControl from '@material-ui/core/FormControl';
+import FormGroup from '@material-ui/core/FormGroup';
+import FormControlLabel from '@material-ui/core/FormControlLabel';
+import { withStyles } from '@material-ui/core/styles';
 
 export const defaultViewColStyles = {
   root: {
-    padding: "16px 24px 16px 24px",
-    fontFamily: "Roboto",
+    padding: '16px 24px 16px 24px',
+    fontFamily: 'Roboto',
   },
   title: {
-    marginLeft: "-7px",
-    fontSize: "14px",
-    color: "#424242",
-    textAlign: "left",
+    marginLeft: '-7px',
+    fontSize: '14px',
+    color: '#424242',
+    textAlign: 'left',
     fontWeight: 500,
   },
   formGroup: {
-    marginTop: "8px",
+    marginTop: '8px',
   },
   formControl: {},
   checkbox: {
-    padding: "0px",
-    width: "32px",
-    height: "32px",
+    padding: '0px',
+    width: '32px',
+    height: '32px',
   },
   checkboxRoot: {
-    "&$checked": {
-      color: "#027cb5",
+    '&$checked': {
+      color: '#027cb5',
     },
   },
   checked: {},
   label: {
-    fontSize: "15px",
-    marginLeft: "8px",
-    color: "#4a4a4a",
+    fontSize: '15px',
+    marginLeft: '8px',
+    color: '#4a4a4a',
   },
 };
 
-class MUIDataTableViewCol extends React.Component {
+class TableViewCol extends React.Component {
   static propTypes = {
     /** Columns used to describe table */
     columns: PropTypes.array.isRequired,
@@ -62,14 +62,14 @@ class MUIDataTableViewCol extends React.Component {
     const textLabels = options.textLabels.viewColumns;
 
     return (
-      <FormControl component={"fieldset"} className={classes.root} aria-label={textLabels.titleAria}>
+      <FormControl component={'fieldset'} className={classes.root} aria-label={textLabels.titleAria}>
         <Typography variant="caption" className={classes.title}>
           {textLabels.title}
         </Typography>
         <FormGroup className={classes.formGroup}>
           {columns.map((column, index) => {
             return (
-              column.display !== "excluded" && (
+              column.display !== 'excluded' && (
                 <FormControlLabel
                   key={index}
                   classes={{
@@ -84,7 +84,7 @@ class MUIDataTableViewCol extends React.Component {
                         checked: classes.checked,
                       }}
                       onChange={this.handleColChange.bind(null, index)}
-                      checked={column.display === "true"}
+                      checked={column.display === 'true'}
                       value={column.name}
                     />
                   }
@@ -99,4 +99,4 @@ class MUIDataTableViewCol extends React.Component {
   }
 }
 
-export default withStyles(defaultViewColStyles, { name: "MUIDataTableViewCol" })(MUIDataTableViewCol);
+export default withStyles(defaultViewColStyles, { name: 'MUIDataTableViewCol' })(TableViewCol);
diff --git a/src/index.js b/src/index.js
index d061642..b1b6d28 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,3 +1,3 @@
-import MUIDataTable from "./MUIDataTable";
+import MUIDataTable from './MUIDataTable';
 
 export default MUIDataTable;
diff --git a/src/styled.js b/src/styled.js
index 14c59fa..543db2f 100644
--- a/src/styled.js
+++ b/src/styled.js
@@ -1,7 +1,7 @@
-import React from "react";
-import PropTypes from "prop-types";
-import merge from "lodash.merge";
-import { withStyles } from "@material-ui/core/styles";
+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()
@@ -11,7 +11,7 @@ import { withStyles } from "@material-ui/core/styles";
  */
 
 const styles = (theme, props, style) => {
-  return typeof style === "function" ? style(theme, props) : style;
+  return typeof style === 'function' ? style(theme, props) : style;
 };
 
 class StyledComponent extends React.Component {
@@ -21,7 +21,7 @@ class StyledComponent extends React.Component {
   };
 
   render() {
-    const { classes, className = "", WrappedComponent, ...passThroughProps } = this.props;
+    const { classes, className = '', WrappedComponent, ...passThroughProps } = this.props;
 
     return <WrappedComponent classes={classes} className={className} {...passThroughProps} />;
   }
diff --git a/src/textLabels.js b/src/textLabels.js
index 8d342a7..a2df615 100644
--- a/src/textLabels.js
+++ b/src/textLabels.js
@@ -3,35 +3,35 @@
  */
 const textLabels = {
   body: {
-    noMatch: "Sorry, no matching records found",
-    toolTip: "Sort",
+    noMatch: 'Sorry, no matching records found',
+    toolTip: 'Sort',
   },
   pagination: {
-    next: "Next Page",
-    previous: "Previous Page",
-    rowsPerPage: "Rows per page:",
-    displayRows: "of",
+    next: 'Next Page',
+    previous: 'Previous Page',
+    rowsPerPage: 'Rows per page:',
+    displayRows: 'of',
   },
   toolbar: {
-    search: "Search",
-    downloadCsv: "Download CSV",
-    print: "Print",
-    viewColumns: "View Columns",
-    filterTable: "Filter Table",
+    search: 'Search',
+    downloadCsv: 'Download CSV',
+    print: 'Print',
+    viewColumns: 'View Columns',
+    filterTable: 'Filter Table',
   },
   filter: {
-    all: "All",
-    title: "FILTERS",
-    reset: "RESET",
+    all: 'All',
+    title: 'FILTERS',
+    reset: 'RESET',
   },
   viewColumns: {
-    title: "Show Columns",
-    titleAria: "Show/Hide Table Columns",
+    title: 'Show Columns',
+    titleAria: 'Show/Hide Table Columns',
   },
   selectedRows: {
-    text: "row(s) selected",
-    delete: "Delete",
-    deleteAria: "Delete Selected Rows",
+    text: 'row(s) selected',
+    delete: 'Delete',
+    deleteAria: 'Delete Selected Rows',
   },
 };
 
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 0000000..54aa8be
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,71 @@
+function buildMap(rows) {
+  return rows.reduce((accum, { dataIndex }) => {
+    accum[dataIndex] = true;
+    return accum;
+  }, {});
+}
+
+function getCollatorComparator() {
+  if (!!Intl) {
+    const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' });
+    return collator.compare;
+  }
+
+  const fallbackComparator = (a, b) => a.localeCompare(b);
+  return fallbackComparator;
+}
+
+function sortCompare(order) {
+  return (a, b) => {
+    if (a.data === null) a.data = '';
+    if (b.data === null) b.data = '';
+    return (
+      (typeof a.data.localeCompare === 'function' ? a.data.localeCompare(b.data) : a.data - b.data) *
+      (order === 'asc' ? -1 : 1)
+    );
+  };
+}
+
+function createCSVDownload(columns, data, options) {
+  const CSVHead =
+    columns
+      .reduce(
+        (soFar, column) =>
+          column.download ? soFar + '"' + column.name + '"' + options.downloadOptions.separator : soFar,
+        '',
+      )
+      .slice(0, -1) + '\r\n';
+
+  const CSVBody = data
+    .reduce(
+      (soFar, row) =>
+        soFar +
+        '"' +
+        row.data.filter((field, index) => columns[index].download).join('"' + options.downloadOptions.separator + '"') +
+        '"\r\n',
+      [],
+    )
+    .trim();
+
+  const csv = `${CSVHead}${CSVBody}`;
+  const blob = new Blob([csv], { type: 'text/csv' });
+
+  /* taken from react-csv */
+  if (navigator && navigator.msSaveOrOpenBlob) {
+    navigator.msSaveOrOpenBlob(blob, options.downloadOptions.filename);
+  } else {
+    const dataURI = `data:text/csv;charset=utf-8,${csv}`;
+
+    const URL = window.URL || window.webkitURL;
+    const downloadURI = typeof URL.createObjectURL === 'undefined' ? dataURI : URL.createObjectURL(blob);
+
+    let link = document.createElement('a');
+    link.setAttribute('href', downloadURI);
+    link.setAttribute('download', options.downloadOptions.filename);
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  }
+}
+
+export { buildMap, getCollatorComparator, sortCompare, createCSVDownload };
diff --git a/webpack.config.js b/webpack.config.js
index ad79743..001e1b0 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,7 +3,7 @@ const webpack = require('webpack');
 
 module.exports = {
   entry: {
-    app: "./examples/fixed-header/index.js"
+    app: "./examples/expandable-rows/index.js"
   },
   stats: "verbose",
   context: __dirname,


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 2e8798d..377c988 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -1,15 +1,16 @@
-import React from "react";
-import { spy } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect } from "chai";
-import MUIDataTable from "../src/MUIDataTable";
-import MUIDataTableFilterList from "../src/MUIDataTableFilterList";
-import MUIDataTablePagination from "../src/MUIDataTablePagination";
-import textLabels from "../src/textLabels";
-import Chip from "@material-ui/core/Chip";
-import Cities from "../examples/component/cities";
-
-describe("<MUIDataTable />", function() {
+import React from 'react';
+import { spy } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect } from 'chai';
+import MUIDataTable from '../src/MUIDataTable';
+import TableFilterList from '../src/components/TableFilterList';
+import TablePagination from '../src/components/TablePagination';
+import textLabels from '../src/textLabels';
+import Chip from '@material-ui/core/Chip';
+import Cities from '../examples/component/cities';
+import { getCollatorComparator } from '../src/utils';
+
+describe('<MUIDataTable />', function() {
   let data;
   let displayData;
   let columns;
@@ -17,99 +18,99 @@ describe("<MUIDataTable />", function() {
   let renderCities = (value, tableMeta, updateValueFn) => (
     <Cities value={value} index={tableMeta.rowIndex} change={event => updateValueFn(event)} />
   );
-  let renderName = value => value.split(" ")[1] + ", " + value.split(" ")[0];
+  let renderName = value => value.split(' ')[1] + ', ' + value.split(' ')[0];
 
   before(() => {
     columns = [
-      { name: "Name", options: { customBodyRender: renderName } },
-      "Company",
-      { name: "City", options: { customBodyRender: renderCities } },
-      { name: "State" },
+      { name: 'Name', options: { customBodyRender: renderName } },
+      'Company',
+      { name: 'City', options: { customBodyRender: renderCities } },
+      { name: 'State' },
     ];
     data = [
-      ["Joe James", "Test Corp", "Yonkers", "NY"],
-      ["John Walsh", "Test Corp", "Hartford", "CT"],
-      ["Bob Herm", "Test Corp", "Tampa", "FL"],
-      ["James Houston", "Test Corp", "Dallas", "TX"],
+      ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
+      ['John Walsh', 'Test Corp', 'Hartford', 'CT'],
+      ['Bob Herm', 'Test Corp', 'Tampa', 'FL'],
+      ['James Houston', 'Test Corp', 'Dallas', 'TX'],
     ];
     // internal table data built from source data provided
     displayData = JSON.stringify([
       {
-        data: ["James, Joe", "Test Corp", renderCities("Yonkers", { rowIndex: 0 }), "NY"],
+        data: ['James, Joe', 'Test Corp', renderCities('Yonkers', { rowIndex: 0 }), 'NY'],
         dataIndex: 0,
       },
       {
-        data: ["Walsh, John", "Test Corp", renderCities("Hartford", { rowIndex: 1 }), "CT"],
+        data: ['Walsh, John', 'Test Corp', renderCities('Hartford', { rowIndex: 1 }), 'CT'],
         dataIndex: 1,
       },
       {
-        data: ["Herm, Bob", "Test Corp", renderCities("Tampa", { rowIndex: 2 }), "FL"],
+        data: ['Herm, Bob', 'Test Corp', renderCities('Tampa', { rowIndex: 2 }), 'FL'],
         dataIndex: 2,
       },
       {
-        data: ["Houston, James", "Test Corp", renderCities("Dallas", { rowIndex: 3 }), "TX"],
+        data: ['Houston, James', 'Test Corp', renderCities('Dallas', { rowIndex: 3 }), 'TX'],
         dataIndex: 3,
       },
     ]);
     tableData = [
-      { index: 0, data: ["James, Joe", "Test Corp", renderCities("Yonkers", { rowIndex: 0 }), "NY"] },
-      { index: 1, data: ["Walsh, John", "Test Corp", renderCities("Hartford", { rowIndex: 1 }), "CT"] },
-      { index: 2, data: ["Herm, Bob", "Test Corp", renderCities("Tampa", { rowIndex: 2 }), "FL"] },
-      { index: 3, data: ["Houston, James", "Test Corp", renderCities("Dallas", { rowIndex: 3 }), "TX"] },
+      { index: 0, data: ['James, Joe', 'Test Corp', renderCities('Yonkers', { rowIndex: 0 }), 'NY'] },
+      { index: 1, data: ['Walsh, John', 'Test Corp', renderCities('Hartford', { rowIndex: 1 }), 'CT'] },
+      { index: 2, data: ['Herm, Bob', 'Test Corp', renderCities('Tampa', { rowIndex: 2 }), 'FL'] },
+      { index: 3, data: ['Houston, James', 'Test Corp', renderCities('Dallas', { rowIndex: 3 }), 'TX'] },
     ];
     renderCities = renderCities;
     renderName = renderName;
   });
 
-  it("should render a table", () => {
+  it('should render a table', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     assert.strictEqual(
       shallowWrapper
         .dive()
         .dive()
         .name(),
-      "Paper",
+      'Paper',
     );
   });
 
-  it("should correctly build internal columns data structure", () => {
+  it('should correctly build internal columns data structure', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const actualResult = shallowWrapper.dive().state().columns;
 
     const expectedResult = [
       {
-        display: "true",
-        name: "Name",
+        display: 'true',
+        name: 'Name',
         sort: true,
         filter: true,
         download: true,
         sortDirection: null,
         customBodyRender: renderName,
       },
-      { display: "true", name: "Company", sort: true, filter: true, download: true, sortDirection: null },
+      { display: 'true', name: 'Company', sort: true, filter: true, download: true, sortDirection: null },
       {
-        display: "true",
-        name: "City",
+        display: 'true',
+        name: 'City',
         sort: true,
         filter: true,
         download: true,
         sortDirection: null,
         customBodyRender: renderCities,
       },
-      { display: "true", name: "State", sort: true, filter: true, download: true, sortDirection: null },
+      { display: 'true', name: 'State', sort: true, filter: true, download: true, sortDirection: null },
     ];
 
     assert.deepEqual(actualResult, expectedResult);
   });
 
-  it("should correctly build internal table data and displayData structure", () => {
+  it('should correctly build internal table data and displayData structure', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const state = shallowWrapper.dive().state();
     //assert.deepEqual(state.data, data);
     assert.deepEqual(JSON.stringify(state.displayData), displayData);
   });
 
-  it("should correctly re-build internal table data and displayData structure with prop change", () => {
+  it('should correctly re-build internal table data and displayData structure with prop change', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     let state = shallowWrapper.dive().state();
 
@@ -117,23 +118,23 @@ describe("<MUIDataTable />", function() {
 
     // now use updated props
     let newData = data.map(item => [...item]);
-    newData[0][0] = "testing";
+    newData[0][0] = 'testing';
     shallowWrapper.setProps({ data: newData });
     shallowWrapper.update();
 
     state = shallowWrapper.dive().state();
     const expectedResult = [
-      { index: 0, data: ["testing", "Test Corp", "Yonkers", "NY"] },
-      { index: 1, data: ["John Walsh", "Test Corp", "Hartford", "CT"] },
-      { index: 2, data: ["Bob Herm", "Test Corp", "Tampa", "FL"] },
-      { index: 3, data: ["James Houston", "Test Corp", "Dallas", "TX"] },
+      { index: 0, data: ['testing', 'Test Corp', 'Yonkers', 'NY'] },
+      { index: 1, data: ['John Walsh', 'Test Corp', 'Hartford', 'CT'] },
+      { index: 2, data: ['Bob Herm', 'Test Corp', 'Tampa', 'FL'] },
+      { index: 3, data: ['James Houston', 'Test Corp', 'Dallas', 'TX'] },
     ];
 
     assert.deepEqual(state.data, expectedResult);
   });
 
-  it("should not re-build internal table data and displayData structure with no prop change to data or columns", () => {
-    const initializeTableSpy = spy(MUIDataTable.Naked.prototype, "initializeTable");
+  it('should not re-build internal table data and displayData structure with no prop change to data or columns', () => {
+    const initializeTableSpy = spy(MUIDataTable.Naked.prototype, 'initializeTable');
     const mountWrapper = mount(shallow(<MUIDataTable columns={columns} data={data} />).get(0));
 
     let state = mountWrapper.state();
@@ -149,7 +150,7 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(initializeTableSpy.callCount, 1);
   });
 
-  it("should correctly build internal filterList structure", () => {
+  it('should correctly build internal filterList structure', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const state = shallowWrapper.dive().state();
     const expectedResult = [[], [], [], []];
@@ -157,20 +158,20 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.filterList, expectedResult);
   });
 
-  it("should correctly build internal unique column data for filterData structure", () => {
+  it('should correctly build internal unique column data for filterData structure', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const state = shallowWrapper.dive().state();
     const expectedResult = [
-      ["Herm, Bob", "Houston, James", "James, Joe", "Walsh, John"],
-      ["Test Corp"],
-      ["Dallas", "Hartford", "Tampa", "Yonkers"],
-      ["CT", "FL", "NY", "TX"],
+      ['Herm, Bob', 'Houston, James', 'James, Joe', 'Walsh, John'],
+      ['Test Corp'],
+      ['Dallas', 'Hartford', 'Tampa', 'Yonkers'],
+      ['CT', 'FL', 'NY', 'TX'],
     ];
 
     assert.deepEqual(state.filterData, expectedResult);
   });
 
-  it("should correctly build internal rowsPerPage when provided in options", () => {
+  it('should correctly build internal rowsPerPage when provided in options', () => {
     const options = {
       rowsPerPage: 20,
       textLabels,
@@ -181,7 +182,7 @@ describe("<MUIDataTable />", function() {
     assert.strictEqual(state.rowsPerPage, 20);
   });
 
-  it("should correctly build internal rowsPerPageOptions when provided in options", () => {
+  it('should correctly build internal rowsPerPageOptions when provided in options', () => {
     const options = {
       rowsPerPageOptions: [5, 10, 15],
     };
@@ -191,101 +192,101 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.rowsPerPageOptions, [5, 10, 15]);
   });
 
-  it("should render pagination when enabled in options", () => {
+  it('should render pagination when enabled in options', () => {
     const options = {
       pagination: true,
     };
 
     const mountWrapper = mount(<MUIDataTable columns={columns} data={data} options={options} />);
-    const actualResult = mountWrapper.find(MUIDataTablePagination);
+    const actualResult = mountWrapper.find(TablePagination);
     assert.lengthOf(actualResult, 1);
   });
 
-  it("should not render pagination when disabled in options", () => {
+  it('should not render pagination when disabled in options', () => {
     const options = {
       pagination: false,
     };
 
     const mountWrapper = mount(<MUIDataTable columns={columns} data={data} options={options} />);
-    const actualResult = mountWrapper.find(MUIDataTablePagination);
+    const actualResult = mountWrapper.find(TablePagination);
     assert.lengthOf(actualResult, 0);
   });
 
-  it("should properly set internal filterList when calling filterUpdate method with type=checkbox", () => {
+  it('should properly set internal filterList when calling filterUpdate method with type=checkbox', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, "Joe James", "checkbox");
+    instance.filterUpdate(0, 'Joe James', 'checkbox');
     table.update();
 
     const state = table.state();
-    assert.deepEqual(state.filterList, [["Joe James"], [], [], []]);
+    assert.deepEqual(state.filterList, [['Joe James'], [], [], []]);
   });
 
-  it("should remove entry from filterList when calling filterUpdate method with type=checkbox and same arguments a second time", () => {
+  it('should remove entry from filterList when calling filterUpdate method with type=checkbox and same arguments a second time', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, "Joe James", "checkbox");
+    instance.filterUpdate(0, 'Joe James', 'checkbox');
     table.update();
 
     let state = table.state();
-    assert.deepEqual(state.filterList, [["Joe James"], [], [], []]);
+    assert.deepEqual(state.filterList, [['Joe James'], [], [], []]);
 
-    instance.filterUpdate(0, "Joe James", "checkbox");
+    instance.filterUpdate(0, 'Joe James', 'checkbox');
     table.update();
 
     state = table.state();
     assert.deepEqual(state.filterList, [[], [], [], []]);
   });
 
-  it("should properly set internal filterList when calling filterUpdate method with type=dropdown", () => {
+  it('should properly set internal filterList when calling filterUpdate method with type=dropdown', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, "Joe James", "dropdown");
+    instance.filterUpdate(0, 'Joe James', 'dropdown');
     table.update();
 
     const state = table.state();
-    assert.deepEqual(state.filterList, [["Joe James"], [], [], []]);
+    assert.deepEqual(state.filterList, [['Joe James'], [], [], []]);
   });
 
-  it("should create Chip when filterList is populated", () => {
-    const filterList = [["Joe James"], [], [], []];
+  it('should create Chip when filterList is populated', () => {
+    const filterList = [['Joe James'], [], [], []];
 
-    const mountWrapper = mount(<MUIDataTableFilterList filterList={filterList} filterUpdate={() => true} />);
+    const mountWrapper = mount(<TableFilterList filterList={filterList} filterUpdate={() => true} />);
     const actualResult = mountWrapper.find(Chip);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  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 same arguments a second time', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, "Joe James", "dropdown");
+    instance.filterUpdate(0, 'Joe James', 'dropdown');
     table.update();
 
     let state = table.state();
-    assert.deepEqual(state.filterList, [["Joe James"], [], [], []]);
+    assert.deepEqual(state.filterList, [['Joe James'], [], [], []]);
 
-    instance.filterUpdate(0, "Joe James", "dropdown");
+    instance.filterUpdate(0, 'Joe James', 'dropdown');
     table.update();
 
     state = table.state();
     assert.deepEqual(state.filterList, [[], [], [], []]);
   });
 
-  it("should properly reset internal filterList when calling resetFilters method", () => {
+  it('should properly reset internal filterList when calling resetFilters method', () => {
     // set a filter
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
-    instance.filterUpdate(0, "Joe James", "checkbox");
+    instance.filterUpdate(0, 'Joe James', 'checkbox');
     table.update();
 
     // now remove it
     let state = table.state();
-    assert.deepEqual(state.filterList, [["Joe James"], [], [], []]);
+    assert.deepEqual(state.filterList, [['Joe James'], [], [], []]);
 
     instance.resetFilters();
     table.update();
@@ -293,23 +294,23 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.filterList, [[], [], [], []]);
   });
 
-  it("should properly set searchText when calling searchTextUpdate method", () => {
+  it('should properly set searchText when calling searchTextUpdate method', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />);
     const table = shallowWrapper.dive();
     const instance = table.instance();
 
-    instance.searchTextUpdate("Joe");
+    instance.searchTextUpdate('Joe');
     table.update();
     const state = table.state();
 
     const expectedResult = JSON.stringify([
-      { data: ["James, Joe", "Test Corp", renderCities("Yonkers", { rowIndex: 0 }), "NY"], dataIndex: 0 },
+      { data: ['James, Joe', 'Test Corp', renderCities('Yonkers', { rowIndex: 0 }), 'NY'], dataIndex: 0 },
     ]);
 
     assert.deepEqual(JSON.stringify(state.displayData), expectedResult);
   });
 
-  it("should sort provided column when calling toggleSortColum method", () => {
+  it('should sort provided column when calling toggleSortColum method', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
@@ -318,16 +319,16 @@ describe("<MUIDataTable />", function() {
     const state = shallowWrapper.state();
 
     const expectedResult = JSON.stringify([
-      { data: ["Herm, Bob", "Test Corp", renderCities("Tampa", { rowIndex: 0 }), "FL"], dataIndex: 2 },
-      { data: ["Houston, James", "Test Corp", renderCities("Dallas", { rowIndex: 1 }), "TX"], dataIndex: 3 },
-      { data: ["James, Joe", "Test Corp", renderCities("Yonkers", { rowIndex: 2 }), "NY"], dataIndex: 0 },
-      { data: ["Walsh, John", "Test Corp", renderCities("Hartford", { rowIndex: 3 }), "CT"], dataIndex: 1 },
+      { data: ['Herm, Bob', 'Test Corp', renderCities('Tampa', { rowIndex: 0 }), 'FL'], dataIndex: 2 },
+      { data: ['Houston, James', 'Test Corp', renderCities('Dallas', { rowIndex: 1 }), 'TX'], dataIndex: 3 },
+      { data: ['James, Joe', 'Test Corp', renderCities('Yonkers', { rowIndex: 2 }), 'NY'], dataIndex: 0 },
+      { data: ['Walsh, John', 'Test Corp', renderCities('Hartford', { rowIndex: 3 }), 'CT'], dataIndex: 1 },
     ]);
 
     assert.deepEqual(JSON.stringify(state.displayData), expectedResult);
   });
 
-  it("should toggle provided column when calling toggleViewCol method", () => {
+  it('should toggle provided column when calling toggleViewCol method', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
@@ -337,40 +338,40 @@ describe("<MUIDataTable />", function() {
 
     const expectedResult = [
       {
-        name: "Name",
-        display: "false",
+        name: 'Name',
+        display: 'false',
         sort: true,
         filter: true,
         download: true,
         sortDirection: null,
         customBodyRender: renderName,
       },
-      { name: "Company", display: "true", sort: true, filter: true, download: true, sortDirection: null },
+      { name: 'Company', display: 'true', sort: true, filter: true, download: true, sortDirection: null },
       {
-        name: "City",
-        display: "true",
+        name: 'City',
+        display: 'true',
         sort: true,
         filter: true,
         download: true,
         sortDirection: null,
         customBodyRender: renderCities,
       },
-      { name: "State", display: "true", sort: true, filter: true, download: true, sortDirection: null },
+      { name: 'State', display: 'true', sort: true, filter: true, download: true, sortDirection: null },
     ];
 
     assert.deepEqual(state.columns, expectedResult);
   });
 
-  it("should get displayable data when calling getDisplayData method", () => {
+  it('should get displayable data when calling getDisplayData method', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
     const state = shallowWrapper.state();
 
-    const actualResult = instance.getDisplayData(columns, tableData, state.filterList, "");
+    const actualResult = instance.getDisplayData(columns, tableData, state.filterList, '');
     assert.deepEqual(JSON.stringify(actualResult), displayData);
   });
 
-  it("should update rowsPerPage when calling changeRowsPerPage method", () => {
+  it('should update rowsPerPage when calling changeRowsPerPage method', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
@@ -381,8 +382,8 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.rowsPerPage, 10);
   });
 
-  it("should recalculate page when calling changeRowsPerPage method", () => {
-    const data = new Array(29).fill("").map(() => ["Joe James", "Test Corp", "Yonkers", "NY"]);
+  it('should recalculate page when calling changeRowsPerPage method', () => {
+    const data = new Array(29).fill('').map(() => ['Joe James', 'Test Corp', 'Yonkers', 'NY']);
     const mountWrapper = mount(shallow(<MUIDataTable columns={columns} data={data} />).get(0));
     const instance = mountWrapper.instance();
 
@@ -393,7 +394,7 @@ describe("<MUIDataTable />", function() {
     assert.equal(state.page, 1);
   });
 
-  it("should update page position when calling changePage method", () => {
+  it('should update page position when calling changePage method', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
@@ -404,11 +405,11 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.page, 2);
   });
 
-  it("should update selectedRows when calling selectRowUpdate method with type=head", () => {
+  it('should update selectedRows when calling selectRowUpdate method with type=head', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
-    instance.selectRowUpdate("head", 0);
+    instance.selectRowUpdate('head', 0);
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
@@ -421,22 +422,22 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.selectedRows.data, expectedResult);
   });
 
-  it("should update selectedRows when calling selectRowUpdate method with type=cell", () => {
+  it('should update selectedRows when calling selectRowUpdate method with type=cell', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
-    instance.selectRowUpdate("cell", 0);
+    instance.selectRowUpdate('cell', 0);
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
     assert.deepEqual(state.selectedRows.data, [0]);
   });
 
-  it("should update selectedRows when calling selectRowUpdate method with type=custom", () => {
+  it('should update selectedRows when calling selectRowUpdate method with type=custom', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
-    instance.selectRowUpdate("custom", [0, 3]);
+    instance.selectRowUpdate('custom', [0, 3]);
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
@@ -445,23 +446,23 @@ describe("<MUIDataTable />", function() {
     assert.deepEqual(state.selectedRows.data, expectedResult);
   });
 
-  it("should update value when calling updateValue method in customBodyRender", () => {
+  it('should update value when calling updateValue method in customBodyRender', () => {
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} />).dive();
     const instance = shallowWrapper.instance();
 
-    instance.updateDataCol(0, 2, "Las Vegas");
+    instance.updateDataCol(0, 2, 'Las Vegas');
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
-    assert.deepEqual(state.data[0].data[2], "Las Vegas");
+    assert.deepEqual(state.data[0].data[2], 'Las Vegas');
   });
 
-  it("should call onTableChange when calling selectRowUpdate method with type=head", () => {
+  it('should call onTableChange when calling selectRowUpdate method with type=head', () => {
     const options = { selectableRows: true, onTableChange: spy() };
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />).dive();
     const instance = shallowWrapper.instance();
 
-    instance.selectRowUpdate("head", 0);
+    instance.selectRowUpdate('head', 0);
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
@@ -475,13 +476,13 @@ describe("<MUIDataTable />", function() {
     assert.strictEqual(options.onTableChange.callCount, 1);
   });
 
-  it("should call onTableChange when calling selectRowUpdate method with type=cell", () => {
+  it('should call onTableChange when calling selectRowUpdate method with type=cell', () => {
     const options = { selectableRows: true, onTableChange: spy() };
 
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />).dive();
     const instance = shallowWrapper.instance();
 
-    instance.selectRowUpdate("cell", 0);
+    instance.selectRowUpdate('cell', 0);
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
@@ -489,12 +490,12 @@ describe("<MUIDataTable />", function() {
     assert.strictEqual(options.onTableChange.callCount, 1);
   });
 
-  it("should call onTableChange when calling selectRowUpdate method with type=custom", () => {
+  it('should call onTableChange when calling selectRowUpdate method with type=custom', () => {
     const options = { selectableRows: true, onTableChange: spy() };
     const shallowWrapper = shallow(<MUIDataTable columns={columns} data={data} options={options} />).dive();
     const instance = shallowWrapper.instance();
 
-    instance.selectRowUpdate("custom", [0, 3]);
+    instance.selectRowUpdate('custom', [0, 3]);
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
@@ -504,36 +505,13 @@ describe("<MUIDataTable />", function() {
     assert.strictEqual(options.onTableChange.callCount, 1);
   });
 
-  describe("fallbackComparator", () => {
-    it("correctly compares two equal strings", () => {
-      expect(MUIDataTable.fallbackComparator("testString", "testString")).to.equal(0);
+  describe('should correctly run comparator function', () => {
+    it('correctly compares two equal strings', () => {
+      expect(getCollatorComparator()('testString', 'testString')).to.equal(0);
     });
 
-    it("correctly compares two different strings", () => {
-      expect(MUIDataTable.fallbackComparator("testStringA", "testStringB")).to.equal(-1);
-    });
-  });
-
-  describe("getCollatzComparator", () => {
-    describe("when Intl is available", () => {
-      it("returns a collator object", () => {
-        const comparator = MUIDataTable.getCollatzComparator();
-
-        expect(comparator).not.to.equal(MUIDataTable.fallbackComparator);
-      });
-    });
-
-    describe("when Intl is not available", () => {
-      it("returns the fallback comparator", () => {
-        const _intl = global.Intl;
-        global.Intl = undefined;
-
-        const comparator = MUIDataTable.getCollatzComparator();
-
-        global.Intl = _intl;
-
-        expect(comparator).to.equal(MUIDataTable.fallbackComparator);
-      });
+    it('correctly compares two different strings', () => {
+      expect(getCollatorComparator()('testStringA', 'testStringB')).to.equal(-1);
     });
   });
 });
diff --git a/test/MUIDataTableBody.test.js b/test/MUIDataTableBody.test.js
index 5aa9590..d9d7fd2 100644
--- a/test/MUIDataTableBody.test.js
+++ b/test/MUIDataTableBody.test.js
@@ -1,50 +1,51 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import textLabels from "../src/textLabels";
-import MUIDataTableBody from "../src/MUIDataTableBody";
-import MUIDataTableSelectCell from "../src/MUIDataTableSelectCell";
-
-describe("<MUIDataTableBody />", function() {
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import textLabels from '../src/textLabels';
+import TableBody from '../src/components/TableBody';
+import TableSelectCell from '../src/components/TableSelectCell';
+
+describe('<TableBody />', function() {
   let data;
   let displayData;
   let columns;
 
   before(() => {
-    columns = [{ name: "First Name" }, { name: "Company" }, { name: "City" }, { name: "State" }];
+    columns = [{ name: 'First Name' }, { name: 'Company' }, { name: 'City' }, { name: 'State' }];
     data = [
-      ["Joe James", "Test Corp", "Yonkers", "NY"],
-      ["John Walsh", "Test Corp", "Hartford", "CT"],
-      ["Bob Herm", "Test Corp", "Tampa", "FL"],
-      ["James Houston", "Test Corp", "Dallas", "TX"],
+      ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
+      ['John Walsh', 'Test Corp', 'Hartford', 'CT'],
+      ['Bob Herm', 'Test Corp', 'Tampa', 'FL'],
+      ['James Houston', 'Test Corp', 'Dallas', 'TX'],
     ];
     displayData = [
       {
-        data: ["Joe James", "Test Corp", "Yonkers", "NY"],
+        data: ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
         dataIndex: 0,
       },
       {
-        data: ["John Walsh", "Test Corp", "Hartford", "CT"],
+        data: ['John Walsh', 'Test Corp', 'Hartford', 'CT'],
         dataIndex: 1,
       },
       {
-        data: ["Bob Herm", "Test Corp", "Tampa", "FL"],
+        data: ['Bob Herm', 'Test Corp', 'Tampa', 'FL'],
         dataIndex: 2,
       },
       {
-        data: ["James Houston", "Test Corp", "Dallas", "TX"],
+        data: ['James Houston', 'Test Corp', 'Dallas', 'TX'],
         dataIndex: 3,
       },
     ];
   });
 
-  it("should render a table body with no selectable cells if selectableRows = false", () => {
+  it('should render a table body with no selectable cells if selectableRows = false', () => {
     const options = { selectableRows: false };
     const selectRowUpdate = () => {};
+    const toggleExpandRow = () => {};
 
     const mountWrapper = mount(
-      <MUIDataTableBody
+      <TableBody
         data={displayData}
         count={displayData.length}
         columns={columns}
@@ -52,44 +53,50 @@ describe("<MUIDataTableBody />", function() {
         rowsPerPage={10}
         selectedRows={[]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     );
 
-    const actualResult = mountWrapper.find(MUIDataTableSelectCell);
+    const actualResult = mountWrapper.find(TableSelectCell);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should render a table body with no records if no data provided", () => {
+  it('should render a table body with no records if no data provided', () => {
     const options = { selectableRows: false, textLabels };
     const selectRowUpdate = () => {};
+    const toggleExpandRow = () => {};
 
     const mountWrapper = mount(
-      <MUIDataTableBody
+      <TableBody
         data={[]}
         columns={columns}
         page={0}
         rowsPerPage={10}
         selectedRows={[]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     );
 
     const actualResult = mountWrapper.html();
-    assert.include(actualResult, "Sorry, no matching records found");
+    assert.include(actualResult, 'Sorry, no matching records found');
   });
 
-  it("should render a table body with selectable cells if selectableRows = true", () => {
+  it('should render a table body with selectable cells if selectableRows = true', () => {
     const options = { selectableRows: true };
     const selectRowUpdate = () => {};
+    const toggleExpandRow = () => {};
 
     const mountWrapper = mount(
-      <MUIDataTableBody
+      <TableBody
         data={displayData}
         count={displayData.length}
         columns={columns}
@@ -97,22 +104,25 @@ describe("<MUIDataTableBody />", function() {
         rowsPerPage={10}
         selectedRows={[]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     );
 
-    const actualResult = mountWrapper.find(MUIDataTableSelectCell);
+    const actualResult = mountWrapper.find(TableSelectCell);
     assert.strictEqual(actualResult.length, 4);
   });
 
-  it("should return the correct rowIndex when calling instance method getRowIndex", () => {
+  it('should return the correct rowIndex when calling instance method getRowIndex', () => {
     const options = { sort: true, selectableRows: true };
     const selectRowUpdate = () => {};
+    const toggleExpandRow = () => {};
 
     const shallowWrapper = shallow(
-      <MUIDataTableBody
+      <TableBody
         data={displayData}
         count={displayData.length}
         columns={columns}
@@ -120,8 +130,10 @@ describe("<MUIDataTableBody />", function() {
         rowsPerPage={2}
         selectedRows={[1, 2, 3]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     ).dive();
@@ -132,12 +144,13 @@ describe("<MUIDataTableBody />", function() {
     assert.strictEqual(actualResult, 4);
   });
 
-  it("should return correctly if row exists in selectedRows when calling instance method isRowSelected", () => {
+  it('should return correctly if row exists in selectedRows when calling instance method isRowSelected', () => {
     const options = { sort: true, selectableRows: true };
     const selectRowUpdate = () => {};
+    const toggleExpandRow = () => {};
 
     const shallowWrapper = shallow(
-      <MUIDataTableBody
+      <TableBody
         data={displayData}
         count={displayData.length}
         columns={columns}
@@ -145,8 +158,10 @@ describe("<MUIDataTableBody />", function() {
         rowsPerPage={15}
         selectedRows={[1, 2, 3]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     ).dive();
@@ -157,12 +172,13 @@ describe("<MUIDataTableBody />", function() {
     assert.strictEqual(actualResult, false);
   });
 
-  it("should trigger selectRowUpdate prop callback when calling method handleRowSelect", () => {
+  it('should trigger selectRowUpdate prop callback when calling method handleRowSelect', () => {
     const options = { sort: true, selectableRows: true };
     const selectRowUpdate = spy();
+    const toggleExpandRow = () => {};
 
     const shallowWrapper = shallow(
-      <MUIDataTableBody
+      <TableBody
         data={displayData}
         count={displayData.length}
         columns={columns}
@@ -170,8 +186,10 @@ describe("<MUIDataTableBody />", function() {
         rowsPerPage={10}
         selectedRows={[]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     ).dive();
@@ -183,12 +201,13 @@ describe("<MUIDataTableBody />", function() {
     assert.strictEqual(selectRowUpdate.callCount, 1);
   });
 
-  it("should call onRowClick when Row is clicked", () => {
+  it('should call onRowClick when Row is clicked', () => {
     const options = { selectableRows: true, onRowClick: spy() };
     const selectRowUpdate = stub();
+    const toggleExpandRow = () => {};
 
     const t = mount(
-      <MUIDataTableBody
+      <TableBody
         data={displayData}
         count={displayData.length}
         columns={columns}
@@ -196,26 +215,29 @@ describe("<MUIDataTableBody />", function() {
         rowsPerPage={10}
         selectedRows={[]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     );
 
-    t.find("#MUIDataTableBodyRow-2")
+    t.find('#MUIDataTableBodyRow-2')
       .first()
-      .simulate("click");
+      .simulate('click');
 
     assert.strictEqual(options.onRowClick.callCount, 1);
     assert(options.onRowClick.calledWith(data[2], { rowIndex: 2, dataIndex: 2 }));
   });
 
   it("should add custom props to rows if 'setRowProps' provided", () => {
-    const options = { setRowProps: stub().returns({ className: "testClass" }) };
+    const options = { setRowProps: stub().returns({ className: 'testClass' }) };
     const selectRowUpdate = stub();
+    const toggleExpandRow = () => {};
 
     const t = mount(
-      <MUIDataTableBody
+      <TableBody
         data={displayData}
         count={displayData.length}
         columns={columns}
@@ -223,18 +245,20 @@ describe("<MUIDataTableBody />", function() {
         rowsPerPage={10}
         selectedRows={[]}
         selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
         options={options}
-        searchText={""}
+        searchText={''}
         filterList={[]}
       />,
     );
 
     const props = t
-      .find("#MUIDataTableBodyRow-1")
+      .find('#MUIDataTableBodyRow-1')
       .first()
       .props();
 
-    assert.strictEqual(props.className, "testClass");
+    assert.strictEqual(props.className, 'testClass');
     assert.isAtLeast(options.setRowProps.callCount, 1);
     assert(options.setRowProps.calledWith(data[1]));
   });
diff --git a/test/MUIDataTableFilter.test.js b/test/MUIDataTableFilter.test.js
index 9984815..0e4bee2 100644
--- a/test/MUIDataTableFilter.test.js
+++ b/test/MUIDataTableFilter.test.js
@@ -1,58 +1,58 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import textLabels from "../src/textLabels";
-import Select from "@material-ui/core/Select";
-import Checkbox from "@material-ui/core/Checkbox";
-import MUIDataTableFilter from "../src/MUIDataTableFilter";
-
-describe("<MUIDataTableFilter />", function() {
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import textLabels from '../src/textLabels';
+import Select from '@material-ui/core/Select';
+import Checkbox from '@material-ui/core/Checkbox';
+import TableFilter from '../src/components/TableFilter';
+
+describe('<TableFilter />', function() {
   let data;
   let columns;
   let filterData;
 
   beforeEach(() => {
     columns = [
-      { name: "First Name", display: true, sort: true, filter: true, sortDirection: "desc" },
-      { name: "Company", display: true, sort: true, filter: true, sortDirection: "desc" },
-      { name: "City", display: true, sort: true, filter: true, sortDirection: "desc" },
-      { name: "State", display: true, sort: true, filter: true, sortDirection: "desc" },
+      { name: 'First Name', display: true, sort: true, filter: true, sortDirection: 'desc' },
+      { name: 'Company', display: true, sort: true, filter: true, sortDirection: 'desc' },
+      { name: 'City', display: true, sort: true, filter: true, sortDirection: 'desc' },
+      { name: 'State', display: true, sort: true, filter: true, sortDirection: 'desc' },
     ];
 
     data = [
-      ["Joe James", "Test Corp", "Yonkers", "NY"],
-      ["John Walsh", "Test Corp", "Hartford", "CT"],
-      ["Bob Herm", "Test Corp", "Tampa", "FL"],
-      ["James Houston", "Test Corp", "Dallas", "TX"],
+      ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
+      ['John Walsh', 'Test Corp', 'Hartford', 'CT'],
+      ['Bob Herm', 'Test Corp', 'Tampa', 'FL'],
+      ['James Houston', 'Test Corp', 'Dallas', 'TX'],
     ];
 
     filterData = [
-      ["Joe James", "John Walsh", "Bob Herm", "James Houston"],
-      ["Test Corp"],
-      ["Yonkers", "Hartford", "Tampa", "Dallas"],
-      ["NY", "CT", "FL", "TX"],
+      ['Joe James', 'John Walsh', 'Bob Herm', 'James Houston'],
+      ['Test Corp'],
+      ['Yonkers', 'Hartford', 'Tampa', 'Dallas'],
+      ['NY', 'CT', 'FL', 'TX'],
     ];
   });
 
   it("should data table filter view with checkboxes if filterType = 'checkbox'", () => {
-    const options = { filterType: "checkbox", textLabels };
+    const options = { filterType: 'checkbox', textLabels };
     const filterList = [[], [], [], []];
     const shallowWrapper = mount(
-      <MUIDataTableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
+      <TableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
     );
 
     const actualResult = shallowWrapper.find(Checkbox);
     assert.strictEqual(actualResult.length, 13);
   });
 
-  it("should data table filter view with no checkboxes if filter=false for each column", () => {
-    const options = { filterType: "checkbox", textLabels };
+  it('should data table filter view with no checkboxes if filter=false for each column', () => {
+    const options = { filterType: 'checkbox', textLabels };
     const filterList = [[], [], [], []];
     columns = columns.map(item => (item.filter = false));
 
     const shallowWrapper = mount(
-      <MUIDataTableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
+      <TableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
     );
 
     const actualResult = shallowWrapper.find(Checkbox);
@@ -60,24 +60,24 @@ describe("<MUIDataTableFilter />", function() {
   });
 
   it("should data table filter view with selects if filterType = 'select'", () => {
-    const options = { filterType: "select", textLabels };
-    const filterList = [["Joe James"], [], [], []];
+    const options = { filterType: 'select', textLabels };
+    const filterList = [['Joe James'], [], [], []];
 
     const mountWrapper = mount(
-      <MUIDataTableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
+      <TableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
     );
 
     const actualResult = mountWrapper.find(Select);
     assert.strictEqual(actualResult.length, 4);
   });
 
-  it("should data table filter view no selects if filter=false for each column", () => {
-    const options = { filterType: "select", textLabels };
-    const filterList = [["Joe James"], [], [], []];
+  it('should data table filter view no selects if filter=false for each column', () => {
+    const options = { filterType: 'select', textLabels };
+    const filterList = [['Joe James'], [], [], []];
     columns = columns.map(item => (item.filter = false));
 
     const mountWrapper = mount(
-      <MUIDataTableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
+      <TableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
     );
 
     const actualResult = mountWrapper.find(Select);
@@ -85,24 +85,24 @@ describe("<MUIDataTableFilter />", function() {
   });
 
   it("should data table filter view with checkbox selects if filterType = 'multiselect'", () => {
-    const options = { filterType: "multiselect", textLabels };
-    const filterList = [["Joe James", "John Walsh"], [], [], []];
+    const options = { filterType: 'multiselect', textLabels };
+    const filterList = [['Joe James', 'John Walsh'], [], [], []];
 
     const mountWrapper = mount(
-      <MUIDataTableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
+      <TableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
     );
 
     const actualResult = mountWrapper.find(Select);
     assert.strictEqual(actualResult.length, 4);
   });
 
-  it("should trigger onFilterUpdate prop callback when calling method handleCheckboxChange", () => {
-    const options = { filterType: "checkbox", textLabels };
+  it('should trigger onFilterUpdate prop callback when calling method handleCheckboxChange', () => {
+    const options = { filterType: 'checkbox', textLabels };
     const filterList = [[], [], [], []];
     const onFilterUpdate = spy();
 
     const shallowWrapper = shallow(
-      <MUIDataTableFilter
+      <TableFilter
         columns={columns}
         onFilterUpdate={onFilterUpdate}
         filterData={filterData}
@@ -117,13 +117,13 @@ describe("<MUIDataTableFilter />", function() {
     assert.strictEqual(onFilterUpdate.callCount, 1);
   });
 
-  it("should trigger onFilterUpdate prop callback when calling method handleDropdownChange", () => {
-    const options = { filterType: "select", textLabels };
+  it('should trigger onFilterUpdate prop callback when calling method handleDropdownChange', () => {
+    const options = { filterType: 'select', textLabels };
     const filterList = [[], [], [], []];
     const onFilterUpdate = spy();
 
     const shallowWrapper = shallow(
-      <MUIDataTableFilter
+      <TableFilter
         columns={columns}
         onFilterUpdate={onFilterUpdate}
         filterData={filterData}
@@ -133,11 +133,11 @@ describe("<MUIDataTableFilter />", function() {
     ).dive();
     const instance = shallowWrapper.instance();
 
-    let event = { target: { value: "All" } };
+    let event = { target: { value: 'All' } };
     instance.handleDropdownChange(event, 0);
     assert.strictEqual(onFilterUpdate.callCount, 1);
 
-    event = { target: { value: "test" } };
+    event = { target: { value: 'test' } };
     instance.handleDropdownChange(event, 0);
     assert.strictEqual(onFilterUpdate.callCount, 2);
   });
diff --git a/test/MUIDataTableHead.test.js b/test/MUIDataTableHead.test.js
index 7c23db8..8b227c7 100644
--- a/test/MUIDataTableHead.test.js
+++ b/test/MUIDataTableHead.test.js
@@ -1,32 +1,32 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import MUIDataTableHead from "../src/MUIDataTableHead";
-import MUIDataTableHeadCell from "../src/MUIDataTableHeadCell";
-import Tooltip from "@material-ui/core/Tooltip";
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import TableHead from '../src/components/TableHead';
+import TableHeadCell from '../src/components/TableHeadCell';
+import Tooltip from '@material-ui/core/Tooltip';
 
-describe("<MUIDataTableHead />", function() {
+describe('<TableHead />', function() {
   let columns;
   let handleHeadUpdateRef;
 
   before(() => {
     columns = [
-      { name: "First Name", display: "true", sort: null },
-      { name: "Company", display: "true", sort: null },
-      { name: "City", display: "true", sort: null },
-      { name: "State", display: "true", sort: null },
+      { name: 'First Name', display: 'true', sort: null },
+      { name: 'Company', display: 'true', sort: null },
+      { name: 'City', display: 'true', sort: null },
+      { name: 'State', display: 'true', sort: null },
     ];
 
     handleHeadUpdateRef = () => {};
   });
 
-  it("should render a table head", () => {
+  it('should render a table head', () => {
     const options = {};
     const toggleSort = () => {};
 
     const mountWrapper = mount(
-      <MUIDataTableHead
+      <TableHead
         columns={columns}
         options={options}
         setCellRef={() => {}}
@@ -34,17 +34,17 @@ describe("<MUIDataTableHead />", function() {
         toggleSort={toggleSort}
       />,
     );
-    const actualResult = mountWrapper.find(MUIDataTableHeadCell);
+    const actualResult = mountWrapper.find(TableHeadCell);
     assert.strictEqual(actualResult.length, 4);
   });
 
-  it("should render a table head with no cells", () => {
+  it('should render a table head with no cells', () => {
     const options = {};
     const toggleSort = () => {};
 
     const newColumns = columns.map(column => ({ ...column, display: false }));
     const mountWrapper = mount(
-      <MUIDataTableHead
+      <TableHead
         columns={newColumns}
         options={options}
         setCellRef={() => {}}
@@ -52,16 +52,16 @@ describe("<MUIDataTableHead />", function() {
         toggleSort={toggleSort}
       />,
     );
-    const actualResult = mountWrapper.find(MUIDataTableHeadCell);
+    const actualResult = mountWrapper.find(TableHeadCell);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should trigger toggleSort prop callback when calling method handleToggleColumn", () => {
+  it('should trigger toggleSort prop callback when calling method handleToggleColumn', () => {
     const options = { sort: true };
     const toggleSort = spy();
 
     const shallowWrapper = shallow(
-      <MUIDataTableHead
+      <TableHead
         columns={columns}
         options={options}
         setCellRef={() => {}}
@@ -77,12 +77,12 @@ describe("<MUIDataTableHead />", function() {
     assert.strictEqual(toggleSort.callCount, 1);
   });
 
-  it("should trigger selectRowUpdate prop callback and selectChecked state update when calling method handleRowSelect", () => {
+  it('should trigger selectRowUpdate prop callback and selectChecked state update when calling method handleRowSelect', () => {
     const options = { sort: true, selectableRows: true };
     const rowSelectUpdate = spy();
 
     const shallowWrapper = shallow(
-      <MUIDataTableHead
+      <TableHead
         columns={columns}
         options={options}
         setCellRef={() => {}}
diff --git a/test/MUIDataTableHeadCell.test.js b/test/MUIDataTableHeadCell.test.js
index 019091a..d7e4c3b 100644
--- a/test/MUIDataTableHeadCell.test.js
+++ b/test/MUIDataTableHeadCell.test.js
@@ -1,13 +1,13 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import textLabels from "../src/textLabels";
-import MUIDataTableHeadCell from "../src/MUIDataTableHeadCell";
-import TableSortLabel from "@material-ui/core/TableSortLabel";
-import HelpIcon from "@material-ui/icons/Help";
-
-describe("<MUIDataTableHeadCell />", function() {
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import textLabels from '../src/textLabels';
+import TableHeadCell from '../src/components/TableHeadCell';
+import TableSortLabel from '@material-ui/core/TableSortLabel';
+import HelpIcon from '@material-ui/icons/Help';
+
+describe('<TableHeadCell />', function() {
   let classes;
 
   before(() => {
@@ -16,83 +16,73 @@ describe("<MUIDataTableHeadCell />", function() {
     };
   });
 
-  it("should render a table head cell with sort label when options.sort = true provided", () => {
+  it('should render a table head cell with sort label when options.sort = true provided', () => {
     const options = { sort: true, textLabels };
     const toggleSort = () => {};
 
     const shallowWrapper = shallow(
-      <MUIDataTableHeadCell
-        options={options}
-        sortDirection={"asc"}
-        sort={true}
-        toggleSort={toggleSort}
-        classes={classes}>
+      <TableHeadCell options={options} sortDirection={'asc'} sort={true} toggleSort={toggleSort} classes={classes}>
         some content
-      </MUIDataTableHeadCell>,
+      </TableHeadCell>,
     ).dive();
 
     const actualResult = shallowWrapper.find(TableSortLabel);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  it("should render a table head cell without sort label when options.sort = false provided", () => {
+  it('should render a table head cell without sort label when options.sort = false provided', () => {
     const options = { sort: false, textLabels };
     const toggleSort = () => {};
 
     const shallowWrapper = shallow(
-      <MUIDataTableHeadCell
-        options={options}
-        sortDirection={"asc"}
-        sort={true}
-        toggleSort={toggleSort}
-        classes={classes}>
+      <TableHeadCell options={options} sortDirection={'asc'} sort={true} toggleSort={toggleSort} classes={classes}>
         some content
-      </MUIDataTableHeadCell>,
+      </TableHeadCell>,
     );
 
     const actualResult = shallowWrapper.find(TableSortLabel);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should render a table help icon when hint provided", () => {
+  it('should render a table help icon when hint provided', () => {
     const options = { sort: true, textLabels };
 
     const shallowWrapper = shallow(
-      <MUIDataTableHeadCell options={options} hint={"hint text"} classes={classes}>
+      <TableHeadCell options={options} hint={'hint text'} classes={classes}>
         some content
-      </MUIDataTableHeadCell>,
+      </TableHeadCell>,
     ).dive();
 
     const actualResult = shallowWrapper.find(HelpIcon);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  it("should render a table head cell without custom tooltip when hint provided", () => {
+  it('should render a table head cell without custom tooltip when hint provided', () => {
     const options = { sort: true, textLabels };
 
     const shallowWrapper = shallow(
-      <MUIDataTableHeadCell options={options} classes={classes}>
+      <TableHeadCell options={options} classes={classes}>
         some content
-      </MUIDataTableHeadCell>,
+      </TableHeadCell>,
     ).dive();
 
     const actualResult = shallowWrapper.find(HelpIcon);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should trigger toggleSort prop callback when calling method handleSortClick", () => {
+  it('should trigger toggleSort prop callback when calling method handleSortClick', () => {
     const options = { sort: true, textLabels };
     const toggleSort = spy();
 
     const shallowWrapper = shallow(
-      <MUIDataTableHeadCell options={options} index={0} sortDirection={"asc"} toggleSort={toggleSort} classes={classes}>
+      <TableHeadCell options={options} index={0} sortDirection={'asc'} toggleSort={toggleSort} classes={classes}>
         some content
-      </MUIDataTableHeadCell>,
+      </TableHeadCell>,
     ).dive();
 
     const instance = shallowWrapper.instance();
 
-    const event = { target: { value: "All" } };
+    const event = { target: { value: 'All' } };
     instance.handleSortClick();
     assert.strictEqual(toggleSort.callCount, 1);
   });
diff --git a/test/MUIDataTablePagination.test.js b/test/MUIDataTablePagination.test.js
index d2b584b..a5a6e9b 100644
--- a/test/MUIDataTablePagination.test.js
+++ b/test/MUIDataTablePagination.test.js
@@ -1,14 +1,14 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import TableRow from "@material-ui/core/TableRow";
-import TableFooter from "@material-ui/core/TableFooter";
-import TablePagination from "@material-ui/core/TablePagination";
-import textLabels from "../src/textLabels";
-import MUIDataTablePagination from "../src/MUIDataTablePagination";
-
-describe("<MUIDataTablePagination />", function() {
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import TableRow from '@material-ui/core/TableRow';
+import TableFooter from '@material-ui/core/TableFooter';
+import MuiTablePagination from '@material-ui/core/TablePagination';
+import textLabels from '../src/textLabels';
+import TablePagination from '../src/components/TablePagination';
+
+describe('<TablePagination />', function() {
   let options;
 
   before(() => {
@@ -18,34 +18,28 @@ describe("<MUIDataTablePagination />", function() {
     };
   });
 
-  it("should render a table footer with pagination", () => {
-    const mountWrapper = mount(<MUIDataTablePagination options={options} count={100} page={1} rowsPerPage={10} />);
+  it('should render a table footer with pagination', () => {
+    const mountWrapper = mount(<TablePagination options={options} count={100} page={1} rowsPerPage={10} />);
 
-    const actualResult = mountWrapper.find(TablePagination);
+    const actualResult = mountWrapper.find(MuiTablePagination);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  it("should trigger changeRowsPerPage prop callback when calling method handleRowChange", () => {
+  it('should trigger changeRowsPerPage prop callback when calling method handleRowChange', () => {
     const changeRowsPerPage = spy();
     const shallowWrapper = shallow(
-      <MUIDataTablePagination
-        options={options}
-        count={100}
-        page={1}
-        rowsPerPage={10}
-        changeRowsPerPage={changeRowsPerPage}
-      />,
+      <TablePagination options={options} count={100} page={1} rowsPerPage={10} changeRowsPerPage={changeRowsPerPage} />,
     ).dive();
     const instance = shallowWrapper.instance();
 
-    instance.handleRowChange({ target: { value: "" } });
+    instance.handleRowChange({ target: { value: '' } });
     assert.strictEqual(changeRowsPerPage.callCount, 1);
   });
 
-  it("should trigger changePage prop callback when calling method handlePageChange", () => {
+  it('should trigger changePage prop callback when calling method handlePageChange', () => {
     const changePage = spy();
     const shallowWrapper = shallow(
-      <MUIDataTablePagination options={options} count={100} page={1} rowsPerPage={10} changePage={changePage} />,
+      <TablePagination options={options} count={100} page={1} rowsPerPage={10} changePage={changePage} />,
     ).dive();
     const instance = shallowWrapper.instance();
 
diff --git a/test/MUIDataTableSearch.test.js b/test/MUIDataTableSearch.test.js
index 8aeb208..d75245e 100644
--- a/test/MUIDataTableSearch.test.js
+++ b/test/MUIDataTableSearch.test.js
@@ -1,54 +1,54 @@
-import React from "react";
-import simulant from "simulant";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import TextField from "@material-ui/core/TextField";
-import MUIDataTableSearch from "../src/MUIDataTableSearch";
-import textLabels from "../src/textLabels";
-
-describe("<MUIDataTableSearch />", function() {
-  it("should render a search bar", () => {
+import React from 'react';
+import simulant from 'simulant';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import TextField from '@material-ui/core/TextField';
+import TableSearch from '../src/components/TableSearch';
+import textLabels from '../src/textLabels';
+
+describe('<TableSearch />', function() {
+  it('should render a search bar', () => {
     const options = { textLabels };
     const onSearch = () => {};
     const onHide = () => {};
 
-    const mountWrapper = mount(<MUIDataTableSearch onSearch={onSearch} onHide={onHide} options={options} />);
+    const mountWrapper = mount(<TableSearch onSearch={onSearch} onHide={onHide} options={options} />);
 
     const actualResult = mountWrapper.find(TextField);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  it("should trigger handleTextChange prop callback when calling method handleTextChange", () => {
+  it('should trigger handleTextChange prop callback when calling method handleTextChange', () => {
     const options = { onSearchChange: () => true, textLabels };
     const onSearch = spy();
     const onHide = () => {};
 
-    const shallowWrapper = shallow(<MUIDataTableSearch onSearch={onSearch} onHide={onHide} options={options} />).dive();
+    const shallowWrapper = shallow(<TableSearch onSearch={onSearch} onHide={onHide} options={options} />).dive();
 
     const instance = shallowWrapper.instance();
 
-    instance.handleTextChange({ target: { value: "" } });
+    instance.handleTextChange({ target: { value: '' } });
     assert.strictEqual(onSearch.callCount, 1);
   });
 
-  it("should hide the search bar when hitting the ESCAPE key", () => {
+  it('should hide the search bar when hitting the ESCAPE key', () => {
     const options = { textLabels };
     const onHide = spy();
 
-    const mountWrapper = mount(<MUIDataTableSearch onHide={onHide} options={options} />, { attachTo: document.body });
+    const mountWrapper = mount(<TableSearch onHide={onHide} options={options} />, { attachTo: document.body });
 
-    simulant.fire(document.body.querySelector("input"), "keydown", { keyCode: 27 });
+    simulant.fire(document.body.querySelector('input'), 'keydown', { keyCode: 27 });
     assert.strictEqual(onHide.callCount, 1);
   });
 
-  it("should hide not hide search bar when entering anything but the ESCAPE key", () => {
+  it('should hide not hide search bar when entering anything but the ESCAPE key', () => {
     const options = { textLabels };
     const onHide = spy();
 
-    const mountWrapper = mount(<MUIDataTableSearch onHide={onHide} options={options} />, { attachTo: document.body });
+    const mountWrapper = mount(<TableSearch onHide={onHide} options={options} />, { attachTo: document.body });
 
-    simulant.fire(document.body.querySelector("input"), "keydown", { keyCode: 25 });
+    simulant.fire(document.body.querySelector('input'), 'keydown', { keyCode: 25 });
     assert.strictEqual(onHide.callCount, 0);
   });
 });
diff --git a/test/MUIDataTableSelectCell.test.js b/test/MUIDataTableSelectCell.test.js
index 4451128..54b3ce7 100644
--- a/test/MUIDataTableSelectCell.test.js
+++ b/test/MUIDataTableSelectCell.test.js
@@ -1,29 +1,29 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import Checkbox from "@material-ui/core/Checkbox";
-import MUIDataTableSelectCell from "../src/MUIDataTableSelectCell";
-
-describe("<MUIDataTableSelectCell />", function() {
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import Checkbox from '@material-ui/core/Checkbox';
+import TableSelectCell from '../src/components/TableSelectCell';
+
+describe('<TableSelectCell />', function() {
   before(() => {});
 
-  it("should render table select cell", () => {
-    const mountWrapper = mount(<MUIDataTableSelectCell checked={false} />);
+  it('should render table select cell', () => {
+    const mountWrapper = mount(<TableSelectCell checked={false} />);
 
     const actualResult = mountWrapper.find(Checkbox);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  it("should render table select cell checked", () => {
-    const mountWrapper = mount(<MUIDataTableSelectCell checked={true} />);
+  it('should render table select cell checked', () => {
+    const mountWrapper = mount(<TableSelectCell checked={true} />);
 
     const actualResult = mountWrapper.find(Checkbox);
     assert.strictEqual(actualResult.props().checked, true);
   });
 
-  it("should render table select cell unchecked", () => {
-    const mountWrapper = mount(<MUIDataTableSelectCell checked={false} />);
+  it('should render table select cell unchecked', () => {
+    const mountWrapper = mount(<TableSelectCell checked={false} />);
 
     const actualResult = mountWrapper.find(Checkbox);
     assert.strictEqual(actualResult.props().checked, false);
diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js
index 35b80b5..1951e2d 100644
--- a/test/MUIDataTableToolbar.test.js
+++ b/test/MUIDataTableToolbar.test.js
@@ -1,22 +1,23 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import IconButton from "@material-ui/core/IconButton";
-import SearchIcon from "@material-ui/icons/Search";
-import DownloadIcon from "@material-ui/icons/CloudDownload";
-import PrintIcon from "@material-ui/icons/Print";
-import ViewColumnIcon from "@material-ui/icons/ViewColumn";
-import ClearIcon from "@material-ui/icons/Clear";
-import FilterIcon from "@material-ui/icons/FilterList";
-import MUIDataTableToolbar from "../src/MUIDataTableToolbar";
-import MUIDataTableSearch from "../src/MUIDataTableSearch";
-import textLabels from "../src/textLabels";
-
-describe("<MUIDataTableToolbar />", function() {
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import IconButton from '@material-ui/core/IconButton';
+import SearchIcon from '@material-ui/icons/Search';
+import DownloadIcon from '@material-ui/icons/CloudDownload';
+import PrintIcon from '@material-ui/icons/Print';
+import ViewColumnIcon from '@material-ui/icons/ViewColumn';
+import ClearIcon from '@material-ui/icons/Clear';
+import FilterIcon from '@material-ui/icons/FilterList';
+import TableToolbar from '../src/components/TableToolbar';
+import TableSearch from '../src/components/TableSearch';
+import textLabels from '../src/textLabels';
+
+describe('<TableToolbar />', function() {
   let data;
   let columns;
   let options;
+  let setTableAction = () => {};
 
   before(() => {
     options = {
@@ -27,99 +28,109 @@ describe("<MUIDataTableToolbar />", function() {
       viewColumns: true,
       textLabels,
       downloadOptions: {
-        separator: ",",
-        filename: "tableDownload.csv",
+        separator: ',',
+        filename: 'tableDownload.csv',
       },
     };
-    columns = ["First Name", "Company", "City", "State"];
+    columns = ['First Name', 'Company', 'City', 'State'];
     data = [
       {
-        data: ["Joe James", "Test Corp", "Yonkers", "NY"],
+        data: ['Joe James', 'Test Corp', 'Yonkers', 'NY'],
         dataIndex: 0,
       },
       {
-        data: ["John Walsh", "Test Corp", "Hartford", "CT"],
+        data: ['John Walsh', 'Test Corp', 'Hartford', 'CT'],
         dataIndex: 1,
       },
       {
-        data: ["Bob Herm", "Test Corp", "Tampa", "FL"],
+        data: ['Bob Herm', 'Test Corp', 'Tampa', 'FL'],
         dataIndex: 2,
       },
       {
-        data: ["James Houston", "Test Corp", "Dallas", "TX"],
+        data: ['James Houston', 'Test Corp', 'Dallas', 'TX'],
         dataIndex: 3,
       },
     ];
   });
 
-  it("should render a toolbar", () => {
+  it('should render a toolbar', () => {
     const mountWrapper = mount(
-      <MUIDataTableToolbar setTableAction={() => {}} columns={columns} data={data} options={options} />,
+      <TableToolbar columns={columns} data={data} options={options} setTableAction={setTableAction} />,
     );
     const actualResult = mountWrapper.find(IconButton);
     assert.strictEqual(actualResult.length, 5);
   });
 
-  it("should render a toolbar with no search icon if option.search = false", () => {
+  it('should render a toolbar with no search icon if option.search = false', () => {
     const newOptions = { ...options, search: false };
-    const mountWrapper = mount(<MUIDataTableToolbar columns={columns} data={data} options={newOptions} />);
+    const mountWrapper = mount(
+      <TableToolbar columns={columns} data={data} options={newOptions} setTableAction={setTableAction} />,
+    );
     const actualResult = mountWrapper.find(SearchIcon);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should render a toolbar with no download icon if option.download = false", () => {
+  it('should render a toolbar with no download icon if option.download = false', () => {
     const newOptions = { ...options, download: false };
-    const mountWrapper = mount(<MUIDataTableToolbar columns={columns} data={data} options={newOptions} />);
+    const mountWrapper = mount(
+      <TableToolbar columns={columns} data={data} options={newOptions} setTableAction={setTableAction} />,
+    );
     const actualResult = mountWrapper.find(DownloadIcon);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should render a toolbar with no print icon if option.print = false", () => {
+  it('should render a toolbar with no print icon if option.print = false', () => {
     const newOptions = { ...options, print: false };
-    const mountWrapper = mount(<MUIDataTableToolbar columns={columns} data={data} options={newOptions} />);
+    const mountWrapper = mount(
+      <TableToolbar columns={columns} data={data} options={newOptions} setTableAction={setTableAction} />,
+    );
     const actualResult = mountWrapper.find(PrintIcon);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should render a toolbar with no view columns icon if option.viewColumns = false", () => {
+  it('should render a toolbar with no view columns icon if option.viewColumns = false', () => {
     const newOptions = { ...options, viewColumns: false };
-    const mountWrapper = mount(<MUIDataTableToolbar columns={columns} data={data} options={newOptions} />);
+    const mountWrapper = mount(
+      <TableToolbar columns={columns} data={data} options={newOptions} setTableAction={setTableAction} />,
+    );
     const actualResult = mountWrapper.find(ViewColumnIcon);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should render a toolbar with no filter icon if option.filter = false", () => {
+  it('should render a toolbar with no filter icon if option.filter = false', () => {
     const newOptions = { ...options, filter: false };
-    const mountWrapper = mount(<MUIDataTableToolbar columns={columns} data={data} options={newOptions} />);
+    const mountWrapper = mount(
+      <TableToolbar columns={columns} data={data} options={newOptions} setTableAction={setTableAction} />,
+    );
     const actualResult = mountWrapper.find(FilterIcon);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should render a toolbar with a search clicking search icon", () => {
+  it('should render a toolbar with a search clicking search icon', () => {
     const shallowWrapper = shallow(
-      <MUIDataTableToolbar columns={columns} setTableAction={() => {}} data={data} options={options} />,
+      <TableToolbar columns={columns} data={data} options={options} setTableAction={setTableAction} />,
     )
       .dive()
       .dive()
       .dive();
     const instance = shallowWrapper.instance();
 
-    instance.setActiveIcon("search");
+    instance.setActiveIcon('search');
     shallowWrapper.update();
 
-    const actualResult = shallowWrapper.find(MUIDataTableSearch);
+    const actualResult = shallowWrapper.find(TableSearch);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  it("should hide search after clicking cancel icon", () => {
+  it('should hide search after clicking cancel icon', () => {
     const searchTextUpdate = () => {};
     const shallowWrapper = shallow(
-      <MUIDataTableToolbar
+      <TableToolbar
         searchTextUpdate={searchTextUpdate}
-        setTableAction={() => {}}
         columns={columns}
         data={data}
         options={options}
+        setTableAction={setTableAction}
       />,
     )
       .dive()
@@ -132,37 +143,45 @@ describe("<MUIDataTableToolbar />", function() {
     };
 
     // display search
-    instance.setActiveIcon("search");
+    instance.setActiveIcon('search');
     shallowWrapper.update();
 
-    let actualResult = shallowWrapper.find(MUIDataTableSearch);
+    let actualResult = shallowWrapper.find(TableSearch);
     assert.strictEqual(actualResult.length, 1);
 
     // now hide it and test
     instance.hideSearch();
     shallowWrapper.update();
 
-    actualResult = shallowWrapper.find(MUIDataTableSearch);
+    actualResult = shallowWrapper.find(TableSearch);
     assert.strictEqual(actualResult.length, 0);
   });
 
-  it("should set icon when calling method setActiveIcon", () => {
-    const shallowWrapper = shallow(<MUIDataTableToolbar columns={columns} data={data} options={options} />)
+  it('should set icon when calling method setActiveIcon', () => {
+    const shallowWrapper = shallow(
+      <TableToolbar columns={columns} data={data} options={options} setTableAction={setTableAction} />,
+    )
       .dive()
       .dive()
       .dive();
     const instance = shallowWrapper.instance();
 
-    instance.setActiveIcon("filter");
+    instance.setActiveIcon('filter');
     shallowWrapper.update();
 
     const state = shallowWrapper.state();
-    assert.strictEqual(state.iconActive, "filter");
+    assert.strictEqual(state.iconActive, 'filter');
   });
 
-  it("should download CSV when calling method handleCSVDownload", () => {
+  it('should download CSV when calling method handleCSVDownload', () => {
     const shallowWrapper = shallow(
-      <MUIDataTableToolbar columns={columns} displayData={data} data={data} options={options} />,
+      <TableToolbar
+        columns={columns}
+        displayData={data}
+        data={data}
+        options={options}
+        setTableAction={setTableAction}
+      />,
     );
     const instance = shallowWrapper
       .dive()
@@ -170,8 +189,8 @@ describe("<MUIDataTableToolbar />", function() {
       .dive()
       .instance();
 
-    const appendSpy = spy(document.body, "appendChild");
-    const removeSpy = spy(document.body, "removeChild");
+    const appendSpy = spy(document.body, 'appendChild');
+    const removeSpy = spy(document.body, 'removeChild');
     instance.handleCSVDownload();
 
     assert.strictEqual(appendSpy.callCount, 1);
diff --git a/test/MUIDataTableToolbarSelect.test.js b/test/MUIDataTableToolbarSelect.test.js
index 4e8c3a7..cafccb4 100644
--- a/test/MUIDataTableToolbarSelect.test.js
+++ b/test/MUIDataTableToolbarSelect.test.js
@@ -1,32 +1,32 @@
-import React from "react";
-import { match, spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import DeleteIcon from "@material-ui/icons/Delete";
-import MUIDataTableToolbarSelect from "../src/MUIDataTableToolbarSelect";
-import textLabels from "../src/textLabels";
+import React from 'react';
+import { match, spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import DeleteIcon from '@material-ui/icons/Delete';
+import TableToolbarSelect from '../src/components/TableToolbarSelect';
+import textLabels from '../src/textLabels';
 
-describe("<MUIDataTableSelectCell />", function() {
+describe('<TableToolbarSelect />', function() {
   before(() => {});
 
-  it("should render table toolbar select", () => {
+  it('should render table toolbar select', () => {
     const onRowsDelete = () => {};
     const mountWrapper = mount(
-      <MUIDataTableToolbarSelect options={{ textLabels }} selectedRows={{ data: [1] }} onRowsDelete={onRowsDelete} />,
+      <TableToolbarSelect options={{ textLabels }} selectedRows={{ data: [1] }} onRowsDelete={onRowsDelete} />,
     );
 
     const actualResult = mountWrapper.find(DeleteIcon);
     assert.strictEqual(actualResult.length, 1);
   });
 
-  it("should call customToolbarSelect with 3 arguments", () => {
+  it('should call customToolbarSelect with 3 arguments', () => {
     const onRowsDelete = () => {};
     const customToolbarSelect = spy();
     const selectedRows = { data: [1] };
     const displayData = [1];
 
     const mountWrapper = mount(
-      <MUIDataTableToolbarSelect
+      <TableToolbarSelect
         options={{ textLabels, customToolbarSelect }}
         selectedRows={selectedRows}
         onRowsDelete={onRowsDelete}
@@ -34,21 +34,21 @@ describe("<MUIDataTableSelectCell />", function() {
       />,
     );
 
-    assert.strictEqual(customToolbarSelect.calledWith(selectedRows, displayData, match.typeOf("function")), true);
+    assert.strictEqual(customToolbarSelect.calledWith(selectedRows, displayData, match.typeOf('function')), true);
   });
 
-  it("should throw TypeError if selectedRows is not an array of numbers", done => {
+  it('should throw TypeError if selectedRows is not an array of numbers', done => {
     const onRowsDelete = () => {};
     const selectRowUpdate = () => {};
     const customToolbarSelect = (_, __, setSelectedRows) => {
       const spySetSelectedRows = spy(setSelectedRows);
       try {
-        spySetSelectedRows("");
+        spySetSelectedRows('');
       } catch (error) {
         //do nothing
       }
       try {
-        spySetSelectedRows(["1"]);
+        spySetSelectedRows(['1']);
       } catch (error) {
         //do nothing
       }
@@ -61,7 +61,7 @@ describe("<MUIDataTableSelectCell />", function() {
     const displayData = [1];
 
     const mountWrapper = mount(
-      <MUIDataTableToolbarSelect
+      <TableToolbarSelect
         options={{ textLabels, customToolbarSelect }}
         selectedRows={selectedRows}
         onRowsDelete={onRowsDelete}
@@ -71,7 +71,7 @@ describe("<MUIDataTableSelectCell />", function() {
     );
   });
 
-  it("should call selectRowUpdate when customToolbarSelect passed and setSelectedRows was called", () => {
+  it('should call selectRowUpdate when customToolbarSelect passed and setSelectedRows was called', () => {
     const onRowsDelete = () => {};
     const selectRowUpdate = spy();
     const customToolbarSelect = (_, __, setSelectedRows) => {
@@ -81,7 +81,7 @@ describe("<MUIDataTableSelectCell />", function() {
     const displayData = [1];
 
     const mountWrapper = mount(
-      <MUIDataTableToolbarSelect
+      <TableToolbarSelect
         options={{ textLabels, customToolbarSelect }}
         selectedRows={selectedRows}
         onRowsDelete={onRowsDelete}
diff --git a/test/MUIDataTableViewCol.test.js b/test/MUIDataTableViewCol.test.js
index 5c31b83..e52ffa0 100644
--- a/test/MUIDataTableViewCol.test.js
+++ b/test/MUIDataTableViewCol.test.js
@@ -1,34 +1,34 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import Checkbox from "@material-ui/core/Checkbox";
-import MUIDataTableViewCol from "../src/MUIDataTableViewCol";
-import textLabels from "../src/textLabels";
-
-describe("<MUIDataTableViewCol />", function() {
+import React from 'react';
+import { spy, stub } from 'sinon';
+import { mount, shallow } from 'enzyme';
+import { assert, expect, should } from 'chai';
+import Checkbox from '@material-ui/core/Checkbox';
+import TableViewCol from '../src/components/TableViewCol';
+import textLabels from '../src/textLabels';
+
+describe('<TableViewCol />', function() {
   let columns;
   let options;
 
   before(() => {
-    columns = ["a", "b", "c", "d"];
+    columns = ['a', 'b', 'c', 'd'];
     options = {
       textLabels,
     };
   });
 
-  it("should render view columns", () => {
-    const mountWrapper = mount(<MUIDataTableViewCol columns={columns} options={options} />);
+  it('should render view columns', () => {
+    const mountWrapper = mount(<TableViewCol columns={columns} options={options} />);
 
     const actualResult = mountWrapper.find(Checkbox);
     assert.strictEqual(actualResult.length, 4);
   });
 
-  it("should trigger onColumnUpdate prop callback when calling method handleColChange", () => {
+  it('should trigger onColumnUpdate prop callback when calling method handleColChange', () => {
     const onColumnUpdate = spy();
 
     const shallowWrapper = shallow(
-      <MUIDataTableViewCol columns={columns} onColumnUpdate={onColumnUpdate} options={options} />,
+      <TableViewCol columns={columns} onColumnUpdate={onColumnUpdate} options={options} />,
     ).dive();
 
     const instance = shallowWrapper.instance();
diff --git a/test/MUIPopover.test.js b/test/MUIPopover.test.js
deleted file mode 100644
index 09e8965..0000000
--- a/test/MUIPopover.test.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import React from "react";
-import { spy, stub } from "sinon";
-import { mount, shallow } from "enzyme";
-import { assert, expect, should } from "chai";
-import { MUIPopover, MUIPopoverTarget, MUIPopoverContent } from "../src/MUIPopover";
-import Popover from "@material-ui/core/Popover";
-
-describe("<MUIPopover />", function() {
-  it("should render a popover", () => {
-    const mountWrapper = mount(
-      <MUIPopover>
-        <MUIPopoverTarget>
-          <a href="#">Simple Link!</a>
-        </MUIPopoverTarget>
-        <MUIPopoverContent>Some content</MUIPopoverContent>
-      </MUIPopover>,
-    );
-
-    const actualResult = mountWrapper.find(Popover);
-    assert.strictEqual(actualResult.length, 1);
-  });
-
-  it("should not render a popover if children are not MUIPopoverContent or MUIPopoverTarget", () => {
-    stub(console, "error");
-    const mountWrapper = mount(
-      <MUIPopover>
-        <div>testing</div>
-      </MUIPopover>,
-    );
-
-    assert(console.error.called);
-    console.error.restore();
-  });
-
-  it("should return children when calling MUIPopoverContent", () => {
-    const shallowWrapper = shallow(<MUIPopoverContent>Some content</MUIPopoverContent>);
-
-    assert.strictEqual(shallowWrapper.text(), "Some content");
-  });
-
-  it("should call handleOnExit when unmounting MUIPopover", () => {
-    const exitFunc = spy();
-    const shallowWrapper = shallow(
-      <MUIPopover refExit={exitFunc}>
-        <MUIPopoverTarget>
-          <a href="#">Simple Link!</a>
-        </MUIPopoverTarget>
-        <MUIPopoverContent>Some content</MUIPopoverContent>
-      </MUIPopover>,
-    );
-
-    const instance = shallowWrapper.instance();
-    instance.handleOnExit();
-    assert.strictEqual(exitFunc.callCount, 1);
-  });
-
-  it("should close popover when calling method handleRequestClose", () => {
-    const refClose = spy();
-    const mountWrapper = mount(
-      <MUIPopover refClose={refClose}>
-        <MUIPopoverTarget>
-          <a href="#">Simple Link!</a>
-        </MUIPopoverTarget>
-        <MUIPopoverContent>Some content</MUIPopoverContent>
-      </MUIPopover>,
-    );
-
-    // open popover
-    mountWrapper.setState({ open: true });
-    mountWrapper.update();
-    let state = mountWrapper.state();
-
-    assert.strictEqual(state.open, true);
-    assert.strictEqual(mountWrapper.find(Popover).length, 1);
-
-    // hide popover
-    const instance = mountWrapper.instance();
-    instance.handleRequestClose();
-    mountWrapper.update();
-    state = mountWrapper.state();
-
-    assert.strictEqual(state.open, false);
-    assert.strictEqual(refClose.callCount, 1);
-  });
-
-  it("should open popover when calling method handleClick", () => {
-    const mountWrapper = mount(
-      <MUIPopover>
-        <MUIPopoverTarget>
-          <a href="#">Simple Link!</a>
-        </MUIPopoverTarget>
-        <MUIPopoverContent>Some content</MUIPopoverContent>
-      </MUIPopover>,
-    );
-
-    let state = mountWrapper.state();
-    const instance = mountWrapper.instance();
-    assert.strictEqual(state.open, false);
-
-    instance.handleClick();
-    mountWrapper.update();
-
-    state = mountWrapper.state();
-    assert.strictEqual(state.open, true);
-    assert.strictEqual(mountWrapper.find(Popover).length, 1);
-  });
-});
diff --git a/test/setup-mocha-env.js b/test/setup-mocha-env.js
index 8482b14..c56683a 100644
--- a/test/setup-mocha-env.js
+++ b/test/setup-mocha-env.js
@@ -1,35 +1,35 @@
-import Enzyme from "enzyme";
-import React from "react";
-import Adapter from "enzyme-adapter-react-16";
+import Enzyme from 'enzyme';
+import React from 'react';
+import Adapter from 'enzyme-adapter-react-16';
 
 /* required when running >= 16.0 */
 Enzyme.configure({ adapter: new Adapter() });
 
 function setupDom() {
-  const { JSDOM } = require("jsdom");
-  const Node = require("jsdom/lib/jsdom/living/node-document-position");
+  const { JSDOM } = require('jsdom');
+  const Node = require('jsdom/lib/jsdom/living/node-document-position');
 
-  const dom = new JSDOM("<!doctype html><html><body></body></html>");
+  const dom = new JSDOM('<!doctype html><html><body></body></html>');
 
   global.window = dom.window;
   global.document = window.document;
   global.Node = Node;
 
   global.navigator = {
-    userAgent: "node.js",
-    appVersion: "",
+    userAgent: 'node.js',
+    appVersion: '',
   };
 
   function copyProps(src, target) {
     const props = Object.getOwnPropertyNames(src)
-      .filter(prop => typeof target[prop] === "undefined")
+      .filter(prop => typeof target[prop] === 'undefined')
       .map(prop => Object.getOwnPropertyDescriptor(src, prop));
     Object.defineProperties(target, props);
   }
 
   copyProps(dom.window, global);
 
-  const KEYS = ["HTMLElement"];
+  const KEYS = ['HTMLElement'];
   KEYS.forEach(key => {
     global[key] = window[key];
   });
@@ -38,11 +38,11 @@ function setupDom() {
     setStart: () => {},
     setEnd: () => {},
     commonAncestorContainer: {
-      nodeName: "BODY",
+      nodeName: 'BODY',
       ownerDocument: {
         documentElement: window.document.body,
         parent: {
-          nodeName: "BODY",
+          nodeName: 'BODY',
         },
       },
     },
@@ -55,8 +55,8 @@ function setupDom() {
   global.window.cancelAnimationFrame = () => {};
   global.getComputedStyle = global.window.getComputedStyle;
 
-  Object.defineProperty(global.window.URL, "createObjectURL", { value: () => {} });
-  global.Blob = () => "";
+  Object.defineProperty(global.window.URL, 'createObjectURL', { value: () => {} });
+  global.Blob = () => '';
 }
 
 setupDom();