mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: new constraint view for flag edit page (#9567)
Refactor components in Targeting (Edit strategy)
This commit is contained in:
		
							parent
							
								
									a10dca44f6
								
							
						
					
					
						commit
						2d47fb3827
					
				@ -1,5 +1,8 @@
 | 
			
		||||
import type { FC } from 'react';
 | 
			
		||||
import { StrategyEvaluationItem } from '../StrategyEvaluationItem/StrategyEvaluationItem';
 | 
			
		||||
import {
 | 
			
		||||
    StrategyEvaluationItem,
 | 
			
		||||
    type StrategyEvaluationItemProps,
 | 
			
		||||
} from '../StrategyEvaluationItem/StrategyEvaluationItem';
 | 
			
		||||
import type { ConstraintSchema } from 'openapi';
 | 
			
		||||
import { formatOperatorDescription } from 'component/common/ConstraintAccordion/ConstraintOperator/formatOperatorDescription';
 | 
			
		||||
import { StrategyEvaluationChip } from '../StrategyEvaluationChip/StrategyEvaluationChip';
 | 
			
		||||
@ -29,18 +32,25 @@ const StyledOperatorGroup = styled('div')(({ theme }) => ({
 | 
			
		||||
    gap: theme.spacing(0.5),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const ConstraintItem: FC<ConstraintSchema> = ({
 | 
			
		||||
export const ConstraintItemHeader: FC<
 | 
			
		||||
    ConstraintSchema & Pick<StrategyEvaluationItemProps, 'onSetTruncated'>
 | 
			
		||||
> = ({
 | 
			
		||||
    caseInsensitive,
 | 
			
		||||
    contextName,
 | 
			
		||||
    inverted,
 | 
			
		||||
    operator,
 | 
			
		||||
    value,
 | 
			
		||||
    values,
 | 
			
		||||
    onSetTruncated,
 | 
			
		||||
}) => {
 | 
			
		||||
    const items = value ? [value, ...(values || [])] : values || [];
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StrategyEvaluationItem type='Constraint' values={items}>
 | 
			
		||||
        <StrategyEvaluationItem
 | 
			
		||||
            type='Constraint'
 | 
			
		||||
            values={items}
 | 
			
		||||
            onSetTruncated={onSetTruncated}
 | 
			
		||||
        >
 | 
			
		||||
            {contextName}
 | 
			
		||||
            <StyledOperatorGroup>
 | 
			
		||||
                {inverted ? <Inverted /> : null}
 | 
			
		||||
@ -10,17 +10,21 @@ const StyledList = styled('ul')(({ theme }) => ({
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledListItem = styled('li')(({ theme }) => ({
 | 
			
		||||
export const ConstraintListItem = styled('div')(({ theme }) => ({
 | 
			
		||||
    position: 'relative',
 | 
			
		||||
    border: `1px solid ${theme.palette.divider}`,
 | 
			
		||||
    borderRadius: theme.shape.borderRadiusMedium,
 | 
			
		||||
    background: theme.palette.background.default,
 | 
			
		||||
    padding: theme.spacing(2, 3),
 | 
			
		||||
    padding: theme.spacing(1.5, 3),
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    flexFlow: 'column',
 | 
			
		||||
    gap: theme.spacing(2),
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledListItem = styled('li')({
 | 
			
		||||
    position: 'relative',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const StyledAnd = styled('div')(({ theme }) => ({
 | 
			
		||||
    position: 'absolute',
 | 
			
		||||
    top: theme.spacing(-0.5),
 | 
			
		||||
 | 
			
		||||
@ -1,24 +1,28 @@
 | 
			
		||||
import { styled } from '@mui/material';
 | 
			
		||||
import {
 | 
			
		||||
    Truncator,
 | 
			
		||||
    type TruncatorProps,
 | 
			
		||||
} from 'component/common/Truncator/Truncator';
 | 
			
		||||
import { disabledStrategyClassName } from 'component/common/StrategyItemContainer/disabled-strategy-utils';
 | 
			
		||||
import type { FC, ReactNode } from 'react';
 | 
			
		||||
 | 
			
		||||
type StrategyItemProps = {
 | 
			
		||||
export type StrategyEvaluationItemProps = {
 | 
			
		||||
    type?: ReactNode;
 | 
			
		||||
    children?: ReactNode;
 | 
			
		||||
    values?: string[];
 | 
			
		||||
};
 | 
			
		||||
} & Pick<TruncatorProps, 'onSetTruncated'>;
 | 
			
		||||
 | 
			
		||||
const StyledContainer = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    fontSize: theme.typography.body2.fontSize,
 | 
			
		||||
    minHeight: theme.spacing(4),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledContent = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
    flexWrap: 'wrap',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    [`.${disabledStrategyClassName} &`]: {
 | 
			
		||||
        filter: 'grayscale(1)',
 | 
			
		||||
@ -35,45 +39,33 @@ const StyledType = styled('span')(({ theme }) => ({
 | 
			
		||||
    width: theme.spacing(10),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledValuesGroup = styled('ul')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    flexFlow: 'row wrap',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    gap: theme.spacing(0.5),
 | 
			
		||||
    listStyle: 'none',
 | 
			
		||||
    padding: 0,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledValue = styled('li')(({ theme }) => ({
 | 
			
		||||
    [`.${disabledStrategyClassName} &`]: {
 | 
			
		||||
        filter: 'grayscale(1)',
 | 
			
		||||
        color: theme.palette.text.secondary,
 | 
			
		||||
    },
 | 
			
		||||
    ':not(&:last-of-type)::after': {
 | 
			
		||||
        content: '", "',
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Abstract building block for a list of constraints, segments and other items inside a strategy
 | 
			
		||||
 */
 | 
			
		||||
export const StrategyEvaluationItem: FC<StrategyItemProps> = ({
 | 
			
		||||
export const StrategyEvaluationItem: FC<StrategyEvaluationItemProps> = ({
 | 
			
		||||
    type,
 | 
			
		||||
    children,
 | 
			
		||||
    values,
 | 
			
		||||
    onSetTruncated,
 | 
			
		||||
}) => (
 | 
			
		||||
    <StyledContainer>
 | 
			
		||||
        <StyledType>{type}</StyledType>
 | 
			
		||||
        <StyledContent>
 | 
			
		||||
            {children}
 | 
			
		||||
            {values && values?.length > 0 ? (
 | 
			
		||||
                <StyledValuesGroup>
 | 
			
		||||
                    {values?.map((value, index) => (
 | 
			
		||||
                        <StyledValue key={`${value}#${index}`}>
 | 
			
		||||
                            {value}
 | 
			
		||||
                        </StyledValue>
 | 
			
		||||
                    ))}
 | 
			
		||||
                </StyledValuesGroup>
 | 
			
		||||
            {values && values?.length === 1 ? (
 | 
			
		||||
                <Truncator
 | 
			
		||||
                    title={values[0]}
 | 
			
		||||
                    arrow
 | 
			
		||||
                    lines={2}
 | 
			
		||||
                    onSetTruncated={() => onSetTruncated?.(false)}
 | 
			
		||||
                >
 | 
			
		||||
                    {values[0]}
 | 
			
		||||
                </Truncator>
 | 
			
		||||
            ) : null}
 | 
			
		||||
            {values && values?.length > 1 ? (
 | 
			
		||||
                <Truncator title='' lines={2} onSetTruncated={onSetTruncated}>
 | 
			
		||||
                    {values.join(', ')}
 | 
			
		||||
                </Truncator>
 | 
			
		||||
            ) : null}
 | 
			
		||||
        </StyledContent>
 | 
			
		||||
    </StyledContainer>
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
import { ConstraintIcon } from 'component/common/ConstraintAccordion/ConstraintIcon';
 | 
			
		||||
import type { IConstraint } from 'interfaces/strategy';
 | 
			
		||||
import { ConstraintAccordionViewHeaderInfo } from './ConstraintAccordionViewHeaderInfo';
 | 
			
		||||
import { ConstraintAccordionViewHeaderInfo as LegacyConstraintAccordionViewHeaderInfo } from './LegacyConstraintAccordionViewHeaderInfo';
 | 
			
		||||
import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeaderActions/ConstraintAccordionHeaderActions';
 | 
			
		||||
import { styled } from '@mui/system';
 | 
			
		||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag';
 | 
			
		||||
 | 
			
		||||
interface IConstraintAccordionViewHeaderProps {
 | 
			
		||||
    constraint: IConstraint;
 | 
			
		||||
@ -38,6 +40,7 @@ export const ConstraintAccordionViewHeader = ({
 | 
			
		||||
    disabled,
 | 
			
		||||
}: IConstraintAccordionViewHeaderProps) => {
 | 
			
		||||
    const { context } = useUnleashContext();
 | 
			
		||||
    const flagOverviewRedesign = useUiFlag('flagOverviewRedesign');
 | 
			
		||||
    const { contextName } = constraint;
 | 
			
		||||
 | 
			
		||||
    const disableEdit = !context
 | 
			
		||||
@ -46,7 +49,10 @@ export const ConstraintAccordionViewHeader = ({
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledContainer>
 | 
			
		||||
            {!flagOverviewRedesign ? (
 | 
			
		||||
                <ConstraintIcon compact={compact} disabled={disabled} />
 | 
			
		||||
            ) : null}
 | 
			
		||||
            {flagOverviewRedesign ? (
 | 
			
		||||
                <ConstraintAccordionViewHeaderInfo
 | 
			
		||||
                    constraint={constraint}
 | 
			
		||||
                    singleValue={singleValue}
 | 
			
		||||
@ -54,6 +60,15 @@ export const ConstraintAccordionViewHeader = ({
 | 
			
		||||
                    expanded={expanded}
 | 
			
		||||
                    disabled={disabled}
 | 
			
		||||
                />
 | 
			
		||||
            ) : (
 | 
			
		||||
                <LegacyConstraintAccordionViewHeaderInfo
 | 
			
		||||
                    constraint={constraint}
 | 
			
		||||
                    singleValue={singleValue}
 | 
			
		||||
                    allowExpand={allowExpand}
 | 
			
		||||
                    expanded={expanded}
 | 
			
		||||
                    disabled={disabled}
 | 
			
		||||
                />
 | 
			
		||||
            )}
 | 
			
		||||
            <ConstraintAccordionHeaderActions
 | 
			
		||||
                onEdit={onEdit}
 | 
			
		||||
                onDelete={onDelete}
 | 
			
		||||
 | 
			
		||||
@ -1,29 +1,9 @@
 | 
			
		||||
import { styled, Tooltip } from '@mui/material';
 | 
			
		||||
import { ConstraintViewHeaderOperator } from './ConstraintViewHeaderOperator';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { ConstraintAccordionViewHeaderSingleValue } from './ConstraintAccordionViewHeaderSingleValue';
 | 
			
		||||
import { ConstraintAccordionViewHeaderMultipleValues } from './ConstraintAccordionViewHeaderMultipleValues';
 | 
			
		||||
import { IconButton, styled } from '@mui/material';
 | 
			
		||||
import type { IConstraint } from 'interfaces/strategy';
 | 
			
		||||
 | 
			
		||||
const StyledHeaderText = styled('span')(({ theme }) => ({
 | 
			
		||||
    display: '-webkit-box',
 | 
			
		||||
    WebkitLineClamp: 3,
 | 
			
		||||
    WebkitBoxOrient: 'vertical',
 | 
			
		||||
    overflow: 'hidden',
 | 
			
		||||
    maxWidth: '100px',
 | 
			
		||||
    minWidth: '100px',
 | 
			
		||||
    marginRight: '10px',
 | 
			
		||||
    marginTop: 'auto',
 | 
			
		||||
    marginBottom: 'auto',
 | 
			
		||||
    wordBreak: 'break-word',
 | 
			
		||||
    fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    [theme.breakpoints.down(710)]: {
 | 
			
		||||
        textAlign: 'center',
 | 
			
		||||
        padding: theme.spacing(1, 0),
 | 
			
		||||
        marginRight: 'inherit',
 | 
			
		||||
        maxWidth: 'inherit',
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader';
 | 
			
		||||
import { useState } from 'react';
 | 
			
		||||
import VisibilityIcon from '@mui/icons-material/Visibility';
 | 
			
		||||
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
 | 
			
		||||
 | 
			
		||||
const StyledHeaderWrapper = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
@ -55,49 +35,26 @@ interface ConstraintAccordionViewHeaderMetaInfoProps {
 | 
			
		||||
 | 
			
		||||
export const ConstraintAccordionViewHeaderInfo = ({
 | 
			
		||||
    constraint,
 | 
			
		||||
    singleValue,
 | 
			
		||||
    allowExpand,
 | 
			
		||||
    expanded,
 | 
			
		||||
    disabled = false,
 | 
			
		||||
    maxLength = 112, //The max number of characters in the values text for NOT allowing expansion
 | 
			
		||||
}: ConstraintAccordionViewHeaderMetaInfoProps) => {
 | 
			
		||||
    const [expandable, setExpandable] = useState(false);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledHeaderWrapper>
 | 
			
		||||
            <StyledHeaderMetaInfo>
 | 
			
		||||
                <Tooltip title={constraint.contextName} arrow>
 | 
			
		||||
                    <StyledHeaderText
 | 
			
		||||
                        sx={(theme) => ({
 | 
			
		||||
                            color: disabled
 | 
			
		||||
                                ? theme.palette.text.secondary
 | 
			
		||||
                                : 'inherit',
 | 
			
		||||
                        })}
 | 
			
		||||
                    >
 | 
			
		||||
                        {constraint.contextName}
 | 
			
		||||
                    </StyledHeaderText>
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
                <ConstraintViewHeaderOperator
 | 
			
		||||
                    constraint={constraint}
 | 
			
		||||
                    disabled={disabled}
 | 
			
		||||
                />
 | 
			
		||||
                <ConditionallyRender
 | 
			
		||||
                    condition={singleValue}
 | 
			
		||||
                    show={
 | 
			
		||||
                        <ConstraintAccordionViewHeaderSingleValue
 | 
			
		||||
                            constraint={constraint}
 | 
			
		||||
                            allowExpand={allowExpand}
 | 
			
		||||
                            disabled={disabled}
 | 
			
		||||
                        />
 | 
			
		||||
                    }
 | 
			
		||||
                    elseShow={
 | 
			
		||||
                        <ConstraintAccordionViewHeaderMultipleValues
 | 
			
		||||
                            constraint={constraint}
 | 
			
		||||
                            expanded={expanded}
 | 
			
		||||
                            allowExpand={allowExpand}
 | 
			
		||||
                            maxLength={maxLength}
 | 
			
		||||
                            disabled={disabled}
 | 
			
		||||
                        />
 | 
			
		||||
                    }
 | 
			
		||||
                <ConstraintItemHeader
 | 
			
		||||
                    {...constraint}
 | 
			
		||||
                    onSetTruncated={(state: boolean) => {
 | 
			
		||||
                        setExpandable(state);
 | 
			
		||||
                        allowExpand(state);
 | 
			
		||||
                    }}
 | 
			
		||||
                />
 | 
			
		||||
                {expandable ? (
 | 
			
		||||
                    <IconButton type='button'>
 | 
			
		||||
                        {expanded ? <VisibilityOffIcon /> : <VisibilityIcon />}
 | 
			
		||||
                    </IconButton>
 | 
			
		||||
                ) : null}
 | 
			
		||||
            </StyledHeaderMetaInfo>
 | 
			
		||||
        </StyledHeaderWrapper>
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,104 @@
 | 
			
		||||
import { styled, Tooltip } from '@mui/material';
 | 
			
		||||
import { ConstraintViewHeaderOperator } from './ConstraintViewHeaderOperator';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { ConstraintAccordionViewHeaderSingleValue } from './ConstraintAccordionViewHeaderSingleValue';
 | 
			
		||||
import { ConstraintAccordionViewHeaderMultipleValues } from './ConstraintAccordionViewHeaderMultipleValues';
 | 
			
		||||
import type { IConstraint } from 'interfaces/strategy';
 | 
			
		||||
 | 
			
		||||
const StyledHeaderText = styled('span')(({ theme }) => ({
 | 
			
		||||
    display: '-webkit-box',
 | 
			
		||||
    WebkitLineClamp: 3,
 | 
			
		||||
    WebkitBoxOrient: 'vertical',
 | 
			
		||||
    overflow: 'hidden',
 | 
			
		||||
    maxWidth: '100px',
 | 
			
		||||
    minWidth: '100px',
 | 
			
		||||
    marginRight: '10px',
 | 
			
		||||
    marginTop: 'auto',
 | 
			
		||||
    marginBottom: 'auto',
 | 
			
		||||
    wordBreak: 'break-word',
 | 
			
		||||
    fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    [theme.breakpoints.down(710)]: {
 | 
			
		||||
        textAlign: 'center',
 | 
			
		||||
        padding: theme.spacing(1, 0),
 | 
			
		||||
        marginRight: 'inherit',
 | 
			
		||||
        maxWidth: 'inherit',
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledHeaderWrapper = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    width: '100%',
 | 
			
		||||
    justifyContent: 'space-between',
 | 
			
		||||
    borderRadius: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledHeaderMetaInfo = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    alignItems: 'stretch',
 | 
			
		||||
    marginLeft: theme.spacing(1),
 | 
			
		||||
    [theme.breakpoints.down('sm')]: {
 | 
			
		||||
        marginLeft: 0,
 | 
			
		||||
        flexDirection: 'column',
 | 
			
		||||
        alignItems: 'center',
 | 
			
		||||
        width: '100%',
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
interface ConstraintAccordionViewHeaderMetaInfoProps {
 | 
			
		||||
    constraint: IConstraint;
 | 
			
		||||
    singleValue: boolean;
 | 
			
		||||
    expanded: boolean;
 | 
			
		||||
    allowExpand: (shouldExpand: boolean) => void;
 | 
			
		||||
    disabled?: boolean;
 | 
			
		||||
    maxLength?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const ConstraintAccordionViewHeaderInfo = ({
 | 
			
		||||
    constraint,
 | 
			
		||||
    singleValue,
 | 
			
		||||
    allowExpand,
 | 
			
		||||
    expanded,
 | 
			
		||||
    disabled = false,
 | 
			
		||||
    maxLength = 112, //The max number of characters in the values text for NOT allowing expansion
 | 
			
		||||
}: ConstraintAccordionViewHeaderMetaInfoProps) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledHeaderWrapper>
 | 
			
		||||
            <StyledHeaderMetaInfo>
 | 
			
		||||
                <Tooltip title={constraint.contextName} arrow>
 | 
			
		||||
                    <StyledHeaderText
 | 
			
		||||
                        sx={(theme) => ({
 | 
			
		||||
                            color: disabled
 | 
			
		||||
                                ? theme.palette.text.secondary
 | 
			
		||||
                                : 'inherit',
 | 
			
		||||
                        })}
 | 
			
		||||
                    >
 | 
			
		||||
                        {constraint.contextName}
 | 
			
		||||
                    </StyledHeaderText>
 | 
			
		||||
                </Tooltip>
 | 
			
		||||
                <ConstraintViewHeaderOperator
 | 
			
		||||
                    constraint={constraint}
 | 
			
		||||
                    disabled={disabled}
 | 
			
		||||
                />
 | 
			
		||||
                <ConditionallyRender
 | 
			
		||||
                    condition={singleValue}
 | 
			
		||||
                    show={
 | 
			
		||||
                        <ConstraintAccordionViewHeaderSingleValue
 | 
			
		||||
                            constraint={constraint}
 | 
			
		||||
                            allowExpand={allowExpand}
 | 
			
		||||
                            disabled={disabled}
 | 
			
		||||
                        />
 | 
			
		||||
                    }
 | 
			
		||||
                    elseShow={
 | 
			
		||||
                        <ConstraintAccordionViewHeaderMultipleValues
 | 
			
		||||
                            constraint={constraint}
 | 
			
		||||
                            expanded={expanded}
 | 
			
		||||
                            allowExpand={allowExpand}
 | 
			
		||||
                            maxLength={maxLength}
 | 
			
		||||
                            disabled={disabled}
 | 
			
		||||
                        />
 | 
			
		||||
                    }
 | 
			
		||||
                />
 | 
			
		||||
            </StyledHeaderMetaInfo>
 | 
			
		||||
        </StyledHeaderWrapper>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@ -7,6 +7,9 @@ interface IConstraintIconProps {
 | 
			
		||||
    disabled?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @deprecated remove with `flagOverviewRedesign`
 | 
			
		||||
 */
 | 
			
		||||
export const ConstraintIcon: VFC<IConstraintIconProps> = ({
 | 
			
		||||
    compact,
 | 
			
		||||
    disabled,
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
import type { IConstraint } from 'interfaces/strategy';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
 | 
			
		||||
import { ConstraintAccordionEdit } from './ConstraintAccordionEdit/ConstraintAccordionEdit';
 | 
			
		||||
import { ConstraintAccordionView } from './ConstraintAccordionView/ConstraintAccordionView';
 | 
			
		||||
@ -27,10 +26,8 @@ export const NewConstraintAccordion = ({
 | 
			
		||||
}: IConstraintAccordionProps) => {
 | 
			
		||||
    if (!constraint) return null;
 | 
			
		||||
 | 
			
		||||
    if (editing && onSave) {
 | 
			
		||||
        return (
 | 
			
		||||
        <ConditionallyRender
 | 
			
		||||
            condition={Boolean(editing && onSave)}
 | 
			
		||||
            show={
 | 
			
		||||
            <ConstraintAccordionEdit
 | 
			
		||||
                constraint={constraint}
 | 
			
		||||
                onCancel={onCancel}
 | 
			
		||||
@ -39,14 +36,14 @@ export const NewConstraintAccordion = ({
 | 
			
		||||
                onAutoSave={onAutoSave!}
 | 
			
		||||
                compact={compact}
 | 
			
		||||
            />
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
            elseShow={
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <ConstraintAccordionView
 | 
			
		||||
            constraint={constraint}
 | 
			
		||||
            onEdit={onEdit}
 | 
			
		||||
            onDelete={onDelete}
 | 
			
		||||
        />
 | 
			
		||||
            }
 | 
			
		||||
        />
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import type React from 'react';
 | 
			
		||||
import { forwardRef, Fragment, useImperativeHandle } from 'react';
 | 
			
		||||
import { styled, Tooltip } from '@mui/material';
 | 
			
		||||
import HelpOutline from '@mui/icons-material/HelpOutline';
 | 
			
		||||
import { styled } from '@mui/material';
 | 
			
		||||
import type { IConstraint } from 'interfaces/strategy';
 | 
			
		||||
import produce from 'immer';
 | 
			
		||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
 | 
			
		||||
@ -43,30 +42,6 @@ const StyledContainer = styled('div')({
 | 
			
		||||
    flexDirection: 'column',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const StyledHelpWrapper = styled(Tooltip)(({ theme }) => ({
 | 
			
		||||
    marginLeft: theme.spacing(0.75),
 | 
			
		||||
    height: theme.spacing(1.5),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledHelp = styled(HelpOutline)(({ theme }) => ({
 | 
			
		||||
    fill: theme.palette.action.active,
 | 
			
		||||
    [theme.breakpoints.down(860)]: {
 | 
			
		||||
        display: 'none',
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledConstraintLabel = styled('p')(({ theme }) => ({
 | 
			
		||||
    marginBottom: theme.spacing(1),
 | 
			
		||||
    color: theme.palette.text.secondary,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledAddCustomLabel = styled('div')(({ theme }) => ({
 | 
			
		||||
    marginTop: theme.spacing(1),
 | 
			
		||||
    marginBottom: theme.spacing(1),
 | 
			
		||||
    color: theme.palette.text.primary,
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const useConstraintAccordionList = (
 | 
			
		||||
    setConstraints:
 | 
			
		||||
        | React.Dispatch<React.SetStateAction<IConstraint[]>>
 | 
			
		||||
 | 
			
		||||
@ -9,9 +9,12 @@ import {
 | 
			
		||||
    styled,
 | 
			
		||||
} from '@mui/material';
 | 
			
		||||
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
 | 
			
		||||
import { ConstraintItem } from 'component/common/ConstraintsList/ConstraintItem/ConstraintItem';
 | 
			
		||||
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader';
 | 
			
		||||
import { objectId } from 'utils/objectId';
 | 
			
		||||
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
 | 
			
		||||
import {
 | 
			
		||||
    ConstraintListItem,
 | 
			
		||||
    ConstraintsList,
 | 
			
		||||
} from 'component/common/ConstraintsList/ConstraintsList';
 | 
			
		||||
 | 
			
		||||
type SegmentItemProps = {
 | 
			
		||||
    segment: Partial<ISegment>;
 | 
			
		||||
@ -21,7 +24,11 @@ type SegmentItemProps = {
 | 
			
		||||
    headerContent?: JSX.Element;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const StyledAccordion = styled(Accordion)(({ theme }) => ({
 | 
			
		||||
const StyledConstraintListItem = styled(ConstraintListItem)(() => ({
 | 
			
		||||
    padding: 0,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledAccordion = styled(Accordion)(() => ({
 | 
			
		||||
    boxShadow: 'none',
 | 
			
		||||
    margin: 0,
 | 
			
		||||
    padding: 0,
 | 
			
		||||
@ -32,16 +39,14 @@ const StyledAccordion = styled(Accordion)(({ theme }) => ({
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
 | 
			
		||||
    padding: 0,
 | 
			
		||||
    padding: theme.spacing(0, 3),
 | 
			
		||||
    fontSize: theme.typography.body2.fontSize,
 | 
			
		||||
    minHeight: 'unset',
 | 
			
		||||
    '.MuiAccordionSummary-content, .MuiAccordionSummary-content.Mui-expanded': {
 | 
			
		||||
        margin: 0,
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
 | 
			
		||||
    padding: theme.spacing(2, 0, 1),
 | 
			
		||||
    borderTop: `1px dashed ${theme.palette.divider}`,
 | 
			
		||||
    padding: theme.spacing(1.5, 3, 2.5),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledLink = styled(Link)({
 | 
			
		||||
@ -55,8 +60,6 @@ const StyledActionsContainer = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    marginLeft: 'auto',
 | 
			
		||||
    marginTop: theme.spacing(-0.5),
 | 
			
		||||
    marginBottom: theme.spacing(-0.5),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledButton = styled(Button)(({ theme }) => ({
 | 
			
		||||
@ -85,10 +88,12 @@ export const SegmentItem: FC<SegmentItemProps> = ({
 | 
			
		||||
            return (
 | 
			
		||||
                <ConstraintsList>
 | 
			
		||||
                    {segment.constraints.map((constraint, index) => (
 | 
			
		||||
                        <ConstraintItem
 | 
			
		||||
                        <ConstraintListItem
 | 
			
		||||
                            key={`${objectId(constraint)}-${index}`}
 | 
			
		||||
                            {...constraint}
 | 
			
		||||
                        />
 | 
			
		||||
                        >
 | 
			
		||||
                            {/* FIXME: use accordion */}
 | 
			
		||||
                            <ConstraintItemHeader {...constraint} />
 | 
			
		||||
                        </ConstraintListItem>
 | 
			
		||||
                    ))}
 | 
			
		||||
                </ConstraintsList>
 | 
			
		||||
            );
 | 
			
		||||
@ -102,6 +107,7 @@ export const SegmentItem: FC<SegmentItemProps> = ({
 | 
			
		||||
    }, [constraintList, segment.constraints]);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledConstraintListItem>
 | 
			
		||||
            <StyledAccordion expanded={isOpen} disableGutters>
 | 
			
		||||
                <StyledAccordionSummary id={`segment-accordion-${segment.id}`}>
 | 
			
		||||
                    <StrategyEvaluationItem type='Segment'>
 | 
			
		||||
@ -124,5 +130,6 @@ export const SegmentItem: FC<SegmentItemProps> = ({
 | 
			
		||||
                </StyledAccordionSummary>
 | 
			
		||||
                <StyledAccordionDetails>{constraints}</StyledAccordionDetails>
 | 
			
		||||
            </StyledAccordion>
 | 
			
		||||
        </StyledConstraintListItem>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -25,13 +25,14 @@ const StyledTruncatorContainer = styled(Box, {
 | 
			
		||||
 | 
			
		||||
type OverridableTooltipProps = Omit<TooltipProps, 'children'>;
 | 
			
		||||
 | 
			
		||||
interface ITruncatorProps extends BoxProps {
 | 
			
		||||
export type TruncatorProps = {
 | 
			
		||||
    lines?: number;
 | 
			
		||||
    title?: string;
 | 
			
		||||
    arrow?: boolean;
 | 
			
		||||
    tooltipProps?: OverridableTooltipProps;
 | 
			
		||||
    children: React.ReactNode;
 | 
			
		||||
}
 | 
			
		||||
    onSetTruncated?: (isTruncated: boolean) => void;
 | 
			
		||||
} & BoxProps;
 | 
			
		||||
 | 
			
		||||
export const Truncator = ({
 | 
			
		||||
    lines = 1,
 | 
			
		||||
@ -40,8 +41,9 @@ export const Truncator = ({
 | 
			
		||||
    tooltipProps,
 | 
			
		||||
    children,
 | 
			
		||||
    component = 'span',
 | 
			
		||||
    onSetTruncated,
 | 
			
		||||
    ...props
 | 
			
		||||
}: ITruncatorProps) => {
 | 
			
		||||
}: TruncatorProps) => {
 | 
			
		||||
    const [isTruncated, setIsTruncated] = useState(false);
 | 
			
		||||
    const ref = useRef<HTMLDivElement>(null);
 | 
			
		||||
 | 
			
		||||
@ -50,7 +52,6 @@ export const Truncator = ({
 | 
			
		||||
            setIsTruncated(ref.current.scrollHeight > ref.current.offsetHeight);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        const resizeObserver = new ResizeObserver(checkTruncation);
 | 
			
		||||
        if (ref.current) {
 | 
			
		||||
@ -59,6 +60,10 @@ export const Truncator = ({
 | 
			
		||||
        return () => resizeObserver.disconnect();
 | 
			
		||||
    }, [title, children]);
 | 
			
		||||
 | 
			
		||||
    useEffect(() => {
 | 
			
		||||
        onSetTruncated?.(isTruncated);
 | 
			
		||||
    }, [isTruncated]);
 | 
			
		||||
 | 
			
		||||
    const overridableTooltipProps: OverridableTooltipProps = {
 | 
			
		||||
        title,
 | 
			
		||||
        arrow,
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,12 @@ const StyledPayloadHeader = styled('div')(({ theme }) => ({
 | 
			
		||||
    marginBottom: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledValuesContainer = styled('div')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    gap: theme.spacing(0.75, 0.5),
 | 
			
		||||
    flexWrap: 'wrap',
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const RolloutVariants: FC<{
 | 
			
		||||
    variants?: StrategyVariantSchema[];
 | 
			
		||||
}> = ({ variants }) => {
 | 
			
		||||
@ -38,6 +44,7 @@ export const RolloutVariants: FC<{
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StrategyEvaluationItem type={`Variants (${variants.length})`}>
 | 
			
		||||
            <StyledValuesContainer>
 | 
			
		||||
                {variants.map((variant, i) => (
 | 
			
		||||
                    <HtmlTooltip
 | 
			
		||||
                        arrow
 | 
			
		||||
@ -66,6 +73,7 @@ export const RolloutVariants: FC<{
 | 
			
		||||
                        />
 | 
			
		||||
                    </HtmlTooltip>
 | 
			
		||||
                ))}
 | 
			
		||||
            </StyledValuesContainer>
 | 
			
		||||
        </StrategyEvaluationItem>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -3,14 +3,17 @@ import type { FeatureStrategySchema } from 'openapi';
 | 
			
		||||
import type { IFeatureStrategyPayload } from 'interfaces/strategy';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag';
 | 
			
		||||
import { StrategyExecution as LegacyStrategyExecution } from './LegacyStrategyExecution';
 | 
			
		||||
import { ConstraintItem } from 'component/common/ConstraintsList/ConstraintItem/ConstraintItem';
 | 
			
		||||
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader';
 | 
			
		||||
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
 | 
			
		||||
import { objectId } from 'utils/objectId';
 | 
			
		||||
import { useCustomStrategyParameters } from './hooks/useCustomStrategyParameters';
 | 
			
		||||
import { useStrategyParameters } from './hooks/useStrategyParameters';
 | 
			
		||||
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
 | 
			
		||||
import { SegmentItem } from 'component/common/SegmentItem/SegmentItem';
 | 
			
		||||
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
 | 
			
		||||
import {
 | 
			
		||||
    ConstraintListItem,
 | 
			
		||||
    ConstraintsList,
 | 
			
		||||
} from 'component/common/ConstraintsList/ConstraintsList';
 | 
			
		||||
 | 
			
		||||
type StrategyExecutionProps = {
 | 
			
		||||
    strategy: IFeatureStrategyPayload | FeatureStrategySchema;
 | 
			
		||||
@ -50,12 +53,16 @@ export const StrategyExecution: FC<StrategyExecutionProps> = ({
 | 
			
		||||
                <SegmentItem segment={segment} key={segment.id} />
 | 
			
		||||
            ))}
 | 
			
		||||
            {constraints?.map((constraint, index) => (
 | 
			
		||||
                <ConstraintItem
 | 
			
		||||
                    key={`${objectId(constraint)}-${index}`}
 | 
			
		||||
                    {...constraint}
 | 
			
		||||
                />
 | 
			
		||||
                <ConstraintListItem key={`${objectId(constraint)}-${index}`}>
 | 
			
		||||
                    {/* FIXME: use constraint accordion */}
 | 
			
		||||
                    <ConstraintItemHeader {...constraint} />
 | 
			
		||||
                </ConstraintListItem>
 | 
			
		||||
            ))}
 | 
			
		||||
            {isCustomStrategy ? customStrategyItems : strategyParameters}
 | 
			
		||||
            {(isCustomStrategy ? customStrategyItems : strategyParameters).map(
 | 
			
		||||
                (item, index) => (
 | 
			
		||||
                    <ConstraintListItem key={index}>{item}</ConstraintListItem>
 | 
			
		||||
                ),
 | 
			
		||||
            )}
 | 
			
		||||
        </ConstraintsList>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -3,10 +3,11 @@ import type {
 | 
			
		||||
    PlaygroundConstraintSchema,
 | 
			
		||||
    PlaygroundRequestSchema,
 | 
			
		||||
} from 'openapi';
 | 
			
		||||
import { ConstraintItem } from 'component/common/ConstraintsList/ConstraintItem/ConstraintItem';
 | 
			
		||||
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader';
 | 
			
		||||
import CheckCircle from '@mui/icons-material/CheckCircle';
 | 
			
		||||
import { styled } from '@mui/material';
 | 
			
		||||
import Cancel from '@mui/icons-material/Cancel';
 | 
			
		||||
import { ConstraintListItem } from 'component/common/ConstraintsList/ConstraintsList';
 | 
			
		||||
 | 
			
		||||
interface IConstraintExecutionProps {
 | 
			
		||||
    constraint?: PlaygroundConstraintSchema;
 | 
			
		||||
@ -20,7 +21,7 @@ const StyledContainer = styled('div', {
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
    paddingInline: theme.spacing(0.25),
 | 
			
		||||
    padding: theme.spacing(0.5, 0.25),
 | 
			
		||||
    color:
 | 
			
		||||
        variant === 'ok'
 | 
			
		||||
            ? theme.palette.success.dark
 | 
			
		||||
@ -67,13 +68,15 @@ export const ConstraintExecution: FC<IConstraintExecutionProps> = ({
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <ConstraintItem {...constraint} />
 | 
			
		||||
        <ConstraintListItem>
 | 
			
		||||
            <div>
 | 
			
		||||
                <ConstraintItemHeader {...constraint} />
 | 
			
		||||
                {constraint.result ? (
 | 
			
		||||
                    <ConstraintOk />
 | 
			
		||||
                ) : (
 | 
			
		||||
                    <ConstraintError text={errorText()} />
 | 
			
		||||
                )}
 | 
			
		||||
        </>
 | 
			
		||||
            </div>
 | 
			
		||||
        </ConstraintListItem>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,10 @@ import { ConstraintExecution } from './ConstraintExecution/ConstraintExecution';
 | 
			
		||||
import { formattedStrategyNames } from 'utils/strategyNames';
 | 
			
		||||
import { StyledBoxSummary } from './StrategyExecution.styles';
 | 
			
		||||
import { Badge } from 'component/common/Badge/Badge';
 | 
			
		||||
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
 | 
			
		||||
import {
 | 
			
		||||
    ConstraintListItem,
 | 
			
		||||
    ConstraintsList,
 | 
			
		||||
} from 'component/common/ConstraintsList/ConstraintsList';
 | 
			
		||||
import { objectId } from 'utils/objectId';
 | 
			
		||||
import type { FC } from 'react';
 | 
			
		||||
import { SegmentExecution } from './SegmentExecution/SegmentExecution';
 | 
			
		||||
@ -60,8 +63,14 @@ export const StrategyExecution: FC<StrategyExecutionProps> = ({
 | 
			
		||||
                  />
 | 
			
		||||
              ))
 | 
			
		||||
            : []),
 | 
			
		||||
        hasExecutionParameters && params,
 | 
			
		||||
        isCustomStrategy && customStrategyItems,
 | 
			
		||||
        hasExecutionParameters &&
 | 
			
		||||
            params.map((param, index) => (
 | 
			
		||||
                <ConstraintListItem key={index}>{param}</ConstraintListItem>
 | 
			
		||||
            )),
 | 
			
		||||
        isCustomStrategy &&
 | 
			
		||||
            customStrategyItems.map((param, index) => (
 | 
			
		||||
                <ConstraintListItem key={index}>{param}</ConstraintListItem>
 | 
			
		||||
            )),
 | 
			
		||||
        name === 'default' && (
 | 
			
		||||
            <StyledBoxSummary sx={{ width: '100%' }}>
 | 
			
		||||
                The standard strategy is <Badge color='success'>ON</Badge> for
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user