[ARVADOS-WORKBENCH2] updated: 2.2.1-123-g0efdf1ed

Git user git at public.arvados.org
Tue Oct 19 20:50:56 UTC 2021


Summary of changes:
 src/components/data-explorer/data-explorer.tsx     |  49 ++++++---
 src/components/data-table/data-table.tsx           |   6 +-
 src/components/icon/icon.tsx                       |   6 ++
 .../multi-panel-view/multi-panel-view.test.tsx     |  85 +++++++++++++++
 .../multi-panel-view/multi-panel-view.tsx          | 118 ++++++++++++++++-----
 src/store/process-logs-panel/process-logs-panel.ts |   7 +-
 .../details-panel/process-details.tsx              |  27 +----
 .../all-processes-panel/all-processes-panel.tsx    |  18 ++--
 .../api-client-authorization-panel-root.tsx        |  62 ++++-------
 .../api-client-authorization-panel.tsx             |   4 -
 .../collection-content-address-panel.tsx           |  32 ++----
 src/views/favorite-panel/favorite-panel.tsx        |   9 +-
 src/views/groups-panel/groups-panel.tsx            |  21 ++--
 src/views/link-panel/link-panel-root.tsx           |  28 +++--
 .../process-panel/process-details-attributes.tsx   |  65 ++++++++++++
 src/views/process-panel/process-details-card.tsx   |  63 +++++++++++
 .../process-panel/process-information-card.tsx     |   9 +-
 src/views/process-panel/process-panel-root.tsx     |  40 +++++--
 src/views/project-panel/project-panel.tsx          |   2 -
 .../public-favorites-panel.tsx                     |   9 +-
 .../shared-with-me-panel/shared-with-me-panel.tsx  |   9 +-
 .../subprocess-panel/subprocess-panel-root.tsx     |   7 +-
 src/views/trash-panel/trash-panel.tsx              |   9 +-
 src/views/user-panel/user-panel.tsx                |  15 ++-
 src/views/workbench/workbench.tsx                  |   3 +
 25 files changed, 499 insertions(+), 204 deletions(-)
 create mode 100644 src/components/multi-panel-view/multi-panel-view.test.tsx
 create mode 100644 src/views/process-panel/process-details-attributes.tsx
 create mode 100644 src/views/process-panel/process-details-card.tsx

       via  0efdf1ed28b92c0574d261ed6f96b4c3f77fe273 (commit)
       via  8eab4f977979b908c65bf263e83f1ca312ba0ab2 (commit)
       via  44dfb8ae1c95a7834f9aec6f217e6f37b9638678 (commit)
       via  d6cc3459a7c57c3fef994df30a04ac846deeeeb4 (commit)
       via  61b498513f5bb98980a852425d4912913f0957fe (commit)
       via  a67404ec61615e4905338efd0470e3298f6ba41f (commit)
       via  a3c4448560dbf77554fa87c4bba4dc2f680395bb (commit)
       via  61428cc9cf2e4b9001a534b65f56cb856ff58afe (commit)
       via  d107ed0f6fa401e1cfe180ca20e2c76233f65ac2 (commit)
       via  410cdd36823f2ce733d45451c2cf1c56af1f4c33 (commit)
       via  8e1c3506127bfcc1ec96df1cf87924b86c16cbe9 (commit)
      from  826a0364d7486b54765d03a384f22f19376bc322 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 0efdf1ed28b92c0574d261ed6f96b4c3f77fe273
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Tue Oct 19 17:50:38 2021 -0300

    18128: Adds unit tests.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/multi-panel-view/multi-panel-view.test.tsx b/src/components/multi-panel-view/multi-panel-view.test.tsx
new file mode 100644
index 00000000..53a3bb60
--- /dev/null
+++ b/src/components/multi-panel-view/multi-panel-view.test.tsx
@@ -0,0 +1,85 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from "react";
+import { configure, mount } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { MPVContainer } from './multi-panel-view';
+import { Button } from "@material-ui/core";
+
+configure({ adapter: new Adapter() });
+
+const PanelMock = ({panelName, panelMaximized, doHidePanel, doMaximizePanel, children, ...rest}) =>
+    <div {...rest}>{children}</div>;
+
+describe('<MPVContainer />', () => {
+    let props;
+
+    beforeEach(() => {
+        props = {
+            classes: {},
+        };
+    });
+
+    it('should show default toggle buttons for every child', () => {
+        const childs = [
+            <PanelMock key={1}>This is one panel</PanelMock>,
+            <PanelMock key={2}>This is another panel</PanelMock>,
+        ];
+        const wrapper = mount(<MPVContainer {...props}>{[...childs]}</MPVContainer>);
+        expect(wrapper.find(Button).first().html()).toContain('Panel 1');
+        expect(wrapper.html()).toContain('This is one panel');
+        expect(wrapper.find(Button).last().html()).toContain('Panel 2');
+        expect(wrapper.html()).toContain('This is another panel');
+    });
+
+    it('should toggle panel when clicking on its button', () => {
+        const childs = [
+            <PanelMock key={1}>This is one panel</PanelMock>,
+        ];
+        const wrapper = mount(<MPVContainer {...props}>{[...childs]}</MPVContainer>);
+
+        // Initial state: panel visible
+        expect(wrapper.html()).toContain('This is one panel');
+
+        // Panel toggling
+        wrapper.find(Button).simulate('click');
+        expect(wrapper.html()).not.toContain('This is one panel');
+        expect(wrapper.html()).toContain('All panels are hidden');
+        wrapper.find(Button).simulate('click');
+        expect(wrapper.html()).toContain('This is one panel');
+        expect(wrapper.html()).not.toContain('All panels are hidden');
+    });
+
+    it('should show custom toggle buttons when config provided', () => {
+        const childs = [
+            <PanelMock key={1}>This is one panel</PanelMock>,
+            <PanelMock key={2}>This is another panel</PanelMock>,
+        ];
+        props.panelStates = [
+            {name: 'First Panel'},
+        ]
+        const wrapper = mount(<MPVContainer {...props}>{[...childs]}</MPVContainer>);
+        expect(wrapper.find(Button).first().html()).toContain('First Panel');
+        expect(wrapper.html()).toContain('This is one panel');
+        // Second panel received the default button naming and hidden status by default
+        expect(wrapper.find(Button).last().html()).toContain('Panel 2');
+        expect(wrapper.html()).not.toContain('This is another panel');
+        wrapper.find(Button).last().simulate('click');
+        expect(wrapper.html()).toContain('This is another panel');
+    });
+
+    it('should set panel hidden when requested', () => {
+        const childs = [
+            <PanelMock key={1}>This is one panel</PanelMock>,
+        ];
+        props.panelStates = [
+            {name: 'First Panel', visible: false},
+        ]
+        const wrapper = mount(<MPVContainer {...props}>{[...childs]}</MPVContainer>);
+        expect(wrapper.find(Button).html()).toContain('First Panel');
+        expect(wrapper.html()).not.toContain('This is one panel');
+        expect(wrapper.html()).toContain('All panels are hidden');
+    });
+});
\ No newline at end of file
diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index 6778b526..35daa13c 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -119,8 +119,8 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
                 ? `Panel ${idx+1}`
                 : (panelStates[idx] && panelStates[idx].name) || `Panel ${idx+1}`;
             const toggleVariant = panelVisibility[idx]
-                ? "raised"
-                : "flat";
+                ? "contained"
+                : "text";
             const toggleTooltip = panelVisibility[idx]
                 ? `Hide ${panelName} panel`
                 : `Show ${panelName} panel`;
@@ -140,7 +140,7 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
             ];
 
             const aPanel =
-                <MPVHideablePanel visible={panelVisibility[idx]} name={panelName}
+                <MPVHideablePanel key={idx} visible={panelVisibility[idx]} name={panelName}
                     maximized={panelIsMaximized}
                     doHidePanel={toggleFn(idx)} doMaximizePanel={maximizeFn(idx)}>
                     {children[idx]}
@@ -151,7 +151,7 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
 
     return <Grid container {...props}>
         <Grid container item direction="row">
-            { toggles.map(tgl => <Grid item>{tgl}</Grid>) }
+            { toggles.map((tgl, idx) => <Grid item key={idx}>{tgl}</Grid>) }
         </Grid>
         <Grid container item {...props} xs className={classes.content}>
             { panelVisibility.includes(true)

commit 8eab4f977979b908c65bf263e83f1ca312ba0ab2
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Tue Oct 19 16:23:21 2021 -0300

    18128: Adds logs panel to process view. WIP
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/store/process-logs-panel/process-logs-panel.ts b/src/store/process-logs-panel/process-logs-panel.ts
index deaaab6a..87b50bd2 100644
--- a/src/store/process-logs-panel/process-logs-panel.ts
+++ b/src/store/process-logs-panel/process-logs-panel.ts
@@ -1,9 +1,10 @@
-import { RootState } from '../store';
-import { matchProcessLogRoute } from 'routes/routes';
 // Copyright (C) The Arvados Authors. All rights reserved.
 //
 // SPDX-License-Identifier: AGPL-3.0
 
+import { RootState } from '../store';
+import { matchProcessLogRoute, matchProcessRoute } from 'routes/routes';
+
 export interface ProcessLogsPanel {
     filters: string[];
     selectedFilter: string;
@@ -20,6 +21,6 @@ export const getProcessPanelLogs = ({ selectedFilter, logs }: ProcessLogsPanel)
 
 export const getProcessLogsPanelCurrentUuid = ({ router }: RootState) => {
     const pathname = router.location ? router.location.pathname : '';
-    const match = matchProcessLogRoute(pathname);
+    const match = matchProcessLogRoute(pathname) || matchProcessRoute(pathname);
     return match ? match.params.id : undefined;
 };
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index e604fe2b..f30ebf70 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -12,6 +12,8 @@ import { SubprocessPanel } from 'views/subprocess-panel/subprocess-panel';
 import { SubprocessFilterDataProps } from 'components/subprocess-filter/subprocess-filter';
 import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
 import { ArvadosTheme } from 'common/custom-theme';
+import { ProcessLogPanel } from 'views/process-log-panel/process-log-panel';
+import { ProcessDetailsCard } from './process-details-card';
 
 type CssRules = 'root';
 
@@ -40,6 +42,8 @@ export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRoot
 
 const panelsData: MPVPanelState[] = [
     {name: "Info"},
+    {name: "Details"},
+    {name: "Logs", visible: false},
     {name: "Subprocesses"},
 ];
 
@@ -56,6 +60,12 @@ export const ProcessPanelRoot = withStyles(styles)(({ process, ...props }: Proce
                     cancelProcess={props.cancelProcess}
                 />
             </MPVPanelContent>
+            <MPVPanelContent xs="auto">
+                <ProcessDetailsCard process={process} />
+            </MPVPanelContent>
+            <MPVPanelContent xs="auto">
+                <ProcessLogPanel />
+            </MPVPanelContent>
             <MPVPanelContent xs>
                 <SubprocessPanel />
             </MPVPanelContent>

commit 44dfb8ae1c95a7834f9aec6f217e6f37b9638678
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Tue Oct 19 16:20:32 2021 -0300

    18128: Separate process details into their own component.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/views-components/details-panel/process-details.tsx b/src/views-components/details-panel/process-details.tsx
index c4b374b9..d9c991f5 100644
--- a/src/views-components/details-panel/process-details.tsx
+++ b/src/views-components/details-panel/process-details.tsx
@@ -5,12 +5,8 @@
 import React from 'react';
 import { ProcessIcon } from 'components/icon/icon';
 import { ProcessResource } from 'models/process';
-import { formatDate } from 'common/formatters';
-import { ResourceKind } from 'models/resource';
-import { resourceLabel } from 'common/labels';
 import { DetailsData } from "./details-data";
-import { DetailsAttribute } from "components/details-attribute/details-attribute";
-import { ResourceOwnerWithName } from '../data-explorer/renderers';
+import { ProcessDetailsAttributes } from 'views/process-panel/process-details-attributes';
 
 export class ProcessDetails extends DetailsData<ProcessResource> {
 
@@ -19,25 +15,6 @@ export class ProcessDetails extends DetailsData<ProcessResource> {
     }
 
     getDetails() {
-        return <div>
-            <DetailsAttribute label='Type' value={resourceLabel(ResourceKind.PROCESS)} />
-            <DetailsAttribute label='Owner' linkToUuid={this.item.ownerUuid} value={this.item.ownerUuid}
-                uuidEnhancer={(uuid: string) => <ResourceOwnerWithName uuid={uuid} />} />
-
-            <DetailsAttribute label='Status' value={this.item.state} />
-            <DetailsAttribute label='Last modified' value={formatDate(this.item.modifiedAt)} />
-
-            <DetailsAttribute label='Started at' value={formatDate(this.item.createdAt)} />
-            <DetailsAttribute label='Finished at' value={formatDate(this.item.expiresAt)} />
-
-            <DetailsAttribute label='Outputs' value={this.item.outputPath} />
-            <DetailsAttribute label='UUID' linkToUuid={this.item.uuid} value={this.item.uuid} />
-            <DetailsAttribute label='Container UUID' value={this.item.containerUuid} />
-
-            <DetailsAttribute label='Priority' value={this.item.priority} />
-            <DetailsAttribute label='Runtime Constraints' value={JSON.stringify(this.item.runtimeConstraints)} />
-
-            <DetailsAttribute label='Docker Image locator' linkToUuid={this.item.containerImage} value={this.item.containerImage} />
-        </div>;
+        return <ProcessDetailsAttributes item={this.item} />;
     }
 }
diff --git a/src/views/process-panel/process-details-attributes.tsx b/src/views/process-panel/process-details-attributes.tsx
new file mode 100644
index 00000000..4f26a71f
--- /dev/null
+++ b/src/views/process-panel/process-details-attributes.tsx
@@ -0,0 +1,65 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from "react";
+import { Grid } from "@material-ui/core";
+import { formatDate } from "common/formatters";
+import { resourceLabel } from "common/labels";
+import { DetailsAttribute } from "components/details-attribute/details-attribute";
+import { ProcessResource } from "models/process";
+import { ResourceKind } from "models/resource";
+import { ResourceOwnerWithName } from "views-components/data-explorer/renderers";
+
+type CssRules = 'label' | 'value';
+
+export const ProcessDetailsAttributes = (props: { item: ProcessResource, twoCol?: boolean, classes?: Record<CssRules, string> }) => {
+    const item = props.item;
+    const classes = props.classes || { label: '', value: '', button: '' };
+    const mdSize = props.twoCol ? 6 : 12;
+    return <Grid container>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Type' value={resourceLabel(ResourceKind.PROCESS)} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute classLabel={classes.label} classValue={classes.value}
+                label='Owner' linkToUuid={item.ownerUuid}
+                uuidEnhancer={(uuid: string) => <ResourceOwnerWithName uuid={uuid} />} />
+        </Grid>
+        <Grid item xs={12} md={12}>
+            <DetailsAttribute label='Status' value={item.state} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Last modified' value={formatDate(item.modifiedAt)} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Started at' value={formatDate(item.createdAt)} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Created at' value={formatDate(item.createdAt)} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Finished at' value={formatDate(item.expiresAt)} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Outputs' value={item.outputPath} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='UUID' linkToUuid={item.uuid} value={item.uuid} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Container UUID' value={item.containerUuid} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Priority' value={item.priority} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Runtime Constraints'
+            value={JSON.stringify(item.runtimeConstraints)} />
+        </Grid>
+        <Grid item xs={12} md={mdSize}>
+            <DetailsAttribute label='Docker Image locator'
+            linkToUuid={item.containerImage} value={item.containerImage} />
+        </Grid>
+    </Grid>;
+};
diff --git a/src/views/process-panel/process-details-card.tsx b/src/views/process-panel/process-details-card.tsx
new file mode 100644
index 00000000..18610781
--- /dev/null
+++ b/src/views/process-panel/process-details-card.tsx
@@ -0,0 +1,63 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import React from 'react';
+import {
+    StyleRulesCallback,
+    WithStyles,
+    withStyles,
+    Card,
+    CardHeader,
+    IconButton,
+    CardContent,
+    Tooltip,
+} from '@material-ui/core';
+import { ArvadosTheme } from 'common/custom-theme';
+import { CloseIcon } from 'components/icon/icon';
+import { Process } from 'store/processes/process';
+import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
+import { ProcessDetailsAttributes } from './process-details-attributes';
+
+type CssRules = 'card' | 'content' | 'title';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    card: {
+        height: '100%'
+    },
+    content: {
+        '&:last-child': {
+            paddingBottom: theme.spacing.unit * 2,
+        }
+    },
+    title: {
+        overflow: 'hidden',
+        paddingTop: theme.spacing.unit * 0.5
+    },
+});
+
+export interface ProcessDetailsCardDataProps {
+    process: Process;
+}
+
+type ProcessDetailsCardProps = ProcessDetailsCardDataProps & WithStyles<CssRules> & MPVPanelProps;
+
+export const ProcessDetailsCard = withStyles(styles)(
+    ({ classes, process, doHidePanel, panelName }: ProcessDetailsCardProps) => {
+        return <Card className={classes.card}>
+            <CardHeader
+                classes={{
+                    content: classes.title,
+                }}
+                title='Details'
+                action={ doHidePanel &&
+                        <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
+                            <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
+                        </Tooltip> } />
+            <CardContent className={classes.content}>
+                <ProcessDetailsAttributes item={process.containerRequest} twoCol />
+            </CardContent>
+        </Card>;
+    }
+);
+

commit d6cc3459a7c57c3fef994df30a04ac846deeeeb4
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Tue Oct 19 13:49:14 2021 -0300

    18128: Makes the toggle button bar always visible.
    
    In case there's vertical scrolling involved, the button bar will always
    be accessible.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index e0698750..6778b526 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -11,7 +11,7 @@ import { InfoIcon, InvisibleIcon, VisibleIcon } from 'components/icon/icon';
 import { ReactNodeArray } from 'prop-types';
 import classNames from 'classnames';
 
-type CssRules = 'button' | 'buttonIcon';
+type CssRules = 'button' | 'buttonIcon' | 'content';
 
 const styles: StyleRulesCallback<CssRules> = theme => ({
     button: {
@@ -23,6 +23,9 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
         padding: '2px 0px 2px 5px',
         fontSize: '1rem'
     },
+    content: {
+        overflow: 'auto',
+    },
 });
 
 interface MPVHideablePanelDataProps {
@@ -150,11 +153,13 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
         <Grid container item direction="row">
             { toggles.map(tgl => <Grid item>{tgl}</Grid>) }
         </Grid>
-        { panelVisibility.includes(true)
-            ? panels
-            : <Grid container item alignItems='center' justify='center'>
-                <DefaultView messages={["All panels are hidden.", "Click on the buttons above to show them."]} icon={InfoIcon} />
-            </Grid> }
+        <Grid container item {...props} xs className={classes.content}>
+            { panelVisibility.includes(true)
+                ? panels
+                : <Grid container item alignItems='center' justify='center'>
+                    <DefaultView messages={["All panels are hidden.", "Click on the buttons above to show them."]} icon={InfoIcon} />
+                </Grid> }
+        </Grid>
     </Grid>;
 };
 

commit 61b498513f5bb98980a852425d4912913f0957fe
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Tue Oct 19 11:58:09 2021 -0300

    18128: Improves process panel.
    
    Makes the subprocess panel take the available screen space.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index 05c1de05..e0698750 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -147,12 +147,12 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
     };
 
     return <Grid container {...props}>
-        <Grid item>
-            { toggles }
+        <Grid container item direction="row">
+            { toggles.map(tgl => <Grid item>{tgl}</Grid>) }
         </Grid>
         { panelVisibility.includes(true)
             ? panels
-            : <Grid container alignItems='center' justify='center'>
+            : <Grid container item alignItems='center' justify='center'>
                 <DefaultView messages={["All panels are hidden.", "Click on the buttons above to show them."]} icon={InfoIcon} />
             </Grid> }
     </Grid>;
diff --git a/src/views/all-processes-panel/all-processes-panel.tsx b/src/views/all-processes-panel/all-processes-panel.tsx
index f9fab44d..928b4fff 100644
--- a/src/views/all-processes-panel/all-processes-panel.tsx
+++ b/src/views/all-processes-panel/all-processes-panel.tsx
@@ -33,7 +33,7 @@ import { getInitialProcessStatusFilters, getInitialProcessTypeFilters } from 'st
 import { getProcess } from 'store/processes/process';
 import { ResourcesState } from 'store/resources/resources';
 
-type CssRules = "toolbar" | "button";
+type CssRules = "toolbar" | "button" | "root";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     toolbar: {
@@ -43,6 +43,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     button: {
         marginLeft: theme.spacing.unit
     },
+    root: {
+        width: '100%',
+    }
 });
 
 export enum AllProcessesPanelColumnNames {
@@ -142,18 +145,17 @@ export const AllProcessesPanel = withStyles(styles)(
             }
 
             render() {
-                return <DataExplorer
+                return <div className={this.props.classes.root}><DataExplorer
                     id={ALL_PROCESSES_PANEL_ID}
                     onRowClick={this.handleRowClick}
                     onRowDoubleClick={this.handleRowDoubleClick}
                     onContextMenu={this.handleContextMenu}
                     contextMenuColumn={true}
-                    dataTableDefaultView={
-                        <DataTableDefaultView
-                            icon={ProcessIcon}
-                            messages={['Processes list empty.']}
-                            />
-                    } />;
+                    dataTableDefaultView={ <DataTableDefaultView
+                        icon={ProcessIcon}
+                        messages={['Processes list empty.']}
+                        /> } />
+                </div>
             }
         }
     )
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index e2854bdd..e604fe2b 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import React from 'react';
-import { Grid } from '@material-ui/core';
+import { Grid, StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
 import { ProcessInformationCard } from './process-information-card';
 import { DefaultView } from 'components/default-view/default-view';
 import { ProcessIcon } from 'components/icon/icon';
@@ -11,6 +11,15 @@ import { Process } from 'store/processes/process';
 import { SubprocessPanel } from 'views/subprocess-panel/subprocess-panel';
 import { SubprocessFilterDataProps } from 'components/subprocess-filter/subprocess-filter';
 import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
+import { ArvadosTheme } from 'common/custom-theme';
+
+type CssRules = 'root';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        width: '100%',
+    },
+});
 
 export interface ProcessPanelRootDataProps {
     process?: Process;
@@ -27,17 +36,17 @@ export interface ProcessPanelRootActionProps {
     cancelProcess: (uuid: string) => void;
 }
 
-export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps;
+export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps & WithStyles<CssRules>;
 
 const panelsData: MPVPanelState[] = [
     {name: "Info"},
     {name: "Subprocesses"},
 ];
 
-export const ProcessPanelRoot = ({ process, ...props }: ProcessPanelRootProps) =>
+export const ProcessPanelRoot = withStyles(styles)(({ process, ...props }: ProcessPanelRootProps) =>
     process
-        ? <MPVContainer spacing={8} panelStates={panelsData} alignItems="stretch">
-            <MPVPanelContent sm={12} md={12}>
+        ? <MPVContainer className={props.classes.root} spacing={8} panelStates={panelsData}  justify-content="flex-start" direction="column" wrap="nowrap">
+            <MPVPanelContent xs="auto">
                 <ProcessInformationCard
                     process={process}
                     onContextMenu={event => props.onContextMenu(event, process)}
@@ -47,7 +56,7 @@ export const ProcessPanelRoot = ({ process, ...props }: ProcessPanelRootProps) =
                     cancelProcess={props.cancelProcess}
                 />
             </MPVPanelContent>
-            <MPVPanelContent sm={12} md={12}>
+            <MPVPanelContent xs>
                 <SubprocessPanel />
             </MPVPanelContent>
         </MPVContainer>
@@ -58,5 +67,5 @@ export const ProcessPanelRoot = ({ process, ...props }: ProcessPanelRootProps) =
             <DefaultView
                 icon={ProcessIcon}
                 messages={['Process not found']} />
-        </Grid>;
+        </Grid>);
 

commit a67404ec61615e4905338efd0470e3298f6ba41f
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Tue Oct 19 11:56:07 2021 -0300

    18128: Fixes app's main content layouts.
    
    Moves the height calculation from the DataExplorer/DataTable component combo
    to the outer layers so that DataExplorer can be used as one of many data
    panels inside a view.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx
index 7fce77de..cf14303d 100644
--- a/src/components/data-explorer/data-explorer.tsx
+++ b/src/components/data-explorer/data-explorer.tsx
@@ -15,7 +15,7 @@ import { CloseIcon, MaximizeIcon, MoreOptionsIcon } from 'components/icon/icon';
 import { PaperProps } from '@material-ui/core/Paper';
 import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
 
-type CssRules = 'searchBox' | "toolbar" | "toolbarUnderTitle" | "footer" | "root" | 'moreOptionsButton' | 'title';
+type CssRules = 'searchBox' | "toolbar" | "toolbarUnderTitle" | "footer" | "root" | 'moreOptionsButton' | 'title' | 'dataTable' | 'container';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     searchBox: {
@@ -32,7 +32,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         overflow: 'auto'
     },
     root: {
-        height: '100%'
+        height: '100%',
     },
     moreOptionsButton: {
         padding: 0
@@ -41,7 +41,14 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         paddingLeft: theme.spacing.unit * 3,
         paddingTop: theme.spacing.unit * 3,
         fontSize: '18px'
-    }
+    },
+    dataTable: {
+        height: '100%',
+        overflow: 'auto',
+    },
+    container: {
+        height: '100%',
+    },
 });
 
 interface DataExplorerDataProps<T> {
@@ -101,8 +108,9 @@ export const DataExplorer = withStyles(styles)(
                 doHidePanel, doMaximizePanel, panelName, panelMaximized
             } = this.props;
             return <Paper className={classes.root} {...paperProps} key={paperKey}>
-                {title && <div className={classes.title}>{title}</div>}
-                {(!hideColumnSelector || !hideSearchInput) && <Toolbar className={title ? classes.toolbarUnderTitle : classes.toolbar}>
+                <Grid container direction="column" wrap="nowrap" className={classes.container}>
+                {title && <Grid item xs className={classes.title}>{title}</Grid>}
+                {(!hideColumnSelector || !hideSearchInput) && <Grid item xs><Toolbar className={title ? classes.toolbarUnderTitle : classes.toolbar}>
                     <Grid container justify="space-between" wrap="nowrap" alignItems="center">
                         <div className={classes.searchBox}>
                             {!hideSearchInput && <SearchInput
@@ -123,8 +131,8 @@ export const DataExplorer = withStyles(styles)(
                         <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
                             <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
                         </Tooltip> }
-                </Toolbar>}
-                <DataTable
+                </Toolbar></Grid>}
+                <Grid item xs="auto" className={classes.dataTable}><DataTable
                     columns={this.props.contextMenuColumn ? [...columns, this.contextMenuColumn] : columns}
                     items={items}
                     onRowClick={(_, item: T) => onRowClick(item)}
@@ -136,8 +144,8 @@ export const DataExplorer = withStyles(styles)(
                     working={working}
                     defaultView={dataTableDefaultView}
                     currentItemUuid={currentItemUuid}
-                    currentRoute={paperKey} />
-                <Toolbar className={classes.footer}>
+                    currentRoute={paperKey} /></Grid>
+                <Grid item xs><Toolbar className={classes.footer}>
                     <Grid container justify="flex-end">
                         {fetchMode === DataTableFetchMode.PAGINATED ? <TablePagination
                             count={itemsAvailable}
@@ -154,7 +162,8 @@ export const DataExplorer = withStyles(styles)(
                                 onClick={this.loadMore}
                             >Load more</Button>}
                     </Grid>
-                </Toolbar>
+                </Toolbar></Grid>
+                </Grid>
             </Paper>;
         }
 
diff --git a/src/components/data-table/data-table.tsx b/src/components/data-table/data-table.tsx
index 0c84f642..de52d365 100644
--- a/src/components/data-table/data-table.tsx
+++ b/src/components/data-table/data-table.tsx
@@ -39,13 +39,11 @@ type CssRules = "tableBody" | "root" | "content" | "noItemsInfo" | 'tableCell' |
 
 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
     root: {
-        overflowX: 'auto',
-        overflowY: 'auto',
-        height: 'calc(100vh - 280px)',
+        width: '100%',
     },
     content: {
         display: 'inline-block',
-        width: '100%'
+        width: '100%',
     },
     tableBody: {
         background: theme.palette.background.paper
diff --git a/src/views/api-client-authorization-panel/api-client-authorization-panel-root.tsx b/src/views/api-client-authorization-panel/api-client-authorization-panel-root.tsx
index 703bbec5..8f87cb26 100644
--- a/src/views/api-client-authorization-panel/api-client-authorization-panel-root.tsx
+++ b/src/views/api-client-authorization-panel/api-client-authorization-panel-root.tsx
@@ -4,10 +4,10 @@
 
 import React from 'react';
 import {
-    StyleRulesCallback, WithStyles, withStyles, Card, CardContent, Grid, Tooltip, IconButton
+    StyleRulesCallback, WithStyles, withStyles
 } from '@material-ui/core';
 import { ArvadosTheme } from 'common/custom-theme';
-import { HelpIcon, ShareMeIcon } from 'components/icon/icon';
+import { ShareMeIcon } from 'components/icon/icon';
 import { createTree } from 'models/tree';
 import { DataColumns } from 'components/data-table/data-table';
 import { SortDirection } from 'components/data-table/data-column';
@@ -20,21 +20,11 @@ import {
     TokenLastUsedAt, TokenLastUsedByIpAddress, TokenScopes, TokenUserId
 } from 'views-components/data-explorer/renderers';
 
-type CssRules = 'card' | 'cardContent' | 'helpIconGrid';
+type CssRules = 'root';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
-    card: {
+    root: {
         width: '100%',
-        overflow: 'auto'
-    },
-    cardContent: {
-        padding: 0,
-        '&:last-child': {
-            paddingBottom: 0
-        }
-    },
-    helpIconGrid: {
-        textAlign: 'right'
     }
 });
 
@@ -132,7 +122,6 @@ export interface ApiClientAuthorizationPanelRootActionProps {
     onItemClick: (item: string) => void;
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: string) => void;
     onItemDoubleClick: (item: string) => void;
-    openHelpDialog: () => void;
 }
 
 export interface ApiClientAuthorizationPanelRootDataProps {
@@ -143,33 +132,18 @@ type ApiClientAuthorizationPanelRootProps = ApiClientAuthorizationPanelRootActio
     & ApiClientAuthorizationPanelRootDataProps & WithStyles<CssRules>;
 
 export const ApiClientAuthorizationPanelRoot = withStyles(styles)(
-    ({ classes, onItemDoubleClick, onItemClick, onContextMenu, openHelpDialog }: ApiClientAuthorizationPanelRootProps) =>
-        <Card className={classes.card}>
-            <CardContent className={classes.cardContent}>
-                <Grid container direction="row" justify="flex-end">
-                    <Grid item xs={12} className={classes.helpIconGrid}>
-                        <Tooltip title="Api token - help">
-                            <IconButton onClick={openHelpDialog}>
-                                <HelpIcon />
-                            </IconButton>
-                        </Tooltip>
-                    </Grid>
-                    <Grid item xs={12}>
-                        <DataExplorer
-                            id={API_CLIENT_AUTHORIZATION_PANEL_ID}
-                            onRowClick={onItemClick}
-                            onRowDoubleClick={onItemDoubleClick}
-                            onContextMenu={onContextMenu}
-                            contextMenuColumn={true}
-                            hideColumnSelector
-                            hideSearchInput
-                            dataTableDefaultView={
-                                <DataTableDefaultView
-                                    icon={ShareMeIcon}
-                                    messages={[DEFAULT_MESSAGE]} />
-                            } />
-                    </Grid>
-                </Grid>
-            </CardContent>
-        </Card>
+    ({ classes, onItemDoubleClick, onItemClick, onContextMenu }: ApiClientAuthorizationPanelRootProps) =>
+        <div className={classes.root}><DataExplorer
+            id={API_CLIENT_AUTHORIZATION_PANEL_ID}
+            onRowClick={onItemClick}
+            onRowDoubleClick={onItemDoubleClick}
+            onContextMenu={onContextMenu}
+            contextMenuColumn={true}
+            hideColumnSelector
+            hideSearchInput
+            dataTableDefaultView={
+                <DataTableDefaultView
+                    icon={ShareMeIcon}
+                    messages={[DEFAULT_MESSAGE]} />
+            } /></div>
 );
\ No newline at end of file
diff --git a/src/views/api-client-authorization-panel/api-client-authorization-panel.tsx b/src/views/api-client-authorization-panel/api-client-authorization-panel.tsx
index 89254dcc..9604bf50 100644
--- a/src/views/api-client-authorization-panel/api-client-authorization-panel.tsx
+++ b/src/views/api-client-authorization-panel/api-client-authorization-panel.tsx
@@ -11,7 +11,6 @@ import {
     ApiClientAuthorizationPanelRootActionProps
 } from 'views/api-client-authorization-panel/api-client-authorization-panel-root';
 import { openApiClientAuthorizationContextMenu } from 'store/context-menu/context-menu-actions';
-import { openApiClientAuthorizationsHelpDialog } from 'store/api-client-authorizations/api-client-authorizations-actions';
 
 const mapStateToProps = (state: RootState): ApiClientAuthorizationPanelRootDataProps => {
     return {
@@ -25,9 +24,6 @@ const mapDispatchToProps = (dispatch: Dispatch): ApiClientAuthorizationPanelRoot
     },
     onItemClick: (resourceUuid: string) => { return; },
     onItemDoubleClick: uuid => { return; },
-    openHelpDialog: () => {
-        dispatch<any>(openApiClientAuthorizationsHelpDialog());
-    }
 });
 
 export const ApiClientAuthorizationPanel = connect(mapStateToProps, mapDispatchToProps)(ApiClientAuthorizationPanelRoot);
\ No newline at end of file
diff --git a/src/views/collection-content-address-panel/collection-content-address-panel.tsx b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
index 88638085..f1278049 100644
--- a/src/views/collection-content-address-panel/collection-content-address-panel.tsx
+++ b/src/views/collection-content-address-panel/collection-content-address-panel.tsx
@@ -7,7 +7,6 @@ import {
     StyleRulesCallback,
     WithStyles,
     withStyles,
-    Grid,
     Button
 } from '@material-ui/core';
 import { CollectionIcon } from 'components/icon/icon';
@@ -38,7 +37,7 @@ import { getResource, ResourcesState } from 'store/resources/resources';
 import { RootState } from 'store/store';
 import { CollectionResource } from 'models/collection';
 
-type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
+type CssRules = 'backLink' | 'backIcon' | 'root' | 'content';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     backLink: {
@@ -53,24 +52,13 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     backIcon: {
         marginRight: theme.spacing.unit
     },
-    card: {
-        width: '100%'
+    root: {
+        width: '100%',
     },
-    title: {
-        color: theme.palette.grey["700"]
+    content: {
+        // reserve space for the content address bar
+        height: `calc(100% - ${theme.spacing.unit * 7}px)`,
     },
-    iconHeader: {
-        fontSize: '1.875rem',
-        color: theme.customs.colors.green700
-    },
-    link: {
-        fontSize: '0.875rem',
-        color: theme.palette.primary.main,
-        textAlign: 'right',
-        '&:hover': {
-            cursor: 'pointer'
-        }
-    }
 });
 
 enum CollectionContentAddressPanelColumnNames {
@@ -162,14 +150,14 @@ export const CollectionsContentAddressPanel = withStyles(styles)(
     connect(mapStateToProps, mapDispatchToProps)(
         class extends React.Component<CollectionContentAddressPanelActionProps & CollectionContentAddressPanelDataProps & CollectionContentAddressDataProps & WithStyles<CssRules>> {
             render() {
-                return <Grid item xs={12}>
+                return <div className={this.props.classes.root}>
                     <Button
                         onClick={() => window.history.back()}
                         className={this.props.classes.backLink}>
                         <BackIcon className={this.props.classes.backIcon} />
                         Back
                     </Button>
-                    <DataExplorer
+                    <div className={this.props.classes.content}><DataExplorer
                         id={COLLECTIONS_CONTENT_ADDRESS_PANEL_ID}
                         hideSearchInput
                         onRowClick={this.props.onItemClick}
@@ -181,8 +169,8 @@ export const CollectionsContentAddressPanel = withStyles(styles)(
                             <DataTableDefaultView
                                 icon={CollectionIcon}
                                 messages={['Collections with this content address not found.']} />
-                        } />;
-                    </Grid >;
+                        } /></div>
+                    </div>;
             }
         }
     )
diff --git a/src/views/favorite-panel/favorite-panel.tsx b/src/views/favorite-panel/favorite-panel.tsx
index 404baeb9..0b6532c1 100644
--- a/src/views/favorite-panel/favorite-panel.tsx
+++ b/src/views/favorite-panel/favorite-panel.tsx
@@ -41,7 +41,7 @@ import { getProperty } from 'store/properties/properties';
 import { PROJECT_PANEL_CURRENT_UUID } from 'store/project-panel/project-panel-action';
 import { CollectionResource } from 'models/collection';
 
-type CssRules = "toolbar" | "button";
+type CssRules = "toolbar" | "button" | "root";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     toolbar: {
@@ -51,6 +51,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     button: {
         marginLeft: theme.spacing.unit
     },
+    root: {
+        width: '100%',
+    },
 });
 
 export enum FavoritePanelColumnNames {
@@ -176,7 +179,7 @@ export const FavoritePanel = withStyles(styles)(
             }
 
             render() {
-                return <DataExplorer
+                return <div className={this.props.classes.root}><DataExplorer
                     id={FAVORITE_PANEL_ID}
                     onRowClick={this.handleRowClick}
                     onRowDoubleClick={this.handleRowDoubleClick}
@@ -187,7 +190,7 @@ export const FavoritePanel = withStyles(styles)(
                             icon={FavoriteIcon}
                             messages={['Your favorites list is empty.']}
                             />
-                    } />;
+                    } /></div>;
             }
         }
     )
diff --git a/src/views/groups-panel/groups-panel.tsx b/src/views/groups-panel/groups-panel.tsx
index 4d15118c..faefab10 100644
--- a/src/views/groups-panel/groups-panel.tsx
+++ b/src/views/groups-panel/groups-panel.tsx
@@ -4,7 +4,7 @@
 
 import React from 'react';
 import { connect } from 'react-redux';
-import { Grid, Button, Typography } from "@material-ui/core";
+import { Grid, Button, Typography, StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core";
 import { DataExplorer } from "views-components/data-explorer/data-explorer";
 import { DataColumns } from 'components/data-table/data-table';
 import { SortDirection } from 'components/data-table/data-column';
@@ -22,6 +22,15 @@ import { openContextMenu } from 'store/context-menu/context-menu-actions';
 import { ResourceKind } from 'models/resource';
 import { LinkClass, LinkResource } from 'models/link';
 import { navigateToGroupDetails } from 'store/navigation/navigation-action';
+import { ArvadosTheme } from 'common/custom-theme';
+
+type CssRules = "root";
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        width: '100%',
+    }
+});
 
 export enum GroupsPanelColumnNames {
     GROUP = "Name",
@@ -74,14 +83,14 @@ export interface GroupsPanelProps {
     resources: ResourcesState;
 }
 
-export const GroupsPanel = connect(
+export const GroupsPanel = withStyles(styles)(connect(
     mapStateToProps, mapDispatchToProps
 )(
-    class GroupsPanel extends React.Component<GroupsPanelProps> {
+    class GroupsPanel extends React.Component<GroupsPanelProps & WithStyles<CssRules>> {
 
         render() {
             return (
-                <DataExplorer
+                <div className={this.props.classes.root}><DataExplorer
                     id={GROUPS_PANEL_ID}
                     onRowClick={noop}
                     onRowDoubleClick={this.props.onRowDoubleClick}
@@ -97,7 +106,7 @@ export const GroupsPanel = connect(
                                 <AddIcon /> New group
                         </Button>
                         </Grid>
-                    } />
+                    } /></div>
             );
         }
 
@@ -113,7 +122,7 @@ export const GroupsPanel = connect(
                 });
             }
         }
-    });
+    }));
 
 
 const GroupMembersCount = connect(
diff --git a/src/views/link-panel/link-panel-root.tsx b/src/views/link-panel/link-panel-root.tsx
index 7a5f0503..b32208cd 100644
--- a/src/views/link-panel/link-panel-root.tsx
+++ b/src/views/link-panel/link-panel-root.tsx
@@ -11,10 +11,20 @@ import { DataTableDefaultView } from 'components/data-table-default-view/data-ta
 import { ResourcesState } from 'store/resources/resources';
 import { ShareMeIcon } from 'components/icon/icon';
 import { createTree } from 'models/tree';
-import { 
-    ResourceLinkUuid, ResourceLinkHead, ResourceLinkTail, 
-    ResourceLinkClass, ResourceLinkName } 
+import {
+    ResourceLinkUuid, ResourceLinkHead, ResourceLinkTail,
+    ResourceLinkClass, ResourceLinkName }
 from 'views-components/data-explorer/renderers';
+import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core';
+import { ArvadosTheme } from 'common/custom-theme';
+
+type CssRules = "root";
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+    root: {
+        width: '100%',
+    }
+});
 
 export enum LinkPanelColumnNames {
     NAME = "Name",
@@ -73,20 +83,20 @@ export interface LinkPanelRootActionProps {
     onItemDoubleClick: (item: string) => void;
 }
 
-export type LinkPanelRootProps = LinkPanelRootDataProps & LinkPanelRootActionProps;
+export type LinkPanelRootProps = LinkPanelRootDataProps & LinkPanelRootActionProps & WithStyles<CssRules>;
 
-export const LinkPanelRoot = (props: LinkPanelRootProps) => {
-    return <DataExplorer
+export const LinkPanelRoot = withStyles(styles)((props: LinkPanelRootProps) => {
+    return <div className={props.classes.root}><DataExplorer
         id={LINK_PANEL_ID}
         onRowClick={props.onItemClick}
         onRowDoubleClick={props.onItemDoubleClick}
         onContextMenu={props.onContextMenu}
-        contextMenuColumn={true} 
+        contextMenuColumn={true}
         hideColumnSelector
         hideSearchInput
         dataTableDefaultView={
             <DataTableDefaultView
                 icon={ShareMeIcon}
                 messages={['Your link list is empty.']} />
-        }/>;
-};
\ No newline at end of file
+        }/></div>;
+});
\ No newline at end of file
diff --git a/src/views/project-panel/project-panel.tsx b/src/views/project-panel/project-panel.tsx
index 67264511..97f79517 100644
--- a/src/views/project-panel/project-panel.tsx
+++ b/src/views/project-panel/project-panel.tsx
@@ -51,9 +51,7 @@ type CssRules = 'root' | "button";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     root: {
-        position: 'relative',
         width: '100%',
-        height: '100%'
     },
     button: {
         marginLeft: theme.spacing.unit
diff --git a/src/views/public-favorites-panel/public-favorites-panel.tsx b/src/views/public-favorites-panel/public-favorites-panel.tsx
index ee09654a..b58aa2f0 100644
--- a/src/views/public-favorites-panel/public-favorites-panel.tsx
+++ b/src/views/public-favorites-panel/public-favorites-panel.tsx
@@ -39,7 +39,7 @@ import { getResource, ResourcesState } from 'store/resources/resources';
 import { GroupContentsResource } from 'services/groups-service/groups-service';
 import { CollectionResource } from 'models/collection';
 
-type CssRules = "toolbar" | "button";
+type CssRules = "toolbar" | "button" | "root";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     toolbar: {
@@ -49,6 +49,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     button: {
         marginLeft: theme.spacing.unit
     },
+    root: {
+        width: '100%',
+    },
 });
 
 export enum PublicFavoritePanelColumnNames {
@@ -160,7 +163,7 @@ export const PublicFavoritePanel = withStyles(styles)(
     connect(mapStateToProps, mapDispatchToProps)(
         class extends React.Component<FavoritePanelProps> {
             render() {
-                return <DataExplorer
+                return <div className={this.props.classes.root}><DataExplorer
                     id={PUBLIC_FAVORITE_PANEL_ID}
                     onRowClick={this.props.onItemClick}
                     onRowDoubleClick={this.props.onItemDoubleClick}
@@ -170,7 +173,7 @@ export const PublicFavoritePanel = withStyles(styles)(
                         <DataTableDefaultView
                             icon={PublicFavoriteIcon}
                             messages={['Public favorites list is empty.']} />
-                    } />;
+                    } /></div>;
             }
         }
     )
diff --git a/src/views/shared-with-me-panel/shared-with-me-panel.tsx b/src/views/shared-with-me-panel/shared-with-me-panel.tsx
index eb3127a7..219410c5 100644
--- a/src/views/shared-with-me-panel/shared-with-me-panel.tsx
+++ b/src/views/shared-with-me-panel/shared-with-me-panel.tsx
@@ -20,7 +20,7 @@ import {
 } from 'store/context-menu/context-menu-actions';
 import { GroupContentsResource } from 'services/groups-service/groups-service';
 
-type CssRules = "toolbar" | "button";
+type CssRules = "toolbar" | "button" | "root";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     toolbar: {
@@ -30,6 +30,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     button: {
         marginLeft: theme.spacing.unit
     },
+    root: {
+        width: '100%',
+    },
 });
 
 interface SharedWithMePanelDataProps {
@@ -46,13 +49,13 @@ export const SharedWithMePanel = withStyles(styles)(
     }))(
         class extends React.Component<SharedWithMePanelProps> {
             render() {
-                return <DataExplorer
+                return <div className={this.props.classes.root}><DataExplorer
                     id={SHARED_WITH_ME_PANEL_ID}
                     onRowClick={this.handleRowClick}
                     onRowDoubleClick={this.handleRowDoubleClick}
                     onContextMenu={this.handleContextMenu}
                     contextMenuColumn={false}
-                    dataTableDefaultView={<DataTableDefaultView icon={ShareMeIcon} />} />;
+                    dataTableDefaultView={<DataTableDefaultView icon={ShareMeIcon} />} /></div>;
             }
 
             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
diff --git a/src/views/trash-panel/trash-panel.tsx b/src/views/trash-panel/trash-panel.tsx
index b67b666c..d303c2f7 100644
--- a/src/views/trash-panel/trash-panel.tsx
+++ b/src/views/trash-panel/trash-panel.tsx
@@ -36,7 +36,7 @@ import {
     getTrashPanelTypeFilters
 } from 'store/resource-type-filters/resource-type-filters';
 
-type CssRules = "toolbar" | "button";
+type CssRules = "toolbar" | "button" | "root";
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     toolbar: {
@@ -46,6 +46,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     button: {
         marginLeft: theme.spacing.unit
     },
+    root: {
+        width: '100%',
+    },
 });
 
 export enum TrashPanelColumnNames {
@@ -146,7 +149,7 @@ export const TrashPanel = withStyles(styles)(
     }))(
         class extends React.Component<TrashPanelProps> {
             render() {
-                return <DataExplorer
+                return <div className={this.props.classes.root}><DataExplorer
                     id={TRASH_PANEL_ID}
                     onRowClick={this.handleRowClick}
                     onRowDoubleClick={this.handleRowDoubleClick}
@@ -156,7 +159,7 @@ export const TrashPanel = withStyles(styles)(
                         <DataTableDefaultView
                             icon={TrashIcon}
                             messages={['Your trash list is empty.']}/>
-                    } />;
+                    } /></div>;
             }
 
             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {
diff --git a/src/views/user-panel/user-panel.tsx b/src/views/user-panel/user-panel.tsx
index c86ca519..5fb979a2 100644
--- a/src/views/user-panel/user-panel.tsx
+++ b/src/views/user-panel/user-panel.tsx
@@ -30,7 +30,7 @@ import { ShareMeIcon, AddIcon } from 'components/icon/icon';
 import { USERS_PANEL_ID, openUserCreateDialog } from 'store/users/users-actions';
 import { noop } from 'lodash';
 
-type UserPanelRules = "button";
+type UserPanelRules = "button" | 'root' | 'content';
 
 const styles = withStyles<UserPanelRules>(theme => ({
     button: {
@@ -39,6 +39,13 @@ const styles = withStyles<UserPanelRules>(theme => ({
         textAlign: 'right',
         alignSelf: 'center'
     },
+    root: {
+        width: '100%',
+    },
+    content: {
+        // reserve space for the tab bar
+        height: `calc(100% - ${theme.spacing.unit * 7}px)`,
+    }
 }));
 
 export enum UserPanelColumnNames {
@@ -149,13 +156,13 @@ export const UserPanel = compose(
 
             render() {
                 const { value } = this.state;
-                return <Paper>
+                return <Paper className={this.props.classes.root}>
                     <Tabs value={value} onChange={this.handleChange} fullWidth>
                         <Tab label="USERS" />
                         <Tab label="ACTIVITY" disabled />
                     </Tabs>
                     {value === 0 &&
-                        <span>
+                        <div className={this.props.classes.content}>
                             <DataExplorer
                                 id={USERS_PANEL_ID}
                                 onRowClick={noop}
@@ -178,7 +185,7 @@ export const UserPanel = compose(
                                         icon={ShareMeIcon}
                                         messages={['Your user list is empty.']} />
                                 } />
-                        </span>}
+                        </div>}
                 </Paper>;
             }
 
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 9ce93bf2..1c6bf03f 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -129,6 +129,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         minWidth: 0,
         paddingLeft: theme.spacing.unit * 3,
         paddingRight: theme.spacing.unit * 3,
+        // Reserve vertical space for app bar + MainContentBar
+        minHeight: `calc(100vh - ${theme.spacing.unit * 16}px)`,
+        display: 'flex',
     }
 });
 

commit a3c4448560dbf77554fa87c4bba4dc2f680395bb
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu Oct 14 18:04:09 2021 -0300

    18128: Adds panel maximizing capabilities.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/data-explorer/data-explorer.tsx b/src/components/data-explorer/data-explorer.tsx
index 940dbd0a..7fce77de 100644
--- a/src/components/data-explorer/data-explorer.tsx
+++ b/src/components/data-explorer/data-explorer.tsx
@@ -11,8 +11,9 @@ import { SearchInput } from 'components/search-input/search-input';
 import { ArvadosTheme } from "common/custom-theme";
 import { createTree } from 'models/tree';
 import { DataTableFilters } from 'components/data-table-filters/data-table-filters-tree';
-import { CloseIcon, MoreOptionsIcon } from 'components/icon/icon';
+import { CloseIcon, MaximizeIcon, MoreOptionsIcon } from 'components/icon/icon';
 import { PaperProps } from '@material-ui/core/Paper';
+import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
 
 type CssRules = 'searchBox' | "toolbar" | "toolbarUnderTitle" | "footer" | "root" | 'moreOptionsButton' | 'title';
 
@@ -21,7 +22,8 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         paddingBottom: theme.spacing.unit * 2
     },
     toolbar: {
-        paddingTop: theme.spacing.unit * 2
+        paddingTop: theme.spacing.unit,
+        paddingRight: theme.spacing.unit * 2,
     },
     toolbarUnderTitle: {
         paddingTop: 0
@@ -62,7 +64,6 @@ interface DataExplorerDataProps<T> {
     title?: React.ReactNode;
     paperKey?: string;
     currentItemUuid: string;
-    panelName?: string
 }
 
 interface DataExplorerActionProps<T> {
@@ -78,10 +79,10 @@ interface DataExplorerActionProps<T> {
     onChangeRowsPerPage: (rowsPerPage: number) => void;
     onLoadMore: (page: number) => void;
     extractKey?: (item: T) => React.Key;
-    doHidePanel?: () => void;
 }
 
-type DataExplorerProps<T> = DataExplorerDataProps<T> & DataExplorerActionProps<T> & WithStyles<CssRules>;
+type DataExplorerProps<T> = DataExplorerDataProps<T> &
+    DataExplorerActionProps<T> & WithStyles<CssRules> & MPVPanelProps;
 
 export const DataExplorer = withStyles(styles)(
     class DataExplorerGeneric<T> extends React.Component<DataExplorerProps<T>> {
@@ -96,7 +97,8 @@ export const DataExplorer = withStyles(styles)(
                 rowsPerPage, rowsPerPageOptions, onColumnToggle, searchLabel, searchValue, onSearch,
                 items, itemsAvailable, onRowClick, onRowDoubleClick, classes,
                 dataTableDefaultView, hideColumnSelector, actions, paperProps, hideSearchInput,
-                paperKey, fetchMode, currentItemUuid, title, doHidePanel, panelName
+                paperKey, fetchMode, currentItemUuid, title,
+                doHidePanel, doMaximizePanel, panelName, panelMaximized
             } = this.props;
             return <Paper className={classes.root} {...paperProps} key={paperKey}>
                 {title && <div className={classes.title}>{title}</div>}
@@ -113,9 +115,13 @@ export const DataExplorer = withStyles(styles)(
                             columns={columns}
                             onColumnToggle={onColumnToggle} />}
                     </Grid>
+                    { doMaximizePanel && !!!panelMaximized &&
+                        <Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
+                            <IconButton onClick={doMaximizePanel}><MaximizeIcon /></IconButton>
+                        </Tooltip> }
                     { doHidePanel &&
                         <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
-                            <Button onClick={doHidePanel}><CloseIcon /></Button>
+                            <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
                         </Tooltip> }
                 </Toolbar>}
                 <DataTable
diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx
index 94374c62..523eefbd 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -67,6 +67,7 @@ import LinkOutlined from '@material-ui/icons/LinkOutlined';
 // Import FontAwesome icons
 import { library } from '@fortawesome/fontawesome-svg-core';
 import { faPencilAlt, faSlash } from '@fortawesome/free-solid-svg-icons';
+import { CropFreeSharp } from '@material-ui/icons';
 library.add(
     faPencilAlt,
     faSlash,
@@ -119,6 +120,7 @@ export const InputIcon: IconType = (props) => <InsertDriveFile {...props} />;
 export const KeyIcon: IconType = (props) => <VpnKey {...props} />;
 export const LogIcon: IconType = (props) => <SettingsEthernet {...props} />;
 export const MailIcon: IconType = (props) => <Mail {...props} />;
+export const MaximizeIcon: IconType = (props) => <CropFreeSharp {...props} />;
 export const MoreOptionsIcon: IconType = (props) => <MoreVert {...props} />;
 export const MoveToIcon: IconType = (props) => <Input {...props} />;
 export const NewProjectIcon: IconType = (props) => <CreateNewFolder {...props} />;
diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index fd192d13..05c1de05 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -28,28 +28,32 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
 interface MPVHideablePanelDataProps {
     name: string;
     visible: boolean;
+    maximized: boolean;
     children: ReactNode;
 }
 
 interface MPVHideablePanelActionProps {
     doHidePanel: () => void;
+    doMaximizePanel: () => void;
 }
 
 type MPVHideablePanelProps = MPVHideablePanelDataProps & MPVHideablePanelActionProps;
 
-const MPVHideablePanel = ({doHidePanel, name, visible, ...props}: MPVHideablePanelProps) =>
+const MPVHideablePanel = ({doHidePanel, doMaximizePanel, name, visible, maximized, ...props}: MPVHideablePanelProps) =>
     visible
     ? <>
-        {React.cloneElement((props.children as ReactElement), { doHidePanel, panelName: name })}
+        {React.cloneElement((props.children as ReactElement), { doHidePanel, doMaximizePanel,panelName: name, panelMaximized: maximized })}
     </>
     : null;
 
 interface MPVPanelDataProps {
     panelName?: string;
+    panelMaximized?: boolean;
 }
 
 interface MPVPanelActionProps {
     doHidePanel?: () => void;
+    doMaximizePanel?: () => void;
 }
 
 // Props received by panel implementors
@@ -58,9 +62,9 @@ export type MPVPanelProps = MPVPanelDataProps & MPVPanelActionProps;
 type MPVPanelContentProps = {children: ReactElement} & MPVPanelProps & GridProps;
 
 // Grid item compatible component for layout and MPV props passing
-export const MPVPanelContent = ({doHidePanel, panelName, ...props}: MPVPanelContentProps) =>
+export const MPVPanelContent = ({doHidePanel, doMaximizePanel, panelName, panelMaximized, ...props}: MPVPanelContentProps) =>
     <Grid item {...props}>
-        {React.cloneElement(props.children, { doHidePanel, panelName })}
+        {React.cloneElement(props.children, { doHidePanel, doMaximizePanel, panelName, panelMaximized })}
     </Grid>;
 
 export interface MPVPanelState {
@@ -97,6 +101,14 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
                     ...panelVisibility.slice(idx+1)
                 ])
             };
+            const maximizeFn = (idx: number) => () => {
+                // Maximize X == hide all but X
+                setPanelVisibility([
+                    ...panelVisibility.slice(0, idx).map(() => false),
+                    true,
+                    ...panelVisibility.slice(idx+1).map(() => false),
+                ])
+            };
             const toggleIcon = panelVisibility[idx]
                 ? <VisibleIcon className={classNames(classes.buttonIcon)} />
                 : <InvisibleIcon className={classNames(classes.buttonIcon)}/>
@@ -109,6 +121,8 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
             const toggleTooltip = panelVisibility[idx]
                 ? `Hide ${panelName} panel`
                 : `Show ${panelName} panel`;
+            const panelIsMaximized = panelVisibility[idx] &&
+                panelVisibility.filter(e => e).length === 1;
 
             toggles = [
                 ...toggles,
@@ -123,7 +137,9 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
             ];
 
             const aPanel =
-                <MPVHideablePanel visible={panelVisibility[idx]} name={panelName} doHidePanel={toggleFn(idx)}>
+                <MPVHideablePanel visible={panelVisibility[idx]} name={panelName}
+                    maximized={panelIsMaximized}
+                    doHidePanel={toggleFn(idx)} doMaximizePanel={maximizeFn(idx)}>
                     {children[idx]}
                 </MPVHideablePanel>;
             panels = [...panels, aPanel];
diff --git a/src/views/process-panel/process-information-card.tsx b/src/views/process-panel/process-information-card.tsx
index 8be7d1cc..4c938017 100644
--- a/src/views/process-panel/process-information-card.tsx
+++ b/src/views/process-panel/process-information-card.tsx
@@ -5,7 +5,7 @@
 import React from 'react';
 import {
     StyleRulesCallback, WithStyles, withStyles, Card,
-    CardHeader, IconButton, CardContent, Grid, Chip, Typography, Tooltip, Button
+    CardHeader, IconButton, CardContent, Grid, Chip, Typography, Tooltip
 } from '@material-ui/core';
 import { ArvadosTheme } from 'common/custom-theme';
 import { CloseIcon, MoreOptionsIcon, ProcessIcon } from 'components/icon/icon';
@@ -114,7 +114,7 @@ export const ProcessInformationCard = withStyles(styles, { withTheme: true })(
                         </Tooltip>
                         { doHidePanel &&
                         <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
-                            <Button onClick={doHidePanel}><CloseIcon /></Button>
+                            <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
                         </Tooltip> }
                     </div>
                 }
diff --git a/src/views/subprocess-panel/subprocess-panel-root.tsx b/src/views/subprocess-panel/subprocess-panel-root.tsx
index 49354671..41a8f66b 100644
--- a/src/views/subprocess-panel/subprocess-panel-root.tsx
+++ b/src/views/subprocess-panel/subprocess-panel-root.tsx
@@ -94,5 +94,7 @@ export const SubprocessPanelRoot = (props: SubprocessPanelProps & MPVPanelProps)
                 messages={DEFAULT_VIEW_MESSAGES} />
         }
         doHidePanel={props.doHidePanel}
+        doMaximizePanel={props.doMaximizePanel}
+        panelMaximized={props.panelMaximized}
         panelName={props.panelName} />;
 };
\ No newline at end of file

commit 61428cc9cf2e4b9001a534b65f56cb856ff58afe
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu Oct 14 16:16:18 2021 -0300

    18128: Adds ability to set up initial per-panel visibility.
    
    If panelStates isn't defined, the default keeps being "show all panels".
    If panelStates is defined, but some panel state isn't included, the default
    is to set its initial visibility to false. If it is indeed included but
    the optional visibility setting isn't defined, by default the panel will
    be shown to avoid having to explicitly set visible:true to all panels.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index 5c16096b..fd192d13 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -63,20 +63,26 @@ export const MPVPanelContent = ({doHidePanel, panelName, ...props}: MPVPanelCont
         {React.cloneElement(props.children, { doHidePanel, panelName })}
     </Grid>;
 
-export interface MPVContainerDataProps {
-    panelNames?: string[];
+export interface MPVPanelState {
+    name: string;
+    visible?: boolean;
+}
+interface MPVContainerDataProps {
+    panelStates?: MPVPanelState[];
 }
-
 type MPVContainerProps = MPVContainerDataProps & GridProps;
 
 // Grid container compatible component that also handles panel toggling.
-const MPVContainerComponent = ({children, panelNames, classes, ...props}: MPVContainerProps & WithStyles<CssRules>) => {
+const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVContainerProps & WithStyles<CssRules>) => {
     if (children === undefined || children === null || children === {}) {
         children = [];
     } else if (!isArray(children)) {
         children = [children];
     }
-    const visibility = (children as ReactNodeArray).map(() => true);
+    const visibility = (children as ReactNodeArray).map((_, idx) =>
+        !!!panelStates || // if panelStates wasn't passed, default to all visible panels
+            (panelStates[idx] &&
+                (panelStates[idx].visible || panelStates[idx].visible === undefined)));
     const [panelVisibility, setPanelVisibility] = useState<boolean[]>(visibility);
 
     let panels: JSX.Element[] = [];
@@ -94,9 +100,9 @@ const MPVContainerComponent = ({children, panelNames, classes, ...props}: MPVCon
             const toggleIcon = panelVisibility[idx]
                 ? <VisibleIcon className={classNames(classes.buttonIcon)} />
                 : <InvisibleIcon className={classNames(classes.buttonIcon)}/>
-            const panelName = panelNames === undefined
+            const panelName = panelStates === undefined
                 ? `Panel ${idx+1}`
-                : panelNames[idx] || `Panel ${idx+1}`;
+                : (panelStates[idx] && panelStates[idx].name) || `Panel ${idx+1}`;
             const toggleVariant = panelVisibility[idx]
                 ? "raised"
                 : "flat";
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index 045e5cfa..e2854bdd 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -10,7 +10,7 @@ import { ProcessIcon } from 'components/icon/icon';
 import { Process } from 'store/processes/process';
 import { SubprocessPanel } from 'views/subprocess-panel/subprocess-panel';
 import { SubprocessFilterDataProps } from 'components/subprocess-filter/subprocess-filter';
-import { MPVContainer, MPVPanelContent } from 'components/multi-panel-view/multi-panel-view';
+import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
 
 export interface ProcessPanelRootDataProps {
     process?: Process;
@@ -29,9 +29,14 @@ export interface ProcessPanelRootActionProps {
 
 export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps;
 
+const panelsData: MPVPanelState[] = [
+    {name: "Info"},
+    {name: "Subprocesses"},
+];
+
 export const ProcessPanelRoot = ({ process, ...props }: ProcessPanelRootProps) =>
     process
-        ? <MPVContainer spacing={8} panelNames={["Info", "Subprocesses"]} alignItems="stretch">
+        ? <MPVContainer spacing={8} panelStates={panelsData} alignItems="stretch">
             <MPVPanelContent sm={12} md={12}>
                 <ProcessInformationCard
                     process={process}

commit d107ed0f6fa401e1cfe180ca20e2c76233f65ac2
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu Oct 14 15:28:40 2021 -0300

    18128: Exports a common type for panel implementors to receive MPV props.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index 8b38e6c6..5c16096b 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -35,25 +35,27 @@ interface MPVHideablePanelActionProps {
     doHidePanel: () => void;
 }
 
-type MPVPanelProps = MPVHideablePanelDataProps & MPVHideablePanelActionProps;
+type MPVHideablePanelProps = MPVHideablePanelDataProps & MPVHideablePanelActionProps;
 
-const MPVHideablePanel = ({doHidePanel, name, visible, ...props}: MPVPanelProps) =>
+const MPVHideablePanel = ({doHidePanel, name, visible, ...props}: MPVHideablePanelProps) =>
     visible
     ? <>
         {React.cloneElement((props.children as ReactElement), { doHidePanel, panelName: name })}
     </>
     : null;
 
-interface MPVPanelContentDataProps {
+interface MPVPanelDataProps {
     panelName?: string;
-    children: ReactElement;
 }
 
-interface MPVPanelContentActionProps {
+interface MPVPanelActionProps {
     doHidePanel?: () => void;
 }
 
-type MPVPanelContentProps = MPVPanelContentDataProps & MPVPanelContentActionProps & GridProps;
+// Props received by panel implementors
+export type MPVPanelProps = MPVPanelDataProps & MPVPanelActionProps;
+
+type MPVPanelContentProps = {children: ReactElement} & MPVPanelProps & GridProps;
 
 // Grid item compatible component for layout and MPV props passing
 export const MPVPanelContent = ({doHidePanel, panelName, ...props}: MPVPanelContentProps) =>
diff --git a/src/views/process-panel/process-information-card.tsx b/src/views/process-panel/process-information-card.tsx
index ad7673af..8be7d1cc 100644
--- a/src/views/process-panel/process-information-card.tsx
+++ b/src/views/process-panel/process-information-card.tsx
@@ -15,6 +15,7 @@ import { getProcessStatus, getProcessStatusColor } from 'store/processes/process
 import { formatDate } from 'common/formatters';
 import classNames from 'classnames';
 import { ContainerState } from 'models/container';
+import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
 
 type CssRules = 'card' | 'iconHeader' | 'label' | 'value' | 'chip' | 'link' | 'content' | 'title' | 'avatar' | 'cancelButton';
 
@@ -81,11 +82,9 @@ export interface ProcessInformationCardDataProps {
     navigateToOutput: (uuid: string) => void;
     openWorkflow: (uuid: string) => void;
     cancelProcess: (uuid: string) => void;
-    doHidePanel?: () => void;
-    panelName?: string;
 }
 
-type ProcessInformationCardProps = ProcessInformationCardDataProps & WithStyles<CssRules, true>;
+type ProcessInformationCardProps = ProcessInformationCardDataProps & WithStyles<CssRules, true> & MPVPanelProps;
 
 export const ProcessInformationCard = withStyles(styles, { withTheme: true })(
     ({ classes, process, onContextMenu, theme, openProcessInputDialog, navigateToOutput, openWorkflow, cancelProcess, doHidePanel, panelName }: ProcessInformationCardProps) => {
diff --git a/src/views/subprocess-panel/subprocess-panel-root.tsx b/src/views/subprocess-panel/subprocess-panel-root.tsx
index e35a5712..49354671 100644
--- a/src/views/subprocess-panel/subprocess-panel-root.tsx
+++ b/src/views/subprocess-panel/subprocess-panel-root.tsx
@@ -17,6 +17,7 @@ import { DataTableDefaultView } from 'components/data-table-default-view/data-ta
 import { createTree } from 'models/tree';
 import { getInitialProcessStatusFilters } from 'store/resource-type-filters/resource-type-filters';
 import { ResourcesState } from 'store/resources/resources';
+import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
 
 export enum SubprocessPanelColumnNames {
     NAME = "Name",
@@ -65,14 +66,12 @@ export const subprocessPanelColumns: DataColumns<string> = [
 
 export interface SubprocessPanelDataProps {
     resources: ResourcesState;
-    panelName?: string;
 }
 
 export interface SubprocessPanelActionProps {
     onItemClick: (item: string) => void;
     onContextMenu: (event: React.MouseEvent<HTMLElement>, item: string, resources: ResourcesState) => void;
     onItemDoubleClick: (item: string) => void;
-    doHidePanel?: () => void;
 }
 
 type SubprocessPanelProps = SubprocessPanelActionProps & SubprocessPanelDataProps;
@@ -82,7 +81,7 @@ const DEFAULT_VIEW_MESSAGES = [
     'The current process may not have any or none matches current filtering.'
 ];
 
-export const SubprocessPanelRoot = (props: SubprocessPanelProps) => {
+export const SubprocessPanelRoot = (props: SubprocessPanelProps & MPVPanelProps) => {
     return <DataExplorer
         id={SUBPROCESS_PANEL_ID}
         onRowClick={props.onItemClick}

commit 410cdd36823f2ce733d45451c2cf1c56af1f4c33
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu Oct 14 14:54:28 2021 -0300

    18128: Improves toggle button bar's alignment & separation.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index ff1680b0..8b38e6c6 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -16,7 +16,7 @@ type CssRules = 'button' | 'buttonIcon';
 const styles: StyleRulesCallback<CssRules> = theme => ({
     button: {
         padding: '2px 5px',
-        marginRight: '2px',
+        marginRight: '5px',
     },
     buttonIcon: {
         boxShadow: 'none',
@@ -123,7 +123,9 @@ const MPVContainerComponent = ({children, panelNames, classes, ...props}: MPVCon
     };
 
     return <Grid container {...props}>
-        { toggles }
+        <Grid item>
+            { toggles }
+        </Grid>
         { panelVisibility.includes(true)
             ? panels
             : <Grid container alignItems='center' justify='center'>

commit 8e1c3506127bfcc1ec96df1cf87924b86c16cbe9
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Thu Oct 14 11:49:29 2021 -0300

    18128: Adds better styling to the toggle bar.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx
index 05b94f7e..94374c62 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -59,6 +59,8 @@ import SettingsEthernet from '@material-ui/icons/SettingsEthernet';
 import Star from '@material-ui/icons/Star';
 import StarBorder from '@material-ui/icons/StarBorder';
 import Warning from '@material-ui/icons/Warning';
+import Visibility from '@material-ui/icons/Visibility';
+import VisibilityOff from '@material-ui/icons/VisibilityOff';
 import VpnKey from '@material-ui/icons/VpnKey';
 import LinkOutlined from '@material-ui/icons/LinkOutlined';
 
@@ -145,6 +147,8 @@ export const SidePanelRightArrowIcon: IconType = (props) => <PlayArrow {...props
 export const TrashIcon: IconType = (props) => <Delete {...props} />;
 export const UserPanelIcon: IconType = (props) => <Person {...props} />;
 export const UsedByIcon: IconType = (props) => <Folder {...props} />;
+export const VisibleIcon: IconType = (props) => <Visibility {...props} />;
+export const InvisibleIcon: IconType = (props) => <VisibilityOff {...props} />;
 export const WorkflowIcon: IconType = (props) => <Code {...props} />;
 export const WarningIcon: IconType = (props) => <Warning style={{ color: '#fbc02d', height: '30px', width: '30px' }} {...props} />;
 export const Link: IconType = (props) => <LinkOutlined {...props} />;
diff --git a/src/components/multi-panel-view/multi-panel-view.tsx b/src/components/multi-panel-view/multi-panel-view.tsx
index 54bea41d..ff1680b0 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -3,12 +3,27 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import React, { ReactElement, ReactNode, useState } from 'react';
-import { Button, Grid } from "@material-ui/core";
+import { Button, Grid, StyleRulesCallback, Tooltip, withStyles, WithStyles } from "@material-ui/core";
 import { GridProps } from '@material-ui/core/Grid';
 import { isArray } from 'lodash';
 import { DefaultView } from 'components/default-view/default-view';
-import { InfoIcon } from 'components/icon/icon';
+import { InfoIcon, InvisibleIcon, VisibleIcon } from 'components/icon/icon';
 import { ReactNodeArray } from 'prop-types';
+import classNames from 'classnames';
+
+type CssRules = 'button' | 'buttonIcon';
+
+const styles: StyleRulesCallback<CssRules> = theme => ({
+    button: {
+        padding: '2px 5px',
+        marginRight: '2px',
+    },
+    buttonIcon: {
+        boxShadow: 'none',
+        padding: '2px 0px 2px 5px',
+        fontSize: '1rem'
+    },
+});
 
 interface MPVHideablePanelDataProps {
     name: string;
@@ -53,7 +68,7 @@ export interface MPVContainerDataProps {
 type MPVContainerProps = MPVContainerDataProps & GridProps;
 
 // Grid container compatible component that also handles panel toggling.
-export const MPVContainer = ({children, panelNames, ...props}: MPVContainerProps) => {
+const MPVContainerComponent = ({children, panelNames, classes, ...props}: MPVContainerProps & WithStyles<CssRules>) => {
     if (children === undefined || children === null || children === {}) {
         children = [];
     } else if (!isArray(children)) {
@@ -74,15 +89,29 @@ export const MPVContainer = ({children, panelNames, ...props}: MPVContainerProps
                     ...panelVisibility.slice(idx+1)
                 ])
             };
-            const toggleLabel = panelVisibility[idx] ? 'Hide' : 'Show'
+            const toggleIcon = panelVisibility[idx]
+                ? <VisibleIcon className={classNames(classes.buttonIcon)} />
+                : <InvisibleIcon className={classNames(classes.buttonIcon)}/>
             const panelName = panelNames === undefined
                 ? `Panel ${idx+1}`
                 : panelNames[idx] || `Panel ${idx+1}`;
-
+            const toggleVariant = panelVisibility[idx]
+                ? "raised"
+                : "flat";
+            const toggleTooltip = panelVisibility[idx]
+                ? `Hide ${panelName} panel`
+                : `Show ${panelName} panel`;
 
             toggles = [
                 ...toggles,
-                <Button onClick={toggleFn(idx)}>{toggleLabel} {panelName}</Button>
+                <Tooltip title={toggleTooltip} disableFocusListener>
+                    <Button variant={toggleVariant} size="small" color="primary"
+                        className={classNames(classes.button)}
+                        onClick={toggleFn(idx)}>
+                            {panelName}
+                            {toggleIcon}
+                    </Button>
+                </Tooltip>
             ];
 
             const aPanel =
@@ -102,3 +131,5 @@ export const MPVContainer = ({children, panelNames, ...props}: MPVContainerProps
             </Grid> }
     </Grid>;
 };
+
+export const MPVContainer = withStyles(styles)(MPVContainerComponent);
\ No newline at end of file

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list