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:
parent
f7c04cc2cb
commit
cf053470e5
@ -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,
|
||||
|
@ -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 & {
|
||||
|
@ -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',
|
||||
}));
|
@ -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>,
|
||||
|
@ -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} />);
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}));
|
||||
|
@ -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) => {
|
||||
|
@ -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}`}>
|
||||
|
@ -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} />
|
||||
</>
|
||||
|
Loading…
Reference in New Issue
Block a user