mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-14 00:19:16 +01:00
feat: responsive strategy icons (#4121)
https://linear.app/unleash/issue/2-1167/multiple-strategies-breaking-the-environment-card https://linear.app/unleash/issue/2-1179/buttons-have-an-extra-space-if-the-icon-its-not-visible This fixes the broken UI when we have too many strategies. Before: <img width="1500" alt="image" src="https://github.com/Unleash/unleash/assets/14320932/ddf2f636-965c-4527-b879-dba5c16d9630"> After: <img width="1303" alt="image" src="https://github.com/Unleash/unleash/assets/14320932/852c20c9-c5f4-4aa5-b8c0-e5bc5286c572"> We also added the new strategy type to the tooltips: <img width="519" alt="image" src="https://github.com/Unleash/unleash/assets/14320932/117ee00f-f2a7-4ecb-8596-44486a2870a2"> <img width="422" alt="image" src="https://github.com/Unleash/unleash/assets/14320932/4281a48c-4b6e-4100-86e2-29dfe9ce4cec"> This also fixes an extra margin we caught on our `PermissionButton` when it had no endIcon set. Co-authored by: @daveleek --------- Co-authored-by: David Leek <david@getunleash.io>
This commit is contained in:
parent
b6f405d1af
commit
73b4ae18c1
@ -1,7 +1,6 @@
|
|||||||
import { Button, ButtonProps } from '@mui/material';
|
import { Button, ButtonProps } from '@mui/material';
|
||||||
import { Lock } from '@mui/icons-material';
|
import { Lock } from '@mui/icons-material';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import {
|
import {
|
||||||
TooltipResolver,
|
TooltipResolver,
|
||||||
ITooltipResolverProps,
|
ITooltipResolverProps,
|
||||||
@ -32,6 +31,22 @@ export interface IProjectPermissionButtonProps extends IPermissionButtonProps {
|
|||||||
environmentId: string;
|
environmentId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getEndIcon = (
|
||||||
|
access: boolean,
|
||||||
|
fallBackIcon?: React.ReactNode,
|
||||||
|
hideLockIcon?: boolean
|
||||||
|
): React.ReactNode => {
|
||||||
|
if (!access && !hideLockIcon) {
|
||||||
|
return <Lock titleAccess="Locked" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fallBackIcon) {
|
||||||
|
return fallBackIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const ProjectEnvironmentPermissionButton: React.FC<IProjectPermissionButtonProps> =
|
const ProjectEnvironmentPermissionButton: React.FC<IProjectPermissionButtonProps> =
|
||||||
React.forwardRef((props, ref) => {
|
React.forwardRef((props, ref) => {
|
||||||
const access = useHasProjectEnvironmentAccess(
|
const access = useHasProjectEnvironmentAccess(
|
||||||
@ -75,6 +90,7 @@ const BasePermissionButton: React.FC<IPermissionBaseButtonProps> =
|
|||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const id = useId();
|
const id = useId();
|
||||||
|
const endIcon = getEndIcon(access, rest.endIcon, hideLockIcon);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TooltipResolver
|
<TooltipResolver
|
||||||
@ -91,18 +107,7 @@ const BasePermissionButton: React.FC<IPermissionBaseButtonProps> =
|
|||||||
variant={variant}
|
variant={variant}
|
||||||
color={color}
|
color={color}
|
||||||
{...rest}
|
{...rest}
|
||||||
endIcon={
|
endIcon={endIcon}
|
||||||
<>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={!access && !hideLockIcon}
|
|
||||||
show={<Lock titleAccess="Locked" />}
|
|
||||||
elseShow={
|
|
||||||
Boolean(rest.endIcon) &&
|
|
||||||
rest.endIcon
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -3,18 +3,25 @@ import {
|
|||||||
formatStrategyName,
|
formatStrategyName,
|
||||||
} from 'utils/strategyNames';
|
} from 'utils/strategyNames';
|
||||||
import { styled, Tooltip } from '@mui/material';
|
import { styled, Tooltip } from '@mui/material';
|
||||||
|
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||||
|
|
||||||
interface IFeatureStrategyIconProps {
|
interface IFeatureStrategyIconProps {
|
||||||
strategyName: string;
|
strategy: IFeatureStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FeatureStrategyIcon = ({
|
export const FeatureStrategyIcon = ({
|
||||||
strategyName,
|
strategy,
|
||||||
}: IFeatureStrategyIconProps) => {
|
}: IFeatureStrategyIconProps) => {
|
||||||
const Icon = getFeatureStrategyIcon(strategyName);
|
const Icon = getFeatureStrategyIcon(strategy.name);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={formatStrategyName(strategyName)} arrow>
|
<Tooltip
|
||||||
|
title={
|
||||||
|
formatStrategyName(strategy.name) +
|
||||||
|
(strategy.title ? ` - ${strategy.title}` : '')
|
||||||
|
}
|
||||||
|
arrow
|
||||||
|
>
|
||||||
<StyledIcon>
|
<StyledIcon>
|
||||||
<Icon />
|
<Icon />
|
||||||
</StyledIcon>
|
</StyledIcon>
|
||||||
|
@ -1,28 +1,8 @@
|
|||||||
import { IFeatureStrategy } from 'interfaces/strategy';
|
import { IFeatureStrategy } from 'interfaces/strategy';
|
||||||
import { FeatureStrategyIcon } from 'component/feature/FeatureStrategy/FeatureStrategyIcon/FeatureStrategyIcon';
|
import { FeatureStrategyIcon } from 'component/feature/FeatureStrategy/FeatureStrategyIcon/FeatureStrategyIcon';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
|
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
|
||||||
interface IFeatureStrategyIconsProps {
|
import { formatStrategyName } from 'utils/strategyNames';
|
||||||
strategies: IFeatureStrategy[] | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FeatureStrategyIcons = ({
|
|
||||||
strategies,
|
|
||||||
}: IFeatureStrategyIconsProps) => {
|
|
||||||
if (!strategies?.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledList aria-label="Feature strategies">
|
|
||||||
{strategies.map(strategy => (
|
|
||||||
<StyledListItem key={strategy.id}>
|
|
||||||
<FeatureStrategyIcon strategyName={strategy.name} />
|
|
||||||
</StyledListItem>
|
|
||||||
))}
|
|
||||||
</StyledList>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledList = styled('ul')(() => ({
|
const StyledList = styled('ul')(() => ({
|
||||||
all: 'unset',
|
all: 'unset',
|
||||||
@ -36,3 +16,61 @@ const StyledListItem = styled('li')(() => ({
|
|||||||
minWidth: 30,
|
minWidth: 30,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const StyledItem = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const THRESHOLD = 5;
|
||||||
|
|
||||||
|
interface IFeatureStrategyIconsProps {
|
||||||
|
strategies: IFeatureStrategy[] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FeatureStrategyIcons = ({
|
||||||
|
strategies,
|
||||||
|
}: IFeatureStrategyIconsProps) => {
|
||||||
|
if (!strategies?.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strategies.length > THRESHOLD + 1) {
|
||||||
|
return (
|
||||||
|
<StyledList aria-label="Feature strategies">
|
||||||
|
{strategies.slice(0, THRESHOLD).map(strategy => (
|
||||||
|
<StyledListItem key={strategy.id}>
|
||||||
|
<FeatureStrategyIcon strategy={strategy} />
|
||||||
|
</StyledListItem>
|
||||||
|
))}
|
||||||
|
<TooltipLink
|
||||||
|
tooltip={strategies.slice(THRESHOLD).map(strategy => (
|
||||||
|
<StyledListItem key={strategy.id}>
|
||||||
|
<StyledItem>
|
||||||
|
<FeatureStrategyIcon strategy={strategy} />{' '}
|
||||||
|
{formatStrategyName(strategy.name) +
|
||||||
|
(strategy.title
|
||||||
|
? ` - ${strategy.title}`
|
||||||
|
: '')}
|
||||||
|
</StyledItem>
|
||||||
|
</StyledListItem>
|
||||||
|
))}
|
||||||
|
>
|
||||||
|
(+{strategies.length - THRESHOLD})
|
||||||
|
</TooltipLink>
|
||||||
|
</StyledList>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledList aria-label="Feature strategies">
|
||||||
|
{strategies.map(strategy => (
|
||||||
|
<StyledListItem key={strategy.id}>
|
||||||
|
<FeatureStrategyIcon strategy={strategy} />
|
||||||
|
</StyledListItem>
|
||||||
|
))}
|
||||||
|
</StyledList>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -20,6 +20,10 @@ interface IFeatureStrategyMenuProps {
|
|||||||
size?: IPermissionButtonProps['size'];
|
size?: IPermissionButtonProps['size'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StyledStrategyMenu = styled('div')({
|
||||||
|
flexShrink: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const StyledAdditionalMenuButton = styled(PermissionButton)(({ theme }) => ({
|
const StyledAdditionalMenuButton = styled(PermissionButton)(({ theme }) => ({
|
||||||
minWidth: 0,
|
minWidth: 0,
|
||||||
width: theme.spacing(4.5),
|
width: theme.spacing(4.5),
|
||||||
@ -71,7 +75,7 @@ export const FeatureStrategyMenu = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div onClick={event => event.stopPropagation()}>
|
<StyledStrategyMenu onClick={event => event.stopPropagation()}>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
permission={CREATE_FEATURE_STRATEGY}
|
permission={CREATE_FEATURE_STRATEGY}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
@ -118,6 +122,6 @@ export const FeatureStrategyMenu = ({
|
|||||||
environmentId={environmentId}
|
environmentId={environmentId}
|
||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</StyledStrategyMenu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -123,6 +123,8 @@ const StyledButtonContainer = styled('div')(({ theme }) => ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginTop: theme.spacing(2),
|
marginTop: theme.spacing(2),
|
||||||
|
gap: theme.spacing(2),
|
||||||
|
flexWrap: 'wrap',
|
||||||
[theme.breakpoints.down(560)]: {
|
[theme.breakpoints.down(560)]: {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
},
|
},
|
||||||
@ -199,6 +201,11 @@ const FeatureOverviewEnvironment = ({
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
|
<FeatureStrategyIcons
|
||||||
|
strategies={
|
||||||
|
featureEnvironment?.strategies
|
||||||
|
}
|
||||||
|
/>
|
||||||
</StyledButtonContainer>
|
</StyledButtonContainer>
|
||||||
}
|
}
|
||||||
elseShow={
|
elseShow={
|
||||||
|
@ -97,9 +97,6 @@ exports[`renders an empty list correctly 1`] = `
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
New tag type
|
New tag type
|
||||||
<span
|
|
||||||
className="MuiButton-endIcon MuiButton-iconSizeMedium css-9tj150-MuiButton-endIcon"
|
|
||||||
/>
|
|
||||||
<span
|
<span
|
||||||
className="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
className="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user