1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-12 01:17:04 +02:00

feat: improve constraints item on small screens (#9609)

Fixing constraint operator item, items alignment and padding for better presentation on mobile devices.
This commit is contained in:
Tymoteusz Czech 2025-03-27 13:33:25 +01:00 committed by GitHub
parent f7c04cc2cb
commit cf053470e5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 132 additions and 59 deletions

View File

@ -9,7 +9,7 @@ import {
} from '@mui/material';
import type { IConstraint } from 'interfaces/strategy';
import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody';
import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader';
import { ConstraintAccordionViewHeader } from 'component/common/NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader';
import { oneOf } from 'utils/oneOf';
import {
dateOperators,

View File

@ -19,6 +19,7 @@ const Operator: FC<{
<Tooltip title={inverted ? `Not ${label}` : label} arrow>
<StrategyEvaluationChip
label={formatOperatorDescription(label, inverted)}
multiline
/>
</Tooltip>
);
@ -49,10 +50,15 @@ const CaseSensitive: FC = () => {
};
const StyledConstraintContainer = styled('div')(({ theme }) => ({
display: 'grid',
gridTemplateColumns: 'repeat(3, auto)',
gap: theme.spacing(2),
placeItems: 'center',
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
[theme.breakpoints.up('sm')]: {
gap: theme.spacing(2),
display: 'grid',
gridTemplateColumns: 'repeat(3, auto)',
placeItems: 'center',
},
}));
const StyledOperatorGroup = styled('div')(({ theme }) => ({
@ -65,6 +71,9 @@ const StyledConstraintName = styled('div')(({ theme }) => ({
maxWidth: '150px',
paddingRight: theme.spacing(0.5),
overflow: 'hidden',
[theme.breakpoints.down('sm')]: {
maxWidth: 'unset',
},
}));
type ConstraintItemHeaderProps = ConstraintSchema & {

View File

@ -0,0 +1,20 @@
import { Box, styled } from '@mui/material';
export const ConstraintSeparator = styled(({ ...props }) => (
<Box role='separator' {...props}>
and
</Box>
))(({ theme }) => ({
position: 'absolute',
top: theme.spacing(-0.5),
left: theme.spacing(2),
transform: 'translateY(-50%)',
padding: theme.spacing(0.75, 1),
lineHeight: 1,
fontSize: theme.fontSizes.smallerBody,
color: theme.palette.text.primary,
background: theme.palette.background.application,
borderRadius: theme.shape.borderRadiusLarge,
zIndex: theme.zIndex.fab,
textTransform: 'uppercase',
}));

View File

@ -1,5 +1,6 @@
import { Children, isValidElement, type FC, type ReactNode } from 'react';
import { styled } from '@mui/material';
import { ConstraintSeparator } from './ConstraintSeparator/ConstraintSeparator';
const StyledList = styled('ul')(({ theme }) => ({
display: 'flex',
@ -15,7 +16,7 @@ export const ConstraintListItem = styled('div')(({ theme }) => ({
border: `1px solid ${theme.palette.divider}`,
borderRadius: theme.shape.borderRadiusMedium,
background: theme.palette.background.default,
padding: theme.spacing(1.5, 3),
padding: theme.spacing(1.5, 2),
display: 'flex',
flexFlow: 'column',
gap: theme.spacing(1),
@ -25,20 +26,6 @@ const StyledListItem = styled('li')({
position: 'relative',
});
const StyledAnd = styled('div')(({ theme }) => ({
position: 'absolute',
top: theme.spacing(-0.5),
left: theme.spacing(2),
transform: 'translateY(-50%)',
padding: theme.spacing(0.75, 1),
lineHeight: 1,
fontSize: theme.fontSizes.smallerBody,
color: theme.palette.text.primary,
background: theme.palette.background.application,
borderRadius: theme.shape.borderRadiusLarge,
zIndex: theme.zIndex.fab,
}));
export const ConstraintsList: FC<{ children: ReactNode }> = ({ children }) => {
const result: ReactNode[] = [];
Children.forEach(children, (child, index) => {
@ -46,9 +33,7 @@ export const ConstraintsList: FC<{ children: ReactNode }> = ({ children }) => {
result.push(
<StyledListItem key={index}>
{index > 0 ? (
<StyledAnd role='separator' key={`${index}-divider`}>
AND
</StyledAnd>
<ConstraintSeparator key={`${index}-divider`} />
) : null}
{child}
</StyledListItem>,

View File

@ -2,17 +2,27 @@ import { forwardRef } from 'react';
import type { ChipProps } from '@mui/material';
import { Chip, styled } from '@mui/material';
const StyledChip = styled(Chip)(({ theme }) => ({
borderRadius: `${theme.shape.borderRadius}px`,
padding: theme.spacing(0.25, 0),
fontSize: theme.fontSizes.smallerBody,
height: 'auto',
background: theme.palette.secondary.light,
border: `1px solid ${theme.palette.secondary.border}`,
color: theme.palette.secondary.dark,
fontWeight: theme.typography.fontWeightBold,
}));
export const StrategyEvaluationChip = forwardRef<HTMLDivElement, ChipProps>(
(props, ref) => <StyledChip size='small' ref={ref} {...props} />,
const StyledChip = styled(Chip)<{ multiline?: boolean }>(
({ theme, multiline }) => ({
borderRadius: `${theme.shape.borderRadius}px`,
padding: theme.spacing(0.25, 0),
fontSize: theme.fontSizes.smallerBody,
height: 'auto',
background: theme.palette.secondary.light,
border: `1px solid ${theme.palette.secondary.border}`,
color: theme.palette.secondary.dark,
fontWeight: theme.typography.fontWeightBold,
...(multiline
? {
'.MuiChip-label': {
whiteSpace: 'collapse',
},
}
: {}),
}),
);
export const StrategyEvaluationChip = forwardRef<
HTMLDivElement,
ChipProps & { multiline?: boolean }
>((props, ref) => <StyledChip size='small' ref={ref} {...props} />);

View File

@ -13,6 +13,9 @@ const StyledContainer = styled('div')(({ theme }) => ({
alignItems: 'center',
fontSize: theme.typography.body2.fontSize,
minHeight: theme.spacing(4),
[theme.breakpoints.down('sm')]: {
flexDirection: 'column',
},
}));
const StyledContent = styled('div')(({ theme }) => ({
@ -23,6 +26,9 @@ const StyledContent = styled('div')(({ theme }) => ({
filter: 'grayscale(1)',
color: theme.palette.text.secondary,
},
[theme.breakpoints.down('sm')]: {
width: '100%',
},
}));
const StyledType = styled('span')(({ theme }) => ({
@ -32,7 +38,11 @@ const StyledType = styled('span')(({ theme }) => ({
fontWeight: theme.typography.fontWeightBold,
color: theme.palette.text.secondary,
width: theme.spacing(10),
[theme.breakpoints.down('sm')]: {
width: '100%',
},
}));
/**
* Abstract building block for a list of constraints, segments and other items inside a strategy
*/

View File

@ -11,9 +11,13 @@ export type ValuesListProps = {
tooltips?: Record<string, string | undefined>;
} & Pick<TruncatorProps, 'onSetTruncated'>;
const StyledValuesContainer = styled('div')({
const StyledValuesContainer = styled('div')(({ theme }) => ({
flex: '1 1 0',
});
[theme.breakpoints.down('sm')]: {
display: 'block',
float: 'left',
},
}));
const StyledTruncator = styled(Truncator)({
padding: 0,

View File

@ -13,17 +13,15 @@ const StyledHeaderWrapper = styled('div')(({ theme }) => ({
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%',
},
}));
const StyledExpandItem = styled('div')(({ theme }) => ({
color: theme.palette.text.secondary,
color: theme.palette.secondary.main,
margin: theme.spacing(0.25, 0, 0, 0.75),
fontSize: theme.fontSizes.smallerBody,
}));

View File

@ -12,6 +12,8 @@ import {
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
import { NewConstraintAccordion } from 'component/common/NewConstraintAccordion/NewConstraintAccordion';
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
import { useUiFlag } from 'hooks/useUiFlag';
export interface IConstraintAccordionListProps {
constraints: IConstraint[];
@ -83,6 +85,7 @@ export const NewConstraintAccordionList = forwardRef<
IConstraintList
>(({ constraints, setConstraints, state }, ref) => {
const { context } = useUnleashContext();
const flagOverviewRedesign = useUiFlag('flagOverviewRedesign');
const onEdit =
setConstraints &&
@ -139,6 +142,28 @@ export const NewConstraintAccordionList = forwardRef<
return null;
}
if (flagOverviewRedesign) {
return (
<StyledContainer id={constraintAccordionListId}>
<ConstraintsList>
{constraints.map((constraint, index) => (
<NewConstraintAccordion
key={constraint[constraintId]}
constraint={constraint}
onEdit={onEdit?.bind(null, constraint)}
onCancel={onCancel.bind(null, index)}
onDelete={onRemove?.bind(null, index)}
onSave={onSave?.bind(null, index)}
onAutoSave={onAutoSave?.(constraint[constraintId])}
editing={Boolean(state.get(constraint)?.editing)}
compact
/>
))}
</ConstraintsList>
</StyledContainer>
);
}
return (
<StyledContainer id={constraintAccordionListId}>
{constraints.map((constraint, index) => {

View File

@ -9,12 +9,12 @@ import {
styled,
} from '@mui/material';
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader';
import { objectId } from 'utils/objectId';
import {
ConstraintListItem,
ConstraintsList,
} from 'component/common/ConstraintsList/ConstraintsList';
import { ConstraintAccordionViewHeaderInfo } from '../NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo';
type SegmentItemProps = {
segment: Partial<ISegment>;
@ -39,7 +39,6 @@ const StyledAccordion = styled(Accordion)(() => ({
}));
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
padding: theme.spacing(0, 3),
fontSize: theme.typography.body2.fontSize,
minHeight: 'unset',
}));
@ -48,12 +47,13 @@ const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
padding: theme.spacing(0.5, 3, 2.5),
}));
const StyledLink = styled(Link)({
const StyledLink = styled(Link)(({ theme }) => ({
textDecoration: 'none',
paddingRight: theme.spacing(0.5),
'&:hover': {
textDecoration: 'underline',
},
});
}));
const StyledActionsContainer = styled('div')(({ theme }) => ({
display: 'flex',
@ -90,8 +90,13 @@ export const SegmentItem: FC<SegmentItemProps> = ({
<ConstraintListItem
key={`${objectId(constraint)}-${index}`}
>
{/* FIXME: use accordion */}
<ConstraintItemHeader {...constraint} />
<ConstraintAccordionViewHeaderInfo
constraint={constraint}
expanded={isOpen}
allowExpand={(shouldExpand) =>
setIsOpen(shouldExpand)
}
/>
</ConstraintListItem>
))}
</ConstraintsList>
@ -107,7 +112,11 @@ export const SegmentItem: FC<SegmentItemProps> = ({
return (
<StyledConstraintListItem>
<StyledAccordion expanded={isOpen} disableGutters>
<StyledAccordion
expanded={isOpen}
disableGutters
TransitionProps={{ mountOnEnter: true, unmountOnExit: true }}
>
<StyledAccordionSummary id={`segment-accordion-${segment.id}`}>
<StrategyEvaluationItem type='Segment'>
<StyledLink to={`/segments/edit/${segment.id}`}>

View File

@ -28,17 +28,20 @@ export const RolloutParameter: FC<{
return (
<>
<StrategyEvaluationItem type='Rollout %'>
<StrategyEvaluationChip label={`${percentage}%`} /> of your base{' '}
{stickiness}
<span>
{hasConstraints ? 'who match constraints ' : ' '}
is included.
</span>
{displayGroupId && parameters?.groupId ? (
<StrategyEvaluationChip
label={`groupId: ${parameters?.groupId}`}
/>
) : null}
<StrategyEvaluationChip label={`${percentage}%`} />
<p>
of your base {stickiness}{' '}
<span>
{hasConstraints ? 'who match constraints ' : ' '}
is included.
</span>
{displayGroupId && parameters?.groupId ? (
<StrategyEvaluationChip
label={`groupId: ${parameters?.groupId}`}
component='span'
/>
) : null}
</p>
</StrategyEvaluationItem>
<RolloutVariants variants={variants} />
</>