Skip to content

Commit 0e2f737

Browse files
authored
Feat: update variants placement (#9832)
1 parent 81c1fb9 commit 0e2f737

File tree

36 files changed

+367
-340
lines changed

36 files changed

+367
-340
lines changed

frontend/src/component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { disabledStrategyClassName } from 'component/common/StrategyItemContaine
55
export type StrategyEvaluationItemProps = {
66
type?: ReactNode;
77
children?: ReactNode;
8+
alignType?: 'center' | 'top';
89
};
910

1011
const StyledContainer = styled('div')(({ theme }) => ({
@@ -22,6 +23,8 @@ const StyledContent = styled('div')(({ theme }) => ({
2223
display: 'flex',
2324
gap: theme.spacing(1),
2425
alignItems: 'center',
26+
flexGrow: 1,
27+
position: 'relative',
2528
[`.${disabledStrategyClassName} &`]: {
2629
filter: 'grayscale(1)',
2730
color: theme.palette.text.secondary,
@@ -31,7 +34,9 @@ const StyledContent = styled('div')(({ theme }) => ({
3134
},
3235
}));
3336

34-
const StyledType = styled('span')(({ theme }) => ({
37+
const StyledType = styled('span')<{
38+
align?: 'top' | 'center';
39+
}>(({ theme, align }) => ({
3540
display: 'block',
3641
flexShrink: 0,
3742
fontSize: theme.fontSizes.smallerBody,
@@ -41,6 +46,9 @@ const StyledType = styled('span')(({ theme }) => ({
4146
[theme.breakpoints.down('sm')]: {
4247
width: '100%',
4348
},
49+
...(align === 'top' && {
50+
alignSelf: 'flex-start',
51+
}),
4452
}));
4553

4654
/**
@@ -49,10 +57,11 @@ const StyledType = styled('span')(({ theme }) => ({
4957
export const StrategyEvaluationItem: FC<StrategyEvaluationItemProps> = ({
5058
type,
5159
children,
60+
alignType,
5261
}) => {
5362
return (
5463
<StyledContainer>
55-
<StyledType>{type}</StyledType>
64+
<StyledType align={alignType}>{type}</StyledType>
5665
<StyledContent>{children}</StyledContent>
5766
</StyledContainer>
5867
);
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { Box, styled, Typography } from '@mui/material';
2+
import type { StrategyVariantSchema } from 'openapi';
3+
4+
const StyledTooltipContainer = styled(Box)(({ theme }) => ({
5+
display: 'flex',
6+
flexDirection: 'column',
7+
}));
8+
9+
const StyledVariantContainer = styled(Box)(({ theme }) => ({
10+
display: 'flex',
11+
alignItems: 'center',
12+
minWidth: '250px',
13+
gap: theme.spacing(1),
14+
}));
15+
16+
const StyledPayloadContainer = styled(Box)(({ theme }) => ({
17+
padding: theme.spacing(1, 0, 0.75, 0),
18+
flexDirection: 'column',
19+
}));
20+
21+
const StyledPayloadLabel = styled('span')(({ theme }) => ({
22+
marginBottom: theme.spacing(1),
23+
fontSize: theme.typography.body2.fontSize,
24+
}));
25+
26+
const StyledVariantBox = styled(Box, {
27+
shouldForwardProp: (prop) => prop !== 'index',
28+
})<{ index: number }>(({ theme, index }) => ({
29+
display: 'flex',
30+
alignItems: 'center',
31+
figure: {
32+
margin: 0,
33+
padding: 0,
34+
width: theme.spacing(1.6),
35+
height: theme.spacing(1.6),
36+
borderRadius: '50%',
37+
background:
38+
theme.palette.variants[index % theme.palette.variants.length],
39+
},
40+
}));
41+
42+
const StyledVariantType = styled('span')(({ theme }) => ({
43+
display: 'inline-block',
44+
background: theme.palette.background.elevation2,
45+
borderRadius: theme.shape.borderRadiusLarge,
46+
padding: theme.spacing(0.25, 1),
47+
align: 'center',
48+
fontSize: theme.fontSizes.smallBody,
49+
}));
50+
51+
const StyledVariantPayload = styled('code')(({ theme }) => ({
52+
display: 'inline-block',
53+
background: theme.palette.background.elevation2,
54+
borderRadius: theme.shape.borderRadiusLarge,
55+
padding: theme.spacing(0.25, 1),
56+
fontSize: theme.fontSizes.smallerBody,
57+
}));
58+
59+
type SplitPreviewTooltipProps = {
60+
variant: StrategyVariantSchema;
61+
index: number;
62+
selected?: boolean;
63+
};
64+
65+
export const SplitPreviewTooltip = ({
66+
variant: { name, weight, payload },
67+
index,
68+
selected,
69+
}: SplitPreviewTooltipProps) => (
70+
<StyledTooltipContainer>
71+
<StyledVariantContainer>
72+
<StyledVariantBox index={index}>
73+
<figure />
74+
</StyledVariantBox>
75+
76+
<Typography variant='subtitle2'>
77+
{weight / 10}% - {name}
78+
</Typography>
79+
</StyledVariantContainer>
80+
81+
{payload ? (
82+
<StyledPayloadContainer
83+
sx={{
84+
display: ['json', 'csv'].includes(payload.type)
85+
? 'flex'
86+
: 'block',
87+
}}
88+
>
89+
<StyledPayloadLabel>
90+
<StyledVariantType>{payload.type}</StyledVariantType>
91+
{' payload: '}
92+
</StyledPayloadLabel>
93+
<StyledVariantPayload>{payload.value}</StyledVariantPayload>
94+
</StyledPayloadContainer>
95+
) : null}
96+
</StyledTooltipContainer>
97+
);
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { Box, Typography, styled } from '@mui/material';
2+
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
3+
import { Truncator } from 'component/common/Truncator/Truncator';
4+
import type { StrategyVariantSchema } from 'openapi';
5+
import { SplitPreviewTooltip } from './SplitPreviewTooltip/SplitPreviewTooltip';
6+
7+
const StyledContainer = styled(Box)(() => ({
8+
display: 'flex',
9+
width: '100%',
10+
position: 'relative',
11+
}));
12+
13+
const StyledVariantItem = styled(Box)<{ selected?: boolean }>(
14+
({ theme, selected }) => ({
15+
position: 'relative',
16+
fontSize: theme.fontSizes.smallerBody,
17+
...(selected
18+
? {
19+
fontWeight: theme.typography.fontWeightBold,
20+
}
21+
: {}),
22+
}),
23+
);
24+
25+
const StyledVariantItemTrack = styled(Box)<{
26+
index: number;
27+
hasError?: boolean;
28+
isFirst?: boolean;
29+
isLast?: boolean;
30+
}>(({ theme, index, hasError, isFirst, isLast }) => ({
31+
height: theme.spacing(2),
32+
width: '100%',
33+
position: 'relative',
34+
color: hasError ? theme.palette.error.main : 'inherit',
35+
background: theme.palette.variants[index % theme.palette.variants.length],
36+
...(isFirst
37+
? {
38+
borderTopLeftRadius: theme.shape.borderRadius,
39+
borderBottomLeftRadius: theme.shape.borderRadius,
40+
}
41+
: {}),
42+
...(isLast
43+
? {
44+
borderTopRightRadius: theme.shape.borderRadius,
45+
borderBottomRightRadius: theme.shape.borderRadius,
46+
}
47+
: {}),
48+
}));
49+
50+
const StyledTrackPercentage = styled(Box)(({ theme }) => ({
51+
padding: theme.spacing(0.25, 0.25),
52+
lineHeight: 1,
53+
}));
54+
55+
const StyledVariantItemInfo = styled(Box)(({ theme }) => ({
56+
padding: theme.spacing(0.125, 0.25, 0),
57+
overflow: 'hidden',
58+
color: theme.palette.text.secondary,
59+
}));
60+
61+
const StyledHeaderContainer = styled(Box)(({ theme }) => ({
62+
display: 'flex',
63+
alignItems: 'center',
64+
marginBottom: theme.spacing(1),
65+
}));
66+
67+
const StyledTypography = styled(Typography)(({ theme }) => ({
68+
marginY: theme.spacing(1),
69+
}));
70+
71+
type VariantsSplitPreviewProps = {
72+
variants: StrategyVariantSchema[];
73+
weightsError?: boolean;
74+
header?: boolean;
75+
selected?: string;
76+
};
77+
78+
export const VariantsSplitPreview = ({
79+
variants,
80+
weightsError,
81+
header = true,
82+
selected,
83+
}: VariantsSplitPreviewProps) => {
84+
if (variants.length < 1) {
85+
return null;
86+
}
87+
88+
return (
89+
<Box sx={(theme) => ({ marginTop: theme.spacing(header ? 2 : 0) })}>
90+
{header ? (
91+
<StyledHeaderContainer>
92+
<StyledTypography variant='body2'>
93+
Flag variants ({variants.length})
94+
</StyledTypography>
95+
</StyledHeaderContainer>
96+
) : null}
97+
<StyledContainer>
98+
{variants.map((variant, index) => {
99+
const value = variant.weight / 10;
100+
if (value === 0) {
101+
return null;
102+
}
103+
return (
104+
<TooltipResolver
105+
variant='custom'
106+
key={index}
107+
arrow
108+
onClick={(e) => e.preventDefault()}
109+
titleComponent={
110+
<SplitPreviewTooltip
111+
variant={variant}
112+
index={index}
113+
selected={selected === variant.name}
114+
/>
115+
}
116+
>
117+
<StyledVariantItem
118+
sx={{ width: `${value}%` }}
119+
selected={selected === variant.name}
120+
>
121+
<StyledVariantItemTrack
122+
index={index}
123+
isFirst={index === 0}
124+
isLast={index === variants.length - 1}
125+
hasError={weightsError}
126+
>
127+
<StyledTrackPercentage>
128+
<Truncator lines={1}>
129+
{`${value}%`}
130+
</Truncator>
131+
</StyledTrackPercentage>
132+
</StyledVariantItemTrack>
133+
<StyledVariantItemInfo>
134+
<Truncator lines={1}>
135+
{variant.name}
136+
</Truncator>
137+
</StyledVariantItemInfo>
138+
</StyledVariantItem>
139+
</TooltipResolver>
140+
);
141+
})}
142+
</StyledContainer>
143+
</Box>
144+
);
145+
};

frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/LegacyStrategyItem.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
1313
import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu';
1414
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
1515
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
16-
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
16+
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
1717
import { Box } from '@mui/material';
1818
interface IStrategyItemProps {
1919
environmentId: string;
@@ -93,10 +93,10 @@ export const StrategyItem: FC<IStrategyItemProps> = ({
9393
strategy.variants.length > 0 &&
9494
(strategy.disabled ? (
9595
<Box sx={{ opacity: '0.5' }}>
96-
<SplitPreviewSlider variants={strategy.variants} />
96+
<VariantsSplitPreview variants={strategy.variants} />
9797
</Box>
9898
) : (
99-
<SplitPreviewSlider variants={strategy.variants} />
99+
<VariantsSplitPreview variants={strategy.variants} />
100100
))}
101101
</StrategyItemContainer>
102102
);

frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/RolloutParameter/RolloutParameter.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
1+
import type { FC } from 'react';
12
import { StrategyEvaluationChip } from 'component/common/ConstraintsList/StrategyEvaluationChip/StrategyEvaluationChip';
23
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
3-
import type { ParametersSchema, StrategyVariantSchema } from 'openapi';
4-
import type { FC } from 'react';
4+
import type { ParametersSchema } from 'openapi';
55
import { parseParameterNumber } from 'utils/parseParameter';
6-
import { RolloutVariants } from './RolloutVariants/RolloutVariants';
76

87
export const RolloutParameter: FC<{
98
value: string;
109
parameters?: ParametersSchema;
1110
hasConstraints?: boolean;
12-
variants?: StrategyVariantSchema[];
1311
displayGroupId?: boolean;
14-
}> = ({ value, parameters, hasConstraints, variants, displayGroupId }) => {
12+
}> = ({ value, parameters, hasConstraints, displayGroupId }) => {
1513
const percentage = parseParameterNumber(value);
1614

1715
const explainStickiness =
@@ -43,7 +41,6 @@ export const RolloutParameter: FC<{
4341
) : null}
4442
</p>
4543
</StrategyEvaluationItem>
46-
<RolloutVariants variants={variants} />
4744
</>
4845
);
4946
};

0 commit comments

Comments
 (0)