ImageOffIcon,
OutputIcon,
MaximizeIcon,
- UnMaximizeIcon
+ UnMaximizeIcon,
+ InfoIcon
} from 'components/icon/icon';
import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
import {
import { navigateTo } from 'store/navigation/navigation-action';
import classNames from 'classnames';
import { DefaultCodeSnippet } from 'components/default-code-snippet/default-code-snippet';
+import { KEEP_URL_REGEX } from 'models/resource';
type CssRules =
| "card"
paddingTop: theme.spacing.unit * 0.5
},
tableWrapper: {
- height: `calc(100% - ${theme.spacing.unit * 6}px)`,
+ height: 'auto',
+ maxHeight: `calc(100% - ${theme.spacing.unit * 4.5}px)`,
overflow: 'auto',
},
tableRoot: {
export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps)(
({ classes, label, params, raw, mounts, outputUuid, doHidePanel, doMaximizePanel, doUnMaximizePanel, panelMaximized, panelName, process, navigateTo }: ProcessIOCardProps) => {
const [mainProcTabState, setMainProcTabState] = useState(0);
+ const [subProcTabState, setSubProcTabState] = useState(0);
const handleMainProcTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
setMainProcTabState(value);
}
+ const handleSubProcTabChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
+ setSubProcTabState(value);
+ }
const [showImagePreview, setShowImagePreview] = useState(false);
const hasRaw = !!(raw && Object.keys(raw).length > 0);
const hasParams = !!(params && params.length > 0);
+ // Subprocess
+ const hasInputMounts = !!(label === ProcessIOCardType.INPUT && mounts && mounts.length);
+ const hasOutputCollecton = !!(label === ProcessIOCardType.OUTPUT && outputUuid);
+
return <Card className={classes.card} data-cy="process-io-card">
<CardHeader
className={classes.header}
</Tooltip> }
{ doHidePanel &&
<Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
- <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
+ <IconButton disabled={panelMaximized} onClick={doHidePanel}><CloseIcon /></IconButton>
</Tooltip> }
</div>
} />
</>) :
// Subprocess
(<>
- {((mounts && mounts.length) || outputUuid) ?
+ {loading && <Grid container item alignItems='center' justify='center'>
+ <CircularProgress />
+ </Grid>}
+ {!loading && (hasInputMounts || hasOutputCollecton || hasRaw) ?
<>
- <Tabs value={0} variant="fullWidth" className={classes.symmetricTabs}>
- {label === ProcessIOCardType.INPUT && <Tab label="Collections" />}
- {label === ProcessIOCardType.OUTPUT && <Tab label="Collection" />}
+ <Tabs value={subProcTabState} onChange={handleSubProcTabChange} variant="fullWidth" className={classes.symmetricTabs}>
+ {hasInputMounts && <Tab label="Collections" />}
+ {hasOutputCollecton && <Tab label="Collection" />}
+ <Tab label="JSON" />
</Tabs>
<div className={classes.tableWrapper}>
- {label === ProcessIOCardType.INPUT && <ProcessInputMounts mounts={mounts || []} />}
- {label === ProcessIOCardType.OUTPUT && <>
+ {subProcTabState === 0 && hasInputMounts && <ProcessInputMounts mounts={mounts || []} />}
+ {subProcTabState === 0 && hasOutputCollecton && <>
{outputUuid && <Typography className={classes.collectionLink}>
Output Collection: <MuiLink className={classes.keepLink} onClick={() => {navigateTo(outputUuid || "")}}>
{outputUuid}
</MuiLink></Typography>}
<ProcessOutputCollectionFiles isWritable={false} currentItemUuid={outputUuid} />
</>}
+ {(subProcTabState === 1 || (!hasInputMounts && !hasOutputCollecton)) && <div className={classes.tableWrapper}>
+ <ProcessIORaw data={raw} />
+ </div>}
</div>
</> :
<Grid container item alignItems='center' justify='center'>
- <DefaultView messages={["No collection(s) found"]} />
+ <DefaultView messages={["No data to display"]} />
</Grid>
}
</>)
switch (true) {
case isPrimitiveOfType(input, CWLType.BOOLEAN):
const boolValue = (input as BooleanCommandInputParameter).value;
-
return boolValue !== undefined &&
!(Array.isArray(boolValue) && boolValue.length === 0) ?
- [{display: <pre>{String(boolValue)}</pre> }] :
+ [{display: renderPrimitiveValue(boolValue, false) }] :
[{display: <EmptyValue />}];
case isPrimitiveOfType(input, CWLType.INT):
case isPrimitiveOfType(input, CWLType.LONG):
const intValue = (input as IntCommandInputParameter).value;
-
return intValue !== undefined &&
// Missing values are empty array
!(Array.isArray(intValue) && intValue.length === 0) ?
- [{display: <pre>{String(intValue)}</pre> }]
+ [{display: renderPrimitiveValue(intValue, false) }]
: [{display: <EmptyValue />}];
case isPrimitiveOfType(input, CWLType.FLOAT):
case isPrimitiveOfType(input, CWLType.DOUBLE):
const floatValue = (input as FloatCommandInputParameter).value;
-
return floatValue !== undefined &&
!(Array.isArray(floatValue) && floatValue.length === 0) ?
- [{display: <pre>{String(floatValue)}</pre> }]:
+ [{display: renderPrimitiveValue(floatValue, false) }]:
[{display: <EmptyValue />}];
case isPrimitiveOfType(input, CWLType.STRING):
const stringValue = (input as StringCommandInputParameter).value || undefined;
-
return stringValue !== undefined &&
!(Array.isArray(stringValue) && stringValue.length === 0) ?
- [{display: <pre>{stringValue}</pre> }] :
+ [{display: renderPrimitiveValue(stringValue, false) }] :
[{display: <EmptyValue />}];
case isPrimitiveOfType(input, CWLType.FILE):
...secondaryFiles
];
const mainFilePdhUrl = mainFile ? getResourcePdhUrl(mainFile, pdh) : "";
-
return files.length ?
files.map((file, i) => fileToProcessIOValue(file, (i > 0), auth, pdh, (i > 0 ? mainFilePdhUrl : ""))) :
[{display: <EmptyValue />}];
case isPrimitiveOfType(input, CWLType.DIRECTORY):
const directory = (input as DirectoryCommandInputParameter).value;
-
return directory !== undefined &&
!(Array.isArray(directory) && directory.length === 0) ?
[directoryToProcessIOValue(directory, auth, pdh)] :
!(input.type instanceof Array) &&
input.type.type === 'enum':
const enumValue = (input as EnumCommandInputParameter).value;
-
- return enumValue !== undefined ?
- [{ display: <pre>{(input as EnumCommandInputParameter).value || ''}</pre> }] :
+ return enumValue !== undefined && enumValue ?
+ [{ display: <pre>{enumValue}</pre> }] :
[{display: <EmptyValue />}];
case isArrayOfType(input, CWLType.STRING):
const strArray = (input as StringArrayCommandInputParameter).value || [];
return strArray.length ?
- [{ display: <>{((input as StringArrayCommandInputParameter).value || []).map((val) => <Chip label={val} />)}</> }] :
+ [{ display: <>{strArray.map((val) => renderPrimitiveValue(val, true))}</> }] :
[{display: <EmptyValue />}];
case isArrayOfType(input, CWLType.INT):
case isArrayOfType(input, CWLType.LONG):
const intArray = (input as IntArrayCommandInputParameter).value || [];
-
return intArray.length ?
- [{ display: <>{((input as IntArrayCommandInputParameter).value || []).map((val) => <Chip label={val} />)}</> }] :
+ [{ display: <>{intArray.map((val) => renderPrimitiveValue(val, true))}</> }] :
[{display: <EmptyValue />}];
case isArrayOfType(input, CWLType.FLOAT):
case isArrayOfType(input, CWLType.DOUBLE):
const floatArray = (input as FloatArrayCommandInputParameter).value || [];
-
return floatArray.length ?
- [{ display: <>{floatArray.map((val) => <Chip label={val} />)}</> }] :
+ [{ display: <>{floatArray.map((val) => renderPrimitiveValue(val, true))}</> }] :
[{display: <EmptyValue />}];
case isArrayOfType(input, CWLType.FILE):
case isArrayOfType(input, CWLType.DIRECTORY):
const directories = (input as DirectoryArrayCommandInputParameter).value || [];
-
return directories.length ?
directories.map(directory => directoryToProcessIOValue(directory, auth, pdh)) :
[{display: <EmptyValue />}];
default:
- return [];
+ return [{display: <UnsupportedValue />}];
+ }
+};
+
+const renderPrimitiveValue = (value: any, asChip: boolean) => {
+ const isObject = typeof value === 'object';
+ if (!isObject) {
+ return asChip ? <Chip label={String(value)} /> : <pre>{String(value)}</pre>;
+ } else {
+ return asChip ? <UnsupportedValueChip /> : <UnsupportedValue />;
}
};
return basename ? (mime.getType(basename) || "").startsWith('image/') : false;
};
+const isFileUrl = (location?: string): boolean => (
+ !!location && !KEEP_URL_REGEX.exec(location) &&
+ (location.startsWith("http://") || location.startsWith("https://"))
+);
+
const normalizeDirectoryLocation = (directory: Directory): Directory => {
if (!directory.location) {
return directory;
};
const directoryToProcessIOValue = (directory: Directory, auth: AuthState, pdh?: string): ProcessIOValue => {
+ if (isExternalValue(directory)) {return {display: <UnsupportedValue />}}
+
const normalizedDirectory = normalizeDirectoryLocation(directory);
return {
display: <KeepUrlPath auth={auth} res={normalizedDirectory} pdh={pdh}/>,
};
const fileToProcessIOValue = (file: File, secondary: boolean, auth: AuthState, pdh: string | undefined, mainFilePdh: string): ProcessIOValue => {
+ if (isExternalValue(file)) {return {display: <UnsupportedValue />}}
+
+ if (isFileUrl(file.location)) {
+ return {
+ display: <MuiLink href={file.location} target="_blank">{file.location}</MuiLink>,
+ secondary,
+ };
+ }
+
const resourcePdh = getResourcePdhUrl(file, pdh);
return {
display: <KeepUrlPath auth={auth} res={file} pdh={pdh}/>,
}
};
+const isExternalValue = (val: any) =>
+ Object.keys(val).includes('$import') ||
+ Object.keys(val).includes('$include')
+
const EmptyValue = withStyles(styles)(
({classes}: WithStyles<CssRules>) => <span className={classes.emptyValue}>No value</span>
);
+const UnsupportedValue = withStyles(styles)(
+ ({classes}: WithStyles<CssRules>) => <span className={classes.emptyValue}>Cannot display value</span>
+);
+
+const UnsupportedValueChip = withStyles(styles)(
+ ({classes}: WithStyles<CssRules>) => <Chip icon={<InfoIcon />} label={"Cannot display value"} />
+);
+
const ImagePlaceholder = withStyles(styles)(
({classes}: WithStyles<CssRules>) => <span className={classes.imagePlaceholder}><ImageIcon /></span>
);