[ARVADOS-WORKBENCH2] created: 1.1.4-32-g9999a9d
Git user
git at public.curoverse.com
Fri Jun 15 09:50:25 EDT 2018
at 9999a9db9fede0e1971dc792389982b428a1bb19 (commit)
commit 9999a9db9fede0e1971dc792389982b428a1bb19
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date: Fri Jun 15 15:34:59 2018 +0200
Tree-component-adjustments-for-dynamic-contents
Feature #13618
Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
diff --git a/src/components/project-tree/project-tree.test.tsx b/src/components/project-tree/project-tree.test.tsx
index d42df08..e88e55a 100644
--- a/src/components/project-tree/project-tree.test.tsx
+++ b/src/components/project-tree/project-tree.test.tsx
@@ -8,6 +8,7 @@ import * as Enzyme from 'enzyme';
import * as Adapter from 'enzyme-adapter-react-16';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import { Collapse } from '@material-ui/core';
+import CircularProgress from '@material-ui/core/CircularProgress';
import ProjectTree from './project-tree';
import { TreeItem } from '../tree/tree';
@@ -16,7 +17,7 @@ Enzyme.configure({ adapter: new Adapter() });
describe("ProjectTree component", () => {
- it("checks is there ListItemIcon in the ProjectTree component", () => {
+ it("should render ListItemIcon", () => {
const project: TreeItem<Project> = {
data: {
name: "sample name",
@@ -28,14 +29,15 @@ describe("ProjectTree component", () => {
},
id: "3",
open: true,
- active: true
+ active: true,
+ status: 1
};
const wrapper = mount(<ProjectTree projects={[project]} toggleProjectTreeItem={() => { }} />);
expect(wrapper.find(ListItemIcon).length).toEqual(1);
});
- it("checks are there two ListItemIcon's in the ProjectTree component", () => {
+ it("should render 2 ListItemIcons", () => {
const project: Array<TreeItem<Project>> = [
{
data: {
@@ -48,7 +50,8 @@ describe("ProjectTree component", () => {
},
id: "3",
open: false,
- active: true
+ active: true,
+ status: 1
},
{
data: {
@@ -61,7 +64,8 @@ describe("ProjectTree component", () => {
},
id: "3",
open: false,
- active: true
+ active: true,
+ status: 1
}
];
const wrapper = mount(<ProjectTree projects={project} toggleProjectTreeItem={() => { }} />);
@@ -69,7 +73,45 @@ describe("ProjectTree component", () => {
expect(wrapper.find(ListItemIcon).length).toEqual(2);
});
- it("check ProjectTree, when open is changed", () => {
+ it("should render Collapse", () => {
+ const project: Array<TreeItem<Project>> = [
+ {
+ data: {
+ name: "sample name",
+ createdAt: "2018-06-12",
+ modifiedAt: "2018-06-13",
+ uuid: "uuid",
+ ownerUuid: "ownerUuid",
+ href: "href",
+ },
+ id: "3",
+ open: true,
+ active: true,
+ status: 2,
+ items: [
+ {
+ data: {
+ name: "sample name",
+ createdAt: "2018-06-12",
+ modifiedAt: "2018-06-13",
+ uuid: "uuid",
+ ownerUuid: "ownerUuid",
+ href: "href",
+ },
+ id: "3",
+ open: true,
+ active: true,
+ status: 1
+ }
+ ]
+ }
+ ];
+ const wrapper = mount(<ProjectTree projects={project} toggleProjectTreeItem={() => { }} />);
+
+ expect(wrapper.find(Collapse).length).toEqual(1);
+ });
+
+ it("should render CircularProgress", () => {
const project: TreeItem<Project> = {
data: {
name: "sample name",
@@ -80,27 +122,12 @@ describe("ProjectTree component", () => {
href: "href",
},
id: "3",
- open: true,
+ open: false,
active: true,
- items: [
- {
- data: {
- name: "sample name",
- createdAt: "2018-06-12",
- modifiedAt: "2018-06-13",
- uuid: "uuid",
- ownerUuid: "ownerUuid",
- href: "href",
- },
- id: "4",
- open: false,
- active: true
- }
- ]
+ status: 1
};
const wrapper = mount(<ProjectTree projects={[project]} toggleProjectTreeItem={() => { }} />);
- wrapper.setState({open: true });
- expect(wrapper.find(Collapse).length).toEqual(1);
+ expect(wrapper.find(CircularProgress).length).toEqual(1);
});
});
diff --git a/src/components/project-tree/project-tree.tsx b/src/components/project-tree/project-tree.tsx
index 5243b5e..56b3da3 100644
--- a/src/components/project-tree/project-tree.tsx
+++ b/src/components/project-tree/project-tree.tsx
@@ -9,7 +9,7 @@ import ListItemText from "@material-ui/core/ListItemText/ListItemText";
import ListItemIcon from '@material-ui/core/ListItemIcon';
import Typography from '@material-ui/core/Typography';
-import Tree, { TreeItem } from '../tree/tree';
+import Tree, { TreeItem, TreeItemStatus } from '../tree/tree';
import { Project } from '../../models/project';
type CssRules = 'active' | 'listItemText' | 'row' | 'treeContainer';
@@ -27,9 +27,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
marginLeft: '20px',
},
treeContainer: {
- position: 'absolute',
overflowX: 'visible',
- marginTop: '80px',
+ overflowY: 'auto',
minWidth: '240px',
whiteSpace: 'nowrap',
}
@@ -37,7 +36,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
export interface ProjectTreeProps {
projects: Array<TreeItem<Project>>;
- toggleProjectTreeItem: (id: string) => void;
+ toggleProjectTreeItem: (id: string, status: TreeItemStatus) => void;
}
class ProjectTree<T> extends React.Component<ProjectTreeProps & WithStyles<CssRules>> {
diff --git a/src/components/tree/tree.test.tsx b/src/components/tree/tree.test.tsx
index ffdc74f..0fab2f3 100644
--- a/src/components/tree/tree.test.tsx
+++ b/src/components/tree/tree.test.tsx
@@ -1,7 +1,55 @@
// Copyright (C) The Arvados Authors. All rights reserved.
//
// SPDX-License-Identifier: AGPL-3.0
+import * as React from 'react';
+import { mount } from 'enzyme';
+import * as Enzyme from 'enzyme';
+import * as Adapter from 'enzyme-adapter-react-16';
+import { Collapse } from '@material-ui/core';
+import CircularProgress from '@material-ui/core/CircularProgress';
+import ListItem from "@material-ui/core/ListItem/ListItem";
-it("should render the tree", () => {
- expect(true).toBe(true);
-});
\ No newline at end of file
+import Tree, {TreeItem} from './tree';
+import { Project } from '../../models/project';
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("ProjectTree component", () => {
+
+ it("should render ListItem", () => {
+ const project: TreeItem<Project> = {
+ data: {
+ name: "sample name",
+ createdAt: "2018-06-12",
+ modifiedAt: "2018-06-13",
+ uuid: "uuid",
+ ownerUuid: "ownerUuid",
+ href: "href",
+ },
+ id: "3",
+ open: true,
+ active: true,
+ loading: true,
+ };
+ const wrapper = mount(<Tree render={project => <div/>} toggleItem={() => { }} items={[project]}/>)
+ expect(wrapper.find(ListItem).length).toEqual(1);
+ });
+
+ it("should render arrow", () => {
+ const project: TreeItem<Project> = {
+ data: {
+ name: "sample name",
+ createdAt: "2018-06-12",
+ modifiedAt: "2018-06-13",
+ uuid: "uuid",
+ ownerUuid: "ownerUuid",
+ href: "href",
+ },
+ id: "3",
+ open: true,
+ active: true,
+ loading: true,
+ };
+ const wrapper = mount(<Tree render={project => <div/>} toggleItem={() => { }} items={[project]}/>)
+ expect(wrapper.find('i').length).toEqual(1);
+ });
+});
diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx
index fd6299f..936c596 100644
--- a/src/components/tree/tree.tsx
+++ b/src/components/tree/tree.tsx
@@ -11,7 +11,7 @@ import Collapse from "@material-ui/core/Collapse/Collapse";
import CircularProgress from '@material-ui/core/CircularProgress';
import { inherits } from 'util';
-type CssRules = 'list' | 'activeArrow' | 'arrow' | 'arrowRotate' | 'arrowTransition' | 'loader';
+type CssRules = 'list' | 'activeArrow' | 'inactiveArrow' | 'arrowRotate' | 'arrowTransition' | 'loader' | 'arrowVisibility';
const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
list: {
@@ -22,16 +22,19 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
color: '#4285F6',
position: 'absolute',
},
- arrow: {
+ inactiveArrow: {
position: 'absolute',
},
arrowTransition: {
- transition: 'all 0.3s ease',
+ transition: 'all 0.1s ease',
},
arrowRotate: {
- transition: 'all 0.3s ease',
+ transition: 'all 0.1s ease',
transform: 'rotate(-90deg)',
},
+ arrowVisibility: {
+ opacity: 0,
+ },
loader: {
position: 'absolute',
transform: 'translate(0px)',
@@ -39,35 +42,43 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
}
});
+export enum TreeItemStatus {
+ Initial,
+ Pending,
+ Loaded
+}
+
export interface TreeItem<T> {
data: T;
id: string;
open: boolean;
active: boolean;
- isLoaded: boolean;
+ status: TreeItemStatus;
+ toggled?: boolean;
items?: Array<TreeItem<T>>;
}
interface TreeProps<T> {
items?: Array<TreeItem<T>>;
render: (item: TreeItem<T>, level?: number) => ReactElement<{}>;
- toggleItem: (id: string) => any;
+ toggleItem: (id: string, status: TreeItemStatus) => any;
level?: number;
}
class Tree<T> extends React.Component<TreeProps<T> & WithStyles<CssRules>, {}> {
- renderArrow (arrowClass: string, open: boolean){
- return <i className={`${arrowClass} ${open ? `fas fa-caret-down ${this.props.classes.arrowTransition}` : `fas fa-caret-down ${this.props.classes.arrowRotate}`}`} />
+ renderArrow (status: TreeItemStatus, arrowClass: string, open: boolean){
+ return <i className={`${arrowClass} ${status === TreeItemStatus.Pending ? this.props.classes.arrowVisibility : ''} ${open ? `fas fa-caret-down ${this.props.classes.arrowTransition}` : `fas fa-caret-down ${this.props.classes.arrowRotate}`}`} />
}
render(): ReactElement<any> {
const level = this.props.level ? this.props.level : 0;
const {classes, render, toggleItem, items} = this.props;
- const {list, arrow, activeArrow, loader} = classes;
+ const {list, inactiveArrow, activeArrow, loader} = classes;
return <List component="div" className={list}>
{items && items.map((it: TreeItem<T>, idx: number) =>
<div key={`item/${level}/${idx}`}>
- <ListItem button onClick={() => toggleItem(it.id)} className={list} style={{paddingLeft: (level + 1) * 20}}>
- {it.isLoaded ? this.renderArrow(it.active ? activeArrow : arrow, it.open) : <CircularProgress size={10} className={loader}/> }
+ <ListItem button onClick={() => toggleItem(it.id, it.status)} className={list} style={{paddingLeft: (level + 1) * 20}}>
+ {it.status === TreeItemStatus.Pending ? <CircularProgress size={10} className={loader}/> : null}
+ {it.toggled && it.items && it.items.length === 0 ? null : this.renderArrow(it.status, it.active ? activeArrow : inactiveArrow, it.open)}
{render(it, level)}
</ListItem>
{it.items && it.items.length > 0 &&
diff --git a/src/services/project-service/project-service.ts b/src/services/project-service/project-service.ts
index e1e490b..e1f4af9 100644
--- a/src/services/project-service/project-service.ts
+++ b/src/services/project-service/project-service.ts
@@ -34,7 +34,7 @@ interface GroupsResponse {
export default class ProjectService {
public getProjectList = (parentUuid?: string) => (dispatch: Dispatch): Promise<Project[]> => {
- dispatch(actions.PROJECTS_REQUEST());
+ dispatch(actions.PROJECTS_REQUEST(parentUuid));
if (parentUuid) {
const fb = new FilterBuilder();
fb.addLike(FilterField.OWNER_UUID, parentUuid);
diff --git a/src/store/project/project-action.ts b/src/store/project/project-action.ts
index 87ecbda..2856de6 100644
--- a/src/store/project/project-action.ts
+++ b/src/store/project/project-action.ts
@@ -8,7 +8,7 @@ import { default as unionize, ofType, UnionOf } from "unionize";
const actions = unionize({
CREATE_PROJECT: ofType<Project>(),
REMOVE_PROJECT: ofType<string>(),
- PROJECTS_REQUEST: {},
+ PROJECTS_REQUEST: ofType<any>(),
PROJECTS_SUCCESS: ofType<{ projects: Project[], parentItemId?: string }>(),
TOGGLE_PROJECT_TREE_ITEM: ofType<string>()
}, {
diff --git a/src/store/project/project-reducer.test.ts b/src/store/project/project-reducer.test.ts
index 9c1ed3b..adfce96 100644
--- a/src/store/project/project-reducer.test.ts
+++ b/src/store/project/project-reducer.test.ts
@@ -39,13 +39,15 @@ describe('project-reducer', () => {
open: false,
id: "test123",
items: [],
- data: project
+ data: project,
+ status: 0
}, {
active: false,
open: false,
id: "test123",
items: [],
- data: project
+ data: project,
+ status: 0
}
]);
});
diff --git a/src/store/project/project-reducer.ts b/src/store/project/project-reducer.ts
index 694235a..8aa69ff 100644
--- a/src/store/project/project-reducer.ts
+++ b/src/store/project/project-reducer.ts
@@ -4,7 +4,7 @@
import { Project } from "../../models/project";
import actions, { ProjectAction } from "./project-action";
-import { TreeItem } from "../../components/tree/tree";
+import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
import * as _ from "lodash";
export type ProjectState = Array<TreeItem<Project>>;
@@ -29,24 +29,19 @@ function resetTreeActivity<T>(tree: Array<TreeItem<T>>) {
}
}
-function resetTreeLoader<T>(tree: Array<TreeItem<T>>) {
- for (const t of tree) {
- t.isLoaded = true;
- resetTreeLoader(t.items ? t.items : []);
- }
-}
-
function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[], parentItemId?: string): Array<TreeItem<Project>> {
- resetTreeLoader(tree)
let treeItem;
if (parentItemId) {
treeItem = findTreeItem(tree, parentItemId);
+ if (treeItem) {
+ treeItem.status = TreeItemStatus.Loaded;
+ }
}
const items = projects.map((p, idx) => ({
id: p.uuid,
open: false,
active: false,
- isLoaded: true,
+ status: TreeItemStatus.Initial,
data: p,
items: []
} as TreeItem<Project>));
@@ -59,12 +54,18 @@ function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[],
return items;
}
-
const projectsReducer = (state: ProjectState = [], action: ProjectAction) => {
return actions.match(action, {
CREATE_PROJECT: project => [...state, project],
REMOVE_PROJECT: () => state,
- PROJECTS_REQUEST: () => state,
+ PROJECTS_REQUEST: itemId => {
+ const tree = _.cloneDeep(state);
+ const item = findTreeItem(tree, itemId);
+ if (item) {
+ item.status = TreeItemStatus.Pending;
+ }
+ return tree;
+ },
PROJECTS_SUCCESS: ({ projects, parentItemId }) => {
return updateProjectTree(state, projects, parentItemId);
},
@@ -75,7 +76,7 @@ const projectsReducer = (state: ProjectState = [], action: ProjectAction) => {
if (item) {
item.open = !item.open;
item.active = true;
- item.isLoaded = false;
+ item.toggled = true;
}
return tree;
},
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 3767e7b..59495e9 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -25,7 +25,7 @@ import { RootState } from "../../store/store";
import projectActions from "../../store/project/project-action"
import ProjectTree from '../../components/project-tree/project-tree';
-import { TreeItem } from "../../components/tree/tree";
+import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
import { Project } from "../../models/project";
import { projectService } from '../../services/services';
@@ -104,54 +104,56 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
});
};
- toggleProjectTreeItem = (itemId: string) => {
- this.props.projects ?
- this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId)) : (
- this.props.dispatch<any>(projectService.getProjectList(itemId)).then(() => {
- this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId));
- }))
+ toggleProjectTreeItem = (itemId: string, status: TreeItemStatus) => {
+ if (status === TreeItemStatus.Loaded) {
+ this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId))
+ } else {
+ this.props.dispatch<any>(projectService.getProjectList(itemId)).then(() => {
+ this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId));
+ })
+ }
};
render() {
- const {classes, user} = this.props;
+ const { classes, user } = this.props;
return (
<div className={classes.root}>
<AppBar position="absolute" className={classes.appBar}>
<Toolbar>
- <Typography variant="title" color="inherit" noWrap style={{flexGrow: 1}}>
- <span>Arvados</span><br/><span style={{fontSize: 12}}>Workbench 2</span>
+ <Typography variant="title" color="inherit" noWrap style={{ flexGrow: 1 }}>
+ <span>Arvados</span><br /><span style={{ fontSize: 12 }}>Workbench 2</span>
</Typography>
{user ?
- <Grid container style={{width: 'auto'}}>
- <Grid container style={{width: 'auto'}} alignItems='center'>
+ <Grid container style={{ width: 'auto' }}>
+ <Grid container style={{ width: 'auto' }} alignItems='center'>
<Typography variant="title" color="inherit" noWrap>
{user.firstName} {user.lastName}
</Typography>
</Grid>
<Grid item>
<IconButton
- aria-owns={this.state.anchorEl ? 'menu-appbar' : undefined}
- aria-haspopup="true"
- onClick={this.handleOpenMenu}
- color="inherit">
- <AccountCircle/>
+ aria-owns={this.state.anchorEl ? 'menu-appbar' : undefined}
+ aria-haspopup="true"
+ onClick={this.handleOpenMenu}
+ color="inherit">
+ <AccountCircle />
</IconButton>
</Grid>
<Menu
- id="menu-appbar"
- anchorEl={this.state.anchorEl}
- anchorOrigin={{
- vertical: 'top',
- horizontal: 'right',
- }}
- transformOrigin={{
- vertical: 'top',
- horizontal: 'right',
- }}
- open={!!this.state.anchorEl}
- onClose={this.handleClose}>
- <MenuItem onClick={this.logout}>Logout</MenuItem>
- <MenuItem onClick={this.handleClose}>My account</MenuItem>
+ id="menu-appbar"
+ anchorEl={this.state.anchorEl}
+ anchorOrigin={{
+ vertical: 'top',
+ horizontal: 'right',
+ }}
+ transformOrigin={{
+ vertical: 'top',
+ horizontal: 'right',
+ }}
+ open={!!this.state.anchorEl}
+ onClose={this.handleClose}>
+ <MenuItem onClick={this.logout}>Logout</MenuItem>
+ <MenuItem onClick={this.handleClose}>My account</MenuItem>
</Menu>
</Grid>
:
@@ -160,20 +162,20 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
</Toolbar>
</AppBar>
{user &&
- <Drawer
- variant="permanent"
- classes={{
- paper: classes.drawerPaper,
- }}>
- <div className={classes.toolbar}/>
- <ProjectTree
- projects={this.props.projects}
- toggleProjectTreeItem={this.toggleProjectTreeItem}/>
- </Drawer>}
+ <Drawer
+ variant="permanent"
+ classes={{
+ paper: classes.drawerPaper,
+ }}>
+ <div className={classes.toolbar} />
+ <ProjectTree
+ projects={this.props.projects}
+ toggleProjectTreeItem={this.toggleProjectTreeItem} />
+ </Drawer>}
<main className={classes.content}>
- <div className={classes.toolbar}/>
+ <div className={classes.toolbar} />
<Switch>
- <Route path="/project/:name" component={ProjectList}/>
+ <Route path="/project/:name" component={ProjectList} />
</Switch>
</main>
</div>
commit 7ba0401cff3906814ba976ea36adab9b3d5c3922
Merge: 7f95b6e d5fb635
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date: Thu Jun 14 09:40:20 2018 +0200
Merge branch 'master' of git.curoverse.com:arvados-workbench2 into 13618-Tree-component-adjustments-for-dynamic-contents
Feature #13618
Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
commit 7f95b6ead934a15e849c7650a08475633a8bfb09
Author: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
Date: Thu Jun 14 09:39:55 2018 +0200
Tree component adjsustments for dynamic contents
Feature #13618
Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk at contractors.roche.com>
diff --git a/src/components/tree/tree.tsx b/src/components/tree/tree.tsx
index d8397d6..fd6299f 100644
--- a/src/components/tree/tree.tsx
+++ b/src/components/tree/tree.tsx
@@ -8,8 +8,10 @@ import ListItem from "@material-ui/core/ListItem/ListItem";
import { StyleRulesCallback, Theme, withStyles, WithStyles } from '@material-ui/core/styles';
import { ReactElement } from "react";
import Collapse from "@material-ui/core/Collapse/Collapse";
+import CircularProgress from '@material-ui/core/CircularProgress';
+import { inherits } from 'util';
-type CssRules = 'list' | 'activeArrow' | 'arrow' | 'arrowRotate';
+type CssRules = 'list' | 'activeArrow' | 'arrow' | 'arrowRotate' | 'arrowTransition' | 'loader';
const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
list: {
@@ -23,8 +25,17 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
arrow: {
position: 'absolute',
},
+ arrowTransition: {
+ transition: 'all 0.3s ease',
+ },
arrowRotate: {
+ transition: 'all 0.3s ease',
transform: 'rotate(-90deg)',
+ },
+ loader: {
+ position: 'absolute',
+ transform: 'translate(0px)',
+ top: '3px'
}
});
@@ -33,6 +44,7 @@ export interface TreeItem<T> {
id: string;
open: boolean;
active: boolean;
+ isLoaded: boolean;
items?: Array<TreeItem<T>>;
}
@@ -44,18 +56,18 @@ interface TreeProps<T> {
}
class Tree<T> extends React.Component<TreeProps<T> & WithStyles<CssRules>, {}> {
- renderArrow (items: boolean, arrowClass: string, open: boolean){
- return <i className={`${arrowClass} ${open ? "fas fa-caret-down" : `fas fa-caret-down ${this.props.classes.arrowRotate}`}`} />
+ renderArrow (arrowClass: string, open: boolean){
+ return <i className={`${arrowClass} ${open ? `fas fa-caret-down ${this.props.classes.arrowTransition}` : `fas fa-caret-down ${this.props.classes.arrowRotate}`}`} />
}
render(): ReactElement<any> {
const level = this.props.level ? this.props.level : 0;
const {classes, render, toggleItem, items} = this.props;
- const {list, arrow, activeArrow} = classes;
+ const {list, arrow, activeArrow, loader} = classes;
return <List component="div" className={list}>
{items && items.map((it: TreeItem<T>, idx: number) =>
<div key={`item/${level}/${idx}`}>
<ListItem button onClick={() => toggleItem(it.id)} className={list} style={{paddingLeft: (level + 1) * 20}}>
- {this.renderArrow(true, it.active ? activeArrow : arrow, it.open)}
+ {it.isLoaded ? this.renderArrow(it.active ? activeArrow : arrow, it.open) : <CircularProgress size={10} className={loader}/> }
{render(it, level)}
</ListItem>
{it.items && it.items.length > 0 &&
diff --git a/src/store/project/project-reducer.ts b/src/store/project/project-reducer.ts
index 887cf89..694235a 100644
--- a/src/store/project/project-reducer.ts
+++ b/src/store/project/project-reducer.ts
@@ -29,7 +29,15 @@ function resetTreeActivity<T>(tree: Array<TreeItem<T>>) {
}
}
+function resetTreeLoader<T>(tree: Array<TreeItem<T>>) {
+ for (const t of tree) {
+ t.isLoaded = true;
+ resetTreeLoader(t.items ? t.items : []);
+ }
+}
+
function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[], parentItemId?: string): Array<TreeItem<Project>> {
+ resetTreeLoader(tree)
let treeItem;
if (parentItemId) {
treeItem = findTreeItem(tree, parentItemId);
@@ -38,6 +46,7 @@ function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[],
id: p.uuid,
open: false,
active: false,
+ isLoaded: true,
data: p,
items: []
} as TreeItem<Project>));
@@ -66,6 +75,7 @@ const projectsReducer = (state: ProjectState = [], action: ProjectAction) => {
if (item) {
item.open = !item.open;
item.active = true;
+ item.isLoaded = false;
}
return tree;
},
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index d18d113..3767e7b 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -105,9 +105,11 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
};
toggleProjectTreeItem = (itemId: string) => {
- this.props.dispatch<any>(projectService.getProjectList(itemId)).then(() => {
- this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId));
- });
+ this.props.projects ?
+ this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId)) : (
+ this.props.dispatch<any>(projectService.getProjectList(itemId)).then(() => {
+ this.props.dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM(itemId));
+ }))
};
render() {
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list