1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-03 01:18:43 +02:00

Update UI for strategies - segmentation and for mobile devices (#1189)

* fix: strategies rwd ui updates

* rwd updates to strategies

* add item numbers to strategies

* update strategy segmentation styles
This commit is contained in:
Tymoteusz Czech 2022-08-04 10:13:07 +02:00 committed by GitHub
parent 367d8a6a5a
commit 0b93776db6
20 changed files with 129 additions and 67 deletions

View File

@ -8,8 +8,7 @@ export const useStyles = makeStyles()(theme => ({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
marginRight: theme.spacing(1), marginRight: theme.spacing(1),
[theme.breakpoints.down(650)]: { [theme.breakpoints.down(710)]: {
marginBottom: '1rem',
marginRight: 0, marginRight: 0,
}, },
}, },
@ -17,8 +16,8 @@ export const useStyles = makeStyles()(theme => ({
fill: '#fff', fill: '#fff',
}, },
accordion: { accordion: {
border: `1px solid ${theme.palette.grey[400]}`, border: `1px solid ${theme.palette.dividerAlternative}`,
borderRadius: '8px', borderRadius: theme.shape.borderRadiusMedium,
backgroundColor: '#fff', backgroundColor: '#fff',
boxShadow: 'none', boxShadow: 'none',
margin: 0, margin: 0,
@ -27,6 +26,9 @@ export const useStyles = makeStyles()(theme => ({
'&:before': { '&:before': {
opacity: '0 !important', opacity: '0 !important',
}, },
'&:first-of-type, &:last-of-type': {
borderRadius: theme.shape.borderRadiusMedium,
},
}, },
accordionEdit: { accordionEdit: {
backgroundColor: '#F6F6FA', backgroundColor: '#F6F6FA',
@ -34,7 +36,10 @@ export const useStyles = makeStyles()(theme => ({
headerMetaInfo: { headerMetaInfo: {
display: 'flex', display: 'flex',
alignItems: 'stretch', alignItems: 'stretch',
[theme.breakpoints.down(710)]: { flexDirection: 'column' }, [theme.breakpoints.down(710)]: {
flexDirection: 'column',
alignItems: 'center',
},
}, },
headerContainer: { headerContainer: {
display: 'flex', display: 'flex',
@ -76,6 +81,9 @@ export const useStyles = makeStyles()(theme => ({
minWidth: '152px', minWidth: '152px',
paddingRight: '0.5rem', paddingRight: '0.5rem',
}, },
[theme.breakpoints.down(710)]: {
paddingRight: 0,
},
}, },
editingBadge: { editingBadge: {
borderRadius: theme.shape.borderRadiusExtraLarge, borderRadius: theme.shape.borderRadiusExtraLarge,

View File

@ -1,18 +1,12 @@
import { styled } from '@mui/material'; import { Box, styled, useTheme } from '@mui/material';
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
interface IStrategySeparatorProps { interface IStrategySeparatorProps {
text: 'AND' | 'OR'; text: 'AND' | 'OR';
} }
const StyledContainer = styled('div')(({ theme }) => ({
height: theme.spacing(1),
position: 'relative',
width: '100%',
}));
const StyledContent = styled('div')(({ theme }) => ({ const StyledContent = styled('div')(({ theme }) => ({
padding: theme.spacing(0.75, 1.5), padding: theme.spacing(0.75, 1),
color: theme.palette.text.primary, color: theme.palette.text.primary,
fontSize: theme.fontSizes.smallerBody, fontSize: theme.fontSizes.smallerBody,
backgroundColor: theme.palette.secondaryContainer, backgroundColor: theme.palette.secondaryContainer,
@ -20,20 +14,31 @@ const StyledContent = styled('div')(({ theme }) => ({
position: 'absolute', position: 'absolute',
zIndex: theme.zIndex.fab, zIndex: theme.zIndex.fab,
top: '50%', top: '50%',
left: theme.spacing(3), left: theme.spacing(2),
transform: 'translateY(-50%)', transform: 'translateY(-50%)',
lineHeight: 1,
})); }));
const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({ const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({
top: '50%', top: '50%',
left: '50%', left: '50%',
transform: 'translate(-50%, -50%)', transform: 'translate(-50%, -50%)',
backgroundColor: theme.palette.secondary.light, backgroundColor: theme.palette.activityIndicators.primary,
border: `1px solid ${theme.palette.primary.border}`, border: `1px solid ${theme.palette.primary.border}`,
borderRadius: theme.shape.borderRadiusLarge,
})); }));
export const StrategySeparator = ({ text }: IStrategySeparatorProps) => ( export const StrategySeparator = ({ text }: IStrategySeparatorProps) => {
<StyledContainer> const theme = useTheme();
return (
<Box
sx={{
height: theme.spacing(text === 'AND' ? 1 : 1.5),
position: 'relative',
width: '100%',
}}
>
<ConditionallyRender <ConditionallyRender
condition={text === 'AND'} condition={text === 'AND'}
show={() => <StyledContent>{text}</StyledContent>} show={() => <StyledContent>{text}</StyledContent>}
@ -41,5 +46,6 @@ export const StrategySeparator = ({ text }: IStrategySeparatorProps) => (
<StyledCenteredContent>{text}</StyledCenteredContent> <StyledCenteredContent>{text}</StyledCenteredContent>
)} )}
/> />
</StyledContainer> </Box>
); );
};

View File

@ -6,6 +6,7 @@ export const useStyles = makeStyles()(theme => ({
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
paddingTop: theme.spacing(2),
}, },
title: { title: {
fontSize: theme.fontSizes.bodySize, fontSize: theme.fontSizes.bodySize,

View File

@ -161,7 +161,7 @@ export const FeatureStrategyEmpty = ({
display: 'grid', display: 'grid',
width: '100%', width: '100%',
gap: 2, gap: 2,
gridTemplateColumns: '1fr 1fr', gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },
}} }}
> >
<PresetCard <PresetCard

View File

@ -40,7 +40,14 @@ export const PresetCard: FC<IPresetCardProps> = ({
{children} {children}
</Typography> </Typography>
<Box sx={{ ml: 'auto', mt: 'auto', pt: 1 }}> <Box
sx={{
ml: 'auto',
mt: 'auto',
pt: 1,
mr: { xs: 'auto', sm: 0 },
}}
>
<PermissionButton <PermissionButton
permission={CREATE_FEATURE_STRATEGY} permission={CREATE_FEATURE_STRATEGY}
projectId={projectId} projectId={projectId}

View File

@ -1,11 +1,6 @@
import { makeStyles } from 'tss-react/mui'; import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({ export const useStyles = makeStyles()(theme => ({
title: {
margin: 0,
fontSize: theme.fontSizes.bodySize,
fontWeight: theme.fontWeight.bold,
},
divider: { divider: {
border: `1px dashed ${theme.palette.divider}`, border: `1px dashed ${theme.palette.divider}`,
}, },

View File

@ -9,7 +9,7 @@ import { FeatureStrategySegmentList } from 'component/feature/FeatureStrategy/Fe
import { useStyles } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegment.styles'; import { useStyles } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegment.styles';
import { SegmentDocsStrategyWarning } from 'component/segments/SegmentDocs/SegmentDocs'; import { SegmentDocsStrategyWarning } from 'component/segments/SegmentDocs/SegmentDocs';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits'; import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
import { Divider } from '@mui/material'; import { Divider, Typography } from '@mui/material';
interface IFeatureStrategySegmentProps { interface IFeatureStrategySegmentProps {
segments: ISegment[]; segments: ISegment[];
@ -53,7 +53,9 @@ export const FeatureStrategySegment = ({
return ( return (
<> <>
<h3 className={styles.title}>Segmentation</h3> <Typography component="h3" sx={{ m: 0 }} variant="h3">
Segmentation
</Typography>
{atStrategySegmentsLimit && <SegmentDocsStrategyWarning />} {atStrategySegmentsLimit && <SegmentDocsStrategyWarning />}
<p>Add a predefined segment to constrain this feature toggle:</p> <p>Add a predefined segment to constrain this feature toggle:</p>
<AutocompleteBox <AutocompleteBox

View File

@ -16,11 +16,13 @@ export const useStyles = makeStyles()(theme => ({
fontSize: theme.fontSizes.smallerBody, fontSize: theme.fontSizes.smallerBody,
border: '1px solid', border: '1px solid',
borderColor: theme.palette.grey[300], borderColor: theme.palette.grey[300],
paddingInline: '0.4rem', padding: theme.spacing(0.75, 1),
marginBlock: '0.2rem', display: 'block',
display: 'grid', marginTop: 'auto',
marginBottom: 'auto',
alignItems: 'center', alignItems: 'center',
borderRadius: theme.shape.borderRadius, borderRadius: theme.shape.borderRadius,
lineHeight: 1,
}, },
selectedSegmentsLabel: { selectedSegmentsLabel: {
color: theme.palette.text.secondary, color: theme.palette.text.secondary,

View File

@ -1,3 +1,4 @@
import { Box, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
import { MoveListItem, useDragItem } from 'hooks/useDragItem'; import { MoveListItem, useDragItem } from 'hooks/useDragItem';
@ -13,6 +14,18 @@ interface IStrategyDraggableItemProps {
onDragAndDrop: MoveListItem; onDragAndDrop: MoveListItem;
} }
const StyledIndexLabel = styled('div')(({ theme }) => ({
fontSize: theme.typography.fontSize,
color: theme.palette.text.secondary,
position: 'absolute',
display: 'none',
right: 'calc(100% + 6px)',
top: theme.spacing(2.5),
[theme.breakpoints.up('md')]: {
display: 'block',
},
}));
export const StrategyDraggableItem = ({ export const StrategyDraggableItem = ({
strategy, strategy,
index, index,
@ -23,17 +36,20 @@ export const StrategyDraggableItem = ({
const ref = useDragItem(index, onDragAndDrop); const ref = useDragItem(index, onDragAndDrop);
return ( return (
<div key={strategy.id} ref={ref}> <Box key={strategy.id} ref={ref}>
<ConditionallyRender <ConditionallyRender
condition={index > 0} condition={index > 0}
show={<StrategySeparator text="OR" />} show={<StrategySeparator text="OR" />}
/> />
<Box sx={{ position: 'relative' }}>
<StyledIndexLabel>{index + 1}</StyledIndexLabel>
<StrategyItem <StrategyItem
strategy={strategy} strategy={strategy}
environmentId={environmentName} environmentId={environmentName}
otherEnvironments={otherEnvironments} otherEnvironments={otherEnvironments}
isDraggable isDraggable
/> />
</div> </Box>
</Box>
); );
}; };

View File

@ -4,7 +4,7 @@ export const useStyles = makeStyles()(theme => ({
container: { container: {
width: '100%', width: '100%',
padding: theme.spacing(2, 3), padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadius, borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.divider}`, border: `1px solid ${theme.palette.divider}`,
}, },
chip: { chip: {

View File

@ -4,7 +4,7 @@ export const useStyles = makeStyles()(theme => ({
valueContainer: { valueContainer: {
padding: theme.spacing(2, 3), padding: theme.spacing(2, 3),
border: `1px solid ${theme.palette.dividerAlternative}`, border: `1px solid ${theme.palette.dividerAlternative}`,
borderRadius: theme.shape.borderRadius, borderRadius: theme.shape.borderRadiusMedium,
}, },
valueSeparator: { valueSeparator: {
color: theme.palette.grey[700], color: theme.palette.grey[700],

View File

@ -17,6 +17,9 @@ export const useStyles = makeStyles()(theme => ({
borderBottom: `1px solid ${theme.palette.divider}`, borderBottom: `1px solid ${theme.palette.divider}`,
fontWeight: theme.typography.fontWeightMedium, fontWeight: theme.typography.fontWeightMedium,
}, },
headerDraggable: {
paddingLeft: theme.spacing(1),
},
icon: { icon: {
fill: theme.palette.inactiveIcon, fill: theme.palette.inactiveIcon,
}, },

View File

@ -1,6 +1,7 @@
import { DragIndicator, Edit } from '@mui/icons-material'; import { DragIndicator, Edit } from '@mui/icons-material';
import { styled, useTheme, IconButton } from '@mui/material'; import { styled, useTheme, IconButton } from '@mui/material';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import classNames from 'classnames';
import { IFeatureEnvironment } from 'interfaces/featureToggle'; import { IFeatureEnvironment } from 'interfaces/featureToggle';
import { IFeatureStrategy } from 'interfaces/strategy'; import { IFeatureStrategy } from 'interfaces/strategy';
import { import {
@ -52,7 +53,11 @@ export const StrategyItem = ({
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.header}> <div
className={classNames(styles.header, {
[styles.headerDraggable]: isDraggable,
})}
>
<ConditionallyRender <ConditionallyRender
condition={Boolean(isDraggable)} condition={Boolean(isDraggable)}
show={() => ( show={() => (
@ -60,6 +65,7 @@ export const StrategyItem = ({
<DragIndicator <DragIndicator
titleAccess="Drag to reorder" titleAccess="Drag to reorder"
cursor="grab" cursor="grab"
sx={{ color: 'neutral.main' }}
/> />
</DragIcon> </DragIcon>
)} )}

View File

@ -28,9 +28,13 @@ export const useStyles = makeStyles()(theme => ({
borderBottomLeftRadius: theme.shape.borderRadiusLarge, borderBottomLeftRadius: theme.shape.borderRadiusLarge,
borderBottomRightRadius: theme.shape.borderRadiusLarge, borderBottomRightRadius: theme.shape.borderRadiusLarge,
borderBottom: `4px solid ${theme.palette.primary.light}`, borderBottom: `4px solid ${theme.palette.primary.light}`,
[theme.breakpoints.down('md')]: {
padding: theme.spacing(2, 1),
},
}, },
accordionDetailsDisabled: { accordionDetailsDisabled: {
borderBottom: `4px solid ${theme.palette.dividerAlternative}`, borderBottom: `4px solid ${theme.palette.neutral.border}`,
}, },
accordionBody: { accordionBody: {
width: '100%', width: '100%',

View File

@ -85,6 +85,7 @@ const FeatureOverviewEnvironment = ({
maxWidth="100" maxWidth="100"
maxLength={15} maxLength={15}
/> />
</div>
<ConditionallyRender <ConditionallyRender
condition={!env.enabled} condition={!env.enabled}
show={ show={
@ -97,7 +98,6 @@ const FeatureOverviewEnvironment = ({
} }
/> />
</div> </div>
</div>
<div className={styles.container}> <div className={styles.container}>
<FeatureStrategyMenu <FeatureStrategyMenu
label="Add strategy" label="Add strategy"

View File

@ -14,7 +14,7 @@ const SeparatorContainer = styled('div')(({ theme }) => ({
transform: 'translateY(-50%)', transform: 'translateY(-50%)',
height: 2, height: 2,
width: '100%', width: '100%',
backgroundColor: theme.palette.divider, backgroundColor: theme.palette.dividerAlternative,
}, },
})); }));
@ -25,7 +25,7 @@ const SeparatorContent = styled('span')(({ theme }) => ({
background: theme.palette.secondaryContainer, background: theme.palette.secondaryContainer,
position: 'relative', position: 'relative',
maxWidth: '80%', maxWidth: '80%',
color: theme.palette.text.secondary, color: theme.palette.text.primary,
})); }));
export const SectionSeparator: FC = ({ children }) => ( export const SectionSeparator: FC = ({ children }) => (

View File

@ -3,6 +3,8 @@ import { Link } from 'react-router-dom';
import { DonutLarge } from '@mui/icons-material'; import { DonutLarge } from '@mui/icons-material';
import { useStyles } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.styles'; import { useStyles } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.styles';
import { useSegments } from 'hooks/api/getters/useSegments/useSegments'; import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
interface IFeatureOverviewSegmentProps { interface IFeatureOverviewSegmentProps {
strategyId: string; strategyId: string;
@ -20,8 +22,12 @@ export const FeatureOverviewSegment = ({
return ( return (
<> <>
{segments.map(segment => ( {segments.map((segment, index) => (
<Fragment key={segment.id}> <Fragment key={segment.id}>
<ConditionallyRender
condition={index > 0}
show={<StrategySeparator text="AND" />}
/>
<div className={styles.container}> <div className={styles.container}>
<DonutLarge color="secondary" sx={{ mr: 1 }} /> Segment:{' '} <DonutLarge color="secondary" sx={{ mr: 1 }} /> Segment:{' '}
<Link <Link

View File

@ -81,9 +81,9 @@ const RolloutSlider = ({
<div className={classes.slider}> <div className={classes.slider}>
<Typography <Typography
id="discrete-slider-always" id="discrete-slider-always"
variant="subtitle2" variant="h3"
gutterBottom gutterBottom
component="h2" component="h3"
> >
{name} {name}
</Typography> </Typography>

View File

@ -26,6 +26,10 @@ export default createTheme({
fontSize: '1.5rem', fontSize: '1.5rem',
lineHeight: 1.875, lineHeight: 1.875,
}, },
h3: {
fontSize: '1rem',
fontWeight: '700',
},
caption: { caption: {
fontSize: `${12 / 16}rem`, fontSize: `${12 / 16}rem`,
}, },
@ -128,6 +132,7 @@ export default createTheme({
recent: colors.green[100], recent: colors.green[100],
inactive: colors.orange[200], inactive: colors.orange[200],
abandoned: colors.red[200], abandoned: colors.red[200],
primary: colors.purple[100],
}, },
inactiveIcon: colors.grey[600], inactiveIcon: colors.grey[600],
}, },

View File

@ -46,13 +46,14 @@ declare module '@mui/material/styles' {
background: string; background: string;
}; };
/** /**
* For 'Seen' column on feature toggles list. * For 'Seen' column on feature toggles list and other.
*/ */
activityIndicators: { activityIndicators: {
unknown: string; unknown: string;
recent: string; recent: string;
inactive: string; inactive: string;
abandoned: string; abandoned: string;
primary: string;
}; };
dividerAlternative: string; dividerAlternative: string;
/** /**