🔧 type(bugfix): Fixed Editor for Verify & Commit and Tune Workflow page 🔨 (#2857)

* Added Updated Editor
* Replaced Editor in Verify & Commit Page
* Fixing Editor cursor
* Added validation to Editor

Signed-off-by: Sayan Mondal <sayan@chaosnative.com>
This commit is contained in:
Sayan Mondal 2021-06-02 13:15:04 +05:30 committed by GitHub
parent c638304960
commit 1b42462345
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 682 additions and 906 deletions

View File

@ -0,0 +1,12 @@
<svg width="39" height="38" viewBox="0 0 39 38" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.970215" width="37.9694" height="38" fill="#5B44BA"/>
<g clip-path="url(#clip0)">
<rect x="20.3857" y="26.1777" width="9.79495" height="1.48408" rx="0.742042" fill="white"/>
<path d="M10.4834 28.053C10.1742 27.7435 10.1742 27.2433 10.4834 26.9338L18.6237 18.7869L10.4834 10.6401C10.1742 10.3306 10.1742 9.83038 10.4834 9.52091C10.7926 9.21145 11.2924 9.21145 11.6016 9.52091L20.3011 18.2274C20.4553 18.3817 20.5328 18.5843 20.5328 18.787C20.5328 18.9896 20.4553 19.1922 20.3011 19.3466L11.6016 28.053C11.2924 28.3625 10.7926 28.3625 10.4834 28.053Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="21.9379" height="21.9556" fill="white" transform="translate(8.56396 8.44434)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 825 B

View File

@ -1,5 +1,6 @@
.root {
background: #f5f6f8;
font-family: 'Ubuntu', sans-serif;
}
.loaderContainer {

View File

@ -38,6 +38,7 @@
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap"
rel="stylesheet"
/>
<link href="https://fonts.googleapis.com/css2?family=Ubuntu&display=swap" rel="stylesheet">
<title>Litmus Portal</title>
</head>

View File

@ -1,6 +1,7 @@
/* eslint-disable no-param-reassign */
import { Box, Button, Typography, useTheme } from '@material-ui/core';
import Divider from '@material-ui/core/Divider';
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import { useTheme } from '@material-ui/core';
import Fade from '@material-ui/core/Fade';
import Tooltip from '@material-ui/core/Tooltip';
import ErrorTwoToneIcon from '@material-ui/icons/ErrorTwoTone';
@ -13,9 +14,9 @@ import SelectAllTwoToneIcon from '@material-ui/icons/SelectAllTwoTone';
import UndoTwoToneIcon from '@material-ui/icons/UndoTwoTone';
import UnfoldLessTwoToneIcon from '@material-ui/icons/UnfoldLessTwoTone';
import UnfoldMoreTwoToneIcon from '@material-ui/icons/UnfoldMoreTwoTone';
import FullscreenIcon from '@material-ui/icons/Fullscreen';
import React, { useEffect, useState } from 'react';
import AceEditor from 'react-ace';
import { useTranslation } from 'react-i18next';
import useStyles from './styles';
import { AceValidations, parseYamlValidations } from './Validations';
import 'ace-builds/src-min-noconflict/ext-beautify';
@ -39,7 +40,7 @@ import 'ace-builds/src-min-noconflict/ext-textarea';
import 'ace-builds/src-min-noconflict/ext-themelist';
import 'ace-builds/src-min-noconflict/ext-whitespace';
import 'brace/mode/yaml';
import 'brace/theme/cobalt';
import 'brace/theme/solarized_dark';
interface YamlEditorProps {
content: string;
@ -47,7 +48,6 @@ interface YamlEditorProps {
readOnly: boolean;
setButtonState?: (btnState: boolean) => void;
saveWorkflowChange?: (updatedManifest: string) => void;
className?: Object;
}
const YamlEditor: React.FC<YamlEditorProps> = ({
@ -56,23 +56,13 @@ const YamlEditor: React.FC<YamlEditorProps> = ({
readOnly,
setButtonState,
saveWorkflowChange,
className,
}) => {
const classes = useStyles();
const { palette } = useTheme();
const { t } = useTranslation();
const [isValid, setIsValid] = useState(true);
const [errors, setErrors] = useState({
errorLine: ' ',
errorPosition: ' ',
errorType: ' ',
errorInfo: ' ',
});
const [editorState, setEditorState] = React.useState({
const [editorState, setEditorState] = useState({
markers: [],
annotations: [],
content,
@ -94,12 +84,6 @@ const YamlEditor: React.FC<YamlEditorProps> = ({
};
if (stateObject.annotations.length > 0) {
setIsValid(false);
setErrors({
errorLine: (stateObject.annotations[0].row as unknown) as string,
errorPosition: (stateObject.annotations[0].column as unknown) as string,
errorType: stateObject.annotations[0].type as string,
errorInfo: stateObject.annotations[0].text as string,
});
const nodeStyleError = (document.getElementsByClassName(
'ace_gutter-cell'
)[stateObject.annotations[0].row - 1] as any).style;
@ -107,12 +91,6 @@ const YamlEditor: React.FC<YamlEditorProps> = ({
nodeStyleError.color = palette.secondary.contrastText;
} else {
setIsValid(true);
setErrors({
errorLine: ' ',
errorPosition: ' ',
errorType: ' ',
errorInfo: ' ',
});
const nodeStyleErrorList = document.getElementsByClassName(
'ace_gutter-cell'
);
@ -181,8 +159,8 @@ const YamlEditor: React.FC<YamlEditorProps> = ({
(YamlAce.current!.editor as any).execCommand('goToNextError');
};
const fullscreentrigger = () => {
const i: any = document.getElementById('resize-editor');
const fullScreenTrigger = () => {
const i: any = document.getElementById('yaml-editor');
(YamlAce.current!.editor as any).setOption(
'maxLines',
document.body.clientHeight
@ -199,31 +177,18 @@ const YamlEditor: React.FC<YamlEditorProps> = ({
};
useEffect(() => {
let editorValidations: AceValidations = {
markers: [],
annotations: [],
};
editorValidations = parseYamlValidations(content, classes);
const editorValidations: AceValidations = parseYamlValidations(
content,
classes
);
const stateObject = {
markers: editorValidations.markers,
annotations: editorValidations.annotations,
};
if (stateObject.annotations.length > 0) {
setIsValid(false);
setErrors({
errorLine: (stateObject.annotations[0].row as unknown) as string,
errorPosition: (stateObject.annotations[0].column as unknown) as string,
errorType: stateObject.annotations[0].type as string,
errorInfo: stateObject.annotations[0].text as string,
});
} else {
setIsValid(true);
setErrors({
errorLine: ' ',
errorPosition: ' ',
errorType: ' ',
errorInfo: ' ',
});
}
setEditorState(stateObject as any);
}, []);
@ -233,318 +198,232 @@ const YamlEditor: React.FC<YamlEditorProps> = ({
}, [isValid]);
return (
<div
className={`${classes.editorBackgroundFull} ${className}`}
id="editor"
data-cy="WorkflowEditor"
>
<Typography
className={
readOnly
? classes.statusHeadingInModal
: classes.statusHeadingOutModal
}
>
{t('editor.status')}
<Typography className={classes.saved} display="inline">
&nbsp; &nbsp;
<strong>
<span>
<Typography
className={
isValid ? classes.markStyleCorrect : classes.markStyleWrong
}
display="inline"
>
{isValid ? '\u2713' : '\u274C'}
</Typography>
</span>
<Typography
id="YamlStatus"
className={
isValid ? classes.markStyleCorrect : classes.markStyleWrong
}
display="inline"
>
&nbsp;
<strong>{isValid ? 'Correct' : 'Incorrect'}</strong>
</Typography>
</strong>
</Typography>
</Typography>
<Typography className={classes.statusDescription}>
{isValid
? ' '
: `Pay attention to Line ${errors.errorLine}'s ` +
` character ${errors.errorPosition}. Type: ${errors.errorType} -> ${errors.errorInfo}.`}
&nbsp;
{isValid
? 'Your code is fine. You can move on!'
: 'Correct this error and keep moving forward!'}
</Typography>
<div className={classes.widthManager}>
<Divider
variant="middle"
classes={{ root: classes.horizontalLineWhite }}
/>
<div id="editor" data-cy="WorkflowEditor">
<>
{!readOnly && (
<div className={classes.editorButtonGrid}>
<Tooltip
title="Undo"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonUndo}`}
onClick={startundo}
startIcon={<UndoTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={startundo}>
<UndoTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Redo"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={classes.editorButtons}
onClick={startredo}
startIcon={<RedoTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={startredo}>
<RedoTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Download"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonDownload}`}
onClick={downloadYamlFile}
startIcon={<GetAppTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={downloadYamlFile}>
<GetAppTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Copy"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonGotoCopyUnfold}`}
onClick={copycontent}
startIcon={<FileCopyTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={copycontent}>
<FileCopyTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Goto Error"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonGotoCopyUnfold}`}
<div
className={classes.editorButtons}
onClick={startgotonexterror}
startIcon={<ErrorTwoToneIcon />}
/>
>
<ErrorTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Find"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonFind}`}
onClick={startfinder}
startIcon={<FindInPageTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={startfinder}>
<FindInPageTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Replace"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonReplace}`}
onClick={startreplace}
startIcon={<FindReplaceTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={startreplace}>
<FindReplaceTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Unfold All"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonGotoCopyUnfold}`}
onClick={startunfoldall}
startIcon={<UnfoldMoreTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={startunfoldall}>
<UnfoldMoreTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Fold All"
placement="bottom"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonFold}`}
onClick={startfoldall}
startIcon={<UnfoldLessTwoToneIcon />}
/>
<div className={classes.editorButtons} onClick={startfoldall}>
<UnfoldLessTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Select"
placement="top"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<div className={classes.editorButtons} onClick={startselectall}>
<SelectAllTwoToneIcon />
</div>
</Tooltip>
<Tooltip
title="Full Screen (Press Escape to End)"
placement="bottom"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={`${classes.editorButtons} ${classes.editorButtonSelectAll}`}
onClick={startselectall}
startIcon={<SelectAllTwoToneIcon />}
/>
<div
className={classes.editorButtons}
onClick={fullScreenTrigger}
>
<FullscreenIcon />
</div>
</Tooltip>
</div>
)}
</div>
</>
<br />
<div className={classes.editor}>
<AceEditor
mode="yaml"
theme="cobalt"
name="code"
width="100%"
height="100%"
maxLines={12000}
minLines={1}
highlightActiveLine={false}
readOnly={readOnly}
tabSize={2}
wrapEnabled
ref={YamlAce}
showGutter
onChange={onEditorChange}
showPrintMargin={false}
enableBasicAutocompletion
enableSnippets
enableLiveAutocompletion
value={editorState.content}
editorProps={{
$blockScrolling: Infinity,
$useWorker: true,
}}
onLoad={(editor) => {
editor.setReadOnly(readOnly);
editor.setOptions({
fontFamily: 'monospace',
highlightGutterLine: false,
autoScrollEditorIntoView: true,
tooltipFollowsMouse: true,
displayIndentGuides: false,
});
editor.focus();
editor.setHighlightSelectedWord(true);
editor.session.setFoldStyle('markbeginend');
editor.setShowFoldWidgets(true);
editor.setAnimatedScroll(true);
editor.setShowInvisibles(false);
editor.setFontSize('0.98rem');
editor.container.style.background = palette.common.black;
editor.container.style.lineHeight = '160%';
const nodeStyle = (document.getElementsByClassName(
'ace_gutter'
)[0] as any).style;
nodeStyle.color = palette.secondary.contrastText;
nodeStyle.borderRight = 0;
nodeStyle.background = palette.common.black;
}}
onCursorChange={(selection) => {
(YamlAce.current!.editor as any).setOptions({
autoScrollEditorIntoView: true,
tooltipFollowsMouse: true,
});
<pre id="yaml-editor">
<AceEditor
mode="yaml"
theme="solarized_dark"
name="code"
width="100%"
height="100%"
maxLines={12000}
minLines={1}
highlightActiveLine={false}
readOnly={readOnly}
tabSize={2}
wrapEnabled
ref={YamlAce}
showGutter
onChange={onEditorChange}
showPrintMargin={false}
enableBasicAutocompletion
enableSnippets
enableLiveAutocompletion
value={editorState.content}
editorProps={{
$blockScrolling: Infinity,
$useWorker: true,
}}
onLoad={(editor) => {
editor.setReadOnly(readOnly);
editor.setOptions({
fontFamily: 'monospace',
highlightGutterLine: false,
autoScrollEditorIntoView: true,
tooltipFollowsMouse: true,
displayIndentGuides: false,
});
editor.focus();
editor.setHighlightSelectedWord(true);
editor.session.setFoldStyle('markbeginend');
editor.setShowFoldWidgets(true);
editor.setAnimatedScroll(true);
editor.setShowInvisibles(false);
editor.setFontSize('0.98rem');
editor.container.style.lineHeight = '160%';
const nodeStyle = (document.getElementsByClassName(
'ace_gutter'
)[0] as any).style;
nodeStyle.color = palette.secondary.contrastText;
nodeStyle.borderRight = 0;
}}
onCursorChange={(selection) => {
(YamlAce.current!.editor as any).setOptions({
autoScrollEditorIntoView: true,
tooltipFollowsMouse: true,
});
const nodeStyleActiveList = document.getElementsByClassName(
'ace_gutter-cell'
);
for (let i = 0; i < nodeStyleActiveList.length; i += 1) {
(nodeStyleActiveList[i] as any).style.backgroundColor =
palette.common.black;
(nodeStyleActiveList[i] as any).style.color =
palette.secondary.contrastText;
}
if (
document.getElementsByClassName('ace_gutter-cell')[
selection.cursor.row
] as any
) {
const nodeStyleActive = (document.getElementsByClassName(
const nodeStyleActiveList = document.getElementsByClassName(
'ace_gutter-cell'
)[selection.cursor.row] as any).style;
nodeStyleActive.backgroundColor = palette.primary.main;
nodeStyleActive.color = palette.secondary.contrastText;
}
}}
annotations={editorState.annotations}
markers={editorState.markers}
/>
<Box p={1} flexGrow={0} className={classes.fullScreenGrid}>
<Tooltip
title="Full Screen (Press Escape to End)"
placement="bottom"
TransitionComponent={Fade}
TransitionProps={{ timeout: 500 }}
arrow
>
<Button
variant="outlined"
className={classes.editorButtonFullScreen}
onClick={fullscreentrigger}
startIcon={
<img
src="/icons/fullscreen.svg"
alt="Full Screen"
color={palette.secondary.contrastText}
className={classes.fullScreenIcon}
/>
);
for (let i = 0; i < nodeStyleActiveList.length; i += 1) {
(nodeStyleActiveList[i] as any).style.backgroundColor =
'#01313F';
(nodeStyleActiveList[i] as any).style.color =
palette.secondary.contrastText;
}
/>
</Tooltip>
</Box>
if (
document.getElementsByClassName('ace_gutter-cell')[
selection.cursor.row
] as any
) {
const nodeStyleActive = (document.getElementsByClassName(
'ace_gutter-cell'
)[selection.cursor.row] as any).style;
nodeStyleActive.backgroundColor = palette.primary.main;
nodeStyleActive.color = palette.secondary.contrastText;
}
}}
annotations={editorState.annotations}
markers={editorState.markers}
/>
</pre>
</div>
</div>
);

View File

@ -3,59 +3,15 @@ import { makeStyles, Theme } from '@material-ui/core';
const useStyles = makeStyles((theme: Theme) => ({
// Editor
statusHeadingInModal: {
marginTop: theme.spacing(-2.5),
fontSize: '1rem',
marginLeft: theme.spacing(2.5),
fontWeight: 500,
lineHeight: '130%',
paddingTop: theme.spacing(-4),
},
statusHeadingOutModal: {
marginTop: theme.spacing(-2.5),
fontSize: '1rem',
marginLeft: theme.spacing(2.5),
fontWeight: 500,
lineHeight: '130%',
paddingTop: theme.spacing(4),
},
statusDescription: {
width: '95%',
marginTop: theme.spacing(1.875),
fontSize: '0.875rem',
marginLeft: theme.spacing(2),
marginRight: theme.spacing(2),
lineHeight: '160%',
},
editorBackgroundFull: {
backgroundColor: theme.palette.common.black,
color: theme.palette.secondary.contrastText,
width: '100%',
},
horizontalLineWhite: {
marginTop: theme.spacing(4),
backgroundColor: theme.palette.border.main,
},
widthManager: {
width: '98.5%',
},
editorButtonGrid: {
marginTop: theme.spacing(3),
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-between',
width: '95%',
margin: '0 auto',
},
editor: {
overflowY: 'auto',
margin: theme.spacing(2, 0),
height: '40vh',
height: '50vh',
'&::-webkit-scrollbar': {
width: '0.2em',
@ -66,186 +22,23 @@ const useStyles = makeStyles((theme: Theme) => ({
'&::-webkit-scrollbar-thumb': {
backgroundColor: theme.palette.primary.main,
},
'& #code': {
padding: '1rem 0',
},
'& #yaml-editor': {
overflowY: 'auto',
width: '100%',
height: '100%',
position: 'relative',
},
},
editorButtons: {
borderRadius: 3,
backgroundColor: theme.palette.common.black,
boxSizing: 'border-box',
color: theme.palette.secondary.contrastText,
borderColor: theme.palette.border.main,
paddingLeft: theme.spacing(3.125),
width: '4rem',
height: '2.75rem',
marginLeft: theme.spacing(1.25),
},
editorButtonGotoCopyUnfold: {
[theme.breakpoints.down('xs')]: {
marginLeft: 0,
marginTop: theme.spacing(1.5),
},
},
editorButtonFind: {
[theme.breakpoints.down('sm')]: {
marginLeft: theme.spacing(4.25),
},
[theme.breakpoints.down('xs')]: {
marginLeft: theme.spacing(0.1),
marginTop: theme.spacing(1.5),
},
},
editorButtonFold: {
[theme.breakpoints.down('xs')]: {
marginLeft: theme.spacing(2.5),
marginTop: theme.spacing(1.5),
},
},
editorButtonUndo: {
marginLeft: theme.spacing(2.5),
[theme.breakpoints.down('sm')]: {
marginLeft: theme.spacing(4.25),
},
[theme.breakpoints.down('xs')]: {
marginLeft: theme.spacing(0.1),
},
},
editorButtonDownload: {
marginLeft: theme.spacing(6.25),
[theme.breakpoints.down('sm')]: {
marginLeft: theme.spacing(1.5),
},
[theme.breakpoints.down('md')]: {
marginLeft: theme.spacing(1),
},
},
editorButtonReplace: {
marginLeft: theme.spacing(15.625),
[theme.breakpoints.down('xs')]: {
marginLeft: theme.spacing(-0.5),
marginTop: theme.spacing(1.5),
},
[theme.breakpoints.down('sm')]: {
marginLeft: theme.spacing(1.3),
},
[theme.breakpoints.down('md')]: {
display: 'none',
},
[theme.breakpoints.down('lg')]: {
marginLeft: theme.spacing(5),
},
},
editorButtonSelectAll: {
marginLeft: theme.spacing(7.5),
[theme.breakpoints.down('sm')]: {
marginLeft: theme.spacing(1.4),
},
[theme.breakpoints.down('md')]: {
marginLeft: theme.spacing(1),
},
[theme.breakpoints.down('lg')]: {
marginRight: theme.spacing(2.5),
},
[theme.breakpoints.down('xs')]: {
marginLeft: theme.spacing(1.5),
marginTop: theme.spacing(1.5),
},
[theme.breakpoints.down('xl')]: {
marginRight: theme.spacing(2),
},
},
editorButtonFullScreen: {
borderRadius: 3,
backgroundColor: theme.palette.common.black,
boxSizing: 'border-box',
color: theme.palette.text.primary,
borderColor: theme.palette.common.black,
paddingLeft: theme.spacing(3.75),
paddingBottom: theme.spacing(1.875),
marginTop: theme.spacing(-2),
width: '1.875rem',
height: '1.875rem',
marginLeft: theme.spacing(-7.5),
[theme.breakpoints.down('sm')]: {
marginLeft: theme.spacing(-2),
width: 0,
height: 0,
padding: 0,
border: 0,
},
[theme.breakpoints.down('xl')]: {
width: 0,
height: 0,
padding: 0,
border: 0,
marginLeft: theme.spacing(-7.5),
},
[theme.breakpoints.down('lg')]: {
width: 0,
height: 0,
padding: 0,
border: 0,
marginLeft: theme.spacing(-7.5),
},
[theme.breakpoints.down('md')]: {
width: 0,
height: 0,
padding: 0,
border: 0,
marginLeft: theme.spacing(-7.5),
},
},
saved: {
width: '25rem',
marginTop: theme.spacing(6),
fontFamily: 'Ubuntu',
fontSize: '1rem',
color: theme.palette.success.main,
display: 'inline',
},
markStyleCorrect: {
display: 'inline-block',
fontFamily: 'Ubuntu',
fontSize: '1rem',
color: theme.palette.success.main,
},
markStyleWrong: {
display: 'inline-block',
fontFamily: 'Ubuntu',
fontSize: '1rem',
color: theme.palette.error.main,
},
fullScreenGrid: {
width: 0,
},
fullWidth: {
width: '100%',
height: '100%',
},
fullScreenIcon: {
width: '1.5625rem',
height: '1.5625rem',
marginRight: '1.5625rem',
},
// Validations
validationError: {
position: 'absolute',
background: theme.palette.error.light,
width: '2rem',
height: '1rem',
margin: theme.spacing(0, 1, 0, 0),
},
}));

View File

@ -10,6 +10,7 @@ const useStyles = makeStyles((theme: Theme) => ({
gridTemplateColumns: '20.5em auto',
gridTemplateRows: '6.5em auto',
gridTemplateAreas: '"header header" "sidebar content"',
fontFamily: 'Ubuntu',
'& ::-webkit-scrollbar': {
width: '0.4rem',

View File

@ -6,6 +6,17 @@ import { v4 as uuidv4 } from 'uuid';
import { constants } from '../constants';
import { ImageRegistryInfo } from '../models/redux/image_registry';
const validateNamespace = (chaosEngine: any) => {
// Condition to check the namespace
if (typeof chaosEngine.metadata.namespace === 'object') {
// Removes any whitespace in '{{workflow.parameters.adminModeNamespace}}'
const namespace = Object.keys(chaosEngine.metadata.namespace)[0].replace(
/\s/g,
''
);
chaosEngine.metadata.namespace = `{${namespace}}`;
}
};
const nameextractor = (val: any) => {
const embeddedworkflowyamlstring = val;
const parsedEmbeddedYaml = YAML.parse(embeddedworkflowyamlstring as string);
@ -32,6 +43,7 @@ export const updateEngineName = (parsedYaml: any) => {
if (template.inputs && template.inputs.artifacts) {
template.inputs.artifacts.forEach((artifact: any) => {
const chaosEngine = YAML.parse(artifact.raw.data);
validateNamespace(chaosEngine);
// Condition to check for the kind as ChaosEngine
if (chaosEngine.kind === 'ChaosEngine') {
if (chaosEngine.metadata.generateName === undefined) {
@ -42,14 +54,7 @@ export const updateEngineName = (parsedYaml: any) => {
chaosEngine.metadata['labels'] = {
instance_id: uuidv4(),
};
// Condition to check the namespace
if (typeof chaosEngine.metadata.namespace === 'object') {
// Removes any whitespace in '{{workflow.parameters.adminModeNamespace}}'
const namespace = Object.keys(
chaosEngine.metadata.namespace
)[0].replace(/\s/g, '');
chaosEngine.metadata.namespace = `{${namespace}}`;
}
validateNamespace(chaosEngine);
// Edge Case: Condition to check the appns
// Required because while parsing the chaos engine
@ -97,6 +102,7 @@ export const updateWorkflowNameLabel = (
if (template.inputs && template.inputs.artifacts) {
template.inputs.artifacts.forEach((artifact: any) => {
const chaosEngine = YAML.parse(artifact.raw.data);
validateNamespace(chaosEngine);
// Condition to check for the kind as ChaosEngine
if (chaosEngine.kind === 'ChaosEngine') {
if (chaosEngine.metadata.labels !== undefined) {
@ -106,14 +112,8 @@ export const updateWorkflowNameLabel = (
workflow_name: workflowName,
};
}
// Condition to check the namespace
if (typeof chaosEngine.metadata.namespace === 'object') {
// Removes any whitespace in '{{workflow.parameters.adminModeNamespace}}'
const namespace = Object.keys(
chaosEngine.metadata.namespace
)[0].replace(/\s/g, '');
chaosEngine.metadata.namespace = `{${namespace}}`;
}
validateNamespace(chaosEngine);
// Edge Case: Condition to check the appns
// Required because while parsing the chaos engine

View File

@ -87,11 +87,11 @@ const WorkflowTable = forwardRef(
expData.push({
StepIndex: index,
Name: chaosEngine.metadata.generateName,
Namespace:
chaosEngine.spec.appinfo?.appns ===
'{{workflow.parameters.adminModeNamespace}}'
? namespace
: chaosEngine.spec.appinfo?.appns ?? '',
Namespace: chaosEngine.spec.appinfo?.appns
.toLowerCase()
.includes('namespace')
? namespace
: chaosEngine.spec.appinfo?.appns ?? '',
Application: chaosEngine.spec.appinfo?.applabel ?? '',
Probes: chaosEngine.spec.experiments[0].spec.probe?.length ?? 0,
ChaosEngine: artifact.raw.data,

View File

@ -1,5 +1,6 @@
import { useLazyQuery } from '@apollo/client';
import { Typography } from '@material-ui/core';
import { Snackbar, Typography } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import { ButtonFilled, ButtonOutlined, Modal } from 'litmus-ui';
import localforage from 'localforage';
import React, {
@ -36,6 +37,7 @@ import { RootState } from '../../../redux/reducers';
import capitalize from '../../../utils/capitalize';
import { getProjectID } from '../../../utils/getSearchParams';
import {
fetchWorkflowNameFromManifest,
updateEngineName,
updateManifestImage,
updateNamespace,
@ -95,6 +97,7 @@ const TuneWorkflow = forwardRef((_, ref) => {
const [addExpModal, setAddExpModal] = useState(false);
const [editManifest, setEditManifest] = useState('');
const [confirmEdit, setConfirmEdit] = useState(false);
const [isAlertOpen, setIsAlertOpen] = useState(false);
const [yamlValid, setYamlValid] = useState(true);
const [editSequence, setEditSequence] = useState(false);
const [steps, setSteps] = useState<StepType>({});
@ -374,6 +377,18 @@ const TuneWorkflow = forwardRef((_, ref) => {
setAddExpModal(false);
};
const AlertBox: React.FC = () => (
<Snackbar
open={isAlertOpen}
autoHideDuration={6000}
onClose={() => setIsAlertOpen(false)}
>
<Alert onClose={() => setIsAlertOpen(false)} severity="error">
The YAML contains errors, resolve them first to proceed
</Alert>
</Snackbar>
);
/**
* UpdateCRD is used to updated the manifest while adding experiments from MyHub
*/
@ -496,6 +511,17 @@ const TuneWorkflow = forwardRef((_, ref) => {
}
}, [experiment]);
const saveManifestChanges = () => {
if (yamlValid) {
workflowAction.setWorkflowManifest({
manifest: editManifest,
});
setYAMLModal(false);
} else {
setIsAlertOpen(true);
}
};
useEffect(() => {
const parsedManifest =
manifest !== '' ? YAML.parse(manifest) : generatedYAML;
@ -577,41 +603,24 @@ const TuneWorkflow = forwardRef((_, ref) => {
}));
const LeftButtonWrapper = () => (
<ButtonOutlined
onClick={() => {
setYAMLModal(true);
setConfirmEdit(false);
}}
className={classes.editBtn}
>
<img src="./icons/viewYAMLicon.svg" alt="view YAML" />
<Width width="1rem" /> {t('createWorkflow.tuneWorkflow.edit')}
</ButtonOutlined>
);
return (
<>
<ButtonOutlined
onClick={() => {
setYAMLModal(true);
setConfirmEdit(false);
}}
className={classes.editBtn}
>
<img src="./icons/viewYAMLicon.svg" alt="view YAML" />
<Width width="1rem" /> {t('createWorkflow.tuneWorkflow.edit')}
</ButtonOutlined>
<Modal
open={YAMLModal}
onClose={() => {
setYAMLModal(false);
}}
width="90%"
height="90%"
modalActions={
<ButtonOutlined
onClick={() => {
if (editManifest === '') {
setYAMLModal(false);
} else {
setConfirmEdit(true);
}
}}
className={classes.closeBtn}
>
<img src="./icons/cross-disabled.svg" alt="cross" />
</ButtonOutlined>
}
>
<div className={classes.saveTemplateRoot}>
{confirmEdit ? (
<AlertBox />
{YAMLModal ? (
<>
<Modal open={confirmEdit} onClose={() => {}} width="50%" height="30%">
<div className={classes.confirmDiv}>
<Typography className={classes.confirmText}>
{t('createWorkflow.tuneWorkflow.confirmText')}
@ -636,167 +645,184 @@ const TuneWorkflow = forwardRef((_, ref) => {
{t('createWorkflow.tuneWorkflow.continue')}
</ButtonFilled>
</div>
) : (
<>
<Typography className={classes.updateText}>
{t('createWorkflow.tuneWorkflow.updateChanges')}
</Typography>
<YamlEditor
content={manifest}
filename={workflow.name}
className={classes.editor}
readOnly={false}
setButtonState={(btnState: boolean) => {
setYamlValid(btnState);
}}
saveWorkflowChange={(updatedManifest: string) => {
setEditManifest(updatedManifest);
}}
/>
<ButtonFilled
className={classes.continueBtn}
disabled={!yamlValid}
onClick={() => {
workflowAction.setWorkflowManifest({
manifest: editManifest === '' ? manifest : editManifest,
});
setEditManifest('');
setYAMLModal(false);
}}
>
{t('createWorkflow.tuneWorkflow.saveChange')}
</ButtonFilled>
</>
)}
</div>
</Modal>
</>
);
return (
<div className={classes.root}>
{/* Header */}
<div className={classes.headerWrapper}>
<Typography className={classes.heading}>
{t('createWorkflow.tuneWorkflow.header')}
</Typography>
<Row className={classes.descriptionWrapper}>
<Typography className={classes.description}>
{selectedRadio === 'A'
? t('createWorkflow.tuneWorkflow.selectedPreDefinedWorkflowInfo')
: selectedRadio === 'B'
? t('createWorkflow.tuneWorkflow.selectedTemplateInfo')
: selectedRadio === 'C'
? t('createWorkflow.tuneWorkflow.selectedCustomWorkflowInfo')
: t('createWorkflow.tuneWorkflow.selectedUploadYAML')}{' '}
<i>
<strong>
{workflow.name.split('-').map((text) => `${capitalize(text)} `)}
</strong>
</i>
<br />
{t('createWorkflow.tuneWorkflow.description')}
</Typography>
{selectedRadio === 'C' ? (
<div className={classes.headerBtn}>
{LeftButtonWrapper()}
<ButtonOutlined
onClick={() => {
setSelectedExp('');
setAddExpModal(true);
}}
>
{t('createWorkflow.tuneWorkflow.addANewExperiment')}
</ButtonOutlined>
</Modal>
<div className={classes.editorWrapper}>
<div className={`${classes.flex} ${classes.additional}`}>
<div className={classes.flex}>
<img
style={{ width: '2rem' }}
src="./icons/terminal.svg"
alt="Terminal Icon"
/>
<Typography className={classes.name}>
{fetchWorkflowNameFromManifest(manifest)}.yaml
</Typography>
</div>
<div className={classes.flex}>
<ButtonOutlined
onClick={() => {
saveManifestChanges();
}}
className={classes.editorTopBtn}
>
Save Changes
</ButtonOutlined>
<hr style={{ margin: '0 1rem', height: '2.5rem' }} />
<ButtonOutlined
onClick={() =>
yamlValid ? setConfirmEdit(true) : setIsAlertOpen(true)
}
className={classes.editorCloseBtn}
>
x
</ButtonOutlined>
</div>
</div>
) : (
<>{LeftButtonWrapper()}</>
)}
</Row>
</div>
<YamlEditor
content={manifest}
filename={workflow.name}
readOnly={false}
setButtonState={(btnState: boolean) => {
setYamlValid(btnState);
}}
saveWorkflowChange={(updatedManifest: string) => {
setEditManifest(updatedManifest);
}}
/>
</div>
</>
) : (
<div className={classes.root}>
{/* Header */}
<div className={classes.headerWrapper}>
<Typography className={classes.heading}>
{t('createWorkflow.tuneWorkflow.header')}
</Typography>
<Row className={classes.descriptionWrapper}>
<Typography className={classes.description}>
{selectedRadio === 'A'
? t(
'createWorkflow.tuneWorkflow.selectedPreDefinedWorkflowInfo'
)
: selectedRadio === 'B'
? t('createWorkflow.tuneWorkflow.selectedTemplateInfo')
: selectedRadio === 'C'
? t('createWorkflow.tuneWorkflow.selectedCustomWorkflowInfo')
: t('createWorkflow.tuneWorkflow.selectedUploadYAML')}{' '}
<i>
<strong>
{workflow.name
.split('-')
.map((text) => `${capitalize(text)} `)}
</strong>
</i>
<br />
{t('createWorkflow.tuneWorkflow.description')}
</Typography>
{selectedRadio === 'C' ? (
<div className={classes.headerBtn}>
{LeftButtonWrapper()}
<ButtonOutlined
onClick={() => {
setSelectedExp('');
setAddExpModal(true);
}}
>
{t('createWorkflow.tuneWorkflow.addANewExperiment')}
</ButtonOutlined>
</div>
) : (
<>{LeftButtonWrapper()}</>
)}
</Row>
</div>
{/* Add Experiment Modal */}
<AddExperimentModal
addExpModal={addExpModal}
onModalClose={onModalClose}
hubName={hubName}
selectedExp={selectedExp}
onSelectChange={onSelectChange}
allExperiments={allExperiments}
handleDone={handleDone}
/>
{/* Add Experiment Modal */}
<AddExperimentModal
addExpModal={addExpModal}
onModalClose={onModalClose}
hubName={hubName}
selectedExp={selectedExp}
onSelectChange={onSelectChange}
allExperiments={allExperiments}
handleDone={handleDone}
/>
{/* Experiment Details */}
<div className={classes.experimentWrapper}>
{/* Edit Button */}
{manifest !== '' && (
<ButtonOutlined onClick={() => setEditSequence(true)}>
<img src="./icons/editsequence.svg" alt="Edit Sequence" />{' '}
<Width width="0.5rem" />
{t('createWorkflow.tuneWorkflow.editSequence')}
</ButtonOutlined>
)}
<Modal
open={editSequence}
onClose={() => {
setEditSequence(false);
}}
width="60%"
modalActions={
<ButtonOutlined
onClick={() => {
{/* Experiment Details */}
<div className={classes.experimentWrapper}>
{/* Edit Button */}
{manifest !== '' && (
<ButtonOutlined onClick={() => setEditSequence(true)}>
<img src="./icons/editsequence.svg" alt="Edit Sequence" />{' '}
<Width width="0.5rem" />
{t('createWorkflow.tuneWorkflow.editSequence')}
</ButtonOutlined>
)}
<Modal
open={editSequence}
onClose={() => {
setEditSequence(false);
}}
className={classes.closeBtn}
>
<img src="./icons/cross-disabled.svg" alt="cross" />
</ButtonOutlined>
}
>
<div className={classes.sequenceMainDiv}>
<div className={classes.sequenceDiv}>
<Typography variant="h4">
{t('createWorkflow.tuneWorkflow.editSequence')}
</Typography>
<Typography className={classes.dropText}>
{t('createWorkflow.tuneWorkflow.dragndrop')}
</Typography>
</div>
<Row>
<Width width="40%">
<WorkflowPreview
SequenceSteps={steps}
isCustomWorkflow={isCustomWorkflow}
/>
</Width>
<Width width="60%">
<WorkflowSequence
getSteps={handleSteps}
handleSequenceModal={(sequenceState: boolean) => {
setEditSequence(sequenceState);
width="60%"
modalActions={
<ButtonOutlined
onClick={() => {
setEditSequence(false);
}}
className={classes.closeBtn}
>
<img src="./icons/cross-disabled.svg" alt="cross" />
</ButtonOutlined>
}
>
<div className={classes.sequenceMainDiv}>
<div className={classes.sequenceDiv}>
<Typography variant="h4">
{t('createWorkflow.tuneWorkflow.editSequence')}
</Typography>
<Typography className={classes.dropText}>
{t('createWorkflow.tuneWorkflow.dragndrop')}
</Typography>
</div>
<Row>
<Width width="40%">
<WorkflowPreview
SequenceSteps={steps}
isCustomWorkflow={isCustomWorkflow}
/>
</Width>
<Width width="60%">
<WorkflowSequence
getSteps={handleSteps}
handleSequenceModal={(sequenceState: boolean) => {
setEditSequence(sequenceState);
}}
/>
</Width>
</Row>
</div>
</Modal>
{/* Details Section -> Graph on the Left and Table on the Right */}
{/* Details Section -> Graph on the Left and Table on the Right */}
<Row>
{/* Argo Workflow Graph */}
<Width width="30%">
<WorkflowPreview isCustomWorkflow={isCustomWorkflow} />
</Width>
{/* Workflow Table */}
<Width width="70%">
<WorkflowTable
ref={childRef}
isCustom={isCustomWorkflow}
namespace={namespace}
/>
</Width>
</Row>
</div>
</Modal>
{/* Details Section -> Graph on the Left and Table on the Right */}
<Row>
{/* Argo Workflow Graph */}
<Width width="30%">
<WorkflowPreview isCustomWorkflow={isCustomWorkflow} />
</Width>
{/* Workflow Table */}
<Width width="70%">
<WorkflowTable
ref={childRef}
isCustom={isCustomWorkflow}
namespace={namespace}
/>
</Width>
</Row>
</div>
</div>
</div>
)}
</>
);
});

View File

@ -10,6 +10,10 @@ const useStyles = makeStyles((theme) => ({
height: '100%',
},
editorWrapper: {
marginBottom: theme.spacing(-4),
},
// Header
headerWrapper: {
padding: theme.spacing(0, 7),
@ -27,10 +31,10 @@ const useStyles = makeStyles((theme) => ({
headerBtn: {
display: 'flex',
justifyContent: 'space-between',
width: '40%',
[theme.breakpoints.up('lg')]: {
width: '25%',
},
width: '23rem',
// [theme.breakpoints.up('lg')]: {
// width: '30rem',
// },
},
descriptionWrapper: {
@ -152,15 +156,34 @@ const useStyles = makeStyles((theme) => ({
},
// Editor
editor: {
height: '100%',
flex: {
display: 'flex',
},
additional: {
width: '95%',
margin: '0rem auto',
justifyContent: 'space-between',
},
name: {
margin: theme.spacing(1, 0, 2, 2),
fontWeight: 'bold',
},
editorTopBtn: {
padding: '0.4rem',
fontSize: '0.8rem',
},
editorCloseBtn: {
width: '0.5rem',
borderColor: theme.palette.disabledBackground,
color: theme.palette.text.disabled,
minWidth: '2rem',
padding: '0.2rem',
fontSize: '1rem',
},
// Confirmation Modal
confirmDiv: {
margin: 'auto',
marginTop: theme.spacing(31.25),
width: '30rem',
margin: '2rem auto',
},
confirmText: {
fontSize: '2.25rem',
@ -171,11 +194,6 @@ const useStyles = makeStyles((theme) => ({
continueBtn: {
marginTop: theme.spacing(2.5),
},
updateText: {
fontSize: '1.6rem',
marginBottom: theme.spacing(3.75),
textAlign: 'left',
},
// Sequence Modal
sequenceMainDiv: {

View File

@ -139,11 +139,6 @@ const VerifyCommit = forwardRef(
setOpen(true);
};
const handleClose = () => {
setModified(true);
setOpen(false);
};
const handleNameChange = ({ changedName }: { changedName: string }) => {
setWorkflow({
...workflow,
@ -343,224 +338,242 @@ const VerifyCommit = forwardRef(
return (
<>
<div className={classes.root}>
<div className={classes.innerContainer}>
<div className={classes.suHeader}>
<div>
<Typography className={classes.headerText}>
{t('createWorkflow.verifyCommit.header')}
</Typography>
<Typography className={classes.description}>
{t('createWorkflow.verifyCommit.info')}
{open ? (
<div className={classes.editorWrapper}>
<div className={`${classes.flex} ${classes.additional}`}>
<div className={classes.flex}>
<img
style={{ width: '2rem' }}
src="./icons/terminal.svg"
alt="Terminal Icon"
/>
<Typography className={classes.name}>
{fetchWorkflowNameFromManifest(manifest)}.yaml
</Typography>
</div>
<img
src="/icons/b-finance.svg"
alt="bfinance"
className={classes.bfinIcon}
/>
<ButtonOutlined
onClick={() => setOpen(false)}
className={classes.editorCloseBtn}
>
x
</ButtonOutlined>
</div>
<Divider />
<Typography className={classes.sumText}>
{t('createWorkflow.verifyCommit.summary.header')}
</Typography>
<div className={classes.summaryWrapper}>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.workflowName')}:
</Typography>
<div className={classes.right} data-cy="WorkflowName">
<div style={{ width: '100%' }}>
<EditableText
defaultValue={fetchWorkflowNameFromManifest(manifest)}
id="name"
fullWidth
multiline
error={checkNameValidation()}
onSave={(value) =>
handleNameChange({ changedName: value })
}
helperText={
checkNameValidation()
? `${t(
`createWorkflow.verifyCommit.workflowNameValidationMessage`
)}`
: undefined
}
/>
</div>
</div>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.clustername')}:
</Typography>
<Typography className={classes.right}>{clustername}</Typography>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.desc')}:
</Typography>
<div className={classes.right}>
{workflow.description !== '' ? (
<div style={{ width: '100%' }}>
<EditableText
defaultValue={workflow.description}
id="desc"
fullWidth
multiline
onSave={(value) =>
handleDescChange({ changedDesc: value })
}
/>
</div>
) : null}
</div>
</div>
<div className={classes.itemWrapper}>
<div className={classes.leftFlex}>
<Typography className={classes.verticalAlign}>
{t('createWorkflow.verifyCommit.summary.subject')}:
<YamlEditor content={manifest} filename={workflow.name} readOnly />
</div>
) : (
<div className={classes.root}>
<div className={classes.innerContainer}>
<div className={classes.suHeader}>
<div>
<Typography className={classes.headerText}>
{t('createWorkflow.verifyCommit.header')}
</Typography>
<Typography className={classes.description}>
{t('createWorkflow.verifyCommit.info')}
</Typography>
<Tooltip
title={
<Typography className={classes.subjectDesc}>
{t('createWorkflow.verifyCommit.summary.subjectDesc')}
</Typography>
}
>
<InfoIcon className={classes.info} />
</Tooltip>
</div>
<img
src="/icons/b-finance.svg"
alt="bfinance"
className={classes.bfinIcon}
/>
</div>
<Divider />
<div className={classes.right}>
{subject !== '' ? (
<Typography className={classes.sumText}>
{t('createWorkflow.verifyCommit.summary.header')}
</Typography>
<div className={classes.summaryWrapper}>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.workflowName')}:
</Typography>
<div className={classes.right} data-cy="WorkflowName">
<div style={{ width: '100%' }}>
<EditableText
defaultValue={subject}
id="subject"
defaultValue={fetchWorkflowNameFromManifest(manifest)}
id="name"
fullWidth
multiline
error={checkSubjectValidation()}
error={checkNameValidation()}
onSave={(value) =>
handleSubjectChange({ changedSubject: value })
handleNameChange({ changedName: value })
}
helperText={
checkSubjectValidation()
checkNameValidation()
? `${t(
'createWorkflow.verifyCommit.subjectValidationMessage'
`createWorkflow.verifyCommit.workflowNameValidationMessage`
)}`
: undefined
}
/>
</div>
) : null}
</div>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.schedule')}:
</Typography>
<div className={classes.right}>
<div className={classes.spaceBetween}>
{cronSyntax === '' ? (
<Typography>
{t('createWorkflow.verifyCommit.summary.schedulingNow')}
</Typography>
) : (
<Typography>{cronstrue.toString(cronSyntax)}</Typography>
)}
<EditIcon
onClick={() => handleGoToStep(5)}
className={classes.editIcon}
data-cy="edit"
/>
</div>
</div>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.adjustedWeights')}:
</Typography>
{weights.length === 0 ? (
<Typography
className={`${classes.errorText} ${classes.right}`}
>
{t('createWorkflow.verifyCommit.error')}
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.clustername')}:
</Typography>
) : (
<Typography className={classes.right}>
{clustername}
</Typography>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.desc')}:
</Typography>
<div className={classes.right}>
<div className={classes.progress}>
{WorkflowTestData.map((Test) => (
<AdjustedWeights
key={Test.weight}
testName={`${Test.experimentName} ${t(
'createWorkflow.verifyCommit.test'
)}`}
testValue={Test.weight}
spacing={false}
icon={false}
{workflow.description !== '' ? (
<div style={{ width: '100%' }}>
<EditableText
defaultValue={workflow.description}
id="desc"
fullWidth
multiline
onSave={(value) =>
handleDescChange({ changedDesc: value })
}
/>
))}
</div>
<ButtonOutlined
onClick={() => handleGoToStep(4)}
data-cy="testRunButton"
>
{t('createWorkflow.verifyCommit.button.edit')}
</ButtonOutlined>
</div>
) : null}
</div>
)}
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.YAML')}
</Typography>
<div className={classes.rightColumn}>
</div>
<div className={classes.itemWrapper}>
<div className={classes.leftFlex}>
<Typography className={classes.verticalAlign}>
{t('createWorkflow.verifyCommit.summary.subject')}:
</Typography>
<Tooltip
title={
<Typography className={classes.subjectDesc}>
{t('createWorkflow.verifyCommit.summary.subjectDesc')}
</Typography>
}
>
<InfoIcon className={classes.info} />
</Tooltip>
</div>
<div className={classes.right}>
{subject !== '' ? (
<div style={{ width: '100%' }}>
<EditableText
defaultValue={subject}
id="subject"
fullWidth
multiline
error={checkSubjectValidation()}
onSave={(value) =>
handleSubjectChange({ changedSubject: value })
}
helperText={
checkSubjectValidation()
? `${t(
'createWorkflow.verifyCommit.subjectValidationMessage'
)}`
: undefined
}
/>
</div>
) : null}
</div>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.schedule')}:
</Typography>
<div className={classes.right}>
<div className={classes.spaceBetween}>
{cronSyntax === '' ? (
<Typography>
{t(
'createWorkflow.verifyCommit.summary.schedulingNow'
)}
</Typography>
) : (
<Typography>
{cronstrue.toString(cronSyntax)}
</Typography>
)}
<EditIcon
onClick={() => handleGoToStep(5)}
className={classes.editIcon}
data-cy="edit"
/>
</div>
</div>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.summary.adjustedWeights')}:
</Typography>
{weights.length === 0 ? (
<Typography className={classes.errorText}>
{t('createWorkflow.verifyCommit.errYaml')}
<Typography
className={`${classes.errorText} ${classes.right}`}
>
{t('createWorkflow.verifyCommit.error')}
</Typography>
) : (
<Typography>
<b>{yamlStatus}</b>
<span className={classes.spacingHorizontal}>
{t('createWorkflow.verifyCommit.youCanMoveOn')}
</span>
</Typography>
<div className={classes.right}>
<div className={classes.progress}>
{WorkflowTestData.map((Test) => (
<AdjustedWeights
key={Test.weight}
testName={`${Test.experimentName} ${t(
'createWorkflow.verifyCommit.test'
)}`}
testValue={Test.weight}
spacing={false}
icon={false}
/>
))}
</div>
<ButtonOutlined
onClick={() => handleGoToStep(4)}
data-cy="testRunButton"
>
{t('createWorkflow.verifyCommit.button.edit')}
</ButtonOutlined>
</div>
)}
<br />
<ButtonFilled
className={classes.verifyYAMLButton}
onClick={handleOpen}
>
{t('createWorkflow.verifyCommit.button.viewYaml')}
</ButtonFilled>
</div>
<div className={classes.itemWrapper}>
<Typography className={classes.left}>
{t('createWorkflow.verifyCommit.YAML')}
</Typography>
<div className={classes.rightColumn}>
{weights.length === 0 ? (
<Typography className={classes.errorText}>
{t('createWorkflow.verifyCommit.errYaml')}
</Typography>
) : (
<Typography>
<b>{yamlStatus}</b>
<span className={classes.spacingHorizontal}>
{t('createWorkflow.verifyCommit.youCanMoveOn')}
</span>
</Typography>
)}
<br />
<ButtonFilled
className={classes.verifyYAMLButton}
onClick={handleOpen}
>
{t('createWorkflow.verifyCommit.button.viewYaml')}
</ButtonFilled>
</div>
</div>
</div>
</div>
</div>
</div>
<Modal
open={open}
onClose={handleClose}
width="60%"
modalActions={
<ButtonOutlined onClick={handleClose} className={classes.closeBtn}>
&#x2715;
</ButtonOutlined>
}
>
<YamlEditor content={manifest} filename={workflow.name} readOnly />
</Modal>
)}
{/* Finish Modal */}
<div>

View File

@ -166,5 +166,34 @@ const useStyles = makeStyles((theme: Theme) => ({
bold: {
fontWeight: 700,
},
// Editor
editorWrapper: {
marginBottom: theme.spacing(-4),
},
flex: {
display: 'flex',
},
additional: {
width: '95%',
margin: '0rem auto',
justifyContent: 'space-between',
},
name: {
margin: theme.spacing(1, 0, 2, 2),
fontWeight: 'bold',
},
editorTopBtn: {
padding: '0.4rem',
fontSize: '0.8rem',
},
editorCloseBtn: {
width: '0.5rem',
borderColor: theme.palette.disabledBackground,
color: theme.palette.text.disabled,
minWidth: '2rem',
padding: '0.2rem',
fontSize: '1rem',
},
}));
export default useStyles;

View File

@ -35,6 +35,9 @@ const WorkflowSettings = forwardRef((_, ref) => {
// Actions
const workflowAction = useActions(WorkflowActions);
const workflowData = useSelector((state: RootState) => state.workflowData);
const { manifest } = useSelector(
(state: RootState) => state.workflowManifest
);
const { t } = useTranslation();
const alert = useActions(AlertActions);
@ -95,7 +98,7 @@ const WorkflowSettings = forwardRef((_, ref) => {
}
if ((value as ChooseWorkflowRadio).selected === 'C') {
setName('custom-chaos-workflow');
workflowAction.setWorkflowManifest({ manifest: '' });
workflowAction.setWorkflowManifest({ manifest: manifest ?? '' });
setDescription('Custom Chaos Workflow');
setIcon('./avatars/litmus.svg');
}