From b77c1fb8506de91fec1db8124c647b81fe8b4277 Mon Sep 17 00:00:00 2001
From: pkupczyk <piotr.kupczyk@id.ethz.ch>
Date: Wed, 10 Apr 2019 20:53:44 +0200
Subject: [PATCH] SSDM-7569 NEW openBIS UI - General Template/Infrastructure
 for Forms - Visualisation/Creation/Edit - select multiple browser nodes if
 they represent the same object + add more tests

---
 openbis_ng_ui/src/store/actions/browser.js    |   8 +-
 .../store/reducers/ui/pages/common/browser.js |  10 +-
 .../src/store/sagas/browser/browser.js        |  30 +++-
 openbis_ng_ui/src/store/selectors/browser.js  |   4 +-
 .../srcTest/store/sagas/browser.test.js       | 146 +++++++++++++++---
 5 files changed, 154 insertions(+), 44 deletions(-)

diff --git a/openbis_ng_ui/src/store/actions/browser.js b/openbis_ng_ui/src/store/actions/browser.js
index afc3bedfd4f..d8d9810126e 100644
--- a/openbis_ng_ui/src/store/actions/browser.js
+++ b/openbis_ng_ui/src/store/actions/browser.js
@@ -7,7 +7,7 @@ export const BROWSER_NODE_COLLAPSE = 'BROWSER_NODE_COLLAPSE'
 
 export const BROWSER_SET_FILTER = 'BROWSER_SET_FILTER'
 export const BROWSER_SET_NODES = 'BROWSER_SET_NODES'
-export const BROWSER_SET_SELECTED_NODE = 'BROWSER_SET_SELECTED_NODE'
+export const BROWSER_SET_SELECTED_NODES = 'BROWSER_SET_SELECTED_NODES'
 export const BROWSER_SET_VISIBLE_NODES = 'BROWSER_SET_VISIBLE_NODES'
 export const BROWSER_SET_EXPANDED_NODES = 'BROWSER_SET_EXPANDED_NODES'
 export const BROWSER_ADD_EXPANDED_NODES = 'BROWSER_ADD_EXPANDED_NODES'
@@ -75,11 +75,11 @@ export const browserSetNodes = (page, nodes) => ({
   }
 })
 
-export const browserSetSelectedNode = (page, id) => ({
-  type: BROWSER_SET_SELECTED_NODE,
+export const browserSetSelectedNodes = (page, ids) => ({
+  type: BROWSER_SET_SELECTED_NODES,
   payload: {
     page,
-    id
+    ids
   }
 })
 
diff --git a/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js b/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js
index ef2410cb73d..56dfe80e0e0 100644
--- a/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js
+++ b/openbis_ng_ui/src/store/reducers/ui/pages/common/browser.js
@@ -6,7 +6,7 @@ export const browser = combineReducers({
   initialized,
   filter,
   nodes,
-  selectedNode,
+  selectedNodes,
   visibleNodes,
   expandedNodes
 })
@@ -46,13 +46,13 @@ function nodes(state = [], action){
   }
 }
 
-function selectedNode(state = null, action){
+function selectedNodes(state = [], action){
   switch(action.type){
-    case actions.BROWSER_SET_SELECTED_NODE:
-      if(_.isEqual(state, action.payload.id)){
+    case actions.BROWSER_SET_SELECTED_NODES:
+      if(_.isEqual(state, action.payload.ids)){
         return state
       }else{
-        return action.payload.id
+        return action.payload.ids
       }
     default:
       return state
diff --git a/openbis_ng_ui/src/store/sagas/browser/browser.js b/openbis_ng_ui/src/store/sagas/browser/browser.js
index d9508d6ca8d..e3c668d68b4 100644
--- a/openbis_ng_ui/src/store/sagas/browser/browser.js
+++ b/openbis_ng_ui/src/store/sagas/browser/browser.js
@@ -67,13 +67,22 @@ function* browserNodeSelect(action){
   let allNodes = yield select(selectors.getAllBrowserNodes, page)
   let allNodesAllLevels = common.getAllNodes(allNodes)
 
-  let nodeToSelect = _.find(allNodesAllLevels, node => {
+  let nodeWithId = _.find(allNodesAllLevels, node => {
     return node.id === id
   })
-  yield put(actions.browserSetSelectedNode(page, nodeToSelect ? nodeToSelect.id : null))
 
-  if(nodeToSelect && nodeToSelect.object){
-    yield put(actions.objectOpen(page, nodeToSelect.object.type, nodeToSelect.object.id))
+  if(nodeWithId && nodeWithId.object){
+    let idsToSelect = _.reduce(allNodesAllLevels, (array, node) => {
+      if(_.isEqual(nodeWithId.object, node.object)){
+        array.push(node.id)
+      }
+      return array
+    }, [])
+    yield put(actions.browserSetSelectedNodes(page, idsToSelect))
+    yield put(actions.objectOpen(page, nodeWithId.object.type, nodeWithId.object.id))
+  }else{
+    let idsToSelect = nodeWithId ? [nodeWithId.id] : []
+    yield put(actions.browserSetSelectedNodes(page, idsToSelect))
   }
 }
 
@@ -87,13 +96,18 @@ function* browserNodeCollapse(action){
 
 function* setSelectedObject(action){
   let {page, type, id} = action.payload
+  let selectedObject = { type, id }
   let allNodes = yield select(selectors.getAllBrowserNodes, page)
   let allNodesAllLevels = common.getAllNodes(allNodes)
 
-  let nodeToSelect = _.find(allNodesAllLevels, node => {
-    return node.object && node.object.type === type && node.object.id === id
-  })
-  yield put(actions.browserSetSelectedNode(page, nodeToSelect ? nodeToSelect.id : null))
+  let idsToSelect = _.reduce(allNodesAllLevels, (array, node) => {
+    if(_.isEqual(selectedObject, node.object)){
+      array.push(node.id)
+    }
+    return array
+  }, [])
+
+  yield put(actions.browserSetSelectedNodes(page, idsToSelect))
 }
 
 function browserFilter(nodes, filter){
diff --git a/openbis_ng_ui/src/store/selectors/browser.js b/openbis_ng_ui/src/store/selectors/browser.js
index bbba82f7995..ca6ec0d2bc0 100644
--- a/openbis_ng_ui/src/store/selectors/browser.js
+++ b/openbis_ng_ui/src/store/selectors/browser.js
@@ -34,14 +34,14 @@ export const getBrowserNodes = createSelector(
   browser => {
     logger.log(logger.DEBUG, 'browserSelector.getBrowserNodes')
 
-    let selectedNode = browser.selectedNode
+    let selectedNodes = new Set(browser.selectedNodes)
     let visibleNodes = new Set(browser.visibleNodes)
     let expandedNodes = new Set(browser.expandedNodes)
     let nodes = browser.nodes
 
     nodes = common.mapNodes(null, nodes, (parent, node) => {
       if(visibleNodes.has(node.id)){
-        let selected = selectedNode === node.id
+        let selected = selectedNodes.has(node.id)
         let expanded = expandedNodes.has(node.id)
         return Object.assign({ selected, expanded }, node)
       }else{
diff --git a/openbis_ng_ui/srcTest/store/sagas/browser.test.js b/openbis_ng_ui/srcTest/store/sagas/browser.test.js
index ba6910b20dc..924d97c12cd 100644
--- a/openbis_ng_ui/srcTest/store/sagas/browser.test.js
+++ b/openbis_ng_ui/srcTest/store/sagas/browser.test.js
@@ -22,6 +22,7 @@ beforeEach(() => {
 })
 
 describe('browser', () => {
+
   test('init', () => {
     openbis.getUsers.mockReturnValue({
       objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ]
@@ -59,11 +60,8 @@ describe('browser', () => {
       ])
     ])
 
-    let selectedObject = selectors.getSelectedObject(state, pages.USERS)
-    let openObjects = selectors.getOpenObjects(state, pages.USERS)
-
-    expect(selectedObject).toEqual(null)
-    expect(openObjects).toEqual([])
+    expectSelectedObject(pages.USERS, null)
+    expectOpenObjects(pages.USERS, [])
   })
 
   test('filter', () => {
@@ -90,23 +88,20 @@ describe('browser', () => {
       ])
     ])
 
-    let selectedObject = selectors.getSelectedObject(state, pages.USERS)
-    let openObjects = selectors.getOpenObjects(state, pages.USERS)
-
-    expect(selectedObject).toEqual(null)
-    expect(openObjects).toEqual([])
+    expectSelectedObject(pages.USERS, null)
+    expectOpenObjects(pages.USERS, [])
   })
 
-  test('selectNode', () => {
+  test('select node', () => {
     openbis.getUsers.mockReturnValue({
-      objects: [ fixture.TEST_USER_DTO ]
+      objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ]
     })
 
     openbis.getGroups.mockReturnValue({
       objects: []
     })
 
-    let object = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId)
+    let testUserObject = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId)
 
     store.dispatch(actions.browserInit(pages.USERS))
     store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.TEST_USER_DTO.userId])))
@@ -114,29 +109,122 @@ describe('browser', () => {
     let state = store.getState()
     expectNodes(selectors.getBrowserNodes(state, pages.USERS), [
       node(['users'], false, false, [
+        node(['users', fixture.ANOTHER_USER_DTO.userId], false, false),
         node(['users', fixture.TEST_USER_DTO.userId], false, true)
       ]),
       node(['groups'])
     ])
 
-    expect(selectors.getSelectedObject(state, pages.USERS)).toEqual(object)
-    expect(selectors.getOpenObjects(state, pages.USERS)).toEqual([object])
+    expectSelectedObject(pages.USERS, testUserObject)
+    expectOpenObjects(pages.USERS, [testUserObject])
+  })
 
-    store.dispatch(actions.objectClose(pages.USERS, object.type, object.id))
+  test('select another node', () => {
+    openbis.getUsers.mockReturnValue({
+      objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ]
+    })
 
-    state = store.getState()
+    openbis.getGroups.mockReturnValue({
+      objects: []
+    })
+
+    let testUserObject = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId)
+    let anotherUserObject = fixture.object(objectType.USER, fixture.ANOTHER_USER_DTO.userId)
+
+    store.dispatch(actions.browserInit(pages.USERS))
+    store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.TEST_USER_DTO.userId])))
+    store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.ANOTHER_USER_DTO.userId])))
+
+    let state = store.getState()
     expectNodes(selectors.getBrowserNodes(state, pages.USERS), [
       node(['users'], false, false, [
+        node(['users', fixture.ANOTHER_USER_DTO.userId], false, true),
         node(['users', fixture.TEST_USER_DTO.userId], false, false)
       ]),
       node(['groups'])
     ])
 
-    expect(selectors.getSelectedObject(state, pages.USERS)).toEqual(null)
-    expect(selectors.getOpenObjects(state, pages.USERS)).toEqual([])
+    expectSelectedObject(pages.USERS, anotherUserObject)
+    expectOpenObjects(pages.USERS, [testUserObject, anotherUserObject])
+  })
+
+  test('select virtual node', () => {
+    openbis.getUsers.mockReturnValue({
+      objects: [ fixture.TEST_USER_DTO, fixture.ANOTHER_USER_DTO ]
+    })
+
+    openbis.getGroups.mockReturnValue({
+      objects: []
+    })
+
+    store.dispatch(actions.browserInit(pages.USERS))
+    store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users'])))
+
+    let state = store.getState()
+    expectNodes(selectors.getBrowserNodes(state, pages.USERS), [
+      node(['users'], false, true, [
+        node(['users', fixture.ANOTHER_USER_DTO.userId], false, false),
+        node(['users', fixture.TEST_USER_DTO.userId], false, false)
+      ]),
+      node(['groups'])
+    ])
+
+    expectSelectedObject(pages.USERS, null)
+    expectOpenObjects(pages.USERS, [])
+  })
+
+  test('select two nodes that represent the same object', () => {
+    openbis.getUsers.mockReturnValue({
+      objects: [ fixture.TEST_USER_DTO ]
+    })
+
+    openbis.getGroups.mockReturnValue({
+      objects: [ fixture.TEST_GROUP_DTO ]
+    })
+
+    let testUserObject = fixture.object(objectType.USER, fixture.TEST_USER_DTO.userId)
+
+    store.dispatch(actions.browserInit(pages.USERS))
+    store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['users', fixture.TEST_USER_DTO.userId])))
+
+    let state = store.getState()
+    expectNodes(selectors.getBrowserNodes(state, pages.USERS), [
+      node(['users'], false, false, [
+        node(['users', fixture.TEST_USER_DTO.userId], false, true, [
+          node(['users', fixture.TEST_USER_DTO.userId, fixture.TEST_GROUP_DTO.code], false, false)
+        ])
+      ]),
+      node(['groups'], false, false, [
+        node(['groups', fixture.TEST_GROUP_DTO.code], false, false, [
+          node(['groups', fixture.TEST_GROUP_DTO.code, fixture.TEST_USER_DTO.userId], false, true)
+        ])
+      ])
+    ])
+
+    expectSelectedObject(pages.USERS, testUserObject)
+    expectOpenObjects(pages.USERS, [testUserObject])
+
+    store.dispatch(actions.browserNodeSelect(pages.USERS, nodeId(['groups', fixture.TEST_GROUP_DTO.code, fixture.TEST_USER_DTO.userId])))
+
+    state = store.getState()
+    expectNodes(selectors.getBrowserNodes(state, pages.USERS), [
+      node(['users'], false, false, [
+        node(['users', fixture.TEST_USER_DTO.userId], false, true, [
+          node(['users', fixture.TEST_USER_DTO.userId, fixture.TEST_GROUP_DTO.code], false, false)
+        ])
+      ]),
+      node(['groups'], false, false, [
+        node(['groups', fixture.TEST_GROUP_DTO.code], false, false, [
+          node(['groups', fixture.TEST_GROUP_DTO.code, fixture.TEST_USER_DTO.userId], false, true)
+        ])
+      ])
+    ])
+
+    expectSelectedObject(pages.USERS, testUserObject)
+    expectOpenObjects(pages.USERS, [testUserObject])
   })
 
-  test('expandNode collapseNode', () => {
+  test('expand and collapse node', () => {
     openbis.getUsers.mockReturnValue({
       objects: []
     })
@@ -156,6 +244,9 @@ describe('browser', () => {
       ])
     ])
 
+    expectSelectedObject(pages.USERS, null)
+    expectOpenObjects(pages.USERS, [])
+
     store.dispatch(actions.browserNodeCollapse(pages.USERS, nodeId(['groups'])))
 
     state = store.getState()
@@ -166,11 +257,8 @@ describe('browser', () => {
       ])
     ])
 
-    let selectedObject = selectors.getSelectedObject(state, pages.USERS)
-    let openObjects = selectors.getOpenObjects(state, pages.USERS)
-
-    expect(selectedObject).toEqual(null)
-    expect(openObjects).toEqual([])
+    expectSelectedObject(pages.USERS, null)
+    expectOpenObjects(pages.USERS, [])
   })
 
 })
@@ -208,3 +296,11 @@ function expectNodes(actualNodes, expectedNodes){
 
   expect(actualNodesClone).toEqual(expectedNodes)
 }
+
+function expectSelectedObject(page, object){
+  expect(selectors.getSelectedObject(store.getState(), page)).toEqual(object)
+}
+
+function expectOpenObjects(page, objects){
+  expect(selectors.getOpenObjects(store.getState(), page)).toEqual(objects)
+}
-- 
GitLab