mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-17 13:46:47 +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';
|
} from '@mui/material';
|
||||||
import type { IConstraint } from 'interfaces/strategy';
|
import type { IConstraint } from 'interfaces/strategy';
|
||||||
import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody';
|
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 { oneOf } from 'utils/oneOf';
|
||||||
import {
|
import {
|
||||||
dateOperators,
|
dateOperators,
|
||||||
|
@ -19,6 +19,7 @@ const Operator: FC<{
|
|||||||
<Tooltip title={inverted ? `Not ${label}` : label} arrow>
|
<Tooltip title={inverted ? `Not ${label}` : label} arrow>
|
||||||
<StrategyEvaluationChip
|
<StrategyEvaluationChip
|
||||||
label={formatOperatorDescription(label, inverted)}
|
label={formatOperatorDescription(label, inverted)}
|
||||||
|
multiline
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
@ -49,10 +50,15 @@ const CaseSensitive: FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const StyledConstraintContainer = styled('div')(({ theme }) => ({
|
const StyledConstraintContainer = styled('div')(({ theme }) => ({
|
||||||
display: 'grid',
|
display: 'flex',
|
||||||
gridTemplateColumns: 'repeat(3, auto)',
|
flexDirection: 'column',
|
||||||
gap: theme.spacing(2),
|
gap: theme.spacing(1),
|
||||||
placeItems: 'center',
|
[theme.breakpoints.up('sm')]: {
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(3, auto)',
|
||||||
|
placeItems: 'center',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledOperatorGroup = styled('div')(({ theme }) => ({
|
const StyledOperatorGroup = styled('div')(({ theme }) => ({
|
||||||
@ -65,6 +71,9 @@ const StyledConstraintName = styled('div')(({ theme }) => ({
|
|||||||
maxWidth: '150px',
|
maxWidth: '150px',
|
||||||
paddingRight: theme.spacing(0.5),
|
paddingRight: theme.spacing(0.5),
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
maxWidth: 'unset',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
type ConstraintItemHeaderProps = ConstraintSchema & {
|
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 { Children, isValidElement, type FC, type ReactNode } from 'react';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
|
import { ConstraintSeparator } from './ConstraintSeparator/ConstraintSeparator';
|
||||||
|
|
||||||
const StyledList = styled('ul')(({ theme }) => ({
|
const StyledList = styled('ul')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -15,7 +16,7 @@ export const ConstraintListItem = styled('div')(({ theme }) => ({
|
|||||||
border: `1px solid ${theme.palette.divider}`,
|
border: `1px solid ${theme.palette.divider}`,
|
||||||
borderRadius: theme.shape.borderRadiusMedium,
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
padding: theme.spacing(1.5, 3),
|
padding: theme.spacing(1.5, 2),
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexFlow: 'column',
|
flexFlow: 'column',
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(1),
|
||||||
@ -25,20 +26,6 @@ const StyledListItem = styled('li')({
|
|||||||
position: 'relative',
|
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 }) => {
|
export const ConstraintsList: FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
const result: ReactNode[] = [];
|
const result: ReactNode[] = [];
|
||||||
Children.forEach(children, (child, index) => {
|
Children.forEach(children, (child, index) => {
|
||||||
@ -46,9 +33,7 @@ export const ConstraintsList: FC<{ children: ReactNode }> = ({ children }) => {
|
|||||||
result.push(
|
result.push(
|
||||||
<StyledListItem key={index}>
|
<StyledListItem key={index}>
|
||||||
{index > 0 ? (
|
{index > 0 ? (
|
||||||
<StyledAnd role='separator' key={`${index}-divider`}>
|
<ConstraintSeparator key={`${index}-divider`} />
|
||||||
AND
|
|
||||||
</StyledAnd>
|
|
||||||
) : null}
|
) : null}
|
||||||
{child}
|
{child}
|
||||||
</StyledListItem>,
|
</StyledListItem>,
|
||||||
|
@ -2,17 +2,27 @@ import { forwardRef } from 'react';
|
|||||||
import type { ChipProps } from '@mui/material';
|
import type { ChipProps } from '@mui/material';
|
||||||
import { Chip, styled } from '@mui/material';
|
import { Chip, styled } from '@mui/material';
|
||||||
|
|
||||||
const StyledChip = styled(Chip)(({ theme }) => ({
|
const StyledChip = styled(Chip)<{ multiline?: boolean }>(
|
||||||
borderRadius: `${theme.shape.borderRadius}px`,
|
({ theme, multiline }) => ({
|
||||||
padding: theme.spacing(0.25, 0),
|
borderRadius: `${theme.shape.borderRadius}px`,
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
padding: theme.spacing(0.25, 0),
|
||||||
height: 'auto',
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
background: theme.palette.secondary.light,
|
height: 'auto',
|
||||||
border: `1px solid ${theme.palette.secondary.border}`,
|
background: theme.palette.secondary.light,
|
||||||
color: theme.palette.secondary.dark,
|
border: `1px solid ${theme.palette.secondary.border}`,
|
||||||
fontWeight: theme.typography.fontWeightBold,
|
color: theme.palette.secondary.dark,
|
||||||
}));
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
|
...(multiline
|
||||||
export const StrategyEvaluationChip = forwardRef<HTMLDivElement, ChipProps>(
|
? {
|
||||||
(props, ref) => <StyledChip size='small' ref={ref} {...props} />,
|
'.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',
|
alignItems: 'center',
|
||||||
fontSize: theme.typography.body2.fontSize,
|
fontSize: theme.typography.body2.fontSize,
|
||||||
minHeight: theme.spacing(4),
|
minHeight: theme.spacing(4),
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledContent = styled('div')(({ theme }) => ({
|
const StyledContent = styled('div')(({ theme }) => ({
|
||||||
@ -23,6 +26,9 @@ const StyledContent = styled('div')(({ theme }) => ({
|
|||||||
filter: 'grayscale(1)',
|
filter: 'grayscale(1)',
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
},
|
},
|
||||||
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledType = styled('span')(({ theme }) => ({
|
const StyledType = styled('span')(({ theme }) => ({
|
||||||
@ -32,7 +38,11 @@ const StyledType = styled('span')(({ theme }) => ({
|
|||||||
fontWeight: theme.typography.fontWeightBold,
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
width: theme.spacing(10),
|
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
|
* 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>;
|
tooltips?: Record<string, string | undefined>;
|
||||||
} & Pick<TruncatorProps, 'onSetTruncated'>;
|
} & Pick<TruncatorProps, 'onSetTruncated'>;
|
||||||
|
|
||||||
const StyledValuesContainer = styled('div')({
|
const StyledValuesContainer = styled('div')(({ theme }) => ({
|
||||||
flex: '1 1 0',
|
flex: '1 1 0',
|
||||||
});
|
[theme.breakpoints.down('sm')]: {
|
||||||
|
display: 'block',
|
||||||
|
float: 'left',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const StyledTruncator = styled(Truncator)({
|
const StyledTruncator = styled(Truncator)({
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
@ -13,17 +13,15 @@ const StyledHeaderWrapper = styled('div')(({ theme }) => ({
|
|||||||
const StyledHeaderMetaInfo = styled('div')(({ theme }) => ({
|
const StyledHeaderMetaInfo = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'stretch',
|
alignItems: 'stretch',
|
||||||
marginLeft: theme.spacing(1),
|
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
marginLeft: 0,
|
marginLeft: 0,
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledExpandItem = styled('div')(({ theme }) => ({
|
const StyledExpandItem = styled('div')(({ theme }) => ({
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.secondary.main,
|
||||||
margin: theme.spacing(0.25, 0, 0, 0.75),
|
margin: theme.spacing(0.25, 0, 0, 0.75),
|
||||||
fontSize: theme.fontSizes.smallerBody,
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
}));
|
}));
|
||||||
|
@ -12,6 +12,8 @@ import {
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
||||||
import { NewConstraintAccordion } from 'component/common/NewConstraintAccordion/NewConstraintAccordion';
|
import { NewConstraintAccordion } from 'component/common/NewConstraintAccordion/NewConstraintAccordion';
|
||||||
|
import { ConstraintsList } from 'component/common/ConstraintsList/ConstraintsList';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
|
||||||
export interface IConstraintAccordionListProps {
|
export interface IConstraintAccordionListProps {
|
||||||
constraints: IConstraint[];
|
constraints: IConstraint[];
|
||||||
@ -83,6 +85,7 @@ export const NewConstraintAccordionList = forwardRef<
|
|||||||
IConstraintList
|
IConstraintList
|
||||||
>(({ constraints, setConstraints, state }, ref) => {
|
>(({ constraints, setConstraints, state }, ref) => {
|
||||||
const { context } = useUnleashContext();
|
const { context } = useUnleashContext();
|
||||||
|
const flagOverviewRedesign = useUiFlag('flagOverviewRedesign');
|
||||||
|
|
||||||
const onEdit =
|
const onEdit =
|
||||||
setConstraints &&
|
setConstraints &&
|
||||||
@ -139,6 +142,28 @@ export const NewConstraintAccordionList = forwardRef<
|
|||||||
return null;
|
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 (
|
return (
|
||||||
<StyledContainer id={constraintAccordionListId}>
|
<StyledContainer id={constraintAccordionListId}>
|
||||||
{constraints.map((constraint, index) => {
|
{constraints.map((constraint, index) => {
|
||||||
|
@ -9,12 +9,12 @@ import {
|
|||||||
styled,
|
styled,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
|
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
|
||||||
import { ConstraintItemHeader } from 'component/common/ConstraintsList/ConstraintItemHeader/ConstraintItemHeader';
|
|
||||||
import { objectId } from 'utils/objectId';
|
import { objectId } from 'utils/objectId';
|
||||||
import {
|
import {
|
||||||
ConstraintListItem,
|
ConstraintListItem,
|
||||||
ConstraintsList,
|
ConstraintsList,
|
||||||
} from 'component/common/ConstraintsList/ConstraintsList';
|
} from 'component/common/ConstraintsList/ConstraintsList';
|
||||||
|
import { ConstraintAccordionViewHeaderInfo } from '../NewConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo';
|
||||||
|
|
||||||
type SegmentItemProps = {
|
type SegmentItemProps = {
|
||||||
segment: Partial<ISegment>;
|
segment: Partial<ISegment>;
|
||||||
@ -39,7 +39,6 @@ const StyledAccordion = styled(Accordion)(() => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
|
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
|
||||||
padding: theme.spacing(0, 3),
|
|
||||||
fontSize: theme.typography.body2.fontSize,
|
fontSize: theme.typography.body2.fontSize,
|
||||||
minHeight: 'unset',
|
minHeight: 'unset',
|
||||||
}));
|
}));
|
||||||
@ -48,12 +47,13 @@ const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
|
|||||||
padding: theme.spacing(0.5, 3, 2.5),
|
padding: theme.spacing(0.5, 3, 2.5),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledLink = styled(Link)({
|
const StyledLink = styled(Link)(({ theme }) => ({
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
|
paddingRight: theme.spacing(0.5),
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
|
|
||||||
const StyledActionsContainer = styled('div')(({ theme }) => ({
|
const StyledActionsContainer = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -90,8 +90,13 @@ export const SegmentItem: FC<SegmentItemProps> = ({
|
|||||||
<ConstraintListItem
|
<ConstraintListItem
|
||||||
key={`${objectId(constraint)}-${index}`}
|
key={`${objectId(constraint)}-${index}`}
|
||||||
>
|
>
|
||||||
{/* FIXME: use accordion */}
|
<ConstraintAccordionViewHeaderInfo
|
||||||
<ConstraintItemHeader {...constraint} />
|
constraint={constraint}
|
||||||
|
expanded={isOpen}
|
||||||
|
allowExpand={(shouldExpand) =>
|
||||||
|
setIsOpen(shouldExpand)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</ConstraintListItem>
|
</ConstraintListItem>
|
||||||
))}
|
))}
|
||||||
</ConstraintsList>
|
</ConstraintsList>
|
||||||
@ -107,7 +112,11 @@ export const SegmentItem: FC<SegmentItemProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledConstraintListItem>
|
<StyledConstraintListItem>
|
||||||
<StyledAccordion expanded={isOpen} disableGutters>
|
<StyledAccordion
|
||||||
|
expanded={isOpen}
|
||||||
|
disableGutters
|
||||||
|
TransitionProps={{ mountOnEnter: true, unmountOnExit: true }}
|
||||||
|
>
|
||||||
<StyledAccordionSummary id={`segment-accordion-${segment.id}`}>
|
<StyledAccordionSummary id={`segment-accordion-${segment.id}`}>
|
||||||
<StrategyEvaluationItem type='Segment'>
|
<StrategyEvaluationItem type='Segment'>
|
||||||
<StyledLink to={`/segments/edit/${segment.id}`}>
|
<StyledLink to={`/segments/edit/${segment.id}`}>
|
||||||
|
@ -28,17 +28,20 @@ export const RolloutParameter: FC<{
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StrategyEvaluationItem type='Rollout %'>
|
<StrategyEvaluationItem type='Rollout %'>
|
||||||
<StrategyEvaluationChip label={`${percentage}%`} /> of your base{' '}
|
<StrategyEvaluationChip label={`${percentage}%`} />
|
||||||
{stickiness}
|
<p>
|
||||||
<span>
|
of your base {stickiness}{' '}
|
||||||
{hasConstraints ? 'who match constraints ' : ' '}
|
<span>
|
||||||
is included.
|
{hasConstraints ? 'who match constraints ' : ' '}
|
||||||
</span>
|
is included.
|
||||||
{displayGroupId && parameters?.groupId ? (
|
</span>
|
||||||
<StrategyEvaluationChip
|
{displayGroupId && parameters?.groupId ? (
|
||||||
label={`groupId: ${parameters?.groupId}`}
|
<StrategyEvaluationChip
|
||||||
/>
|
label={`groupId: ${parameters?.groupId}`}
|
||||||
) : null}
|
component='span'
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</p>
|
||||||
</StrategyEvaluationItem>
|
</StrategyEvaluationItem>
|
||||||
<RolloutVariants variants={variants} />
|
<RolloutVariants variants={variants} />
|
||||||
</>
|
</>
|
||||||
|
Loading…
Reference in New Issue
Block a user