1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-27 01:19:00 +02:00

Constraint values preview and filtering (#9603)

Restore constraint accordion to flag page.
This commit is contained in:
Tymoteusz Czech 2025-03-25 11:24:22 +01:00 committed by GitHub
parent c161291d09
commit d8c7e31b18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 87 additions and 48 deletions

View File

@ -1,4 +1,4 @@
import type { ComponentProps, FC } from 'react'; import type { ComponentProps, FC, ReactNode } from 'react';
import { StrategyEvaluationItem } from '../StrategyEvaluationItem/StrategyEvaluationItem'; import { StrategyEvaluationItem } 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';
@ -48,6 +48,13 @@ const CaseSensitive: FC = () => {
); );
}; };
const StyledConstraintContainer = styled('div')(({ theme }) => ({
display: 'grid',
gridTemplateColumns: 'repeat(3, auto)',
gap: theme.spacing(2),
placeItems: 'center',
}));
const StyledOperatorGroup = styled('div')(({ theme }) => ({ const StyledOperatorGroup = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@ -60,9 +67,15 @@ const StyledConstraintName = styled('div')(({ theme }) => ({
overflow: 'hidden', overflow: 'hidden',
})); }));
export const ConstraintItemHeader: FC< type ConstraintItemHeaderProps = ConstraintSchema & {
ConstraintSchema & Pick<ComponentProps<typeof ValuesList>, 'onSetTruncated'> viewMore?: ReactNode;
> = ({ onSetTruncated, ...constraint }) => { } & Pick<ComponentProps<typeof ValuesList>, 'onSetTruncated'>;
export const ConstraintItemHeader: FC<ConstraintItemHeaderProps> = ({
onSetTruncated,
viewMore,
...constraint
}) => {
const { caseInsensitive, contextName, inverted, operator, value, values } = const { caseInsensitive, contextName, inverted, operator, value, values } =
constraint; constraint;
const { locationSettings } = useLocationSettings(); const { locationSettings } = useLocationSettings();
@ -77,22 +90,29 @@ export const ConstraintItemHeader: FC<
return ( return (
<StrategyEvaluationItem type='Constraint'> <StrategyEvaluationItem type='Constraint'>
<StyledConstraintName> <StyledConstraintContainer>
<Truncator lines={2} title={contextName} arrow> <StyledConstraintName>
{contextName} <Truncator title={contextName} arrow>
</Truncator> {contextName}
</StyledConstraintName> </Truncator>
<StyledOperatorGroup> </StyledConstraintName>
<Operator label={operator} inverted={inverted} /> <StyledOperatorGroup>
{isCaseSensitive(operator, caseInsensitive) ? ( <Operator label={operator} inverted={inverted} />
<CaseSensitive /> {isCaseSensitive(operator, caseInsensitive) ? (
) : null} <CaseSensitive />
</StyledOperatorGroup> ) : null}
<ValuesList </StyledOperatorGroup>
values={items} <div>
onSetTruncated={onSetTruncated} <div>
tooltips={tooltips} <ValuesList
/> values={items}
onSetTruncated={onSetTruncated}
tooltips={tooltips}
/>
{viewMore}
</div>
</div>
</StyledConstraintContainer>
</StrategyEvaluationItem> </StrategyEvaluationItem>
); );
}; };

View File

@ -1,9 +1,10 @@
import type { FC } from 'react'; import type { FC } from 'react';
import { styled, Tooltip } from '@mui/material'; import { styled } from '@mui/material';
import { import {
Truncator, Truncator,
type TruncatorProps, type TruncatorProps,
} from 'component/common/Truncator/Truncator'; } from 'component/common/Truncator/Truncator';
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
export type ValuesListProps = { export type ValuesListProps = {
values?: string[]; values?: string[];
@ -14,7 +15,12 @@ const StyledValuesContainer = styled('div')({
flex: '1 1 0', flex: '1 1 0',
}); });
const StyledValueItem = styled('span')(({ theme }) => ({ const StyledTruncator = styled(Truncator)({
padding: 0,
margin: 0,
});
const StyledValueItem = styled('li')(({ theme }) => ({
padding: theme.spacing(0.25), padding: theme.spacing(0.25),
display: 'inline-block', display: 'inline-block',
span: { span: {
@ -45,22 +51,30 @@ export const ValuesList: FC<ValuesListProps> = ({
lines={2} lines={2}
onSetTruncated={() => onSetTruncated?.(false)} onSetTruncated={() => onSetTruncated?.(false)}
> >
<Tooltip title={tooltips?.[values[0]] || ''}> <TooltipResolver title={tooltips?.[values[0]] || ''}>
<span>{values[0]}</span> <span>{values[0]}</span>
</Tooltip> </TooltipResolver>
</Truncator> </Truncator>
</StyledSingleValue> </StyledSingleValue>
) : null} ) : null}
{values && values?.length > 1 ? ( {values && values?.length > 1 ? (
<Truncator title='' lines={2} onSetTruncated={onSetTruncated}> <StyledTruncator
title=''
lines={2}
onSetTruncated={onSetTruncated}
component='ul'
>
{values.map((value) => ( {values.map((value) => (
<Tooltip title={tooltips?.[value] || ''} key={value}> <TooltipResolver
title={tooltips?.[value] || ''}
key={value}
>
<StyledValueItem> <StyledValueItem>
<span>{value}</span> <span>{value}</span>
</StyledValueItem> </StyledValueItem>
</Tooltip> </TooltipResolver>
))} ))}
</Truncator> </StyledTruncator>
) : null} ) : null}
</StyledValuesContainer> </StyledValuesContainer>
); );

View File

@ -30,7 +30,6 @@ interface IConstraintAccordionViewProps {
const StyledAccordion = styled(Accordion)(({ theme }) => ({ const StyledAccordion = styled(Accordion)(({ theme }) => ({
border: `1px solid ${theme.palette.divider}`, border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadiusMedium, borderRadius: theme.shape.borderRadiusMedium,
backgroundColor: 'transparent',
boxShadow: 'none', boxShadow: 'none',
margin: 0, margin: 0,
'&:before': { '&:before': {

View File

@ -10,7 +10,7 @@ interface IConstraintAccordionViewBodyProps {
} }
const StyledValueContainer = styled('div')(({ theme }) => ({ const StyledValueContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(2, 0), padding: theme.spacing(1, 0),
maxHeight: '400px', maxHeight: '400px',
overflowY: 'auto', overflowY: 'auto',
})); }));

View File

@ -55,7 +55,6 @@ export const ConstraintAccordionViewHeader = ({
{flagOverviewRedesign ? ( {flagOverviewRedesign ? (
<ConstraintAccordionViewHeaderInfo <ConstraintAccordionViewHeaderInfo
constraint={constraint} constraint={constraint}
singleValue={singleValue}
allowExpand={allowExpand} allowExpand={allowExpand}
expanded={expanded} expanded={expanded}
disabled={disabled} disabled={disabled}

View File

@ -1,9 +1,7 @@
import { IconButton, styled } from '@mui/material'; import { styled } from '@mui/material';
import type { IConstraint } from 'interfaces/strategy'; import type { IConstraint } from 'interfaces/strategy';
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader'; import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader';
import { useState } from 'react'; import { useState } from 'react';
import VisibilityIcon from '@mui/icons-material/Visibility';
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
const StyledHeaderWrapper = styled('div')(({ theme }) => ({ const StyledHeaderWrapper = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
@ -24,9 +22,14 @@ const StyledHeaderMetaInfo = styled('div')(({ theme }) => ({
}, },
})); }));
const StyledExpandItem = styled('div')(({ theme }) => ({
color: theme.palette.text.secondary,
margin: theme.spacing(0.25, 0, 0, 0.75),
fontSize: theme.fontSizes.smallerBody,
}));
interface ConstraintAccordionViewHeaderMetaInfoProps { interface ConstraintAccordionViewHeaderMetaInfoProps {
constraint: IConstraint; constraint: IConstraint;
singleValue: boolean;
expanded: boolean; expanded: boolean;
allowExpand: (shouldExpand: boolean) => void; allowExpand: (shouldExpand: boolean) => void;
disabled?: boolean; disabled?: boolean;
@ -49,12 +52,16 @@ export const ConstraintAccordionViewHeaderInfo = ({
setExpandable(state); setExpandable(state);
allowExpand(state); allowExpand(state);
}} }}
viewMore={
expandable ? (
<StyledExpandItem>
{expanded
? 'View less'
: `View all (${constraint.values?.length})`}
</StyledExpandItem>
) : null
}
/> />
{expandable ? (
<IconButton type='button'>
{expanded ? <VisibilityOffIcon /> : <VisibilityIcon />}
</IconButton>
) : null}
</StyledHeaderMetaInfo> </StyledHeaderMetaInfo>
</StyledHeaderWrapper> </StyledHeaderWrapper>
); );

View File

@ -20,10 +20,10 @@ export const ConstraintValueSearch = ({
value={filter} value={filter}
onChange={(e) => setFilter(e.target.value)} onChange={(e) => setFilter(e.target.value)}
placeholder='Filter values' placeholder='Filter values'
style={{ sx={(theme) => ({
width: '100%', width: '100%',
margin: '1rem 0', margin: theme.spacing(1, 0, 2),
}} })}
variant='outlined' variant='outlined'
size='small' size='small'
InputProps={{ InputProps={{

View File

@ -3,7 +3,7 @@ import type { FeatureStrategySchema } from 'openapi';
import type { IFeatureStrategyPayload } from 'interfaces/strategy'; import type { IFeatureStrategyPayload } from 'interfaces/strategy';
import { useUiFlag } from 'hooks/useUiFlag'; import { useUiFlag } from 'hooks/useUiFlag';
import { StrategyExecution as LegacyStrategyExecution } from './LegacyStrategyExecution'; import { StrategyExecution as LegacyStrategyExecution } from './LegacyStrategyExecution';
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader'; import { ConstraintAccordionView } from 'component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies'; import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
import { objectId } from 'utils/objectId'; import { objectId } from 'utils/objectId';
import { useCustomStrategyParameters } from './hooks/useCustomStrategyParameters'; import { useCustomStrategyParameters } from './hooks/useCustomStrategyParameters';
@ -53,10 +53,10 @@ export const StrategyExecution: FC<StrategyExecutionProps> = ({
<SegmentItem segment={segment} key={segment.id} /> <SegmentItem segment={segment} key={segment.id} />
))} ))}
{constraints?.map((constraint, index) => ( {constraints?.map((constraint, index) => (
<ConstraintListItem key={`${objectId(constraint)}-${index}`}> <ConstraintAccordionView
{/* FIXME: use constraint accordion */} key={`${objectId(constraint)}-${index}`}
<ConstraintItemHeader {...constraint} /> constraint={constraint}
</ConstraintListItem> />
))} ))}
{(isCustomStrategy ? customStrategyItems : strategyParameters).map( {(isCustomStrategy ? customStrategyItems : strategyParameters).map(
(item, index) => ( (item, index) => (