[ARVADOS-WORKBENCH2] created: cf035c0589f045849ba635a82b6857fdd6f618ed

Git user git at public.curoverse.com
Thu May 31 03:18:50 EDT 2018


        at  cf035c0589f045849ba635a82b6857fdd6f618ed (commit)


commit cf035c0589f045849ba635a82b6857fdd6f618ed
Author: Daniel Kos <daniel.kos at contractors.roche.com>
Date:   Thu May 31 09:16:32 2018 +0200

    Add tree structure rendering
    
    Feature #13535
    
    Arvados-DCO-1.1-Signed-off-by: Daniel Kos <daniel.kos at contractors.roche.com>:

diff --git a/package.json b/package.json
index b213bb5..13f88a4 100644
--- a/package.json
+++ b/package.json
@@ -4,6 +4,7 @@
   "private": true,
   "dependencies": {
     "@material-ui/core": "1.0.0",
+    "lodash": "4.17.10",
     "react": "16.3.2",
     "react-dom": "16.3.2",
     "react-redux": "5.0.7",
@@ -32,6 +33,7 @@
     "@types/react-router-dom": "4.2.6",
     "@types/react-router-redux": "5.0.14",
     "@types/redux-devtools": "3.0.44",
+    "@types/lodash": "4.14.109",
     "typescript": "2.8.3"
   },
   "moduleNameMapper": {
diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx
index 0c99db8..e8ba710 100644
--- a/src/components/tree/tree.tsx
+++ b/src/components/tree/tree.tsx
@@ -6,20 +6,39 @@ import * as React from 'react';
 import List from "@material-ui/core/List/List";
 import ListItem from "@material-ui/core/ListItem/ListItem";
 import { ReactElement } from "react";
+import Collapse from "@material-ui/core/Collapse/Collapse";
+
+export interface TreeItem<T> {
+    data: T;
+    id: string;
+    open: boolean;
+    items?: Array<TreeItem<T>>;
+}
 
 interface TreeProps<T> {
-    items: T[],
-    render: (item: T) => ReactElement<{}>
+    items?: Array<TreeItem<T>>;
+    render: (item: T) => ReactElement<{}>;
+    toggleItem: (id: string) => any;
+    level?: number;
 }
 
 class Tree<T> extends React.Component<TreeProps<T>, {}> {
-    render() {
-        return <List>
-            {this.props.items.map((it: T, idx: number) =>
-                <ListItem key={`item/${idx}`} button>
-                    {this.props.render(it)}
+    render(): ReactElement<any> {
+        const level = this.props.level ? this.props.level : 0;
+        return <List component="div">
+            {this.props.items && this.props.items.map((it: TreeItem<T>, idx: number) =>
+             <div key={`item/${level}/${idx}`}>
+                <ListItem button onClick={() => this.props.toggleItem(it.id)} style={{paddingLeft: (level + 1) * 30}}>
+                    {this.props.render(it.data)}
                 </ListItem>
-            )}
+                {it.items && it.items.length > 0 &&
+                <Collapse in={it.open} timeout="auto" unmountOnExit>
+                    <Tree items={it.items}
+                        render={this.props.render}
+                        toggleItem={this.props.toggleItem}
+                        level={level + 1}/>
+                </Collapse>}
+             </div>)}
         </List>
     }
 }
diff --git a/src/index.tsx b/src/index.tsx
index dacada2..ae16dce 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -12,14 +12,44 @@ import { Route, Router } from "react-router";
 import createBrowserHistory from "history/createBrowserHistory";
 import configureStore from "./store/store";
 import { ConnectedRouter } from "react-router-redux";
+import { TreeItem } from "./components/tree/tree";
+import { Project } from "./models/project";
+
+const sampleProjects = [
+    [
+        'Project 1', [
+            ['Project 1.1', [['Project 1.1.1'], ['Project 1.1.2']]],
+            ['Project 1.2', [['Project 1.2.1'], ['Project 1.2.2'], ['Project 1.2.3']]]
+        ]
+    ],
+    [
+        'Project 2'
+    ],
+    [
+        'Project 3', [['Project 3.1'], ['Project 3.2']]
+    ]
+];
+
+
+function buildProjectTree(tree: any[], level = 0): Array<TreeItem<Project>> {
+    const projects = tree.map((t, idx) => ({
+        id: `l${level}i${idx}`,
+        open: false,
+        data: {
+            name: t[0],
+            createdAt: '2018-05-05',
+        },
+        items: t.length > 1 ? buildProjectTree(t[1], level + 1) : []
+    }));
+    return projects;
+}
+
 
 const history = createBrowserHistory();
+const projects = buildProjectTree(sampleProjects);
+
 const store = configureStore({
-    projects: [
-        { name: 'Mouse genome', createdAt: '2018-05-01' },
-        { name: 'Human body', createdAt: '2018-05-01' },
-        { name: 'Secret operation', createdAt: '2018-05-01' }
-    ],
+    projects,
     router: {
         location: null
     }
diff --git a/src/store/project-action.ts b/src/store/project-action.ts
index 784adb3..7cd3bac 100644
--- a/src/store/project-action.ts
+++ b/src/store/project-action.ts
@@ -7,7 +7,8 @@ import { Project } from "../models/project";
 
 export const actions = {
     createProject: createStandardAction('@@project/create')<Project>(),
-    removeProject: createStandardAction('@@project/remove')<string>()
+    removeProject: createStandardAction('@@project/remove')<string>(),
+    toggleProjectTreeItem: createStandardAction('@@project/toggleTreeItem')<string>()
 };
 
 export type ProjectAction = ActionType<typeof actions>;
diff --git a/src/store/project-reducer.ts b/src/store/project-reducer.ts
index 5e2d192..f1056f3 100644
--- a/src/store/project-reducer.ts
+++ b/src/store/project-reducer.ts
@@ -5,14 +5,38 @@
 import { getType } from "typesafe-actions";
 import { Project } from "../models/project";
 import { actions, ProjectAction } from "./project-action";
+import { TreeItem } from "../components/tree/tree";
+import * as _ from 'lodash';
 
-type ProjectState = Project[];
+type ProjectState = Array<TreeItem<Project>>;
+
+function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
+    let item;
+    for (const t of tree) {
+        item = t.id === itemId
+            ? t
+            : findTreeItem(t.items ? t.items : [], itemId);
+        if (item) {
+            break;
+        }
+    }
+    return item;
+}
 
 const projectsReducer = (state: ProjectState = [], action: ProjectAction) => {
     switch (action.type) {
         case getType(actions.createProject): {
             return [...state, action.payload];
         }
+        case getType(actions.toggleProjectTreeItem): {
+            const tree = _.cloneDeep(state);
+            const itemId = action.payload;
+            const item = findTreeItem(tree, itemId);
+            if (item) {
+                item.open = !item.open;
+            }
+            return tree;
+        }
         default:
             return state;
     }
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 365835a..7265380 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -10,13 +10,16 @@ import AppBar from '@material-ui/core/AppBar';
 import Toolbar from '@material-ui/core/Toolbar';
 import Typography from '@material-ui/core/Typography';
 import { connect } from "react-redux";
-import Tree from "../../components/tree/tree";
+import Tree, { TreeItem } from "../../components/tree/tree";
 import { Project } from "../../models/project";
 import { RootState } from "../../store/root-reducer";
 import ProjectList from "../../components/project-list/project-list";
 import { Route, Switch } from "react-router";
 import { Link } from "react-router-dom";
 
+import { actions as projectActions } from "../../store/project-action";
+import ListItemText from "@material-ui/core/ListItemText/ListItemText";
+
 const drawerWidth = 240;
 
 type CssRules = 'root' | 'appBar' | 'drawerPaper' | 'content' | 'toolbar';
@@ -49,7 +52,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
 });
 
 interface WorkbenchProps {
-    projects: Project[]
+    projects: Array<TreeItem<Project>>;
+    toggleProjectTreeItem: (id: string) => any;
 }
 
 interface WorkbenchState {
@@ -58,7 +62,6 @@ interface WorkbenchState {
 class Workbench extends React.Component<WorkbenchProps & WithStyles<CssRules>, WorkbenchState> {
     render() {
         const {classes} = this.props;
-
         return (
             <div className={classes.root}>
                 <AppBar position="absolute" className={classes.appBar}>
@@ -74,9 +77,10 @@ class Workbench extends React.Component<WorkbenchProps & WithStyles<CssRules>, W
                         paper: classes.drawerPaper,
                     }}>
                     <div className={classes.toolbar}/>
-                    <Tree items={this.props.projects} render={(p: Project) =>
-                        <Link to={`/project/${p.name}`}>{p.name}</Link>
-                    }/>
+                    <Tree items={this.props.projects}
+                        toggleItem={this.props.toggleProjectTreeItem}
+                        render={(p: Project) => <ListItemText primary={p.name}/>}
+                        />
                 </Drawer>
                 <main className={classes.content}>
                     <div className={classes.toolbar}/>
@@ -92,10 +96,12 @@ class Workbench extends React.Component<WorkbenchProps & WithStyles<CssRules>, W
     }
 }
 
-export default connect<WorkbenchProps>(
+export default connect(
     (state: RootState) => ({
         projects: state.projects
-    })
+    }), {
+        toggleProjectTreeItem: (id: string) => projectActions.toggleProjectTreeItem(id)
+    }
 )(
     withStyles(styles)(Workbench)
 );
diff --git a/tslint.json b/tslint.json
index 18630ab..1b26ab5 100644
--- a/tslint.json
+++ b/tslint.json
@@ -9,7 +9,9 @@
     "member-access": false,
     "jsx-boolean-value": false,
     "jsx-no-lambda": false,
-    "no-debugger": false
+    "no-debugger": false,
+    "no-console": false,
+    "no-shadowed-variable": false
   },
   "linterOptions": {
     "exclude": [
diff --git a/yarn.lock b/yarn.lock
index 93702cd..6d5ec0d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -70,6 +70,10 @@
     csstype "^2.0.0"
     indefinite-observable "^1.0.1"
 
+"@types/lodash@^4.14.109":
+  version "4.14.109"
+  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.109.tgz#b1c4442239730bf35cabaf493c772b18c045886d"
+
 "@types/node@*", "@types/node at 10.1.2":
   version "10.1.2"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.1.2.tgz#1b928a0baa408fc8ae3ac012cc81375addc147c6"
@@ -5914,7 +5918,7 @@ react-redux at 5.0.7:
     loose-envify "^1.1.0"
     prop-types "^15.6.0"
 
-react-router-dom@^4.2.2:
+react-router-dom at 4.2.2:
   version "4.2.2"
   resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d"
   dependencies:

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list