mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Feat/new strategy variants tab (#5649)
This PR sets up the variants tab for the new strategy configuration. Also some minor adjustments to the new form: * Change where padding is controlled to allow us to have more granular control over how the buttons width and border should look and have the tabs have full-width borders across the form * Move the buttons to be absolutely positioned at the bottom of the page * Move where we display banners to avoid clutter <img width="1284" alt="Skjermbilde 2023-12-14 kl 21 17 53" src="https://github.com/Unleash/unleash/assets/16081982/45e6a364-e4aa-47ac-b420-f3be9b39a15e">
This commit is contained in:
		
							parent
							
								
									848415c5ca
								
							
						
					
					
						commit
						cbd6aa1324
					
				| @ -1,15 +1,6 @@ | ||||
| import React, { useState } from 'react'; | ||||
| import { useNavigate } from 'react-router-dom'; | ||||
| import { | ||||
|     Alert, | ||||
|     Button, | ||||
|     styled, | ||||
|     Tabs, | ||||
|     Tab, | ||||
|     Typography, | ||||
|     Divider, | ||||
|     Box, | ||||
| } from '@mui/material'; | ||||
| import { Alert, Button, styled, Tabs, Tab, Box, Divider } from '@mui/material'; | ||||
| import { | ||||
|     IFeatureStrategy, | ||||
|     IFeatureStrategyParameters, | ||||
| @ -42,6 +33,7 @@ import { FeatureStrategyTitle } from './FeatureStrategyTitle/FeatureStrategyTitl | ||||
| import { FeatureStrategyEnabledDisabled } from './FeatureStrategyEnabledDisabled/FeatureStrategyEnabledDisabled'; | ||||
| import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants'; | ||||
| import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; | ||||
| import { formatStrategyName } from 'utils/strategyNames'; | ||||
| 
 | ||||
| interface IFeatureStrategyFormProps { | ||||
|     feature: IFeatureToggle; | ||||
| @ -77,8 +69,15 @@ const StyledDividerContent = styled(Box)(({ theme }) => ({ | ||||
| })); | ||||
| 
 | ||||
| const StyledForm = styled('form')(({ theme }) => ({ | ||||
|     display: 'grid', | ||||
|     position: 'relative', | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
|     gap: theme.spacing(2), | ||||
|     padding: theme.spacing(6), | ||||
|     paddingBottom: theme.spacing(12), | ||||
|     paddingTop: theme.spacing(4), | ||||
|     overflow: 'auto', | ||||
|     height: '100%', | ||||
| })); | ||||
| 
 | ||||
| const StyledHr = styled('hr')(({ theme }) => ({ | ||||
| @ -89,11 +88,31 @@ const StyledHr = styled('hr')(({ theme }) => ({ | ||||
|     background: theme.palette.background.elevation2, | ||||
| })); | ||||
| 
 | ||||
| const StyledButtons = styled('div')(({ theme }) => ({ | ||||
| const StyledTitle = styled('h1')(({ theme }) => ({ | ||||
|     fontWeight: 'normal', | ||||
|     display: 'flex', | ||||
|     alignItems: 'center', | ||||
| })); | ||||
| 
 | ||||
| const StyledButtons = styled('div')(({ theme }) => ({ | ||||
|     bottom: 0, | ||||
|     right: 0, | ||||
|     left: 0, | ||||
|     position: 'absolute', | ||||
|     display: 'flex', | ||||
|     padding: theme.spacing(3), | ||||
|     paddingRight: theme.spacing(6), | ||||
|     paddingLeft: theme.spacing(6), | ||||
|     backgroundColor: theme.palette.common.white, | ||||
|     justifyContent: 'end', | ||||
|     gap: theme.spacing(2), | ||||
|     paddingBottom: theme.spacing(10), | ||||
|     borderTop: `1px solid ${theme.palette.divider}`, | ||||
| })); | ||||
| 
 | ||||
| const StyledTabs = styled(Tabs)(({ theme }) => ({ | ||||
|     borderTop: `1px solid ${theme.palette.divider}`, | ||||
|     borderBottom: `1px solid ${theme.palette.divider}`, | ||||
|     paddingLeft: theme.spacing(6), | ||||
|     paddingRight: theme.spacing(6), | ||||
| })); | ||||
| 
 | ||||
| const StyledBox = styled(Box)(({ theme }) => ({ | ||||
| @ -111,6 +130,15 @@ const StyledTargetingHeader = styled('div')(({ theme }) => ({ | ||||
|     marginTop: theme.spacing(1.5), | ||||
| })); | ||||
| 
 | ||||
| const StyledHeaderBox = styled(Box)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     alignItems: 'center', | ||||
|     paddingLeft: theme.spacing(6), | ||||
|     paddingRight: theme.spacing(6), | ||||
|     paddingTop: theme.spacing(2), | ||||
|     paddingBottom: theme.spacing(2), | ||||
| })); | ||||
| 
 | ||||
| export const NewFeatureStrategyForm = ({ | ||||
|     projectId, | ||||
|     feature, | ||||
| @ -225,58 +253,22 @@ export const NewFeatureStrategyForm = ({ | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <StyledForm onSubmit={onSubmitWithValidation}> | ||||
|             <Tabs value={tab} onChange={handleChange}> | ||||
|         <> | ||||
|             <StyledHeaderBox> | ||||
|                 <StyledTitle> | ||||
|                     {formatStrategyName(strategy.name || '')} | ||||
|                 </StyledTitle> | ||||
|             </StyledHeaderBox> | ||||
|             <StyledTabs value={tab} onChange={handleChange}> | ||||
|                 <Tab label='General' /> | ||||
|                 <Tab label='Targeting' /> | ||||
|                 <Tab label='Variants' /> | ||||
|             </Tabs> | ||||
|             </StyledTabs> | ||||
|             <StyledForm onSubmit={onSubmitWithValidation}> | ||||
|                 <ConditionallyRender | ||||
|                     condition={tab === 0} | ||||
|                     show={ | ||||
|                         <> | ||||
|                         <ConditionallyRender | ||||
|                             condition={hasChangeRequestInReviewForEnvironment} | ||||
|                             show={alert} | ||||
|                             elseShow={ | ||||
|                                 <ConditionallyRender | ||||
|                                     condition={Boolean(isChangeRequest)} | ||||
|                                     show={ | ||||
|                                         <FeatureStrategyChangeRequestAlert | ||||
|                                             environment={environmentId} | ||||
|                                         /> | ||||
|                                     } | ||||
|                                 /> | ||||
|                             } | ||||
|                         /> | ||||
|                         <FeatureStrategyEnabled | ||||
|                             projectId={feature.project} | ||||
|                             featureId={feature.name} | ||||
|                             environmentId={environmentId} | ||||
|                         > | ||||
|                             <ConditionallyRender | ||||
|                                 condition={Boolean(isChangeRequest)} | ||||
|                                 show={ | ||||
|                                     <Alert severity='success'> | ||||
|                                         This feature toggle is currently enabled | ||||
|                                         in the <strong>{environmentId}</strong>{' '} | ||||
|                                         environment. Any changes made here will | ||||
|                                         be available to users as soon as these | ||||
|                                         changes are approved and applied. | ||||
|                                     </Alert> | ||||
|                                 } | ||||
|                                 elseShow={ | ||||
|                                     <Alert severity='success'> | ||||
|                                         This feature toggle is currently enabled | ||||
|                                         in the <strong>{environmentId}</strong>{' '} | ||||
|                                         environment. Any changes made here will | ||||
|                                         be available to users as soon as you hit{' '} | ||||
|                                         <strong>save</strong>. | ||||
|                                     </Alert> | ||||
|                                 } | ||||
|                             /> | ||||
|                         </FeatureStrategyEnabled> | ||||
|                         <StyledHr /> | ||||
|                             <FeatureStrategyTitle | ||||
|                                 title={strategy.title || ''} | ||||
|                                 setTitle={(title) => { | ||||
| @ -303,6 +295,54 @@ export const NewFeatureStrategyForm = ({ | ||||
|                                 errors={errors} | ||||
|                                 hasAccess={access} | ||||
|                             /> | ||||
| 
 | ||||
|                             <ConditionallyRender | ||||
|                                 condition={ | ||||
|                                     hasChangeRequestInReviewForEnvironment | ||||
|                                 } | ||||
|                                 show={alert} | ||||
|                                 elseShow={ | ||||
|                                     <ConditionallyRender | ||||
|                                         condition={Boolean(isChangeRequest)} | ||||
|                                         show={ | ||||
|                                             <FeatureStrategyChangeRequestAlert | ||||
|                                                 environment={environmentId} | ||||
|                                             /> | ||||
|                                         } | ||||
|                                     /> | ||||
|                                 } | ||||
|                             /> | ||||
| 
 | ||||
|                             <FeatureStrategyEnabled | ||||
|                                 projectId={feature.project} | ||||
|                                 featureId={feature.name} | ||||
|                                 environmentId={environmentId} | ||||
|                             > | ||||
|                                 <ConditionallyRender | ||||
|                                     condition={Boolean(isChangeRequest)} | ||||
|                                     show={ | ||||
|                                         <Alert severity='success'> | ||||
|                                             This feature toggle is currently | ||||
|                                             enabled in the{' '} | ||||
|                                             <strong>{environmentId}</strong>{' '} | ||||
|                                             environment. Any changes made here | ||||
|                                             will be available to users as soon | ||||
|                                             as these changes are approved and | ||||
|                                             applied. | ||||
|                                         </Alert> | ||||
|                                     } | ||||
|                                     elseShow={ | ||||
|                                         <Alert severity='success'> | ||||
|                                             This feature toggle is currently | ||||
|                                             enabled in the{' '} | ||||
|                                             <strong>{environmentId}</strong>{' '} | ||||
|                                             environment. Any changes made here | ||||
|                                             will be available to users as soon | ||||
|                                             as you hit <strong>save</strong>. | ||||
|                                         </Alert> | ||||
|                                     } | ||||
|                                 /> | ||||
|                             </FeatureStrategyEnabled> | ||||
|                         </> | ||||
|                     } | ||||
|                 /> | ||||
| @ -313,9 +353,9 @@ export const NewFeatureStrategyForm = ({ | ||||
|                         <> | ||||
|                             <StyledTargetingHeader> | ||||
|                                 Segmentation and constraints allow you to set | ||||
|                             filters on your strategies, so that they will only | ||||
|                             be evaluated for users and applications that match | ||||
|                             the specified preconditions. | ||||
|                                 filters on your strategies, so that they will | ||||
|                                 only be evaluated for users and applications | ||||
|                                 that match the specified preconditions. | ||||
|                             </StyledTargetingHeader> | ||||
|                             <FeatureStrategySegment | ||||
|                                 segments={segments} | ||||
| @ -393,5 +433,6 @@ export const NewFeatureStrategyForm = ({ | ||||
|                     /> | ||||
|                 </StyledButtons> | ||||
|             </StyledForm> | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| @ -180,10 +180,10 @@ export const NewFeatureStrategyCreate = () => { | ||||
|     return ( | ||||
|         <FormTemplate | ||||
|             modal | ||||
|             title={formatStrategyName(strategyName)} | ||||
|             description={featureStrategyHelp} | ||||
|             documentationLink={featureStrategyDocsLink} | ||||
|             documentationLinkLabel={featureStrategyDocsLinkLabel} | ||||
|             disablePadding | ||||
|             formatApiCode={() => | ||||
|                 formatAddStrategyApiCode( | ||||
|                     projectId, | ||||
|  | ||||
| @ -200,7 +200,7 @@ export const NewFeatureStrategyEdit = () => { | ||||
|     return ( | ||||
|         <FormTemplate | ||||
|             modal | ||||
|             title={formatStrategyName(strategy.name ?? '')} | ||||
|             disablePadding | ||||
|             description={featureStrategyHelp} | ||||
|             documentationLink={featureStrategyDocsLink} | ||||
|             documentationLinkLabel={featureStrategyDocsLinkLabel} | ||||
|  | ||||
| @ -6,18 +6,32 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton'; | ||||
| import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions'; | ||||
| import { v4 as uuidv4 } from 'uuid'; | ||||
| import { WeightType } from '../../../constants/variantTypes'; | ||||
| import { Link, styled, Typography, useTheme } from '@mui/material'; | ||||
| import { Box, Link, styled, Typography, useTheme } from '@mui/material'; | ||||
| import { IFeatureStrategy } from 'interfaces/strategy'; | ||||
| import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider'; | ||||
| import { HelpIcon } from '../../common/HelpIcon/HelpIcon'; | ||||
| import { StrategyVariantsUpgradeAlert } from '../../common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert'; | ||||
| import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; | ||||
| import { useUiFlag } from 'hooks/useUiFlag'; | ||||
| import { Add } from '@mui/icons-material'; | ||||
| 
 | ||||
| const StyledVariantForms = styled('div')({ | ||||
|     display: 'flex', | ||||
|     flexDirection: 'column', | ||||
| }); | ||||
| 
 | ||||
| const StyledHelpIconBox = styled(Box)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
|     alignItems: 'center', | ||||
|     marginTop: theme.spacing(1), | ||||
|     marginBottom: theme.spacing(1), | ||||
| })); | ||||
| 
 | ||||
| const StyledVariantsHeader = styled('div')(({ theme }) => ({ | ||||
|     color: theme.palette.text.secondary, | ||||
|     marginTop: theme.spacing(1.5), | ||||
| })); | ||||
| 
 | ||||
| export const StrategyVariants: FC<{ | ||||
|     setStrategy: React.Dispatch< | ||||
|         React.SetStateAction<Partial<IFeatureStrategy>> | ||||
| @ -29,6 +43,8 @@ export const StrategyVariants: FC<{ | ||||
|     const { trackEvent } = usePlausibleTracker(); | ||||
|     const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]); | ||||
|     const theme = useTheme(); | ||||
|     const newStrategyConfiguration = useUiFlag('newStrategyConfiguration'); | ||||
| 
 | ||||
|     const stickiness = | ||||
|         strategy?.parameters && 'stickiness' in strategy?.parameters | ||||
|             ? String(strategy.parameters.stickiness) | ||||
| @ -91,6 +107,86 @@ export const StrategyVariants: FC<{ | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     if (newStrategyConfiguration) { | ||||
|         return ( | ||||
|             <> | ||||
|                 <StyledVariantsHeader> | ||||
|                     Variants enhance a feature flag by providing a version of | ||||
|                     the feature to be enabled | ||||
|                 </StyledVariantsHeader> | ||||
|                 <StyledHelpIconBox> | ||||
|                     <Typography>Variants</Typography> | ||||
|                     <HelpIcon | ||||
|                         htmlTooltip | ||||
|                         tooltip={ | ||||
|                             <Box> | ||||
|                                 <Typography variant='body2'> | ||||
|                                     Variants in feature toggling allow you to | ||||
|                                     serve different versions of a feature to | ||||
|                                     different users. This can be used for A/B | ||||
|                                     testing, gradual rollouts, and canary | ||||
|                                     releases. Variants provide a way to control | ||||
|                                     the user experience at a granular level, | ||||
|                                     enabling you to test and optimize different | ||||
|                                     aspects of your features. Read more about | ||||
|                                     variants{' '} | ||||
|                                     <a | ||||
|                                         href='https://docs.getunleash.io/reference/strategy-variants' | ||||
|                                         target='_blank' | ||||
|                                         rel='noopener noreferrer' | ||||
|                                     > | ||||
|                                         here | ||||
|                                     </a> | ||||
|                                 </Typography> | ||||
|                             </Box> | ||||
|                         } | ||||
|                     /> | ||||
|                 </StyledHelpIconBox> | ||||
|                 <StyledVariantForms> | ||||
|                     <StrategyVariantsUpgradeAlert /> | ||||
|                     {variantsEdit.map((variant, i) => ( | ||||
|                         <VariantForm | ||||
|                             disableOverrides={true} | ||||
|                             key={variant.id} | ||||
|                             variant={variant} | ||||
|                             variants={variantsEdit} | ||||
|                             updateVariant={(updatedVariant) => | ||||
|                                 updateVariant(updatedVariant, variant.id) | ||||
|                             } | ||||
|                             removeVariant={() => | ||||
|                                 setVariantsEdit((variantsEdit) => | ||||
|                                     updateWeightEdit( | ||||
|                                         variantsEdit.filter( | ||||
|                                             (v) => v.id !== variant.id, | ||||
|                                         ), | ||||
|                                         1000, | ||||
|                                     ), | ||||
|                                 ) | ||||
|                             } | ||||
|                             decorationColor={ | ||||
|                                 theme.palette.variants[ | ||||
|                                     i % theme.palette.variants.length | ||||
|                                 ] | ||||
|                             } | ||||
|                         /> | ||||
|                     ))} | ||||
|                 </StyledVariantForms> | ||||
|                 <PermissionButton | ||||
|                     onClick={addVariant} | ||||
|                     variant='outlined' | ||||
|                     permission={UPDATE_FEATURE_ENVIRONMENT_VARIANTS} | ||||
|                     projectId={projectId} | ||||
|                     environmentId={environment} | ||||
|                     data-testid='ADD_STRATEGY_VARIANT_BUTTON' | ||||
|                     startIcon={<Add />} | ||||
|                 > | ||||
|                     Add variant | ||||
|                 </PermissionButton> | ||||
|                 <SplitPreviewSlider variants={variantsEdit} /> | ||||
|             </> | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <Typography | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user