mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
Feat: strategy variant slider (#4344)
## About the changes 
This commit is contained in:
parent
2b565aeef7
commit
909831db6f
@ -4,6 +4,7 @@ import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
|||||||
import SelectMenu from 'component/common/select';
|
import SelectMenu from 'component/common/select';
|
||||||
import { OverrideConfig } from 'component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantOverrides/VariantOverrides';
|
import { OverrideConfig } from 'component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantOverrides/VariantOverrides';
|
||||||
import {
|
import {
|
||||||
|
Box,
|
||||||
Button,
|
Button,
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
@ -28,8 +29,20 @@ const StyledVariantForm = styled('div')(({ theme }) => ({
|
|||||||
padding: theme.spacing(3),
|
padding: theme.spacing(3),
|
||||||
marginBottom: theme.spacing(3),
|
marginBottom: theme.spacing(3),
|
||||||
borderRadius: theme.shape.borderRadiusLarge,
|
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 }) => ({
|
const StyledDeleteButtonTooltip = styled(Tooltip)(({ theme }) => ({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: theme.spacing(2),
|
top: theme.spacing(2),
|
||||||
@ -151,6 +164,7 @@ interface IVariantFormProps {
|
|||||||
removeVariant: (variantId: string) => void;
|
removeVariant: (variantId: string) => void;
|
||||||
error?: string;
|
error?: string;
|
||||||
disableOverrides?: boolean;
|
disableOverrides?: boolean;
|
||||||
|
decorationColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VariantForm = ({
|
export const VariantForm = ({
|
||||||
@ -160,6 +174,7 @@ export const VariantForm = ({
|
|||||||
removeVariant,
|
removeVariant,
|
||||||
error,
|
error,
|
||||||
disableOverrides = false,
|
disableOverrides = false,
|
||||||
|
decorationColor,
|
||||||
}: IVariantFormProps) => {
|
}: IVariantFormProps) => {
|
||||||
const [name, setName] = useState(variant.name);
|
const [name, setName] = useState(variant.name);
|
||||||
const [customPercentage, setCustomPercentage] = useState(
|
const [customPercentage, setCustomPercentage] = useState(
|
||||||
@ -306,6 +321,7 @@ export const VariantForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledVariantForm data-testid="VARIANT">
|
<StyledVariantForm data-testid="VARIANT">
|
||||||
|
<StyledDecoration color={decorationColor} />
|
||||||
<StyledDeleteButtonTooltip
|
<StyledDeleteButtonTooltip
|
||||||
arrow
|
arrow
|
||||||
title={
|
title={
|
||||||
|
@ -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;
|
@ -7,9 +7,10 @@ import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvi
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { WeightType } from '../../../constants/variantTypes';
|
import { WeightType } from '../../../constants/variantTypes';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { styled, Typography } from '@mui/material';
|
import { styled, Typography, useTheme } from '@mui/material';
|
||||||
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
import { useRequiredQueryParam } from 'hooks/useRequiredQueryParam';
|
||||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||||
|
import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
|
||||||
|
|
||||||
const StyledVariantForms = styled('div')({
|
const StyledVariantForms = styled('div')({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -25,6 +26,7 @@ export const StrategyVariants: FC<{
|
|||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const environment = useRequiredQueryParam('environmentId');
|
const environment = useRequiredQueryParam('environmentId');
|
||||||
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
|
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
|
||||||
|
const theme = useTheme();
|
||||||
const stickiness =
|
const stickiness =
|
||||||
strategy?.parameters && 'stickiness' in strategy?.parameters
|
strategy?.parameters && 'stickiness' in strategy?.parameters
|
||||||
? String(strategy.parameters.stickiness)
|
? String(strategy.parameters.stickiness)
|
||||||
@ -88,7 +90,7 @@ export const StrategyVariants: FC<{
|
|||||||
Variants
|
Variants
|
||||||
</Typography>
|
</Typography>
|
||||||
<StyledVariantForms>
|
<StyledVariantForms>
|
||||||
{variantsEdit.map(variant => (
|
{variantsEdit.map((variant, i) => (
|
||||||
<VariantForm
|
<VariantForm
|
||||||
disableOverrides={true}
|
disableOverrides={true}
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
@ -107,6 +109,11 @@ export const StrategyVariants: FC<{
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
decorationColor={
|
||||||
|
theme.palette.variants[
|
||||||
|
i % theme.palette.variants.length
|
||||||
|
]
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StyledVariantForms>
|
</StyledVariantForms>
|
||||||
@ -119,6 +126,9 @@ export const StrategyVariants: FC<{
|
|||||||
>
|
>
|
||||||
Add variant
|
Add variant
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
|
<SplitPreviewSlider
|
||||||
|
values={variantsEdit.map(variant => variant.weight / 10)}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -94,4 +94,17 @@ export const colors = {
|
|||||||
600: '#1f3751',
|
600: '#1f3751',
|
||||||
500: '#0e2840',
|
500: '#0e2840',
|
||||||
},
|
},
|
||||||
|
variants: [
|
||||||
|
'#BEBBF3',
|
||||||
|
'#FFC46F',
|
||||||
|
'#B0D182',
|
||||||
|
'#96D2FA',
|
||||||
|
'#F7E3AE',
|
||||||
|
'#7FBAA9',
|
||||||
|
'#D3B9DB',
|
||||||
|
'#FBC5A0',
|
||||||
|
'#DDE7B5',
|
||||||
|
'#9EC4E3',
|
||||||
|
'#F8B6CC',
|
||||||
|
] as string[],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { createTheme } from '@mui/material/styles';
|
import { createTheme } from '@mui/material/styles';
|
||||||
import { alpha } from '@mui/material';
|
import { alpha } from '@mui/material';
|
||||||
import { focusable } from 'themes/themeStyles';
|
import { focusable } from 'themes/themeStyles';
|
||||||
|
import { colors } from './colors';
|
||||||
|
|
||||||
const actionColors = {
|
const actionColors = {
|
||||||
0.54: 'rgba(223, 222, 255, 0.54)',
|
0.54: 'rgba(223, 222, 255, 0.54)',
|
||||||
@ -271,6 +272,7 @@ const theme = {
|
|||||||
// A400: '#A6000E',
|
// A400: '#A6000E',
|
||||||
// A700: '#A6000E',
|
// A700: '#A6000E',
|
||||||
},
|
},
|
||||||
|
variants: colors.variants,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -257,6 +257,7 @@ const theme = {
|
|||||||
// A400: '#A6000E',
|
// A400: '#A6000E',
|
||||||
// A700: '#A6000E',
|
// A700: '#A6000E',
|
||||||
},
|
},
|
||||||
|
variants: colors.variants,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -112,6 +112,11 @@ declare module '@mui/material/styles' {
|
|||||||
disabled: string;
|
disabled: string;
|
||||||
expanded: string;
|
expanded: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variants, percentage split in strategies
|
||||||
|
**/
|
||||||
|
variants: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Theme extends CustomTheme {}
|
interface Theme extends CustomTheme {}
|
||||||
|
Loading…
Reference in New Issue
Block a user