[arvados-workbench2] updated: 2.4.0-206-g7869d0e6

git repository hosting git at public.arvados.org
Thu Sep 15 18:08:23 UTC 2022


Summary of changes:
 cypress/integration/process.spec.js                | 117 +++++-----
 src/components/icon/icon.tsx                       |  10 +-
 .../generic-projects-tree-picker.tsx               |   4 +-
 src/views/process-panel/process-io-card.tsx        | 238 ++++++++++++++++-----
 src/views/process-panel/process-panel-root.tsx     |  12 +-
 5 files changed, 265 insertions(+), 116 deletions(-)

       via  7869d0e6e67f4c6d376f8c0ff4bb3123cabe7ed7 (commit)
       via  4a1dcd7be586c39cf4bc1649990e41f6e79c0f4a (commit)
       via  d9d9a67f9b269bcb9cf597c92c4e999ca308d36e (commit)
       via  5eea75dd1622bee999bf0f0a6a98a5d98913f752 (commit)
       via  6df149b9bebe91e92de4c396f60127ee5b5b4de1 (commit)
       via  e961f9c6546d4e201396fe2593b5d7a40f93cdc5 (commit)
       via  14a44a526eb847da76be872892f15b6b2248bc26 (commit)
       via  b70b5d61574f80e8b81f48b6e2a9e2ff6df489d8 (commit)
       via  4f88234088269faa064da9459376c3af2a740a7d (commit)
       via  1bc5df05a276ee007908435a7067764c4d6cf357 (commit)
      from  ee347878e1ad3b6f82787ac27b6d3b6510401ecd (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 7869d0e6e67f4c6d376f8c0ff4bb3123cabe7ed7
Author: Stephen Smith <stephen at curii.com>
Date:   Thu Sep 15 14:08:08 2022 -0400

    16073: Update process io panel cypress tests
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/cypress/integration/process.spec.js b/cypress/integration/process.spec.js
index c119997d..7884b988 100644
--- a/cypress/integration/process.spec.js
+++ b/cypress/integration/process.spec.js
@@ -747,16 +747,36 @@ describe('Process tests', function() {
         }
     ];
 
-    const verifyIOParameter = (name, doc, val) => {
-        cy.get('table tr').contains(name).parents('tr').within(() => {
+    const verifyIOParameter = (name, label, doc, val, collection, multipleRows) => {
+        cy.get('table tr').contains(name).parents('tr').within(($mainRow) => {
             doc && cy.contains(doc);
-            if (val) {
-                if (Array.isArray(val)) {
-                    val.forEach(v => cy.contains(v));
-                } else {
-                    cy.contains(val);
+
+            if (multipleRows) {
+                cy.get($mainRow).nextUntil('[data-cy="process-io-param"]').as('secondaryRows');
+                if (val) {
+                    if (Array.isArray(val)) {
+                        val.forEach(v => cy.get('@secondaryRows').contains(v));
+                    } else {
+                        cy.get('@secondaryRows').contains(val);
+                    }
+                }
+                if (collection) {
+                    cy.get('@secondaryRows').contains(collection);
+                }
+            } else {
+                if (val) {
+                    if (Array.isArray(val)) {
+                        val.forEach(v => cy.contains(v));
+                    } else {
+                        cy.contains(val);
+                    }
+                }
+                if (collection) {
+                    cy.contains(collection);
                 }
             }
+
+
         });
     };
 
@@ -851,54 +871,55 @@ describe('Process tests', function() {
                 .parents('[data-cy=process-io-card]').within(() => {
                     cy.wait(2000);
                     cy.waitForDom();
-                    verifyIOParameter('input_file', "Label Description", 'keep:00000000000000000000000000000000+01/input1.tar');
-                    verifyIOParameter('input_file', "Label Description", 'keep:00000000000000000000000000000000+01/input1-2.txt');
-                    verifyIOParameter('input_file', "Label Description", 'keep:00000000000000000000000000000000+01/input1-3.txt');
-                    verifyIOParameter('input_file', "Label Description", 'keep:00000000000000000000000000000000+01/input1-4.txt');
-                    verifyIOParameter('input_dir', "Doc Description", 'keep:11111111111111111111111111111111+01/');
-                    verifyIOParameter('input_bool', "Doc desc 1, Doc desc 2", 'true');
-                    verifyIOParameter('input_int', null, '1');
-                    verifyIOParameter('input_long', null, '1');
-                    verifyIOParameter('input_float', null, '1.5');
-                    verifyIOParameter('input_double', null, '1.3');
-                    verifyIOParameter('input_string', null, 'Hello World');
-                    verifyIOParameter('input_file_array', null, 'keep:00000000000000000000000000000000+02/input2.tar');
-                    verifyIOParameter('input_file_array', null, 'keep:00000000000000000000000000000000+03/input3.tar');
-                    verifyIOParameter('input_file_array', null, 'keep:00000000000000000000000000000000+03/input3-2.txt');
-                    verifyIOParameter('input_dir_array', null, 'keep:11111111111111111111111111111111+02/');
-                    verifyIOParameter('input_dir_array', null, 'keep:11111111111111111111111111111111+03/');
-                    verifyIOParameter('input_int_array', null, ["1", "3", "5"]);
-                    verifyIOParameter('input_long_array', null, ["10", "20"]);
-                    verifyIOParameter('input_float_array', null, ["10.2", "10.4", "10.6"]);
-                    verifyIOParameter('input_double_array', null, ["20.1", "20.2", "20.3"]);
-                    verifyIOParameter('input_string_array', null, ["Hello", "World", "!"]);
+                    verifyIOParameter('input_file', null, "Label Description", 'input1.tar', 'keep:00000000000000000000000000000000+01');
+                    verifyIOParameter('input_file', null, "Label Description", 'input1-2.txt', 'keep:00000000000000000000000000000000+01', true);
+                    verifyIOParameter('input_file', null, "Label Description", 'input1-3.txt', 'keep:00000000000000000000000000000000+01', true);
+                    verifyIOParameter('input_file', null, "Label Description", 'input1-4.txt', 'keep:00000000000000000000000000000000+01', true);
+                    verifyIOParameter('input_dir', null, "Doc Description", 'No value', 'keep:11111111111111111111111111111111+01');
+                    verifyIOParameter('input_bool', null, "Doc desc 1, Doc desc 2", 'true');
+                    verifyIOParameter('input_int', null, null, '1');
+                    verifyIOParameter('input_long', null, null, '1');
+                    verifyIOParameter('input_float', null, null, '1.5');
+                    verifyIOParameter('input_double', null, null, '1.3');
+                    verifyIOParameter('input_string', null, null, 'Hello World');
+                    verifyIOParameter('input_file_array', null, null, 'input2.tar', 'keep:00000000000000000000000000000000+02');
+                    verifyIOParameter('input_file_array', null, null, 'input3.tar', 'keep:00000000000000000000000000000000+03', true);
+                    verifyIOParameter('input_file_array', null, null, 'input3-2.txt', 'keep:00000000000000000000000000000000+03', true);
+                    verifyIOParameter('input_dir_array', null, null, 'No value', 'keep:11111111111111111111111111111111+02');
+                    verifyIOParameter('input_dir_array', null, null, 'No value', 'keep:11111111111111111111111111111111+03', true);
+                    verifyIOParameter('input_int_array', null, null, ["1", "3", "5"]);
+                    verifyIOParameter('input_long_array', null, null, ["10", "20"]);
+                    verifyIOParameter('input_float_array', null, null, ["10.2", "10.4", "10.6"]);
+                    verifyIOParameter('input_double_array', null, null, ["20.1", "20.2", "20.3"]);
+                    verifyIOParameter('input_string_array', null, null, ["Hello", "World", "!"]);
                 });
             cy.get('[data-cy=process-io-card] h6').contains('Outputs')
                 .parents('[data-cy=process-io-card]').within((ctx) => {
                     cy.get(ctx).scrollIntoView();
+                    cy.get('[data-cy="io-preview-image-toggle"]').click();
                     const outPdh = testOutputCollection.portable_data_hash;
 
-                    verifyIOParameter('output_file', "Label Description", `keep:${outPdh}/cat.png`);
+                    verifyIOParameter('output_file', null, "Label Description", 'cat.png', `keep:${outPdh}`);
                     verifyIOParameterImage('output_file', `/c=${outPdh}/cat.png`);
-                    verifyIOParameter('output_file_with_secondary', "Doc Description", `keep:${outPdh}/main.dat`);
-                    verifyIOParameter('output_file_with_secondary', "Doc Description", `keep:${outPdh}/secondary.dat`);
-                    verifyIOParameter('output_file_with_secondary', "Doc Description", `keep:${outPdh}/secondary2.dat`);
-                    verifyIOParameter('output_dir', "Doc desc 1, Doc desc 2", `keep:${outPdh}/outdir1`);
-                    verifyIOParameter('output_bool', null, 'true');
-                    verifyIOParameter('output_int', null, '1');
-                    verifyIOParameter('output_long', null, '1');
-                    verifyIOParameter('output_float', null, '100.5');
-                    verifyIOParameter('output_double', null, '100.3');
-                    verifyIOParameter('output_string', null, 'Hello output');
-                    verifyIOParameter('output_file_array', null, `keep:${outPdh}/output2.tar`);
-                    verifyIOParameter('output_file_array', null, `keep:${outPdh}/output3.tar`);
-                    verifyIOParameter('output_dir_array', null, `keep:${outPdh}/outdir2`);
-                    verifyIOParameter('output_dir_array', null, `keep:${outPdh}/outdir3`);
-                    verifyIOParameter('output_int_array', null, ["10", "11", "12"]);
-                    verifyIOParameter('output_long_array', null, ["51", "52"]);
-                    verifyIOParameter('output_float_array', null, ["100.2", "100.4", "100.6"]);
-                    verifyIOParameter('output_double_array', null, ["100.1", "100.2", "100.3"]);
-                    verifyIOParameter('output_string_array', null, ["Hello", "Output", "!"]);
+                    verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'main.dat', `keep:${outPdh}`);
+                    verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary.dat', `keep:${outPdh}`, true);
+                    verifyIOParameter('output_file_with_secondary', null, "Doc Description", 'secondary2.dat', `keep:${outPdh}`, true);
+                    verifyIOParameter('output_dir', null, "Doc desc 1, Doc desc 2", 'outdir1', `keep:${outPdh}`);
+                    verifyIOParameter('output_bool', null, null, 'true');
+                    verifyIOParameter('output_int', null, null, '1');
+                    verifyIOParameter('output_long', null, null, '1');
+                    verifyIOParameter('output_float', null, null, '100.5');
+                    verifyIOParameter('output_double', null, null, '100.3');
+                    verifyIOParameter('output_string', null, null, 'Hello output');
+                    verifyIOParameter('output_file_array', null, null, 'output2.tar', `keep:${outPdh}`);
+                    verifyIOParameter('output_file_array', null, null, 'output3.tar', `keep:${outPdh}`, true);
+                    verifyIOParameter('output_dir_array', null, null, 'outdir2', `keep:${outPdh}`);
+                    verifyIOParameter('output_dir_array', null, null, 'outdir3', `keep:${outPdh}`, true);
+                    verifyIOParameter('output_int_array', null, null, ["10", "11", "12"]);
+                    verifyIOParameter('output_long_array', null, null, ["51", "52"]);
+                    verifyIOParameter('output_float_array', null, null, ["100.2", "100.4", "100.6"]);
+                    verifyIOParameter('output_double_array', null, null, ["100.1", "100.2", "100.3"]);
+                    verifyIOParameter('output_string_array', null, null, ["Hello", "Output", "!"]);
                 });
         });
     });
diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index cc4ac182..e1daa256 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -213,7 +213,7 @@ export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps
                 action={
                     <div>
                         { mainProcess && <Tooltip title={"Toggle Image Preview"} disableFocusListener>
-                            <IconButton onClick={() =>{setShowImagePreview(!showImagePreview)}}>{showImagePreview ? <VisibleIcon /> : <InvisibleIcon />}</IconButton>
+                            <IconButton data-cy="io-preview-image-toggle" onClick={() =>{setShowImagePreview(!showImagePreview)}}>{showImagePreview ? <VisibleIcon /> : <InvisibleIcon />}</IconButton>
                         </Tooltip> }
                         { doHidePanel &&
                         <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
@@ -306,7 +306,7 @@ const ProcessIOPreview = withStyles(styles)(
                     const rowClass = rest.length > 0 ? classes.halfRow : undefined;
 
                     return <>
-                        <TableRow key={param.id} className={rowClass}>
+                        <TableRow className={rowClass} data-cy="process-io-param">
                             <TableCell>
                                 {param.id}
                             </TableCell>

commit 4a1dcd7be586c39cf4bc1649990e41f6e79c0f4a
Author: Stephen Smith <stephen at curii.com>
Date:   Thu Sep 15 11:16:21 2022 -0400

    16073: Add ellipses to truncated io param docstrings
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index 3efcb335..4508f5ac 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -172,7 +172,7 @@ const formatInputData = (inputs: CommandInputParameter[], auth: AuthState): Proc
         return {
             id: getIOParamId(input),
             label: input.label || "",
-            doc: doc.substring(0,50),
+            doc: doc.substring(0,50) + (doc.length > 50 ? "..." : ""),
             value: getIOParamDisplayValue(auth, input)
         };
     });
@@ -184,7 +184,7 @@ const formatOutputData = (definitions: CommandOutputParameter[], values: any, pd
         return {
             id: getIOParamId(output),
             label: output.label || "",
-            doc: doc.substring(0,50),
+            doc: doc.substring(0,50) + (doc.length > 50 ? "..." : ""),
             value: getIOParamDisplayValue(auth, Object.assign(output, { value: values[getIOParamId(output)] || [] }), pdh)
         };
     });

commit d9d9a67f9b269bcb9cf597c92c4e999ca308d36e
Author: Stephen Smith <stephen at curii.com>
Date:   Thu Sep 15 11:15:32 2022 -0400

    16073: Add link to collection in subprocess output collection tab
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index 61007dc6..cc4ac182 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import React, { ReactElement, useState } from 'react';
+import { Dispatch } from 'redux';
 import {
     StyleRulesCallback,
     WithStyles,
@@ -60,8 +61,9 @@ import { connect } from 'react-redux';
 import { RootState } from 'store/store';
 import { ProcessOutputCollectionFiles } from './process-output-collection-files';
 import { Process } from 'store/processes/process';
+import { navigateTo } from 'store/navigation/navigation-action';
 
-type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'imagePreview' | 'valArray' | 'emptyValue' | 'halfRow' | 'symmetricTabs' | 'imagePlaceholder' | 'rowWithPreview';
+type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'collectionLink' | 'imagePreview' | 'valArray' | 'emptyValue' | 'halfRow' | 'symmetricTabs' | 'imagePlaceholder' | 'rowWithPreview';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     card: {
@@ -111,6 +113,15 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         overflowWrap: 'break-word',
         cursor: 'pointer',
     },
+    collectionLink: {
+        margin: '10px',
+        '& a': {
+            color: theme.palette.primary.main,
+            textDecoration: 'none',
+            overflowWrap: 'break-word',
+            cursor: 'pointer',
+        }
+    },
     imagePreview: {
         maxHeight: '15em',
         maxWidth: '15em',
@@ -164,10 +175,18 @@ export interface ProcessIOCardDataProps {
     outputUuid?: string;
 }
 
-type ProcessIOCardProps = ProcessIOCardDataProps & WithStyles<CssRules> & MPVPanelProps;
+export interface ProcessIOCardActionProps {
+    navigateTo: (uuid: string) => void;
+}
 
-export const ProcessIOCard = withStyles(styles)(
-    ({ classes, label, params, raw, mounts, outputUuid, doHidePanel, panelName, process }: ProcessIOCardProps) => {
+const mapDispatchToProps = (dispatch: Dispatch): ProcessIOCardActionProps => ({
+    navigateTo: (uuid) => dispatch<any>(navigateTo(uuid)),
+});
+
+type ProcessIOCardProps = ProcessIOCardDataProps & ProcessIOCardActionProps & WithStyles<CssRules> & MPVPanelProps;
+
+export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps)(
+    ({ classes, label, params, raw, mounts, outputUuid, doHidePanel, panelName, process, navigateTo }: ProcessIOCardProps) => {
         const [mainProcTabState, setMainProcTabState] = useState(0);
         const handleMainProcTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
             setMainProcTabState(value);
@@ -232,7 +251,13 @@ export const ProcessIOCard = withStyles(styles)(
                             </Tabs>
                             <div className={classes.tableWrapper}>
                                 {label === ProcessIOCardType.INPUT && <ProcessInputMounts mounts={mounts || []} />}
-                                {label === ProcessIOCardType.OUTPUT && <ProcessOutputCollectionFiles isWritable={false} currentItemUuid={outputUuid} />}
+                                {label === ProcessIOCardType.OUTPUT && <>
+                                    {outputUuid && <Typography className={classes.collectionLink}>
+                                        Output Collection: <MuiLink className={classes.keepLink} onClick={() => {navigateTo(outputUuid)}}>
+                                        {outputUuid}
+                                    </MuiLink></Typography>}
+                                    <ProcessOutputCollectionFiles isWritable={false} currentItemUuid={outputUuid} />
+                                </>}
                             </div>
                         </>)
                     }
@@ -240,7 +265,7 @@ export const ProcessIOCard = withStyles(styles)(
             </CardContent>
         </Card>;
     }
-);
+));
 
 export type ProcessIOValue = {
     display: ReactElement<any, any>;

commit 5eea75dd1622bee999bf0f0a6a98a5d98913f752
Author: Stephen Smith <stephen at curii.com>
Date:   Thu Sep 15 10:24:02 2022 -0400

    16073: Add image preview toggle with placeholder, hide tabs on main/subprocess, show truncated docstring, split collection link to separate column
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx
index e97b37c4..0457bbb1 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -74,6 +74,7 @@ import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline';
 import NotInterested from '@material-ui/icons/NotInterested';
 import Archive from '@material-ui/icons/Archive';
 import Unarchive from '@material-ui/icons/Unarchive';
+import Image from '@material-ui/icons/Image';
 
 // Import FontAwesome icons
 import { library } from '@fortawesome/fontawesome-svg-core';
@@ -192,3 +193,4 @@ export const LoginAsIcon: IconType = (props) => <ExitToApp {...props} />;
 export const ActiveIcon: IconType = (props) => <CheckCircleOutline {...props} />;
 export const SetupIcon: IconType = (props) => <RemoveCircleOutline {...props} />;
 export const InactiveIcon: IconType = (props) => <NotInterested {...props} />;
+export const ImageIcon: IconType = (props) => <Image {...props} />;
diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index 7d456522..61007dc6 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -25,7 +25,7 @@ import {
     Chip,
 } from '@material-ui/core';
 import { ArvadosTheme } from 'common/custom-theme';
-import { CloseIcon, InfoIcon, ProcessIcon, InputIcon, OutputIcon } from 'components/icon/icon';
+import { CloseIcon, ImageIcon, InfoIcon, InputIcon, InvisibleIcon, OutputIcon, VisibleIcon } from 'components/icon/icon';
 import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
 import {
   BooleanCommandInputParameter,
@@ -61,7 +61,7 @@ import { RootState } from 'store/store';
 import { ProcessOutputCollectionFiles } from './process-output-collection-files';
 import { Process } from 'store/processes/process';
 
-type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'imagePreview' | 'valArray' | 'emptyValue' | 'symmetricTabs';
+type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'imagePreview' | 'valArray' | 'emptyValue' | 'halfRow' | 'symmetricTabs' | 'imagePlaceholder' | 'rowWithPreview';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     card: {
@@ -127,11 +127,28 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     emptyValue: {
         color: theme.customs.colors.grey500,
     },
+    halfRow: {
+        '& td': {
+            borderBottom: 'none',
+        }
+    },
     symmetricTabs: {
         '& button': {
             flexBasis: '0',
         }
     },
+    imagePlaceholder: {
+        width: '60px',
+        height: '60px',
+        display: 'flex',
+        alignItems: 'center',
+        justifyContent: 'center',
+        backgroundColor: '#cecece',
+        borderRadius: '10px',
+    },
+    rowWithPreview: {
+        verticalAlign: 'bottom',
+    }
 });
 
 export enum ProcessIOCardType {
@@ -156,7 +173,10 @@ export const ProcessIOCard = withStyles(styles)(
             setMainProcTabState(value);
         }
 
-        const PanelIcon = label == ProcessIOCardType.INPUT ? InputIcon : OutputIcon;
+        const [showImagePreview, setShowImagePreview] = useState(false);
+
+        const PanelIcon = label === ProcessIOCardType.INPUT ? InputIcon : OutputIcon;
+        const mainProcess = !process.containerRequest.requestingContainerUuid;
 
         return <Card className={classes.card} data-cy="process-io-card">
             <CardHeader
@@ -173,6 +193,9 @@ export const ProcessIOCard = withStyles(styles)(
                 }
                 action={
                     <div>
+                        { mainProcess && <Tooltip title={"Toggle Image Preview"} disableFocusListener>
+                            <IconButton onClick={() =>{setShowImagePreview(!showImagePreview)}}>{showImagePreview ? <VisibleIcon /> : <InvisibleIcon />}</IconButton>
+                        </Tooltip> }
                         { doHidePanel &&
                         <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
                             <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
@@ -181,7 +204,7 @@ export const ProcessIOCard = withStyles(styles)(
                 } />
             <CardContent className={classes.content}>
                 <div>
-                    {!process.containerRequest.requestingContainerUuid ?
+                    {mainProcess ?
                         (<>
                             <Tabs value={mainProcTabState} onChange={handleMainProcTabChange} variant="fullWidth" className={classes.symmetricTabs}>
                                 <Tab label="Parameters" />
@@ -189,7 +212,7 @@ export const ProcessIOCard = withStyles(styles)(
                             </Tabs>
                             {mainProcTabState === 0 && <div className={classes.tableWrapper}>
                                 {params.length ?
-                                    <ProcessIOPreview data={params} /> :
+                                    <ProcessIOPreview data={params} showImagePreview={showImagePreview} /> :
                                     <Grid container item alignItems='center' justify='center'>
                                         <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
                                     </Grid>}
@@ -222,57 +245,105 @@ export const ProcessIOCard = withStyles(styles)(
 export type ProcessIOValue = {
     display: ReactElement<any, any>;
     imageUrl?: string;
+    collection?: ReactElement<any, any>;
 }
 
 export type ProcessIOParameter = {
     id: string;
+    label: string;
     doc: string;
     value: ProcessIOValue[];
 }
 
 interface ProcessIOPreviewDataProps {
     data: ProcessIOParameter[];
+    showImagePreview: boolean;
 }
 
 type ProcessIOPreviewProps = ProcessIOPreviewDataProps & WithStyles<CssRules>;
 
 const ProcessIOPreview = withStyles(styles)(
-    ({ classes, data }: ProcessIOPreviewProps) =>
+    ({ classes, data, showImagePreview }: ProcessIOPreviewProps) =>
         <Table className={classes.tableRoot} aria-label="Process IO Preview">
             <TableHead>
                 <TableRow>
+                    <TableCell>Name</TableCell>
                     <TableCell>Label</TableCell>
                     <TableCell>Description</TableCell>
                     <TableCell>Value</TableCell>
+                    <TableCell>Collection</TableCell>
                 </TableRow>
             </TableHead>
             <TableBody>
                 {data.map((param: ProcessIOParameter) => {
-                    return <TableRow key={param.id}>
-                        <TableCell component="th" scope="row">
-                            {param.id}
-                        </TableCell>
-                        <TableCell>{param.doc}</TableCell>
-                        <TableCell>{param.value.map(val => (
-                            <Typography className={classes.paramValue}>
-                                {val.imageUrl ? <img className={classes.imagePreview} src={val.imageUrl} alt="Inline Preview" /> : ""}
-                                <span className={classes.valArray}>
-                                    {val.display}
-                                </span>
-                            </Typography>
-                        ))}</TableCell>
-                    </TableRow>;
+                    const firstVal = param.value.length > 0 ? param.value[0] : undefined;
+                    const rest = param.value.slice(1);
+                    const rowClass = rest.length > 0 ? classes.halfRow : undefined;
+
+                    return <>
+                        <TableRow key={param.id} className={rowClass}>
+                            <TableCell>
+                                {param.id}
+                            </TableCell>
+                            <TableCell >{param.label}</TableCell>
+                            <TableCell >{param.doc}</TableCell>
+                            <TableCell>
+                                {firstVal && <ProcessValuePreview value={firstVal} showImagePreview={showImagePreview} />}
+                            </TableCell>
+                            <TableCell className={firstVal?.imageUrl ? classes.rowWithPreview : undefined}>
+                                <Typography className={classes.paramValue}>
+                                    {firstVal?.collection}
+                                </Typography>
+                            </TableCell>
+                        </TableRow>
+                        {rest.map((val, i) => (
+                            <TableRow className={(i < rest.length-1) ? rowClass : undefined}>
+                                <TableCell />
+                                <TableCell />
+                                <TableCell />
+                                <TableCell>
+                                    <ProcessValuePreview value={val} showImagePreview={showImagePreview} />
+                                </TableCell>
+                                <TableCell className={firstVal?.imageUrl ? classes.rowWithPreview : undefined}>
+                                    <Typography className={classes.paramValue}>
+                                        {val.collection}
+                                    </Typography>
+                                </TableCell>
+                            </TableRow>
+                        ))}
+                    </>;
                 })}
             </TableBody>
         </Table>
 );
 
+interface ProcessValuePreviewProps {
+    value: ProcessIOValue;
+    showImagePreview: boolean;
+}
+
+const ProcessValuePreview = withStyles(styles)(
+    ({value, showImagePreview, classes}: ProcessValuePreviewProps & WithStyles<CssRules>) =>
+        <Typography className={classes.paramValue}>
+            {value.imageUrl && showImagePreview ? <img className={classes.imagePreview} src={value.imageUrl} alt="Inline Preview" /> : ""}
+            {value.imageUrl && !showImagePreview ? <ImagePlaceholder /> : ""}
+            <span className={classes.valArray}>
+                {value.display}
+            </span>
+        </Typography>
+)
+
 const handleClick = (url) => {
     window.open(url, '_blank');
 }
 
+
+interface ProcessIORawDataProps {
+    data: ProcessIOParameter[];
+}
+
 const ProcessIORaw = withStyles(styles)(
-    ({ data }: ProcessIOPreviewProps) =>
+    ({ data }: ProcessIORawDataProps) =>
         <Paper elevation={0}>
             <pre>
                 {JSON.stringify(data, null, 2)}
@@ -490,21 +561,23 @@ const normalizeDirectoryLocation = (directory: Directory): Directory => {
 const directoryToProcessIOValue = (directory: Directory, auth: AuthState, pdh?: string): ProcessIOValue => {
     const normalizedDirectory = normalizeDirectoryLocation(directory);
     return {
-        display: <span>
-            <KeepUrlBase auth={auth} res={normalizedDirectory} pdh={pdh}/>/<KeepUrlPath auth={auth} res={normalizedDirectory} pdh={pdh}/>
-        </span>,
+        display: <KeepUrlPath auth={auth} res={normalizedDirectory} pdh={pdh}/>,
+        collection: <KeepUrlBase auth={auth} res={normalizedDirectory} pdh={pdh}/>,
     };
 };
 
 const fileToProcessIOValue = (file: File, auth: AuthState, pdh?: string): ProcessIOValue => {
     return {
-        display: <span>
-            <KeepUrlBase auth={auth} res={file} pdh={pdh}/>/<KeepUrlPath auth={auth} res={file} pdh={pdh}/>
-        </span>,
+        display: <KeepUrlPath auth={auth} res={file} pdh={pdh}/>,
         imageUrl: isFileImage(file.basename) ? getImageUrl(auth, file, pdh) : undefined,
+        collection: <KeepUrlBase auth={auth} res={file} pdh={pdh}/>,
     }
 };
 
 const EmptyValue = withStyles(styles)(
     ({classes}: WithStyles<CssRules>) => <span className={classes.emptyValue}>No value</span>
 );
+
+const ImagePlaceholder = withStyles(styles)(
+    ({classes}: WithStyles<CssRules>) => <span className={classes.imagePlaceholder}><ImageIcon /></span>
+);
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index e85d67a7..3efcb335 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -168,10 +168,11 @@ export const ProcessPanelRoot = withStyles(styles)(
 
 const formatInputData = (inputs: CommandInputParameter[], auth: AuthState): ProcessIOParameter[] => {
     return inputs.map(input => {
-        const doc = Array.isArray(input.doc) ? input.doc.join(', ') : input.doc;
+        const doc = Array.isArray(input.doc) ? input.doc.join(', ') : input.doc || "";
         return {
             id: getIOParamId(input),
-            doc: input.label || doc || "",
+            label: input.label || "",
+            doc: doc.substring(0,50),
             value: getIOParamDisplayValue(auth, input)
         };
     });
@@ -179,10 +180,11 @@ const formatInputData = (inputs: CommandInputParameter[], auth: AuthState): Proc
 
 const formatOutputData = (definitions: CommandOutputParameter[], values: any, pdh: string | undefined, auth: AuthState): ProcessIOParameter[] => {
     return definitions.map(output => {
-        const doc = Array.isArray(output.doc) ? output.doc.join(', ') : output.doc;
+        const doc = Array.isArray(output.doc) ? output.doc.join(', ') : output.doc || "";
         return {
             id: getIOParamId(output),
-            doc: output.label || doc || "",
+            label: output.label || "",
+            doc: doc.substring(0,50),
             value: getIOParamDisplayValue(auth, Object.assign(output, { value: values[getIOParamId(output)] || [] }), pdh)
         };
     });

commit 6df149b9bebe91e92de4c396f60127ee5b5b4de1
Author: Stephen Smith <stephen at curii.com>
Date:   Thu Sep 15 10:21:20 2022 -0400

    16073: Style io panel tabs to be symmetrical
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index 340d1298..7d456522 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -61,7 +61,7 @@ import { RootState } from 'store/store';
 import { ProcessOutputCollectionFiles } from './process-output-collection-files';
 import { Process } from 'store/processes/process';
 
-type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'imagePreview' | 'valArray' | 'emptyValue';
+type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'imagePreview' | 'valArray' | 'emptyValue' | 'symmetricTabs';
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     card: {
@@ -127,6 +127,11 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     emptyValue: {
         color: theme.customs.colors.grey500,
     },
+    symmetricTabs: {
+        '& button': {
+            flexBasis: '0',
+        }
+    },
 });
 
 export enum ProcessIOCardType {
@@ -178,7 +183,7 @@ export const ProcessIOCard = withStyles(styles)(
                 <div>
                     {!process.containerRequest.requestingContainerUuid ?
                         (<>
-                            <Tabs value={mainProcTabState} onChange={handleMainProcTabChange} variant="fullWidth">
+                            <Tabs value={mainProcTabState} onChange={handleMainProcTabChange} variant="fullWidth" className={classes.symmetricTabs}>
                                 <Tab label="Parameters" />
                                 <Tab label="JSON" />
                             </Tabs>
@@ -198,7 +203,7 @@ export const ProcessIOCard = withStyles(styles)(
                                 </div>}
                         </>) :
                         (<>
-                            <Tabs value={0} variant="fullWidth">
+                            <Tabs value={0} variant="fullWidth" className={classes.symmetricTabs}>
                                 {label === ProcessIOCardType.INPUT && <Tab label="Collections" />}
                                 {label === ProcessIOCardType.OUTPUT && <Tab label="Collection" />}
                             </Tabs>

commit e961f9c6546d4e201396fe2593b5d7a40f93cdc5
Author: Stephen Smith <stephen at curii.com>
Date:   Wed Sep 14 19:22:27 2022 -0400

    16073: Add tooltip hints to collection/keep links
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index 1f5d6aa6..340d1298 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -442,7 +442,7 @@ const KeepUrlBase = withStyles(styles)(({auth, res, pdh, classes}: KeepUrlProps
     // Passing a pdh always returns a relative wb2 collection url
     const pdhWbPath = getNavUrl(pdhUrl.replace('keep:', ''), auth);
     return pdhUrl && pdhWbPath ?
-        <RouterLink to={pdhWbPath} className={classes.keepLink}>{pdhUrl}</RouterLink> :
+        <Tooltip title={"View collection in Workbench"}><RouterLink to={pdhWbPath} className={classes.keepLink}>{pdhUrl}</RouterLink></Tooltip> :
         <></>;
 });
 
@@ -453,8 +453,9 @@ const KeepUrlPath = withStyles(styles)(({auth, res, pdh, classes}: KeepUrlProps
 
     const keepUrlPathNav = getKeepNavUrl(auth, res, pdh);
     return keepUrlPath && keepUrlPathNav ?
-        <MuiLink className={classes.keepLink} onClick={() => handleClick(keepUrlPathNav)}>{keepUrlPath}</MuiLink> :
-        <></>;
+        <Tooltip title={"View in keep-web"}><MuiLink className={classes.keepLink} onClick={() => handleClick(keepUrlPathNav)}>{keepUrlPath}</MuiLink></Tooltip> :
+        // Show No value for root collection io that lacks path part
+        <EmptyValue />;
 });
 
 const getKeepNavUrl = (auth: AuthState, file: File | Directory, pdh?: string): string => {

commit 14a44a526eb847da76be872892f15b6b2248bc26
Author: Stephen Smith <stephen at curii.com>
Date:   Wed Sep 14 19:09:47 2022 -0400

    16073: Reduce io preview table header padding
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index bbccb644..1f5d6aa6 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -95,6 +95,10 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     },
     tableRoot: {
         width: '100%',
+        '& thead th': {
+            verticalAlign: 'bottom',
+            paddingBottom: '10px',
+        }
     },
     paramValue: {
         display: 'flex',

commit b70b5d61574f80e8b81f48b6e2a9e2ff6df489d8
Author: Stephen Smith <stephen at curii.com>
Date:   Tue Sep 13 10:31:26 2022 -0400

    16073: Hide param tabs on subprocess and collection tabs on main process
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index 2857aa13..bbccb644 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -59,6 +59,7 @@ import { InputCollectionMount } from 'store/processes/processes-actions';
 import { connect } from 'react-redux';
 import { RootState } from 'store/store';
 import { ProcessOutputCollectionFiles } from './process-output-collection-files';
+import { Process } from 'store/processes/process';
 
 type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'imagePreview' | 'valArray' | 'emptyValue';
 
@@ -129,6 +130,7 @@ export enum ProcessIOCardType {
     OUTPUT = 'Outputs',
 }
 export interface ProcessIOCardDataProps {
+    process: Process;
     label: ProcessIOCardType;
     params: ProcessIOParameter[];
     raw?: any;
@@ -139,11 +141,12 @@ export interface ProcessIOCardDataProps {
 type ProcessIOCardProps = ProcessIOCardDataProps & WithStyles<CssRules> & MPVPanelProps;
 
 export const ProcessIOCard = withStyles(styles)(
-    ({ classes, label, params, raw, mounts, outputUuid, doHidePanel, panelName }: ProcessIOCardProps) => {
-        const [tabState, setTabState] = useState(0);
-        const handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
-            setTabState(value);
+    ({ classes, label, params, raw, mounts, outputUuid, doHidePanel, panelName, process }: ProcessIOCardProps) => {
+        const [mainProcTabState, setMainProcTabState] = useState(0);
+        const handleMainProcTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
+            setMainProcTabState(value);
         }
+
         const PanelIcon = label == ProcessIOCardType.INPUT ? InputIcon : OutputIcon;
 
         return <Card className={classes.card} data-cy="process-io-card">
@@ -169,30 +172,38 @@ export const ProcessIOCard = withStyles(styles)(
                 } />
             <CardContent className={classes.content}>
                 <div>
-                    <Tabs value={tabState} onChange={handleChange} variant="fullWidth">
-                        <Tab label="Parameters" />
-                        <Tab label="JSON" />
-                        {label === ProcessIOCardType.INPUT && <Tab label="Collections" />}
-                        {label === ProcessIOCardType.OUTPUT && <Tab label="Collection" />}
-                    </Tabs>
-                    {tabState === 0 && <div className={classes.tableWrapper}>
-                        {params.length ?
-                            <ProcessIOPreview data={params} /> :
-                            <Grid container item alignItems='center' justify='center'>
-                                <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
-                            </Grid>}
-                        </div>}
-                    {tabState === 1 && <div className={classes.tableWrapper}>
-                        {params.length ?
-                            <ProcessIORaw data={raw || params} /> :
-                            <Grid container item alignItems='center' justify='center'>
-                                <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
-                            </Grid>}
-                        </div>}
-                    {tabState === 2 && <div className={classes.tableWrapper}>
-                        {label === ProcessIOCardType.INPUT && <ProcessInputMounts mounts={mounts || []} />}
-                        {label === ProcessIOCardType.OUTPUT && <ProcessOutputCollectionFiles isWritable={false} currentItemUuid={outputUuid} />}
-                        </div>}
+                    {!process.containerRequest.requestingContainerUuid ?
+                        (<>
+                            <Tabs value={mainProcTabState} onChange={handleMainProcTabChange} variant="fullWidth">
+                                <Tab label="Parameters" />
+                                <Tab label="JSON" />
+                            </Tabs>
+                            {mainProcTabState === 0 && <div className={classes.tableWrapper}>
+                                {params.length ?
+                                    <ProcessIOPreview data={params} /> :
+                                    <Grid container item alignItems='center' justify='center'>
+                                        <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
+                                    </Grid>}
+                                </div>}
+                            {mainProcTabState === 1 && <div className={classes.tableWrapper}>
+                                {params.length ?
+                                    <ProcessIORaw data={raw || params} /> :
+                                    <Grid container item alignItems='center' justify='center'>
+                                        <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
+                                    </Grid>}
+                                </div>}
+                        </>) :
+                        (<>
+                            <Tabs value={0} variant="fullWidth">
+                                {label === ProcessIOCardType.INPUT && <Tab label="Collections" />}
+                                {label === ProcessIOCardType.OUTPUT && <Tab label="Collection" />}
+                            </Tabs>
+                            <div className={classes.tableWrapper}>
+                                {label === ProcessIOCardType.INPUT && <ProcessInputMounts mounts={mounts || []} />}
+                                {label === ProcessIOCardType.OUTPUT && <ProcessOutputCollectionFiles isWritable={false} currentItemUuid={outputUuid} />}
+                            </div>
+                        </>)
+                    }
                 </div>
             </CardContent>
         </Card>;
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index 1cf60300..e85d67a7 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -136,6 +136,7 @@ export const ProcessPanelRoot = withStyles(styles)(
             <MPVPanelContent forwardProps xs="auto" data-cy="process-inputs">
                 <ProcessIOCard
                     label={ProcessIOCardType.INPUT}
+                    process={process}
                     params={processedInputs}
                     raw={rawInputs}
                     mounts={inputMounts}
@@ -144,6 +145,7 @@ export const ProcessPanelRoot = withStyles(styles)(
             <MPVPanelContent forwardProps xs="auto" data-cy="process-outputs">
                 <ProcessIOCard
                     label={ProcessIOCardType.OUTPUT}
+                    process={process}
                     params={processedOutputs}
                     raw={outputDetails.rawOutputs}
                     outputUuid={outputUuid || ""}

commit 4f88234088269faa064da9459376c3af2a740a7d
Author: Stephen Smith <stephen at curii.com>
Date:   Tue Sep 13 10:30:40 2022 -0400

    16073: Rename process io panel tabs
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index eafefb2e..2857aa13 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -170,10 +170,10 @@ export const ProcessIOCard = withStyles(styles)(
             <CardContent className={classes.content}>
                 <div>
                     <Tabs value={tabState} onChange={handleChange} variant="fullWidth">
-                        <Tab label="Preview" />
-                        <Tab label="Raw" />
-                        {label === ProcessIOCardType.INPUT && <Tab label="Input Mounts" />}
-                        {label === ProcessIOCardType.OUTPUT && <Tab label="Output Collection" />}
+                        <Tab label="Parameters" />
+                        <Tab label="JSON" />
+                        {label === ProcessIOCardType.INPUT && <Tab label="Collections" />}
+                        {label === ProcessIOCardType.OUTPUT && <Tab label="Collection" />}
                     </Tabs>
                     {tabState === 0 && <div className={classes.tableWrapper}>
                         {params.length ?

commit 1bc5df05a276ee007908435a7067764c4d6cf357
Author: Stephen Smith <stephen at curii.com>
Date:   Mon Sep 12 19:35:28 2022 -0400

    16073: Use import/export as process i/o icons
    
    Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>

diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx
index db603597..e97b37c4 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -43,7 +43,6 @@ import ListAlt from '@material-ui/icons/ListAlt';
 import Menu from '@material-ui/icons/Menu';
 import MoreVert from '@material-ui/icons/MoreVert';
 import Mail from '@material-ui/icons/Mail';
-import MoveToInbox from '@material-ui/icons/MoveToInbox';
 import Notifications from '@material-ui/icons/Notifications';
 import OpenInNew from '@material-ui/icons/OpenInNew';
 import People from '@material-ui/icons/People';
@@ -73,6 +72,8 @@ import ExitToApp from '@material-ui/icons/ExitToApp';
 import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
 import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline';
 import NotInterested from '@material-ui/icons/NotInterested';
+import Archive from '@material-ui/icons/Archive';
+import Unarchive from '@material-ui/icons/Unarchive';
 
 // Import FontAwesome icons
 import { library } from '@fortawesome/fontawesome-svg-core';
@@ -138,7 +139,7 @@ export const HelpIcon: IconType = (props) => <Help {...props} />;
 export const HelpOutlineIcon: IconType = (props) => <HelpOutline {...props} />;
 export const ImportContactsIcon: IconType = (props) => <ImportContacts {...props} />;
 export const InfoIcon: IconType = (props) => <Info {...props} />;
-export const InputIcon: IconType = (props) => <InsertDriveFile {...props} />;
+export const FileInputIcon: 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} />;
@@ -148,7 +149,8 @@ export const MoveToIcon: IconType = (props) => <Input {...props} />;
 export const NewProjectIcon: IconType = (props) => <CreateNewFolder {...props} />;
 export const NotificationIcon: IconType = (props) => <Notifications {...props} />;
 export const OpenIcon: IconType = (props) => <OpenInNew {...props} />;
-export const OutputIcon: IconType = (props) => <MoveToInbox {...props} />;
+export const InputIcon: IconType = (props) => <Archive {...props} />;
+export const OutputIcon: IconType = (props) => <Unarchive {...props} />;
 export const PaginationDownIcon: IconType = (props) => <ArrowDropDown {...props} />;
 export const PaginationLeftArrowIcon: IconType = (props) => <ChevronLeft {...props} />;
 export const PaginationRightArrowIcon: IconType = (props) => <ChevronRight {...props} />;
diff --git a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
index aa9fb60b..29c34e67 100644
--- a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
@@ -10,7 +10,7 @@ import { TreeItem, TreeItemStatus } from 'components/tree/tree';
 import { ProjectResource } from "models/project";
 import { treePickerActions } from "store/tree-picker/tree-picker-actions";
 import { ListItemTextIcon } from "components/list-item-text-icon/list-item-text-icon";
-import { ProjectIcon, InputIcon, IconType, CollectionIcon } from 'components/icon/icon';
+import { ProjectIcon, FileInputIcon, IconType, CollectionIcon } from 'components/icon/icon';
 import { loadProject, loadCollection } from 'store/tree-picker/tree-picker-actions';
 import { GroupContentsResource } from 'services/groups-service/groups-service';
 import { CollectionDirectory, CollectionFile, CollectionFileType } from 'models/collection-file';
@@ -104,7 +104,7 @@ const getProjectPickerIcon = ({ data }: TreeItem<ProjectsTreePickerItem>, rootIc
     } else if ('type' in data) {
         switch (data.type) {
             case CollectionFileType.FILE:
-                return InputIcon;
+                return FileInputIcon;
             default:
                 return ProjectIcon;
         }
diff --git a/src/views/process-panel/process-io-card.tsx b/src/views/process-panel/process-io-card.tsx
index 4b2413ce..eafefb2e 100644
--- a/src/views/process-panel/process-io-card.tsx
+++ b/src/views/process-panel/process-io-card.tsx
@@ -25,7 +25,7 @@ import {
     Chip,
 } from '@material-ui/core';
 import { ArvadosTheme } from 'common/custom-theme';
-import { CloseIcon, InfoIcon, ProcessIcon } from 'components/icon/icon';
+import { CloseIcon, InfoIcon, ProcessIcon, InputIcon, OutputIcon } from 'components/icon/icon';
 import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
 import {
   BooleanCommandInputParameter,
@@ -144,6 +144,7 @@ export const ProcessIOCard = withStyles(styles)(
         const handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
             setTabState(value);
         }
+        const PanelIcon = label == ProcessIOCardType.INPUT ? InputIcon : OutputIcon;
 
         return <Card className={classes.card} data-cy="process-io-card">
             <CardHeader
@@ -152,7 +153,7 @@ export const ProcessIOCard = withStyles(styles)(
                     content: classes.title,
                     avatar: classes.avatar,
                 }}
-                avatar={<ProcessIcon className={classes.iconHeader} />}
+                avatar={<PanelIcon className={classes.iconHeader} />}
                 title={
                     <Typography noWrap variant='h6' color='inherit'>
                         {label}

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list