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:
parent
d7c32d688a
commit
74ae35298d
@ -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>
|
||||
);
|
||||
};
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
@ -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>
|
||||
);
|
||||
};
|
@ -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>
|
||||
);
|
||||
};
|
@ -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,
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
@ -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;
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
@ -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={
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -29,11 +29,7 @@ export const ConstraintExecutionWithoutResults: FC<
|
||||
condition={index > 0}
|
||||
show={<ConstraintSeparator />}
|
||||
/>
|
||||
<ConstraintAccordionView
|
||||
constraint={constraint}
|
||||
compact
|
||||
disabled
|
||||
/>
|
||||
<ConstraintAccordionView constraint={constraint} disabled />
|
||||
</Fragment>
|
||||
))}
|
||||
</ConstraintExecutionWrapper>
|
||||
|
@ -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',
|
||||
|
Loading…
Reference in New Issue
Block a user