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

refactor: fix small issues around custom strategies (#1181)

* refactor: validate strategy name on blur

* refactor: remove strategy parameter type text in favor of docs

* refactor: improve pie chart rendering

* refactor: show icons for all feature strategies

* refactor: fix list parameter add button style
This commit is contained in:
olav 2022-08-02 10:10:01 +02:00 committed by GitHub
parent 79aa2d4ebe
commit 710ebe08b3
10 changed files with 67 additions and 88 deletions

View File

@ -1,51 +1,45 @@
import { useTheme } from '@mui/material';
import { CSSProperties } from 'react';
interface IPercentageCircleProps {
styles?: object;
percentage: number;
secondaryPieColor?: string;
className?: string;
hideNumber?: boolean;
size?: `${number}rem`;
}
const PercentageCircle = ({
styles,
percentage,
secondaryPieColor,
hideNumber,
...rest
size = '4rem',
}: IPercentageCircleProps) => {
const theme = useTheme();
let circle = {
height: '65px',
width: '65px',
borderRadius: '50%',
color: '#fff',
backgroundColor: theme.palette.grey[200],
backgroundImage: `conic-gradient(${
theme.palette.primary.light
} ${percentage}%, ${secondaryPieColor || theme.palette.grey[200]} 1%)`,
const style: CSSProperties = {
display: 'block',
borderRadius: '100%',
transform: 'rotate(-90deg)',
height: size,
width: size,
background: theme.palette.grey[200],
};
if (percentage === 100) {
return (
<div
style={{
...circle,
...styles,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '12px',
}}
>
{hideNumber ? null : '100%'}
</div>
);
}
// The percentage circle used to be drawn by CSS with a conic-gradient,
// but the result was either jagged or blurry. SVG seems to look better.
// See https://stackoverflow.com/a/70659532.
const r = 100 / (2 * Math.PI);
const d = 2 * r;
return <div style={{ ...circle, ...styles }} {...rest} />;
return (
<svg viewBox={`0 0 ${d} ${d}`} style={style} aria-hidden>
<circle
r={r}
cx={r}
cy={r}
fill="none"
stroke={theme.palette.primary.light}
strokeWidth={d}
strokeDasharray={`${percentage} 100`}
/>
</svg>
);
};
export default PercentageCircle;

View File

@ -13,24 +13,17 @@ export const FeatureStrategyIcons = ({
return null;
}
const strategyNames = strategies.map(strategy => strategy.name);
const uniqueStrategyNames = uniqueValues(strategyNames);
return (
<StyledList aria-label="Feature strategies">
{uniqueStrategyNames.map(strategyName => (
<StyledListItem key={strategyName}>
<FeatureStrategyIcon strategyName={strategyName} />
{strategies.map(strategy => (
<StyledListItem key={strategy.name}>
<FeatureStrategyIcon strategyName={strategy.name} />
</StyledListItem>
))}
</StyledList>
);
};
const uniqueValues = <T,>(values: T[]): T[] => {
return [...new Set(values)];
};
const StyledList = styled('ul')(() => ({
all: 'unset',
display: 'flex',

View File

@ -43,29 +43,25 @@ export const StrategyExecution = ({ strategy }: IStrategyExecutionProps) => {
switch (key) {
case 'rollout':
case 'Rollout':
const percentage = parseParameterNumber(parameters[key]);
return (
<Box
className={styles.summary}
key={key}
sx={{ display: 'flex', alignItems: 'center' }}
>
<PercentageCircle
hideNumber
percentage={parseParameterNumber(
parameters[key]
)}
styles={{
width: '2rem',
height: '2rem',
marginRight: '1rem',
}}
/>
<Box sx={{ mr: '1rem' }}>
<PercentageCircle
percentage={percentage}
size="2rem"
/>
</Box>
<div>
<Chip
color="success"
variant="outlined"
size="small"
label={`${parameters[key]}%`}
label={`${percentage}%`}
/>{' '}
of your base{' '}
{constraints.length > 0

View File

@ -23,6 +23,7 @@ export const useStyles = makeStyles()(theme => ({
maxWidth: '270px',
marginTop: '0.25rem',
fontSize: theme.fontSizes.smallBody,
textAlign: 'right',
[theme.breakpoints.down(700)]: {
display: 'none',
},
@ -33,7 +34,7 @@ export const useStyles = makeStyles()(theme => ({
fontSize: theme.fontSizes.subHeader,
},
percentageCircle: {
transform: 'scale(0.85)',
margin: '0 1rem',
[theme.breakpoints.down(500)]: {
display: 'none',
},

View File

@ -73,11 +73,9 @@ const FeatureOverviewEnvironmentMetrics = ({
hour
</p>
</div>
<PercentageCircle
className={styles.percentageCircle}
percentage={percentage}
data-loading
/>
<div className={styles.percentageCircle} data-loading>
<PercentageCircle percentage={percentage} size="3rem" />
</div>
</div>
);
};

View File

@ -97,7 +97,13 @@ const StrategyInputList = ({
<ConditionallyRender
condition={!disabled}
show={
<div style={{ display: 'flex', alignItems: 'center' }}>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '1rem',
}}
>
<TextField
name={`input_field`}
variant="outlined"
@ -116,6 +122,7 @@ const StrategyInputList = ({
<Button
onClick={setValue}
data-testid={ADD_TO_STRATEGY_INPUT_LIST}
variant="outlined"
color="secondary"
startIcon={<Add />}
>

View File

@ -83,6 +83,7 @@ export const CreateStrategy = () => {
handleCancel={handleCancel}
strategyName={strategyName}
setStrategyName={setStrategyName}
validateStrategyName={validateStrategyName}
strategyDesc={strategyDesc}
setStrategyDesc={setStrategyDesc}
params={params}

View File

@ -12,6 +12,7 @@ interface IStrategyFormProps {
strategyDesc: string;
params: IStrategyParameter[];
setStrategyName: React.Dispatch<React.SetStateAction<string>>;
validateStrategyName?: () => void;
setStrategyDesc: React.Dispatch<React.SetStateAction<string>>;
setParams: React.Dispatch<React.SetStateAction<IStrategyParameter[]>>;
handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
@ -31,6 +32,7 @@ export const StrategyForm: React.FC<IStrategyFormProps> = ({
params,
setParams,
setStrategyName,
validateStrategyName,
setStrategyDesc,
errors,
mode,
@ -65,7 +67,8 @@ export const StrategyForm: React.FC<IStrategyFormProps> = ({
onChange={e => setStrategyName(trim(e.target.value))}
error={Boolean(errors.name)}
errorText={errors.name}
onFocus={() => clearErrors()}
onFocus={clearErrors}
onBlur={validateStrategyName}
/>
<p className={styles.inputDescription}>
What is your strategy description?

View File

@ -29,12 +29,6 @@ export const useStyles = makeStyles()(theme => ({
inputDescription: {
marginBottom: '0.5rem',
},
typeDescription: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.grey[600],
top: '-13px',
position: 'relative',
},
errorMessage: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,

View File

@ -17,31 +17,22 @@ const paramTypesOptions = [
{
key: 'string',
label: 'string',
description: 'A string is a collection of characters',
},
{
key: 'percentage',
label: 'percentage',
description:
'Percentage is used when you want to make your feature visible to a process part of your customers',
},
{
key: 'list',
label: 'list',
description:
'A list is used when you want to define several parameters that must be met before your feature becomes visible to your customers',
},
{
key: 'number',
label: 'number',
description:
'Number is used when you have one or more digits that must be met for your feature to be visible to your customers',
},
{
key: 'boolean',
label: 'boolean',
description:
'A boolean value represents a truth value, which is either true or false',
},
];
@ -68,11 +59,6 @@ export const StrategyParameter = ({
set({ type });
};
const renderParamTypeDescription = () => {
return paramTypesOptions.find(param => param.key === input.type)
?.description;
};
return (
<div className={styles.paramsContainer}>
<Divider className={styles.divider} />
@ -80,7 +66,16 @@ export const StrategyParameter = ({
condition={index === 0}
show={
<p className={styles.input}>
The parameters define what the strategy will look like.
Parameters let you provide arguments to your strategy
that it can access for evaluation. Read more in the{' '}
<a
href="https://docs.getunleash.io/advanced/custom_activation_strategy#parameter-types"
target="_blank"
rel="noreferrer"
>
parameter types documentation
</a>
.
</p>
}
/>
@ -114,9 +109,6 @@ export const StrategyParameter = ({
id={`prop-type-${index}-select`}
className={styles.input}
/>
<p className={styles.typeDescription}>
{renderParamTypeDescription()}
</p>
<Input
rows={2}
multiline