Repository: gregnb/mui-datatables (js)

Issue:
weird `allRowsSelected` state after setting `rowsSelected` and clicking checkbox in context of filter
<!--- Provide a general summary of the issue in the Title above -->

My app is setting `options.rowsSelected` to allow user to select checkboxes `onRowClick`. However, this is leading to weird behavior in `onRowsSelect` where `allRowsSelected` will have the right `dataIndex` but the wrong [row]`index` in the context of a filter.

In the console below, you can see the first `allRowsSelected` is fine after clicking the first checkbox the first time (`index: 0`), but then after setting `rowsSelected: [32]`, the next time I click the same checkbox to disable it, you can see that the same (`dataIndex: 32`) now has two indexes (`index: 32` and `index:0`).

![image](https://user-images.githubusercontent.com/1125565/54839316-a1837e00-4c98-11e9-8dfe-0f9fb9664e9c.png)

<!--
    Thank you very much for contributing to MUI-datatables by creating an issue! ❤️
-->

## Expected Behavior
<!---
    If you're describing a bug, tell us what should happen.
    If you're suggesting a change/improvement, tell us how it should work.
-->

Expected that after setting `rowsSelected` to `[32]`, clicking the checkbox again would result in `allRowsSelected` to be empty `[]` rather than having two duplicate `dataIndexes`.

## Current Behavior
<!---
    If describing a bug, tell us what happens instead of the expected behavior.
    If suggesting a change/improvement, explain the difference from current behavior.
-->

## Steps to Reproduce (for bugs)
<!---
    Provide a link to a live example (you can use codesandbox.io) and an unambiguous set of steps to reproduce this bug.
    Include code to reproduce, if relevant (which it most likely is).
-->

1. Apply a filter (via UI)
2. Click checkbox (where `dataIndex > 0`) and in `onRowsSelect` change app state (e.g. via redux) so that a `options.rowsSelected` eventually receives all the dataIndexes reported in allRowsSelected.
3. Click same checkbox and notice duplicates in allRowsSelected, rather than empty array.

## Your Environment
<!--- Include as many relevant details about the environment with which you experienced the bug. -->

| Tech         | Version |
|--------------|---------|
| Material-UI  |  3.9.2       |
| MUI-datatables  |   2.0.0-beta.58      |
| React        |  16.3.0       |
| browser      | Chrome    |


Pull request:
fix issue 514 index for rowsSelected option
This PR fix index for rowsSelected option. When rowsSelected are added in option, { index: rowPos, dataIndex: row } are added into selectedRowsData. However, the index could not be same as dataIndex, e.g, users filter data first and set rowsSelected. The PR fix #514 

Patch:
diff --git a/README.md b/README.md
index 5611170..05aab14 100644
--- a/README.md
+++ b/README.md
@@ -219,7 +219,7 @@ const columns = [
 |**`print`**|boolean|true|Display column when printing
 |**`download`**|boolean|true|Display column in CSV download file
 |**`hint`**|string||Display hint icon with string as tooltip on hover.
-|**`customHeadRender`**|function||Function that returns a string or React component. Used as display for column header. `function(value, tableMeta, updateValue) => string`&#124;
+|**`customHeadRender`**|function||Function that returns a string or React component. Used as display for column header. `function(columnMeta, handleToggleColumn) => string`&#124;` React Component`
 |**`customBodyRender`**|function||Function that returns a string or React component. Used as display data within all table cells of a given column. `function(value, tableMeta, updateValue) => string`&#124;` React Component` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/component/index.js)
 |**`setCellProps`**|function||Is called for each cell and allows to return custom props for this cell based on its data. `function(cellValue: string, rowIndex: number, columnIndex: number) => object`
 
@@ -227,11 +227,20 @@ const columns = [
 
 ```js
 function(columnMeta: {
+  customHeadRender: func,
   display: enum('true', 'false', 'excluded'),
   filter: bool,
   sort: bool,
   sortDirection: bool,
-}, updateDirection: function)
+  download: bool,
+  empty: bool,
+  index: number,
+  label: string,
+  name: string,
+  print: bool,
+  searchable: bool,
+  viewColumns: bool
+}, handleToggleColumn: function(columnIndex))
 ```
 
 
diff --git a/examples/customize-columns/index.js b/examples/customize-columns/index.js
index a3113db..04c11a0 100644
--- a/examples/customize-columns/index.js
+++ b/examples/customize-columns/index.js
@@ -26,6 +26,11 @@ class Example extends React.Component {
         name: "Location",
         options: {
           filter: false,
+          customHeadRender: (columnMeta, updateDirection) => (
+            <th key={2} onClick={() => updateDirection(2)} style={{ cursor: 'pointer' }}>
+              {columnMeta.name}
+            </th>
+          )
         }
       },
       {
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index 3fb12ac..f4367ad 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -437,7 +437,16 @@ class MUIDataTable extends React.Component {
     if (TABLE_LOAD.INITIAL) {
       if (options.rowsSelected && options.rowsSelected.length) {
         options.rowsSelected.forEach(row => {
-          selectedRowsData.data.push({ index: row, dataIndex: row });
+          let rowPos = row;
+
+          for (let cIndex = 0; cIndex < this.state.displayData.length; cIndex++) {
+            if (this.state.displayData[cIndex].dataIndex === row) {
+              rowPos = cIndex;
+              break;
+            }
+          }
+
+          selectedRowsData.data.push({ index: rowPos, dataIndex: row });
           selectedRowsData.lookup[row] = true;
         });
       }
diff --git a/src/components/TableBodyRow.js b/src/components/TableBodyRow.js
index fe3307d..8594ae4 100644
--- a/src/components/TableBodyRow.js
+++ b/src/components/TableBodyRow.js
@@ -6,6 +6,7 @@ import { withStyles } from '@material-ui/core/styles';
 
 const defaultBodyRowStyles = theme => ({
   root: {},
+  hover: {},
   responsiveStacked: {
     [theme.breakpoints.down('sm')]: {
       border: 'solid 2px rgba(0, 0, 0, 0.15)',
@@ -35,6 +36,7 @@ class TableBodyRow extends React.Component {
         className={classNames(
           {
             [classes.root]: true,
+            [classes.hover]: options.rowHover,
             [classes.responsiveStacked]: options.responsive === 'stacked',
           },
           className,
diff --git a/src/components/TableFilter.js b/src/components/TableFilter.js
index ba7f025..ea975fa 100644
--- a/src/components/TableFilter.js
+++ b/src/components/TableFilter.js
@@ -222,7 +222,7 @@ class TableFilter extends React.Component {
         <div className={classes.textFieldRoot}>
           <FormControl className={classes.textFieldFormControl} key={index}>
             <TextField
-              label={column.name}
+              label={column.label}
               value={filterList[index].toString() || ''}
               onChange={event => this.handleTextFieldChange(event, index)}
             />


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 107e1c9..174d217 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -21,6 +21,7 @@ describe('<MUIDataTable />', function() {
   );
   let renderName = value => value.split(' ')[1] + ', ' + value.split(' ')[0];
   let renderState = value => value;
+  let renderHead = columnMeta => columnMeta.name + 's';
   let defaultRenderCustomFilterList = f => f;
   let renderCustomFilterList = f => `Name: ${f}`;
 
@@ -29,7 +30,7 @@ describe('<MUIDataTable />', function() {
       { name: 'Name', options: { customBodyRender: renderName, customFilterListRender: renderCustomFilterList } },
       'Company',
       { name: 'City', label: 'City Label', options: { customBodyRender: renderCities, filterType: 'textField' } },
-      { name: 'State', options: { customBodyRender: renderState, filterType: 'multiselect' } },
+      { name: 'State', options: { customBodyRender: renderState, filterType: 'multiselect', customHeadRender: renderHead } },
       { name: 'Empty', options: { empty: true, filterType: 'checkbox' } },
     ];
     data = [
@@ -141,6 +142,7 @@ describe('<MUIDataTable />', function() {
         viewColumns: true,
         sortDirection: null,
         customBodyRender: renderState,
+        customHeadRender: renderHead,
       },
       {
         display: 'true',
@@ -511,6 +513,7 @@ describe('<MUIDataTable />', function() {
         viewColumns: true,
         sortDirection: null,
         customBodyRender: renderState,
+        customHeadRender: renderHead,
       },
       {
         name: 'Empty',
diff --git a/test/MUIDataTableFilter.test.js b/test/MUIDataTableFilter.test.js
index 723a9cf..ab52813 100644
--- a/test/MUIDataTableFilter.test.js
+++ b/test/MUIDataTableFilter.test.js
@@ -16,10 +16,10 @@ describe('<TableFilter />', function() {
 
   beforeEach(() => {
     columns = [
-      { name: 'First Name', label: 'First Name', display: true, sort: true, filter: true, sortDirection: 'desc' },
-      { name: 'Company', label: 'Company', display: true, sort: true, filter: true, sortDirection: 'desc' },
-      { name: 'City', label: 'City Label', display: true, sort: true, filter: true, sortDirection: 'desc' },
-      { name: 'State', label: 'State', display: true, sort: true, filter: true, sortDirection: 'desc' },
+      { name: 'firstName', label: 'First Name', display: true, sort: true, filter: true, sortDirection: 'desc' },
+      { name: 'company', label: 'Company', display: true, sort: true, filter: true, sortDirection: 'desc' },
+      { name: 'city', label: 'City Label', display: true, sort: true, filter: true, sortDirection: 'desc' },
+      { name: 'state', label: 'State', display: true, sort: true, filter: true, sortDirection: 'desc' },
     ];
 
     data = [
@@ -111,6 +111,19 @@ describe('<TableFilter />', function() {
     assert.strictEqual(actualResult.length, 4);
   });
 
+  it("should render column.label as filter label if filterType = 'textField'", () => {
+    const options = { filterType: 'textField', textLabels };
+    const filterList = [[], [], [], []];
+    const shallowWrapper = mount(
+      <TableFilter columns={columns} filterData={filterData} filterList={filterList} options={options} />,
+    );
+    const labels = shallowWrapper
+      .find(TextField)
+      .filterWhere(n => n.html().match(/MuiInputLabel-formControl/))
+      .map(n => n.text());
+    assert.deepEqual(labels, ['First Name', 'Company', 'City Label', 'State']);
+  });
+
   it("should data table filter view with TextFields if filterType = 'textfield'", () => {
     const options = { filterType: 'textField', textLabels };
     const filterList = [[], [], [], []];
diff --git a/test/MUIDataTableHead.test.js b/test/MUIDataTableHead.test.js
index 0b0a37a..beff059 100644
--- a/test/MUIDataTableHead.test.js
+++ b/test/MUIDataTableHead.test.js
@@ -16,7 +16,18 @@ describe('<TableHead />', function() {
       { name: 'First Name', label: 'First Name', display: 'true', sort: null },
       { name: 'Company', label: 'Company', display: 'true', sort: null },
       { name: 'City', label: 'City Label', display: 'true', sort: null },
-      { name: 'State', label: 'State', display: 'true', sort: null },
+      {
+        name: 'State',
+        label: 'State',
+        display: 'true',
+        options: { fixedHeader: true },
+        customHeadRender: columnMeta => (
+          <TableHeadCell {...columnMeta}>
+            {columnMeta.name + 's'}
+          </TableHeadCell>
+        ),
+        sort: null
+      },
     ];
 
     handleHeadUpdateRef = () => {};
@@ -53,7 +64,7 @@ describe('<TableHead />', function() {
       />,
     );
     const labels = mountWrapper.find(TableHeadCell).map(n => n.text());
-    assert.deepEqual(labels, ['First Name', 'Company', 'City Label', 'State']);
+    assert.deepEqual(labels, ['First Name', 'Company', 'City Label', 'States']);
   });
 
   it('should render a table head with no cells', () => {