From cf5e7e695d221d83b515fa41836028837fc3a4cf Mon Sep 17 00:00:00 2001
From: pkupczyk <piotr.kupczyk@id.ethz.ch>
Date: Tue, 29 Jan 2019 18:43:57 +0100
Subject: [PATCH] Add in memory filtering of browsers: - SSDM-7560 : NEW
 openBIS UI - User/Groups - Navigation Tree - In Memory Filter - SSDM-7567 :
 NEW openBIS UI - Types - Navigation Tree - In Memory Filter

---
 openbis_ng_ui/src/components/Browser.jsx      |  2 +-
 .../src/components/BrowserFilter.jsx          | 22 ++++++++++++++++++-
 openbis_ng_ui/src/components/BrowserList.jsx  |  1 +
 openbis_ng_ui/src/reducer/actions.js          |  2 +-
 openbis_ng_ui/src/reducer/common/reducer.js   | 17 ++++++++++++++
 openbis_ng_ui/src/reducer/database/reducer.js | 10 +++++++--
 openbis_ng_ui/src/reducer/initialstate.js     |  6 +++++
 openbis_ng_ui/src/reducer/types/reducer.js    |  8 +++++--
 openbis_ng_ui/src/reducer/users/reducer.js    | 16 ++++++++++----
 9 files changed, 73 insertions(+), 11 deletions(-)

diff --git a/openbis_ng_ui/src/components/Browser.jsx b/openbis_ng_ui/src/components/Browser.jsx
index c707b5f9434..dfdf863a3ed 100644
--- a/openbis_ng_ui/src/components/Browser.jsx
+++ b/openbis_ng_ui/src/components/Browser.jsx
@@ -35,7 +35,7 @@ class Browser extends React.Component {
         selectedNodeId={this.props.selectedNodeId}
         onSelect={this.props.selectNode}
         renderNode={node => {
-          return (<ListItemText inset secondary={node.permId || node.id}/>)
+          return (<ListItemText inset secondary={node.text}/>)
         }}
       />
     )
diff --git a/openbis_ng_ui/src/components/BrowserFilter.jsx b/openbis_ng_ui/src/components/BrowserFilter.jsx
index 82abe17fdf7..93c7e1a02e7 100644
--- a/openbis_ng_ui/src/components/BrowserFilter.jsx
+++ b/openbis_ng_ui/src/components/BrowserFilter.jsx
@@ -1,8 +1,11 @@
 import React from 'react'
+import {connect} from 'react-redux'
 import {withStyles} from '@material-ui/core/styles'
 import InputAdornment from '@material-ui/core/InputAdornment'
 import TextField from '@material-ui/core/TextField'
 import FilterIcon from '@material-ui/icons/FilterList'
+import actions from '../reducer/actions'
+import {getTabState} from '../reducer/selectors'
 
 /*eslint-disable-next-line no-unused-vars*/
 const styles = theme => ({
@@ -11,6 +14,21 @@ const styles = theme => ({
   }
 })
 
+function mapDispatchToProps(dispatch) {
+  return {
+    setFilter: filter => {
+      dispatch(actions.setFilter(filter))
+    }
+  }
+}
+
+function mapStateToProps(state) {
+  let tabState = getTabState(state)
+  return {
+    filter: tabState.browser.filter
+  }
+}
+
 class BrowserFilter extends React.Component {
 
   render() {
@@ -20,6 +38,8 @@ class BrowserFilter extends React.Component {
       <TextField
         className={classes.browserFilter}
         placeholder="Filter"
+        value={this.props.filter}
+        onChange={e => this.props.setFilter(e.target.value)}
         InputProps={{
           startAdornment: (
             <InputAdornment position="start">
@@ -31,4 +51,4 @@ class BrowserFilter extends React.Component {
   }
 }
 
-export default withStyles(styles)(BrowserFilter)
+export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(BrowserFilter))
diff --git a/openbis_ng_ui/src/components/BrowserList.jsx b/openbis_ng_ui/src/components/BrowserList.jsx
index 9afd304aa81..422fba3dd83 100644
--- a/openbis_ng_ui/src/components/BrowserList.jsx
+++ b/openbis_ng_ui/src/components/BrowserList.jsx
@@ -55,6 +55,7 @@ class BrowserList extends React.Component {
       <List className={classes.noPadding}>
         {
           this.props.nodes.map(node =>
+            (!node.filterable || node.filtered) &&
             <div key={node.id}>
               <ListItem
                 button
diff --git a/openbis_ng_ui/src/reducer/actions.js b/openbis_ng_ui/src/reducer/actions.js
index 9a6837a2945..c41597f6bd8 100644
--- a/openbis_ng_ui/src/reducer/actions.js
+++ b/openbis_ng_ui/src/reducer/actions.js
@@ -46,7 +46,7 @@ export default {
   }),
   setFilter: filter => ({
     type: 'SET-FILTER',
-    value: filter
+    filter: filter
   }),
   setMode: mode => ({
     type: 'SET-MODE',
diff --git a/openbis_ng_ui/src/reducer/common/reducer.js b/openbis_ng_ui/src/reducer/common/reducer.js
index 69cca8d92c7..3b02cced47c 100644
--- a/openbis_ng_ui/src/reducer/common/reducer.js
+++ b/openbis_ng_ui/src/reducer/common/reducer.js
@@ -81,12 +81,28 @@ export function browserCollapseNode(browser, action) {
   return newBrowser
 }
 
+export function browserSetFilter(browser, action) {
+  let newBrowser = _.cloneDeep(browser)
+  newBrowser.filter = action.filter
+  visitNodes(newBrowser.nodes, node => {
+    if (action.filter === null || action.filter.trim() === '') {
+      node.filtered = true
+    } else {
+      node.filtered = node.text.toLowerCase().indexOf(action.filter.toLowerCase()) !== -1
+    }
+  })
+  return newBrowser
+}
+
 export function emptyTreeNode(values = {}) {
   return _.merge({
     id: null,
     permId: null,
     type: null,
+    text: null,
     selectable: false,
+    filterable: false,
+    filtered: true,
     expanded: false,
     loading: false,
     loaded: false,
@@ -98,6 +114,7 @@ export function entityTreeNode(entity, values = {}) {
   return _.merge(emptyTreeNode(), {
     id: entity['@type'] + '#' + entity.permId.permId,
     permId: entity.permId.permId,
+    text: entity.permId.permId,
     type: entity['@type'],
   }, values)
 }
diff --git a/openbis_ng_ui/src/reducer/database/reducer.js b/openbis_ng_ui/src/reducer/database/reducer.js
index b0eab779112..2578d2292c6 100644
--- a/openbis_ng_ui/src/reducer/database/reducer.js
+++ b/openbis_ng_ui/src/reducer/database/reducer.js
@@ -1,6 +1,6 @@
 import initialState from '../initialstate.js'
 import merge from 'lodash/merge'
-import {openEntities, dirtyEntities, entityTreeNode} from '../common/reducer.js'
+import {openEntities, dirtyEntities, entityTreeNode, browserSetFilter} from '../common/reducer.js'
 
 function filterOf(filter, columns) {
   if (filter == null || filter.length === 0) {
@@ -152,7 +152,8 @@ function browser(browser = initialState.database.browser, action) {
   switch (action.type) {
   case 'SET-SPACES': {
     return {
-      nodes: action.spaces.map(space => entityTreeNode(space, {selectable: true}))
+      filter: browser.filter,
+      nodes: action.spaces.map(space => entityTreeNode(space, {selectable: true, filterable: true}))
     }
   }
   case 'SET-PROJECTS': {
@@ -160,19 +161,24 @@ function browser(browser = initialState.database.browser, action) {
     const projectNodes = action.projects.map(project => entityTreeNode(project, {loaded: true}))
     const node = merge({}, oldNode, {loading: false, loaded: true, children: projectNodes})
     return {
+      filter: browser.filter,
       nodes: replaceNode(browser.nodes, node)
     }
   }
+  case 'SET-FILTER':
+    return browserSetFilter(browser, action)
   case 'EXPAND-NODE': {
     const loading = action.node.loaded === false
     const node = merge({}, action.node, {expanded: true, loading: loading})
     return {
+      filter: browser.filter,
       nodes: replaceNode(browser.nodes, node)
     }
   }
   case 'COLLAPSE-NODE': {
     const node = merge({}, action.node, {expanded: false})
     return {
+      filter: browser.filter,
       nodes: replaceNode(browser.nodes, node)
     }
   }
diff --git a/openbis_ng_ui/src/reducer/initialstate.js b/openbis_ng_ui/src/reducer/initialstate.js
index 51d4acf0c4d..ee3d0a84049 100644
--- a/openbis_ng_ui/src/reducer/initialstate.js
+++ b/openbis_ng_ui/src/reducer/initialstate.js
@@ -57,6 +57,8 @@ export default {
       sortDirection: 'asc'
     },
     browser: {
+      loaded: false,
+      filter: '',
       nodes: []
     },
     openEntities: {
@@ -68,6 +70,8 @@ export default {
 
   types: {
     browser: {
+      loaded: false,
+      filter: '',
       nodes: []
     },
     openEntities: {
@@ -78,6 +82,8 @@ export default {
 
   users: {
     browser: {
+      loaded: false,
+      filter: '',
       nodes: []
     },
     openEntities: {
diff --git a/openbis_ng_ui/src/reducer/types/reducer.js b/openbis_ng_ui/src/reducer/types/reducer.js
index 236ebdfbef3..67d1f22d8ec 100644
--- a/openbis_ng_ui/src/reducer/types/reducer.js
+++ b/openbis_ng_ui/src/reducer/types/reducer.js
@@ -2,6 +2,7 @@ import initialState from '../initialstate.js'
 import {
   browserExpandNode,
   browserCollapseNode,
+  browserSetFilter,
   openEntities,
   dirtyEntities,
   sortBy,
@@ -21,6 +22,8 @@ function browser(browser = initialState.types.browser, action) {
   switch (action.type) {
   case 'SET-MODE-DONE':
     return browserSetModeDone(browser, action)
+  case 'SET-FILTER':
+    return browserSetFilter(browser, action)
   case 'EXPAND-NODE':
     return browserExpandNode(browser, action)
   case 'COLLAPSE-NODE':
@@ -34,6 +37,7 @@ function browserSetModeDone(browser, action) {
   if (action.data) {
     return {
       loaded: true,
+      filter: '',
       nodes: [
         browserSetModeDoneTypeNodes('Object Types', action.data.objectTypes),
         browserSetModeDoneTypeNodes('Collection Types', action.data.collectionTypes),
@@ -50,10 +54,10 @@ function browserSetModeDoneTypeNodes(groupId, types) {
   let typeNodes = []
 
   types.forEach(type => {
-    typeNodes.push(entityTreeNode(type, {loaded: true, selectable: true}))
+    typeNodes.push(entityTreeNode(type, {loaded: true, selectable: true, filterable: true}))
   })
 
   sortBy(typeNodes, 'permId')
 
-  return emptyTreeNode({id: groupId, loaded: true, children: typeNodes})
+  return emptyTreeNode({id: groupId, text: groupId, loaded: true, children: typeNodes})
 }
\ No newline at end of file
diff --git a/openbis_ng_ui/src/reducer/users/reducer.js b/openbis_ng_ui/src/reducer/users/reducer.js
index 50f10b718bb..1e92590e3c1 100644
--- a/openbis_ng_ui/src/reducer/users/reducer.js
+++ b/openbis_ng_ui/src/reducer/users/reducer.js
@@ -2,12 +2,14 @@ import initialState from '../initialstate.js'
 import {
   browserExpandNode,
   browserCollapseNode,
+  browserSetFilter,
   sortBy,
   openEntities,
   dirtyEntities,
   emptyTreeNode,
   entityTreeNode
 } from '../common/reducer'
+import _ from 'lodash'
 
 export default function users(users = initialState.users, action) {
   return {
@@ -21,6 +23,8 @@ function browser(browser = initialState.users.browser, action) {
   switch (action.type) {
   case 'SET-MODE-DONE':
     return browserSetModeDone(browser, action)
+  case 'SET-FILTER':
+    return browserSetFilter(browser, action)
   case 'EXPAND-NODE':
     return browserExpandNode(browser, action)
   case 'COLLAPSE-NODE':
@@ -34,6 +38,7 @@ function browserSetModeDone(browser, action) {
   if (action.data) {
     return {
       loaded: true,
+      filter: '',
       nodes: [browserSetModeDoneUserNodes(action.data.users, action.data.groups), browserSetModeDoneGroupNodes(action.data.groups)]
     }
   } else {
@@ -58,18 +63,19 @@ function browserSetModeDoneUserNodes(users, groups) {
     let groupNodes = []
 
     userGroups.forEach(group => {
-      groupNodes.push(entityTreeNode(group, {loaded: true, selectable: true}))
+      groupNodes.push(entityTreeNode(group, {loaded: true, selectable: true, filterable: true}))
     })
 
     sortBy(groupNodes, 'permId')
 
-    userNodes.push(entityTreeNode(user, {loaded: true, selectable: true, children: groupNodes}))
+    userNodes.push(entityTreeNode(user, {loaded: true, selectable: true, filterable: true, children: groupNodes}))
   })
 
   sortBy(userNodes, 'permId')
 
   return emptyTreeNode({
     id: 'Users',
+    text: 'Users',
     loaded: true,
     children: userNodes
   })
@@ -82,20 +88,22 @@ function browserSetModeDoneGroupNodes(groups) {
     let userNodes = []
 
     group.getUsers().forEach(user => {
-      userNodes.push(entityTreeNode(user, {loaded: true, selectable: true}))
+      userNodes.push(entityTreeNode(user, {loaded: true, selectable: true, filterable: true}))
     })
 
     sortBy(userNodes, 'permId')
 
-    groupNodes.push(entityTreeNode(group, {loaded: true, selectable: true, children: userNodes}))
+    groupNodes.push(entityTreeNode(group, {loaded: true, selectable: true, filterable: true, children: userNodes}))
   })
 
   sortBy(groupNodes, 'permId')
 
   return emptyTreeNode({
     id: 'Groups',
+    text: 'Groups',
     loaded: true,
     children: groupNodes
   })
 }
 
+
-- 
GitLab