diff --git a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm.tsx b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm.tsx index 62565c9b39..fa16a51ac0 100644 --- a/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm.tsx @@ -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 ( + ({ + 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 ( + ({ marginTop: theme.spacing(2) })}> + ({ marginY: theme.spacing(1) })} + > + Split preview + + + + {values.map((value, index) => ( + + ({ + background: + theme.palette.variants[ + index % theme.palette.variants.length + ], + })} + /> + ({ marginTop: theme.spacing(1) })} + > + {value}% + + + ))} + + + ); +}; + +export default SplitPreviewSlider; diff --git a/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx b/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx index 931d7bf44d..d6e6ad2e54 100644 --- a/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx +++ b/frontend/src/component/feature/StrategyTypes/StrategyVariants.tsx @@ -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([]); + const theme = useTheme(); const stickiness = strategy?.parameters && 'stickiness' in strategy?.parameters ? String(strategy.parameters.stickiness) @@ -88,7 +90,7 @@ export const StrategyVariants: FC<{ Variants - {variantsEdit.map(variant => ( + {variantsEdit.map((variant, i) => ( ))} @@ -119,6 +126,9 @@ export const StrategyVariants: FC<{ > Add variant + variant.weight / 10)} + /> ); }; diff --git a/frontend/src/themes/colors.ts b/frontend/src/themes/colors.ts index 3cff2a77a8..47cba0aba6 100644 --- a/frontend/src/themes/colors.ts +++ b/frontend/src/themes/colors.ts @@ -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; diff --git a/frontend/src/themes/dark-theme.ts b/frontend/src/themes/dark-theme.ts index f035c732dc..50f11b4df1 100644 --- a/frontend/src/themes/dark-theme.ts +++ b/frontend/src/themes/dark-theme.ts @@ -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, }, }; diff --git a/frontend/src/themes/theme.ts b/frontend/src/themes/theme.ts index 60cdb807d9..bb640c25ea 100644 --- a/frontend/src/themes/theme.ts +++ b/frontend/src/themes/theme.ts @@ -257,6 +257,7 @@ const theme = { // A400: '#A6000E', // A700: '#A6000E', }, + variants: colors.variants, }, }; diff --git a/frontend/src/themes/themeTypes.ts b/frontend/src/themes/themeTypes.ts index 5bde8f34f0..9252e6c477 100644 --- a/frontend/src/themes/themeTypes.ts +++ b/frontend/src/themes/themeTypes.ts @@ -112,6 +112,11 @@ declare module '@mui/material/styles' { disabled: string; expanded: string; }; + + /** + * Variants, percentage split in strategies + **/ + variants: string[]; } interface Theme extends CustomTheme {}