mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-14 01:16:17 +02:00
values truncation
This commit is contained in:
parent
a65c8baf56
commit
bb779828d3
@ -1,5 +1,8 @@
|
|||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { StrategyEvaluationItem } from '../StrategyEvaluationItem/StrategyEvaluationItem';
|
import {
|
||||||
|
StrategyEvaluationItem,
|
||||||
|
type StrategyEvaluationItemProps,
|
||||||
|
} from '../StrategyEvaluationItem/StrategyEvaluationItem';
|
||||||
import type { ConstraintSchema } from 'openapi';
|
import type { ConstraintSchema } from 'openapi';
|
||||||
import { formatOperatorDescription } from 'component/common/ConstraintAccordion/ConstraintOperator/formatOperatorDescription';
|
import { formatOperatorDescription } from 'component/common/ConstraintAccordion/ConstraintOperator/formatOperatorDescription';
|
||||||
import { StrategyEvaluationChip } from '../StrategyEvaluationChip/StrategyEvaluationChip';
|
import { StrategyEvaluationChip } from '../StrategyEvaluationChip/StrategyEvaluationChip';
|
||||||
@ -29,18 +32,25 @@ const StyledOperatorGroup = styled('div')(({ theme }) => ({
|
|||||||
gap: theme.spacing(0.5),
|
gap: theme.spacing(0.5),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const ConstraintItem: FC<ConstraintSchema> = ({
|
export const ConstraintItem: FC<
|
||||||
|
ConstraintSchema & Pick<StrategyEvaluationItemProps, 'onSetTruncated'>
|
||||||
|
> = ({
|
||||||
caseInsensitive,
|
caseInsensitive,
|
||||||
contextName,
|
contextName,
|
||||||
inverted,
|
inverted,
|
||||||
operator,
|
operator,
|
||||||
value,
|
value,
|
||||||
values,
|
values,
|
||||||
|
onSetTruncated,
|
||||||
}) => {
|
}) => {
|
||||||
const items = value ? [value, ...(values || [])] : values || [];
|
const items = value ? [value, ...(values || [])] : values || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StrategyEvaluationItem type='Constraint' values={items}>
|
<StrategyEvaluationItem
|
||||||
|
type='Constraint'
|
||||||
|
values={items}
|
||||||
|
onSetTruncated={onSetTruncated}
|
||||||
|
>
|
||||||
{contextName}
|
{contextName}
|
||||||
<StyledOperatorGroup>
|
<StyledOperatorGroup>
|
||||||
{inverted ? <Inverted /> : null}
|
{inverted ? <Inverted /> : null}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import { Chip, type ChipProps, styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
|
import {
|
||||||
|
Truncator,
|
||||||
|
type TruncatorProps,
|
||||||
|
} from 'component/common/Truncator/Truncator';
|
||||||
import type { FC, ReactNode } from 'react';
|
import type { FC, ReactNode } from 'react';
|
||||||
|
|
||||||
type StrategyItemProps = {
|
export type StrategyEvaluationItemProps = {
|
||||||
type?: ReactNode;
|
type?: ReactNode;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
values?: string[];
|
values?: string[];
|
||||||
};
|
} & Pick<TruncatorProps, 'onSetTruncated'>;
|
||||||
|
|
||||||
const StyledContainer = styled('div')(({ theme }) => ({
|
const StyledContainer = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -17,7 +21,6 @@ const StyledContainer = styled('div')(({ theme }) => ({
|
|||||||
const StyledContent = styled('div')(({ theme }) => ({
|
const StyledContent = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(1),
|
||||||
flexWrap: 'wrap',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -30,37 +33,33 @@ const StyledType = styled('span')(({ theme }) => ({
|
|||||||
width: theme.spacing(10),
|
width: theme.spacing(10),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledValuesGroup = styled('div')(({ theme }) => ({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: theme.spacing(0.5),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledValue = styled(({ ...props }: ChipProps) => (
|
|
||||||
<Chip size='small' {...props} />
|
|
||||||
))(({ theme }) => ({
|
|
||||||
padding: theme.spacing(0.5),
|
|
||||||
background: theme.palette.background.elevation1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract building block for a list of constraints, segments and other items inside a strategy
|
* 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,
|
type,
|
||||||
children,
|
children,
|
||||||
values,
|
values,
|
||||||
|
onSetTruncated,
|
||||||
}) => (
|
}) => (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledType>{type}</StyledType>
|
<StyledType>{type}</StyledType>
|
||||||
<StyledContent>
|
<StyledContent>
|
||||||
{children}
|
{children}
|
||||||
{values && values?.length > 0 ? (
|
{values && values?.length === 1 ? (
|
||||||
<StyledValuesGroup>
|
<Truncator
|
||||||
{values?.map((value, index) => (
|
title={values[0]}
|
||||||
<StyledValue key={`${value}#${index}`} label={value} />
|
arrow
|
||||||
))}
|
lines={2}
|
||||||
</StyledValuesGroup>
|
onSetTruncated={() => onSetTruncated?.(false)}
|
||||||
|
>
|
||||||
|
{values[0]}
|
||||||
|
</Truncator>
|
||||||
|
) : null}
|
||||||
|
{values && values?.length > 1 ? (
|
||||||
|
<Truncator title='' lines={2} onSetTruncated={onSetTruncated}>
|
||||||
|
{values.join(', ')}
|
||||||
|
</Truncator>
|
||||||
) : null}
|
) : null}
|
||||||
</StyledContent>
|
</StyledContent>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
|
@ -4,6 +4,7 @@ import { ConstraintAccordionViewHeaderInfo } from './ConstraintAccordionViewHead
|
|||||||
import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeaderActions/ConstraintAccordionHeaderActions';
|
import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeaderActions/ConstraintAccordionHeaderActions';
|
||||||
import { styled } from '@mui/system';
|
import { styled } from '@mui/system';
|
||||||
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
|
||||||
interface IConstraintAccordionViewHeaderProps {
|
interface IConstraintAccordionViewHeaderProps {
|
||||||
constraint: IConstraint;
|
constraint: IConstraint;
|
||||||
@ -38,6 +39,7 @@ export const ConstraintAccordionViewHeader = ({
|
|||||||
disabled,
|
disabled,
|
||||||
}: IConstraintAccordionViewHeaderProps) => {
|
}: IConstraintAccordionViewHeaderProps) => {
|
||||||
const { context } = useUnleashContext();
|
const { context } = useUnleashContext();
|
||||||
|
const flagOverviewRedesign = useUiFlag('flagOverviewRedesign');
|
||||||
const { contextName } = constraint;
|
const { contextName } = constraint;
|
||||||
|
|
||||||
const disableEdit = !context
|
const disableEdit = !context
|
||||||
@ -46,7 +48,9 @@ export const ConstraintAccordionViewHeader = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<ConstraintIcon compact={compact} disabled={disabled} />
|
{!flagOverviewRedesign ? (
|
||||||
|
<ConstraintIcon compact={compact} disabled={disabled} />
|
||||||
|
) : null}
|
||||||
<ConstraintAccordionViewHeaderInfo
|
<ConstraintAccordionViewHeaderInfo
|
||||||
constraint={constraint}
|
constraint={constraint}
|
||||||
singleValue={singleValue}
|
singleValue={singleValue}
|
||||||
|
@ -1,29 +1,9 @@
|
|||||||
import { styled, Tooltip } from '@mui/material';
|
import { IconButton, styled } 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';
|
import type { IConstraint } from 'interfaces/strategy';
|
||||||
|
import { ConstraintItem } from 'component/common/ConstraintsList/ConstraintItem/ConstraintItem';
|
||||||
const StyledHeaderText = styled('span')(({ theme }) => ({
|
import { useState } from 'react';
|
||||||
display: '-webkit-box',
|
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||||
WebkitLineClamp: 3,
|
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||||
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 }) => ({
|
const StyledHeaderWrapper = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -55,49 +35,26 @@ interface ConstraintAccordionViewHeaderMetaInfoProps {
|
|||||||
|
|
||||||
export const ConstraintAccordionViewHeaderInfo = ({
|
export const ConstraintAccordionViewHeaderInfo = ({
|
||||||
constraint,
|
constraint,
|
||||||
singleValue,
|
|
||||||
allowExpand,
|
allowExpand,
|
||||||
expanded,
|
expanded,
|
||||||
disabled = false,
|
|
||||||
maxLength = 112, //The max number of characters in the values text for NOT allowing expansion
|
|
||||||
}: ConstraintAccordionViewHeaderMetaInfoProps) => {
|
}: ConstraintAccordionViewHeaderMetaInfoProps) => {
|
||||||
|
const [expandable, setExpandable] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledHeaderWrapper>
|
<StyledHeaderWrapper>
|
||||||
<StyledHeaderMetaInfo>
|
<StyledHeaderMetaInfo>
|
||||||
<Tooltip title={constraint.contextName} arrow>
|
<ConstraintItem
|
||||||
<StyledHeaderText
|
{...constraint}
|
||||||
sx={(theme) => ({
|
onSetTruncated={(state: boolean) => {
|
||||||
color: disabled
|
setExpandable(state);
|
||||||
? theme.palette.text.secondary
|
allowExpand(state);
|
||||||
: '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}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
{expandable ? (
|
||||||
|
<IconButton type='button'>
|
||||||
|
{expanded ? <VisibilityOffIcon /> : <VisibilityIcon />}
|
||||||
|
</IconButton>
|
||||||
|
) : null}
|
||||||
</StyledHeaderMetaInfo>
|
</StyledHeaderMetaInfo>
|
||||||
</StyledHeaderWrapper>
|
</StyledHeaderWrapper>
|
||||||
);
|
);
|
||||||
|
@ -25,13 +25,14 @@ const StyledTruncatorContainer = styled(Box, {
|
|||||||
|
|
||||||
type OverridableTooltipProps = Omit<TooltipProps, 'children'>;
|
type OverridableTooltipProps = Omit<TooltipProps, 'children'>;
|
||||||
|
|
||||||
interface ITruncatorProps extends BoxProps {
|
export type TruncatorProps = {
|
||||||
lines?: number;
|
lines?: number;
|
||||||
title?: string;
|
title?: string;
|
||||||
arrow?: boolean;
|
arrow?: boolean;
|
||||||
tooltipProps?: OverridableTooltipProps;
|
tooltipProps?: OverridableTooltipProps;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
onSetTruncated?: (isTruncated: boolean) => void;
|
||||||
|
} & BoxProps;
|
||||||
|
|
||||||
export const Truncator = ({
|
export const Truncator = ({
|
||||||
lines = 1,
|
lines = 1,
|
||||||
@ -40,8 +41,9 @@ export const Truncator = ({
|
|||||||
tooltipProps,
|
tooltipProps,
|
||||||
children,
|
children,
|
||||||
component = 'span',
|
component = 'span',
|
||||||
|
onSetTruncated,
|
||||||
...props
|
...props
|
||||||
}: ITruncatorProps) => {
|
}: TruncatorProps) => {
|
||||||
const [isTruncated, setIsTruncated] = useState(false);
|
const [isTruncated, setIsTruncated] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@ -50,7 +52,6 @@ export const Truncator = ({
|
|||||||
setIsTruncated(ref.current.scrollHeight > ref.current.offsetHeight);
|
setIsTruncated(ref.current.scrollHeight > ref.current.offsetHeight);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const resizeObserver = new ResizeObserver(checkTruncation);
|
const resizeObserver = new ResizeObserver(checkTruncation);
|
||||||
if (ref.current) {
|
if (ref.current) {
|
||||||
@ -59,6 +60,10 @@ export const Truncator = ({
|
|||||||
return () => resizeObserver.disconnect();
|
return () => resizeObserver.disconnect();
|
||||||
}, [title, children]);
|
}, [title, children]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onSetTruncated?.(isTruncated);
|
||||||
|
}, [isTruncated]);
|
||||||
|
|
||||||
const overridableTooltipProps: OverridableTooltipProps = {
|
const overridableTooltipProps: OverridableTooltipProps = {
|
||||||
title,
|
title,
|
||||||
arrow,
|
arrow,
|
||||||
|
@ -29,6 +29,12 @@ const StyledPayloadHeader = styled('div')(({ theme }) => ({
|
|||||||
marginBottom: theme.spacing(1),
|
marginBottom: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const StyledValuesContainer = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
gap: theme.spacing(0.75, 0.5),
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}));
|
||||||
|
|
||||||
export const RolloutVariants: FC<{
|
export const RolloutVariants: FC<{
|
||||||
variants?: StrategyVariantSchema[];
|
variants?: StrategyVariantSchema[];
|
||||||
}> = ({ variants }) => {
|
}> = ({ variants }) => {
|
||||||
@ -38,34 +44,36 @@ export const RolloutVariants: FC<{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StrategyEvaluationItem type={`Variants (${variants.length})`}>
|
<StrategyEvaluationItem type={`Variants (${variants.length})`}>
|
||||||
{variants.map((variant, i) => (
|
<StyledValuesContainer>
|
||||||
<HtmlTooltip
|
{variants.map((variant, i) => (
|
||||||
arrow
|
<HtmlTooltip
|
||||||
title={
|
arrow
|
||||||
variant.payload?.value ? (
|
title={
|
||||||
<div>
|
variant.payload?.value ? (
|
||||||
<StyledPayloadHeader>
|
<div>
|
||||||
Payload:
|
<StyledPayloadHeader>
|
||||||
</StyledPayloadHeader>
|
Payload:
|
||||||
<code>{variant.payload?.value}</code>
|
</StyledPayloadHeader>
|
||||||
</div>
|
<code>{variant.payload?.value}</code>
|
||||||
) : null
|
</div>
|
||||||
}
|
) : null
|
||||||
key={variant.name}
|
|
||||||
>
|
|
||||||
<StyledVariantChip
|
|
||||||
key={variant.name}
|
|
||||||
order={i}
|
|
||||||
label={
|
|
||||||
<>
|
|
||||||
<span>
|
|
||||||
{variant.weight / 10}% – {variant.name}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
/>
|
key={variant.name}
|
||||||
</HtmlTooltip>
|
>
|
||||||
))}
|
<StyledVariantChip
|
||||||
|
key={variant.name}
|
||||||
|
order={i}
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
<span>
|
||||||
|
{variant.weight / 10}% – {variant.name}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</HtmlTooltip>
|
||||||
|
))}
|
||||||
|
</StyledValuesContainer>
|
||||||
</StrategyEvaluationItem>
|
</StrategyEvaluationItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user