Repository: gregnb/mui-datatables
Pull request #738 (2019-07-02 17:14:57)

Fix for 737
Fix for #737

Avd6977: @gabrielliwerant Found a small issue with the custom filter stuff.
coveralls: 
[![Coverage Status](https://coveralls.io/builds/24330205/badge)](https://coveralls.io/builds/24330205)

Coverage remained the same at 75.916% when pulling **7e08d80eb2078e7d99d212488f6be5c1a3d333e4 on Avd6977:FixFor737** into **3df7994ab34d5cb0717e34046464b69d0a11bd4c on gregnb:master**.


Patch:
diff --git a/README.md b/README.md
index 45f52be..4a3dea7 100644
--- a/README.md
+++ b/README.md
@@ -126,7 +126,7 @@ The component accepts the following props:
 |:--:|:-----|:-----|
 |**`title`**|array|Title used to caption table
 |**`columns`**|array|Columns used to describe table. Must be either an array of simple strings or objects describing a column
-|**`data`**|array|Data used to describe table. Must be an array containing objects. (Arrays containing just strings or numbers also supported) 
+|**`data`**|array|Data used to describe table. Must be an array containing objects. (Arrays containing just strings or numbers also supported)
 |**`options`**|object|Options used to describe table
 
 #### Options:
@@ -216,10 +216,10 @@ const columns = [
 |**`empty`**|boolean|false|This denotes whether the column has data or not (for use with intentionally empty columns)
 |**`viewColumns`**|boolean|true|Allow user to toggle column visibility through 'View Column' list
 |**`filterList`**|array||Filter value list [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js)
-|**`filterOptions`**|{names, logic}||With filter options, it's possible to use custom names for the filter fields [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js) and/or custom filter logic [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-filter/index.js)
+|**`filterOptions`**|{names, logic, display}||With filter options, it's possible to use custom names for the filter fields [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js), custom filter logic [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-filter/index.js), and custom rendering [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-filter/index.js)
 |**`customFilterListRender`**|function||Function that returns a string used as the chip label. `function(value) => string` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js)
 |**`filter`**|boolean|true|Display column in filter list
-|**`filterType `**|string|'dropdown'|Choice of filtering view. Takes priority over global filterType option.`enum('checkbox', 'dropdown', 'multiselect', 'textField')`.
+|**`filterType `**|string|'dropdown'|Choice of filtering view. Takes priority over global filterType option.`enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom')` Use 'custom' if you are supplying your own rendering via `filterOptions`.
 |**`sort`**|boolean|true|Enable/disable sorting on column
 |**`searchable`**|boolean|true|Exclude/include column from search results
 |**`sortDirection`**|string||Set default sort order `enum('asc', 'desc')`
@@ -381,7 +381,7 @@ npm run dev
 ```
 open  http://localhost:5050/ in browser
 
-After you make your changes locally, you can run the test suite with `npm test`. 
+After you make your changes locally, you can run the test suite with `npm test`.
 
 ## License
 The files included in this repository are licensed under the MIT license.
diff --git a/examples/customize-filter/index.js b/examples/customize-filter/index.js
index db58014..a829180 100644
--- a/examples/customize-filter/index.js
+++ b/examples/customize-filter/index.js
@@ -32,25 +32,26 @@ class Example extends React.Component {
         options: {
           filter: true,
           filterType: 'custom',
+          filterList: [25, 50],
           customFilterListRender: v => {
-            if (v['min'] && v['max']) {
-              return `Min Age: ${v['min']}, Max Age: ${v['max']}`;
-            } else if (v['min']) {
-              return `Min Age: ${v['min']}`;
-            } else if (v['max']) {
-              return `Max Age: ${v['max']}`;
+            if (v[0] && v[1]) {
+              return `Min Age: ${v[0]}, Max Age: ${v[1]}`;
+            } else if (v[0]) {
+              return `Min Age: ${v[0]}`;
+            } else if (v[1]) {
+              return `Max Age: ${v[1]}`;
             }
             return false;
           },
           filterOptions: {
             names: [],
             logic(age, filters) {
-              if (filters['min'] && filters['max']) {
-                return age < filters['min'] || age > filters['max'];
-              } else if (filters['min']) {
-                return age < filters['min'];
-              } else if (filters['max']) {
-                return age > filters['max'];
+              if (filters[0] && filters[1]) {
+                return age < filters[0] || age > filters[1];
+              } else if (filters[0]) {
+                return age < filters[0];
+              } else if (filters[1]) {
+                return age > filters[1];
               }
               return false;
             },
@@ -60,18 +61,18 @@ class Example extends React.Component {
                 <FormGroup row>
                   <TextField
                     label="min"
-                    value={filterList[index]['min'] || ''}
+                    value={filterList[index][0] || ''}
                     onChange={event => {
-                      filterList[index]['min'] = event.target.value;
+                      filterList[index][0] = event.target.value;
                       onChange(filterList[index], index, column);
                     }}
                     style={{ width: '45%', marginRight: '5%' }}
                   />
                   <TextField
                     label="max"
-                    value={filterList[index]['max'] || ''}
+                    value={filterList[index][1] || ''}
                     onChange={event => {
-                      filterList[index]['max'] = event.target.value;
+                      filterList[index][1] = event.target.value;
                       onChange(filterList[index], index, column);
                     }}
                     style={{ width: '45%' }}
diff --git a/examples/selectable-rows/index.js b/examples/selectable-rows/index.js
index 9860ba9..4323549 100644
--- a/examples/selectable-rows/index.js
+++ b/examples/selectable-rows/index.js
@@ -3,6 +3,9 @@ import ReactDOM from "react-dom";
 import MUIDataTable from "../../src/";
 
 class Example extends React.Component {
+  state = {
+    rowsSelected: []
+  };
 
   render() {
 
@@ -49,12 +52,15 @@ class Example extends React.Component {
 
     const options = {
       filter: true,
-      selectableRows: true,
+      selectableRows: 'multiple',
+      selectableRowsOnClick: true,
       filterType: 'dropdown',
       responsive: 'stacked',
       rowsPerPage: 10,
+      rowsSelected: this.state.rowsSelected,
       onRowsSelect: (rowsSelected, allRows) => {
         console.log(rowsSelected, allRows);
+        this.setState({ rowsSelected: allRows.map(row => row.dataIndex) });
       },
       onRowsDelete: (rowsDeleted) => {
         if (rowsDeleted.data[0].dataIndex === 0) {
diff --git a/package-lock.json b/package-lock.json
index fa7bbb1..e93f58f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.6.0",
+  "version": "2.6.4",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
diff --git a/package.json b/package.json
index 5e4398e..7a3ba0c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "mui-datatables",
-  "version": "2.6.0",
+  "version": "2.6.4",
   "description": "Datatables for React using Material-UI",
   "main": "dist/index.js",
   "files": [
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index c6a04e7..9cc02bf 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -4,7 +4,6 @@ import MuiTable from '@material-ui/core/Table';
 import classnames from 'classnames';
 import cloneDeep from 'lodash.clonedeep';
 import find from 'lodash.find';
-import isEqual from 'lodash.isequal';
 import isUndefined from 'lodash.isundefined';
 import merge from 'lodash.merge';
 import PropTypes from 'prop-types';
@@ -19,7 +18,7 @@ import TableToolbarSelect from './components/TableToolbarSelect';
 import textLabels from './textLabels';
 import { buildMap, getCollatorComparator, sortCompare } from './utils';
 
-const defaultTableStyles = {
+const defaultTableStyles = theme => ({
   root: {},
   paper: {},
   tableRoot: {
@@ -31,6 +30,14 @@ const defaultTableStyles = {
     height: '100%',
     maxHeight: '499px',
   },
+  responsiveStacked: {
+    overflowX: 'auto',
+    overflow: 'auto',
+    [theme.breakpoints.down('sm')]: {
+      overflowX: 'hidden',
+      overflow: 'hidden',
+    },
+  },
   caption: {
     position: 'absolute',
     left: '-3000px',
@@ -52,7 +59,7 @@ const defaultTableStyles = {
       },
     },
   },
-};
+});
 
 const TABLE_LOAD = {
   INITIAL: 1,
@@ -990,7 +997,7 @@ class MUIDataTable extends React.Component {
           let rowPos = -1;
 
           for (let cIndex = 0; cIndex < selectedRows.length; cIndex++) {
-            if (selectedRows[cIndex].index === index) {
+            if (selectedRows[cIndex].dataIndex === dataIndex) {
               rowPos = cIndex;
               break;
             }
@@ -1144,7 +1151,7 @@ class MUIDataTable extends React.Component {
         />
         <div
           style={{ position: 'relative' }}
-          className={this.options.responsive === 'scroll' ? classes.responsiveScroll : null}>
+          className={this.options.responsive === 'scroll' ? classes.responsiveScroll : classes.responsiveStacked}>
           {this.options.resizableColumns && (
             <TableResize
               key={rowCount}
diff --git a/src/components/TableBody.js b/src/components/TableBody.js
index eee66db..6fe61a6 100644
--- a/src/components/TableBody.js
+++ b/src/components/TableBody.js
@@ -116,13 +116,15 @@ class TableBody extends React.Component {
       return;
     }
 
-    // Don't trigger onRowClick if the event was actually a row selection
-    if (event.target.id && event.target.id.startsWith('MUIDataTableSelectCell')) {
-      return;
-    }
+    // Don't trigger onRowClick if the event was actually a row selection via checkbox
+    if (event.target.id && event.target.id.startsWith('MUIDataTableSelectCell')) return;
 
     // Check if we should toggle row select when row is clicked anywhere
-    if (this.props.options.selectableRowsOnClick && this.props.options.selectableRows !== 'none') {
+    if (
+      this.props.options.selectableRowsOnClick &&
+      this.props.options.selectableRows !== 'none' &&
+      this.isRowSelectable(data.dataIndex)
+    ) {
       const selectRow = { index: data.rowIndex, dataIndex: data.dataIndex };
       this.handleRowSelect(selectRow);
     }
@@ -132,6 +134,9 @@ class TableBody extends React.Component {
       this.props.toggleExpandRow(expandRow);
     }
 
+    // Don't trigger onRowClick if the event was actually a row selection via click
+    if (this.props.options.selectableRowsOnClick) return;
+
     this.props.options.onRowClick && this.props.options.onRowClick(row, data, event);
   };
 
diff --git a/src/components/TableHeadCell.js b/src/components/TableHeadCell.js
index 0708d38..a5aafba 100644
--- a/src/components/TableHeadCell.js
+++ b/src/components/TableHeadCell.js
@@ -31,6 +31,8 @@ const defaultHeadCellStyles = theme => ({
     display: 'flex',
     verticalAlign: 'top',
     cursor: 'pointer',
+  },
+  sortLabelRoot: {
     height: '10px',
   },
   sortActive: {
@@ -38,10 +40,16 @@ const defaultHeadCellStyles = theme => ({
   },
   toolButton: {
     display: 'flex',
-    height: '10px',
     outline: 'none',
     cursor: 'pointer',
   },
+  hintIconAlone: {
+    marginTop: '-3px',
+    marginLeft: '3px',
+  },
+  hintIconWithSortIcon: {
+    marginTop: '-3px',
+  },
 });
 
 class TableHeadCell extends React.Component {
@@ -77,6 +85,7 @@ class TableHeadCell extends React.Component {
     const sortActive = sortDirection !== null && sortDirection !== undefined ? true : false;
 
     const sortLabelProps = {
+      classes: { root: classes.sortLabelRoot },
       active: sortActive,
       hideSortIcon: true,
       ...(sortDirection ? { direction: sortDirection } : {}),
@@ -133,28 +142,32 @@ class TableHeadCell extends React.Component {
                     open={isHintTooltipOpen}
                     onOpen={() => this.setState({ isSortTooltipOpen: false, isHintTooltipOpen: true })}
                     onClose={() => this.setState({ isHintTooltipOpen: false })}>
-                    <HelpIcon fontSize="small" />
+                    <HelpIcon
+                      className={!sortActive ? classes.hintIconAlone : classes.hintIconWithSortIcon}
+                      fontSize="small"
+                    />
                   </Tooltip>
                 )}
               </div>
             </span>
           </Tooltip>
         ) : (
-          children
+          <div className={classes.sortAction}>
+            {children}
+            {hint && (
+              <Tooltip
+                title={hint}
+                placement={'bottom-end'}
+                classes={{
+                  tooltip: classes.tooltip,
+                }}
+                enterDelay={300}
+                classes={{ popper: classes.mypopper }}>
+                <HelpIcon className={classes.hintIconAlone} fontSize="small" />
+              </Tooltip>
+            )}
+          </div>
         )}
-        {!options.sort ||
-          (!sort && hint && (
-            <Tooltip
-              title={hint}
-              placement={'bottom-end'}
-              classes={{
-                tooltip: classes.tooltip,
-              }}
-              enterDelay={300}
-              classes={{ popper: classes.mypopper }}>
-              <HelpIcon fontSize="small" />
-            </Tooltip>
-          ))}
       </TableCell>
     );
   }


Test patch:
diff --git a/test/MUIDataTableBody.test.js b/test/MUIDataTableBody.test.js
index 2fb80ac..1fb866e 100644
--- a/test/MUIDataTableBody.test.js
+++ b/test/MUIDataTableBody.test.js
@@ -235,6 +235,42 @@ describe('<TableBody />', function() {
     assert.strictEqual(toggleExpandRow.callCount, 0);
   });
 
+  it('should not gather selected row data when clicking row with selectableRowsOnClick=true when it is disabled with isRowSelectable.', () => {
+    let selectedRowData;
+    const options = {
+      selectableRows: true,
+      selectableRowsOnClick: true,
+      isRowSelectable: dataIndex => (dataIndex === 2 ? false : true),
+    };
+    const selectRowUpdate = (type, data) => (selectedRowData = data);
+    const toggleExpandRow = spy();
+
+    const mountWrapper = mount(
+      <TableBody
+        data={displayData}
+        count={displayData.length}
+        columns={columns}
+        page={0}
+        rowsPerPage={10}
+        selectedRows={[]}
+        selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
+        options={options}
+        searchText={''}
+        filterList={[]}
+      />,
+    );
+
+    mountWrapper
+      .find('#MUIDataTableBodyRow-2')
+      .first()
+      .simulate('click');
+
+    assert.isUndefined(selectedRowData);
+    assert.strictEqual(toggleExpandRow.callCount, 0);
+  });
+
   it('should gather expanded row data when clicking row with expandableRows=true and expandableRowsOnClick=true.', () => {
     let expandedRowData;
     const options = { selectableRows: true, expandableRows: true, expandableRowsOnClick: true };
@@ -338,6 +374,36 @@ describe('<TableBody />', function() {
     assert.strictEqual(options.onRowClick.callCount, 0);
   });
 
+  it('should not call onRowClick when clicking to select a row', () => {
+    const options = { selectableRows: true, selectableRowsOnClick: true, onRowClick: spy() };
+    const selectRowUpdate = spy();
+    const toggleExpandRow = spy();
+
+    const mountWrapper = mount(
+      <TableBody
+        data={displayData}
+        count={displayData.length}
+        columns={columns}
+        page={0}
+        rowsPerPage={10}
+        selectedRows={[]}
+        selectRowUpdate={selectRowUpdate}
+        expandedRows={[]}
+        toggleExpandRow={toggleExpandRow}
+        options={options}
+        searchText={''}
+        filterList={[]}
+      />,
+    );
+
+    mountWrapper
+      .find('TableSelectCell')
+      .first()
+      .simulate('click');
+
+    assert.strictEqual(options.onRowClick.callCount, 0);
+  });
+
   it('should call onRowClick when Row is clicked', () => {
     const options = { selectableRows: true, onRowClick: spy() };
     const selectRowUpdate = stub();