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:
parent
1cdbd21212
commit
083273b49b
@ -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]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user