Repository: gregnb/mui-datatables (js)

Issue:
Enhancement: Allow for excluded, hidden, or 'never visible' columns
### Add third column visibility option
Right now if you include a column of data and set `options: { display: false }` the column is hidden, but it's still available to be displayed via the "view columns" menu in the UI. Sometimes rows need related data that never needs to be displayed (nor should it be) but is necessary for things like calculations or end-user display customization.

#### Use Case
- John is creating a table to display a list of Users.
- This table includes a "Post Count" column which is the count (a number) of the total "Posts" belonging to a user.
- John would like to style this cell so that the number of posts is a link that navigates to a related table (the "Posts" table) filtered to only show posts from the User of that row. For instance, that table could be located at a path such as `/posts/?userId=${userId}`
- `userId` relates the Users and Posts data. John does not want to display the `userId` in a column or have it be selectable in 'view columns' menu, as it brings no value to be displayed for the user in the UI.
- John creates a mui-tables column definition for the userId in both the Users and Posts table and marks it as hidden or excluded, so that it is accessible to the `customRender` function, but does not clutter the UI.

#### Implementation
One clean way to implement is by adding an additional column option such as `excluded`, `hidden`, or `private` which is a bool that excludes it from the 'view columns' UI menu. This is similar to how the `filter: false` column setting excludes it from the filter menu. In that way, `display` would still control whether the column is showing or not, but this new option would affect **whether the user is allowed to change** the column showing or not via the 'view columns' menu. Coincidentally, this would allow for an *additional* use case, where you would like to display a column that a user can not hide.

Another possibility,  we could change `display` to a string type with `enum('true', 'false', 'excluded')` .. or possibly `hidden` instead of `excluded`. And then (to avoid a breaking change) just do a type check and, on boolean, use the old behavior.

Pull request description:
change column display to enum: ('true', 'false', 'excluded')
When merged, this should resolve #68 



Patch:
diff --git a/README.md b/README.md
index 96f28de..d7b1c9c 100644
--- a/README.md
+++ b/README.md
@@ -192,7 +192,7 @@ const columns = [
 #### Column Options:
 |Name|Type|Default|Description
 |:--:|:-----|:--|:-----|
-|**`display`**|boolean|true|Display column in table
+|**`display`**|enum|'true'|Display column in table. `enum('true', 'false', 'excluded')`
 |**`filter`**|boolean|true|Display column in filter list
 |**`sort`**|boolean|true|Enable/disable sorting on column
 |**`customHeadRender`**|function||Function that returns a string or React component. Used as display for column header. `function(value, tableMeta, updateValue) => string`&#124;`
@@ -202,7 +202,7 @@ const columns = [
 
 ```
 function(columnMeta: {
-  display: bool,
+  display: enum('true', 'false', 'excluded'),
   filter: bool,
   sort: bool,
   sortDirection: bool,
diff --git a/examples/customize-columns/index.js b/examples/customize-columns/index.js
index 5056347..016afed 100644
--- a/examples/customize-columns/index.js
+++ b/examples/customize-columns/index.js
@@ -11,6 +11,7 @@ class Example extends React.Component {
         name: "Name",
         options: {
           filter: true,
+          display: 'excluded',
         }
       },      
       {
diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js
index df41116..66e21ac 100644
--- a/src/MUIDataTable.js
+++ b/src/MUIDataTable.js
@@ -53,7 +53,7 @@ class MUIDataTable extends React.Component {
         PropTypes.shape({
           name: PropTypes.string.isRequired,
           options: PropTypes.shape({
-            display: PropTypes.bool,
+            display: PropTypes.string, // enum('true', 'false', 'excluded')
             filter: PropTypes.bool,
             sort: PropTypes.bool,
             customHeadRender: PropTypes.func,
@@ -187,9 +187,9 @@ class MUIDataTable extends React.Component {
   };
 
   setTableOptions(props) {
-    const optionNames = ["rowsPerPage", "page", "filterList", "rowsPerPageOptions"];
+    const optionNames = ["rowsPerPage", "page", "rowsSelected", "filterList", "rowsPerPageOptions"];
     const optState = optionNames.reduce((acc, cur) => {
-      if (this.options[cur]) {
+      if (this.options[cur] !== undefined) {
         acc[cur] = this.options[cur];
       }
       return acc;
@@ -217,13 +217,17 @@ class MUIDataTable extends React.Component {
 
     columns.forEach((column, colIndex) => {
       let columnOptions = {
-        display: true,
+        display: "true",
         filter: true,
         sort: true,
         sortDirection: null,
       };
 
       if (typeof column === "object") {
+        if (column.options && column.options.display !== undefined) {
+          column.options.display = column.options.display.toString();
+        }
+
         columnOptions = {
           name: column.name,
           ...columnOptions,
@@ -418,7 +422,7 @@ class MUIDataTable extends React.Component {
     this.setState(
       prevState => {
         const columns = cloneDeep(prevState.columns);
-        columns[index].display = !columns[index].display;
+        columns[index].display = columns[index].display === "true" ? "false" : "true";
         return {
           columns: columns,
         };
@@ -427,7 +431,7 @@ class MUIDataTable extends React.Component {
         if (this.options.onColumnViewChange) {
           this.options.onColumnViewChange(
             this.state.columns[index].name,
-            this.state.columns[index].display ? "add" : "remove",
+            this.state.columns[index].display === "true" ? "add" : "remove",
           );
         }
       },
diff --git a/src/MUIDataTableBody.js b/src/MUIDataTableBody.js
index db8a6a8..65b0d54 100644
--- a/src/MUIDataTableBody.js
+++ b/src/MUIDataTableBody.js
@@ -108,7 +108,7 @@ class MUIDataTableBody extends React.Component {
               )}
               {row.map(
                 (column, index) =>
-                  columns[index].display ? (
+                  columns[index].display === "true" ? (
                     <MUIDataTableBodyCell
                       rowIndex={rowIndex}
                       colIndex={index}
diff --git a/src/MUIDataTableHead.js b/src/MUIDataTableHead.js
index 9bef5c9..5f6444a 100644
--- a/src/MUIDataTableHead.js
+++ b/src/MUIDataTableHead.js
@@ -50,7 +50,7 @@ class MUIDataTableHead extends React.Component {
           )}
           {columns.map(
             (column, index) =>
-              column.display &&
+              column.display === "true" &&
               (column.customHeadRender ? (
                 column.customHeadRender({ index, ...column }, this.handleToggleColumn)
               ) : (
diff --git a/src/MUIDataTableViewCol.js b/src/MUIDataTableViewCol.js
index b2e70a0..ac00c6b 100644
--- a/src/MUIDataTableViewCol.js
+++ b/src/MUIDataTableViewCol.js
@@ -68,26 +68,28 @@ class MUIDataTableViewCol extends React.Component {
         <FormGroup className={classes.formGroup}>
           {columns.map((column, index) => {
             return (
-              <FormControlLabel
-                key={index}
-                classes={{
-                  root: classes.formControl,
-                  label: classes.label,
-                }}
-                control={
-                  <Checkbox
-                    className={classes.checkbox}
-                    classes={{
-                      root: classes.checkboxRoot,
-                      checked: classes.checked,
-                    }}
-                    onChange={this.handleColChange.bind(null, index)}
-                    checked={column.display}
-                    value={column.name}
-                  />
-                }
-                label={column.name}
-              />
+              column.display !== "excluded" && (
+                <FormControlLabel
+                  key={index}
+                  classes={{
+                    root: classes.formControl,
+                    label: classes.label,
+                  }}
+                  control={
+                    <Checkbox
+                      className={classes.checkbox}
+                      classes={{
+                        root: classes.checkboxRoot,
+                        checked: classes.checked,
+                      }}
+                      onChange={this.handleColChange.bind(null, index)}
+                      checked={column.display === "true"}
+                      value={column.name}
+                    />
+                  }
+                  label={column.name}
+                />
+              )
             );
           })}
         </FormGroup>
diff --git a/webpack.config.js b/webpack.config.js
index f3cd389..197ed3a 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -3,7 +3,7 @@ const webpack = require('webpack');
 
 module.exports = {
   entry: {
-    app: "./examples/selectable-rows/index.js"
+    app: "./examples/customize-columns/index.js"
   },
   stats: "verbose",
   context: __dirname,


Test patch:
diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js
index 08380b3..929133a 100644
--- a/test/MUIDataTable.test.js
+++ b/test/MUIDataTable.test.js
@@ -77,17 +77,17 @@ describe("<MUIDataTable />", function() {
     const actualResult = shallowWrapper.dive().state().columns;
 
     const expectedResult = [
-      { display: true, name: "Name", sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
-      { display: true, name: "Company", sort: true, filter: true, sortDirection: null },
+      { display: "true", name: "Name", sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
+      { display: "true", name: "Company", sort: true, filter: true, sortDirection: null },
       {
-        display: true,
+        display: "true",
         name: "City",
         sort: true,
         filter: true,
         sortDirection: null,
         customBodyRender: renderCities,
       },
-      { display: true, name: "State", sort: true, filter: true, sortDirection: null },
+      { display: "true", name: "State", sort: true, filter: true, sortDirection: null },
     ];
 
     assert.deepEqual(actualResult, expectedResult);
@@ -327,17 +327,17 @@ describe("<MUIDataTable />", function() {
     const state = shallowWrapper.state();
 
     const expectedResult = [
-      { name: "Name", display: false, sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
-      { name: "Company", display: true, sort: true, filter: true, sortDirection: null },
+      { name: "Name", display: "false", sort: true, filter: true, sortDirection: null, customBodyRender: renderName },
+      { name: "Company", display: "true", sort: true, filter: true, sortDirection: null },
       {
         name: "City",
-        display: true,
+        display: "true",
         sort: true,
         filter: true,
         sortDirection: null,
         customBodyRender: renderCities,
       },
-      { name: "State", display: true, sort: true, filter: true, sortDirection: null },
+      { name: "State", display: "true", sort: true, filter: true, sortDirection: null },
     ];
 
     assert.deepEqual(state.columns, expectedResult);
diff --git a/test/MUIDataTableHead.test.js b/test/MUIDataTableHead.test.js
index 3f60c8c..7c23db8 100644
--- a/test/MUIDataTableHead.test.js
+++ b/test/MUIDataTableHead.test.js
@@ -12,10 +12,10 @@ describe("<MUIDataTableHead />", function() {
 
   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 = () => {};