top-left-title-does-not-function-as-a-link
authorPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Wed, 5 Sep 2018 12:52:43 +0000 (14:52 +0200)
committerPawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>
Wed, 5 Sep 2018 12:52:43 +0000 (14:52 +0200)
Feature #14123

Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk <pawel.kowalczyk@contractors.roche.com>

src/routes/route-change-handlers.ts
src/store/navigation/navigation-action.ts
src/views-components/main-app-bar/main-app-bar.test.tsx
src/views-components/main-app-bar/main-app-bar.tsx

index b29e5d1e1afdf993b54135048cce74563dacc515..00fb4bc05acbfcf8a4dc47e3c20f19516443cf19 100644 (file)
@@ -4,8 +4,9 @@
 
 import { History, Location } from 'history';
 import { RootStore } from '~/store/store';
-import {  matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute } from './routes';
+import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute } from './routes';
 import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog } from '~/store/workbench/workbench-actions';
+import { navigateToRootProject } from '~/store/navigation/navigation-action';
 
 export const addRouteChangeHandlers = (history: History, store: RootStore) => {
     const handler = handleLocationChange(store);
@@ -14,6 +15,7 @@ export const addRouteChangeHandlers = (history: History, store: RootStore) => {
 };
 
 const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
+    const rootMatch = matchRootRoute(pathname);
     const projectMatch = matchProjectRoute(pathname);
     const collectionMatch = matchCollectionRoute(pathname);
     const favoriteMatch = matchFavoritesRoute(pathname);
@@ -33,5 +35,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
         store.dispatch(loadProcess(processMatch.params.id));
     } else if (processLogMatch) {
         store.dispatch(loadProcessLog(processLogMatch.params.id));
+    } else if (rootMatch) {
+        store.dispatch(navigateToRootProject);
     }
 };
index ddb9d29ffdaa05a79f591773412f2723f4500337..33181b373f66e995d53869cb74820c6da1c2db4d 100644 (file)
@@ -7,9 +7,10 @@ import { push } from "react-router-redux";
 import { ResourceKind, extractUuidKind } from '~/models/resource';
 import { getCollectionUrl } from "~/models/collection";
 import { getProjectUrl } from "~/models/project";
-
 import { SidePanelTreeCategory } from '../side-panel-tree/side-panel-tree-actions';
 import { Routes, getProcessUrl, getProcessLogUrl } from '~/routes/routes';
+import { RootState } from '~/store/store';
+import { ServiceRepository } from '~/services/services';
 
 export const navigateTo = (uuid: string) =>
     async (dispatch: Dispatch) => {
@@ -36,4 +37,11 @@ export const navigateToCollection = compose(push, getCollectionUrl);
 
 export const navigateToProcess = compose(push, getProcessUrl);
 
-export const navigateToProcessLogs = compose(push, getProcessLogUrl);
\ No newline at end of file
+export const navigateToProcessLogs = compose(push, getProcessLogUrl);
+
+export const navigateToRootProject = (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+    const rootProjectUuid = services.authService.getUuid();
+    if(rootProjectUuid){
+        dispatch(navigateToProject(rootProjectUuid));
+    }
+};
\ No newline at end of file
index 030fb353e3d3e52f15dbfd22f478126a87b55807..69b4dd648cc86ea2b60fa2ebc8fb16173c1e6e29 100644 (file)
@@ -11,6 +11,7 @@ import { Breadcrumbs } from "~/components/breadcrumbs/breadcrumbs";
 import { DropdownMenu } from "~/components/dropdown-menu/dropdown-menu";
 import { Button, MenuItem, IconButton } from "@material-ui/core";
 import { User } from "~/models/user";
+import { MemoryRouter } from 'react-router-dom';
 
 configure({ adapter: new Adapter() });
 
@@ -26,9 +27,11 @@ describe("<MainAppBar />", () => {
 
     it("renders all components and the menu for authenticated user if user prop has value", () => {
         const mainAppBar = mount(
-            <MainAppBar
-                {...mockMainAppBarProps({ user })}
-            />
+            <MemoryRouter>
+                <MainAppBar
+                    {...mockMainAppBarProps({ user })}
+                />
+            </MemoryRouter>
         );
         expect(mainAppBar.find(SearchBar)).toHaveLength(1);
         expect(mainAppBar.find(Breadcrumbs)).toHaveLength(1);
@@ -38,9 +41,11 @@ describe("<MainAppBar />", () => {
     it("renders only the menu for anonymous user if user prop is undefined", () => {
         const menuItems = { accountMenu: [], helpMenu: [], anonymousMenu: [{ label: 'Sign in' }] };
         const mainAppBar = mount(
-            <MainAppBar
-                {...mockMainAppBarProps({ user: undefined, menuItems })}
-            />
+            <MemoryRouter>
+                <MainAppBar
+                    {...mockMainAppBarProps({ user: undefined, menuItems })}
+                />
+            </MemoryRouter>
         );
         expect(mainAppBar.find(SearchBar)).toHaveLength(0);
         expect(mainAppBar.find(Breadcrumbs)).toHaveLength(0);
@@ -51,9 +56,11 @@ describe("<MainAppBar />", () => {
     it("communicates with <SearchBar />", () => {
         const onSearch = jest.fn();
         const mainAppBar = mount(
-            <MainAppBar
-                {...mockMainAppBarProps({ searchText: 'search text', searchDebounce: 2000, onSearch, user })}
-            />
+            <MemoryRouter>
+                <MainAppBar
+                    {...mockMainAppBarProps({ searchText: 'search text', searchDebounce: 2000, onSearch, user })}
+                />
+            </MemoryRouter>
         );
         const searchBar = mainAppBar.find(SearchBar);
         expect(searchBar.prop("value")).toBe("search text");
@@ -66,9 +73,11 @@ describe("<MainAppBar />", () => {
         const onMenuItemClick = jest.fn();
         const menuItems = { accountMenu: [{ label: "log out" }], helpMenu: [], anonymousMenu: [] };
         const mainAppBar = mount(
-            <MainAppBar
-                {...mockMainAppBarProps({ menuItems, onMenuItemClick, user })}
-            />
+            <MemoryRouter>
+                <MainAppBar
+                    {...mockMainAppBarProps({ menuItems, onMenuItemClick, user })}
+                />
+            </MemoryRouter>
         );
 
         mainAppBar.find(DropdownMenu).at(0).find(IconButton).simulate("click");
index de6be7e7948fa78fbf8a232db386139a351052a1..44be9f7aa103f3584da4a99091cd11fab4566791 100644 (file)
@@ -4,10 +4,23 @@
 
 import * as React from "react";
 import { AppBar, Toolbar, Typography, Grid, IconButton, Badge, Button, MenuItem } from "@material-ui/core";
+import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
+import { ArvadosTheme } from '~/common/custom-theme';
+import { Link } from "react-router-dom";
 import { User, getUserFullname } from "~/models/user";
 import { SearchBar } from "~/components/search-bar/search-bar";
 import { DropdownMenu } from "~/components/dropdown-menu/dropdown-menu";
 import { DetailsIcon, NotificationIcon, UserPanelIcon, HelpIcon } from "~/components/icon/icon";
+import { Routes } from '~/routes/routes';
+
+type CssRules = 'link';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    link: {
+        textDecoration: 'none',
+        color: 'inherit'
+    }
+});
 
 export interface MainAppBarMenuItem {
     label: string;
@@ -34,45 +47,49 @@ export interface MainAppBarActionProps {
     onDetailsPanelToggle: () => void;
 }
 
-export type MainAppBarProps = MainAppBarDataProps & MainAppBarActionProps;
+export type MainAppBarProps = MainAppBarDataProps & MainAppBarActionProps & WithStyles<CssRules>;
 
-export const MainAppBar: React.SFC<MainAppBarProps> = (props) => {
-    return <AppBar position="static">
-        <Toolbar>
-            <Grid container justify="space-between">
-                <Grid item xs={3}>
-                    <Typography variant="headline" color="inherit" noWrap>
-                        Arvados 2
-                    </Typography>
-                    <Typography variant="body1" color="inherit" noWrap >
-                        {props.buildInfo}
-                    </Typography>
-                </Grid>
-                <Grid item xs={6} container alignItems="center">
-                    {
-                        props.user && <SearchBar
-                            value={props.searchText}
-                            onSearch={props.onSearch}
-                            debounce={props.searchDebounce}
-                        />
-                    }
+export const MainAppBar = withStyles(styles)(
+    (props: MainAppBarProps) => {
+        return <AppBar position="static">
+            <Toolbar>
+                <Grid container justify="space-between">
+                    <Grid item xs={3}>
+                        <Typography variant="headline" color="inherit" noWrap>
+                            <Link to={Routes.ROOT} className={props.classes.link}>
+                                Arvados 2
+                            </Link>
+                        </Typography>
+                        <Typography variant="body1" color="inherit" noWrap >
+                            {props.buildInfo}
+                        </Typography>
+                    </Grid>
+                    <Grid item xs={6} container alignItems="center">
+                        {
+                            props.user && <SearchBar
+                                value={props.searchText}
+                                onSearch={props.onSearch}
+                                debounce={props.searchDebounce}
+                            />
+                        }
+                    </Grid>
+                    <Grid item xs={3} container alignItems="center" justify="flex-end">
+                        {
+                            props.user ? renderMenuForUser(props) : renderMenuForAnonymous(props)
+                        }
+                    </Grid>
                 </Grid>
-                <Grid item xs={3} container alignItems="center" justify="flex-end">
-                    {
-                        props.user ? renderMenuForUser(props) : renderMenuForAnonymous(props)
-                    }
-                </Grid>
-            </Grid>
-        </Toolbar>
-        <Toolbar >
-            {props.user && <props.breadcrumbs />}
-            {props.user && <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
-                <DetailsIcon />
-            </IconButton>
-            }
-        </Toolbar>
-    </AppBar>;
-};
+            </Toolbar>
+            <Toolbar >
+                {props.user && <props.breadcrumbs />}
+                {props.user && <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
+                    <DetailsIcon />
+                </IconButton>
+                }
+            </Toolbar>
+        </AppBar>;
+    }
+);
 
 const renderMenuForUser = ({ user, menuItems, onMenuItemClick }: MainAppBarProps) => {
     return (