1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01: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:
Fredrik Strand Oseberg 2023-09-21 14:28:45 +02:00 committed by GitHub
parent 5799d0c90f
commit 6884f9cdc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 170 additions and 37 deletions

View File

@ -18,7 +18,6 @@ export const TooltipResolver = ({
if (!title && !titleComponent) {
return children;
}
if (variant === 'custom') {
return (
<HtmlTooltip {...rest} title={title || titleComponent} arrow>

View File

@ -12,6 +12,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu';
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
interface IStrategyItemProps {
environmentId: string;
@ -86,6 +87,9 @@ export const StrategyItem: FC<IStrategyItemProps> = ({
}
>
<StrategyExecution strategy={strategy} />
{strategy.variants ? (
<SplitPreviewSlider variants={strategy.variants} />
) : null}
</StrategyItemContainer>
);
};

View File

@ -1,10 +1,9 @@
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 = {
values: number[];
};
const StyledContainer = styled(Box)(({ theme }) => ({
const StyledContainer = styled(Box)(() => ({
display: 'flex',
width: '100%',
position: 'relative',
@ -18,55 +17,188 @@ const StyledTrack = styled(Box)(({ theme }) => ({
overflow: 'hidden',
}));
const StyledSegment = styled(Box)(({ theme }) => ({
const StyledSegment = styled(Box)(() => ({
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
width: '100%',
}));
const StyledSegmentTrack = styled(Box)(({ theme }) => ({
height: theme.spacing(3),
const StyledSegmentTrack = styled(Box, {
shouldForwardProp: prop => prop !== 'index',
})<{ index: number }>(({ theme, index }) => ({
height: theme.spacing(1.8),
width: '100%',
position: 'relative',
background: theme.palette.variants[index % theme.palette.variants.length],
}));
const SplitPreviewSlider = ({ values }: SplitPreviewSliderProps) => {
if (values.length < 2) {
const StyledHeaderContainer = styled(Box)(({ theme }) => ({
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 (
<Box sx={theme => ({ marginTop: theme.spacing(2) })}>
<Typography
variant="body2"
sx={theme => ({ marginY: theme.spacing(1) })}
>
Split preview
</Typography>
<SplitPreviewHeader variants={variants} />
<StyledContainer>
<StyledTrack />
{values.map((value, index) => (
<StyledSegment key={index} sx={{ width: `${value}%` }}>
<StyledSegmentTrack
sx={theme => ({
background:
theme.palette.variants[
index % theme.palette.variants.length
],
})}
/>
<Typography
variant="subtitle2"
sx={theme => ({ marginTop: theme.spacing(1) })}
{variants.map((variant, index) => {
const value = variant.weight / 10;
return (
<TooltipResolver
variant="custom"
key={index}
arrow
onClick={e => e.preventDefault()}
titleComponent={
<SplitPreviewTooltip
variant={variant}
index={index}
/>
}
>
{value}%
</Typography>
</StyledSegment>
))}
<Box
style={{
width: `${value}%`,
}}
>
{' '}
<StyledSegment>
<StyledSegmentTrack index={index} />
<StyledTypographySubtitle variant="subtitle2">
{value}%
</StyledTypographySubtitle>
</StyledSegment>
</Box>
</TooltipResolver>
);
})}
</StyledContainer>
</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;

View File

@ -157,9 +157,7 @@ export const StrategyVariants: FC<{
>
Add variant
</PermissionButton>
<SplitPreviewSlider
values={variantsEdit.map(variant => variant.weight / 10)}
/>
<SplitPreviewSlider variants={variantsEdit} />
</>
);
};