Repository: gregnb/mui-datatables (js)

Issue:
Only search data in specific columns or customize search function?
## Expected Behavior
I would like for the searched text to be tested only in one (or more) columns. Basically, the ability to specify if a column's data is included or excluded from a search. The output would be only rows in the searchable columns who's data contains the searched text are rendered. 

Alternatively, if we could customize the search function to use a custom function that returns a Boolean indicating if a row should be rendered or not would also fulfill this request. The custom function would ideally receive all row data.  

## Current Behavior
This is a guess, but it seems the current search functionality will search all the data in each column and if any data in those columns contains the searched text, then that row is rendered. 



Pull request description:
Feature/search updates
When merged should close #294 #380 

Patch:
diff --git a/README.md b/README.md
index a789e52..68fbf4b 100644
--- a/README.md
+++ b/README.md
@@ -147,6 +147,7 @@ The component accepts the following props:
 |**`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`
 |**`customSort`**|function||Override default sorting with custom function. `function(data: array, colIndex: number, order: string) => array`
+|**`customSearch `**|function||Override default search with custom function. `customSearch(searchQuery: string, currentRow: array, columns: array) => boolean`
 |**`elevation`**|number|4|Shadow depth applied to Paper component
 |**`caseSensitive `**|boolean|false|Enable/disable case sensitivity for search
 |**`responsive`**|string|'stacked'|Enable/disable responsive table views. Options: 'stacked', 'scroll'
@@ -209,6 +210,7 @@ const columns = [
 |**`filterOptons`**|array||Filter options [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js)
 |**`filter`**|boolean|true|Display column in filter list
 |**`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')`
 |**`download`**|boolean|true|Display column in CSV download file
 |**`hint`**|string||Display hint icon with string as tooltip on hover.
diff --git a/examples/customize-search/index.js b/examples/customize-search/index.js
new file mode 100644
index 0000000..a79b781
--- /dev/null
+++ b/examples/customize-search/index.js
@@ -0,0 +1,101 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import MUIDataTable from "../../src/";
+
+class Example extends React.Component {
+
+  render() {
+
+    const columns = [
+      {
+        name: "Name",
+        options: {
+          filter: true,
+          display: 'excluded',
+        }
+      },      
+      {
+        label: "Modified Title Label",
+        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: 'stacked',
+      customSearch: (searchQuery, currentRow, columns) => {
+        let isFound = false;
+        currentRow.forEach(col => {
+          if (col.toString().indexOf(searchQuery) >= 0) {
+            isFound = true;
+          }          
+        });
+        return isFound;
+      }
+    };
+
+    return (
+      <MUIDataTable title={"ACME Employee list"} data={data} columns={columns} options={options} />
+    );
+
+  }
+}
+
+ReactDOM.render(<Example />, document.getElementById("app-root"));
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index f947d3e..061ee29 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -66,6 +66,7 @@ class MUIDataTable extends React.Component {
             display: PropTypes.string, // enum('true', 'false', 'excluded')
             filter: PropTypes.bool,
             sort: PropTypes.bool,
+            searchable: PropTypes.bool,
             download: PropTypes.bool,
             viewColumns: PropTypes.bool,
             filterList: PropTypes.array,
@@ -104,6 +105,7 @@ class MUIDataTable extends React.Component {
       filter: PropTypes.bool,
       sort: PropTypes.bool,
       customSort: PropTypes.func,
+      customSearch: PropTypes.func,
       search: PropTypes.bool,
       print: PropTypes.bool,
       viewColumns: PropTypes.bool,
@@ -293,6 +295,7 @@ class MUIDataTable extends React.Component {
         display: 'true',
         filter: true,
         sort: true,
+        searchable: true,
         download: true,
         viewColumns: true,
         sortDirection: null,
@@ -466,12 +469,24 @@ class MUIDataTable extends React.Component {
       if (
         searchText &&
         this.hasSearchText(columnVal, searchText, caseSensitive) &&
-        columns[index].display !== 'false'
+        columns[index].display !== 'false' &&
+        columns[index].searchable
       ) {
         isSearchFound = true;
       }
     }
 
+    const { customSearch } = this.props.options;
+
+    if (searchText && customSearch) {
+      const customSearchResult = customSearch(searchText, row, columns);
+      if (typeof customSearchResult !== 'boolean') {
+        console.error('customSearch must return a boolean');
+      } else {
+        isSearchFound = customSearchResult;
+      }
+    }
+
     if (isFiltered || (!this.options.serverSide && searchText && !isSearchFound)) return null;
     else return displayRow;
   }
diff --git a/src/components/TableBodyCell.js b/src/components/TableBodyCell.js
index e89c6a9..caaa752 100644
--- a/src/components/TableBodyCell.js
+++ b/src/components/TableBodyCell.js
@@ -30,7 +30,7 @@ const defaultBodyCellStyles = theme => ({
 });
 
 class TableBodyCell extends React.Component {
-  handleClick = (event) => {
+  handleClick = event => {
     const { colIndex, options, children, dataIndex, rowIndex } = this.props;
     if (options.onCellClick) {
       options.onCellClick(children, { colIndex, rowIndex, dataIndex, event });
diff --git a/webpack.config.js b/webpack.config.js
index a5e0241..e507b2a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,7 +3,7 @@ const webpack = require('webpack');
 
 module.exports = {
   entry: {
-    app: "./examples/data-as-objects/index.js"
+    app: "./examples/customize-search/index.js"
   },
   stats: "verbose",
   context: __dirname,


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 94ec229..42e2899 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -85,6 +85,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'Name',
         download: true,
+        searchable: true,
         sortDirection: null,
         viewColumns: true,
         customBodyRender: renderName,
@@ -96,6 +97,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'Company',
         download: true,
+        searchable: true,
         viewColumns: true,
         sortDirection: null,
       },
@@ -106,6 +108,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'City Label',
         download: true,
+        searchable: true,
         viewColumns: true,
         sortDirection: null,
         customBodyRender: renderCities,
@@ -117,6 +120,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'State',
         download: true,
+        searchable: true,
         viewColumns: true,
         sortDirection: null,
       },
@@ -366,6 +370,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'Name',
         download: true,
+        searchable: true,
         sortDirection: null,
         customBodyRender: renderName,
         viewColumns: true,
@@ -377,6 +382,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'Company',
         download: true,
+        searchable: true,
         viewColumns: true,
         sortDirection: null,
       },
@@ -387,6 +393,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'City Label',
         download: true,
+        searchable: true,
         sortDirection: null,
         customBodyRender: renderCities,
         viewColumns: true,
@@ -398,6 +405,7 @@ describe('<MUIDataTable />', function() {
         filter: true,
         label: 'State',
         download: true,
+        searchable: true,
         viewColumns: true,
         sortDirection: null,
       },