1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-18 13:48:58 +02:00

Chore(1-3825)/remove legacy strategy separator (#10137)

Deletes the legacy strategy separator and removes all references to it.
Luckily, all references to the separator were in dangling files that
could themselves be deleted directly.
This commit is contained in:
Thomas Heartman 2025-06-13 12:06:48 +02:00 committed by GitHub
parent 74ae35298d
commit b66cff9af5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 0 additions and 558 deletions

View File

@ -1,55 +0,0 @@
// deprecated; remove with the `flagOverviewRedesign` flag
import { Box, styled, useTheme } from '@mui/material';
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender.tsx';
interface IStrategySeparatorProps {
text: 'AND' | 'OR';
}
const StyledContent = styled('div')(({ theme }) => ({
padding: theme.spacing(0.75, 1),
color: theme.palette.text.primary,
fontSize: theme.fontSizes.smallerBody,
backgroundColor: theme.palette.background.elevation2,
borderRadius: theme.shape.borderRadius,
position: 'absolute',
zIndex: theme.zIndex.fab,
top: '50%',
left: theme.spacing(2),
transform: 'translateY(-50%)',
lineHeight: 1,
}));
const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: theme.palette.seen.primary,
borderRadius: theme.shape.borderRadiusLarge,
padding: theme.spacing(0.75, 1.5),
}));
/**
* @deprecated remove with 'flagOverviewRedesign' flag. This pollutes a lot of places in the codebase 😞
*/
export const StrategySeparator = ({ text }: IStrategySeparatorProps) => {
const theme = useTheme();
return (
<Box
sx={{
height: theme.spacing(text === 'AND' ? 1 : 1.5),
position: 'relative',
width: '100%',
}}
>
<ConditionallyRender
condition={text === 'AND'}
show={() => <StyledContent>{text}</StyledContent>}
elseShow={() => (
<StyledCenteredContent>{text}</StyledCenteredContent>
)}
/>
</Box>
);
};

View File

@ -1,33 +0,0 @@
import { Fragment } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
import type { ISegment } from 'interfaces/segment';
import { SegmentItem } from 'component/common/SegmentItem/SegmentItem';
interface IFeatureOverviewSegmentProps {
segments?: ISegment[];
disabled?: boolean | null;
}
export const FeatureOverviewSegment = ({
segments,
disabled = false,
}: IFeatureOverviewSegmentProps) => {
if (!segments || segments.length === 0) {
return null;
}
return (
<>
{segments.map((segment, index) => (
<Fragment key={segment.id}>
<ConditionallyRender
condition={index > 0}
show={<StrategySeparator text='AND' />}
/>
<SegmentItem segment={segment} disabled={disabled} />
</Fragment>
))}
</>
);
};

View File

@ -1,97 +0,0 @@
import { Box, styled, Typography, useTheme } from '@mui/material';
import CancelOutlined from '@mui/icons-material/CancelOutlined';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
interface ICustomParameterItem {
text: string;
input?: string | null;
isRequired?: boolean;
}
const StyledWrapper = styled(Box)(({ theme }) => ({
width: '100%',
padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.divider}`,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
gap: 2,
}));
export const CustomParameterItem = ({
text,
input = null,
isRequired = false,
}: ICustomParameterItem) => {
const theme = useTheme();
const color = input === null ? 'error' : 'neutral';
const requiredError = isRequired && input === null;
return (
<StyledWrapper>
<Typography
variant='subtitle1'
color={theme.palette[color].main}
sx={{ minWidth: 118 }}
>
{`${input === null ? 'no value' : input}`}
</Typography>
<Box
sx={{
flexGrow: 1,
flexDirection: 'column',
}}
>
<Box sx={{ flexGrow: 1 }}>
<ConditionallyRender
condition={Boolean(requiredError)}
show={
<>
<Typography
component='span'
color={theme.palette.error.main}
>
{' required parameter '}
</Typography>
<StringTruncator
maxWidth='300'
text={text}
maxLength={50}
/>
<Typography
component='span'
color={theme.palette.error.main}
>
{' is not set '}
</Typography>
</>
}
elseShow={
<>
<Typography
component='span'
color='text.disabled'
>
{' set on parameter '}
</Typography>
<StringTruncator
maxWidth='300'
text={text}
maxLength={50}
/>
</>
}
/>
</Box>
</Box>
<ConditionallyRender
condition={Boolean(requiredError)}
show={<CancelOutlined color={'error'} />}
elseShow={<div />}
/>
</StyledWrapper>
);
};

View File

@ -1,115 +0,0 @@
import { Fragment, type VFC } from 'react';
import {
parseParameterNumber,
parseParameterString,
parseParameterStrings,
} from 'utils/parseParameter';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
import { CustomParameterItem } from './CustomParameterItem/CustomParameterItem.tsx';
interface ICustomStrategyProps {
parameters: { [key: string]: string };
strategyName: string;
}
export const CustomStrategyParams: VFC<ICustomStrategyProps> = ({
strategyName,
parameters,
}) => {
const { strategies } = useStrategies();
const definition = strategies.find((strategyDefinition) => {
return strategyDefinition.name === strategyName;
});
if (!definition?.editable) {
return null;
}
const items = definition?.parameters.map((param) => {
const paramValue = parameters[param.name];
const isRequired = param.required;
switch (param?.type) {
case 'list': {
const values = parseParameterStrings(paramValue);
return (
<CustomParameterItem
isRequired={isRequired}
text={param.name}
input={values?.length > 0 ? values.join(', ') : null}
/>
);
}
case 'percentage': {
const percentage = parseParameterNumber(paramValue);
const correctPercentage = !(
paramValue === undefined ||
paramValue === '' ||
percentage < 0 ||
percentage > 100
);
return (
<CustomParameterItem
text={param.name}
isRequired={isRequired}
input={correctPercentage ? `${percentage}%` : undefined}
/>
);
}
case 'boolean': {
const bool = ['true', 'false'].includes(paramValue)
? paramValue
: undefined;
return (
<CustomParameterItem
isRequired={isRequired}
text={param.name}
input={paramValue !== undefined ? bool : undefined}
/>
);
}
case 'string': {
const value = parseParameterString(paramValue);
return (
<CustomParameterItem
text={param.name}
isRequired={isRequired}
input={value !== undefined ? value : undefined}
/>
);
}
case 'number': {
const isCorrect = !(
paramValue === undefined || paramValue === ''
);
const number = parseParameterNumber(paramValue);
return (
<CustomParameterItem
text={param.name}
isRequired={isRequired}
input={isCorrect ? `${number}` : undefined}
/>
);
}
case 'default':
return null;
}
return null;
});
return (
<>
{items.map((item, index) => (
<Fragment key={index}>
<ConditionallyRender
condition={index > 0}
show={<StrategySeparator text='AND' />}
/>
{item}
</Fragment>
))}
</>
);
};

View File

@ -1,92 +0,0 @@
import { Fragment, type VFC } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
import { styled } from '@mui/material';
import type {
PlaygroundRequestSchema,
PlaygroundStrategySchema,
} from 'openapi';
import { PlaygroundResultStrategyExecutionParameters } from './StrategyExecutionParameters/StrategyExecutionParameters.tsx';
import { CustomStrategyParams } from './CustomStrategyParams/CustomStrategyParams.tsx';
import { formattedStrategyNames } from 'utils/strategyNames';
import { StyledBoxSummary } from './StrategyExecution.styles';
import { Badge } from 'component/common/Badge/Badge';
import { ConstraintExecutionWithoutResults } from './ConstraintExecution/ConstraintExecutionWithoutResults.tsx';
import { SegmentExecutionWithoutResult } from './SegmentExecution/SegmentExecutionWithoutResult.tsx';
interface IDisabledStrategyExecutionProps {
strategyResult: PlaygroundStrategySchema;
percentageFill?: string;
input?: PlaygroundRequestSchema;
}
const StyledStrategyExecutionWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0),
}));
export const DisabledStrategyExecution: VFC<
IDisabledStrategyExecutionProps
> = ({ strategyResult, input, percentageFill }) => {
const { name, constraints, segments, parameters } = strategyResult;
const hasSegments = Boolean(segments && segments.length > 0);
const hasConstraints = Boolean(constraints && constraints?.length > 0);
const hasExecutionParameters =
name !== 'default' &&
Object.keys(formattedStrategyNames).includes(name);
const hasCustomStrategyParameters =
Object.keys(parameters).length > 0 &&
strategyResult.result.evaluationStatus === 'incomplete'; // Use of custom strategy can be more explicit from the API
if (!parameters) {
return null;
}
const items = [
hasSegments && <SegmentExecutionWithoutResult segments={segments} />,
hasConstraints && (
<ConstraintExecutionWithoutResults constraints={constraints} />
),
hasExecutionParameters && (
<PlaygroundResultStrategyExecutionParameters
parameters={parameters}
constraints={constraints}
input={input}
disabled
/>
),
hasCustomStrategyParameters && (
<CustomStrategyParams strategyName={name} parameters={parameters} />
),
name === 'default' && (
<StyledBoxSummary
sx={(theme) => ({
width: '100%',
color: theme.palette.text.secondary,
})}
>
The standard strategy is <Badge color={'disabled'}>ON</Badge>{' '}
for all users.
</StyledBoxSummary>
),
].filter(Boolean);
return (
<StyledStrategyExecutionWrapper>
{items.map((item, index) => (
<Fragment key={index}>
<ConditionallyRender
condition={
index > 0 &&
(strategyResult.name === 'flexibleRollout'
? index < items.length
: index < items.length - 1)
}
show={<StrategySeparator text='AND' />}
/>
{item}
</Fragment>
))}
</StyledStrategyExecutionWrapper>
);
};

View File

@ -1,65 +0,0 @@
import type { IReleasePlanMilestoneStrategy } from 'interfaces/releasePlans';
import { type DragEventHandler, type RefObject, useRef } from 'react';
import { Box, IconButton } from '@mui/material';
import Edit from '@mui/icons-material/Edit';
import Delete from '@mui/icons-material/DeleteOutlined';
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
import { MilestoneStrategyItem } from './MilestoneStrategyItem.tsx';
interface IMilestoneStrategyDraggableItemProps {
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>;
index: number;
isDragging?: boolean;
onDragStartRef: (
ref: RefObject<HTMLDivElement>,
index: number,
) => DragEventHandler<HTMLButtonElement>;
onDragOver: (
ref: RefObject<HTMLDivElement>,
index: number,
) => DragEventHandler<HTMLDivElement>;
onDragEnd: () => void;
onDeleteClick: () => void;
onEditClick: () => void;
}
export const MilestoneStrategyDraggableItem = ({
strategy,
index,
isDragging,
onDragStartRef,
onDragOver,
onDragEnd,
onDeleteClick,
onEditClick,
}: IMilestoneStrategyDraggableItemProps) => {
const ref = useRef<HTMLDivElement>(null);
return (
<Box
key={strategy.id}
ref={ref}
onDragOver={onDragOver(ref, index)}
sx={{ opacity: isDragging ? '0.5' : '1' }}
>
{index > 0 && <StrategySeparator text='OR' />}
<MilestoneStrategyItem
strategy={strategy}
onDragStart={onDragStartRef(ref, index)}
onDragEnd={onDragEnd}
actions={
<>
<IconButton title='Edit strategy' onClick={onEditClick}>
<Edit />
</IconButton>
<IconButton
title='Remove release plan'
onClick={onDeleteClick}
>
<Delete />
</IconButton>
</>
}
/>
</Box>
);
};

View File

@ -1,101 +0,0 @@
import { Box, IconButton, styled } from '@mui/material';
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
import {
formatStrategyName,
getFeatureStrategyIcon,
} from 'utils/strategyNames';
import type { IFeatureStrategy } from 'interfaces/strategy';
import { StrategyExecution } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
import type { DragEventHandler, ReactNode } from 'react';
import DragIndicator from '@mui/icons-material/DragIndicator';
const StyledStrategy = styled('div')(({ theme }) => ({
background: theme.palette.background.paper,
}));
const DragIcon = styled(IconButton)({
padding: 0,
cursor: 'inherit',
transition: 'color 0.2s ease-in-out',
});
const StyledHeader = styled('div', {
shouldForwardProp: (prop) => prop !== 'draggable',
})<{ draggable: boolean }>(({ theme, draggable }) => ({
display: 'flex',
padding: theme.spacing(2),
gap: theme.spacing(1),
alignItems: 'center',
color: theme.palette.text.primary,
'& > svg': {
fill: theme.palette.action.disabled,
},
borderBottom: `1px solid ${theme.palette.divider}`,
}));
const StyledStrategyExecution = styled('div')(({ theme }) => ({
padding: theme.spacing(2),
}));
interface IReleasePlanMilestoneStrategyProps {
strategy: IFeatureStrategy;
onDragStart?: DragEventHandler<HTMLButtonElement>;
onDragEnd?: DragEventHandler<HTMLButtonElement>;
actions?: ReactNode;
}
export const MilestoneStrategyItem = ({
strategy,
onDragStart,
onDragEnd,
actions,
}: IReleasePlanMilestoneStrategyProps) => {
const Icon = getFeatureStrategyIcon(strategy.strategyName);
return (
<StyledStrategy>
<StyledHeader draggable={Boolean(onDragStart)}>
<DragIcon
draggable
disableRipple
size='small'
onDragStart={onDragStart}
onDragEnd={onDragEnd}
sx={{ cursor: 'move' }}
>
<DragIndicator
titleAccess='Drag to reorder'
cursor='grab'
sx={{ color: 'action.active' }}
/>
</DragIcon>
<Icon />
{`${formatStrategyName(String(strategy.strategyName))}${strategy.title ? `: ${strategy.title}` : ''}`}
<Box
sx={{
marginLeft: 'auto',
display: 'flex',
minHeight: (theme) => theme.spacing(6),
alignItems: 'center',
}}
>
{actions}
</Box>
</StyledHeader>
<StyledStrategyExecution>
<StrategyExecution strategy={strategy} />
{strategy.variants &&
strategy.variants.length > 0 &&
(strategy.disabled ? (
<Box sx={{ opacity: '0.5' }}>
<VariantsSplitPreview
variants={strategy.variants}
/>
</Box>
) : (
<VariantsSplitPreview variants={strategy.variants} />
))}
</StyledStrategyExecution>
</StyledStrategy>
);
};