mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01: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