Merge branch 'master'
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 20 Aug 2018 09:23:39 +0000 (11:23 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Mon, 20 Aug 2018 09:23:39 +0000 (11:23 +0200)
Feature #14014

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

29 files changed:
package.json
src/common/file.ts
src/common/webdav.test.ts
src/components/data-explorer/data-explorer.tsx
src/components/file-upload-dialog/file-upload-dialog.tsx
src/components/side-panel/side-panel.tsx
src/index.tsx
src/models/tree.test.ts
src/models/tree.ts
src/services/collection-files-service/collection-manifest-mapper.ts
src/services/collection-service/collection-service-files-response.ts
src/services/collection-service/collection-service.ts
src/store/auth/auth-action.ts
src/store/collection-panel/collection-panel-files/collection-panel-files-reducer.ts
src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts
src/store/collections/uploader/collection-uploader-actions.ts
src/store/data-explorer/data-explorer-reducer.test.tsx
src/store/data-explorer/data-explorer-reducer.ts
src/store/navigation/navigation-action.ts
src/store/side-panel/side-panel-action.ts
src/store/side-panel/side-panel-reducer.test.ts
src/store/side-panel/side-panel-reducer.ts
src/store/tree-picker/tree-picker-reducer.test.ts
src/views-components/collection-panel-files/collection-panel-files.ts
src/views-components/data-explorer/data-explorer.tsx
src/views-components/main-app-bar/main-app-bar.tsx
src/views-components/tree-picker/tree-picker.ts
src/views/workbench/workbench.tsx
yarn.lock

index e2b6c4e1ec21f4017c0096a75bba61191a923dbd..b9484c5c0f644f0881527f60c885f2ea947e3f14 100644 (file)
@@ -3,19 +3,19 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
-    "@material-ui/core": "1.4.2",
-    "@material-ui/icons": "2.0.0",
+    "@material-ui/core": "1.5.0",
+    "@material-ui/icons": "2.0.2",
     "@types/lodash": "4.14.116",
-    "@types/react-copy-to-clipboard": "4.2.5",
-    "@types/react-dropzone": "4.2.1",
-    "@types/redux-form": "7.4.4",
+    "@types/react-copy-to-clipboard": "4.2.6",
+    "@types/react-dropzone": "4.2.2",
+    "@types/redux-form": "7.4.5",
     "axios": "0.18.0",
     "classnames": "2.2.6",
     "lodash": "4.17.10",
     "react": "16.4.2",
     "react-copy-to-clipboard": "5.0.1",
     "react-dom": "16.4.2",
-    "react-dropzone": "4.2.13",
+    "react-dropzone": "5.0.1",
     "react-redux": "5.0.7",
     "react-router": "4.3.1",
     "react-router-dom": "4.3.1",
   },
   "devDependencies": {
     "@types/classnames": "^2.2.4",
-    "@types/enzyme": "3.1.12",
-    "@types/enzyme-adapter-react-16": "1.0.2",
+    "@types/enzyme": "3.1.13",
+    "@types/enzyme-adapter-react-16": "1.0.3",
     "@types/jest": "23.3.1",
-    "@types/node": "10.5.5",
+    "@types/node": "10.7.1",
     "@types/react": "16.4",
-    "@types/react-dom": "16.0.6",
+    "@types/react-dom": "16.0.7",
     "@types/react-redux": "6.0.6",
     "@types/react-router": "4.0.29",
     "@types/react-router-dom": "4.3.0",
     "@types/react-router-redux": "5.0.15",
     "@types/redux-devtools": "3.0.44",
-    "@types/redux-form": "7.4.4",
+    "@types/redux-form": "7.4.5",
     "axios-mock-adapter": "1.15.0",
-    "enzyme": "3.3.0",
-    "enzyme-adapter-react-16": "1.1.1",
+    "enzyme": "3.4.4",
+    "enzyme-adapter-react-16": "1.2.0",
     "jest-localstorage-mock": "2.2.0",
     "redux-devtools": "3.4.1",
     "redux-form": "7.4.2",
index 3f17a38ffe2e7999e2f2e926e2db8520fe0c97e8..23113992247ef1d1a018b06973d39576dc2109df 100644 (file)
@@ -3,10 +3,13 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 export const fileToArrayBuffer = (file: File) =>
-    new Promise<ArrayBuffer>(resolve => {
+    new Promise<ArrayBuffer>((resolve, reject) => {
         const reader = new FileReader();
         reader.onload = () => {
             resolve(reader.result as ArrayBuffer);
         };
+        reader.onerror = () => {
+            reject();
+        };
         reader.readAsArrayBuffer(file);
     });
index e1eefad373a31df64b991da92022b9f84260fa6f..c85f30e793864ecc3cb5798c44a85de75afa6d55 100644 (file)
@@ -41,15 +41,13 @@ describe('WebDAV', () => {
 
     it('PUT', async () => {
         const { open, send, load, progress, createRequest } = mockCreateRequest();
-        const onUploadProgress = jest.fn();
         const webdav = new WebDAV(undefined, createRequest);
-        const promise = webdav.put('foo', 'Test data', { onUploadProgress });
+        const promise = webdav.put('foo', 'Test data');
         progress();
         load();
         const request = await promise;
         expect(open).toHaveBeenCalledWith('PUT', 'foo');
         expect(send).toHaveBeenCalledWith('Test data');
-        expect(onUploadProgress).toHaveBeenCalled();
         expect(request).toBeInstanceOf(XMLHttpRequest);
     });
 
index 681caa9478c15d194c0ab1f904a59369b015089a..af14db9c9e08818f3a9dc945718d1296f707d5e8 100644 (file)
@@ -51,6 +51,7 @@ interface DataExplorerDataProps<T> {
 }
 
 interface DataExplorerActionProps<T> {
+    onSetColumns: (columns: DataColumns<T>) => void;
     onSearch: (value: string) => void;
     onRowClick: (item: T) => void;
     onRowDoubleClick: (item: T) => void;
@@ -67,6 +68,11 @@ type DataExplorerProps<T> = DataExplorerDataProps<T> & DataExplorerActionProps<T
 
 export const DataExplorer = withStyles(styles)(
     class DataExplorerGeneric<T> extends React.Component<DataExplorerProps<T>> {
+        componentDidMount() {
+            if (this.props.onSetColumns) {
+                this.props.onSetColumns(this.props.columns);
+            }
+        }
         render() {
             const {
                 columns, onContextMenu, onFiltersChange, onSortToggle, extractKey,
index a7644112e3f468430d3b94198e88486065b80abd..7810c4915c180dc34d638b93e3c0321167778d62 100644 (file)
@@ -49,4 +49,4 @@ export const FilesUploadDialog = (props: FilesUploadDialogProps & WithDialogProp
                     : 'Upload data'}
             </Button>
         </DialogActions>
-    </Dialog>;
\ No newline at end of file
+    </Dialog>;
index 206cb6322b84a1d181d451f76d8886328837a969..84e5c5476f5f8266c3464f82c880b9f052a5af42 100644 (file)
@@ -3,7 +3,6 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
-import { ReactElement } from 'react';
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { List, ListItem, ListItemIcon, Collapse } from "@material-ui/core";
@@ -11,6 +10,7 @@ import { SidePanelRightArrowIcon, IconType } from '../icon/icon';
 import * as classnames from "classnames";
 import { ListItemTextIcon } from '../list-item-text-icon/list-item-text-icon';
 import { Dispatch } from "redux";
+import { RouteComponentProps, withRouter } from "react-router";
 
 type CssRules = 'active' | 'row' | 'root' | 'list' | 'iconClose' | 'iconOpen' | 'toggableIconContainer' | 'toggableIcon';
 
@@ -54,8 +54,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 export interface SidePanelItem {
     id: string;
     name: string;
+    url: string;
     icon: IconType;
-    active?: boolean;
     open?: boolean;
     margin?: boolean;
     openAble?: boolean;
@@ -69,30 +69,34 @@ interface SidePanelDataProps {
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: SidePanelItem) => void;
 }
 
-type SidePanelProps = SidePanelDataProps & WithStyles<CssRules>;
+type SidePanelProps = RouteComponentProps<{}> & SidePanelDataProps & WithStyles<CssRules>;
 
-export const SidePanel = withStyles(styles)(
+export const SidePanel = withStyles(styles)(withRouter(
     class extends React.Component<SidePanelProps> {
-        render(): ReactElement<any> {
+        render() {
             const { classes, toggleOpen, toggleActive, sidePanelItems, children } = this.props;
             const { root, row, list, toggableIconContainer } = classes;
+
+            const path = this.props.location.pathname.split('/');
+            const activeUrl = path.length > 1 ? "/" + path[1] : "/";
             return (
                 <div className={root}>
                     <List>
-                        {sidePanelItems.map(it => (
-                            <span key={it.name}>
+                        {sidePanelItems.map(it => {
+                            const active = it.url === activeUrl;
+                            return <span key={it.name}>
                                 <ListItem button className={list} onClick={() => toggleActive(it.id)}
                                           onContextMenu={this.handleRowContextMenu(it)}>
                                     <span className={row}>
                                         {it.openAble ? (
                                             <i onClick={() => toggleOpen(it.id)} className={toggableIconContainer}>
                                                 <ListItemIcon
-                                                    className={this.getToggableIconClassNames(it.open, it.active)}>
+                                                    className={this.getToggableIconClassNames(it.open, active)}>
                                                     < SidePanelRightArrowIcon/>
                                                 </ListItemIcon>
                                             </i>
                                         ) : null}
-                                        <ListItemTextIcon icon={it.icon} name={it.name} isActive={it.active}
+                                        <ListItemTextIcon icon={it.icon} name={it.name} isActive={active}
                                                           hasMargin={it.margin}/>
                                     </span>
                                 </ListItem>
@@ -101,8 +105,8 @@ export const SidePanel = withStyles(styles)(
                                         {children}
                                     </Collapse>
                                 ) : null}
-                            </span>
-                        ))}
+                            </span>;
+                        })}
                     </List>
                 </div>
             );
@@ -121,4 +125,4 @@ export const SidePanel = withStyles(styles)(
             (event: React.MouseEvent<HTMLElement>) =>
                 item.openAble ? this.props.onContextMenu(event, item) : null
     }
-);
+));
index cfdbb46cf55882743fd714e7520a9c64884a8d22..443e76f3e62a42597d804aefd779b32b3f65ffa9 100644 (file)
@@ -28,6 +28,14 @@ import { collectionFilesItemActionSet } from './views-components/context-menu/ac
 import { collectionActionSet } from './views-components/context-menu/action-sets/collection-action-set';
 import { collectionResourceActionSet } from './views-components/context-menu/action-sets/collection-resource-action-set';
 
+const getBuildNumber = () => "BN-" + (process.env.BUILD_NUMBER || "dev");
+const getGitCommit = () => "GIT-" + (process.env.GIT_COMMIT || "latest").substr(0, 7);
+const getBuildInfo = () => getBuildNumber() + " / " + getGitCommit();
+
+const buildInfo = getBuildInfo();
+
+console.log(`Starting arvados [${buildInfo}]`);
+
 addMenuActionSet(ContextMenuKind.ROOT_PROJECT, rootProjectActionSet);
 addMenuActionSet(ContextMenuKind.PROJECT, projectActionSet);
 addMenuActionSet(ContextMenuKind.RESOURCE, resourceActionSet);
@@ -45,9 +53,9 @@ fetchConfig()
 
         store.dispatch(initAuth());
         store.dispatch(getProjectList(services.authService.getUuid()));
-        
+
         const TokenComponent = (props: any) => <ApiToken authService={services.authService} {...props}/>;
-        const WorkbenchComponent = (props: any) => <Workbench authService={services.authService} {...props}/>;
+        const WorkbenchComponent = (props: any) => <Workbench authService={services.authService} buildInfo={buildInfo} {...props}/>;
 
         const App = () =>
             <MuiThemeProvider theme={CustomTheme}>
index 708cf4045c75b73fae7650b8a3bba789a46362bc..375a012054f9bea3a26a7bd8033642b44697ea24 100644 (file)
@@ -30,7 +30,7 @@ describe('Tree', () => {
             { children: [], id: 'Node 2', parent: 'Node 1', value: 'Value 1' },
             { children: [], id: 'Node 3', parent: 'Node 2', value: 'Value 1' }
         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
-        expect(Tree.getNodeAncestors('Node 3')(newTree)).toEqual(['Node 1', 'Node 2']);
+        expect(Tree.getNodeAncestorsIds('Node 3')(newTree)).toEqual(['Node 1', 'Node 2']);
     });
 
     it('gets node descendants', () => {
@@ -41,7 +41,7 @@ describe('Tree', () => {
             { children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' },
             { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
-        expect(Tree.getNodeDescendants('Node 1')(newTree)).toEqual(['Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
+        expect(Tree.getNodeDescendantsIds('Node 1')(newTree)).toEqual(['Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
     });
 
     it('gets root descendants', () => {
@@ -52,7 +52,7 @@ describe('Tree', () => {
             { children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' },
             { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
-        expect(Tree.getNodeDescendants('')(newTree)).toEqual(['Node 1', 'Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
+        expect(Tree.getNodeDescendantsIds('')(newTree)).toEqual(['Node 1', 'Node 2', 'Node 3', 'Node 2.1', 'Node 3.1']);
     });
 
     it('gets node children', () => {
@@ -63,7 +63,7 @@ describe('Tree', () => {
             { children: [], id: 'Node 3', parent: 'Node 1', value: 'Value 1' },
             { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
-        expect(Tree.getNodeChildren('Node 1')(newTree)).toEqual(['Node 2', 'Node 3']);
+        expect(Tree.getNodeChildrenIds('Node 1')(newTree)).toEqual(['Node 2', 'Node 3']);
     });
 
     it('gets root children', () => {
@@ -74,7 +74,7 @@ describe('Tree', () => {
             { children: [], id: 'Node 3', parent: '', value: 'Value 1' },
             { children: [], id: 'Node 3.1', parent: 'Node 3', value: 'Value 1' }
         ].reduce((tree, node) => Tree.setNode(node)(tree), tree);
-        expect(Tree.getNodeChildren('')(newTree)).toEqual(['Node 1', 'Node 3']);
+        expect(Tree.getNodeChildrenIds('')(newTree)).toEqual(['Node 1', 'Node 3']);
     });
 
     it('maps tree', () => {
index 8b66e50da7961df58c5c60f0b8fd23556f749467..a5fb49cff4a3adb3945cdcfbcb1a1ec6b4ac5080 100644 (file)
@@ -6,7 +6,7 @@ export type Tree<T> = Record<string, TreeNode<T>>;
 
 export const TREE_ROOT_ID = '';
 
-export interface TreeNode<T> {
+export interface TreeNode<T = any> {
     children: string[];
     value: T;
     id: string;
@@ -21,7 +21,7 @@ export const setNode = <T>(node: TreeNode<T>) => (tree: Tree<T>): Tree<T> => {
     const [newTree] = [tree]
         .map(tree => getNode(node.id)(tree) === node
             ? tree
-            : {...tree, [node.id]: node})
+            : { ...tree, [node.id]: node })
         .map(addChild(node.parent, node.id));
     return newTree;
 };
@@ -46,25 +46,32 @@ export const setNodeValueWith = <T>(mapFn: (value: T) => T) => (id: string) => (
 };
 
 export const mapTreeValues = <T, R>(mapFn: (value: T) => R) => (tree: Tree<T>): Tree<R> =>
-    getNodeDescendants('')(tree)
+    getNodeDescendantsIds('')(tree)
         .map(id => getNode(id)(tree))
         .map(mapNodeValue(mapFn))
         .reduce((newTree, node) => setNode(node)(newTree), createTree<R>());
 
-export const mapTree = <T, R>(mapFn: (node: TreeNode<T>) => TreeNode<R>) => (tree: Tree<T>): Tree<R> =>
-    getNodeDescendants('')(tree)
+export const mapTree = <T, R = T>(mapFn: (node: TreeNode<T>) => TreeNode<R>) => (tree: Tree<T>): Tree<R> =>
+    getNodeDescendantsIds('')(tree)
         .map(id => getNode(id)(tree))
         .map(mapFn)
         .reduce((newTree, node) => setNode(node)(newTree), createTree<R>());
 
-export const getNodeAncestors = (id: string) => <T>(tree: Tree<T>): string[] => {
+export const getNodeAncestors = (id: string) => <T>(tree: Tree<T>) =>
+    mapIdsToNodes(getNodeAncestorsIds(id)(tree))(tree);
+
+
+export const getNodeAncestorsIds = (id: string) => <T>(tree: Tree<T>): string[] => {
     const node = getNode(id)(tree);
     return node && node.parent
-        ? [...getNodeAncestors(node.parent)(tree), node.parent]
+        ? [...getNodeAncestorsIds(node.parent)(tree), node.parent]
         : [];
 };
 
-export const getNodeDescendants = (id: string, limit = Infinity) => <T>(tree: Tree<T>): string[] => {
+export const getNodeDescendants = (id: string, limit = Infinity) => <T>(tree: Tree<T>) =>
+    mapIdsToNodes(getNodeDescendantsIds(id, limit)(tree))(tree);
+
+export const getNodeDescendantsIds = (id: string, limit = Infinity) => <T>(tree: Tree<T>): string[] => {
     const node = getNode(id)(tree);
     const children = node ? node.children :
         id === TREE_ROOT_ID
@@ -75,12 +82,18 @@ export const getNodeDescendants = (id: string, limit = Infinity) => <T>(tree: Tr
         .concat(limit < 1
             ? []
             : children
-                .map(id => getNodeDescendants(id, limit - 1)(tree))
+                .map(id => getNodeDescendantsIds(id, limit - 1)(tree))
                 .reduce((nodes, nodeChildren) => [...nodes, ...nodeChildren], []));
 };
 
-export const getNodeChildren = (id: string) => <T>(tree: Tree<T>): string[] =>
-    getNodeDescendants(id, 0)(tree);
+export const getNodeChildren = (id: string) => <T>(tree: Tree<T>) =>
+    mapIdsToNodes(getNodeChildrenIds(id)(tree))(tree);
+
+export const getNodeChildrenIds = (id: string) => <T>(tree: Tree<T>): string[] =>
+    getNodeDescendantsIds(id, 0)(tree);
+
+export const mapIdsToNodes = (ids: string[]) => <T>(tree: Tree<T>) =>
+    ids.map(id => getNode(id)(tree)).filter((node): node is TreeNode<T> => node !== undefined);
 
 const mapNodeValue = <T, R>(mapFn: (value: T) => R) => (node: TreeNode<T>): TreeNode<R> =>
     ({ ...node, value: mapFn(node.value) });
index c3fd43ead1d0d4c013e066f0331712710af9f38f..0c7e91deecf4aba5bc9465569c40118f9401d536 100644 (file)
@@ -4,11 +4,11 @@
 
 import { uniqBy, groupBy } from 'lodash';
 import { KeepManifestStream, KeepManifestStreamFile, KeepManifest } from "~/models/keep-manifest";
-import { TreeNode, setNode, createTree, getNodeDescendants, getNodeValue } from '~/models/tree';
+import { TreeNode, setNode, createTree, getNodeDescendantsIds, getNodeValue } from '~/models/tree';
 import { CollectionFilesTree, CollectionFile, CollectionDirectory, createCollectionDirectory, createCollectionFile, CollectionFileType } from '../../models/collection-file';
 
 export const mapCollectionFilesTreeToManifest = (tree: CollectionFilesTree): KeepManifest => {
-    const values = getNodeDescendants('')(tree).map(id => getNodeValue(id)(tree));
+    const values = getNodeDescendantsIds('')(tree).map(id => getNodeValue(id)(tree));
     const files = values.filter(value => value && value.type === CollectionFileType.FILE) as CollectionFile[];
     const fileGroups = groupBy(files, file => file.path);
     return Object
index 581a6fa6613e34e002fd847ddbe9c9f10c71116f..b8a7970d58d4310d8228b8d0c80fb04188614810 100644 (file)
@@ -3,8 +3,8 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { createCollectionFilesTree, CollectionDirectory, CollectionFile, CollectionFileType, createCollectionDirectory, createCollectionFile } from "../../models/collection-file";
-import { Tree, mapTree, getNodeChildren, getNode, TreeNode } from "../../models/tree";
-import { getTagValue } from "../../common/xml";
+import { getTagValue } from "~/common/xml";
+import { getNodeChildren, Tree, mapTree } from '~/models/tree';
 
 export const parseFilesResponse = (document: Document) => {
     const files = extractFilesData(document);
@@ -13,10 +13,8 @@ export const parseFilesResponse = (document: Document) => {
 };
 
 export const sortFilesTree = (tree: Tree<CollectionDirectory | CollectionFile>) => {
-    return mapTree(node => {
-        const children = getNodeChildren(node.id)(tree)
-            .map(id => getNode(id)(tree))
-            .filter(node => node !== undefined) as TreeNode<CollectionDirectory | CollectionFile>[];
+    return mapTree<CollectionDirectory | CollectionFile>(node => {
+        const children = getNodeChildren(node.id)(tree);
 
         children.sort((a, b) =>
             a.value.type !== b.value.type
@@ -24,7 +22,7 @@ export const sortFilesTree = (tree: Tree<CollectionDirectory | CollectionFile>)
                 : a.value.name.localeCompare(b.value.name)
         );
         return { ...node, children: children.map(child => child.id) };
-    })(tree) as Tree<CollectionDirectory | CollectionFile>;
+    })(tree);
 };
 
 export const extractFilesData = (document: Document) => {
index 671a1e1556155ba39529920e2be6e2f4b1d49bd6..6aa2123d0c6ac228d7e2517116807ad247791a1e 100644 (file)
@@ -35,6 +35,7 @@ export class CollectionService extends CommonResourceService<CollectionResource>
     }
 
     async uploadFiles(collectionUuid: string, files: File[], onProgress?: UploadProgress) {
+        // files have to be uploaded sequentially
         for (let idx = 0; idx < files.length; idx++) {
             await this.uploadFile(collectionUuid, files[idx], idx, onProgress);
         }
index 33a5df90450e2cde772c73c8d80c7b06854c4f12..00af5ce5b0bb7614f4fbc97316a61dd712759ba3 100644 (file)
@@ -17,9 +17,9 @@ export const authActions = unionize({
     USER_DETAILS_REQUEST: {},
     USER_DETAILS_SUCCESS: ofType<User>()
 }, {
-        tag: 'type',
-        value: 'payload'
-    });
+    tag: 'type',
+    value: 'payload'
+});
 
 function setAuthorizationHeader(services: ServiceRepository, token: string) {
     services.apiClient.defaults.headers.common = {
index a34fc21e9af2281c82e59c7af72767b71964e683..57961538708c900b56631e47e33639a90d66a559 100644 (file)
@@ -4,7 +4,7 @@
 
 import { CollectionPanelFilesState, CollectionPanelFile, CollectionPanelDirectory, mapCollectionFileToCollectionPanelFile, mergeCollectionPanelFilesStates } from './collection-panel-files-state';
 import { CollectionPanelFilesAction, collectionPanelFilesAction } from "./collection-panel-files-actions";
-import { createTree, mapTreeValues, getNode, setNode, getNodeAncestors, getNodeDescendants, setNodeValueWith, mapTree } from "~/models/tree";
+import { createTree, mapTreeValues, getNode, setNode, getNodeAncestorsIds, getNodeDescendantsIds, setNodeValueWith, mapTree } from "~/models/tree";
 import { CollectionFileType } from "~/models/collection-file";
 
 export const collectionPanelFilesReducer = (state: CollectionPanelFilesState = createTree(), action: CollectionPanelFilesAction) => {
@@ -44,7 +44,7 @@ const toggleSelected = (id: string) => (tree: CollectionPanelFilesState) =>
 const toggleDescendants = (id: string) => (tree: CollectionPanelFilesState) => {
     const node = getNode(id)(tree);
     if (node && node.value.type === CollectionFileType.DIRECTORY) {
-        return getNodeDescendants(id)(tree)
+        return getNodeDescendantsIds(id)(tree)
             .reduce((newTree, id) =>
                 setNodeValueWith(v => ({ ...v, selected: node.value.selected }))(id)(newTree), tree);
     }
@@ -52,7 +52,7 @@ const toggleDescendants = (id: string) => (tree: CollectionPanelFilesState) => {
 };
 
 const toggleAncestors = (id: string) => (tree: CollectionPanelFilesState) => {
-    const ancestors = getNodeAncestors(id)(tree).reverse();
+    const ancestors = getNodeAncestorsIds(id)(tree).reverse();
     return ancestors.reduce((newTree, parent) => parent ? toggleParentNode(parent)(newTree) : newTree, tree);
 };
 
index 8c5ebd72936a5d60e0714589e33b6635187b47bd..9d5b06cea6b9c94f74e5fadbebd022b5b6366178 100644 (file)
@@ -37,9 +37,7 @@ export const mergeCollectionPanelFilesStates = (oldState: CollectionPanelFilesSt
 };
 
 export const filterCollectionFilesBySelection = (tree: CollectionPanelFilesState, selected: boolean) => {
-    const allFiles = getNodeDescendants('')(tree)
-        .map(id => getNodeValue(id)(tree))
-        .filter(file => file !== undefined) as Array<CollectionPanelDirectory | CollectionPanelFile>;
+    const allFiles = getNodeDescendants('')(tree).map(node => node.value);
 
     const selectedDirectories = allFiles.filter(file => file.selected === selected && file.type === CollectionFileType.DIRECTORY);
     const selectedFiles = allFiles.filter(file => file.selected === selected && !selectedDirectories.some(dir => dir.id === file.path));
index 351f3b5c953bbeb67629a72da3297e5b5b11d807..0fa55d836cd9510d1840ffb450b9e57801a4a34b 100644 (file)
@@ -27,9 +27,9 @@ export const collectionUploaderActions = unionize({
     SET_UPLOAD_PROGRESS: ofType<{ fileId: number, loaded: number, total: number, currentTime: number }>(),\r
     CLEAR_UPLOAD: ofType()\r
 }, {\r
-        tag: 'type',\r
-        value: 'payload'\r
-    });\r
+    tag: 'type',\r
+    value: 'payload'\r
+});\r
 \r
 export type CollectionUploaderAction = UnionOf<typeof collectionUploaderActions>;\r
 \r
index 6b1c90798962032ddc273005b1f1b0c7d41de123..0bc44ba85bf06195bb34ef88500ec807c4b99e54 100644 (file)
@@ -12,9 +12,11 @@ describe('data-explorer-reducer', () => {
     it('should set columns', () => {
         const columns: DataColumns<any> = [{
             name: "Column 1",
+            filters: [],
             render: jest.fn(),
             selected: true,
-            configurable: true
+            configurable: true,
+            sortDirection: SortDirection.NONE
         }];
         const state = dataExplorerReducer(undefined,
             dataExplorerActions.SET_COLUMNS({ id: "Data explorer", columns }));
@@ -24,12 +26,14 @@ describe('data-explorer-reducer', () => {
     it('should toggle sorting', () => {
         const columns: DataColumns<any> = [{
             name: "Column 1",
+            filters: [],
             render: jest.fn(),
             selected: true,
             configurable: true,
             sortDirection: SortDirection.ASC
         }, {
             name: "Column 2",
+            filters: [],
             render: jest.fn(),
             selected: true,
             configurable: true,
@@ -44,9 +48,11 @@ describe('data-explorer-reducer', () => {
     it('should set filters', () => {
         const columns: DataColumns<any> = [{
             name: "Column 1",
+            filters: [],
             render: jest.fn(),
             selected: true,
-            configurable: true
+            configurable: true,
+            sortDirection: SortDirection.NONE
         }];
 
         const filters: DataTableFilterItem[] = [{
index 175cd0b2817fd0ea419f8474f1326bb16bd2dc20..cc800244abc1d0d9c1673bd03743d1eff7024f14 100644 (file)
@@ -67,9 +67,23 @@ export const getDataExplorer = (state: DataExplorerState, id: string) =>
 const update = (state: DataExplorerState, id: string, updateFn: (dataExplorer: DataExplorer) => DataExplorer) =>
     ({ ...state, [id]: updateFn(getDataExplorer(state, id)) });
 
+const canUpdateColumns = (prevColumns: DataColumns<any>, nextColumns: DataColumns<any>) => {
+    if (prevColumns.length !== nextColumns.length) {
+        return true;
+    }
+    for (let i = 0; i < nextColumns.length; i++) {
+        const pc = prevColumns[i];
+        const nc = nextColumns[i];
+        if (pc.key !== nc.key || pc.name !== nc.name) {
+            return true;
+        }
+    }
+    return false;
+};
+
 const setColumns = (columns: DataColumns<any>) =>
     (dataExplorer: DataExplorer) =>
-        ({ ...dataExplorer, columns });
+        ({ ...dataExplorer, columns: canUpdateColumns(dataExplorer.columns, columns) ? columns : dataExplorer.columns });
 
 const mapColumns = (mapFn: (column: DataColumn<any>) => DataColumn<any>) =>
     (dataExplorer: DataExplorer) =>
index 79d24471491fcd5e2ced64a8bdcd8edb6b38bd87..50ec93d25e8091e2186e0eccf7a6f403d0ca90ae 100644 (file)
@@ -3,26 +3,27 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { Dispatch } from "redux";
-import { projectActions, getProjectList } from "../project/project-action";
+import { getProjectList, projectActions } from "../project/project-action";
 import { push } from "react-router-redux";
 import { TreeItemStatus } from "~/components/tree/tree";
 import { findTreeItem } from "../project/project-reducer";
 import { RootState } from "../store";
-import { Resource, ResourceKind } from "~/models/resource";
+import { ResourceKind } from "~/models/resource";
 import { projectPanelActions } from "../project-panel/project-panel-action";
 import { getCollectionUrl } from "~/models/collection";
 import { getProjectUrl, ProjectResource } from "~/models/project";
 import { ProjectService } from "~/services/project-service/project-service";
 import { ServiceRepository } from "~/services/services";
 import { sidePanelActions } from "../side-panel/side-panel-action";
-import { SidePanelIdentifiers } from "../side-panel/side-panel-reducer";
+import { SidePanelId } from "../side-panel/side-panel-reducer";
 import { getUuidObjectType, ObjectTypes } from "~/models/object-types";
 
-export const getResourceUrl = <T extends Resource>(resource: T): string => {
-    switch (resource.kind) {
-        case ResourceKind.PROJECT: return getProjectUrl(resource.uuid);
-        case ResourceKind.COLLECTION: return getCollectionUrl(resource.uuid);
-        default: return resource.href;
+export const getResourceUrl = (resourceKind: ResourceKind, resourceUuid: string): string => {
+    switch (resourceKind) {
+        case ResourceKind.PROJECT: return getProjectUrl(resourceUuid);
+        case ResourceKind.COLLECTION: return getCollectionUrl(resourceUuid);
+        default:
+            return '';
     }
 };
 
@@ -38,7 +39,7 @@ export const setProjectItem = (itemId: string, itemMode: ItemMode) =>
         const treeItem = findTreeItem(projects.items, itemId);
 
         if (treeItem) {
-            const resourceUrl = getResourceUrl(treeItem.data);
+            const resourceUrl = getResourceUrl(treeItem.data.kind, treeItem.data.uuid);
 
             if (itemMode === ItemMode.ACTIVE || itemMode === ItemMode.BOTH) {
                 if (router.location && !router.location.pathname.includes(resourceUrl)) {
@@ -74,8 +75,7 @@ export const restoreBranch = (itemId: string) =>
         const ancestors = await loadProjectAncestors(itemId, services.projectService);
         const uuids = ancestors.map(ancestor => ancestor.uuid);
         await loadBranch(uuids, dispatch);
-        dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_OPEN(SidePanelIdentifiers.PROJECTS));
-        dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_ACTIVE(SidePanelIdentifiers.PROJECTS));
+        dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_OPEN(SidePanelId.PROJECTS));
         uuids.forEach(uuid => {
             dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_OPEN(uuid));
         });
index 0dd6aad19e3ca0ee2e2311cb333a72a83a889176..ecea3535e35040fdbb8095830ea60e21bacff95e 100644 (file)
@@ -3,11 +3,10 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { default as unionize, ofType, UnionOf } from "unionize";
+import { SidePanelId } from "~/store/side-panel/side-panel-reducer";
 
 export const sidePanelActions = unionize({
-    TOGGLE_SIDE_PANEL_ITEM_OPEN: ofType<string>(),
-    TOGGLE_SIDE_PANEL_ITEM_ACTIVE: ofType<string>(),
-    RESET_SIDE_PANEL_ACTIVITY: ofType<{}>(),
+    TOGGLE_SIDE_PANEL_ITEM_OPEN: ofType<SidePanelId>()
 }, {
     tag: 'type',
     value: 'payload'
index 4872a72cf750486565eeaee5f8a36b94f4dc9233..a76e33a49b0508e52e2a8e1cc60cadb2f7d2e97d 100644 (file)
@@ -7,39 +7,14 @@ import { sidePanelActions } from "./side-panel-action";
 import { ProjectsIcon } from "~/components/icon/icon";
 
 describe('side-panel-reducer', () => {
-
-    it('should toggle activity on side-panel', () => {
-        const initialState = [
-            {
-                id: "1",
-                name: "Projects",
-                icon: ProjectsIcon,
-                open: false,
-                active: false,
-            }
-        ];
-        const project = [
-            {
-                id: "1",
-                name: "Projects",
-                icon: ProjectsIcon,
-                open: false,
-                active: true,
-            }
-        ];
-
-        const state = sidePanelReducer(initialState, sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_ACTIVE(initialState[0].id));
-        expect(state).toEqual(project);
-    });
-
     it('should open side-panel item', () => {
         const initialState = [
             {
                 id: "1",
                 name: "Projects",
+                url: "/projects",
                 icon: ProjectsIcon,
-                open: false,
-                active: false,
+                open: false
             }
         ];
         const project = [
@@ -48,35 +23,11 @@ describe('side-panel-reducer', () => {
                 name: "Projects",
                 icon: ProjectsIcon,
                 open: true,
-                active: false,
+                url: "/projects"
             }
         ];
 
         const state = sidePanelReducer(initialState, sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_OPEN(initialState[0].id));
         expect(state).toEqual(project);
     });
-
-    it('should remove activity on side-panel item', () => {
-        const initialState = [
-            {
-                id: "1",
-                name: "Projects",
-                icon: ProjectsIcon,
-                open: false,
-                active: true,
-            }
-        ];
-        const project = [
-            {
-                id: "1",
-                name: "Projects",
-                icon: ProjectsIcon,
-                open: false,
-                active: false,
-            }
-        ];
-
-        const state = sidePanelReducer(initialState, sidePanelActions.RESET_SIDE_PANEL_ACTIVITY(initialState[0].id));
-        expect(state).toEqual(project);
-    });
 });
index 8c73a8f596b6c9a96d0359dd804e7838ce307827..db1cbe5de51a7133b0a26f00a4f427223872e5d9 100644 (file)
@@ -2,7 +2,6 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as _ from "lodash";
 import { sidePanelActions, SidePanelAction } from './side-panel-action';
 import { SidePanelItem } from '~/components/side-panel/side-panel';
 import { ProjectsIcon, ShareMeIcon, WorkflowIcon, RecentIcon, FavoriteIcon, TrashIcon } from "~/components/icon/icon";
@@ -12,37 +11,20 @@ import { favoritePanelActions } from "../favorite-panel/favorite-panel-action";
 import { projectPanelActions } from "../project-panel/project-panel-action";
 import { projectActions } from "../project/project-action";
 import { getProjectUrl } from "../../models/project";
+import { columns as projectPanelColumns } from "../../views/project-panel/project-panel";
+import { columns as favoritePanelColumns } from "../../views/favorite-panel/favorite-panel";
 
 export type SidePanelState = SidePanelItem[];
 
-export const sidePanelReducer = (state: SidePanelState = sidePanelData, action: SidePanelAction) => {
-    if (state.length === 0) {
-        return sidePanelData;
-    } else {
-        return sidePanelActions.match(action, {
-            TOGGLE_SIDE_PANEL_ITEM_OPEN: itemId =>
-                state.map(it => ({...it, open: itemId === it.id && it.open === false})),
-            TOGGLE_SIDE_PANEL_ITEM_ACTIVE: itemId => {
-                const sidePanel = _.cloneDeep(state);
-                resetSidePanelActivity(sidePanel);
-                sidePanel.forEach(it => {
-                    if (it.id === itemId) {
-                        it.active = true;
-                    }
-                });
-                return sidePanel;
-            },
-            RESET_SIDE_PANEL_ACTIVITY: () => {
-                const sidePanel = _.cloneDeep(state);
-                resetSidePanelActivity(sidePanel);
-                return sidePanel;
-            },
-            default: () => state
-        });
-    }
+export const sidePanelReducer = (state: SidePanelState = sidePanelItems, action: SidePanelAction) => {
+    return sidePanelActions.match(action, {
+        TOGGLE_SIDE_PANEL_ITEM_OPEN: itemId =>
+            state.map(it => ({...it, open: itemId === it.id && it.open === false})),
+        default: () => state
+    });
 };
 
-export enum SidePanelIdentifiers {
+export enum SidePanelId {
     PROJECTS = "Projects",
     SHARED_WITH_ME = "SharedWithMe",
     WORKFLOWS = "Workflows",
@@ -51,61 +33,75 @@ export enum SidePanelIdentifiers {
     TRASH = "Trash"
 }
 
-export const sidePanelData = [
+export const sidePanelItems = [
     {
-        id: SidePanelIdentifiers.PROJECTS,
+        id: SidePanelId.PROJECTS,
         name: "Projects",
+        url: "/projects",
         icon: ProjectsIcon,
         open: false,
         active: false,
         margin: true,
         openAble: true,
         activeAction: (dispatch: Dispatch, uuid: string) => {
-            dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(uuid));
             dispatch(push(getProjectUrl(uuid)));
+            dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(uuid));
+            dispatch(projectPanelActions.SET_COLUMNS({ columns: projectPanelColumns }));
             dispatch(projectPanelActions.RESET_PAGINATION());
-            dispatch(projectPanelActions.REQUEST_ITEMS()); 
+            dispatch(projectPanelActions.REQUEST_ITEMS());
         }
     },
     {
-        id: SidePanelIdentifiers.SHARED_WITH_ME,
+        id: SidePanelId.SHARED_WITH_ME,
         name: "Shared with me",
+        url: "/shared",
         icon: ShareMeIcon,
         active: false,
+        activeAction: (dispatch: Dispatch) => {
+            dispatch(push("/shared"));
+        }
     },
     {
-        id: SidePanelIdentifiers.WORKFLOWS,
+        id: SidePanelId.WORKFLOWS,
         name: "Workflows",
+        url: "/workflows",
         icon: WorkflowIcon,
         active: false,
+        activeAction: (dispatch: Dispatch) => {
+            dispatch(push("/workflows"));
+        }
     },
     {
-        id: SidePanelIdentifiers.RECENT_OPEN,
+        id: SidePanelId.RECENT_OPEN,
         name: "Recent open",
+        url: "/recent",
         icon: RecentIcon,
         active: false,
+        activeAction: (dispatch: Dispatch) => {
+            dispatch(push("/recent"));
+        }
     },
     {
-        id: SidePanelIdentifiers.FAVORITES,
+        id: SidePanelId.FAVORITES,
         name: "Favorites",
+        url: "/favorites",
         icon: FavoriteIcon,
         active: false,
         activeAction: (dispatch: Dispatch) => {
             dispatch(push("/favorites"));
+            dispatch(favoritePanelActions.SET_COLUMNS({ columns: favoritePanelColumns }));
             dispatch(favoritePanelActions.RESET_PAGINATION());
             dispatch(favoritePanelActions.REQUEST_ITEMS());
         }
     },
     {
-        id: SidePanelIdentifiers.TRASH,
+        id: SidePanelId.TRASH,
         name: "Trash",
+        url: "/trash",
         icon: TrashIcon,
         active: false,
+        activeAction: (dispatch: Dispatch) => {
+            dispatch(push("/trash"));
+        }
     }
 ];
-
-function resetSidePanelActivity(sidePanel: SidePanelItem[]) {
-    for (const t of sidePanel) {
-        t.active = false;
-    }
-}
index 3248cb2efba7f06af804c0b4f6a169de02170a67..b092d5ac2a89c80ed29b0d525bacee3790e24821 100644 (file)
@@ -2,7 +2,7 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { createTree, getNodeValue, getNodeChildren } from "~/models/tree";
+import { createTree, getNodeValue, getNodeChildrenIds } from "~/models/tree";
 import { TreePickerNode, createTreePickerNode } from "./tree-picker";
 import { treePickerReducer } from "./tree-picker-reducer";
 import { treePickerActions } from "./tree-picker-actions";
@@ -31,7 +31,7 @@ describe('TreePickerReducer', () => {
         const tree = createTree<TreePickerNode>();
         const subNode = createTreePickerNode({ id: '1.1', value: '1.1' });
         const newTree = treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [subNode] }));
-        expect(getNodeChildren('')(newTree)).toEqual(['1.1']);
+        expect(getNodeChildrenIds('')(newTree)).toEqual(['1.1']);
     });
 
     it('LOAD_TREE_PICKER_NODE_SUCCESS', () => {
@@ -41,7 +41,7 @@ describe('TreePickerReducer', () => {
         const [newTree] = [tree]
             .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '', nodes: [node] })))
             .map(tree => treePickerReducer(tree, treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({ id: '1', nodes: [subNode] })));
-        expect(getNodeChildren('1')(newTree)).toEqual(['1.1']);
+        expect(getNodeChildrenIds('1')(newTree)).toEqual(['1.1']);
         expect(getNodeValue('1')(newTree)).toEqual({
             ...createTreePickerNode({ id: '1', value: '1' }),
             status: TreeItemStatus.LOADED
index 21706e856624c57aacd59c90749c6051fdd636da..3e99e10a709bca515d695c4ed734023bc229748a 100644 (file)
@@ -12,7 +12,7 @@ import { Dispatch } from "redux";
 import { collectionPanelFilesAction } from "~/store/collection-panel/collection-panel-files/collection-panel-files-actions";
 import { contextMenuActions } from "~/store/context-menu/context-menu-actions";
 import { ContextMenuKind } from "../context-menu/context-menu";
-import { Tree, getNodeChildren, getNode } from "~/models/tree";
+import { Tree, getNodeChildrenIds, getNode } from "~/models/tree";
 import { CollectionFileType } from "~/models/collection-file";
 import { openUploadCollectionFilesDialog } from '~/store/collections/uploader/collection-uploader-actions';
 
@@ -23,7 +23,7 @@ const memoizedMapStateToProps = () => {
     return (state: RootState): Pick<CollectionPanelFilesProps, "items"> => {
         if (prevState !== state.collectionPanelFiles) {
             prevState = state.collectionPanelFiles;
-            prevTree = getNodeChildren('')(state.collectionPanelFiles)
+            prevTree = getNodeChildrenIds('')(state.collectionPanelFiles)
                 .map(collectionItemToTreeItem(state.collectionPanelFiles));
         }
         return {
@@ -80,7 +80,7 @@ const collectionItemToTreeItem = (tree: Tree<CollectionPanelDirectory | Collecti
                 type: node.value.type
             },
             id: node.id,
-            items: getNodeChildren(node.id)(tree)
+            items: getNodeChildrenIds(node.id)(tree)
                 .map(collectionItemToTreeItem(tree)),
             open: node.value.type === CollectionFileType.DIRECTORY ? !node.value.collapsed : false,
             selected: node.value.selected,
index 68eeb3c144210dbbb247e94f634014d8d3726a98..d548f607f550637cd5a120fc358c620d913bd29b 100644 (file)
@@ -21,48 +21,50 @@ interface Props {
     extractKey?: (item: any) => React.Key;
 }
 
-const mapStateToProps = (state: RootState, { id }: Props) =>
-    getDataExplorer(state.dataExplorer, id);
+const mapStateToProps = (state: RootState, { id, columns }: Props) => {
+    const s = getDataExplorer(state.dataExplorer, id);
+    if (s.columns.length === 0) {
+        s.columns = columns;
+    }
+    return s;
+};
 
 const mapDispatchToProps = () => {
-    let prevColumns: DataColumns<any>;
-    return (dispatch: Dispatch, { id, columns, onRowClick, onRowDoubleClick, onContextMenu }: Props) => {
-        if (columns !== prevColumns) {
-            prevColumns = columns;
+    return (dispatch: Dispatch, { id, columns, onRowClick, onRowDoubleClick, onContextMenu }: Props) => ({
+        onSetColumns: (columns: DataColumns<any>) => {
             dispatch(dataExplorerActions.SET_COLUMNS({ id, columns }));
-        }
-        return {
-            onSearch: (searchValue: string) => {
-                dispatch(dataExplorerActions.SET_SEARCH_VALUE({ id, searchValue }));
-            },
+        },
+
+        onSearch: (searchValue: string) => {
+            dispatch(dataExplorerActions.SET_SEARCH_VALUE({ id, searchValue }));
+        },
 
-            onColumnToggle: (column: DataColumn<any>) => {
-                dispatch(dataExplorerActions.TOGGLE_COLUMN({ id, columnName: column.name }));
-            },
+        onColumnToggle: (column: DataColumn<any>) => {
+            dispatch(dataExplorerActions.TOGGLE_COLUMN({ id, columnName: column.name }));
+        },
 
-            onSortToggle: (column: DataColumn<any>) => {
-                dispatch(dataExplorerActions.TOGGLE_SORT({ id, columnName: column.name }));
-            },
+        onSortToggle: (column: DataColumn<any>) => {
+            dispatch(dataExplorerActions.TOGGLE_SORT({ id, columnName: column.name }));
+        },
 
-            onFiltersChange: (filters: DataTableFilterItem[], column: DataColumn<any>) => {
-                dispatch(dataExplorerActions.SET_FILTERS({ id, columnName: column.name, filters }));
-            },
+        onFiltersChange: (filters: DataTableFilterItem[], column: DataColumn<any>) => {
+            dispatch(dataExplorerActions.SET_FILTERS({ id, columnName: column.name, filters }));
+        },
 
-            onChangePage: (page: number) => {
-                dispatch(dataExplorerActions.SET_PAGE({ id, page }));
-            },
+        onChangePage: (page: number) => {
+            dispatch(dataExplorerActions.SET_PAGE({ id, page }));
+        },
 
-            onChangeRowsPerPage: (rowsPerPage: number) => {
-                dispatch(dataExplorerActions.SET_ROWS_PER_PAGE({ id, rowsPerPage }));
-            },
+        onChangeRowsPerPage: (rowsPerPage: number) => {
+            dispatch(dataExplorerActions.SET_ROWS_PER_PAGE({ id, rowsPerPage }));
+        },
 
-            onRowClick,
+        onRowClick,
 
-            onRowDoubleClick,
+        onRowDoubleClick,
 
-            onContextMenu,
-        };
-    };
+        onContextMenu,
+    });
 };
 
 export const DataExplorer = connect(mapStateToProps, mapDispatchToProps())(DataExplorerComponent);
index 8bce325425f7c52a07cbbb958c8f316b48cdb29f..54d6a5da0ec8c5306734ba27861a5288fd21fd89 100644 (file)
@@ -26,6 +26,7 @@ interface MainAppBarDataProps {
     breadcrumbs: Breadcrumb[];
     user?: User;
     menuItems: MainAppBarMenuItems;
+    buildInfo: string;
 }
 
 export interface MainAppBarActionProps {
@@ -44,10 +45,10 @@ export const MainAppBar: React.SFC<MainAppBarProps> = (props) => {
             <Grid container justify="space-between">
                 <Grid item xs={3}>
                     <Typography variant="headline" color="inherit" noWrap>
-                        Arvados
+                        Arvados 2
                     </Typography>
                     <Typography variant="body1" color="inherit" noWrap >
-                        Workbench 2
+                        {props.buildInfo}
                     </Typography>
                 </Grid>
                 <Grid item xs={6} container alignItems="center">
index 09a07443f26b9097ddcccfa9ded1b95d2dea5d85..ba9ccb916efd38d955badedae7e6a7418871d354 100644 (file)
@@ -6,7 +6,7 @@ import { connect } from "react-redux";
 import { Tree, TreeProps, TreeItem } from "~/components/tree/tree";
 import { RootState } from "~/store/store";
 import { TreePicker as TTreePicker, TreePickerNode, createTreePickerNode } from "~/store/tree-picker/tree-picker";
-import { getNodeValue, getNodeChildren } from "~/models/tree";
+import { getNodeValue, getNodeChildrenIds } from "~/models/tree";
 
 const memoizedMapStateToProps = () => {
     let prevState: TTreePicker;
@@ -15,7 +15,7 @@ const memoizedMapStateToProps = () => {
     return (state: RootState): Pick<TreeProps<any>, 'items'> => {
         if (prevState !== state.treePicker) {
             prevState = state.treePicker;
-            prevTree = getNodeChildren('')(state.treePicker)
+            prevTree = getNodeChildrenIds('')(state.treePicker)
                 .map(treePickerToTreeItems(state.treePicker));
         }
         return {
@@ -33,7 +33,7 @@ export const TreePicker = connect(memoizedMapStateToProps(), mapDispatchToProps)
 const treePickerToTreeItems = (tree: TTreePicker) =>
     (id: string): TreeItem<any> => {
         const node: TreePickerNode = getNodeValue(id)(tree) || createTreePickerNode({ id: '', value: 'InvalidNode' });
-        const items = getNodeChildren(node.id)(tree)
+        const items = getNodeChildrenIds(node.id)(tree)
             .map(treePickerToTreeItems(tree));
         return {
             active: node.selected,
index 71e5a03b7bfd2231c18492e227b041b52d951012..4949b6a0b60e8194c04c0bdce967c5b83ce9af9d 100644 (file)
@@ -29,7 +29,6 @@ import { CreateProjectDialog } from "~/views-components/create-project-dialog/cr
 
 import { detailsPanelActions, loadDetails } from "~/store/details-panel/details-panel-action";
 import { contextMenuActions } from "~/store/context-menu/context-menu-actions";
-import { SidePanelIdentifiers } from '~/store/side-panel/side-panel-reducer';
 import { ProjectResource } from '~/models/project';
 import { ResourceKind } from '~/models/resource';
 import { ContextMenu, ContextMenuKind } from "~/views-components/context-menu/context-menu";
@@ -102,14 +101,15 @@ interface WorkbenchDataProps {
     sidePanelItems: SidePanelItem[];
 }
 
-interface WorkbenchServiceProps {
+interface WorkbenchGeneralProps {
     authService: AuthService;
+    buildInfo: string;
 }
 
 interface WorkbenchActionProps {
 }
 
-type WorkbenchProps = WorkbenchDataProps & WorkbenchServiceProps & WorkbenchActionProps & DispatchProp<any> & WithStyles<CssRules>;
+type WorkbenchProps = WorkbenchDataProps & WorkbenchGeneralProps & WorkbenchActionProps & DispatchProp<any> & WithStyles<CssRules>;
 
 interface NavBreadcrumb extends Breadcrumb {
     itemId: string;
@@ -194,6 +194,7 @@ export const Workbench = withStyles(styles)(
                                 searchText={this.state.searchText}
                                 user={this.props.user}
                                 menuItems={this.state.menuItems}
+                                buildInfo={this.props.buildInfo}
                                 {...this.mainAppBarActions} />
                         </div>
                         {user &&
@@ -223,7 +224,6 @@ export const Workbench = withStyles(styles)(
                                         toggleActive={itemId => {
                                             this.props.dispatch(setProjectItem(itemId, ItemMode.ACTIVE));
                                             this.props.dispatch(loadDetails(itemId, ResourceKind.PROJECT));
-                                            this.props.dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_ACTIVE(SidePanelIdentifiers.PROJECTS));
                                         }} />
                                 </SidePanel>
                             </Drawer>}
@@ -332,7 +332,6 @@ export const Workbench = withStyles(styles)(
                         default:
                             this.props.dispatch(loadDetails(item.uuid, ResourceKind.PROJECT));
                             this.props.dispatch(setProjectItem(item.uuid, ItemMode.ACTIVE));
-                            this.props.dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_ACTIVE(SidePanelIdentifiers.PROJECTS));
                     }
 
                 }}
@@ -365,7 +364,6 @@ export const Workbench = withStyles(styles)(
             }
 
             toggleSidePanelActive = (itemId: string) => {
-                this.props.dispatch(sidePanelActions.TOGGLE_SIDE_PANEL_ITEM_ACTIVE(itemId));
                 this.props.dispatch(projectActions.RESET_PROJECT_TREE_ACTIVITY(itemId));
 
                 const panelItem = this.props.sidePanelItems.find(it => it.id === itemId);
index ae2b1c571627921abf4fc87cee07613423376134..67c12647b4625337eae116a1f6feea7f9322da8e 100644 (file)
--- a/yarn.lock
+++ b/yarn.lock
     esutils "^2.0.2"
     js-tokens "^3.0.0"
 
-"@babel/runtime@^7.0.0-beta.42":
-  version "7.0.0-beta.54"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.54.tgz#39ebb42723fe7ca4b3e1b00e967e80138d47cadf"
+"@babel/runtime@7.0.0-beta.42":
+  version "7.0.0-beta.42"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.42.tgz#352e40c92e0460d3e82f49bd7e79f6cda76f919f"
+  dependencies:
+    core-js "^2.5.3"
+    regenerator-runtime "^0.11.1"
+
+"@babel/runtime@7.0.0-beta.56":
+  version "7.0.0-beta.56"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0-beta.56.tgz#cda612dffd5b1719a7b8e91e3040bd6ae64de8b0"
   dependencies:
-    core-js "^2.5.7"
     regenerator-runtime "^0.12.0"
 
-"@material-ui/core@1.4.2":
-  version "1.4.2"
-  resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.4.2.tgz#8a1282e985d4922a4d2b4f7e287d8a716a2fc108"
+"@material-ui/core@1.5.0":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-1.5.0.tgz#00884bb4139d98786d05a97803d19426d4afa55d"
   dependencies:
-    "@babel/runtime" "^7.0.0-beta.42"
+    "@babel/runtime" "7.0.0-beta.42"
     "@types/jss" "^9.5.3"
     "@types/react-transition-group" "^2.0.8"
     brcast "^3.0.1"
     normalize-scroll-left "^0.1.2"
     popper.js "^1.14.1"
     prop-types "^15.6.0"
-    react-event-listener "^0.6.0"
+    react-event-listener "^0.6.2"
     react-jss "^8.1.0"
     react-transition-group "^2.2.1"
-    recompose "^0.27.0"
+    recompose "^0.28.0"
     warning "^4.0.1"
 
-"@material-ui/icons@2.0.0":
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-2.0.0.tgz#f2c4e80d0cb4bbbd433127781da67d93393535f8"
+"@material-ui/icons@2.0.2":
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/@material-ui/icons/-/icons-2.0.2.tgz#0150c38cda089ef284e9b4a730dfe6e88a0b5de6"
   dependencies:
-    "@babel/runtime" "^7.0.0-beta.42"
-    recompose "^0.27.0"
+    "@babel/runtime" "7.0.0-beta.42"
+    recompose "^0.28.0"
 
 "@types/cheerio@*":
   version "0.22.8"
   version "2.2.6"
   resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.6.tgz#dbe8a666156d556ed018e15a4c65f08937c3f628"
 
-"@types/enzyme-adapter-react-16@1.0.2":
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.2.tgz#15ae37c64d6221a6f4b3a4aacc357cf773859de4"
+"@types/enzyme-adapter-react-16@1.0.3":
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.3.tgz#0cf7025b036694ca8d596fe38f24162e7117acf1"
   dependencies:
     "@types/enzyme" "*"
 
-"@types/enzyme@*", "@types/enzyme@3.1.12":
+"@types/enzyme@*":
   version "3.1.12"
   resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.12.tgz#293bb07c1ef5932d37add3879e72e0f5bc614f3c"
   dependencies:
     "@types/cheerio" "*"
     "@types/react" "*"
 
+"@types/enzyme@3.1.13":
+  version "3.1.13"
+  resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.1.13.tgz#4bbc5c81fa40c9fc7efee25c4a23cb37119a33ea"
+  dependencies:
+    "@types/cheerio" "*"
+    "@types/react" "*"
+
 "@types/history@*":
   version "4.6.2"
   resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.2.tgz#12cfaba693ba20f114ed5765467ff25fdf67ddb0"
   version "10.5.2"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.2.tgz#f19f05314d5421fe37e74153254201a7bf00a707"
 
-"@types/node@10.5.5":
-  version "10.5.5"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.5.tgz#8e84d24e896cd77b0d4f73df274027e3149ec2ba"
+"@types/node@10.7.1":
+  version "10.7.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.7.1.tgz#b704d7c259aa40ee052eec678758a68d07132a2e"
 
-"@types/react-copy-to-clipboard@4.2.5":
-  version "4.2.5"
-  resolved "https://registry.yarnpkg.com/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-4.2.5.tgz#bda288b4256288676019b75ca86f1714bbd206d4"
+"@types/react-copy-to-clipboard@4.2.6":
+  version "4.2.6"
+  resolved "https://registry.yarnpkg.com/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-4.2.6.tgz#d1374550dec803f17f26ec71b62783c5737bfc02"
   dependencies:
     "@types/react" "*"
 
-"@types/react-dom@16.0.6":
-  version "16.0.6"
-  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.6.tgz#f1a65a4e7be8ed5d123f8b3b9eacc913e35a1a3c"
+"@types/react-dom@16.0.7":
+  version "16.0.7"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.7.tgz#54d0f867a76b90597e8432030d297982f25c20ba"
   dependencies:
     "@types/node" "*"
     "@types/react" "*"
 
-"@types/react-dropzone@4.2.1":
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/@types/react-dropzone/-/react-dropzone-4.2.1.tgz#4a973b63a8a227e263ff4eece053f643220f28fc"
+"@types/react-dropzone@4.2.2":
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/@types/react-dropzone/-/react-dropzone-4.2.2.tgz#af0a2595169700c8ab1114e9096285499beaff40"
   dependencies:
     "@types/react" "*"
 
     "@types/react" "*"
     redux "^3.6.0"
 
-"@types/redux-form@7.4.4":
-  version "7.4.4"
-  resolved "https://registry.yarnpkg.com/@types/redux-form/-/redux-form-7.4.4.tgz#2cf62b8eb1dc1b1df95b6b25c2763db196e5c190"
+"@types/redux-form@7.4.5":
+  version "7.4.5"
+  resolved "https://registry.yarnpkg.com/@types/redux-form/-/redux-form-7.4.5.tgz#fae0fa6cfbc613867093d1e0f6a84db17177305e"
   dependencies:
     "@types/react" "*"
     redux "^3.6.0 || ^4.0.0"
@@ -407,6 +420,14 @@ array-unique@^0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
 
+array.prototype.flat@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4"
+  dependencies:
+    define-properties "^1.1.2"
+    es-abstract "^1.10.0"
+    function-bind "^1.1.1"
+
 arrify@^1.0.0, arrify@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
@@ -471,7 +492,7 @@ atob@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a"
 
-attr-accept@^1.0.3:
+attr-accept@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52"
   dependencies:
@@ -1849,7 +1870,7 @@ core-js@^1.0.0:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
 
-core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7:
+core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3:
   version "2.5.7"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
 
@@ -2440,39 +2461,42 @@ entities@^1.1.1, entities@~1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
 
-enzyme-adapter-react-16@1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz#a8f4278b47e082fbca14f5bfb1ee50ee650717b4"
+enzyme-adapter-react-16@1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.2.0.tgz#c6e80f334e0a817873262d7d01ee9e4747e3c97e"
   dependencies:
-    enzyme-adapter-utils "^1.3.0"
-    lodash "^4.17.4"
-    object.assign "^4.0.4"
+    enzyme-adapter-utils "^1.5.0"
+    function.prototype.name "^1.1.0"
+    object.assign "^4.1.0"
     object.values "^1.0.4"
-    prop-types "^15.6.0"
+    prop-types "^15.6.2"
+    react-is "^16.4.2"
     react-reconciler "^0.7.0"
     react-test-renderer "^16.0.0-0"
 
-enzyme-adapter-utils@^1.3.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.4.0.tgz#c403b81e8eb9953658569e539780964bdc98de62"
+enzyme-adapter-utils@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.5.0.tgz#a020ab3ae79bb1c85e1d51f48f35e995e0eed810"
   dependencies:
+    function.prototype.name "^1.1.0"
     object.assign "^4.1.0"
-    prop-types "^15.6.0"
+    prop-types "^15.6.2"
 
-enzyme@3.3.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.3.0.tgz#0971abd167f2d4bf3f5bd508229e1c4b6dc50479"
+enzyme@3.4.4:
+  version "3.4.4"
+  resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.4.4.tgz#92c7c6b9e59d4ef0c3d36a75dccc0e41a5c14d21"
   dependencies:
+    array.prototype.flat "^1.2.1"
     cheerio "^1.0.0-rc.2"
-    function.prototype.name "^1.0.3"
-    has "^1.0.1"
+    function.prototype.name "^1.1.0"
+    has "^1.0.3"
     is-boolean-object "^1.0.0"
-    is-callable "^1.1.3"
+    is-callable "^1.1.4"
     is-number-object "^1.0.3"
     is-string "^1.0.4"
     is-subset "^0.1.1"
     lodash "^4.17.4"
-    object-inspect "^1.5.0"
+    object-inspect "^1.6.0"
     object-is "^1.0.1"
     object.assign "^4.1.0"
     object.entries "^1.0.4"
@@ -2492,7 +2516,7 @@ error-ex@^1.2.0:
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
+es-abstract@^1.10.0, es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0:
   version "1.12.0"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165"
   dependencies:
@@ -3082,7 +3106,7 @@ function-bind@^1.1.0, function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
 
-function.prototype.name@^1.0.3:
+function.prototype.name@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327"
   dependencies:
@@ -3319,7 +3343,7 @@ has-values@^1.0.0:
     is-number "^3.0.0"
     kind-of "^4.0.0"
 
-has@^1.0.1:
+has@^1.0.1, has@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
   dependencies:
@@ -3689,7 +3713,7 @@ is-builtin-module@^1.0.0:
   dependencies:
     builtin-modules "^1.0.0"
 
-is-callable@^1.1.1, is-callable@^1.1.3:
+is-callable@^1.1.1, is-callable@^1.1.3, is-callable@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
 
@@ -5230,7 +5254,7 @@ object-copy@^0.1.0:
     define-property "^0.2.5"
     kind-of "^3.0.3"
 
-object-inspect@^1.5.0:
+object-inspect@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
 
@@ -5248,7 +5272,7 @@ object-visit@^1.0.0:
   dependencies:
     isobject "^3.0.0"
 
-object.assign@^4.0.4, object.assign@^4.1.0:
+object.assign@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
   dependencies:
@@ -6138,22 +6162,22 @@ react-dom@16.4.2:
     object-assign "^4.1.1"
     prop-types "^15.6.0"
 
-react-dropzone@4.2.13:
-  version "4.2.13"
-  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-4.2.13.tgz#31393c079b4e5ddcc176c095cebc3545d1248b9d"
+react-dropzone@5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-5.0.1.tgz#3ed201215794c0f650c6f25a8311a9d96d35ebb6"
   dependencies:
-    attr-accept "^1.0.3"
+    attr-accept "^1.1.3"
     prop-types "^15.5.7"
 
 react-error-overlay@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
 
-react-event-listener@^0.6.0:
-  version "0.6.1"
-  resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.1.tgz#41c7a80a66b398c27dd511e22712b02f3d4eccca"
+react-event-listener@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.6.2.tgz#df405e9578be052b77a76e4c3914686637caecff"
   dependencies:
-    "@babel/runtime" "^7.0.0-beta.42"
+    "@babel/runtime" "7.0.0-beta.42"
     prop-types "^15.6.0"
     warning "^4.0.1"
 
@@ -6161,6 +6185,10 @@ react-is@^16.4.1:
   version "16.4.1"
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.1.tgz#d624c4650d2c65dbd52c72622bbf389435d9776e"
 
+react-is@^16.4.2:
+  version "16.4.2"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.2.tgz#84891b56c2b6d9efdee577cc83501dfc5ecead88"
+
 react-jss@^8.1.0:
   version "8.6.1"
   resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-8.6.1.tgz#a06e2e1d2c4d91b4d11befda865e6c07fbd75252"
@@ -6363,11 +6391,11 @@ realpath-native@^1.0.0:
   dependencies:
     util.promisify "^1.0.0"
 
-recompose@^0.27.0:
-  version "0.27.1"
-  resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.27.1.tgz#1a49e931f183634516633bbb4f4edbfd3f38a7ba"
+recompose@^0.28.0:
+  version "0.28.2"
+  resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.28.2.tgz#19e679227bdf979e0d31b73ffe7ae38c9194f4a7"
   dependencies:
-    babel-runtime "^6.26.0"
+    "@babel/runtime" "7.0.0-beta.56"
     change-emitter "^0.1.2"
     fbjs "^0.8.1"
     hoist-non-react-statics "^2.3.1"
@@ -6453,7 +6481,7 @@ regenerate@^1.2.1:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
 
-regenerator-runtime@^0.11.0:
+regenerator-runtime@^0.11.0, regenerator-runtime@^0.11.1:
   version "0.11.1"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"