1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-18 00:19:49 +01:00

Fix/minor 41 bugs (#329)

* fix: bigger buttons

* feat: dialogue on strategies

* fix: constraint errors

* fix: add constraint button

* fix: variant dialogue size

* fix: remove commented code

* fix: remove unused imports

* fix: change xs to int

* fix: verify constraint config before saving strategy

* fix: reset error when context field is removed

* fix: preserve previous state when updating

* fix: disable lint for useEffect

* fix: update snapshots
This commit is contained in:
Fredrik Strand Oseberg 2021-08-27 13:18:11 +02:00 committed by GitHub
parent c33581081b
commit e0f7a78833
12 changed files with 491 additions and 401 deletions

View File

@ -16,6 +16,7 @@ body {
.MuiButton-root {
border-radius: 3px;
text-transform: none;
font-size: 16px;
}
.skeleton {

View File

@ -16,8 +16,11 @@ const Dialogue = ({
onClick,
onClose,
title,
style,
primaryButtonText,
disabledPrimaryButton = false,
secondaryButtonText,
maxWidth = 'sm',
fullWidth = false,
}) => {
const styles = useStyles();
@ -28,6 +31,7 @@ const Dialogue = ({
fullWidth={fullWidth}
aria-labelledby={'simple-modal-title'}
aria-describedby={'simple-modal-description'}
maxWidth={maxWidth}
>
<DialogTitle className={styles.dialogTitle}>{title}</DialogTitle>
<ConditionallyRender
@ -48,6 +52,7 @@ const Dialogue = ({
variant="contained"
onClick={onClick}
autoFocus
disabled={disabledPrimaryButton}
>
{primaryButtonText || "Yes, I'm sure"}
</Button>

View File

@ -84,7 +84,7 @@ const FeatureToggleListNew = ({
)}
align="left"
>
<span data-loading>name</span>
<span data-loading>Name</span>
</TableCell>
<TableCell
className={classnames(
@ -94,7 +94,7 @@ const FeatureToggleListNew = ({
)}
align="left"
>
<span data-loading>type</span>
<span data-loading>Type</span>
</TableCell>
{getEnvironments().map((env: any) => {
return (
@ -109,7 +109,7 @@ const FeatureToggleListNew = ({
>
<span data-loading>
{env.name === ':global:'
? 'status'
? 'Status'
: env.name}
</span>
</TableCell>

View File

@ -1,19 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Button,
Dialog,
DialogContent,
DialogTitle,
DialogActions,
Typography,
} from '@material-ui/core';
import { Typography } from '@material-ui/core';
import CreateStrategyCard from './AddStrategyCard/AddStrategyCard';
import { useStyles } from './AddStrategy.styles';
import ConditionallyRender from '../../../common/ConditionallyRender';
import { resolveDefaultParamValue } from './utils';
import { getHumanReadbleStrategy } from '../../../../utils/strategy-names';
import Dialogue from '../../../common/Dialogue';
const AddStrategy = ({
strategies,
@ -98,15 +91,15 @@ const AddStrategy = ({
));
return (
<Dialog
<Dialogue
open={showCreateStrategy}
title="Add a new strategy"
aria-labelledby="form-dialog-title"
fullWidth
onClose={() => setShowCreateStrategy(false)}
secondaryButtonText="Cancel"
maxWidth="md"
fullWidth
>
<DialogTitle id="form-dialog-title">Add a new strategy</DialogTitle>
<DialogContent>
<Typography variant="subtitle1" className={styles.subTitle}>
Built in strategies
</Typography>
@ -130,17 +123,7 @@ const AddStrategy = ({
</>
}
/>
</DialogContent>
<DialogActions>
<Button
color="secondary"
onClick={() => setShowCreateStrategy(false)}
>
Cancel
</Button>
</DialogActions>
</Dialog>
</Dialogue>
);
};

View File

@ -1,12 +1,5 @@
import React from 'react';
import { useState } from 'react';
import PropTypes from 'prop-types';
import {
Button,
Dialog,
DialogContent,
DialogTitle,
DialogActions,
} from '@material-ui/core';
import FlexibleStrategy from './FlexibleStrategy';
import DefaultStrategy from './default-strategy';
@ -15,6 +8,7 @@ import GeneralStrategy from './general-strategy';
import StrategyConstraints from '../StrategyConstraint/StrategyConstraintInput';
import { getHumanReadbleStrategyName } from '../../../../utils/strategy-names';
import Dialogue from '../../../common/Dialogue';
const EditStrategyModal = ({
onCancel,
@ -24,6 +18,8 @@ const EditStrategyModal = ({
strategyDefinition,
context,
}) => {
const [constraintError, setConstraintError] = useState({});
const updateParameters = parameters => {
const updatedStrategy = { ...strategy, parameters };
updateStrategy(updatedStrategy);
@ -57,21 +53,53 @@ const EditStrategyModal = ({
const { parameters } = strategy;
const disabledPrimaryButton = Object.keys(constraintError).some(key => {
return constraintError[key];
});
const save = () => {
const { constraints } = strategy;
let valid = true;
constraints.forEach((constraint, index) => {
const { values } = constraint;
if (values.length === 0) {
setConstraintError(prev => ({
...prev,
[`${constraint.contextName}-${index}`]:
'You need to specify at least one value',
}));
valid = false;
}
});
if (valid) {
saveStrategy();
}
};
return (
<Dialog
<Dialogue
open={!!strategy}
aria-labelledby="form-dialog-title"
fullWidth
onClose={onCancel}
onClick={save}
title={`Configure ${getHumanReadbleStrategyName(
strategy.name
)} strategy`}
primaryButtonText="Save"
secondaryButtonText="Cancel"
maxWidth="md"
disabledPrimaryButton={disabledPrimaryButton}
>
<DialogTitle id="form-dialog-title">
Configure {getHumanReadbleStrategyName(strategy.name)} strategy
</DialogTitle>
<DialogContent>
<div>
<StrategyConstraints
updateConstraints={updateConstraints}
constraints={strategy.constraints || []}
constraintError={constraintError}
setConstraintError={setConstraintError}
/>
</div>
@ -84,20 +112,7 @@ const EditStrategyModal = ({
editable
context={context}
/>
</DialogContent>
<DialogActions>
<Button onClick={onCancel} color="secondary">
Cancel
</Button>
<Button
onClick={saveStrategy}
color="primary"
variant="contained"
>
Save
</Button>
</DialogActions>
</Dialog>
</Dialogue>
);
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Tooltip, Typography } from '@material-ui/core';
import { Button, Tooltip, Typography } from '@material-ui/core';
import { Info } from '@material-ui/icons';
import StrategyConstraintInputField from '../StrategyConstraintInputField';
@ -12,6 +12,8 @@ const StrategyConstraintInput = ({
contextNames,
contextFields,
enabled,
constraintError,
setConstraintError,
}) => {
const commonStyles = useCommonStyles();
const addConstraint = evt => {
@ -71,18 +73,21 @@ const StrategyConstraintInput = ({
contextFields={contextFields}
updateConstraint={updateConstraint(index)}
removeConstraint={removeConstraint(index)}
constraintError={constraintError}
setConstraintError={setConstraintError}
/>
))}
</tbody>
</table>
<small>
<a
href="#add-constraint"
<Button
title="Add constraint"
variant="contained"
color="primary"
onClick={addConstraint}
>
Add constraint
</a>
</Button>
</small>
</div>
);

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useEffect } from 'react';
import PropTypes from 'prop-types';
import { IconButton, TextField } from '@material-ui/core';
import { Autocomplete } from '@material-ui/lab';
@ -20,27 +20,47 @@ const StrategyConstraintInputField = ({
updateConstraint,
removeConstraint,
contextFields,
id,
constraintError,
setConstraintError,
}) => {
const [error, setError] = useState();
const commonStyles = useCommonStyles();
const styles = useStyles();
const onBlur = evt => {
evt.preventDefault();
useEffect(() => {
return () => {
resetError();
};
/*eslint-disable-next-line */
}, []);
const checkError = () => {
const values = constraint.values;
const filtered = values.filter(v => v).map(v => v.trim());
if (filtered.length !== values.length) {
updateConstraint(filtered, 'values');
}
if (filtered.length === 0) {
setError('You need to specify at least one value');
setConstraintError(prev => ({
...prev,
[id]: 'You need to specify at least one value',
}));
} else {
setError(undefined);
resetError();
}
};
const resetError = () =>
setConstraintError(prev => ({ ...prev, [id]: undefined }));
const commonStyles = useCommonStyles();
const styles = useStyles();
const onBlur = evt => {
evt.preventDefault();
checkError();
};
const handleChangeValue = selectedOptions => {
const values = selectedOptions ? selectedOptions.map(o => o.value) : [];
updateConstraint(values, 'values');
checkError();
};
const constraintContextNames = contextFields.map(f => ({
@ -59,9 +79,11 @@ const StrategyConstraintInputField = ({
: undefined;
const values = constraint.values.map(v => ({ value: v, label: v }));
const error = constraintError[id];
return (
<tr className={commonStyles.contentSpacingY}>
<td>
<td className={styles.tableCell}>
<MySelect
name="contextName"
label="Context Field"
@ -73,7 +95,7 @@ const StrategyConstraintInputField = ({
className={styles.contextField}
/>
</td>
<td>
<td className={styles.tableCell}>
<MySelect
name="operator"
label="Operator"
@ -85,23 +107,29 @@ const StrategyConstraintInputField = ({
className={styles.operator}
/>
</td>
<td style={{ width: '100%' }}>
<td className={styles.tableCell} style={{ width: '100%' }}>
<ConditionallyRender
condition={options}
show={
<div className={styles.inputContainer}>
<Autocomplete
multiple
size="small"
options={options}
value={values || []}
getOptionLabel={option => option.label}
onBlur={onBlur}
onFocus={() => resetError()}
getOptionSelected={(option, value) =>
option.value === value.value
}
filterSelectedOptions
filterOptions={options =>
options.filter(
o => !values.some(v => v.value === o.value)
o =>
!values.some(
v => v.value === o.value
)
)
}
onChange={(evt, values) =>
@ -111,26 +139,42 @@ const StrategyConstraintInputField = ({
<TextField
{...params}
variant="outlined"
label="Values"
label={'Values'}
error={Boolean(error)}
helperText={error}
FormHelperTextProps={{
classes: {
root: styles.helperText,
},
}}
/>
)}
/>
</div>
}
elseShow={
<div className={styles.inputContainer}>
<InputListField
name="values"
error={error}
error={Boolean(error)}
onBlur={onBlur}
values={constraint.values}
label="Values (v1, v2, v3)"
updateValues={values =>
updateConstraint(values, 'values')
}
helperText={error}
FormHelperTextProps={{
classes: {
root: styles.helperText,
},
}}
/>
</div>
}
/>
</td>
<td>
<td className={styles.tableCell}>
<IconButton onClick={removeConstraint}>
<Delete />
</IconButton>

View File

@ -7,4 +7,21 @@ export const useStyles = makeStyles({
operator: {
minWidth: '105px',
},
inputContainer: {
position: 'relative',
},
inputError: {
position: 'absolute',
fontSize: '0.9rem',
color: 'red',
top: '10px',
left: '12px',
},
tableCell: {
paddingBottom: '1.25rem',
},
helperText: {
position: 'absolute',
top: '35px',
},
});

View File

@ -187,6 +187,8 @@ const AddVariant = ({
primaryButtonText="Save"
secondaryButtonText="Cancel"
title={title}
fullWidth
maxWidth="md"
>
<form onSubmit={submit} className={commonStyles.contentSpacingY}>
<p style={{ color: 'red' }}>{error.general}</p>
@ -195,6 +197,7 @@ const AddVariant = ({
name="name"
placeholder=""
className={commonStyles.fullWidth}
style={{ maxWidth: '350px' }}
helperText={error.name}
value={data.name || ''}
error={Boolean(error.name)}
@ -250,11 +253,17 @@ const AddVariant = ({
title="Passed to the variant object. Can be anything
(json, value, csv)"
>
<Info style={{ width: '18.5px', height: '18.5px' }} />
<Info
style={{
width: '18.5px',
height: '18.5px',
color: 'grey',
}}
/>
</Tooltip>
</p>
<Grid container>
<Grid item md={3}>
<Grid item md={2} sm={2} xs={4}>
<MySelect
name="type"
label="Type"
@ -262,10 +271,10 @@ const AddVariant = ({
value={payload.type}
options={payloadOptions}
onChange={onPayload}
style={{ minWidth: '100px' }}
style={{ minWidth: '100px', width: '100%' }}
/>
</Grid>
<Grid item md={9}>
<Grid item md={8} sm={8} xs={6}>
<TextField
rows={1}
label="Value"
@ -281,13 +290,14 @@ const AddVariant = ({
<ConditionallyRender
condition={overrides.length > 0}
show={
<p style={{ marginBottom: '.5rem' }}>
<p style={{ marginBottom: '1rem' }}>
<strong>Overrides </strong>
<Tooltip title="Here you can specify which users should get this variant.">
<Info
style={{
width: '18.5px',
height: '18.5px',
color: 'grey',
}}
/>
</Tooltip>
@ -301,7 +311,13 @@ const AddVariant = ({
updateOverrideValues={updateOverrideValues}
updateValues={updateOverrideValues}
/>
<Button onClick={onAddOverride}>Add override</Button>{' '}
<Button
onClick={onAddOverride}
variant="contained"
color="primary"
>
Add override
</Button>{' '}
</form>
</Dialog>
);

View File

@ -41,7 +41,13 @@ const OverrideConfig = ({
return (
<Grid container key={`override=${i}`} alignItems="center">
<Grid item md={3} className={styles.contextFieldSelect}>
<Grid
item
md={3}
sm={3}
xs={3}
className={styles.contextFieldSelect}
>
<MySelect
name="contextName"
label="Context Field"
@ -53,7 +59,7 @@ const OverrideConfig = ({
onChange={updateOverrideType(i)}
/>
</Grid>
<Grid md={7} item>
<Grid md={7} sm={7} xs={6} item>
<ConditionallyRender
condition={legalValues && legalValues.length > 0}
show={
@ -68,6 +74,7 @@ const OverrideConfig = ({
getOptionLabel={option => option}
defaultValue={o.values}
value={o.values}
style={{ width: '100%' }}
filterSelectedOptions
size="small"
renderInput={params => (
@ -75,6 +82,7 @@ const OverrideConfig = ({
{...params}
variant="outlined"
label="Legal values"
style={{ width: '100%' }}
/>
)}
/>

View File

@ -16,8 +16,8 @@ export const Footer = () => {
<Grid item md={4} xs={12}>
<ShowApiDetailsContainer />
</Grid>
<Grid item xs="12" md="auto">
<Grid container spacing={7} direction="row" >
<Grid item xs={12} md="auto">
<Grid container spacing={7} direction="row">
<Grid item>
<section title="Unleash SDK">
<h4>Server SDKs</h4>
@ -129,7 +129,7 @@ export const Footer = () => {
</List>
</section>
</Grid>
<Grid item justifyContent="center">
<Grid item>
<section title="Unleash SDK">
<h4>Frontend SDKs</h4>
<List className={styles.list} dense>
@ -263,8 +263,6 @@ export const Footer = () => {
</Grid>
</Grid>
</Grid>
</footer>
);
};

View File

@ -199,7 +199,6 @@ exports[`should render DrawerMenu 1`] = `
</div>
<div
className="MuiGrid-root MuiGrid-item"
justifyContent="center"
>
<section
title="Unleash SDK"
@ -602,7 +601,6 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
</div>
<div
className="MuiGrid-root MuiGrid-item"
justifyContent="center"
>
<section
title="Unleash SDK"