1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-29 01:15:48 +02:00

fix: prevent strategy variant weight from going into negative numbers on Frontend (#7460)

Added validation if sum goes over 100%. Remaining split is never negative
This commit is contained in:
Tymoteusz Czech 2024-06-27 11:06:59 +02:00 committed by GitHub
parent 1cdbd21212
commit 083273b49b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 98 additions and 6 deletions

View File

@ -114,4 +114,70 @@ describe('updateWeightEdit', () => {
);
expect(weights).toEqual([500, 500, 0]);
});
describe('sum over 100% does not result in negative weight', () => {
it('when 2 items exceed 100%', () => {
const variants = [
{
...variantTemplate,
weightType: 'fix' as const,
weight: 600,
id: '1',
name: 'A',
},
{
...variantTemplate,
weightType: 'fix' as const,
weight: 600,
id: '2',
name: 'B',
},
{ ...variantTemplate, id: '3', name: 'C' },
];
const weights = updateWeightEdit(variants, 1000).map(
(variant) => variant.weight,
);
expect(weights).toEqual([600, 600, 0]);
});
it('when sum of multiple items exceed 100%', () => {
const variants = [
{
...variantTemplate,
weightType: 'fix' as const,
weight: 400,
id: '1',
name: 'A',
},
{
...variantTemplate,
weightType: 'fix' as const,
weight: 450,
id: '2',
name: 'B',
},
{
...variantTemplate,
id: '3',
name: 'C',
},
{ ...variantTemplate, id: '4', name: 'D' },
{
...variantTemplate,
id: '5',
name: 'E',
weightType: 'fix' as const,
weight: 350,
},
];
const weights = updateWeightEdit(variants, 1000).map(
(variant) => variant.weight,
);
expect(weights).toEqual([400, 450, 0, 0, 350]);
});
});
});

View File

@ -143,7 +143,7 @@ export function updateWeightEdit(
);
const getPercentage = () =>
Math.round(remainingPercentage / variableVariantCount);
Math.max(Math.round(remainingPercentage / variableVariantCount), 0);
return variants.map((variant) => {
if (variant.weightType !== weightTypes.FIX) {

View File

@ -173,6 +173,7 @@ interface IVariantFormProps {
error?: string;
disableOverrides?: boolean;
decorationColor?: string;
weightsError?: boolean;
}
export const VariantForm = ({
@ -183,6 +184,7 @@ export const VariantForm = ({
error,
disableOverrides = false,
decorationColor,
weightsError,
}: IVariantFormProps) => {
const [name, setName] = useState(variant.name);
const [customPercentage, setCustomPercentage] = useState(
@ -333,6 +335,11 @@ export const VariantForm = ({
}
}, [variant.weight]);
const percentageError =
errors?.percentage || weightsError
? 'Total weight may not exceed 100%'
: '';
return (
<StyledVariantForm data-testid='VARIANT'>
<StyledDecoration color={decorationColor} />
@ -394,8 +401,8 @@ export const VariantForm = ({
data-testid='VARIANT_WEIGHT_INPUT'
type='number'
label='Variant weight'
error={Boolean(errors.percentage)}
errorText={errors.percentage}
error={Boolean(percentageError)}
errorText={percentageError}
value={percentage}
onChange={(e) =>
onSetPercentage(e.target.value)

View File

@ -115,6 +115,12 @@ export const NewStrategyVariants: FC<{
});
};
const variantWeightsError =
variantsEdit.reduce(
(acc, variant) => acc + (variant.weight || 0),
0,
) !== 1000;
return (
<>
<StyledVariantsHeader>
@ -178,6 +184,7 @@ export const NewStrategyVariants: FC<{
i % theme.palette.variants.length
]
}
weightsError={variantWeightsError}
/>
))}
</StyledVariantForms>
@ -192,7 +199,10 @@ export const NewStrategyVariants: FC<{
>
Add variant
</PermissionButton>
<SplitPreviewSlider variants={variantsEdit} />
<SplitPreviewSlider
variants={variantsEdit}
weightsError={variantWeightsError}
/>
</>
);
};

View File

@ -72,9 +72,13 @@ const StyledTypographySubtitle = styled(Typography)(({ theme }) => ({
interface ISplitPreviewSliderProps {
variants: IFeatureVariant[];
weightsError?: boolean;
}
const SplitPreviewSlider = ({ variants }: ISplitPreviewSliderProps) => {
const SplitPreviewSlider = ({
variants,
weightsError,
}: ISplitPreviewSliderProps) => {
if (variants.length < 1) {
return null;
}
@ -108,7 +112,12 @@ const SplitPreviewSlider = ({ variants }: ISplitPreviewSliderProps) => {
{' '}
<StyledSegment>
<StyledSegmentTrack index={index} />
<StyledTypographySubtitle variant='subtitle2'>
<StyledTypographySubtitle
variant='subtitle2'
color={
weightsError ? 'error' : 'inherit'
}
>
{value}%
</StyledTypographySubtitle>
</StyledSegment>