Restore full item as param of tree callbacks
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 8 Oct 2018 19:59:55 +0000 (21:59 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 8 Oct 2018 19:59:55 +0000 (21:59 +0200)
Feature #13862

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

src/components/file-tree/file-tree.tsx
src/components/tree/tree.tsx
src/index.tsx
src/store/tree-picker/tree-picker-actions.ts
src/views-components/project-tree-picker/project-tree-picker.tsx
src/views-components/project-tree/project-tree.tsx
src/views-components/side-panel-tree/side-panel-tree.tsx
src/views-components/tree-picker/tree-picker.ts
src/views/workbench/workbench.tsx

index 165b4bfcf138ebcc8e94df4b26c63bbcad1a49c3..0a96254c58607c2ccc477a1128c624fb75f76cc9 100644 (file)
@@ -32,7 +32,7 @@ export class FileTree extends React.Component<FileTreeProps> {
         this.props.onMenuOpen(event, item);
     }
 
-    handleToggle = (id: string, status: TreeItemStatus) => {
+    handleToggle = (event: React.MouseEvent<{}>, { id, status }: TreeItem<{}>) => {
         this.props.onCollapseToggle(id, status);
     }
 
index 247661f1819868d556e6c99564073e35a1cac872..df3fc7f3bd76bbf396e8f16e0975dede4c87d0d7 100644 (file)
@@ -82,15 +82,15 @@ export interface TreeItem<T> {
 }
 
 export interface TreeProps<T> {
+    disableRipple?: boolean;
     items?: Array<TreeItem<T>>;
-    render: (item: TreeItem<T>, level?: number) => ReactElement<{}>;
-    toggleItemOpen: (id: string, status: TreeItemStatus) => void;
-    toggleItemActive: (id: string, status: TreeItemStatus) => void;
     level?: number;
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
+    render: (item: TreeItem<T>, level?: number) => ReactElement<{}>;
     showSelection?: boolean;
+    toggleItemActive: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
+    toggleItemOpen: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
     toggleItemSelection?: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
-    disableRipple?: boolean;
 }
 
 export const Tree = withStyles(styles)(
@@ -104,11 +104,11 @@ export const Tree = withStyles(styles)(
                     <div key={`item/${level}/${idx}`}>
                         <ListItem button className={listItem} style={{ paddingLeft: (level + 1) * 20 }}
                             disableRipple={disableRipple}
-                            onClick={() => toggleItemActive(it.id, it.status)}
+                            onClick={event => toggleItemActive(event, it)}
                             onContextMenu={this.handleRowContextMenu(it)}>
                             {it.status === TreeItemStatus.PENDING ?
                                 <CircularProgress size={10} className={loader} /> : null}
-                            <i onClick={this.handleToggleItemOpen(it.id, it.status)}
+                            <i onClick={this.handleToggleItemOpen(it)}
                                 className={toggableIconContainer}>
                                 <ListItemIcon className={this.getToggableIconClassNames(it.open, it.active)}>
                                     {this.getProperArrowAnimation(it.status, it.items!)}
@@ -174,9 +174,9 @@ export const Tree = withStyles(styles)(
                 : undefined;
         }
 
-        handleToggleItemOpen = (id: string, status: TreeItemStatus) => (event: React.MouseEvent<HTMLElement>) => {
+        handleToggleItemOpen = (item: TreeItem<T>) => (event: React.MouseEvent<HTMLElement>) => {
             event.stopPropagation();
-            this.props.toggleItemOpen(id, status);
+            this.props.toggleItemOpen(event, item);
         }
     }
 );
index a4a5e366a26b1c93713e580c4ce717a1c4e4a117..16533edab1b708137b74f5eb7de569fe7260d9e3 100644 (file)
@@ -41,7 +41,10 @@ import { progressIndicatorActions } from '~/store/progress-indicator/progress-in
 import { setUuidPrefix } from '~/store/workflow-panel/workflow-panel-actions';
 import { trashedCollectionActionSet } from '~/views-components/context-menu/action-sets/trashed-collection-action-set';
 import { ContainerRequestState } from '~/models/container-request';
-import { MountKind } from './models/mount-types';
+import { MountKind } from '~/models/mount-types';
+import { receiveTreePickerData } from '~/store/tree-picker/tree-picker-actions';
+import { loadProject, loadCollection } from './store/tree-picker/tree-picker-actions';
+import { ResourceKind } from '~/models/resource';
 
 const getBuildNumber = () => "BN-" + (process.env.REACT_APP_BUILD_NUMBER || "dev");
 const getGitCommit = () => "GIT-" + (process.env.REACT_APP_GIT_COMMIT || "latest").substr(0, 7);
@@ -114,24 +117,39 @@ const initListener = (history: History, store: RootStore, services: ServiceRepos
             await store.dispatch(loadWorkbench());
             addRouteChangeHandlers(history, store);
             // createEnumCollectorWorkflow(services);
+            store.dispatch(receiveTreePickerData({
+                id: '',
+                pickerId: 'testPicker',
+                data: [{name: 'Projects', uuid: 'c97qk-tpzed-1k14sp44jonn0n3'}],
+                extractNodeData: value => ({id: value.uuid, value}),
+            }));
+            await store.dispatch(loadProject(
+                'c97qk-tpzed-1k14sp44jonn0n3',
+                'testPicker',
+                [ResourceKind.COLLECTION],
+            ));
+            await store.dispatch(loadCollection(
+                'c97qk-4zz18-9sn8ygaf62chkkd',
+                'testPicker',
+            ));
         }
     };
 };
 
-const createPrimitivesCollectorWorkflow = ({workflowService}:ServiceRepository) => {
+const createPrimitivesCollectorWorkflow = ({ workflowService }: ServiceRepository) => {
     workflowService.create({
-            name: 'Primitive values collector',
-            description: 'Workflow for collecting primitive values',
-            definition: "cwlVersion: v1.0\n$graph:\n- class: CommandLineTool\n  requirements:\n  - listing:\n    - entryname: input_collector.log\n      entry: |\n        \"flag\":\n          $(inputs.example_flag)\n        \"string\":\n          $(inputs.example_string)\n        \"int\":\n          $(inputs.example_int)\n        \"long\":\n          $(inputs.example_long)\n        \"float\":\n          $(inputs.example_float)\n        \"double\":\n          $(inputs.example_double)\n    class: InitialWorkDirRequirement\n  inputs:\n  - type: double\n    id: '#input_collector.cwl/example_double'\n  - type: boolean\n    id: '#input_collector.cwl/example_flag'\n  - type: float\n    id: '#input_collector.cwl/example_float'\n  - type: int\n    id: '#input_collector.cwl/example_int'\n  - type: long\n    id: '#input_collector.cwl/example_long'\n  - type: string\n    id: '#input_collector.cwl/example_string'\n  outputs:\n  - type: File\n    outputBinding:\n      glob: '*'\n    id: '#input_collector.cwl/output'\n  baseCommand: [echo]\n  id: '#input_collector.cwl'\n- class: Workflow\n  doc: Workflw for collecting primitive values\n  inputs:\n  - type: double\n    label: Double value\n    doc: This should allow for entering a decimal number (64-bit).\n    id: '#main/example_double'\n    default: 0.3333333333333333\n  - type: boolean\n    label: Boolean Flag\n    doc: This should render as in checkbox.\n    id: '#main/example_flag'\n    default: true\n  - type: float\n    label: Float value\n    doc: This should allow for entering a decimal number (32-bit).\n    id: '#main/example_float'\n    default: 0.15625\n  - type: int\n    label: Integer Number\n    doc: This should allow for entering a number (32-bit signed).\n    id: '#main/example_int'\n    default: 2147483647\n  - type: long\n    label: Long Number\n    doc: This should allow for entering a number (64-bit signed).\n    id: '#main/example_long'\n    default: 9223372036854775807\n  - type: string\n    label: Freetext\n    doc: This should allow for entering an arbitrary char sequence.\n    id: '#main/example_string'\n    default: This is a string\n  outputs:\n  - type: File\n    outputSource: '#main/input_collector/output'\n    id: '#main/log_file'\n  steps:\n  - run: '#input_collector.cwl'\n    in:\n    - source: '#main/example_double'\n      id: '#main/input_collector/example_double'\n    - source: '#main/example_flag'\n      id: '#main/input_collector/example_flag'\n    - source: '#main/example_float'\n      id: '#main/input_collector/example_float'\n    - source: '#main/example_int'\n      id: '#main/input_collector/example_int'\n    - source: '#main/example_long'\n      id: '#main/input_collector/example_long'\n    - source: '#main/example_string'\n      id: '#main/input_collector/example_string'\n    out: ['#main/input_collector/output']\n    id: '#main/input_collector'\n  id: '#main'\n",
-        });
+        name: 'Primitive values collector',
+        description: 'Workflow for collecting primitive values',
+        definition: "cwlVersion: v1.0\n$graph:\n- class: CommandLineTool\n  requirements:\n  - listing:\n    - entryname: input_collector.log\n      entry: |\n        \"flag\":\n          $(inputs.example_flag)\n        \"string\":\n          $(inputs.example_string)\n        \"int\":\n          $(inputs.example_int)\n        \"long\":\n          $(inputs.example_long)\n        \"float\":\n          $(inputs.example_float)\n        \"double\":\n          $(inputs.example_double)\n    class: InitialWorkDirRequirement\n  inputs:\n  - type: double\n    id: '#input_collector.cwl/example_double'\n  - type: boolean\n    id: '#input_collector.cwl/example_flag'\n  - type: float\n    id: '#input_collector.cwl/example_float'\n  - type: int\n    id: '#input_collector.cwl/example_int'\n  - type: long\n    id: '#input_collector.cwl/example_long'\n  - type: string\n    id: '#input_collector.cwl/example_string'\n  outputs:\n  - type: File\n    outputBinding:\n      glob: '*'\n    id: '#input_collector.cwl/output'\n  baseCommand: [echo]\n  id: '#input_collector.cwl'\n- class: Workflow\n  doc: Workflw for collecting primitive values\n  inputs:\n  - type: double\n    label: Double value\n    doc: This should allow for entering a decimal number (64-bit).\n    id: '#main/example_double'\n    default: 0.3333333333333333\n  - type: boolean\n    label: Boolean Flag\n    doc: This should render as in checkbox.\n    id: '#main/example_flag'\n    default: true\n  - type: float\n    label: Float value\n    doc: This should allow for entering a decimal number (32-bit).\n    id: '#main/example_float'\n    default: 0.15625\n  - type: int\n    label: Integer Number\n    doc: This should allow for entering a number (32-bit signed).\n    id: '#main/example_int'\n    default: 2147483647\n  - type: long\n    label: Long Number\n    doc: This should allow for entering a number (64-bit signed).\n    id: '#main/example_long'\n    default: 9223372036854775807\n  - type: string\n    label: Freetext\n    doc: This should allow for entering an arbitrary char sequence.\n    id: '#main/example_string'\n    default: This is a string\n  outputs:\n  - type: File\n    outputSource: '#main/input_collector/output'\n    id: '#main/log_file'\n  steps:\n  - run: '#input_collector.cwl'\n    in:\n    - source: '#main/example_double'\n      id: '#main/input_collector/example_double'\n    - source: '#main/example_flag'\n      id: '#main/input_collector/example_flag'\n    - source: '#main/example_float'\n      id: '#main/input_collector/example_float'\n    - source: '#main/example_int'\n      id: '#main/input_collector/example_int'\n    - source: '#main/example_long'\n      id: '#main/input_collector/example_long'\n    - source: '#main/example_string'\n      id: '#main/input_collector/example_string'\n    out: ['#main/input_collector/output']\n    id: '#main/input_collector'\n  id: '#main'\n",
+    });
 };
 
-const createEnumCollectorWorkflow = ({workflowService}:ServiceRepository) => {
+const createEnumCollectorWorkflow = ({ workflowService }: ServiceRepository) => {
     workflowService.create({
-            name: 'Enum values collector',
-            description: 'Workflow for collecting enum values',
-            definition: "cwlVersion: v1.0\n$graph:\n- class: CommandLineTool\n  requirements:\n  - listing:\n    - entryname: input_collector.log\n      entry: |\n        \"enum_type\":\n          $(inputs.enum_type)\n\n    class: InitialWorkDirRequirement\n  inputs:\n  - type:\n      type: enum\n      symbols: ['#input_collector.cwl/enum_type/OTU table', '#input_collector.cwl/enum_type/Pathway\n          table', '#input_collector.cwl/enum_type/Function table', '#input_collector.cwl/enum_type/Ortholog\n          table']\n    id: '#input_collector.cwl/enum_type'\n  outputs:\n  - type: File\n    outputBinding:\n      glob: '*'\n    id: '#input_collector.cwl/output'\n  baseCommand: [echo]\n  id: '#input_collector.cwl'\n- class: Workflow\n  doc: This is the description of the workflow\n  inputs:\n  - type:\n      type: enum\n      symbols: ['#main/enum_type/OTU table', '#main/enum_type/Pathway table', '#main/enum_type/Function\n          table', '#main/enum_type/Ortholog table']\n      name: '#enum_typef4179c7f-45f9-482d-a5db-1abb86698384'\n    label: Enumeration Type\n    doc: This should render as a drop-down menu.\n    id: '#main/enum_type'\n    default: OTU table\n  outputs:\n  - type: File\n    outputSource: '#main/input_collector/output'\n    id: '#main/log_file'\n  steps:\n  - run: '#input_collector.cwl'\n    in:\n    - source: '#main/enum_type'\n      id: '#main/input_collector/enum_type'\n    out: ['#main/input_collector/output']\n    id: '#main/input_collector'\n  id: '#main'\n",
-        });
+        name: 'Enum values collector',
+        description: 'Workflow for collecting enum values',
+        definition: "cwlVersion: v1.0\n$graph:\n- class: CommandLineTool\n  requirements:\n  - listing:\n    - entryname: input_collector.log\n      entry: |\n        \"enum_type\":\n          $(inputs.enum_type)\n\n    class: InitialWorkDirRequirement\n  inputs:\n  - type:\n      type: enum\n      symbols: ['#input_collector.cwl/enum_type/OTU table', '#input_collector.cwl/enum_type/Pathway\n          table', '#input_collector.cwl/enum_type/Function table', '#input_collector.cwl/enum_type/Ortholog\n          table']\n    id: '#input_collector.cwl/enum_type'\n  outputs:\n  - type: File\n    outputBinding:\n      glob: '*'\n    id: '#input_collector.cwl/output'\n  baseCommand: [echo]\n  id: '#input_collector.cwl'\n- class: Workflow\n  doc: This is the description of the workflow\n  inputs:\n  - type:\n      type: enum\n      symbols: ['#main/enum_type/OTU table', '#main/enum_type/Pathway table', '#main/enum_type/Function\n          table', '#main/enum_type/Ortholog table']\n      name: '#enum_typef4179c7f-45f9-482d-a5db-1abb86698384'\n    label: Enumeration Type\n    doc: This should render as a drop-down menu.\n    id: '#main/enum_type'\n    default: OTU table\n  outputs:\n  - type: File\n    outputSource: '#main/input_collector/output'\n    id: '#main/log_file'\n  steps:\n  - run: '#input_collector.cwl'\n    in:\n    - source: '#main/enum_type'\n      id: '#main/input_collector/enum_type'\n    out: ['#main/input_collector/output']\n    id: '#main/input_collector'\n  id: '#main'\n",
+    });
 };
 
 const createSampleProcess = ({ containerRequestService }: ServiceRepository) => {
@@ -157,15 +175,15 @@ const createSampleProcess = ({ containerRequestService }: ServiceRepository) =>
                             "class": "CommandLineTool",
                             "requirements": [
                                 {
-                                  "listing": [
-                                    {
-                                      "entryname": "input_collector.log",
-                                      "entry": "$(inputs.single_file.basename)\n"
-                                    }
-                                  ],
-                                  "class": "InitialWorkDirRequirement"
+                                    "listing": [
+                                        {
+                                            "entryname": "input_collector.log",
+                                            "entry": "$(inputs.single_file.basename)\n"
+                                        }
+                                    ],
+                                    "class": "InitialWorkDirRequirement"
                                 }
-                              ],
+                            ],
                             "inputs": [
                                 {
                                     "type": "File",
@@ -231,7 +249,7 @@ const createSampleProcess = ({ containerRequestService }: ServiceRepository) =>
                         "class": "File",
                         "location": "keep:233454526794c0a2d56a305baeff3d30+145/1.txt",
                         "basename": "fileA"
-                      }
+                    }
                 },
             }
         },
index 0385766f5fc76f9d055a933dcb0ad461ef1b5812..f0f6bfcd63a02bf384b0d9af6cb7e6005368018a 100644 (file)
@@ -3,8 +3,15 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { unionize, ofType, UnionOf } from "~/common/unionize";
-import { TreeNode } from '~/models/tree';
-
+import { TreeNode, initTreeNode, getNodeDescendants, getNodeDescendantsIds, getNodeValue, TreeNodeStatus } from '~/models/tree';
+import { Dispatch } from 'redux';
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
+import { FilterBuilder } from '~/services/api/filter-builder';
+import { pipe } from 'lodash/fp';
+import { ResourceKind } from '~/models/resource';
+import { GroupContentsResource } from '../../services/groups-service/groups-service';
+import { CollectionDirectory, CollectionFile } from '../../models/collection-file';
 
 export const treePickerActions = unionize({
     LOAD_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
@@ -17,3 +24,61 @@ export const treePickerActions = unionize({
 });
 
 export type TreePickerAction = UnionOf<typeof treePickerActions>;
+
+interface ReceiveTreePickerDataParams<T> {
+    data: T[];
+    extractNodeData: (value: T) => { id: string, value: T, status?: TreeNodeStatus };
+    id: string;
+    pickerId: string;
+}
+export const receiveTreePickerData = <T>(params: ReceiveTreePickerDataParams<T>) =>
+    (dispatch: Dispatch) => {
+        const { data, extractNodeData, id, pickerId, } = params;
+        dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
+            id,
+            nodes: data.map(item => initTreeNode(extractNodeData(item))),
+            pickerId,
+        }));
+        dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+    };
+
+export const loadProject = (id: string, pickerId: string, include?: ResourceKind[]) =>
+    async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
+        dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
+
+        const filters = pipe(
+            (fb: FilterBuilder) => fb.addEqual('ownerUuid', id),
+            fb => include ? fb.addIsA('uuid', include) : fb,
+            fb => fb.getFilters(),
+        )(new FilterBuilder());
+
+        const { items } = await services.groupsService.contents(id, { filters });
+
+        dispatch<any>(receiveTreePickerData<GroupContentsResource>({
+            id,
+            pickerId,
+            data: items,
+            extractNodeData: item => ({ id: item.uuid, value: item }),
+        }));
+    };
+
+export const loadCollection = (id: string, pickerId: string) =>
+    async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
+        dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
+
+        const files = await services.collectionService.files(id);
+        const data = getNodeDescendants('')(files).map(node => node.value);
+
+        dispatch<any>(receiveTreePickerData<CollectionDirectory | CollectionFile>({
+            id,
+            pickerId,
+            data,
+            extractNodeData: value => {
+                return {
+                    id: value.id,
+                    value,
+                    status: TreeNodeStatus.LOADED,
+                };
+            },
+        }));
+    };
index 16ab12284bf025a353a0958e3313bfef3bda06ec..36104d43624dd37746794e232e140b2cd412f5f7 100644 (file)
@@ -18,21 +18,21 @@ import { FilterBuilder } from "~/services/api/filter-builder";
 import { WrappedFieldProps } from 'redux-form';
 import { initTreeNode } from '~/models/tree';
 
-type ProjectTreePickerProps = Pick<TreePickerProps, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
+type ProjectTreePickerProps = Pick<TreePickerProps<ProjectResource>, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
 
 const mapDispatchToProps = (dispatch: Dispatch, props: { onChange: (projectUuid: string) => void }): ProjectTreePickerProps => ({
     onContextMenu: () => { return; },
-    toggleItemActive: (id, status, pickerId) => {
+    toggleItemActive: (_, { id }, pickerId) => {
         getNotSelectedTreePickerKind(pickerId)
             .forEach(pickerId => dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id: '', pickerId })));
         dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id, pickerId }));
 
         props.onChange(id);
     },
-    toggleItemOpen: (id, status, pickerId) => {
+    toggleItemOpen: (_, { id, status }, pickerId) => {
         dispatch<any>(toggleItemOpen(id, status, pickerId));
     },
-    toggleItemSelection: (id, pickerId) => {
+    toggleItemSelection: (_, { id }, pickerId) => {
         dispatch<any>(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECTION({ id, pickerId }));
     },
 });
index 8c1ed33059b75bb1ad3056ad2c9f34414eae4374..fe808af5c7c3e7e7124dcd68fb77a46558568ac7 100644 (file)
@@ -19,15 +19,15 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     }
 });
 
-export interface ProjectTreeProps {
+export interface ProjectTreeProps<T> {
     projects: Array<TreeItem<ProjectResource>>;
-    toggleOpen: (id: string, status: TreeItemStatus) => void;
-    toggleActive: (id: string, status: TreeItemStatus) => void;
+    toggleOpen: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
+    toggleActive: (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>) => void;
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: TreeItem<ProjectResource>) => void;
 }
 
 export const ProjectTree = withStyles(styles)(
-    class ProjectTreeGeneric<T> extends React.Component<ProjectTreeProps & WithStyles<CssRules>> {
+    class ProjectTreeGeneric<T> extends React.Component<ProjectTreeProps<T> & WithStyles<CssRules>> {
         render(): ReactElement<any> {
             const { classes, projects, toggleOpen, toggleActive, onContextMenu } = this.props;
             return (
@@ -42,8 +42,8 @@ export const ProjectTree = withStyles(styles)(
                                     icon={ProjectIcon}
                                     name={project.data.name}
                                     isActive={project.active}
-                                    hasMargin={true}/>
-                        }/>
+                                    hasMargin={true} />
+                        } />
                 </div>
             );
         }
index 96224cfa7967dd26c729ce694f525f11493d88c5..0e34d3ad84704f7f123c5bf3fb5a47b79b0a8e5a 100644 (file)
@@ -19,18 +19,18 @@ export interface SidePanelTreeProps {
     sidePanelProgress?: boolean;
 }
 
-type SidePanelTreeActionProps = Pick<TreePickerProps, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
+type SidePanelTreeActionProps = Pick<TreePickerProps<ProjectResource | string>, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
 
 const mapDispatchToProps = (dispatch: Dispatch, props: SidePanelTreeProps): SidePanelTreeActionProps => ({
-    onContextMenu: (event, id) => {
+    onContextMenu: (event, { id }) => {
         dispatch<any>(openSidePanelContextMenu(event, id));
     },
-    toggleItemActive: (nodeId) => {
-        dispatch<any>(activateSidePanelTreeItem(nodeId));
-        props.onItemActivation(nodeId);
+    toggleItemActive: (_, { id }) => {
+        dispatch<any>(activateSidePanelTreeItem(id));
+        props.onItemActivation(id);
     },
-    toggleItemOpen: (nodeId) => {
-        dispatch<any>(toggleSidePanelTreeItemCollapse(nodeId));
+    toggleItemOpen: (_, { id }) => {
+        dispatch<any>(toggleSidePanelTreeItemCollapse(id));
     },
     toggleItemSelection: noop,
 });
index ff119132e24c4ba80517df9d5d8d639862f4c424..2e1d98ea8725e838aeb0f65b39bac450532d826c 100644 (file)
@@ -5,22 +5,23 @@
 import { connect } from "react-redux";
 import { Tree, TreeProps, TreeItem, TreeItemStatus } from "~/components/tree/tree";
 import { RootState } from "~/store/store";
-import { getNodeValue, getNodeChildrenIds, Tree as Ttree, createTree, getNode, TreeNodeStatus } from '~/models/tree';
+import { getNodeChildrenIds, Tree as Ttree, createTree, getNode, TreeNodeStatus } from '~/models/tree';
 import { Dispatch } from "redux";
 import { initTreeNode } from '../../models/tree';
 
-export interface TreePickerProps {
+type Callback<T> = (event: React.MouseEvent<HTMLElement>, item: TreeItem<T>, pickerId: string) => void;
+export interface TreePickerProps<T> {
     pickerId: string;
-    onContextMenu: (event: React.MouseEvent<HTMLElement>, nodeId: string, pickerId: string) => void;
-    toggleItemOpen: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
-    toggleItemActive: (nodeId: string, status: TreeItemStatus, pickerId: string) => void;
-    toggleItemSelection: (nodeId: string, pickerId: string) => void;
+    onContextMenu: Callback<T>;
+    toggleItemOpen: Callback<T>;
+    toggleItemActive: Callback<T>;
+    toggleItemSelection: Callback<T>;
 }
 
 const memoizedMapStateToProps = () => {
     let prevTree: Ttree<any>;
     let mappedProps: Pick<TreeProps<any>, 'items'>;
-    return (state: RootState, props: TreePickerProps): Pick<TreeProps<any>, 'items'> => {
+    return <T>(state: RootState, props: TreePickerProps<T>): Pick<TreeProps<T>, 'items'> => {
         const tree = state.treePicker[props.pickerId] || createTree();
         if (tree !== prevTree) {
             prevTree = tree;
@@ -33,11 +34,11 @@ const memoizedMapStateToProps = () => {
     };
 };
 
-const mapDispatchToProps = (dispatch: Dispatch, props: TreePickerProps): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive' | 'toggleItemSelection'> => ({
-    onContextMenu: (event, item) => props.onContextMenu(event, item.id, props.pickerId),
-    toggleItemActive: (id, status) => props.toggleItemActive(id, status, props.pickerId),
-    toggleItemOpen: (id, status) => props.toggleItemOpen(id, status, props.pickerId),
-    toggleItemSelection: (_, item) => props.toggleItemSelection(item.id, props.pickerId),
+const mapDispatchToProps = (_: Dispatch, props: TreePickerProps<any>): Pick<TreeProps<any>, 'onContextMenu' | 'toggleItemOpen' | 'toggleItemActive' | 'toggleItemSelection'> => ({
+    onContextMenu: (event, item) => props.onContextMenu(event, item, props.pickerId),
+    toggleItemActive: (event, item) => props.toggleItemActive(event, item, props.pickerId),
+    toggleItemOpen: (event, item) => props.toggleItemOpen(event, item, props.pickerId),
+    toggleItemSelection: (event, item) => props.toggleItemSelection(event, item, props.pickerId),
 });
 
 export const TreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)(Tree);
index 49ce4725bbc4a4c9b964064154b6946313af04d9..407488b6d6a78df6164bc9096fe2152daa0fde68 100644 (file)
@@ -41,6 +41,10 @@ import { SharedWithMePanel } from '~/views/shared-with-me-panel/shared-with-me-p
 import { RunProcessPanel } from '~/views/run-process-panel/run-process-panel';
 import SplitterLayout from 'react-splitter-layout';
 import { WorkflowPanel } from '~/views/workflow-panel/workflow-panel';
+import { TreePicker } from '../../views-components/tree-picker/tree-picker';
+import { noop } from 'lodash';
+import { TreeItem } from '~/components/tree/tree';
+import { GroupContentsResource } from '~/services/groups-service/groups-service';
 
 type CssRules = 'root' | 'container' | 'splitter' | 'asidePanel' | 'contentWrapper' | 'content';
 
@@ -74,8 +78,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 
 type WorkbenchPanelProps = WithStyles<CssRules>;
 
-export const WorkbenchPanel = 
-    withStyles(styles)(({ classes }: WorkbenchPanelProps) => 
+export const WorkbenchPanel =
+    withStyles(styles)(({ classes }: WorkbenchPanelProps) =>
         <Grid container item xs className={classes.root}>
             <Grid container item xs className={classes.container}>
                 <SplitterLayout customClassName={classes.splitter} percentage={true}
@@ -88,6 +92,14 @@ export const WorkbenchPanel =
                             <MainContentBar />
                         </Grid>
                         <Grid item xs className={classes.content}>
+                            <TreePicker
+                                render={({ data }: TreeItem<GroupContentsResource>) =>
+                                    <p>{JSON.stringify(data.name)}</p>}
+                                pickerId='testPicker'
+                                onContextMenu={noop}
+                                toggleItemOpen={noop}
+                                toggleItemActive={item => console.log(item)}
+                                toggleItemSelection={noop} />
                             <Switch>
                                 <Route path={Routes.PROJECTS} component={ProjectPanel} />
                                 <Route path={Routes.COLLECTIONS} component={CollectionPanel} />