mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-09 01:17:06 +02:00
feat: strategy variants on strategy overview (#4776)
Refactors the breakdown of feature variants per strategy on the environment overview level:
This commit is contained in:
parent
5799d0c90f
commit
6884f9cdc9
@ -18,7 +18,6 @@ export const TooltipResolver = ({
|
|||||||
if (!title && !titleComponent) {
|
if (!title && !titleComponent) {
|
||||||
return children;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variant === 'custom') {
|
if (variant === 'custom') {
|
||||||
return (
|
return (
|
||||||
<HtmlTooltip {...rest} title={title || titleComponent} arrow>
|
<HtmlTooltip {...rest} title={title || titleComponent} arrow>
|
||||||
|
@ -12,6 +12,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu';
|
import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu';
|
||||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||||
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
|
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
|
||||||
|
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||||
|
|
||||||
interface IStrategyItemProps {
|
interface IStrategyItemProps {
|
||||||
environmentId: string;
|
environmentId: string;
|
||||||
@ -86,6 +87,9 @@ export const StrategyItem: FC<IStrategyItemProps> = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StrategyExecution strategy={strategy} />
|
<StrategyExecution strategy={strategy} />
|
||||||
|
{strategy.variants ? (
|
||||||
|
<SplitPreviewSlider variants={strategy.variants} />
|
||||||
|
) : null}
|
||||||
</StrategyItemContainer>
|
</StrategyItemContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { Box, Typography, styled } from '@mui/material';
|
import { Box, Typography, styled } from '@mui/material';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||||
|
import { IFeatureVariant } from 'interfaces/featureToggle';
|
||||||
|
|
||||||
type SplitPreviewSliderProps = {
|
const StyledContainer = styled(Box)(() => ({
|
||||||
values: number[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledContainer = styled(Box)(({ theme }) => ({
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@ -18,55 +17,188 @@ const StyledTrack = styled(Box)(({ theme }) => ({
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledSegment = styled(Box)(({ theme }) => ({
|
const StyledSegment = styled(Box)(() => ({
|
||||||
height: '100%',
|
height: '100%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledSegmentTrack = styled(Box)(({ theme }) => ({
|
const StyledSegmentTrack = styled(Box, {
|
||||||
height: theme.spacing(3),
|
shouldForwardProp: prop => prop !== 'index',
|
||||||
|
})<{ index: number }>(({ theme, index }) => ({
|
||||||
|
height: theme.spacing(1.8),
|
||||||
width: '100%',
|
width: '100%',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
background: theme.palette.variants[index % theme.palette.variants.length],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const SplitPreviewSlider = ({ values }: SplitPreviewSliderProps) => {
|
const StyledHeaderContainer = styled(Box)(({ theme }) => ({
|
||||||
if (values.length < 2) {
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTypography = styled(Typography)(({ theme }) => ({
|
||||||
|
marginY: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledVariantBoxContainer = styled(Box)(() => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledVariantBox = styled(Box, {
|
||||||
|
shouldForwardProp: prop => prop !== 'index',
|
||||||
|
})<{ index: number }>(({ theme, index }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginRight: theme.spacing(2),
|
||||||
|
'& div': {
|
||||||
|
width: theme.spacing(1.6),
|
||||||
|
height: theme.spacing(1.6),
|
||||||
|
borderRadius: '50%',
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
background:
|
||||||
|
theme.palette.variants[index % theme.palette.variants.length],
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTypographySubtitle = styled(Typography)(({ theme }) => ({
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface ISplitPreviewSliderProps {
|
||||||
|
variants: IFeatureVariant[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const SplitPreviewSlider = ({ variants }: ISplitPreviewSliderProps) => {
|
||||||
|
if (variants.length < 2) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={theme => ({ marginTop: theme.spacing(2) })}>
|
<Box sx={theme => ({ marginTop: theme.spacing(2) })}>
|
||||||
<Typography
|
<SplitPreviewHeader variants={variants} />
|
||||||
variant="body2"
|
|
||||||
sx={theme => ({ marginY: theme.spacing(1) })}
|
|
||||||
>
|
|
||||||
Split preview
|
|
||||||
</Typography>
|
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledTrack />
|
<StyledTrack />
|
||||||
{values.map((value, index) => (
|
|
||||||
<StyledSegment key={index} sx={{ width: `${value}%` }}>
|
{variants.map((variant, index) => {
|
||||||
<StyledSegmentTrack
|
const value = variant.weight / 10;
|
||||||
sx={theme => ({
|
return (
|
||||||
background:
|
<TooltipResolver
|
||||||
theme.palette.variants[
|
variant="custom"
|
||||||
index % theme.palette.variants.length
|
key={index}
|
||||||
],
|
arrow
|
||||||
})}
|
onClick={e => e.preventDefault()}
|
||||||
|
titleComponent={
|
||||||
|
<SplitPreviewTooltip
|
||||||
|
variant={variant}
|
||||||
|
index={index}
|
||||||
/>
|
/>
|
||||||
<Typography
|
}
|
||||||
variant="subtitle2"
|
|
||||||
sx={theme => ({ marginTop: theme.spacing(1) })}
|
|
||||||
>
|
>
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
width: `${value}%`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
<StyledSegment>
|
||||||
|
<StyledSegmentTrack index={index} />
|
||||||
|
<StyledTypographySubtitle variant="subtitle2">
|
||||||
{value}%
|
{value}%
|
||||||
</Typography>
|
</StyledTypographySubtitle>
|
||||||
</StyledSegment>
|
</StyledSegment>
|
||||||
))}
|
</Box>
|
||||||
|
</TooltipResolver>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SplitPreviewHeader = ({ variants }: ISplitPreviewSliderProps) => {
|
||||||
|
return (
|
||||||
|
<StyledHeaderContainer>
|
||||||
|
<StyledTypography variant="body2">
|
||||||
|
Feature variants ({variants.length})
|
||||||
|
</StyledTypography>
|
||||||
|
<StyledVariantBoxContainer>
|
||||||
|
{variants.map((variant, index) => (
|
||||||
|
<StyledVariantBox key={index} index={index}>
|
||||||
|
<Box />
|
||||||
|
<StyledTypography variant="body2">
|
||||||
|
{variant.name}
|
||||||
|
</StyledTypography>
|
||||||
|
</StyledVariantBox>
|
||||||
|
))}
|
||||||
|
</StyledVariantBoxContainer>
|
||||||
|
</StyledHeaderContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ISplitPreviewTooltip {
|
||||||
|
variant: IFeatureVariant;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledTooltipContainer = styled(Box)(() => ({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledVariantContainer = styled(Box)(() => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
minWidth: '250px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledPayloadContainer = styled(Box)(({ theme }) => ({
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledPayloadLabel = styled(Typography)(({ theme }) => ({
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const SplitPreviewTooltip = ({ variant, index }: ISplitPreviewTooltip) => {
|
||||||
|
return (
|
||||||
|
<StyledTooltipContainer>
|
||||||
|
<StyledVariantContainer>
|
||||||
|
<StyledVariantBox index={index}>
|
||||||
|
<Box />
|
||||||
|
</StyledVariantBox>
|
||||||
|
|
||||||
|
<Typography variant="subtitle2">
|
||||||
|
{variant.weight / 10}% - {variant.name}
|
||||||
|
</Typography>
|
||||||
|
</StyledVariantContainer>
|
||||||
|
|
||||||
|
{variant.payload ? (
|
||||||
|
<StyledPayloadContainer>
|
||||||
|
<StyledPayloadLabel variant="body2">
|
||||||
|
Payload
|
||||||
|
</StyledPayloadLabel>
|
||||||
|
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={variant.payload.type === 'json'}
|
||||||
|
show={<code>{variant.payload.value}</code>}
|
||||||
|
elseShow={
|
||||||
|
<Typography variant="body2">
|
||||||
|
{variant.payload.value}
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</StyledPayloadContainer>
|
||||||
|
) : null}
|
||||||
|
</StyledTooltipContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default SplitPreviewSlider;
|
export default SplitPreviewSlider;
|
||||||
|
@ -157,9 +157,7 @@ export const StrategyVariants: FC<{
|
|||||||
>
|
>
|
||||||
Add variant
|
Add variant
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<SplitPreviewSlider
|
<SplitPreviewSlider variants={variantsEdit} />
|
||||||
values={variantsEdit.map(variant => variant.weight / 10)}
|
|
||||||
/>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user