1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

refactor: rewrite feature strategy icons list (#1039)

* refactor: fix strategy action icon layout

* refactor: use a custom SVG for the rollout strategy icon

* refactor: rewrite feature strategy icons list
This commit is contained in:
olav 2022-05-31 10:50:24 +02:00 committed by GitHub
parent 670bb33fad
commit f4d02e37b7
9 changed files with 99 additions and 163 deletions

View File

@ -0,0 +1 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M16.584 10H22a2 2 0 1 1 0 4h-5.416a5.001 5.001 0 0 1-9.168 0H2a2 2 0 1 1 0-4h5.416a5.001 5.001 0 0 1 9.168 0Z" /></svg>

After

Width:  |  Height:  |  Size: 272 B

View File

@ -1,27 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
display: 'flex',
alignItems: 'center',
position: 'relative',
width: '50px',
height: '100%',
padding: '15px 0px',
},
vertical: {
borderRadius: '1px',
height: '50px',
width: '50px',
},
circle: {
width: '15px',
height: '15px',
},
pos: {
position: 'absolute',
right: 0,
left: 0,
margin: '0 auto',
},
}));

View File

@ -1,24 +0,0 @@
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import Remove from '@mui/icons-material/Remove';
import { useStyles } from './RolloutIcon.styles';
import classnames from 'classnames';
interface IRolloutIconProps {
className?: string;
}
const RolloutIcon = ({ className }: IRolloutIconProps) => {
const { classes: styles } = useStyles();
return (
<div className={styles.container}>
<Remove
className={classnames(styles.vertical, styles.pos, className)}
/>
<FiberManualRecordIcon
className={classnames(styles.circle, styles.pos, className)}
/>
</div>
);
};
export default RolloutIcon;

View File

@ -0,0 +1,39 @@
import {
getFeatureStrategyIcon,
formatStrategyName,
} from 'utils/strategyNames';
import { styled, Tooltip } from '@mui/material';
import { useId } from 'hooks/useId';
interface IFeatureStrategyIconProps {
strategyName: string;
}
export const FeatureStrategyIcon = ({
strategyName,
}: IFeatureStrategyIconProps) => {
const Icon = getFeatureStrategyIcon(strategyName);
const id = useId();
return (
<StyledIcon>
<Tooltip title={formatStrategyName(strategyName)} arrow>
<div id={id} role="tooltip">
<Icon aria-labelledby={id} />
</div>
</Tooltip>
</StyledIcon>
);
};
const StyledIcon = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: theme.palette.inactiveIcon,
'& svg': {
width: theme.spacing(2.5),
height: theme.spacing(2.5),
},
}));

View File

@ -0,0 +1,45 @@
import { IFeatureStrategy } from 'interfaces/strategy';
import { FeatureStrategyIcon } from 'component/feature/FeatureStrategy/FeatureStrategyIcon/FeatureStrategyIcon';
import { styled } from '@mui/material';
interface IFeatureStrategyIconsProps {
strategies: IFeatureStrategy[] | undefined;
}
export const FeatureStrategyIcons = ({
strategies,
}: IFeatureStrategyIconsProps) => {
if (!strategies?.length) {
return null;
}
const strategyNames = strategies.map(strategy => strategy.name);
const uniqueStrategyNames = uniqueValues(strategyNames);
return (
<StyledList aria-label="Feature strategies">
{uniqueStrategyNames.map(strategyName => (
<StyledListItem key={strategyName}>
<FeatureStrategyIcon strategyName={strategyName} />
</StyledListItem>
))}
</StyledList>
);
};
const uniqueValues = <T,>(values: T[]): T[] => {
return [...new Set(values)];
};
const StyledList = styled('ul')(() => ({
all: 'unset',
display: 'flex',
alignItems: 'center',
alignContent: 'center',
}));
const StyledListItem = styled('li')(() => ({
all: 'unset',
minWidth: 30,
textAlign: 'center',
}));

View File

@ -94,30 +94,11 @@ export const useStyles = makeStyles()(theme => ({
justifyContent: 'flex-end',
marginBottom: '1rem',
},
strategyIconContainer: {
minWidth: '40px',
marginRight: '5px',
display: 'flex',
justifyContent: 'center',
},
strategiesIconsContainer: {
transform: 'scale(0.8)',
display: 'flex',
alignItems: 'center',
position: 'relative',
[theme.breakpoints.down(560)]: {
marginLeft: '0px',
top: '5px',
},
},
truncator: {
[theme.breakpoints.down(560)]: {
textAlign: 'center',
},
},
strategyIcon: {
fill: theme.palette.inactiveIcon,
},
container: {
display: 'flex',
alignItems: 'center',
@ -127,7 +108,4 @@ export const useStyles = makeStyles()(theme => ({
marginLeft: '0',
},
},
strategyMenu: {
marginRight: '-.5rem',
},
}));

View File

@ -1,19 +1,10 @@
import {
Accordion,
AccordionDetails,
AccordionSummary,
Tooltip,
} from '@mui/material';
import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material';
import { ExpandMore } from '@mui/icons-material';
import React from 'react';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import useFeatureMetrics from 'hooks/api/getters/useFeatureMetrics/useFeatureMetrics';
import { IFeatureEnvironment } from 'interfaces/featureToggle';
import { getFeatureMetrics } from 'utils/getFeatureMetrics';
import {
getFeatureStrategyIcon,
formatStrategyName,
} from 'utils/strategyNames';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon';
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
@ -25,12 +16,7 @@ import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureSt
import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { StatusBadge } from 'component/common/StatusBadge/StatusBadge';
interface IStrategyIconObject {
count: number;
Icon: React.ReactElement;
name: string;
}
import { FeatureStrategyIcons } from 'component/feature/FeatureStrategy/FeatureStrategyIcons/FeatureStrategyIcons';
interface IFeatureOverviewEnvironmentProps {
env: IFeatureEnvironment;
@ -55,36 +41,11 @@ const FeatureOverviewEnvironment = ({
const getOverviewText = () => {
if (env.enabled) {
return `${environmentMetric?.yes} received this feature
because the following strategies are executing`;
return `${environmentMetric?.yes} received this feature because the following strategies are executing`;
}
return `This environment is disabled, which means that none of your strategies are executing`;
};
const getStrategyIcons = () => {
const strategyObjects = featureEnvironment?.strategies.reduce(
(acc, current) => {
if (acc[current.name]) {
acc[current.name].count = acc[current.name].count + 1;
} else {
acc[current.name] = {
count: 1,
// @ts-expect-error
Icon: getFeatureStrategyIcon(current.name),
};
}
return acc;
},
{} as { [key: string]: IStrategyIconObject }
);
if (!strategyObjects) return [];
return Object.keys(strategyObjects).map(strategyName => {
return { ...strategyObjects[strategyName], name: strategyName };
});
};
return (
<div className={styles.featureOverviewEnvironment}>
<Accordion
@ -112,53 +73,15 @@ const FeatureOverviewEnvironment = ({
</p>
</div>
<div className={styles.container}>
<div className={styles.strategyMenu}>
<FeatureStrategyMenu
label="Add strategy"
projectId={projectId}
featureId={featureId}
environmentId={env.name}
variant="text"
/>
</div>
<ConditionallyRender
condition={
featureEnvironment?.strategies.length !== 0
}
show={
<>
<div
className={
styles.strategiesIconsContainer
}
>
{getStrategyIcons()?.map(
({ name, Icon }) => (
<Tooltip
title={formatStrategyName(
name
)}
arrow
key={name}
>
<div
className={
styles.strategyIconContainer
}
>
{/* @ts-expect-error */}
<Icon
className={
styles.strategyIcon
}
/>
</div>
</Tooltip>
)
)}
</div>{' '}
</>
}
<FeatureStrategyMenu
label="Add strategy"
projectId={projectId}
featureId={featureId}
environmentId={env.name}
variant="text"
/>
<FeatureStrategyIcons
strategies={featureEnvironment?.strategies}
/>
</div>
<ConditionallyRender

View File

@ -20,6 +20,7 @@ export const useStyles = makeStyles()(theme => ({
},
actions: {
marginLeft: 'auto',
display: 'flex',
},
body: {
padding: '1rem',

View File

@ -2,7 +2,7 @@ import LocationOnIcon from '@mui/icons-material/LocationOn';
import PeopleIcon from '@mui/icons-material/People';
import LanguageIcon from '@mui/icons-material/Language';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
import RolloutIcon from 'component/common/RolloutIcon/RolloutIcon';
import { ReactComponent as RolloutIcon } from 'assets/icons/rollout.svg';
import { ElementType } from 'react';
export const formatStrategyName = (strategyName: string): string => {