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

Feat: strategy variant slider (#4344)

## About the changes

![image](https://github.com/Unleash/unleash/assets/2625371/835cdb1f-c0ad-4966-81a3-9e35944ee1ae)
This commit is contained in:
Tymoteusz Czech 2023-07-26 11:36:16 +02:00 committed by GitHub
parent 2b565aeef7
commit 909831db6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 2 deletions

View File

@ -4,6 +4,7 @@ import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
import SelectMenu from 'component/common/select';
import { OverrideConfig } from 'component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantOverrides/VariantOverrides';
import {
Box,
Button,
FormControlLabel,
IconButton,
@ -28,8 +29,20 @@ const StyledVariantForm = styled('div')(({ theme }) => ({
padding: theme.spacing(3),
marginBottom: theme.spacing(3),
borderRadius: theme.shape.borderRadiusLarge,
overflow: 'hidden',
}));
const StyledDecoration = styled('div')<{ color?: string }>(
({ theme, color }) => ({
position: 'absolute',
left: 0,
top: 0,
height: '100%',
background: color || 'transparent',
width: theme.spacing(1),
})
);
const StyledDeleteButtonTooltip = styled(Tooltip)(({ theme }) => ({
position: 'absolute',
top: theme.spacing(2),
@ -151,6 +164,7 @@ interface IVariantFormProps {
removeVariant: (variantId: string) => void;
error?: string;
disableOverrides?: boolean;
decorationColor?: string;
}
export const VariantForm = ({
@ -160,6 +174,7 @@ export const VariantForm = ({
removeVariant,
error,
disableOverrides = false,
decorationColor,
}: IVariantFormProps) => {
const [name, setName] = useState(variant.name);
const [customPercentage, setCustomPercentage] = useState(
@ -306,6 +321,7 @@ export const VariantForm = ({
return (
<StyledVariantForm data-testid="VARIANT">
<StyledDecoration color={decorationColor} />
<StyledDeleteButtonTooltip
arrow
title={

View File

@ -0,0 +1,72 @@
import { Box, Typography, styled } from '@mui/material';
type SplitPreviewSliderProps = {
values: number[];
};
const StyledContainer = styled(Box)(({ theme }) => ({
display: 'flex',
width: '100%',
position: 'relative',
}));
const StyledTrack = styled(Box)(({ theme }) => ({
position: 'absolute',
height: theme.spacing(3),
width: '100%',
display: 'flex',
overflow: 'hidden',
}));
const StyledSegment = styled(Box)(({ theme }) => ({
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}));
const StyledSegmentTrack = styled(Box)(({ theme }) => ({
height: theme.spacing(3),
width: '100%',
position: 'relative',
}));
const SplitPreviewSlider = ({ values }: SplitPreviewSliderProps) => {
if (values.length < 2) {
return null;
}
return (
<Box sx={theme => ({ marginTop: theme.spacing(2) })}>
<Typography
variant="body2"
sx={theme => ({ marginY: theme.spacing(1) })}
>
Split preview
</Typography>
<StyledContainer>
<StyledTrack />
{values.map((value, index) => (
<StyledSegment key={index} sx={{ width: `${value}%` }}>
<StyledSegmentTrack
sx={theme => ({
background:
theme.palette.variants[
index % theme.palette.variants.length
],
})}
/>
<Typography
variant="subtitle2"
sx={theme => ({ marginTop: theme.spacing(1) })}
>
{value}%
</Typography>
</StyledSegment>
))}
</StyledContainer>
</Box>
);
};
export default SplitPreviewSlider;

View File

@ -7,9 +7,10 @@ import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvi
import { v4 as uuidv4 } from 'uuid';
import { WeightType } from '../../../constants/variantTypes';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { styled, Typography } from '@mui/material';
import { styled, Typography, useTheme } from '@mui/material';
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
import { IFeatureStrategy } from 'interfaces/strategy';
import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
const StyledVariantForms = styled('div')({
display: 'flex',
@ -25,6 +26,7 @@ export const StrategyVariants: FC<{
const projectId = useRequiredPathParam('projectId');
const environment = useRequiredQueryParam('environmentId');
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
const theme = useTheme();
const stickiness =
strategy?.parameters && 'stickiness' in strategy?.parameters
? String(strategy.parameters.stickiness)
@ -88,7 +90,7 @@ export const StrategyVariants: FC<{
Variants
</Typography>
<StyledVariantForms>
{variantsEdit.map(variant => (
{variantsEdit.map((variant, i) => (
<VariantForm
disableOverrides={true}
key={variant.id}
@ -107,6 +109,11 @@ export const StrategyVariants: FC<{
)
)
}
decorationColor={
theme.palette.variants[
i % theme.palette.variants.length
]
}
/>
))}
</StyledVariantForms>
@ -119,6 +126,9 @@ export const StrategyVariants: FC<{
>
Add variant
</PermissionButton>
<SplitPreviewSlider
values={variantsEdit.map(variant => variant.weight / 10)}
/>
</>
);
};

View File

@ -94,4 +94,17 @@ export const colors = {
600: '#1f3751',
500: '#0e2840',
},
variants: [
'#BEBBF3',
'#FFC46F',
'#B0D182',
'#96D2FA',
'#F7E3AE',
'#7FBAA9',
'#D3B9DB',
'#FBC5A0',
'#DDE7B5',
'#9EC4E3',
'#F8B6CC',
] as string[],
} as const;

View File

@ -1,6 +1,7 @@
import { createTheme } from '@mui/material/styles';
import { alpha } from '@mui/material';
import { focusable } from 'themes/themeStyles';
import { colors } from './colors';
const actionColors = {
0.54: 'rgba(223, 222, 255, 0.54)',
@ -271,6 +272,7 @@ const theme = {
// A400: '#A6000E',
// A700: '#A6000E',
},
variants: colors.variants,
},
};

View File

@ -257,6 +257,7 @@ const theme = {
// A400: '#A6000E',
// A700: '#A6000E',
},
variants: colors.variants,
},
};

View File

@ -112,6 +112,11 @@ declare module '@mui/material/styles' {
disabled: string;
expanded: string;
};
/**
* Variants, percentage split in strategies
**/
variants: string[];
}
interface Theme extends CustomTheme {}