type(ux): Added delete option in experiment table, fixed agent select radio buttons and minor ux changes (#2862)

* Added delete option in exp table, fixed select agent radio buttons and minor ux change
* Minor Radio Group Fix
* Minor CSS change

Signed-off-by: Amit Kumar Das <amit@chaosnative.com>
This commit is contained in:
Amit Kumar Das 2021-06-09 12:48:48 +05:30 committed by GitHub
parent ed36c9c16a
commit 89f29a2a74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 137 additions and 144 deletions

View File

@ -1,5 +1,5 @@
import { useLazyQuery, useQuery } from '@apollo/client'; import { useLazyQuery, useQuery } from '@apollo/client';
import { Typography, useTheme } from '@material-ui/core'; import { RadioGroup, Typography, useTheme } from '@material-ui/core';
import { LitmusCard, RadioButton, Search } from 'litmus-ui'; import { LitmusCard, RadioButton, Search } from 'litmus-ui';
import React, { import React, {
forwardRef, forwardRef,
@ -215,32 +215,38 @@ const ChooseWorkflowAgent = forwardRef((_, ref) => {
/> />
{/* Cluster Data */} {/* Cluster Data */}
<div className={classes.agentWrapperDiv} data-cy="AgentsRadioGroup"> <RadioGroup
{filteredCluster.map((cluster) => ( name="Agent Selection"
<LitmusCard value={currentlySelectedAgent}
key={cluster.cluster_id} onChange={(e) => handleChange(e)}
glow={currentlySelectedAgent === cluster.cluster_id} >
width="100%" <div className={classes.agentWrapperDiv} data-cy="AgentsRadioGroup">
height="4rem" {filteredCluster.map((cluster) => (
className={classes.litmusCard} <LitmusCard
borderColor={ key={cluster.cluster_id}
currentlySelectedAgent === cluster.cluster_id glow={currentlySelectedAgent === cluster.cluster_id}
? palette.primary.main width="40%"
: palette.border.main height="4rem"
} className={classes.litmusCard}
> borderColor={
<RadioButton currentlySelectedAgent === cluster.cluster_id
data-cy="AgentRadioButtons" ? palette.primary.main
value={cluster.cluster_id} : palette.border.main
className={classes.agentRadioButton} }
onChange={(e) => handleChange(e)}
> >
{cluster.cluster_name} <br /> <RadioButton
{cluster.cluster_id} value={cluster.cluster_id}
</RadioButton> className={classes.agentRadioButton}
</LitmusCard> >
))} <div>
</div> <Typography>{cluster.cluster_name}</Typography>
<Typography>{cluster.cluster_id}</Typography>
</div>
</RadioButton>
</LitmusCard>
))}
</div>
</RadioGroup>
</div> </div>
</div> </div>
); );

View File

@ -43,12 +43,13 @@ const useStyles = makeStyles((theme: Theme) => ({
// Agent Div // Agent Div
agentWrapperDiv: { agentWrapperDiv: {
marginTop: theme.spacing(5), marginTop: theme.spacing(5),
display: 'grid', display: 'flex',
gridTemplateColumns: '1fr 1fr', flexDirection: 'row',
gridGap: '1.5rem', flexWrap: 'wrap',
}, },
litmusCard: { litmusCard: {
background: theme.palette.cards.background, background: theme.palette.cards.background,
margin: theme.spacing(2, 2, 2, 0),
}, },
agentRadioButton: { agentRadioButton: {
marginTop: theme.spacing(1), marginTop: theme.spacing(1),

View File

@ -247,7 +247,7 @@ const AddProbe: React.FC<AddProbeProps> = ({
<div className={classes.detailContainer}> <div className={classes.detailContainer}>
<div className={classes.formField}> <div className={classes.formField}>
<InputLabel className={classes.formLabel} htmlFor="timeout"> <InputLabel className={classes.formLabel} htmlFor="timeout">
{t('createWorkflow.tuneWorkflow.addProbe.labels.timeout')}(ms) {t('createWorkflow.tuneWorkflow.addProbe.labels.timeout')}(sec)
</InputLabel> </InputLabel>
<InputField <InputField
variant="primary" variant="primary"
@ -279,7 +279,7 @@ const AddProbe: React.FC<AddProbeProps> = ({
<div className={classes.detailContainer}> <div className={classes.detailContainer}>
<div className={classes.formField}> <div className={classes.formField}>
<InputLabel className={classes.formLabel} htmlFor="interval"> <InputLabel className={classes.formLabel} htmlFor="interval">
{t('createWorkflow.tuneWorkflow.addProbe.labels.interval')}(ms) {t('createWorkflow.tuneWorkflow.addProbe.labels.interval')}(sec)
</InputLabel> </InputLabel>
<InputField <InputField
variant="primary" variant="primary"
@ -294,7 +294,7 @@ const AddProbe: React.FC<AddProbeProps> = ({
</div> </div>
<div className={classes.formField}> <div className={classes.formField}>
<InputLabel className={classes.formLabel} htmlFor="polling"> <InputLabel className={classes.formLabel} htmlFor="polling">
{t('createWorkflow.tuneWorkflow.addProbe.labels.polling')}(ms) {t('createWorkflow.tuneWorkflow.addProbe.labels.polling')}(sec)
</InputLabel> </InputLabel>
<InputField <InputField
variant="primary" variant="primary"
@ -310,7 +310,7 @@ const AddProbe: React.FC<AddProbeProps> = ({
<div className={classes.formField}> <div className={classes.formField}>
<InputLabel className={classes.formLabel} htmlFor="initial-delay"> <InputLabel className={classes.formLabel} htmlFor="initial-delay">
{t('createWorkflow.tuneWorkflow.addProbe.labels.initialDelay')} {t('createWorkflow.tuneWorkflow.addProbe.labels.initialDelay')}
(ms) (sec)
</InputLabel> </InputLabel>
<InputField <InputField
variant="primary" variant="primary"

View File

@ -5,17 +5,10 @@ import StepLabel from '@material-ui/core/StepLabel';
import StepContent from '@material-ui/core/StepContent'; import StepContent from '@material-ui/core/StepContent';
import Typography from '@material-ui/core/Typography'; import Typography from '@material-ui/core/Typography';
import { ButtonOutlined } from 'litmus-ui'; import { ButtonOutlined } from 'litmus-ui';
import { useSelector } from 'react-redux';
import YAML from 'yaml';
import { useTranslation } from 'react-i18next';
import General from '../TuneWorkflowSteps/General'; import General from '../TuneWorkflowSteps/General';
import SteadyState from '../TuneWorkflowSteps/SteadyState'; import SteadyState from '../TuneWorkflowSteps/SteadyState';
import TargetApplication from '../TuneWorkflowSteps/TargetApplication'; import TargetApplication from '../TuneWorkflowSteps/TargetApplication';
import useStyles from './styles'; import useStyles from './styles';
import * as WorkflowActions from '../../../../redux/actions/workflow';
import { WorkflowManifest } from '../../../../models/redux/workflow';
import { RootState } from '../../../../redux/reducers';
import useActions from '../../../../redux/actions';
interface ConfigurationStepperProps { interface ConfigurationStepperProps {
experimentIndex: number; experimentIndex: number;
@ -57,7 +50,6 @@ const ConfigurationStepper: React.FC<ConfigurationStepperProps> = ({
closeStepper, closeStepper,
isCustom, isCustom,
}) => { }) => {
const { t } = useTranslation();
const classes = useStyles(); const classes = useStyles();
// State variable to handle Stepper Steps // State variable to handle Stepper Steps
@ -73,76 +65,6 @@ const ConfigurationStepper: React.FC<ConfigurationStepperProps> = ({
const gotoStep = (page: number) => { const gotoStep = (page: number) => {
setActiveStep(page); setActiveStep(page);
}; };
const workflow = useActions(WorkflowActions);
const manifest: WorkflowManifest = useSelector(
(state: RootState) => state.workflowManifest
);
const deleteExperiment = () => {
/**
* Workflow manifest saved in redux state
*/
const wfManifest = YAML.parse(manifest.manifest);
/**
* Get template name according to the experiment index
*/
const templateName = wfManifest.spec.templates[experimentIndex].name;
/**
* Get the workflow name according to the experiment index
*/
const wfName = YAML.parse(manifest.engineYAML).metadata.generateName;
/**
* if the template is a revert-chaos template
* the engine name is removed from the
* revert-chaos template args
*/
if (
wfManifest.spec.templates[
wfManifest.spec.templates.length - 1
].name.includes('revert-')
) {
const argument = wfManifest.spec.templates[
wfManifest.spec.templates.length - 1
].container.args[0].replace(wfName, '');
wfManifest.spec.templates[
wfManifest.spec.templates.length - 1
].container.args[0] = argument;
}
/**
* Remove the experiment name from steps
*/
wfManifest.spec.templates[0].steps.forEach(
(data: any, stepIndex: number) => {
data.forEach((step: any, index: number) => {
if (step.name === templateName) {
data.splice(index, 1);
}
});
if (data.length === 0) {
wfManifest.spec.templates[0].steps.splice(stepIndex, 1);
}
}
);
/**
* Remove the chaos engine from the overall manifest
* according to the experiment index
*/
wfManifest.spec.templates.splice(experimentIndex, 1);
/**
* Set the updated manifest to redux state
*/
workflow.setWorkflowManifest({
manifest: YAML.stringify(wfManifest),
engineYAML: '',
});
closeStepper();
};
return ( return (
<div className={classes.root}> <div className={classes.root}>
@ -151,26 +73,6 @@ const ConfigurationStepper: React.FC<ConfigurationStepperProps> = ({
&#x2715; &#x2715;
</ButtonOutlined> </ButtonOutlined>
</div> </div>
<div className={classes.deleteExpDiv}>
<Typography className={classes.editExpText}>
{t('createWorkflow.tuneWorkflow.verticalStepper.editExp')}
</Typography>
<ButtonOutlined
onClick={() => {
deleteExperiment();
}}
className={classes.deleteBtn}
>
<img
src="./icons/delete-exp.svg"
alt="delete"
className={classes.deleteIcon}
/>
<Typography color="error" className={classes.deleteExpText}>
{t('createWorkflow.tuneWorkflow.verticalStepper.deleteExp')}
</Typography>
</ButtonOutlined>
</div>
<Stepper activeStep={activeStep} orientation="vertical"> <Stepper activeStep={activeStep} orientation="vertical">
{steps.map((label, index) => ( {steps.map((label, index) => (
<Step key={label}> <Step key={label}>

View File

@ -1,5 +1,5 @@
/* eslint-disable no-const-assign */ /* eslint-disable no-const-assign */
import { Typography, useTheme } from '@material-ui/core'; import { IconButton, Typography, useTheme } from '@material-ui/core';
import Paper from '@material-ui/core/Paper'; import Paper from '@material-ui/core/Paper';
import Table from '@material-ui/core/Table'; import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody'; import TableBody from '@material-ui/core/TableBody';
@ -224,6 +224,75 @@ const WorkflowTable = forwardRef(
}); });
}, [manifest]); }, [manifest]);
const deleteExperiment = (experimentIndex: number) => {
/**
* Workflow manifest saved in redux state
*/
const wfManifest = YAML.parse(manifest);
/**
* Get template name according to the experiment index
*/
const templateName = wfManifest.spec.templates[experimentIndex].name;
/**
* Get instance_id of Chaos Engines
*/
const selectedEngine =
wfManifest.spec.templates[experimentIndex].inputs.artifacts[0];
const { instance_id } = YAML.parse(
selectedEngine.raw.data
).metadata.labels;
/**
* if the template is a revert-chaos template
* the engine name is removed from the
* revert-chaos template args
*/
if (
wfManifest.spec.templates[
wfManifest.spec.templates.length - 1
].name.includes('revert-')
) {
const argument = wfManifest.spec.templates[
wfManifest.spec.templates.length - 1
].container.args[0].replace(`${instance_id}, `, '');
wfManifest.spec.templates[
wfManifest.spec.templates.length - 1
].container.args[0] = argument;
}
/**
* Remove the experiment name from steps
*/
wfManifest.spec.templates[0].steps.forEach(
(data: any, stepIndex: number) => {
data.forEach((step: any, index: number) => {
if (step.name === templateName) {
data.splice(index, 1);
}
});
if (data.length === 0) {
wfManifest.spec.templates[0].steps.splice(stepIndex, 1);
}
}
);
/**
* Remove the chaos engine from the overall manifest
* according to the experiment index
*/
wfManifest.spec.templates.splice(experimentIndex, 1);
/**
* Set the updated manifest to redux state
*/
workflow.setWorkflowManifest({
manifest: YAML.stringify(wfManifest),
engineYAML: '',
});
};
function onNext() { function onNext() {
if (experiments.length === 0) { if (experiments.length === 0) {
return false; // Should show alert return false; // Should show alert
@ -264,26 +333,29 @@ const WorkflowTable = forwardRef(
<TableCell align="left"> <TableCell align="left">
{t('createWorkflow.chooseWorkflow.table.head5')} {t('createWorkflow.chooseWorkflow.table.head5')}
</TableCell> </TableCell>
<TableCell />
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{experiments.length > 0 ? ( {experiments.length > 0 ? (
experiments.map((experiment: ChaosCRDTable, index) => ( experiments.map((experiment: ChaosCRDTable, index) => (
<TableRow <TableRow key={experiment.Name}>
key={experiment.Name}
onClick={() => {
setDisplayStepper(true);
setEngineIndex(experiment.StepIndex);
workflow.setWorkflowManifest({
engineYAML: experiment.ChaosEngine,
});
}}
className={classes.selection}
>
<TableCell component="th" scope="row"> <TableCell component="th" scope="row">
{index + 1} {index + 1}
</TableCell> </TableCell>
<TableCell align="left">{experiment.Name}</TableCell> <TableCell
className={classes.selection}
align="left"
onClick={() => {
setDisplayStepper(true);
setEngineIndex(experiment.StepIndex);
workflow.setWorkflowManifest({
engineYAML: experiment.ChaosEngine,
});
}}
>
{experiment.Name}
</TableCell>
<TableCell align="left"> <TableCell align="left">
{experiment.Namespace} {experiment.Namespace}
</TableCell> </TableCell>
@ -291,6 +363,18 @@ const WorkflowTable = forwardRef(
{experiment.Application} {experiment.Application}
</TableCell> </TableCell>
<TableCell align="left">{experiment.Probes}</TableCell> <TableCell align="left">{experiment.Probes}</TableCell>
<TableCell>
<IconButton
onClick={() =>
deleteExperiment(experiment.StepIndex)
}
>
<img
src="./icons/bin-red.svg"
alt="delete experiment"
/>
</IconButton>
</TableCell>
</TableRow> </TableRow>
)) ))
) : ( ) : (