[ARVADOS-WORKBENCH2] created: 2.3.0-204-g0b2a5b15
Git user
git at public.arvados.org
Fri Mar 18 21:32:09 UTC 2022
at 0b2a5b15b1a2159591d50a683864a76f6fc58a00 (commit)
commit 0b2a5b15b1a2159591d50a683864a76f6fc58a00
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Fri Mar 18 18:31:42 2022 -0300
16672: Adds font size control to the log viewer.
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>
diff --git a/src/components/code-snippet/code-snippet.tsx b/src/components/code-snippet/code-snippet.tsx
index d0cf6ab3..6dc12401 100644
--- a/src/components/code-snippet/code-snippet.tsx
+++ b/src/components/code-snippet/code-snippet.tsx
@@ -25,17 +25,18 @@ export interface CodeSnippetDataProps {
className?: string;
apiResponse?: boolean;
containerClassName?: string;
+ fontSize?: number;
}
type CodeSnippetProps = CodeSnippetDataProps & WithStyles<CssRules>;
export const CodeSnippet = withStyles(styles)(
- ({ classes, lines, className, containerClassName, apiResponse }: CodeSnippetProps) =>
+ ({ classes, lines, className, containerClassName, apiResponse, fontSize }: CodeSnippetProps) =>
<Typography
component="div"
className={classNames(classes.root, containerClassName, className)}>
{ lines.map((line: string, index: number) => {
- return <Typography key={index} className={apiResponse ? classes.space : className} component="pre">{line}</Typography>;
+ return <Typography key={index} style={{ fontSize: fontSize }} className={apiResponse ? classes.space : className} component="pre">{line}</Typography>;
}) }
</Typography>
);
\ No newline at end of file
diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx
index 7fb74e81..557e22e7 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -66,6 +66,8 @@ import LinkOutlined from '@material-ui/icons/LinkOutlined';
import RemoveRedEye from '@material-ui/icons/RemoveRedEye';
import Computer from '@material-ui/icons/Computer';
import WrapText from '@material-ui/icons/WrapText';
+import TextIncrease from '@material-ui/icons/ZoomIn';
+import TextDecrease from '@material-ui/icons/ZoomOut';
// Import FontAwesome icons
import { library } from '@fortawesome/fontawesome-svg-core';
@@ -175,3 +177,5 @@ export const CanWriteIcon: IconType = (props) => <Edit {...props} />;
export const CanManageIcon: IconType = (props) => <Computer {...props} />;
export const AddUserIcon: IconType = (props) => <PersonAdd {...props} />;
export const WordWrapIcon: IconType = (props) => <WrapText {...props} />;
+export const TextIncreaseIcon: IconType = (props) => <TextIncrease {...props} />;
+export const TextDecreaseIcon: IconType = (props) => <TextDecrease {...props} />;
diff --git a/src/views/process-panel/process-log-card.tsx b/src/views/process-panel/process-log-card.tsx
index bbb4ff9d..56ac4d92 100644
--- a/src/views/process-panel/process-log-card.tsx
+++ b/src/views/process-panel/process-log-card.tsx
@@ -21,6 +21,8 @@ import {
CollectionIcon,
LogIcon,
MaximizeIcon,
+ TextDecreaseIcon,
+ TextIncreaseIcon,
WordWrapIcon
} from 'components/icon/icon';
import { Process } from 'store/processes/process';
@@ -87,6 +89,10 @@ export const ProcessLogsCard = withStyles(styles)(
({ classes, process, filters, selectedFilter, lines, onLogFilterChange, navigateToLog,
doHidePanel, doMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) => {
const [wordWrapToggle, setWordWrapToggle] = useState<boolean>(true);
+ const [fontSize, setFontSize] = useState<number>(3);
+ const fontBaseSize = 10;
+ const fontStepSize = 1;
+
return <Grid item className={classes.root} xs={12}>
<Card className={classes.card}>
<CardHeader className={classes.header}
@@ -96,6 +102,20 @@ export const ProcessLogsCard = withStyles(styles)(
<ProcessLogForm selectedFilter={selectedFilter}
filters={filters} onChange={onLogFilterChange} />
</Grid>
+ <Grid item>
+ <Tooltip title="Decrease font size" disableFocusListener>
+ <IconButton onClick={() => fontSize > 1 && setFontSize(fontSize-1)}>
+ <TextDecreaseIcon />
+ </IconButton>
+ </Tooltip>
+ </Grid>
+ <Grid item>
+ <Tooltip title="Increase font size" disableFocusListener>
+ <IconButton onClick={() => fontSize < 5 && setFontSize(fontSize+1)}>
+ <TextIncreaseIcon />
+ </IconButton>
+ </Tooltip>
+ </Grid>
<Grid item>
<Tooltip title="Toggle word wrapping" disableFocusListener>
<IconButton onClick={() => setWordWrapToggle(!wordWrapToggle)}>
@@ -133,7 +153,7 @@ export const ProcessLogsCard = withStyles(styles)(
spacing={24}
direction='column'>
<Grid className={classes.logViewer} item xs>
- <ProcessLogCodeSnippet wordWrap={wordWrapToggle} lines={lines} />
+ <ProcessLogCodeSnippet fontSize={fontBaseSize+(fontStepSize*fontSize)} wordWrap={wordWrapToggle} lines={lines} />
</Grid>
</Grid>
: <DefaultView
diff --git a/src/views/process-panel/process-log-code-snippet.tsx b/src/views/process-panel/process-log-code-snippet.tsx
index eb3ede6f..1ea83912 100644
--- a/src/views/process-panel/process-log-code-snippet.tsx
+++ b/src/views/process-panel/process-log-code-snippet.tsx
@@ -38,13 +38,14 @@ const theme = createMuiTheme({
interface ProcessLogCodeSnippetProps {
lines: string[];
+ fontSize: number;
wordWrap?: boolean;
}
export const ProcessLogCodeSnippet = withStyles(styles)(
(props: ProcessLogCodeSnippetProps & WithStyles<CssRules>) =>
<MuiThemeProvider theme={theme}>
- <CodeSnippet lines={props.lines}
+ <CodeSnippet lines={props.lines} fontSize={props.fontSize}
className={props.wordWrap ? props.classes.wordWrap : undefined}
containerClassName={props.classes.codeSnippetContainer} />
</MuiThemeProvider>);
\ No newline at end of file
commit 00e9a740ca3abec187faf4959cca6a6c84388704
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Fri Mar 18 17:51:17 2022 -0300
16672: Adds toggable word-wrapping to the log panel.
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 54b7bee6..7fb74e81 100644
--- a/src/components/icon/icon.tsx
+++ b/src/components/icon/icon.tsx
@@ -65,6 +65,7 @@ import VpnKey from '@material-ui/icons/VpnKey';
import LinkOutlined from '@material-ui/icons/LinkOutlined';
import RemoveRedEye from '@material-ui/icons/RemoveRedEye';
import Computer from '@material-ui/icons/Computer';
+import WrapText from '@material-ui/icons/WrapText';
// Import FontAwesome icons
import { library } from '@fortawesome/fontawesome-svg-core';
@@ -173,3 +174,4 @@ export const CanReadIcon: IconType = (props) => <RemoveRedEye {...props} />;
export const CanWriteIcon: IconType = (props) => <Edit {...props} />;
export const CanManageIcon: IconType = (props) => <Computer {...props} />;
export const AddUserIcon: IconType = (props) => <PersonAdd {...props} />;
+export const WordWrapIcon: IconType = (props) => <WrapText {...props} />;
diff --git a/src/views/process-panel/process-log-card.tsx b/src/views/process-panel/process-log-card.tsx
index bd700bcd..bbb4ff9d 100644
--- a/src/views/process-panel/process-log-card.tsx
+++ b/src/views/process-panel/process-log-card.tsx
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import React from 'react';
+import React, { useState } from 'react';
import {
StyleRulesCallback,
WithStyles,
@@ -20,7 +20,8 @@ import {
CloseIcon,
CollectionIcon,
LogIcon,
- MaximizeIcon
+ MaximizeIcon,
+ WordWrapIcon
} from 'components/icon/icon';
import { Process } from 'store/processes/process';
import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
@@ -84,8 +85,9 @@ type ProcessLogsCardProps = ProcessLogsCardDataProps
export const ProcessLogsCard = withStyles(styles)(
({ classes, process, filters, selectedFilter, lines, onLogFilterChange, navigateToLog,
- doHidePanel, doMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) =>
- <Grid item className={classes.root} xs={12}>
+ doHidePanel, doMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) => {
+ const [wordWrapToggle, setWordWrapToggle] = useState<boolean>(true);
+ return <Grid item className={classes.root} xs={12}>
<Card className={classes.card}>
<CardHeader className={classes.header}
avatar={<LogIcon className={classes.iconHeader} />}
@@ -94,6 +96,13 @@ export const ProcessLogsCard = withStyles(styles)(
<ProcessLogForm selectedFilter={selectedFilter}
filters={filters} onChange={onLogFilterChange} />
</Grid>
+ <Grid item>
+ <Tooltip title="Toggle word wrapping" disableFocusListener>
+ <IconButton onClick={() => setWordWrapToggle(!wordWrapToggle)}>
+ <WordWrapIcon />
+ </IconButton>
+ </Tooltip>
+ </Grid>
<Grid item>
<Tooltip title="Go to Log collection" disableFocusListener>
<IconButton onClick={() => navigateToLog(process.containerRequest.logUuid!)}>
@@ -124,7 +133,7 @@ export const ProcessLogsCard = withStyles(styles)(
spacing={24}
direction='column'>
<Grid className={classes.logViewer} item xs>
- <ProcessLogCodeSnippet lines={lines} />
+ <ProcessLogCodeSnippet wordWrap={wordWrapToggle} lines={lines} />
</Grid>
</Grid>
: <DefaultView
@@ -134,5 +143,5 @@ export const ProcessLogsCard = withStyles(styles)(
</CardContent>
</Card>
</Grid >
-);
+});
diff --git a/src/views/process-panel/process-log-code-snippet.tsx b/src/views/process-panel/process-log-code-snippet.tsx
index 01f5ca1c..eb3ede6f 100644
--- a/src/views/process-panel/process-log-code-snippet.tsx
+++ b/src/views/process-panel/process-log-code-snippet.tsx
@@ -8,10 +8,11 @@ import { CodeSnippet } from 'components/code-snippet/code-snippet';
import grey from '@material-ui/core/colors/grey';
import { ArvadosTheme } from 'common/custom-theme';
-type CssRules = 'codeSnippet' | 'codeSnippetContainer';
+type CssRules = 'wordWrap' | 'codeSnippetContainer';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
- codeSnippet: {
+ wordWrap: {
+ whiteSpace: 'pre-wrap',
},
codeSnippetContainer: {
height: `calc(100% - ${theme.spacing.unit * 4}px)`, // so that horizontal scollbar is visible
@@ -37,11 +38,13 @@ const theme = createMuiTheme({
interface ProcessLogCodeSnippetProps {
lines: string[];
+ wordWrap?: boolean;
}
export const ProcessLogCodeSnippet = withStyles(styles)(
(props: ProcessLogCodeSnippetProps & WithStyles<CssRules>) =>
<MuiThemeProvider theme={theme}>
- <CodeSnippet lines={props.lines} className={props.classes.codeSnippet}
+ <CodeSnippet lines={props.lines}
+ className={props.wordWrap ? props.classes.wordWrap : undefined}
containerClassName={props.classes.codeSnippetContainer} />
</MuiThemeProvider>);
\ No newline at end of file
commit dec4bfd5ffe77240544059f443fc9952301e53ea
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Fri Mar 18 17:33:05 2022 -0300
16672: Adds new prop to MPVContent: max height when not maximized.
This applies to the Log panel so that it doesn't take the whole vertical
space available when sharing the UI with other 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 b242f805..48241c0b 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -33,7 +33,6 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
},
content: {
overflow: 'auto',
- height: '100%',
},
});
@@ -66,6 +65,7 @@ interface MPVPanelDataProps {
panelIlluminated?: boolean;
panelRef?: MutableRefObject<any>;
forwardProps?: boolean;
+ maxHeight?: string;
}
interface MPVPanelActionProps {
@@ -79,14 +79,23 @@ 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, doMaximizePanel, panelName, panelMaximized, panelIlluminated, panelRef, forwardProps, ...props}: MPVPanelContentProps) => {
+export const MPVPanelContent = ({doHidePanel, doMaximizePanel, panelName,
+ panelMaximized, panelIlluminated, panelRef, forwardProps, maxHeight,
+ ...props}: MPVPanelContentProps) => {
useEffect(() => {
if (panelRef && panelRef.current) {
panelRef.current.scrollIntoView({behavior: 'smooth'});
}
}, [panelRef]);
- return <Grid item style={{height: '100%'}} {...props}>
+ // If maxHeight is set, only apply it when not maximized
+ const mh = maxHeight
+ ? panelMaximized
+ ? '100%'
+ : maxHeight
+ : undefined;
+
+ return <Grid item style={{maxHeight: mh}} {...props}>
<span ref={panelRef} /> {/* Element to scroll to when the panel is selected */}
<Paper style={{height: '100%'}} elevation={panelIlluminated ? 8 : 0}>
{ forwardProps
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index cf32b50f..78c79f8f 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -70,7 +70,7 @@ export const ProcessPanelRoot = withStyles(styles)(
<MPVPanelContent forwardProps xs="auto">
<ProcessDetailsCard process={process} />
</MPVPanelContent>
- <MPVPanelContent forwardProps xs>
+ <MPVPanelContent forwardProps xs maxHeight='50%'>
<ProcessLogsCard
process={process}
lines={getProcessPanelLogs(processLogsPanel)}
commit c7c3e46b08dacd4f06bd2117ce7accde21f55ac8
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Thu Mar 17 18:19:01 2022 -0300
16672: Fixes panels' vertical space layout issues.
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>
diff --git a/src/components/code-snippet/code-snippet.tsx b/src/components/code-snippet/code-snippet.tsx
index f0a2b213..d0cf6ab3 100644
--- a/src/components/code-snippet/code-snippet.tsx
+++ b/src/components/code-snippet/code-snippet.tsx
@@ -24,19 +24,18 @@ export interface CodeSnippetDataProps {
lines: string[];
className?: string;
apiResponse?: boolean;
+ containerClassName?: string;
}
type CodeSnippetProps = CodeSnippetDataProps & WithStyles<CssRules>;
export const CodeSnippet = withStyles(styles)(
- ({ classes, lines, className, apiResponse }: CodeSnippetProps) =>
+ ({ classes, lines, className, containerClassName, apiResponse }: CodeSnippetProps) =>
<Typography
- component="div"
- className={classNames(classes.root, className)}>
- {
- lines.map((line: string, index: number) => {
+ component="div"
+ className={classNames(classes.root, containerClassName, className)}>
+ { lines.map((line: string, index: number) => {
return <Typography key={index} className={apiResponse ? classes.space : className} component="pre">{line}</Typography>;
- })
- }
+ }) }
</Typography>
);
\ 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 6fb3cc49..b242f805 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -33,6 +33,7 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
},
content: {
overflow: 'auto',
+ height: '100%',
},
});
@@ -85,7 +86,7 @@ export const MPVPanelContent = ({doHidePanel, doMaximizePanel, panelName, panelM
}
}, [panelRef]);
- return <Grid item {...props}>
+ return <Grid item style={{height: '100%'}} {...props}>
<span ref={panelRef} /> {/* Element to scroll to when the panel is selected */}
<Paper style={{height: '100%'}} elevation={panelIlluminated ? 8 : 0}>
{ forwardProps
diff --git a/src/views/process-panel/process-details-card.tsx b/src/views/process-panel/process-details-card.tsx
index 18610781..d3349c3a 100644
--- a/src/views/process-panel/process-details-card.tsx
+++ b/src/views/process-panel/process-details-card.tsx
@@ -19,12 +19,16 @@ 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';
+type CssRules = 'card' | 'content' | 'title' | 'header';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
card: {
height: '100%'
},
+ header: {
+ paddingTop: theme.spacing.unit,
+ paddingBottom: theme.spacing.unit,
+ },
content: {
'&:last-child': {
paddingBottom: theme.spacing.unit * 2,
@@ -46,6 +50,7 @@ export const ProcessDetailsCard = withStyles(styles)(
({ classes, process, doHidePanel, panelName }: ProcessDetailsCardProps) => {
return <Card className={classes.card}>
<CardHeader
+ className={classes.header}
classes={{
content: classes.title,
}}
diff --git a/src/views/process-panel/process-information-card.tsx b/src/views/process-panel/process-information-card.tsx
index 4c938017..fc34a31c 100644
--- a/src/views/process-panel/process-information-card.tsx
+++ b/src/views/process-panel/process-information-card.tsx
@@ -17,12 +17,16 @@ 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';
+type CssRules = 'card' | 'iconHeader' | 'label' | 'value' | 'chip' | 'link' | 'content' | 'title' | 'avatar' | 'cancelButton' | 'header';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
card: {
height: '100%'
},
+ header: {
+ paddingTop: theme.spacing.unit,
+ paddingBottom: theme.spacing.unit,
+ },
iconHeader: {
fontSize: '1.875rem',
color: theme.customs.colors.green700,
@@ -93,6 +97,7 @@ export const ProcessInformationCard = withStyles(styles, { withTheme: true })(
const finishedAt = container ? formatDate(container.finishedAt) : 'N/A';
return <Card className={classes.card}>
<CardHeader
+ className={classes.header}
classes={{
content: classes.title,
avatar: classes.avatar
diff --git a/src/views/process-panel/process-log-card.tsx b/src/views/process-panel/process-log-card.tsx
index b87bb6e4..bd700bcd 100644
--- a/src/views/process-panel/process-log-card.tsx
+++ b/src/views/process-panel/process-log-card.tsx
@@ -32,16 +32,25 @@ import { ProcessLogCodeSnippet } from 'views/process-panel/process-log-code-snip
import { DefaultView } from 'components/default-view/default-view';
import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
-type CssRules = 'card' | 'content' | 'title' | 'iconHeader';
+type CssRules = 'card' | 'content' | 'title' | 'iconHeader' | 'header' | 'root' | 'logViewer' | 'logViewerContainer';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
card: {
height: '100%'
},
+ header: {
+ paddingTop: theme.spacing.unit,
+ paddingBottom: theme.spacing.unit,
+ },
content: {
- '&:last-child': {
- paddingBottom: theme.spacing.unit * 2,
- }
+ padding: theme.spacing.unit * 0,
+ height: '100%',
+ },
+ logViewer: {
+ height: '100%',
+ },
+ logViewerContainer: {
+ height: '100%',
},
title: {
overflow: 'hidden',
@@ -51,6 +60,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
fontSize: '1.875rem',
color: theme.customs.colors.green700
},
+ root: {
+ height: '100%',
+ },
});
export interface ProcessLogsCardDataProps {
@@ -73,9 +85,9 @@ type ProcessLogsCardProps = ProcessLogsCardDataProps
export const ProcessLogsCard = withStyles(styles)(
({ classes, process, filters, selectedFilter, lines, onLogFilterChange, navigateToLog,
doHidePanel, doMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) =>
- <Grid item xs={12}>
+ <Grid item className={classes.root} xs={12}>
<Card className={classes.card}>
- <CardHeader
+ <CardHeader className={classes.header}
avatar={<LogIcon className={classes.iconHeader} />}
action={<Grid container direction='row' alignItems='center'>
<Grid item>
@@ -107,10 +119,11 @@ export const ProcessLogsCard = withStyles(styles)(
<CardContent className={classes.content}>
{lines.length > 0
? < Grid
+ className={classes.logViewerContainer}
container
spacing={24}
direction='column'>
- <Grid item xs>
+ <Grid className={classes.logViewer} item xs>
<ProcessLogCodeSnippet lines={lines} />
</Grid>
</Grid>
diff --git a/src/views/process-panel/process-log-code-snippet.tsx b/src/views/process-panel/process-log-code-snippet.tsx
index d1756c77..01f5ca1c 100644
--- a/src/views/process-panel/process-log-code-snippet.tsx
+++ b/src/views/process-panel/process-log-code-snippet.tsx
@@ -6,13 +6,16 @@ import React from 'react';
import { MuiThemeProvider, createMuiTheme, StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core/styles';
import { CodeSnippet } from 'components/code-snippet/code-snippet';
import grey from '@material-ui/core/colors/grey';
+import { ArvadosTheme } from 'common/custom-theme';
-type CssRules = 'codeSnippet';
+type CssRules = 'codeSnippet' | 'codeSnippetContainer';
-const styles: StyleRulesCallback<CssRules> = () => ({
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
codeSnippet: {
- maxHeight: '550px',
- }
+ },
+ codeSnippetContainer: {
+ height: `calc(100% - ${theme.spacing.unit * 4}px)`, // so that horizontal scollbar is visible
+ },
});
const theme = createMuiTheme({
@@ -39,5 +42,6 @@ interface ProcessLogCodeSnippetProps {
export const ProcessLogCodeSnippet = withStyles(styles)(
(props: ProcessLogCodeSnippetProps & WithStyles<CssRules>) =>
<MuiThemeProvider theme={theme}>
- <CodeSnippet lines={props.lines} className={props.classes.codeSnippet} />
+ <CodeSnippet lines={props.lines} className={props.classes.codeSnippet}
+ containerClassName={props.classes.codeSnippetContainer} />
</MuiThemeProvider>);
\ No newline at end of file
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index c3980648..cf32b50f 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -22,6 +22,7 @@ type CssRules = 'root';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
root: {
width: '100%',
+ height: '100%',
},
});
commit 3f648ad1e37b5322981a9943258ba0f26f0aa0af
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Thu Mar 17 13:54:33 2022 -0300
16672: Removes the old process logs panel & related code.
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>
diff --git a/src/routes/route-change-handlers.ts b/src/routes/route-change-handlers.ts
index 044a38bf..5e07e6e8 100644
--- a/src/routes/route-change-handlers.ts
+++ b/src/routes/route-change-handlers.ts
@@ -27,7 +27,6 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
const publicFavoritesMatch = Routes.matchPublicFavoritesRoute(pathname);
const trashMatch = Routes.matchTrashRoute(pathname);
const processMatch = Routes.matchProcessRoute(pathname);
- const processLogMatch = Routes.matchProcessLogRoute(pathname);
const repositoryMatch = Routes.matchRepositoriesRoute(pathname);
const searchResultsMatch = Routes.matchSearchResultsRoute(pathname);
const sharedWithMeMatch = Routes.matchSharedWithMeRoute(pathname);
@@ -71,8 +70,6 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => {
store.dispatch(WorkbenchActions.loadTrash());
} else if (processMatch) {
store.dispatch(WorkbenchActions.loadProcess(processMatch.params.id));
- } else if (processLogMatch) {
- store.dispatch(WorkbenchActions.loadProcessLog(processLogMatch.params.id));
} else if (rootMatch) {
store.dispatch(navigateToRootProject);
} else if (sharedWithMeMatch) {
diff --git a/src/routes/routes.ts b/src/routes/routes.ts
index 41c71f7c..d7257b51 100644
--- a/src/routes/routes.ts
+++ b/src/routes/routes.ts
@@ -25,7 +25,6 @@ export const Routes = {
PROCESSES: `/processes/:id(${RESOURCE_UUID_PATTERN})`,
FAVORITES: '/favorites',
TRASH: '/trash',
- PROCESS_LOGS: `/process-logs/:id(${RESOURCE_UUID_PATTERN})`,
REPOSITORIES: '/repositories',
SHARED_WITH_ME: '/shared-with-me',
RUN_PROCESS: '/run-process',
@@ -95,8 +94,6 @@ export const getNavUrl = (uuid: string, config: FederationConfig) => {
export const getProcessUrl = (uuid: string) => `/processes/${uuid}`;
-export const getProcessLogUrl = (uuid: string) => `/process-logs/${uuid}`;
-
export const getGroupUrl = (uuid: string) => `/group/${uuid}`;
export interface ResourceRouteParams {
@@ -124,9 +121,6 @@ export const matchCollectionRoute = (route: string) =>
export const matchProcessRoute = (route: string) =>
matchPath<ResourceRouteParams>(route, { path: Routes.PROCESSES });
-export const matchProcessLogRoute = (route: string) =>
- matchPath<ResourceRouteParams>(route, { path: Routes.PROCESS_LOGS });
-
export const matchSharedWithMeRoute = (route: string) =>
matchPath(route, { path: Routes.SHARED_WITH_ME });
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index 19cc36ae..49f56591 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -6,7 +6,7 @@ import { Dispatch, compose, AnyAction } from 'redux';
import { push } from "react-router-redux";
import { ResourceKind, extractUuidKind } from 'models/resource';
import { SidePanelTreeCategory } from '../side-panel-tree/side-panel-tree-actions';
-import { Routes, getProcessLogUrl, getGroupUrl, getNavUrl } from 'routes/routes';
+import { Routes, getGroupUrl, getNavUrl } from 'routes/routes';
import { RootState } from 'store/store';
import { ServiceRepository } from 'services/services';
import { pluginConfig } from 'plugins';
@@ -99,8 +99,6 @@ export const pushOrGoto = (url: string): AnyAction => {
};
-export const navigateToProcessLogs = compose(push, getProcessLogUrl);
-
export const navigateToRootProject = (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
navigateTo(SidePanelTreeCategory.PROJECTS)(dispatch, getState);
};
diff --git a/src/store/process-logs-panel/process-logs-panel.ts b/src/store/process-logs-panel/process-logs-panel.ts
index 74a18041..0ca5d679 100644
--- a/src/store/process-logs-panel/process-logs-panel.ts
+++ b/src/store/process-logs-panel/process-logs-panel.ts
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import { matchProcessLogRoute, matchProcessRoute } from 'routes/routes';
+import { matchProcessRoute } from 'routes/routes';
import { RouterState } from 'react-router-redux';
export interface ProcessLogsPanel {
@@ -21,6 +21,6 @@ export const getProcessPanelLogs = ({ selectedFilter, logs }: ProcessLogsPanel)
export const getProcessLogsPanelCurrentUuid = (router: RouterState) => {
const pathname = router.location ? router.location.pathname : '';
- const match = matchProcessLogRoute(pathname) || matchProcessRoute(pathname);
+ const match = matchProcessRoute(pathname);
return match ? match.params.id : undefined;
};
diff --git a/src/views-components/context-menu/action-sets/process-resource-action-set.ts b/src/views-components/context-menu/action-sets/process-resource-action-set.ts
index be2f82fd..55b2d31f 100644
--- a/src/views-components/context-menu/action-sets/process-resource-action-set.ts
+++ b/src/views-components/context-menu/action-sets/process-resource-action-set.ts
@@ -8,7 +8,7 @@ import { toggleFavorite } from "store/favorites/favorites-actions";
import {
RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon,
RemoveIcon, ReRunProcessIcon, InputIcon, OutputIcon, CommandIcon,
- LogIcon, AdvancedIcon
+ AdvancedIcon
} from "components/icon/icon";
import { favoritePanelActions } from "store/favorite-panel/favorite-panel-action";
import { openMoveProcessDialog } from 'store/processes/process-move-actions';
@@ -21,7 +21,6 @@ import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions";
import { openProcessInputDialog } from "store/processes/process-input-actions";
import { navigateToOutput } from "store/process-panel/process-panel-actions";
import { openProcessCommandDialog } from "store/processes/process-command-actions";
-import { navigateToProcessLogs } from "store/navigation/navigation-action";
import { openAdvancedTabDialog } from "store/advanced-tab/advanced-tab";
import { TogglePublicFavoriteAction } from "../actions/public-favorite-action";
import { togglePublicFavorite } from "store/public-favorites/public-favorites-actions";
@@ -77,13 +76,6 @@ export const readOnlyProcessResourceActionSet: ContextMenuActionSet = [[
dispatch<any>(openProcessCommandDialog(resource.uuid));
}
},
- {
- icon: LogIcon,
- name: "Log",
- execute: (dispatch, resource) => {
- dispatch<any>(navigateToProcessLogs(resource.uuid));
- }
- },
{
icon: DetailsIcon,
name: "View details",
diff --git a/src/views/process-log-panel/process-log-main-card.tsx b/src/views/process-log-panel/process-log-main-card.tsx
deleted file mode 100644
index aab44da4..00000000
--- a/src/views/process-log-panel/process-log-main-card.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-// 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,
- Grid,
- Typography,
- Tooltip
-} from '@material-ui/core';
-import { Process } from 'store/processes/process';
-import { ProcessLogCodeSnippet } from 'views/process-log-panel/process-log-code-snippet';
-import {
- ProcessLogForm,
- ProcessLogFormDataProps,
- ProcessLogFormActionProps
-} from 'views/process-log-panel/process-log-form';
-import { MoreOptionsIcon, LogIcon } from 'components/icon/icon';
-import { ArvadosTheme } from 'common/custom-theme';
-import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
-import { DefaultView } from 'components/default-view/default-view';
-
-type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
-
-const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
- backLink: {
- fontSize: '14px',
- fontWeight: 600,
- display: 'flex',
- alignItems: 'center',
- textDecoration: 'none',
- padding: theme.spacing.unit,
- color: theme.palette.grey["700"],
- },
- backIcon: {
- marginRight: theme.spacing.unit
- },
- card: {
- width: '100%'
- },
- title: {
- color: theme.palette.grey["700"]
- },
- iconHeader: {
- fontSize: '1.875rem',
- color: theme.customs.colors.green700
- },
- link: {
- fontSize: '0.875rem',
- color: theme.palette.primary.main,
- textAlign: 'right',
- '&:hover': {
- cursor: 'pointer'
- }
- }
-});
-
-interface ProcessLogMainCardDataProps {
- process: Process;
-}
-
-export interface ProcessLogMainCardActionProps {
- onContextMenu: (event: React.MouseEvent<any>, process: Process) => void;
- navigateToLogCollection: (uuid: string) => void;
-}
-
-export type ProcessLogMainCardProps = ProcessLogMainCardDataProps
- & ProcessLogMainCardActionProps
- & CodeSnippetDataProps
- & ProcessLogFormDataProps
- & ProcessLogFormActionProps;
-
-export const ProcessLogMainCard = withStyles(styles)(
- ({ classes, process, selectedFilter, filters, onChange, lines, onContextMenu, navigateToLogCollection }: ProcessLogMainCardProps & WithStyles<CssRules>) =>
- <Grid item xs={12}>
- <Card className={classes.card}>
- <CardHeader
- avatar={<LogIcon className={classes.iconHeader} />}
- action={
- <Tooltip title="More options" disableFocusListener>
- <IconButton onClick={event => onContextMenu(event, process)} aria-label="More options">
- <MoreOptionsIcon />
- </IconButton>
- </Tooltip>}
- title={
- <Typography noWrap variant='h6' className={classes.title}>
- Logs for {process.containerRequest.name}
- </Typography>}
- />
- <CardContent>
- {lines.length > 0
- ? < Grid
- container
- spacing={24}
- direction='column'>
- <Grid container item>
- <Grid item xs={6}>
- <ProcessLogForm selectedFilter={selectedFilter} filters={filters} onChange={onChange} />
- </Grid>
- <Grid item xs={6} className={classes.link}>
- <span onClick={() => navigateToLogCollection(process.containerRequest.logUuid!)}>
- Go to Log collection
- </span>
- </Grid>
- </Grid>
- <Grid item xs>
- <ProcessLogCodeSnippet lines={lines} />
- </Grid>
- </Grid>
- : <DefaultView
- icon={LogIcon}
- messages={['No logs yet']} />
- }
- </CardContent>
- </Card>
- </Grid >
-);
\ No newline at end of file
diff --git a/src/views/process-log-panel/process-log-panel-root.tsx b/src/views/process-log-panel/process-log-panel-root.tsx
deleted file mode 100644
index be043722..00000000
--- a/src/views/process-log-panel/process-log-panel-root.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-// 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 { Process } from 'store/processes/process';
-import { ProcessLogMainCard } from 'views/process-log-panel/process-log-main-card';
-import { ProcessLogFormDataProps, ProcessLogFormActionProps } from 'views/process-log-panel/process-log-form';
-import { DefaultView } from 'components/default-view/default-view';
-import { ProcessIcon } from 'components/icon/icon';
-import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
-import { ProcessLogMainCardActionProps } from './process-log-main-card';
-
-export type ProcessLogPanelRootDataProps = {
- process?: Process;
-} & ProcessLogFormDataProps & CodeSnippetDataProps;
-
-export type ProcessLogPanelRootActionProps = ProcessLogMainCardActionProps & ProcessLogFormActionProps;
-
-export type ProcessLogPanelRootProps = ProcessLogPanelRootDataProps & ProcessLogPanelRootActionProps;
-
-export const ProcessLogPanelRoot = (props: ProcessLogPanelRootProps) =>
- props.process
- ? <Grid container spacing={16}>
- <ProcessLogMainCard
- process={props.process}
- {...props} />
- </Grid>
- : <Grid container
- alignItems='center'
- justify='center'>
- <DefaultView
- icon={ProcessIcon}
- messages={['Process Log not found']} />
- </Grid>;
diff --git a/src/views/process-log-panel/process-log-panel.tsx b/src/views/process-log-panel/process-log-panel.tsx
deleted file mode 100644
index b11d1432..00000000
--- a/src/views/process-log-panel/process-log-panel.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { RootState } from 'store/store';
-import { connect } from 'react-redux';
-import { getProcess } from 'store/processes/process';
-import { Dispatch } from 'redux';
-import { openProcessContextMenu } from 'store/context-menu/context-menu-actions';
-import { ProcessLogPanelRootDataProps, ProcessLogPanelRootActionProps, ProcessLogPanelRoot } from './process-log-panel-root';
-import { getProcessPanelLogs } from 'store/process-logs-panel/process-logs-panel';
-import { setProcessLogsPanelFilter, navigateToLogCollection } from 'store/process-logs-panel/process-logs-panel-actions';
-import { getProcessLogsPanelCurrentUuid } from 'store/process-logs-panel/process-logs-panel';
-
-export interface Log {
- object_uuid: string;
- event_at: string;
- event_type: string;
- summary: string;
- properties: any;
-}
-
-export interface FilterOption {
- label: string;
- value: string;
-}
-
-const mapStateToProps = ({resources, processLogsPanel, router}: RootState): ProcessLogPanelRootDataProps => {
- const uuid = getProcessLogsPanelCurrentUuid(router) || '';
- return {
- process: getProcess(uuid)(resources),
- selectedFilter: { label: processLogsPanel.selectedFilter, value: processLogsPanel.selectedFilter },
- filters: processLogsPanel.filters.map(filter => ({ label: filter, value: filter })),
- lines: getProcessPanelLogs(processLogsPanel)
- };
-};
-
-const mapDispatchToProps = (dispatch: Dispatch): ProcessLogPanelRootActionProps => ({
- onContextMenu: (event, process) => {
- dispatch<any>(openProcessContextMenu(event, process));
- },
- onChange: filter => {
- dispatch(setProcessLogsPanelFilter(filter.value));
- },
- navigateToLogCollection: (uuid: string) => {
- dispatch<any>(navigateToLogCollection(uuid));
- }
-});
-
-export const ProcessLogPanel = connect(mapStateToProps, mapDispatchToProps)(ProcessLogPanelRoot);
diff --git a/src/views/process-panel/process-log-card.tsx b/src/views/process-panel/process-log-card.tsx
index 85195d40..b87bb6e4 100644
--- a/src/views/process-panel/process-log-card.tsx
+++ b/src/views/process-panel/process-log-card.tsx
@@ -24,9 +24,11 @@ import {
} from 'components/icon/icon';
import { Process } from 'store/processes/process';
import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
-import { FilterOption } from 'views/process-log-panel/process-log-panel';
-import { ProcessLogForm } from 'views/process-log-panel/process-log-form';
-import { ProcessLogCodeSnippet } from 'views/process-log-panel/process-log-code-snippet';
+import {
+ FilterOption,
+ ProcessLogForm
+} from 'views/process-panel/process-log-form';
+import { ProcessLogCodeSnippet } from 'views/process-panel/process-log-code-snippet';
import { DefaultView } from 'components/default-view/default-view';
import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
diff --git a/src/views/process-log-panel/process-log-code-snippet.tsx b/src/views/process-panel/process-log-code-snippet.tsx
similarity index 100%
rename from src/views/process-log-panel/process-log-code-snippet.tsx
rename to src/views/process-panel/process-log-code-snippet.tsx
diff --git a/src/views/process-log-panel/process-log-form.tsx b/src/views/process-panel/process-log-form.tsx
similarity index 95%
rename from src/views/process-log-panel/process-log-form.tsx
rename to src/views/process-panel/process-log-form.tsx
index 7f98c978..6a8e5221 100644
--- a/src/views/process-log-panel/process-log-form.tsx
+++ b/src/views/process-panel/process-log-form.tsx
@@ -13,7 +13,6 @@ import {
Input
} from '@material-ui/core';
import { ArvadosTheme } from 'common/custom-theme';
-import { FilterOption } from './process-log-panel';
type CssRules = 'formControl';
@@ -23,6 +22,11 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
}
});
+export interface FilterOption {
+ label: string;
+ value: string;
+}
+
export interface ProcessLogFormDataProps {
selectedFilter: FilterOption;
filters: FilterOption[];
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index 2bd115f1..c3980648 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -13,9 +13,9 @@ import { SubprocessFilterDataProps } from 'components/subprocess-filter/subproce
import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
import { ArvadosTheme } from 'common/custom-theme';
import { ProcessDetailsCard } from './process-details-card';
-import { FilterOption } from 'views/process-log-panel/process-log-panel';
import { getProcessPanelLogs, ProcessLogsPanel } from 'store/process-logs-panel/process-logs-panel';
import { ProcessLogsCard } from './process-log-card';
+import { FilterOption } from 'views/process-panel/process-log-form';
type CssRules = 'root';
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 49922202..fe97bd3b 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -20,7 +20,6 @@ import { MultipleFilesRemoveDialog } from 'views-components/file-remove-dialog/m
import { Routes } from 'routes/routes';
import { SidePanel } from 'views-components/side-panel/side-panel';
import { ProcessPanel } from 'views/process-panel/process-panel';
-import { ProcessLogPanel } from 'views/process-log-panel/process-log-panel';
import { ChangeWorkflowDialog } from 'views-components/run-process-dialog/change-workflow-dialog';
import { CreateProjectDialog } from 'views-components/dialog-forms/create-project-dialog';
import { CreateCollectionDialog } from 'views-components/dialog-forms/create-collection-dialog';
@@ -158,7 +157,6 @@ let routes = <>
<Route path={Routes.ALL_PROCESSES} component={AllProcessesPanel} />
<Route path={Routes.PROCESSES} component={ProcessPanel} />
<Route path={Routes.TRASH} component={TrashPanel} />
- <Route path={Routes.PROCESS_LOGS} component={ProcessLogPanel} />
<Route path={Routes.SHARED_WITH_ME} component={SharedWithMePanel} />
<Route path={Routes.RUN_PROCESS} component={RunProcessPanel} />
<Route path={Routes.WORKFLOWS} component={WorkflowPanel} />
commit b00d3a5a0dc69ffaf0016b049fb2b5d6e6ce0d06
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Thu Mar 17 13:29:17 2022 -0300
16672: Process log card fully implemented in process panel.
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>
diff --git a/src/views/process-log-panel/process-log-form.tsx b/src/views/process-log-panel/process-log-form.tsx
index 946e575e..7f98c978 100644
--- a/src/views/process-log-panel/process-log-form.tsx
+++ b/src/views/process-log-panel/process-log-form.tsx
@@ -3,7 +3,15 @@
// SPDX-License-Identifier: AGPL-3.0
import React from 'react';
-import { withStyles, WithStyles, StyleRulesCallback, FormControl, InputLabel, Select, MenuItem, Input } from '@material-ui/core';
+import {
+ withStyles,
+ WithStyles,
+ StyleRulesCallback,
+ FormControl,
+ Select,
+ MenuItem,
+ Input
+} from '@material-ui/core';
import { ArvadosTheme } from 'common/custom-theme';
import { FilterOption } from './process-log-panel';
@@ -11,7 +19,7 @@ type CssRules = 'formControl';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
formControl: {
- minWidth: 200
+ minWidth: theme.spacing.unit * 15,
}
});
@@ -30,9 +38,6 @@ export const ProcessLogForm = withStyles(styles)(
({ classes, selectedFilter, onChange, filters }: ProcessLogFormProps) =>
<form autoComplete="off">
<FormControl className={classes.formControl}>
- <InputLabel shrink htmlFor="log-label-placeholder">
- Event Type
- </InputLabel>
<Select
value={selectedFilter.value}
onChange={({ target }) => onChange({ label: target.innerText, value: target.value })}
diff --git a/src/views/process-panel/process-log-card.tsx b/src/views/process-panel/process-log-card.tsx
new file mode 100644
index 00000000..85195d40
--- /dev/null
+++ b/src/views/process-panel/process-log-card.tsx
@@ -0,0 +1,123 @@
+// 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,
+ Grid,
+ Typography,
+} from '@material-ui/core';
+import { ArvadosTheme } from 'common/custom-theme';
+import {
+ CloseIcon,
+ CollectionIcon,
+ LogIcon,
+ MaximizeIcon
+} from 'components/icon/icon';
+import { Process } from 'store/processes/process';
+import { MPVPanelProps } from 'components/multi-panel-view/multi-panel-view';
+import { FilterOption } from 'views/process-log-panel/process-log-panel';
+import { ProcessLogForm } from 'views/process-log-panel/process-log-form';
+import { ProcessLogCodeSnippet } from 'views/process-log-panel/process-log-code-snippet';
+import { DefaultView } from 'components/default-view/default-view';
+import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
+
+type CssRules = 'card' | 'content' | 'title' | 'iconHeader';
+
+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
+ },
+ iconHeader: {
+ fontSize: '1.875rem',
+ color: theme.customs.colors.green700
+ },
+});
+
+export interface ProcessLogsCardDataProps {
+ process: Process;
+ selectedFilter: FilterOption;
+ filters: FilterOption[];
+}
+
+export interface ProcessLogsCardActionProps {
+ onLogFilterChange: (filter: FilterOption) => void;
+ navigateToLog: (uuid: string) => void;
+}
+
+type ProcessLogsCardProps = ProcessLogsCardDataProps
+ & ProcessLogsCardActionProps
+ & CodeSnippetDataProps
+ & WithStyles<CssRules>
+ & MPVPanelProps;
+
+export const ProcessLogsCard = withStyles(styles)(
+ ({ classes, process, filters, selectedFilter, lines, onLogFilterChange, navigateToLog,
+ doHidePanel, doMaximizePanel, panelMaximized, panelName }: ProcessLogsCardProps) =>
+ <Grid item xs={12}>
+ <Card className={classes.card}>
+ <CardHeader
+ avatar={<LogIcon className={classes.iconHeader} />}
+ action={<Grid container direction='row' alignItems='center'>
+ <Grid item>
+ <ProcessLogForm selectedFilter={selectedFilter}
+ filters={filters} onChange={onLogFilterChange} />
+ </Grid>
+ <Grid item>
+ <Tooltip title="Go to Log collection" disableFocusListener>
+ <IconButton onClick={() => navigateToLog(process.containerRequest.logUuid!)}>
+ <CollectionIcon />
+ </IconButton>
+ </Tooltip>
+ </Grid>
+ { doMaximizePanel && !panelMaximized &&
+ <Tooltip title={`Maximize ${panelName || 'panel'}`} disableFocusListener>
+ <IconButton onClick={doMaximizePanel}><MaximizeIcon /></IconButton>
+ </Tooltip> }
+ { doHidePanel && <Grid item>
+ <Tooltip title={`Close ${panelName || 'panel'}`} disableFocusListener>
+ <IconButton onClick={doHidePanel}><CloseIcon /></IconButton>
+ </Tooltip>
+ </Grid> }
+ </Grid>}
+ title={
+ <Typography noWrap variant='h6' className={classes.title}>
+ Logs
+ </Typography>}
+ />
+ <CardContent className={classes.content}>
+ {lines.length > 0
+ ? < Grid
+ container
+ spacing={24}
+ direction='column'>
+ <Grid item xs>
+ <ProcessLogCodeSnippet lines={lines} />
+ </Grid>
+ </Grid>
+ : <DefaultView
+ icon={LogIcon}
+ messages={['No logs yet']} />
+ }
+ </CardContent>
+ </Card>
+ </Grid >
+);
+
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index 5a0b6b64..2bd115f1 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -14,8 +14,8 @@ import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-p
import { ArvadosTheme } from 'common/custom-theme';
import { ProcessDetailsCard } from './process-details-card';
import { FilterOption } from 'views/process-log-panel/process-log-panel';
-import { ProcessLogMainCard } from 'views/process-log-panel/process-log-main-card';
import { getProcessPanelLogs, ProcessLogsPanel } from 'store/process-logs-panel/process-logs-panel';
+import { ProcessLogsCard } from './process-log-card';
type CssRules = 'root';
@@ -69,8 +69,8 @@ export const ProcessPanelRoot = withStyles(styles)(
<MPVPanelContent forwardProps xs="auto">
<ProcessDetailsCard process={process} />
</MPVPanelContent>
- <MPVPanelContent xs="auto">
- <ProcessLogMainCard
+ <MPVPanelContent forwardProps xs>
+ <ProcessLogsCard
process={process}
lines={getProcessPanelLogs(processLogsPanel)}
selectedFilter={{
@@ -80,11 +80,8 @@ export const ProcessPanelRoot = withStyles(styles)(
filters={processLogsPanel.filters.map(
filter => ({ label: filter, value: filter })
)}
- onChange={props.onLogFilterChange}
- onContextMenu={function (event: any, process: Process): void {
- throw new Error('Function not implemented.');
- } }
- navigateToLogCollection={props.navigateToLog}
+ onLogFilterChange={props.onLogFilterChange}
+ navigateToLog={props.navigateToLog}
/>
</MPVPanelContent>
<MPVPanelContent forwardProps xs>
commit 34e639cc5c40d2814f31732eed47682fab45ef51
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Tue Mar 15 16:25:05 2022 -0300
16672: Initial Log viewer placement on process panel. WIP
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>
diff --git a/src/store/process-panel/process-panel-actions.ts b/src/store/process-panel/process-panel-actions.ts
index de114a3e..2e5fb6b9 100644
--- a/src/store/process-panel/process-panel-actions.ts
+++ b/src/store/process-panel/process-panel-actions.ts
@@ -13,6 +13,7 @@ import { snackbarActions } from 'store/snackbar/snackbar-actions';
import { SnackbarKind } from '../snackbar/snackbar-actions';
import { showWorkflowDetails } from 'store/workflow-panel/workflow-panel-actions';
import { loadSubprocessPanel } from "../subprocess-panel/subprocess-panel-actions";
+import { initProcessLogsPanel } from "store/process-logs-panel/process-logs-panel-actions";
export const processPanelActions = unionize({
SET_PROCESS_PANEL_CONTAINER_REQUEST_UUID: ofType<string>(),
@@ -29,6 +30,7 @@ export const loadProcessPanel = (uuid: string) =>
dispatch<ProcessPanelAction>(processPanelActions.SET_PROCESS_PANEL_CONTAINER_REQUEST_UUID(uuid));
dispatch<any>(loadProcess(uuid));
dispatch(initProcessPanelFilters);
+ dispatch<any>(initProcessLogsPanel(uuid));
dispatch<any>(loadSubprocessPanel());
};
diff --git a/src/views/process-log-panel/process-log-main-card.tsx b/src/views/process-log-panel/process-log-main-card.tsx
index e6d4091d..aab44da4 100644
--- a/src/views/process-log-panel/process-log-main-card.tsx
+++ b/src/views/process-log-panel/process-log-main-card.tsx
@@ -3,18 +3,28 @@
// SPDX-License-Identifier: AGPL-3.0
import React from 'react';
-import { Link } from 'react-router-dom';
import {
- StyleRulesCallback, WithStyles, withStyles, Card,
- CardHeader, IconButton, CardContent, Grid, Typography, Tooltip
+ StyleRulesCallback,
+ WithStyles,
+ withStyles,
+ Card,
+ CardHeader,
+ IconButton,
+ CardContent,
+ Grid,
+ Typography,
+ Tooltip
} from '@material-ui/core';
import { Process } from 'store/processes/process';
import { ProcessLogCodeSnippet } from 'views/process-log-panel/process-log-code-snippet';
-import { ProcessLogForm, ProcessLogFormDataProps, ProcessLogFormActionProps } from 'views/process-log-panel/process-log-form';
-import { MoreOptionsIcon, ProcessIcon } from 'components/icon/icon';
+import {
+ ProcessLogForm,
+ ProcessLogFormDataProps,
+ ProcessLogFormActionProps
+} from 'views/process-log-panel/process-log-form';
+import { MoreOptionsIcon, LogIcon } from 'components/icon/icon';
import { ArvadosTheme } from 'common/custom-theme';
import { CodeSnippetDataProps } from 'components/code-snippet/code-snippet';
-import { BackIcon } from 'components/icon/icon';
import { DefaultView } from 'components/default-view/default-view';
type CssRules = 'backLink' | 'backIcon' | 'card' | 'title' | 'iconHeader' | 'link';
@@ -52,7 +62,6 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
}
});
-
interface ProcessLogMainCardDataProps {
process: Process;
}
@@ -71,12 +80,9 @@ export type ProcessLogMainCardProps = ProcessLogMainCardDataProps
export const ProcessLogMainCard = withStyles(styles)(
({ classes, process, selectedFilter, filters, onChange, lines, onContextMenu, navigateToLogCollection }: ProcessLogMainCardProps & WithStyles<CssRules>) =>
<Grid item xs={12}>
- <Link to={`/processes/${process.containerRequest.uuid}`} className={classes.backLink}>
- <BackIcon className={classes.backIcon} /> BACK
- </Link>
<Card className={classes.card}>
<CardHeader
- avatar={<ProcessIcon className={classes.iconHeader} />}
+ avatar={<LogIcon className={classes.iconHeader} />}
action={
<Tooltip title="More options" disableFocusListener>
<IconButton onClick={event => onContextMenu(event, process)} aria-label="More options">
@@ -84,12 +90,10 @@ export const ProcessLogMainCard = withStyles(styles)(
</IconButton>
</Tooltip>}
title={
- <Tooltip title={process.containerRequest.name} placement="bottom-start">
- <Typography noWrap variant='h6' className={classes.title}>
- {process.containerRequest.name}
- </Typography>
- </Tooltip>}
- subheader={process.containerRequest.description} />
+ <Typography noWrap variant='h6' className={classes.title}>
+ Logs for {process.containerRequest.name}
+ </Typography>}
+ />
<CardContent>
{lines.length > 0
? < Grid
@@ -111,7 +115,7 @@ export const ProcessLogMainCard = withStyles(styles)(
</Grid>
</Grid>
: <DefaultView
- icon={ProcessIcon}
+ icon={LogIcon}
messages={['No logs yet']} />
}
</CardContent>
diff --git a/src/views/process-panel/process-panel-root.tsx b/src/views/process-panel/process-panel-root.tsx
index 6fb9c09d..5a0b6b64 100644
--- a/src/views/process-panel/process-panel-root.tsx
+++ b/src/views/process-panel/process-panel-root.tsx
@@ -13,6 +13,9 @@ import { SubprocessFilterDataProps } from 'components/subprocess-filter/subproce
import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
import { ArvadosTheme } from 'common/custom-theme';
import { ProcessDetailsCard } from './process-details-card';
+import { FilterOption } from 'views/process-log-panel/process-log-panel';
+import { ProcessLogMainCard } from 'views/process-log-panel/process-log-main-card';
+import { getProcessPanelLogs, ProcessLogsPanel } from 'store/process-logs-panel/process-logs-panel';
type CssRules = 'root';
@@ -26,6 +29,7 @@ export interface ProcessPanelRootDataProps {
process?: Process;
subprocesses: Array<Process>;
filters: Array<SubprocessFilterDataProps>;
+ processLogsPanel: ProcessLogsPanel;
}
export interface ProcessPanelRootActionProps {
@@ -35,6 +39,8 @@ export interface ProcessPanelRootActionProps {
navigateToOutput: (uuid: string) => void;
navigateToWorkflow: (uuid: string) => void;
cancelProcess: (uuid: string) => void;
+ onLogFilterChange: (filter: FilterOption) => void;
+ navigateToLog: (uuid: string) => void;
}
export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps & WithStyles<CssRules>;
@@ -42,10 +48,12 @@ export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRoot
const panelsData: MPVPanelState[] = [
{name: "Info"},
{name: "Details", visible: false},
+ {name: "Logs", visible: true},
{name: "Subprocesses"},
];
-export const ProcessPanelRoot = withStyles(styles)(({ process, ...props }: ProcessPanelRootProps) =>
+export const ProcessPanelRoot = withStyles(styles)(
+ ({ process, processLogsPanel, ...props }: ProcessPanelRootProps) =>
process
? <MPVContainer className={props.classes.root} spacing={8} panelStates={panelsData} justify-content="flex-start" direction="column" wrap="nowrap">
<MPVPanelContent forwardProps xs="auto">
@@ -61,6 +69,24 @@ export const ProcessPanelRoot = withStyles(styles)(({ process, ...props }: Proce
<MPVPanelContent forwardProps xs="auto">
<ProcessDetailsCard process={process} />
</MPVPanelContent>
+ <MPVPanelContent xs="auto">
+ <ProcessLogMainCard
+ process={process}
+ lines={getProcessPanelLogs(processLogsPanel)}
+ selectedFilter={{
+ label: processLogsPanel.selectedFilter,
+ value: processLogsPanel.selectedFilter
+ }}
+ filters={processLogsPanel.filters.map(
+ filter => ({ label: filter, value: filter })
+ )}
+ onChange={props.onLogFilterChange}
+ onContextMenu={function (event: any, process: Process): void {
+ throw new Error('Function not implemented.');
+ } }
+ navigateToLogCollection={props.navigateToLog}
+ />
+ </MPVPanelContent>
<MPVPanelContent forwardProps xs>
<SubprocessPanel />
</MPVPanelContent>
@@ -73,4 +99,3 @@ export const ProcessPanelRoot = withStyles(styles)(({ process, ...props }: Proce
icon={ProcessIcon}
messages={['Process not found']} />
</Grid>);
-
diff --git a/src/views/process-panel/process-panel.tsx b/src/views/process-panel/process-panel.tsx
index 27acc869..6dd02c9b 100644
--- a/src/views/process-panel/process-panel.tsx
+++ b/src/views/process-panel/process-panel.tsx
@@ -24,14 +24,16 @@ import {
} from 'store/process-panel/process-panel-actions';
import { openProcessInputDialog } from 'store/processes/process-input-actions';
import { cancelRunningWorkflow } from 'store/processes/processes-actions';
+import { navigateToLogCollection, setProcessLogsPanelFilter } from 'store/process-logs-panel/process-logs-panel-actions';
-const mapStateToProps = ({ router, resources, processPanel }: RootState): ProcessPanelRootDataProps => {
+const mapStateToProps = ({ router, resources, processPanel, processLogsPanel }: RootState): ProcessPanelRootDataProps => {
const uuid = getProcessPanelCurrentUuid(router) || '';
const subprocesses = getSubprocesses(uuid)(resources);
return {
process: getProcess(uuid)(resources),
subprocesses: subprocesses.filter(subprocess => processPanel.filters[getProcessStatus(subprocess)]),
filters: getFilters(processPanel, subprocesses),
+ processLogsPanel: processLogsPanel,
};
};
@@ -45,7 +47,9 @@ const mapDispatchToProps = (dispatch: Dispatch): ProcessPanelRootActionProps =>
openProcessInputDialog: (uuid) => dispatch<any>(openProcessInputDialog(uuid)),
navigateToOutput: (uuid) => dispatch<any>(navigateToOutput(uuid)),
navigateToWorkflow: (uuid) => dispatch<any>(openWorkflow(uuid)),
- cancelProcess: (uuid) => dispatch<any>(cancelRunningWorkflow(uuid))
+ cancelProcess: (uuid) => dispatch<any>(cancelRunningWorkflow(uuid)),
+ onLogFilterChange: (filter) => dispatch(setProcessLogsPanelFilter(filter.value)),
+ navigateToLog: (uuid) => dispatch<any>(navigateToLogCollection(uuid)),
});
const getFilters = (processPanel: ProcessPanelState, processes: Process[]) => {
commit 8f1bf53627bcbdda8ee4909ac0537e665dacd6dd
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Thu Mar 10 18:57:29 2022 -0300
16672: Rearranges code related to getting current process' UUID.
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-actions.ts b/src/store/process-logs-panel/process-logs-panel-actions.ts
index ecbd030b..f0caf052 100644
--- a/src/store/process-logs-panel/process-logs-panel-actions.ts
+++ b/src/store/process-logs-panel/process-logs-panel-actions.ts
@@ -45,7 +45,7 @@ export const initProcessLogsPanel = (processUuid: string) =>
export const addProcessLogsPanelItem = (message: ResourceEventMessage<{ text: string }>) =>
async (dispatch: Dispatch, getState: () => RootState, { logService }: ServiceRepository) => {
if (PROCESS_PANEL_LOG_EVENT_TYPES.indexOf(message.eventType) > -1) {
- const uuid = getProcessLogsPanelCurrentUuid(getState());
+ const uuid = getProcessLogsPanelCurrentUuid(getState().router);
if (uuid) {
const process = getProcess(uuid)(getState().resources);
if (process) {
diff --git a/src/store/process-logs-panel/process-logs-panel.ts b/src/store/process-logs-panel/process-logs-panel.ts
index 87b50bd2..74a18041 100644
--- a/src/store/process-logs-panel/process-logs-panel.ts
+++ b/src/store/process-logs-panel/process-logs-panel.ts
@@ -2,8 +2,8 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import { RootState } from '../store';
import { matchProcessLogRoute, matchProcessRoute } from 'routes/routes';
+import { RouterState } from 'react-router-redux';
export interface ProcessLogsPanel {
filters: string[];
@@ -19,7 +19,7 @@ export const getProcessPanelLogs = ({ selectedFilter, logs }: ProcessLogsPanel)
return logs[selectedFilter];
};
-export const getProcessLogsPanelCurrentUuid = ({ router }: RootState) => {
+export const getProcessLogsPanelCurrentUuid = (router: RouterState) => {
const pathname = router.location ? router.location.pathname : '';
const match = matchProcessLogRoute(pathname) || matchProcessRoute(pathname);
return match ? match.params.id : undefined;
diff --git a/src/store/process-panel/process-panel.ts b/src/store/process-panel/process-panel.ts
index 935cfa58..49c2691d 100644
--- a/src/store/process-panel/process-panel.ts
+++ b/src/store/process-panel/process-panel.ts
@@ -2,7 +2,16 @@
//
// SPDX-License-Identifier: AGPL-3.0
+import { RouterState } from "react-router-redux";
+import { matchProcessRoute } from "routes/routes";
+
export interface ProcessPanel {
containerRequestUuid: string;
filters: { [status: string]: boolean };
}
+
+export const getProcessPanelCurrentUuid = (router: RouterState) => {
+ const pathname = router.location ? router.location.pathname : '';
+ const match = matchProcessRoute(pathname);
+ return match ? match.params.id : undefined;
+};
\ No newline at end of file
diff --git a/src/views/process-log-panel/process-log-panel.tsx b/src/views/process-log-panel/process-log-panel.tsx
index 9f61f8b6..b11d1432 100644
--- a/src/views/process-log-panel/process-log-panel.tsx
+++ b/src/views/process-log-panel/process-log-panel.tsx
@@ -25,9 +25,8 @@ export interface FilterOption {
value: string;
}
-const mapStateToProps = (state: RootState): ProcessLogPanelRootDataProps => {
- const { resources, processLogsPanel } = state;
- const uuid = getProcessLogsPanelCurrentUuid(state) || '';
+const mapStateToProps = ({resources, processLogsPanel, router}: RootState): ProcessLogPanelRootDataProps => {
+ const uuid = getProcessLogsPanelCurrentUuid(router) || '';
return {
process: getProcess(uuid)(resources),
selectedFilter: { label: processLogsPanel.selectedFilter, value: processLogsPanel.selectedFilter },
diff --git a/src/views/process-panel/process-panel.tsx b/src/views/process-panel/process-panel.tsx
index 3364a8d6..27acc869 100644
--- a/src/views/process-panel/process-panel.tsx
+++ b/src/views/process-panel/process-panel.tsx
@@ -7,18 +7,26 @@ import { connect } from 'react-redux';
import { getProcess, getSubprocesses, Process, getProcessStatus } from 'store/processes/process';
import { Dispatch } from 'redux';
import { openProcessContextMenu } from 'store/context-menu/context-menu-actions';
-import { matchProcessRoute } from 'routes/routes';
-import { ProcessPanelRootDataProps, ProcessPanelRootActionProps, ProcessPanelRoot } from './process-panel-root';
-import { ProcessPanel as ProcessPanelState} from 'store/process-panel/process-panel';
+import {
+ ProcessPanelRootDataProps,
+ ProcessPanelRootActionProps,
+ ProcessPanelRoot
+} from './process-panel-root';
+import {
+ getProcessPanelCurrentUuid,
+ ProcessPanel as ProcessPanelState
+} from 'store/process-panel/process-panel';
import { groupBy } from 'lodash';
-import { toggleProcessPanelFilter, navigateToOutput, openWorkflow } from 'store/process-panel/process-panel-actions';
+import {
+ toggleProcessPanelFilter,
+ navigateToOutput,
+ openWorkflow
+} from 'store/process-panel/process-panel-actions';
import { openProcessInputDialog } from 'store/processes/process-input-actions';
import { cancelRunningWorkflow } from 'store/processes/processes-actions';
const mapStateToProps = ({ router, resources, processPanel }: RootState): ProcessPanelRootDataProps => {
- const pathname = router.location ? router.location.pathname : '';
- const match = matchProcessRoute(pathname);
- const uuid = match ? match.params.id : '';
+ const uuid = getProcessPanelCurrentUuid(router) || '';
const subprocesses = getSubprocesses(uuid)(resources);
return {
process: getProcess(uuid)(resources),
@@ -40,9 +48,7 @@ const mapDispatchToProps = (dispatch: Dispatch): ProcessPanelRootActionProps =>
cancelProcess: (uuid) => dispatch<any>(cancelRunningWorkflow(uuid))
});
-export const ProcessPanel = connect(mapStateToProps, mapDispatchToProps)(ProcessPanelRoot);
-
-export const getFilters = (processPanel: ProcessPanelState, processes: Process[]) => {
+const getFilters = (processPanel: ProcessPanelState, processes: Process[]) => {
const grouppedProcesses = groupBy(processes, getProcessStatus);
return Object
.keys(processPanel.filters)
@@ -52,4 +58,6 @@ export const getFilters = (processPanel: ProcessPanelState, processes: Process[]
checked: processPanel.filters[filter],
key: filter,
}));
- };
\ No newline at end of file
+ };
+
+export const ProcessPanel = connect(mapStateToProps, mapDispatchToProps)(ProcessPanelRoot);
commit 1d7eeeeb37f79887777439251cd07f1b3279c577
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date: Thu Mar 10 18:00:05 2022 -0300
16672: Improves UX by avoiding to instantly focus on a MPV panel.
100ms seems "instant enough" for cases where the user is really attempting
to use the multi-panel-view task bar. For other cases where the user
casually hovers through any of the buttons because is trying to reach
some different part of the UI, not making the UI "move" is a lot less
confusing.
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 185c3b90..6fb3cc49 100644
--- a/src/components/multi-panel-view/multi-panel-view.tsx
+++ b/src/components/multi-panel-view/multi-panel-view.tsx
@@ -112,7 +112,7 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
children = [children];
}
const visibility = (children as ReactNodeArray).map((_, idx) =>
- !!!panelStates || // if panelStates wasn't passed, default to all visible panels
+ !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);
@@ -159,13 +159,20 @@ const MPVContainerComponent = ({children, panelStates, classes, ...props}: MPVCo
const panelIsMaximized = panelVisibility[idx] &&
panelVisibility.filter(e => e).length === 1;
+ let brightenerTimer: NodeJS.Timer;
toggles = [
...toggles,
<Tooltip title={toggleTooltip} disableFocusListener>
<Button variant={toggleVariant} size="small" color="primary"
className={classNames(classes.button)}
- onMouseEnter={() => setBrightenedPanel(idx)}
- onMouseLeave={() => setBrightenedPanel(-1)}
+ onMouseEnter={() => {
+ brightenerTimer = setTimeout(
+ () => setBrightenedPanel(idx), 100);
+ }}
+ onMouseLeave={() => {
+ brightenerTimer && clearTimeout(brightenerTimer);
+ setBrightenedPanel(-1);
+ }}
onClick={showFn(idx)}>
{panelName}
{toggleIcon}
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list