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:
parent
670bb33fad
commit
f4d02e37b7
1
frontend/src/assets/icons/rollout.svg
Normal file
1
frontend/src/assets/icons/rollout.svg
Normal 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 |
@ -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',
|
||||
},
|
||||
}));
|
@ -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;
|
@ -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),
|
||||
},
|
||||
}));
|
@ -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',
|
||||
}));
|
@ -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',
|
||||
},
|
||||
}));
|
||||
|
@ -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
|
||||
|
@ -20,6 +20,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
actions: {
|
||||
marginLeft: 'auto',
|
||||
display: 'flex',
|
||||
},
|
||||
body: {
|
||||
padding: '1rem',
|
||||
|
@ -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 => {
|
||||
|
Loading…
Reference in New Issue
Block a user