diff --git a/frontend/src/component/common/AutocompleteBox/AutocompleteBox.tsx b/frontend/src/component/common/AutocompleteBox/AutocompleteBox.tsx
index 12007e48b0..b42f43269b 100644
--- a/frontend/src/component/common/AutocompleteBox/AutocompleteBox.tsx
+++ b/frontend/src/component/common/AutocompleteBox/AutocompleteBox.tsx
@@ -1,8 +1,10 @@
import { useStyles } from 'component/common/AutocompleteBox/AutocompleteBox.styles';
-import { Search, ArrowDropDown } from '@mui/icons-material';
-import { Autocomplete, styled } from '@mui/material';
+import { Search, ArrowDropDown, Add } from '@mui/icons-material';
+import { Autocomplete, styled, InputAdornment, useTheme } from '@mui/material';
import { AutocompleteRenderInputParams } from '@mui/material/Autocomplete';
import { TextField } from '@mui/material';
+import { useUiFlag } from 'hooks/useUiFlag';
+import { useState } from 'react';
interface IAutocompleteBoxProps {
label: string;
@@ -54,12 +56,80 @@ export const AutocompleteBox = ({
onChange,
disabled,
}: IAutocompleteBoxProps) => {
+ const [placeHolder, setPlaceholder] = useState('Add Segments');
const { classes: styles } = useStyles();
+ const theme = useTheme();
+
+ const newStrategyConfiguration = useUiFlag('newStrategyConfiguration');
const renderInput = (params: AutocompleteRenderInputParams) => {
return ;
};
+ const renderCustomInput = (params: AutocompleteRenderInputParams) => {
+ const { InputProps } = params;
+ return (
+
+
+
+ ),
+ }}
+ variant='outlined'
+ sx={{
+ width: '215px',
+ '& .MuiOutlinedInput-root': {
+ '& .MuiInputBase-input': {
+ color: theme.palette.primary.main,
+ opacity: 1,
+ '&::placeholder': {
+ color: theme.palette.primary.main,
+ fontWeight: 'bold',
+ opacity: 1,
+ },
+ },
+ '& .MuiOutlinedInput-notchedOutline': {
+ borderColor: theme.palette.primary.main,
+ opacity: 0.5,
+ },
+ '&.Mui-focused .MuiOutlinedInput-notchedOutline': {
+ borderWidth: '1px',
+ },
+ },
+ }}
+ placeholder={placeHolder}
+ onFocus={() => setPlaceholder('')}
+ onBlur={() => setPlaceholder('Add Segments')}
+ />
+ );
+ };
+ if (newStrategyConfiguration) {
+ return (
+
+ onChange(value || [])}
+ renderInput={renderCustomInput}
+ getOptionLabel={(value) => value.label}
+ disabled={disabled}
+ size='small'
+ multiple
+ />
+
+ );
+ }
+
return (
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx
index 2d4bfadb58..e6332bab54 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx
@@ -1,6 +1,6 @@
import React, { forwardRef, Fragment, useImperativeHandle } from 'react';
-import { Button, styled, Tooltip } from '@mui/material';
-import { HelpOutline } from '@mui/icons-material';
+import { Box, Button, styled, Tooltip, Typography } from '@mui/material';
+import { Add, HelpOutline } from '@mui/icons-material';
import { IConstraint } from 'interfaces/strategy';
import { ConstraintAccordion } from 'component/common/ConstraintAccordion/ConstraintAccordion';
import produce from 'immer';
@@ -10,6 +10,8 @@ import { objectId } from 'utils/objectId';
import { createEmptyConstraint } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
+import { useUiFlag } from 'hooks/useUiFlag';
+import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
interface IConstraintAccordionListProps {
constraints: IConstraint[];
@@ -64,6 +66,13 @@ const StyledAddCustomLabel = styled('div')(({ theme }) => ({
display: 'flex',
}));
+const StyledHelpIconBox = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ marginTop: theme.spacing(1),
+ marginBottom: theme.spacing(1),
+}));
+
export const ConstraintAccordionList = forwardRef<
IConstraintAccordionListRef | undefined,
IConstraintAccordionListProps
@@ -78,6 +87,8 @@ export const ConstraintAccordionList = forwardRef<
>();
const { context } = useUnleashContext();
+ const newStrategyConfiguration = useUiFlag('newStrategyConfiguration');
+
const addConstraint =
setConstraints &&
((contextName: string) => {
@@ -135,6 +146,86 @@ export const ConstraintAccordionList = forwardRef<
return null;
}
+ if (newStrategyConfiguration) {
+ return (
+
+
+
+ Constraints
+
+
+ Constraints are advanced
+ targeting rules that you can
+ use to enable a feature
+ toggle for a subset of your
+ users. Read more about
+ constraints{' '}
+
+ here
+
+
+
+ }
+ />
+
+ {constraints.map((constraint, index) => (
+
+ 0}
+ show={
+
+ }
+ />
+
+
+ ))}
+ }
+ variant='outlined'
+ color='primary'
+ data-testid='ADD_CONSTRAINT_BUTTON'
+ >
+ Add constraint
+
+
+ }
+ />
+
+ );
+ }
+
return (
>;
}
+const StyledDividerContent = styled(Box)(({ theme }) => ({
+ padding: theme.spacing(0.75, 1),
+ color: theme.palette.text.primary,
+ fontSize: theme.fontSizes.smallerBody,
+ backgroundColor: theme.palette.background.elevation2,
+ borderRadius: theme.shape.borderRadius,
+ width: '45px',
+ position: 'absolute',
+ top: '-10px',
+ left: 'calc(50% - 45px)',
+ lineHeight: 1,
+}));
+
const StyledForm = styled('form')(({ theme }) => ({
display: 'grid',
gap: theme.spacing(2),
@@ -74,6 +96,21 @@ const StyledButtons = styled('div')(({ theme }) => ({
paddingBottom: theme.spacing(10),
}));
+const StyledBox = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ position: 'relative',
+ marginTop: theme.spacing(3.5),
+}));
+
+const StyledDivider = styled(Divider)(({ theme }) => ({
+ width: '100%',
+}));
+
+const StyledTargetingHeader = styled('div')(({ theme }) => ({
+ color: theme.palette.text.secondary,
+ marginTop: theme.spacing(1.5),
+}));
+
export const NewFeatureStrategyForm = ({
projectId,
feature,
@@ -274,11 +311,22 @@ export const NewFeatureStrategyForm = ({
condition={tab === 1}
show={
<>
+
+ 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.
+
+
+
+
+ AND
+
({
fontSize: theme.fontSizes.smallBody,
}));
+const StyledHelpIconBox = styled(Box)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ marginTop: theme.spacing(1),
+ marginBottom: theme.spacing(1),
+}));
+
export const FeatureStrategySegment = ({
segments: selectedSegments,
setSegments: setSelectedSegments,
@@ -28,6 +37,8 @@ export const FeatureStrategySegment = ({
const { segments: allSegments } = useSegments();
const { strategySegmentsLimit } = useSegmentLimits();
+ const newStrategyConfiguration = useUiFlag('newStrategyConfiguration');
+
const atStrategySegmentsLimit: boolean = Boolean(
strategySegmentsLimit &&
selectedSegments.length >= strategySegmentsLimit,
@@ -59,6 +70,49 @@ export const FeatureStrategySegment = ({
}
};
+ if (newStrategyConfiguration) {
+ return (
+ <>
+
+ Segments
+
+
+ Segments are reusable sets of constraints
+ that can be defined once and reused across
+ feature toggle configurations. You can
+ create a segment on the global or the
+ project level. Read more about segments{' '}
+
+ here
+
+
+
+ }
+ />
+
+
+ {atStrategySegmentsLimit && }
+
+
+ >
+ );
+ }
+
return (
<>
@@ -76,6 +130,7 @@ export const FeatureStrategySegment = ({
segments={selectedSegments}
setSegments={setSelectedSegments}
/>
+
>
);
diff --git a/frontend/src/component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy.tsx b/frontend/src/component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy.tsx
index 2802767a6c..4971c4192b 100644
--- a/frontend/src/component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy.tsx
+++ b/frontend/src/component/feature/StrategyTypes/FlexibleStrategy/FlexibleStrategy.tsx
@@ -35,7 +35,7 @@ const StyledBox = styled(Box)(({ theme }) => ({
}));
const StyledOuterBox = styled(Box)(({ theme }) => ({
- marginTop: '1rem',
+ marginTop: theme.spacing(1),
display: 'flex',
width: '100%',
justifyContent: 'space-between',
diff --git a/frontend/src/component/feature/StrategyTypes/RolloutSlider/RolloutSlider.tsx b/frontend/src/component/feature/StrategyTypes/RolloutSlider/RolloutSlider.tsx
index 4df0425a56..a147fa8656 100644
--- a/frontend/src/component/feature/StrategyTypes/RolloutSlider/RolloutSlider.tsx
+++ b/frontend/src/component/feature/StrategyTypes/RolloutSlider/RolloutSlider.tsx
@@ -25,6 +25,15 @@ const StyledSlider = withStyles(Slider, (theme) => ({
},
}));
+const StyledHeader = styled(Typography)(({ theme }) => ({
+ marginBottom: theme.spacing(1),
+}));
+
+const StyledSubheader = styled(Typography)(({ theme }) => ({
+ marginBottom: theme.spacing(1),
+ marginTop: theme.spacing(1),
+}));
+
const StyledBox = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
@@ -91,12 +100,9 @@ const RolloutSlider = ({
htmlTooltip
tooltip={
-
+
Rollout percentage
-
+
The rollout percentage determines the proportion
of users exposed to a feature. It's based on the
@@ -108,12 +114,9 @@ const RolloutSlider = ({
of the feature among users.
-
+
Stickiness
-
+
Stickiness refers to the value used for hashing
to ensure a consistent user experience. It
@@ -122,12 +125,9 @@ const RolloutSlider = ({
consistent across sessions.
-
+
GroupId
-
+
The groupId is used as a seed for the hash
function, ensuring consistent feature exposure