//
// SPDX-License-Identifier: AGPL-3.0
-import React, { ReactElement, useState } from "react";
+import React, { ReactElement, memo, useState } from "react";
import { Dispatch } from "redux";
import {
StyleRulesCallback,
isPrimitiveOfType,
StringArrayCommandInputParameter,
StringCommandInputParameter,
+ getEnumType,
} from "models/workflow";
import { CommandOutputParameter } from "cwlts/mappings/v1.0/CommandOutputParameter";
import { File } from "models/workflow";
return (
<Card
className={classes.card}
- data-cy="process-io-card">
- {/* here */}
+ data-cy="process-io-card"
+ >
<CardHeader
className={classes.header}
classes={{
<Typography
noWrap
variant="h6"
- color="inherit">
+ color="inherit"
+ >
{label}
</Typography>
}
{mainProcess && (
<Tooltip
title={"Toggle Image Preview"}
- disableFocusListener>
+ disableFocusListener
+ >
<IconButton
data-cy="io-preview-image-toggle"
onClick={() => {
setShowImagePreview(!showImagePreview);
- }}>
+ }}
+ >
{showImagePreview ? <ImageIcon /> : <ImageOffIcon />}
</IconButton>
</Tooltip>
{doUnMaximizePanel && panelMaximized && (
<Tooltip
title={`Unmaximize ${panelName || "panel"}`}
- disableFocusListener>
+ disableFocusListener
+ >
<IconButton onClick={doUnMaximizePanel}>
<UnMaximizeIcon />
</IconButton>
{doMaximizePanel && !panelMaximized && (
<Tooltip
title={`Maximize ${panelName || "panel"}`}
- disableFocusListener>
+ disableFocusListener
+ >
<IconButton onClick={doMaximizePanel}>
<MaximizeIcon />
</IconButton>
{doHidePanel && (
<Tooltip
title={`Close ${panelName || "panel"}`}
- disableFocusListener>
+ disableFocusListener
+ >
<IconButton
disabled={panelMaximized}
- onClick={doHidePanel}>
+ onClick={doHidePanel}
+ >
<CloseIcon />
</IconButton>
</Tooltip>
container
item
alignItems="center"
- justify="center">
+ justify="center"
+ >
<CircularProgress />
</Grid>
)}
value={mainProcTabState}
onChange={handleMainProcTabChange}
variant="fullWidth"
- className={classes.symmetricTabs}>
+ className={classes.symmetricTabs}
+ >
{/* params will be empty on processes without workflow definitions in mounts, so we only show raw */}
{hasParams && <Tab label="Parameters" />}
{!showParams && <Tab label="JSON" />}
container
item
alignItems="center"
- justify="center">
+ justify="center"
+ >
<DefaultView messages={["No parameters found"]} />
</Grid>
)}
container
item
alignItems="center"
- justify="center">
+ justify="center"
+ >
<CircularProgress />
</Grid>
)}
value={subProcTabState}
onChange={handleSubProcTabChange}
variant="fullWidth"
- className={classes.symmetricTabs}>
+ className={classes.symmetricTabs}
+ >
{hasInputMounts && <Tab label="Collections" />}
{hasOutputCollecton && <Tab label="Collection" />}
<Tab label="JSON" />
className={classes.keepLink}
onClick={() => {
navigateTo(outputUuid || "");
- }}>
+ }}
+ >
{outputUuid}
</MuiLink>
</Typography>
container
item
alignItems="center"
- justify="center">
+ justify="center"
+ >
<DefaultView messages={["No data to display"]} />
</Grid>
)}
type ProcessIOPreviewProps = ProcessIOPreviewDataProps & WithStyles<CssRules>;
-const ProcessIOPreview = withStyles(styles)(({ classes, data, showImagePreview, valueLabel }: ProcessIOPreviewProps) => {
- const showLabel = data.some((param: ProcessIOParameter) => param.label);
- return (
- <Table
- className={classes.tableRoot}
- aria-label="Process IO Preview">
- <TableHead>
- <TableRow>
- <TableCell>Name</TableCell>
- {showLabel && <TableCell className={classes.labelColumn}>Label</TableCell>}
- <TableCell>{valueLabel}</TableCell>
- <TableCell>Collection</TableCell>
- </TableRow>
- </TableHead>
- <TableBody>
- {data.map((param: ProcessIOParameter) => {
- const firstVal = param.value.length > 0 ? param.value[0] : undefined;
- const rest = param.value.slice(1);
- const mainRowClasses = {
- [classes.noBorderRow]: rest.length > 0,
- };
-
- return (
- <React.Fragment key={param.id}>
- <TableRow
- className={classNames(mainRowClasses)}
- data-cy="process-io-param">
- <TableCell>{param.id}</TableCell>
- {showLabel && <TableCell>{param.label}</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) => {
- const rowClasses = {
- [classes.noBorderRow]: i < rest.length - 1,
- [classes.secondaryRow]: val.secondary,
- };
- return (
- <TableRow
- className={classNames(rowClasses)}
- key={i}>
- <TableCell />
- {showLabel && <TableCell />}
- <TableCell>
+const ProcessIOPreview = memo(
+ withStyles(styles)(({ classes, data, showImagePreview, valueLabel }: ProcessIOPreviewProps) => {
+ const showLabel = data.some((param: ProcessIOParameter) => param.label);
+ return (
+ <Table
+ className={classes.tableRoot}
+ aria-label="Process IO Preview"
+ >
+ <TableHead>
+ <TableRow>
+ <TableCell>Name</TableCell>
+ {showLabel && <TableCell className={classes.labelColumn}>Label</TableCell>}
+ <TableCell>{valueLabel}</TableCell>
+ <TableCell>Collection</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {data.map((param: ProcessIOParameter) => {
+ const firstVal = param.value.length > 0 ? param.value[0] : undefined;
+ const rest = param.value.slice(1);
+ const mainRowClasses = {
+ [classes.noBorderRow]: rest.length > 0,
+ };
+
+ return (
+ <React.Fragment key={param.id}>
+ <TableRow
+ className={classNames(mainRowClasses)}
+ data-cy="process-io-param"
+ >
+ <TableCell>{param.id}</TableCell>
+ {showLabel && <TableCell>{param.label}</TableCell>}
+ <TableCell>
+ {firstVal && (
<ProcessValuePreview
- value={val}
+ value={firstVal}
showImagePreview={showImagePreview}
/>
- </TableCell>
- <TableCell className={firstVal?.imageUrl ? classes.rowWithPreview : undefined}>
- <Typography className={classes.paramValue}>{val.collection}</Typography>
- </TableCell>
- </TableRow>
- );
- })}
- </React.Fragment>
- );
- })}
- </TableBody>
- </Table>
- );
-});
+ )}
+ </TableCell>
+ <TableCell className={firstVal?.imageUrl ? classes.rowWithPreview : undefined}>
+ <Typography className={classes.paramValue}>{firstVal?.collection}</Typography>
+ </TableCell>
+ </TableRow>
+ {rest.map((val, i) => {
+ const rowClasses = {
+ [classes.noBorderRow]: i < rest.length - 1,
+ [classes.secondaryRow]: val.secondary,
+ };
+ return (
+ <TableRow
+ className={classNames(rowClasses)}
+ key={i}
+ >
+ <TableCell />
+ {showLabel && <TableCell />}
+ <TableCell>
+ <ProcessValuePreview
+ value={val}
+ showImagePreview={showImagePreview}
+ />
+ </TableCell>
+ <TableCell className={firstVal?.imageUrl ? classes.rowWithPreview : undefined}>
+ <Typography className={classes.paramValue}>{val.collection}</Typography>
+ </TableCell>
+ </TableRow>
+ );
+ })}
+ </React.Fragment>
+ );
+ })}
+ </TableBody>
+ </Table>
+ );
+ })
+);
interface ProcessValuePreviewProps {
value: ProcessIOValue;
}))(({ mounts, classes, auth }: ProcessInputMountsProps & { auth: AuthState }) => (
<Table
className={classes.tableRoot}
- aria-label="Process Input Mounts">
+ aria-label="Process Input Mounts"
+ >
<TableHead>
<TableRow>
<TableCell>Path</TableCell>
<TableCell>
<RouterLink
to={getNavUrl(mount.pdh, auth)}
- className={classes.keepLink}>
+ className={classes.keepLink}
+ >
{mount.pdh}
</RouterLink>
</TableCell>
? [directoryToProcessIOValue(directory, auth, pdh)]
: [{ display: <EmptyValue /> }];
- case typeof input.type === "object" && !(input.type instanceof Array) && input.type.type === "enum":
+ case getEnumType(input) !== null:
const enumValue = (input as EnumCommandInputParameter).value;
return enumValue !== undefined && enumValue ? [{ display: <pre>{enumValue}</pre> }] : [{ display: <EmptyValue /> }];
const fileArrayMainFiles = (input as FileArrayCommandInputParameter).value || [];
const firstMainFilePdh = fileArrayMainFiles.length > 0 && fileArrayMainFiles[0] ? getResourcePdhUrl(fileArrayMainFiles[0], pdh) : "";
- // Convert each main file into separate arrays of ProcessIOValue to preserve secondaryFile grouping
- const fileArrayValues = fileArrayMainFiles
- .map((mainFile: File, i): ProcessIOValue[] => {
- const secondaryFiles = (mainFile as unknown as FileWithSecondaryFiles)?.secondaryFiles || [];
- return [
- // Pass firstMainFilePdh to secondary files and every main file besides the first to hide pdh if equal
- ...(mainFile ? [fileToProcessIOValue(mainFile, false, auth, pdh, i > 0 ? firstMainFilePdh : "")] : []),
- ...secondaryFiles.map(file => fileToProcessIOValue(file, true, auth, pdh, firstMainFilePdh)),
- ];
- // Reduce each mainFile/secondaryFile group into single array preserving ordering
- })
- .reduce((acc: ProcessIOValue[], mainFile: ProcessIOValue[]) => acc.concat(mainFile), []);
+ // Convert each main and secondaryFiles into array of ProcessIOValue preserving ordering
+ let fileArrayValues: ProcessIOValue[] = [];
+ for (let i = 0; i < fileArrayMainFiles.length; i++) {
+ const secondaryFiles = (fileArrayMainFiles[i] as unknown as FileWithSecondaryFiles)?.secondaryFiles || [];
+ fileArrayValues.push(
+ // Pass firstMainFilePdh to secondary files and every main file besides the first to hide pdh if equal
+ ...(fileArrayMainFiles[i] ? [fileToProcessIOValue(fileArrayMainFiles[i], false, auth, pdh, i > 0 ? firstMainFilePdh : "")] : []),
+ ...secondaryFiles.map(file => fileToProcessIOValue(file, true, auth, pdh, firstMainFilePdh))
+ );
+ }
return fileArrayValues.length ? fileArrayValues : [{ display: <EmptyValue /> }];
<Tooltip title={"View collection in Workbench"}>
<RouterLink
to={pdhWbPath}
- className={classes.keepLink}>
+ className={classes.keepLink}
+ >
{pdhUrl}
</RouterLink>
</Tooltip>
className={classes.keepLink}
href={keepUrlPathNav}
target="_blank"
- rel="noopener noreferrer">
+ rel="noopener noreferrer"
+ >
{keepUrlPath || "/"}
</a>
</Tooltip>
display: (
<MuiLink
href={file.location}
- target="_blank">
+ target="_blank"
+ >
{file.location}
</MuiLink>
),