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]); 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 = () => const getPercentage = () =>
Math.round(remainingPercentage / variableVariantCount); Math.max(Math.round(remainingPercentage / variableVariantCount), 0);
return variants.map((variant) => { return variants.map((variant) => {
if (variant.weightType !== weightTypes.FIX) { if (variant.weightType !== weightTypes.FIX) {

View File

@ -173,6 +173,7 @@ interface IVariantFormProps {
error?: string; error?: string;
disableOverrides?: boolean; disableOverrides?: boolean;
decorationColor?: string; decorationColor?: string;
weightsError?: boolean;
} }
export const VariantForm = ({ export const VariantForm = ({
@ -183,6 +184,7 @@ export const VariantForm = ({
error, error,
disableOverrides = false, disableOverrides = false,
decorationColor, decorationColor,
weightsError,
}: IVariantFormProps) => { }: IVariantFormProps) => {
const [name, setName] = useState(variant.name); const [name, setName] = useState(variant.name);
const [customPercentage, setCustomPercentage] = useState( const [customPercentage, setCustomPercentage] = useState(
@ -333,6 +335,11 @@ export const VariantForm = ({
} }
}, [variant.weight]); }, [variant.weight]);
const percentageError =
errors?.percentage || weightsError
? 'Total weight may not exceed 100%'
: '';
return ( return (
<StyledVariantForm data-testid='VARIANT'> <StyledVariantForm data-testid='VARIANT'>
<StyledDecoration color={decorationColor} /> <StyledDecoration color={decorationColor} />
@ -394,8 +401,8 @@ export const VariantForm = ({
data-testid='VARIANT_WEIGHT_INPUT' data-testid='VARIANT_WEIGHT_INPUT'
type='number' type='number'
label='Variant weight' label='Variant weight'
error={Boolean(errors.percentage)} error={Boolean(percentageError)}
errorText={errors.percentage} errorText={percentageError}
value={percentage} value={percentage}
onChange={(e) => onChange={(e) =>
onSetPercentage(e.target.value) 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 ( return (
<> <>
<StyledVariantsHeader> <StyledVariantsHeader>
@ -178,6 +184,7 @@ export const NewStrategyVariants: FC<{
i % theme.palette.variants.length i % theme.palette.variants.length
] ]
} }
weightsError={variantWeightsError}
/> />
))} ))}
</StyledVariantForms> </StyledVariantForms>
@ -192,7 +199,10 @@ export const NewStrategyVariants: FC<{
> >
Add variant Add variant
</PermissionButton> </PermissionButton>
<SplitPreviewSlider variants={variantsEdit} /> <SplitPreviewSlider
variants={variantsEdit}
weightsError={variantWeightsError}
/>
</> </>
); );
}; };

View File

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