Skip to content

Commit

Permalink
feat: strategy variants on strategy overview (#4776)
Browse files Browse the repository at this point in the history
Refactors the breakdown of feature variants per strategy on the
environment overview level:
  • Loading branch information
FredrikOseberg committed Sep 21, 2023
1 parent 5799d0c commit 6884f9c
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 37 deletions.
Expand Up @@ -18,7 +18,6 @@ export const TooltipResolver = ({
if (!title && !titleComponent) {
return children;
}

if (variant === 'custom') {
return (
<HtmlTooltip {...rest} title={title || titleComponent} arrow>
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -86,6 +87,9 @@ export const StrategyItem: FC<IStrategyItemProps> = ({
}
>
<StrategyExecution strategy={strategy} />
{strategy.variants ? (
<SplitPreviewSlider variants={strategy.variants} />
) : null}
</StrategyItemContainer>
);
};
@@ -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',
Expand All @@ -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;
Expand Up @@ -157,9 +157,7 @@ export const StrategyVariants: FC<{
>
Add variant
</PermissionButton>
<SplitPreviewSlider
values={variantsEdit.map(variant => variant.weight / 10)}
/>
<SplitPreviewSlider variants={variantsEdit} />
</>
);
};

0 comments on commit 6884f9c

Please sign in to comment.