mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
fix: remove empty variants when changing tabs (#5850)
This PR makes a change to how variants work in the new setup. Variants will now: * Be removed if you change tab or unmount the component and it has no name * Moved StrategyVariants into a separate component to isolate this change * Add error handling around onSubmit and only trigger feedback if it's successful
This commit is contained in:
parent
3a2d4ae60b
commit
2a723ea9e8
@ -25,7 +25,7 @@ import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
|||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { NewFeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/NewFeatureStrategyForm';
|
import { NewFeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/NewFeatureStrategyForm';
|
||||||
import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants';
|
import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants';
|
||||||
|
|
||||||
interface IEditChangeProps {
|
interface IEditChangeProps {
|
||||||
change: IChangeRequestAddStrategy | IChangeRequestUpdateStrategy;
|
change: IChangeRequestAddStrategy | IChangeRequestUpdateStrategy;
|
||||||
@ -174,7 +174,7 @@ export const NewEditChange = ({
|
|||||||
tab={tab}
|
tab={tab}
|
||||||
setTab={setTab}
|
setTab={setTab}
|
||||||
StrategyVariants={
|
StrategyVariants={
|
||||||
<StrategyVariants
|
<NewStrategyVariants
|
||||||
strategy={strategy}
|
strategy={strategy}
|
||||||
setStrategy={setStrategy}
|
setStrategy={setStrategy}
|
||||||
environment={environment}
|
environment={environment}
|
||||||
|
@ -47,6 +47,7 @@ import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon';
|
|||||||
import { useFeedback } from 'component/feedbackNew/useFeedback';
|
import { useFeedback } from 'component/feedbackNew/useFeedback';
|
||||||
import { useUserSubmittedFeedback } from 'hooks/useSubmittedFeedback';
|
import { useUserSubmittedFeedback } from 'hooks/useSubmittedFeedback';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants';
|
||||||
|
|
||||||
interface IFeatureStrategyFormProps {
|
interface IFeatureStrategyFormProps {
|
||||||
feature: IFeatureToggle;
|
feature: IFeatureToggle;
|
||||||
@ -330,11 +331,15 @@ export const NewFeatureStrategyForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSubmitWithFeedback = async () => {
|
const onSubmitWithFeedback = async () => {
|
||||||
|
try {
|
||||||
await onSubmit();
|
await onSubmit();
|
||||||
|
|
||||||
if (newStrategyConfigurationFeedback && !hasSubmittedFeedback) {
|
if (newStrategyConfigurationFeedback && !hasSubmittedFeedback) {
|
||||||
createFeedbackContext();
|
createFeedbackContext();
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
|
||||||
|
@ -199,6 +199,38 @@ describe('NewFeatureStrategyCreate', () => {
|
|||||||
const addVariantEl = await screen.findByText('Add variant');
|
const addVariantEl = await screen.findByText('Add variant');
|
||||||
fireEvent.click(addVariantEl);
|
fireEvent.click(addVariantEl);
|
||||||
|
|
||||||
|
const inputElement = screen.getAllByRole('textbox')[0];
|
||||||
|
fireEvent.change(inputElement, {
|
||||||
|
target: { value: expectedVariantName },
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetingEl = await screen.findByText('Targeting');
|
||||||
|
fireEvent.click(targetingEl);
|
||||||
|
|
||||||
|
const addConstraintEl = await screen.findByText('Add constraint');
|
||||||
|
expect(addConstraintEl).toBeInTheDocument();
|
||||||
|
|
||||||
|
fireEvent.click(variantsEl);
|
||||||
|
const inputElement2 = screen.getAllByRole('textbox')[0];
|
||||||
|
|
||||||
|
expect(inputElement2).not.toBeDisabled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should remove empty variants when changing tabs', async () => {
|
||||||
|
setupComponent();
|
||||||
|
|
||||||
|
const titleEl = await screen.findByText('Gradual rollout');
|
||||||
|
expect(titleEl).toBeInTheDocument();
|
||||||
|
|
||||||
|
const variantsEl = screen.getByText('Variants');
|
||||||
|
fireEvent.click(variantsEl);
|
||||||
|
|
||||||
|
const addVariantEl = await screen.findByText('Add variant');
|
||||||
|
fireEvent.click(addVariantEl);
|
||||||
|
|
||||||
|
const variants = screen.queryAllByTestId('VARIANT');
|
||||||
|
expect(variants.length).toBe(1);
|
||||||
|
|
||||||
const targetingEl = await screen.findByText('Targeting');
|
const targetingEl = await screen.findByText('Targeting');
|
||||||
fireEvent.click(targetingEl);
|
fireEvent.click(targetingEl);
|
||||||
|
|
||||||
@ -207,12 +239,8 @@ describe('NewFeatureStrategyCreate', () => {
|
|||||||
|
|
||||||
fireEvent.click(variantsEl);
|
fireEvent.click(variantsEl);
|
||||||
|
|
||||||
const inputElement = screen.getAllByRole('textbox')[0];
|
const variants2 = screen.queryAllByTestId('VARIANT');
|
||||||
fireEvent.change(inputElement, {
|
expect(variants2.length).toBe(0);
|
||||||
target: { value: expectedVariantName },
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(screen.getByText(expectedVariantName)).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Should autosave constraint settings when navigating between tabs', async () => {
|
test('Should autosave constraint settings when navigating between tabs', async () => {
|
||||||
|
@ -34,7 +34,7 @@ import useQueryParams from 'hooks/useQueryParams';
|
|||||||
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
|
||||||
import { useDefaultStrategy } from '../../../project/Project/ProjectSettings/ProjectDefaultStrategySettings/ProjectEnvironment/ProjectEnvironmentDefaultStrategy/EditDefaultStrategy';
|
import { useDefaultStrategy } from '../../../project/Project/ProjectSettings/ProjectDefaultStrategySettings/ProjectEnvironment/ProjectEnvironmentDefaultStrategy/EditDefaultStrategy';
|
||||||
import { NewFeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/NewFeatureStrategyForm';
|
import { NewFeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/NewFeatureStrategyForm';
|
||||||
import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants';
|
import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants';
|
||||||
|
|
||||||
export const NewFeatureStrategyCreate = () => {
|
export const NewFeatureStrategyCreate = () => {
|
||||||
const [tab, setTab] = useState(0);
|
const [tab, setTab] = useState(0);
|
||||||
@ -211,7 +211,7 @@ export const NewFeatureStrategyCreate = () => {
|
|||||||
tab={tab}
|
tab={tab}
|
||||||
setTab={setTab}
|
setTab={setTab}
|
||||||
StrategyVariants={
|
StrategyVariants={
|
||||||
<StrategyVariants
|
<NewStrategyVariants
|
||||||
strategy={strategy}
|
strategy={strategy}
|
||||||
setStrategy={setStrategy}
|
setStrategy={setStrategy}
|
||||||
environment={environmentId}
|
environment={environmentId}
|
||||||
|
@ -29,7 +29,7 @@ import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useCh
|
|||||||
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
import { NewFeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/NewFeatureStrategyForm';
|
import { NewFeatureStrategyForm } from 'component/feature/FeatureStrategy/FeatureStrategyForm/NewFeatureStrategyForm';
|
||||||
import { StrategyVariants } from 'component/feature/StrategyTypes/StrategyVariants';
|
import { NewStrategyVariants } from 'component/feature/StrategyTypes/NewStrategyVariants';
|
||||||
|
|
||||||
const useTitleTracking = () => {
|
const useTitleTracking = () => {
|
||||||
const [previousTitle, setPreviousTitle] = useState<string>('');
|
const [previousTitle, setPreviousTitle] = useState<string>('');
|
||||||
@ -233,7 +233,7 @@ export const NewFeatureStrategyEdit = () => {
|
|||||||
tab={tab}
|
tab={tab}
|
||||||
setTab={setTab}
|
setTab={setTab}
|
||||||
StrategyVariants={
|
StrategyVariants={
|
||||||
<StrategyVariants
|
<NewStrategyVariants
|
||||||
strategy={strategy}
|
strategy={strategy}
|
||||||
setStrategy={setStrategy}
|
setStrategy={setStrategy}
|
||||||
environment={environmentId}
|
environment={environmentId}
|
||||||
|
@ -0,0 +1,202 @@
|
|||||||
|
import { VariantForm } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm';
|
||||||
|
import { updateWeightEdit } from '../../common/util';
|
||||||
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
|
import { IFeatureVariantEdit } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/EnvironmentVariantsModal';
|
||||||
|
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 { Box, 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';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
|
||||||
|
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 NewStrategyVariants: FC<{
|
||||||
|
setStrategy: React.Dispatch<
|
||||||
|
React.SetStateAction<Partial<IFeatureStrategy>>
|
||||||
|
>;
|
||||||
|
strategy: Partial<IFeatureStrategy>;
|
||||||
|
projectId: string;
|
||||||
|
environment: string;
|
||||||
|
editable?: boolean;
|
||||||
|
}> = ({ strategy, setStrategy, projectId, environment, editable }) => {
|
||||||
|
const { trackEvent } = usePlausibleTracker();
|
||||||
|
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
|
||||||
|
const theme = useTheme();
|
||||||
|
|
||||||
|
const stickiness =
|
||||||
|
strategy?.parameters && 'stickiness' in strategy?.parameters
|
||||||
|
? String(strategy.parameters.stickiness)
|
||||||
|
: 'default';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
setStrategy((prev) => ({
|
||||||
|
...prev,
|
||||||
|
variants: variantsEdit.filter((variant) =>
|
||||||
|
Boolean(variant.name),
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
}, [JSON.stringify(variantsEdit)]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setVariantsEdit(
|
||||||
|
(strategy.variants || []).map((variant) => ({
|
||||||
|
...variant,
|
||||||
|
new: editable || false,
|
||||||
|
isValid: true,
|
||||||
|
id: uuidv4(),
|
||||||
|
overrides: [],
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStrategy((prev) => ({
|
||||||
|
...prev,
|
||||||
|
variants: variantsEdit.map((variant) => ({
|
||||||
|
stickiness,
|
||||||
|
name: variant.name,
|
||||||
|
weight: variant.weight,
|
||||||
|
payload: variant.payload,
|
||||||
|
weightType: variant.weightType,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
}, [stickiness, JSON.stringify(variantsEdit)]);
|
||||||
|
|
||||||
|
const updateVariant = (updatedVariant: IFeatureVariantEdit, id: string) => {
|
||||||
|
setVariantsEdit((prevVariants) =>
|
||||||
|
updateWeightEdit(
|
||||||
|
prevVariants.map((prevVariant) =>
|
||||||
|
prevVariant.id === id ? updatedVariant : prevVariant,
|
||||||
|
),
|
||||||
|
1000,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addVariant = () => {
|
||||||
|
const id = uuidv4();
|
||||||
|
setVariantsEdit((variantsEdit) => [
|
||||||
|
...variantsEdit,
|
||||||
|
{
|
||||||
|
name: '',
|
||||||
|
weightType: WeightType.VARIABLE,
|
||||||
|
weight: 0,
|
||||||
|
stickiness,
|
||||||
|
new: true,
|
||||||
|
isValid: false,
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
trackEvent('strategy-variants', {
|
||||||
|
props: {
|
||||||
|
eventType: 'variant added',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={variantsEdit.length > 0}
|
||||||
|
show={<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} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -6,33 +6,18 @@ import PermissionButton from '../../common/PermissionButton/PermissionButton';
|
|||||||
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
|
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { WeightType } from '../../../constants/variantTypes';
|
import { WeightType } from '../../../constants/variantTypes';
|
||||||
import { Box, Link, styled, Typography, useTheme } from '@mui/material';
|
import { Link, styled, Typography, useTheme } from '@mui/material';
|
||||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||||
import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
|
import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
|
||||||
import { HelpIcon } from '../../common/HelpIcon/HelpIcon';
|
import { HelpIcon } from '../../common/HelpIcon/HelpIcon';
|
||||||
import { StrategyVariantsUpgradeAlert } from '../../common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
import { StrategyVariantsUpgradeAlert } from '../../common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
import { useUiFlag } from 'hooks/useUiFlag';
|
|
||||||
import { Add } from '@mui/icons-material';
|
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
|
|
||||||
const StyledVariantForms = styled('div')({
|
const StyledVariantForms = styled('div')({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
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<{
|
export const StrategyVariants: FC<{
|
||||||
setStrategy: React.Dispatch<
|
setStrategy: React.Dispatch<
|
||||||
React.SetStateAction<Partial<IFeatureStrategy>>
|
React.SetStateAction<Partial<IFeatureStrategy>>
|
||||||
@ -45,7 +30,6 @@ export const StrategyVariants: FC<{
|
|||||||
const { trackEvent } = usePlausibleTracker();
|
const { trackEvent } = usePlausibleTracker();
|
||||||
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
|
const [variantsEdit, setVariantsEdit] = useState<IFeatureVariantEdit[]>([]);
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const newStrategyConfiguration = useUiFlag('newStrategyConfiguration');
|
|
||||||
|
|
||||||
const stickiness =
|
const stickiness =
|
||||||
strategy?.parameters && 'stickiness' in strategy?.parameters
|
strategy?.parameters && 'stickiness' in strategy?.parameters
|
||||||
@ -109,90 +93,6 @@ 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>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={variantsEdit.length > 0}
|
|
||||||
show={<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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography
|
<Typography
|
||||||
|
Loading…
Reference in New Issue
Block a user