1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-26 13:48:33 +02:00

Chore(1-3831)/add edit strategy cleanup pt iv (#10135)

This PR addresses and removes the last comment related to the
addEditStrategy flag. In doing so, I have also removed the remaining
dangling files from the new constraint accordion directory. I believe
that everything that's left in there now is currently in use.
This commit is contained in:
Thomas Heartman 2025-06-13 12:02:12 +02:00 committed by GitHub
parent d7c32d688a
commit 74ae35298d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 23 additions and 583 deletions

View File

@ -1,104 +0,0 @@
import type React from 'react';
import { IconButton, styled, Tooltip } from '@mui/material';
import Delete from '@mui/icons-material/Delete';
import Edit from '@mui/icons-material/Edit';
import Undo from '@mui/icons-material/Undo';
import { ConditionallyRender } from '../../ConditionallyRender/ConditionallyRender.tsx';
import type { IConstraint } from 'interfaces/strategy';
interface ConstraintAccordionEditActionsProps {
onDelete?: () => void;
onEdit?: () => void;
onUndo?: () => void;
constraintChanges?: IConstraint[];
disableEdit?: boolean;
disableDelete?: boolean;
}
const StyledHeaderActions = styled('div')(({ theme }) => ({
marginLeft: 'auto',
whiteSpace: 'nowrap',
[theme.breakpoints.down('sm')]: {
display: 'none',
},
}));
export const ConstraintAccordionEditActions = ({
onEdit,
onDelete,
onUndo,
constraintChanges = [],
disableDelete = false,
disableEdit = false,
}: ConstraintAccordionEditActionsProps) => {
const onEditClick =
onEdit &&
((event: React.SyntheticEvent) => {
event.stopPropagation();
onEdit();
});
const onDeleteClick =
onDelete &&
((event: React.SyntheticEvent) => {
event.stopPropagation();
onDelete();
});
const onUndoClick =
onUndo &&
((event: React.SyntheticEvent) => {
event.stopPropagation();
onUndo();
});
return (
<StyledHeaderActions>
<ConditionallyRender
condition={Boolean(onEditClick) && !disableEdit}
show={
<Tooltip title='Edit constraint' arrow>
<IconButton
type='button'
onClick={onEditClick}
disabled={disableEdit}
data-testid='EDIT_CONSTRAINT_BUTTON'
>
<Edit />
</IconButton>
</Tooltip>
}
/>
<ConditionallyRender
condition={Boolean(onUndoClick) && constraintChanges.length > 1}
show={
<Tooltip title='Undo last change' arrow>
<IconButton
type='button'
onClick={onUndoClick}
disabled={disableDelete}
data-testid='UNDO_CONSTRAINT_CHANGE_BUTTON'
>
<Undo />
</IconButton>
</Tooltip>
}
/>
<ConditionallyRender
condition={Boolean(onDeleteClick) && !disableDelete}
show={
<Tooltip title='Delete constraint' arrow>
<IconButton
type='button'
onClick={onDeleteClick}
disabled={disableDelete}
data-testid='DELETE_CONSTRAINT_BUTTON'
>
<Delete />
</IconButton>
</Tooltip>
}
/>
</StyledHeaderActions>
);
};

View File

@ -10,20 +10,11 @@ import {
import type { IConstraint } from 'interfaces/strategy';
import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx';
import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx';
import { oneOf } from 'utils/oneOf';
import {
dateOperators,
numOperators,
semVerOperators,
} from 'constants/operators';
interface IConstraintAccordionViewProps {
constraint: IConstraint;
onDelete?: () => void;
onEdit?: () => void;
onUse?: () => void;
sx?: SxProps<Theme>;
compact?: boolean;
disabled?: boolean;
renderAfter?: JSX.Element;
borderStyle?: 'solid' | 'dashed';
@ -73,11 +64,8 @@ const StyledWrapper = styled('div')({
export const ConstraintAccordionView = ({
constraint,
onEdit,
onDelete,
onUse,
sx = undefined,
compact = false,
disabled = false,
renderAfter,
borderStyle = 'solid',
@ -85,10 +73,6 @@ export const ConstraintAccordionView = ({
const [expandable, setExpandable] = useState(true);
const [expanded, setExpanded] = useState(false);
const singleValue = oneOf(
[...semVerOperators, ...numOperators, ...dateOperators],
constraint.operator,
);
const handleClick = () => {
if (expandable) {
setExpanded(!expanded);
@ -111,14 +95,10 @@ export const ConstraintAccordionView = ({
<StyledWrapper>
<ConstraintAccordionViewHeader
constraint={constraint}
onEdit={onEdit}
onDelete={onDelete}
onUse={onUse}
singleValue={singleValue}
allowExpand={setExpandable}
disabled={disabled}
expanded={expanded}
compact={compact}
/>
{renderAfter}
</StyledWrapper>

View File

@ -1,25 +1,13 @@
import type { IConstraint } from 'interfaces/strategy';
import { ConstraintAccordionViewHeaderInfo } from './ConstraintAccordionViewHeaderInfo.tsx';
import { styled } from '@mui/system';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import { ConstraintAccordionViewActions } from '../../ConstraintAccordionViewActions/ConstraintAccordionViewActions.tsx';
import { ConstraintAccordionEditActions } from '../../ConstraintAccordionEditActions/ConstraintAccordionEditActions.tsx';
interface IConstraintAccordionViewHeaderProps {
constraint: IConstraint;
onDelete?: () => void;
onEdit?: () => void;
onUse?: () => void;
/**
* @deprecated
*/
singleValue: boolean;
expanded: boolean;
allowExpand: (shouldExpand: boolean) => void;
/**
* @deprecated
*/
compact?: boolean;
disabled?: boolean;
}
@ -36,22 +24,11 @@ const StyledContainer = styled('div')(({ theme }) => ({
export const ConstraintAccordionViewHeader = ({
constraint,
onEdit,
onDelete,
onUse,
singleValue,
allowExpand,
expanded,
compact,
disabled,
}: IConstraintAccordionViewHeaderProps) => {
const { context } = useUnleashContext();
const { contextName } = constraint;
const disableEdit = !context
.map((contextDefinition) => contextDefinition.name)
.includes(contextName);
return (
<StyledContainer>
<ConstraintAccordionViewHeaderInfo
@ -60,16 +37,7 @@ export const ConstraintAccordionViewHeader = ({
expanded={expanded}
disabled={disabled}
/>
{onUse ? (
<ConstraintAccordionViewActions onUse={onUse} />
) : (
// @deprecated : remove onEdit and onDelete from current file together with NewConstraintAccordionList and addEditStrategy flag
<ConstraintAccordionEditActions
onEdit={onEdit}
onDelete={onDelete}
disableEdit={disableEdit}
/>
)}
{onUse ? <ConstraintAccordionViewActions onUse={onUse} /> : null}
</StyledContainer>
);
};

View File

@ -1,101 +0,0 @@
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { styled } from '@mui/material';
import { useEffect, useMemo, useState } from 'react';
import type { IConstraint } from 'interfaces/strategy';
const StyledValuesSpan = styled('span')(({ theme }) => ({
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
wordBreak: 'break-word',
fontSize: theme.fontSizes.smallBody,
margin: 'auto 0',
[theme.breakpoints.down('sm')]: {
margin: theme.spacing(1, 0),
textAlign: 'center',
},
}));
interface ConstraintSingleValueProps {
constraint: IConstraint;
expanded: boolean;
maxLength: number;
allowExpand: (shouldExpand: boolean) => void;
disabled?: boolean;
}
const StyledHeaderValuesContainerWrapper = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'stretch',
margin: 'auto 0',
}));
const StyledHeaderValuesContainer = styled('div')(({ theme }) => ({
display: 'flex',
justifyContent: 'stretch',
margin: 'auto 0',
flexDirection: 'column',
marginLeft: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
marginLeft: 0,
},
}));
const StyledHeaderValuesExpand = styled('p')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
marginTop: theme.spacing(0.5),
color: theme.palette.links,
[theme.breakpoints.down('sm')]: {
textAlign: 'center',
},
}));
export const ConstraintAccordionViewHeaderMultipleValues = ({
constraint,
expanded,
allowExpand,
maxLength,
disabled = false,
}: ConstraintSingleValueProps) => {
const [expandable, setExpandable] = useState(false);
const text = useMemo(() => {
return constraint?.values?.map((value) => value).join(', ');
}, [constraint]);
useEffect(() => {
if (text) {
allowExpand((text?.length ?? 0) > maxLength);
setExpandable((text?.length ?? 0) > maxLength);
}
}, [text, maxLength, allowExpand, setExpandable]);
return (
<StyledHeaderValuesContainerWrapper>
<StyledHeaderValuesContainer>
<StyledValuesSpan
sx={(theme) => ({
color: disabled
? theme.palette.text.secondary
: 'inherit',
})}
>
{text}
</StyledValuesSpan>
<ConditionallyRender
condition={expandable}
show={
<StyledHeaderValuesExpand
className={'valuesExpandLabel'}
>
{!expanded
? `View all (${constraint?.values?.length})`
: 'View less'}
</StyledHeaderValuesExpand>
}
/>
</StyledHeaderValuesContainer>
</StyledHeaderValuesContainerWrapper>
);
};

View File

@ -1,48 +0,0 @@
import { useEffect } from 'react';
import { Chip, styled } from '@mui/material';
import { formatConstraintValue } from 'utils/formatConstraintValue';
import type { IConstraint } from 'interfaces/strategy';
import { useLocationSettings } from 'hooks/useLocationSettings';
const StyledSingleValueChip = styled(Chip)(({ theme }) => ({
margin: 'auto 0',
marginLeft: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
margin: theme.spacing(1, 0),
},
}));
interface ConstraintSingleValueProps {
constraint: IConstraint;
allowExpand: (shouldExpand: boolean) => void;
disabled?: boolean;
}
const StyledHeaderValuesContainerWrapper = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'stretch',
margin: 'auto 0',
}));
export const ConstraintAccordionViewHeaderSingleValue = ({
constraint,
allowExpand,
disabled = false,
}: ConstraintSingleValueProps) => {
const { locationSettings } = useLocationSettings();
useEffect(() => {
allowExpand(false);
}, [allowExpand]);
return (
<StyledHeaderValuesContainerWrapper>
<StyledSingleValueChip
sx={(theme) => ({
color: disabled ? theme.palette.text.secondary : 'inherit',
})}
label={formatConstraintValue(constraint, locationSettings)}
/>
</StyledHeaderValuesContainerWrapper>
);
};

View File

@ -1,72 +0,0 @@
import type { IConstraint } from 'interfaces/strategy';
import { ConditionallyRender } from '../../../ConditionallyRender/ConditionallyRender.tsx';
import { Tooltip, Box, styled } from '@mui/material';
import { stringOperators } from 'constants/operators';
import { ReactComponent as NegatedOnIcon } from 'assets/icons/not_operator_selected.svg';
import { ConstraintOperator } from '../../ConstraintOperator/ConstraintOperator.tsx';
import { StyledIconWrapper } from './StyledIconWrapper.tsx';
import { ReactComponent as CaseSensitive } from 'assets/icons/24_Text format.svg';
import { oneOf } from 'utils/oneOf';
import { useTheme } from '@mui/material';
interface ConstraintViewHeaderOperatorProps {
constraint: IConstraint;
disabled?: boolean;
}
const StyledHeaderValuesContainerWrapper = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'stretch',
margin: 'auto 0',
}));
const StyledHeaderConstraintContainer = styled('div')(({ theme }) => ({
minWidth: '152px',
position: 'relative',
[theme.breakpoints.down('sm')]: {
paddingRight: 0,
},
}));
export const ConstraintViewHeaderOperator = ({
constraint,
disabled = false,
}: ConstraintViewHeaderOperatorProps) => {
const theme = useTheme();
return (
<StyledHeaderValuesContainerWrapper>
<ConditionallyRender
condition={Boolean(constraint.inverted)}
show={
<Tooltip title={'Operator is negated'} arrow>
<Box sx={{ display: 'flex' }}>
<StyledIconWrapper isPrefix>
<NegatedOnIcon />
</StyledIconWrapper>
</Box>
</Tooltip>
}
/>
<StyledHeaderConstraintContainer>
<ConstraintOperator
constraint={constraint}
hasPrefix={Boolean(constraint.inverted)}
disabled={disabled}
/>
</StyledHeaderConstraintContainer>
<ConditionallyRender
condition={
!constraint.caseInsensitive &&
oneOf(stringOperators, constraint.operator)
}
show={
<Tooltip title='Case sensitive is active' arrow>
<StyledIconWrapper>
<CaseSensitive />
</StyledIconWrapper>
</Tooltip>
}
/>
</StyledHeaderValuesContainerWrapper>
);
};

View File

@ -2,7 +2,7 @@ import { forwardRef, type ReactNode } from 'react';
import { styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
export const StyledIconWrapperBase = styled('div')<{
const StyledIconWrapperBase = styled('div')<{
prefix?: boolean;
}>(({ theme }) => ({
backgroundColor: theme.palette.background.elevation2,

View File

@ -1,53 +0,0 @@
import type { IConstraint } from 'interfaces/strategy';
import { styled } from '@mui/material';
import { formatOperatorDescription } from 'utils/formatOperatorDescription';
interface IConstraintOperatorProps {
constraint: IConstraint;
hasPrefix?: boolean;
disabled?: boolean;
}
const StyledContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(0.5, 1.5),
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.elevation2,
lineHeight: 1.25,
}));
const StyledName = styled('p', {
shouldForwardProp: (prop) => prop !== 'disabled',
})<{ disabled: boolean }>(({ theme, disabled }) => ({
fontSize: theme.fontSizes.smallBody,
lineHeight: 17 / 14,
color: disabled ? theme.palette.text.secondary : theme.palette.text.primary,
}));
const StyledText = styled('p', {
shouldForwardProp: (prop) => prop !== 'disabled',
})<{ disabled: boolean }>(({ theme, disabled }) => ({
fontSize: theme.fontSizes.smallerBody,
color: disabled ? theme.palette.text.secondary : theme.palette.neutral.main,
}));
export const ConstraintOperator = ({
constraint,
hasPrefix,
disabled = false,
}: IConstraintOperatorProps) => {
const operatorName = constraint.operator;
const operatorText = formatOperatorDescription(constraint.operator);
return (
<StyledContainer
style={{
borderTopLeftRadius: hasPrefix ? 0 : undefined,
borderBottomLeftRadius: hasPrefix ? 0 : undefined,
paddingLeft: hasPrefix ? 0 : undefined,
}}
>
<StyledName disabled={disabled}>{operatorName}</StyledName>
<StyledText disabled={disabled}>{operatorText}</StyledText>
</StyledContainer>
);
};

View File

@ -7,7 +7,7 @@ import produce from 'immer';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint';
import { createEmptyConstraint } from '../NewConstraintAccordionList/createEmptyConstraint.ts';
import { createEmptyConstraint } from '../../../../utils/createEmptyConstraint.ts';
import { constraintId } from 'constants/constraintId.ts';
export interface IEditableConstraintsListRef {
addConstraint?: (contextName: string) => void;

View File

@ -1,133 +0,0 @@
import type React from 'react';
import { forwardRef } from 'react';
import { styled } from '@mui/material';
import type { IConstraint } from 'interfaces/strategy';
import produce from 'immer';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import type { IUseWeakMap } from 'hooks/useWeakMap';
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
import { ConstraintAccordionView } from 'component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
import { EditableConstraint } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/EditableConstraint';
import { constraintId } from 'constants/constraintId';
export interface IConstraintAccordionListProps {
constraints: IConstraint[];
setConstraints?: React.Dispatch<React.SetStateAction<IConstraint[]>>;
showCreateButton?: boolean;
/* Add "constraints" title on the top - default `true` */
showLabel?: boolean;
}
// Ref methods exposed by this component.
export interface IConstraintAccordionListRef {
addConstraint?: (contextName: string) => void;
}
// Extra form state for each constraint.
interface IConstraintAccordionListItemState {
// Is the constraint new (never been saved)?
new?: boolean;
// Is the constraint currently being edited?
editing?: boolean;
}
export const constraintAccordionListId = 'constraintAccordionListId';
const StyledContainer = styled('div')({
width: '100%',
display: 'flex',
flexDirection: 'column',
});
interface IConstraintList {
constraints: IConstraint[];
setConstraints?: React.Dispatch<React.SetStateAction<IConstraint[]>>;
state: IUseWeakMap<IConstraint, IConstraintAccordionListItemState>;
}
export const NewConstraintAccordionList = forwardRef<
IConstraintAccordionListRef | undefined,
IConstraintList
>(({ constraints, setConstraints, state }, ref) => {
const { context } = useUnleashContext();
const onEdit =
setConstraints &&
((constraint: IConstraint) => {
state.set(constraint, { editing: true });
});
const onRemove =
setConstraints &&
((index: number) => {
const constraint = constraints[index];
state.set(constraint, {});
setConstraints(
produce((draft) => {
draft.splice(index, 1);
}),
);
});
const onSave =
setConstraints &&
((index: number, constraint: IConstraint) => {
state.set(constraint, {});
setConstraints(
produce((draft) => {
draft[index] = constraint;
}),
);
});
const onAutoSave =
setConstraints &&
((id: string | undefined) => (constraint: IConstraint) => {
state.set(constraint, { editing: true });
setConstraints(
produce((draft) => {
return draft.map((oldConstraint) => {
if (oldConstraint[constraintId] === id) {
return constraint;
}
return oldConstraint;
});
}),
);
});
const onCancel = (index: number) => {
const constraint = constraints[index];
state.get(constraint)?.new && onRemove?.(index);
state.set(constraint, {});
};
if (context.length === 0) {
return null;
}
return (
<StyledContainer id={constraintAccordionListId}>
<ConstraintsList>
{constraints.map((constraint, index) =>
state.get(constraint)?.editing &&
Boolean(setConstraints) ? (
<EditableConstraint
key={constraint[constraintId]}
constraint={constraint}
// @ts-ignore todo: find a better way to do this
onDelete={() => onRemove(index)}
// @ts-ignore
onUpdate={onAutoSave(constraintId)}
/>
) : (
<ConstraintAccordionView
key={constraint[constraintId]}
constraint={constraint}
/>
),
)}
</ConstraintsList>
</StyledContainer>
);
});

View File

@ -12,7 +12,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { RecentlyUsedConstraints } from '../RecentlyUsedConstraints/RecentlyUsedConstraints.tsx';
import { useWeakMap } from 'hooks/useWeakMap.ts';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext.ts';
import { createEmptyConstraint } from 'component/common/NewConstraintAccordion/NewConstraintAccordionList/createEmptyConstraint.ts';
import { createEmptyConstraint } from 'utils/createEmptyConstraint.ts';
interface IConstraintAccordionListProps {
constraints: IConstraint[];
@ -20,8 +20,6 @@ interface IConstraintAccordionListProps {
showCreateButton?: boolean;
}
export const constraintAccordionListId = 'constraintAccordionListId';
const StyledContainer = styled('div')({
width: '100%',
display: 'flex',
@ -102,7 +100,7 @@ export const FeatureStrategyConstraintAccordionList = forwardRef<
}
return (
<StyledContainer id={constraintAccordionListId}>
<StyledContainer>
<ConditionallyRender
condition={Boolean(showCreateButton && onAdd)}
show={

View File

@ -6,13 +6,13 @@ import VisibilityOff from '@mui/icons-material/VisibilityOff';
import Visibility from '@mui/icons-material/Visibility';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { styled, type Theme, Tooltip } from '@mui/material';
import { constraintAccordionListId } from 'component/common/NewConstraintAccordion/NewConstraintAccordionList/NewConstraintAccordionList';
interface IFeatureStrategySegmentListProps {
segment: ISegment;
setSegments: React.Dispatch<React.SetStateAction<ISegment[]>>;
preview?: ISegment;
setPreview: React.Dispatch<React.SetStateAction<ISegment | undefined>>;
'aria-controls'?: string;
}
const StyledChip = styled('span')(({ theme }) => ({
@ -48,6 +48,7 @@ export const FeatureStrategySegmentChip = ({
setSegments,
preview,
setPreview,
'aria-controls': ariaControls,
}: IFeatureStrategySegmentListProps) => {
const onRemove = () => {
setSegments((prev) => {
@ -91,7 +92,7 @@ export const FeatureStrategySegmentChip = ({
type='button'
onClick={onTogglePreview}
aria-expanded={segment === preview}
aria-controls={constraintAccordionListId}
aria-controls={ariaControls}
>
{togglePreviewIcon}
</StyledButton>

View File

@ -1,5 +1,5 @@
import type React from 'react';
import { Fragment, useState } from 'react';
import { Fragment, useId, useState } from 'react';
import type { ISegment } from 'interfaces/segment';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { FeatureStrategySegmentChip } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentChip';
@ -34,12 +34,17 @@ const StyledAnd = styled('p')(({ theme }) => ({
backgroundColor: theme.palette.background.elevation2,
}));
const StyledPreviewContainer = styled('div')({
display: 'contents',
});
export const FeatureStrategySegmentList = ({
segments,
setSegments,
}: IFeatureStrategySegmentListProps) => {
const [preview, setPreview] = useState<ISegment>();
const lastSegmentIndex = segments.length - 1;
const segmentDetailsId = useId();
if (segments.length === 0) {
return null;
@ -63,6 +68,7 @@ export const FeatureStrategySegmentList = ({
setSegments={setSegments}
preview={preview}
setPreview={setPreview}
aria-controls={segmentDetailsId}
/>
<ConditionallyRender
condition={i < lastSegmentIndex}
@ -71,10 +77,12 @@ export const FeatureStrategySegmentList = ({
</Fragment>
))}
</StyledList>
<ConditionallyRender
condition={Boolean(preview)}
show={() => <SegmentItem segment={preview!} isExpanded />}
/>
<StyledPreviewContainer id={segmentDetailsId}>
<ConditionallyRender
condition={Boolean(preview)}
show={<SegmentItem segment={preview!} isExpanded />}
/>
</StyledPreviewContainer>
</>
);
};

View File

@ -29,11 +29,7 @@ export const ConstraintExecutionWithoutResults: FC<
condition={index > 0}
show={<ConstraintSeparator />}
/>
<ConstraintAccordionView
constraint={constraint}
compact
disabled
/>
<ConstraintAccordionView constraint={constraint} disabled />
</Fragment>
))}
</ConstraintExecutionWrapper>

View File

@ -9,7 +9,6 @@ import type { ActionsFilterState } from '../../useProjectActionsForm.ts';
import Delete from '@mui/icons-material/Delete';
import Input from 'component/common/Input/Input';
import { ProjectActionsFormItem } from '../ProjectActionsFormItem.tsx';
import { ConstraintOperatorSelect } from 'component/common/NewConstraintAccordion/ConstraintOperatorSelect';
import {
inOperators,
numOperators,
@ -24,6 +23,7 @@ import { CaseSensitiveButton } from './StyledToggleButton/CaseSensitiveButton/Ca
import { InvertedOperatorButton } from './StyledToggleButton/InvertedOperatorButton/InvertedOperatorButton.tsx';
import { constraintValidator } from 'component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint/useEditableConstraint/constraint-validator.ts';
import { ResolveInput } from './ProjectActionsFilterItemInputs/ResolveInput.tsx';
import { ConstraintOperatorSelect } from './ConstraintOperatorSelect.tsx';
const StyledDeleteButton = styled(IconButton)({
marginRight: '-6px',