mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-13 13:48:59 +02:00
Feat: update variants placement (#9832)
This commit is contained in:
parent
81c1fb9143
commit
0e2f7374e5
@ -5,6 +5,7 @@ import { disabledStrategyClassName } from 'component/common/StrategyItemContaine
|
||||
export type StrategyEvaluationItemProps = {
|
||||
type?: ReactNode;
|
||||
children?: ReactNode;
|
||||
alignType?: 'center' | 'top';
|
||||
};
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
@ -22,6 +23,8 @@ const StyledContent = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: theme.spacing(1),
|
||||
alignItems: 'center',
|
||||
flexGrow: 1,
|
||||
position: 'relative',
|
||||
[`.${disabledStrategyClassName} &`]: {
|
||||
filter: 'grayscale(1)',
|
||||
color: theme.palette.text.secondary,
|
||||
@ -31,7 +34,9 @@ const StyledContent = styled('div')(({ theme }) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledType = styled('span')(({ theme }) => ({
|
||||
const StyledType = styled('span')<{
|
||||
align?: 'top' | 'center';
|
||||
}>(({ theme, align }) => ({
|
||||
display: 'block',
|
||||
flexShrink: 0,
|
||||
fontSize: theme.fontSizes.smallerBody,
|
||||
@ -41,6 +46,9 @@ const StyledType = styled('span')(({ theme }) => ({
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
width: '100%',
|
||||
},
|
||||
...(align === 'top' && {
|
||||
alignSelf: 'flex-start',
|
||||
}),
|
||||
}));
|
||||
|
||||
/**
|
||||
@ -49,10 +57,11 @@ const StyledType = styled('span')(({ theme }) => ({
|
||||
export const StrategyEvaluationItem: FC<StrategyEvaluationItemProps> = ({
|
||||
type,
|
||||
children,
|
||||
alignType,
|
||||
}) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledType>{type}</StyledType>
|
||||
<StyledType align={alignType}>{type}</StyledType>
|
||||
<StyledContent>{children}</StyledContent>
|
||||
</StyledContainer>
|
||||
);
|
||||
|
@ -0,0 +1,97 @@
|
||||
import { Box, styled, Typography } from '@mui/material';
|
||||
import type { StrategyVariantSchema } from 'openapi';
|
||||
|
||||
const StyledTooltipContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}));
|
||||
|
||||
const StyledVariantContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minWidth: '250px',
|
||||
gap: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const StyledPayloadContainer = styled(Box)(({ theme }) => ({
|
||||
padding: theme.spacing(1, 0, 0.75, 0),
|
||||
flexDirection: 'column',
|
||||
}));
|
||||
|
||||
const StyledPayloadLabel = styled('span')(({ theme }) => ({
|
||||
marginBottom: theme.spacing(1),
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
}));
|
||||
|
||||
const StyledVariantBox = styled(Box, {
|
||||
shouldForwardProp: (prop) => prop !== 'index',
|
||||
})<{ index: number }>(({ theme, index }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
figure: {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
width: theme.spacing(1.6),
|
||||
height: theme.spacing(1.6),
|
||||
borderRadius: '50%',
|
||||
background:
|
||||
theme.palette.variants[index % theme.palette.variants.length],
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledVariantType = styled('span')(({ theme }) => ({
|
||||
display: 'inline-block',
|
||||
background: theme.palette.background.elevation2,
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
padding: theme.spacing(0.25, 1),
|
||||
align: 'center',
|
||||
fontSize: theme.fontSizes.smallBody,
|
||||
}));
|
||||
|
||||
const StyledVariantPayload = styled('code')(({ theme }) => ({
|
||||
display: 'inline-block',
|
||||
background: theme.palette.background.elevation2,
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
padding: theme.spacing(0.25, 1),
|
||||
fontSize: theme.fontSizes.smallerBody,
|
||||
}));
|
||||
|
||||
type SplitPreviewTooltipProps = {
|
||||
variant: StrategyVariantSchema;
|
||||
index: number;
|
||||
selected?: boolean;
|
||||
};
|
||||
|
||||
export const SplitPreviewTooltip = ({
|
||||
variant: { name, weight, payload },
|
||||
index,
|
||||
selected,
|
||||
}: SplitPreviewTooltipProps) => (
|
||||
<StyledTooltipContainer>
|
||||
<StyledVariantContainer>
|
||||
<StyledVariantBox index={index}>
|
||||
<figure />
|
||||
</StyledVariantBox>
|
||||
|
||||
<Typography variant='subtitle2'>
|
||||
{weight / 10}% - {name}
|
||||
</Typography>
|
||||
</StyledVariantContainer>
|
||||
|
||||
{payload ? (
|
||||
<StyledPayloadContainer
|
||||
sx={{
|
||||
display: ['json', 'csv'].includes(payload.type)
|
||||
? 'flex'
|
||||
: 'block',
|
||||
}}
|
||||
>
|
||||
<StyledPayloadLabel>
|
||||
<StyledVariantType>{payload.type}</StyledVariantType>
|
||||
{' payload: '}
|
||||
</StyledPayloadLabel>
|
||||
<StyledVariantPayload>{payload.value}</StyledVariantPayload>
|
||||
</StyledPayloadContainer>
|
||||
) : null}
|
||||
</StyledTooltipContainer>
|
||||
);
|
@ -0,0 +1,145 @@
|
||||
import { Box, Typography, styled } from '@mui/material';
|
||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||
import { Truncator } from 'component/common/Truncator/Truncator';
|
||||
import type { StrategyVariantSchema } from 'openapi';
|
||||
import { SplitPreviewTooltip } from './SplitPreviewTooltip/SplitPreviewTooltip';
|
||||
|
||||
const StyledContainer = styled(Box)(() => ({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
}));
|
||||
|
||||
const StyledVariantItem = styled(Box)<{ selected?: boolean }>(
|
||||
({ theme, selected }) => ({
|
||||
position: 'relative',
|
||||
fontSize: theme.fontSizes.smallerBody,
|
||||
...(selected
|
||||
? {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
);
|
||||
|
||||
const StyledVariantItemTrack = styled(Box)<{
|
||||
index: number;
|
||||
hasError?: boolean;
|
||||
isFirst?: boolean;
|
||||
isLast?: boolean;
|
||||
}>(({ theme, index, hasError, isFirst, isLast }) => ({
|
||||
height: theme.spacing(2),
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
color: hasError ? theme.palette.error.main : 'inherit',
|
||||
background: theme.palette.variants[index % theme.palette.variants.length],
|
||||
...(isFirst
|
||||
? {
|
||||
borderTopLeftRadius: theme.shape.borderRadius,
|
||||
borderBottomLeftRadius: theme.shape.borderRadius,
|
||||
}
|
||||
: {}),
|
||||
...(isLast
|
||||
? {
|
||||
borderTopRightRadius: theme.shape.borderRadius,
|
||||
borderBottomRightRadius: theme.shape.borderRadius,
|
||||
}
|
||||
: {}),
|
||||
}));
|
||||
|
||||
const StyledTrackPercentage = styled(Box)(({ theme }) => ({
|
||||
padding: theme.spacing(0.25, 0.25),
|
||||
lineHeight: 1,
|
||||
}));
|
||||
|
||||
const StyledVariantItemInfo = styled(Box)(({ theme }) => ({
|
||||
padding: theme.spacing(0.125, 0.25, 0),
|
||||
overflow: 'hidden',
|
||||
color: theme.palette.text.secondary,
|
||||
}));
|
||||
|
||||
const StyledHeaderContainer = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const StyledTypography = styled(Typography)(({ theme }) => ({
|
||||
marginY: theme.spacing(1),
|
||||
}));
|
||||
|
||||
type VariantsSplitPreviewProps = {
|
||||
variants: StrategyVariantSchema[];
|
||||
weightsError?: boolean;
|
||||
header?: boolean;
|
||||
selected?: string;
|
||||
};
|
||||
|
||||
export const VariantsSplitPreview = ({
|
||||
variants,
|
||||
weightsError,
|
||||
header = true,
|
||||
selected,
|
||||
}: VariantsSplitPreviewProps) => {
|
||||
if (variants.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={(theme) => ({ marginTop: theme.spacing(header ? 2 : 0) })}>
|
||||
{header ? (
|
||||
<StyledHeaderContainer>
|
||||
<StyledTypography variant='body2'>
|
||||
Flag variants ({variants.length})
|
||||
</StyledTypography>
|
||||
</StyledHeaderContainer>
|
||||
) : null}
|
||||
<StyledContainer>
|
||||
{variants.map((variant, index) => {
|
||||
const value = variant.weight / 10;
|
||||
if (value === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<TooltipResolver
|
||||
variant='custom'
|
||||
key={index}
|
||||
arrow
|
||||
onClick={(e) => e.preventDefault()}
|
||||
titleComponent={
|
||||
<SplitPreviewTooltip
|
||||
variant={variant}
|
||||
index={index}
|
||||
selected={selected === variant.name}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<StyledVariantItem
|
||||
sx={{ width: `${value}%` }}
|
||||
selected={selected === variant.name}
|
||||
>
|
||||
<StyledVariantItemTrack
|
||||
index={index}
|
||||
isFirst={index === 0}
|
||||
isLast={index === variants.length - 1}
|
||||
hasError={weightsError}
|
||||
>
|
||||
<StyledTrackPercentage>
|
||||
<Truncator lines={1}>
|
||||
{`${value}%`}
|
||||
</Truncator>
|
||||
</StyledTrackPercentage>
|
||||
</StyledVariantItemTrack>
|
||||
<StyledVariantItemInfo>
|
||||
<Truncator lines={1}>
|
||||
{variant.name}
|
||||
</Truncator>
|
||||
</StyledVariantItemInfo>
|
||||
</StyledVariantItem>
|
||||
</TooltipResolver>
|
||||
);
|
||||
})}
|
||||
</StyledContainer>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -13,7 +13,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
||||
import { CopyStrategyIconMenu } from './CopyStrategyIconMenu/CopyStrategyIconMenu';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
|
||||
import MenuStrategyRemove from './MenuStrategyRemove/MenuStrategyRemove';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import { Box } from '@mui/material';
|
||||
interface IStrategyItemProps {
|
||||
environmentId: string;
|
||||
@ -93,10 +93,10 @@ export const StrategyItem: FC<IStrategyItemProps> = ({
|
||||
strategy.variants.length > 0 &&
|
||||
(strategy.disabled ? (
|
||||
<Box sx={{ opacity: '0.5' }}>
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
<VariantsSplitPreview variants={strategy.variants} />
|
||||
</Box>
|
||||
) : (
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
<VariantsSplitPreview variants={strategy.variants} />
|
||||
))}
|
||||
</StrategyItemContainer>
|
||||
);
|
||||
|
@ -1,17 +1,15 @@
|
||||
import type { FC } from 'react';
|
||||
import { StrategyEvaluationChip } from 'component/common/ConstraintsList/StrategyEvaluationChip/StrategyEvaluationChip';
|
||||
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
|
||||
import type { ParametersSchema, StrategyVariantSchema } from 'openapi';
|
||||
import type { FC } from 'react';
|
||||
import type { ParametersSchema } from 'openapi';
|
||||
import { parseParameterNumber } from 'utils/parseParameter';
|
||||
import { RolloutVariants } from './RolloutVariants/RolloutVariants';
|
||||
|
||||
export const RolloutParameter: FC<{
|
||||
value: string;
|
||||
parameters?: ParametersSchema;
|
||||
hasConstraints?: boolean;
|
||||
variants?: StrategyVariantSchema[];
|
||||
displayGroupId?: boolean;
|
||||
}> = ({ value, parameters, hasConstraints, variants, displayGroupId }) => {
|
||||
}> = ({ value, parameters, hasConstraints, displayGroupId }) => {
|
||||
const percentage = parseParameterNumber(value);
|
||||
|
||||
const explainStickiness =
|
||||
@ -43,7 +41,6 @@ export const RolloutParameter: FC<{
|
||||
) : null}
|
||||
</p>
|
||||
</StrategyEvaluationItem>
|
||||
<RolloutVariants variants={variants} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,71 +0,0 @@
|
||||
import { styled } from '@mui/material';
|
||||
import { StrategyEvaluationChip } from 'component/common/ConstraintsList/StrategyEvaluationChip/StrategyEvaluationChip';
|
||||
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
|
||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||
import type { StrategyVariantSchema } from 'openapi';
|
||||
import type { FC } from 'react';
|
||||
|
||||
const StyledVariantChip = styled(StrategyEvaluationChip)<{ order: number }>(
|
||||
({ theme, order }) => ({
|
||||
borderRadius: theme.shape.borderRadiusExtraLarge,
|
||||
border: 'none',
|
||||
color: theme.palette.text.primary,
|
||||
background:
|
||||
theme.palette.variants[order % theme.palette.variants.length],
|
||||
fontWeight: theme.typography.fontWeightRegular,
|
||||
}),
|
||||
);
|
||||
|
||||
const StyledPayloadHeader = styled('div')(({ theme }) => ({
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
marginBottom: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const StyledValuesContainer = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: theme.spacing(0.75, 0.5),
|
||||
flexWrap: 'wrap',
|
||||
}));
|
||||
|
||||
export const RolloutVariants: FC<{
|
||||
variants?: StrategyVariantSchema[];
|
||||
}> = ({ variants }) => {
|
||||
if (!variants?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StrategyEvaluationItem type={`Variants (${variants.length})`}>
|
||||
<StyledValuesContainer>
|
||||
{variants.map((variant, i) => (
|
||||
<HtmlTooltip
|
||||
arrow
|
||||
title={
|
||||
variant.payload?.value ? (
|
||||
<div>
|
||||
<StyledPayloadHeader>
|
||||
Payload:
|
||||
</StyledPayloadHeader>
|
||||
<code>{variant.payload?.value}</code>
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
key={variant.name}
|
||||
>
|
||||
<StyledVariantChip
|
||||
key={variant.name}
|
||||
order={i}
|
||||
label={
|
||||
<>
|
||||
<span>
|
||||
{variant.weight / 10}% – {variant.name}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</HtmlTooltip>
|
||||
))}
|
||||
</StyledValuesContainer>
|
||||
</StrategyEvaluationItem>
|
||||
);
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
import { styled } from '@mui/material';
|
||||
import { StrategyEvaluationItem } from 'component/common/ConstraintsList/StrategyEvaluationItem/StrategyEvaluationItem';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import type { StrategyVariantSchema } from 'openapi';
|
||||
import type { FC } from 'react';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(3.5, 2, 0.5),
|
||||
}));
|
||||
|
||||
const StyledTrack = styled('div')(() => ({
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
}));
|
||||
|
||||
export const RolloutVariants: FC<{
|
||||
variants?: StrategyVariantSchema[];
|
||||
selected?: StrategyVariantSchema['name'];
|
||||
}> = ({ variants, selected }) => {
|
||||
if (!variants?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StrategyEvaluationItem
|
||||
type={`Variants (${variants.length})`}
|
||||
alignType='top'
|
||||
>
|
||||
<StyledTrack>
|
||||
<VariantsSplitPreview
|
||||
variants={variants}
|
||||
header={false}
|
||||
selected={selected}
|
||||
/>
|
||||
</StyledTrack>
|
||||
</StrategyEvaluationItem>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
@ -14,6 +14,7 @@ import {
|
||||
ConstraintListItem,
|
||||
ConstraintsList,
|
||||
} from 'component/common/ConstraintsList/ConstraintsList';
|
||||
import { RolloutVariants } from './RolloutVariants/RolloutVariants';
|
||||
|
||||
type StrategyExecutionProps = {
|
||||
strategy: IFeatureStrategyPayload | FeatureStrategySchema;
|
||||
@ -48,21 +49,25 @@ export const StrategyExecution: FC<StrategyExecutionProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<ConstraintsList>
|
||||
{strategySegments?.map((segment) => (
|
||||
<SegmentItem segment={segment} key={segment.id} />
|
||||
))}
|
||||
{constraints?.map((constraint, index) => (
|
||||
<ConstraintAccordionView
|
||||
key={`${objectId(constraint)}-${index}`}
|
||||
constraint={constraint}
|
||||
/>
|
||||
))}
|
||||
{(isCustomStrategy ? customStrategyItems : strategyParameters).map(
|
||||
(item, index) => (
|
||||
<>
|
||||
<ConstraintsList>
|
||||
{strategySegments?.map((segment) => (
|
||||
<SegmentItem segment={segment} key={segment.id} />
|
||||
))}
|
||||
{constraints?.map((constraint, index) => (
|
||||
<ConstraintAccordionView
|
||||
key={`${objectId(constraint)}-${index}`}
|
||||
constraint={constraint}
|
||||
/>
|
||||
))}
|
||||
{(isCustomStrategy
|
||||
? customStrategyItems
|
||||
: strategyParameters
|
||||
).map((item, index) => (
|
||||
<ConstraintListItem key={index}>{item}</ConstraintListItem>
|
||||
),
|
||||
)}
|
||||
</ConstraintsList>
|
||||
))}
|
||||
</ConstraintsList>
|
||||
<RolloutVariants variants={strategy.variants} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -30,7 +30,6 @@ export const useStrategyParameters = (
|
||||
parameters={parameters}
|
||||
hasConstraints={hasConstraints}
|
||||
displayGroupId={displayGroupId}
|
||||
variants={variants}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// deprecated; remove with `flagOverviewRedesign` flag
|
||||
import { Box, styled } from '@mui/material';
|
||||
import { StrategyExecution } from '../../FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import {
|
||||
formatStrategyName,
|
||||
getFeatureStrategyIcon,
|
||||
@ -46,10 +46,10 @@ export const ReleasePlanMilestoneStrategy = ({
|
||||
strategy.variants.length > 0 &&
|
||||
(strategy.disabled ? (
|
||||
<Box sx={{ opacity: '0.5' }}>
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
<VariantsSplitPreview variants={strategy.variants} />
|
||||
</Box>
|
||||
) : (
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
<VariantsSplitPreview variants={strategy.variants} />
|
||||
))}
|
||||
</StyledStrategy>
|
||||
);
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { VariantForm } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm';
|
||||
import { updateWeightEdit } from '../../common/util';
|
||||
import { updateWeightEdit } from 'component/common/util';
|
||||
import type React from 'react';
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
import type { IFeatureVariantEdit } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/EnvironmentVariantsModal';
|
||||
import PermissionButton from '../../common/PermissionButton/PermissionButton';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { WeightType } from '../../../constants/variantTypes';
|
||||
import { Box, styled, Typography, useTheme } from '@mui/material';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { HelpIcon } from '../../common/HelpIcon/HelpIcon';
|
||||
import { StrategyVariantsUpgradeAlert } from '../../common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||
import { StrategyVariantsUpgradeAlert } from 'component/common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
import Add from '@mui/icons-material/Add';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
@ -199,7 +199,7 @@ export const NewStrategyVariants: FC<{
|
||||
>
|
||||
Add variant
|
||||
</PermissionButton>
|
||||
<SplitPreviewSlider
|
||||
<VariantsSplitPreview
|
||||
variants={variantsEdit}
|
||||
weightsError={variantWeightsError}
|
||||
/>
|
||||
|
@ -1,214 +0,0 @@
|
||||
import { Box, Typography, styled } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||
import type { IFeatureVariant } from 'interfaces/featureToggle';
|
||||
|
||||
const StyledContainer = styled(Box)(() => ({
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
}));
|
||||
|
||||
const StyledTrack = styled(Box)(({ theme }) => ({
|
||||
position: 'absolute',
|
||||
height: theme.spacing(3),
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
overflow: 'hidden',
|
||||
}));
|
||||
|
||||
const StyledSegment = styled(Box)(() => ({
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
}));
|
||||
|
||||
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 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',
|
||||
flexWrap: 'wrap',
|
||||
}));
|
||||
|
||||
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[];
|
||||
weightsError?: boolean;
|
||||
}
|
||||
|
||||
const SplitPreviewSlider = ({
|
||||
variants,
|
||||
weightsError,
|
||||
}: ISplitPreviewSliderProps) => {
|
||||
if (variants.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={(theme) => ({ marginTop: theme.spacing(2) })}>
|
||||
<SplitPreviewHeader variants={variants} />
|
||||
<StyledContainer>
|
||||
<StyledTrack />
|
||||
|
||||
{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}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
width: `${value}%`,
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
<StyledSegment>
|
||||
<StyledSegmentTrack index={index} />
|
||||
<StyledTypographySubtitle
|
||||
variant='subtitle2'
|
||||
color={
|
||||
weightsError ? 'error' : 'inherit'
|
||||
}
|
||||
>
|
||||
{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;
|
@ -1,17 +1,17 @@
|
||||
import { VariantForm } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/VariantForm/VariantForm';
|
||||
import { updateWeightEdit } from '../../common/util';
|
||||
import { updateWeightEdit } from 'component/common/util';
|
||||
import type React from 'react';
|
||||
import { type FC, useEffect, useState } from 'react';
|
||||
import type { IFeatureVariantEdit } from '../FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsModal/EnvironmentVariantsModal';
|
||||
import PermissionButton from '../../common/PermissionButton/PermissionButton';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { UPDATE_FEATURE_ENVIRONMENT_VARIANTS } from '../../providers/AccessProvider/permissions';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { WeightType } from '../../../constants/variantTypes';
|
||||
import { Link, styled, Typography, useTheme } from '@mui/material';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import SplitPreviewSlider from './SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { HelpIcon } from '../../common/HelpIcon/HelpIcon';
|
||||
import { StrategyVariantsUpgradeAlert } from '../../common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||
import { StrategyVariantsUpgradeAlert } from 'component/common/StrategyVariantsUpgradeAlert/StrategyVariantsUpgradeAlert';
|
||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||
|
||||
const StyledVariantForms = styled('div')({
|
||||
@ -168,7 +168,7 @@ export const StrategyVariants: FC<{
|
||||
>
|
||||
Add variant
|
||||
</PermissionButton>
|
||||
<SplitPreviewSlider variants={variantsEdit} />
|
||||
<VariantsSplitPreview variants={variantsEdit} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ import type {
|
||||
} from 'openapi';
|
||||
import { objectId } from 'utils/objectId';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
|
||||
import { StrategyExecution } from './StrategyExecution/StrategyExecution';
|
||||
import { PlaygroundStrategyExecution } from './PlaygroundStrategyExecution/PlaygroundStrategyExecution';
|
||||
|
||||
interface IFeatureStrategyItemProps {
|
||||
strategy: PlaygroundStrategySchema;
|
||||
@ -42,7 +42,10 @@ export const FeatureStrategyItem = ({
|
||||
)
|
||||
}
|
||||
>
|
||||
<StrategyExecution strategyResult={strategy} input={input} />
|
||||
<PlaygroundStrategyExecution
|
||||
strategyResult={strategy}
|
||||
input={input}
|
||||
/>
|
||||
</StrategyItemContainer>
|
||||
);
|
||||
};
|
||||
|
@ -4,10 +4,10 @@ import type {
|
||||
PlaygroundStrategySchema,
|
||||
PlaygroundRequestSchema,
|
||||
} from 'openapi';
|
||||
import { StrategyExecution } from './StrategyExecution/LegacyStrategyExecution';
|
||||
import { StrategyExecution } from './PlaygroundStrategyExecution/LegacyStrategyExecution';
|
||||
import { objectId } from 'utils/objectId';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { DisabledStrategyExecution } from './StrategyExecution/DisabledStrategyExecution';
|
||||
import { DisabledStrategyExecution } from './PlaygroundStrategyExecution/DisabledStrategyExecution';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
|
||||
|
||||
interface IFeatureStrategyItemProps {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import type {
|
||||
PlaygroundFeatureSchemaVariant,
|
||||
PlaygroundRequestSchema,
|
||||
PlaygroundStrategySchema,
|
||||
StrategyVariantSchema,
|
||||
} from 'openapi';
|
||||
import { ConstraintExecution } from './ConstraintExecution/ConstraintExecution';
|
||||
import { formattedStrategyNames } from 'utils/strategyNames';
|
||||
@ -16,6 +18,7 @@ import { SegmentExecution } from './SegmentExecution/SegmentExecution';
|
||||
import { useStrategyParameters } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/hooks/useStrategyParameters';
|
||||
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
||||
import { useCustomStrategyParameters } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/hooks/useCustomStrategyParameters';
|
||||
import { RolloutVariants } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/RolloutVariants/RolloutVariants';
|
||||
|
||||
type StrategyExecutionProps = {
|
||||
strategyResult: PlaygroundStrategySchema;
|
||||
@ -23,7 +26,7 @@ type StrategyExecutionProps = {
|
||||
input?: PlaygroundRequestSchema;
|
||||
};
|
||||
|
||||
export const StrategyExecution: FC<StrategyExecutionProps> = ({
|
||||
export const PlaygroundStrategyExecution: FC<StrategyExecutionProps> = ({
|
||||
strategyResult,
|
||||
input,
|
||||
}) => {
|
||||
@ -79,5 +82,17 @@ export const StrategyExecution: FC<StrategyExecutionProps> = ({
|
||||
),
|
||||
].filter(Boolean);
|
||||
|
||||
return <ConstraintsList>{items}</ConstraintsList>;
|
||||
const { variant, variants } = strategyResult.result as unknown as Partial<{
|
||||
variant: PlaygroundFeatureSchemaVariant;
|
||||
variants: StrategyVariantSchema[];
|
||||
}>;
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConstraintsList>{items}</ConstraintsList>
|
||||
{variants?.length ? (
|
||||
<RolloutVariants variants={variants} selected={variant?.name} />
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
@ -10,7 +10,7 @@ import {
|
||||
PROJECT_DEFAULT_STRATEGY_WRITE,
|
||||
UPDATE_PROJECT,
|
||||
} from '@server/types/permissions';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import { StrategyItemContainer } from 'component/common/StrategyItemContainer/LegacyStrategyItemContainer';
|
||||
|
||||
interface ProjectEnvironmentDefaultStrategyProps {
|
||||
@ -84,7 +84,7 @@ const ProjectEnvironmentDefaultStrategy = ({
|
||||
<StrategyExecution strategy={strategy} />
|
||||
|
||||
{strategy.variants && strategy.variants.length > 0 ? (
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
<VariantsSplitPreview variants={strategy.variants} />
|
||||
) : null}
|
||||
</StrategyItemContainer>
|
||||
</>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Box, IconButton, styled } from '@mui/material';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import {
|
||||
formatStrategyName,
|
||||
getFeatureStrategyIcon,
|
||||
@ -88,10 +88,12 @@ export const MilestoneStrategyItem = ({
|
||||
strategy.variants.length > 0 &&
|
||||
(strategy.disabled ? (
|
||||
<Box sx={{ opacity: '0.5' }}>
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
<VariantsSplitPreview
|
||||
variants={strategy.variants}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<SplitPreviewSlider variants={strategy.variants} />
|
||||
<VariantsSplitPreview variants={strategy.variants} />
|
||||
))}
|
||||
</StyledStrategyExecution>
|
||||
</StyledStrategy>
|
||||
|
@ -10,7 +10,7 @@ import { updateWeightEdit } from 'component/common/util';
|
||||
import { WeightType } from 'constants/variantTypes';
|
||||
import { useTheme } from '@mui/material';
|
||||
import Add from '@mui/icons-material/Add';
|
||||
import SplitPreviewSlider from 'component/feature/StrategyTypes/SplitPreviewSlider/SplitPreviewSlider';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
|
||||
const StyledVariantForms = styled('div')({
|
||||
display: 'flex',
|
||||
@ -180,7 +180,7 @@ export const MilestoneStrategyVariants = ({
|
||||
<Button onClick={addVariant} variant='outlined' startIcon={<Add />}>
|
||||
Add variant
|
||||
</Button>
|
||||
<SplitPreviewSlider
|
||||
<VariantsSplitPreview
|
||||
variants={variantsEdit}
|
||||
weightsError={variantWeightsError}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user