mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-04 01:18:20 +02: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