1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-03-23 00:16:25 +01:00

Chore(1-3449): release plans in strategy env ()

Use new design for release plans in flag environments.

- Move old ReleasePlanMilestone into Legacy file and update imports
- In the new version, use the same strategy list and item as in the
general strategy list and milestone template creation (components to be
extracted in the future)
- Fix an issue with the border being obscured by overflow by hiding
overflow


![image](https://github.com/user-attachments/assets/2258263d-aa96-4939-8af1-88236050cbd6)
This commit is contained in:
Thomas Heartman 2025-03-07 11:18:09 +01:00 committed by GitHub
parent 7db692e976
commit f6987909e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 175 additions and 17 deletions
frontend/src/component
changeRequest/ChangeRequest/Changes/Change
feature/FeatureView/FeatureOverview
FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem
ReleasePlan
releases/ReleasePlanTemplate/TemplateForm/MilestoneList

View File

@ -12,7 +12,7 @@ import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePla
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
import EventDiff from 'component/events/EventDiff/EventDiff';
import { ReleasePlan } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan';
import { ReleasePlanMilestone } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone';
import { ReleasePlanMilestone } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/LegacyReleasePlanMilestone';
import type { IReleasePlan } from 'interfaces/releasePlans';
export const ChangeItemWrapper = styled(Box)({

View File

@ -6,7 +6,7 @@ import { Box } from '@mui/material';
import { StrategyItemContainer as NewStrategyItemContainer } from 'component/common/StrategyItemContainer/StrategyItemContainer';
type StrategyItemProps = {
headerItemsRight: ReactNode;
headerItemsRight?: ReactNode;
strategy: IFeatureStrategy;
onDragStart?: DragEventHandler<HTMLButtonElement>;
onDragEnd?: DragEventHandler<HTMLButtonElement>;

View File

@ -13,7 +13,7 @@ import type {
import { useState } from 'react';
import { formatUnknownError } from 'utils/formatUnknownError';
import { ReleasePlanRemoveDialog } from './ReleasePlanRemoveDialog';
import { ReleasePlanMilestone } from './ReleasePlanMilestone/ReleasePlanMilestone';
import { ReleasePlanMilestone } from './ReleasePlanMilestone/LegacyReleasePlanMilestone';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { useUiFlag } from 'hooks/useUiFlag';

View File

@ -0,0 +1,130 @@
// deprecated; remove with `flagOverviewRedesign` flag
import ExpandMore from '@mui/icons-material/ExpandMore';
import {
Accordion,
AccordionDetails,
AccordionSummary,
styled,
} from '@mui/material';
import type { IReleasePlanMilestone } from 'interfaces/releasePlans';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ReleasePlanMilestoneStrategy } from './ReleasePlanMilestoneStrategy';
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
import {
ReleasePlanMilestoneStatus,
type MilestoneStatus,
} from './ReleasePlanMilestoneStatus';
import { useState } from 'react';
const StyledAccordion = styled(Accordion, {
shouldForwardProp: (prop) => prop !== 'status',
})<{ status: MilestoneStatus }>(({ theme, status }) => ({
border: `1px solid ${status === 'active' ? theme.palette.success.border : theme.palette.divider}`,
boxShadow: 'none',
margin: 0,
backgroundColor: theme.palette.background.paper,
'&:before': {
display: 'none',
},
}));
const StyledAccordionSummary = styled(AccordionSummary)({
'& .MuiAccordionSummary-content': {
justifyContent: 'space-between',
alignItems: 'center',
minHeight: '30px',
},
});
const StyledTitleContainer = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'start',
flexDirection: 'column',
gap: theme.spacing(0.5),
}));
const StyledTitle = styled('span')(({ theme }) => ({
fontWeight: theme.fontWeight.bold,
}));
const StyledSecondaryLabel = styled('span')(({ theme }) => ({
color: theme.palette.text.secondary,
fontSize: theme.fontSizes.smallBody,
}));
const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
borderBottomLeftRadius: theme.shape.borderRadiusLarge,
borderBottomRightRadius: theme.shape.borderRadiusLarge,
}));
interface IReleasePlanMilestoneProps {
milestone: IReleasePlanMilestone;
status?: MilestoneStatus;
onStartMilestone?: (milestone: IReleasePlanMilestone) => void;
readonly?: boolean;
}
export const ReleasePlanMilestone = ({
milestone,
status = 'not-started',
onStartMilestone,
readonly,
}: IReleasePlanMilestoneProps) => {
const [expanded, setExpanded] = useState(false);
if (!milestone.strategies.length) {
return (
<StyledAccordion status={status}>
<StyledAccordionSummary>
<StyledTitleContainer>
<StyledTitle>{milestone.name}</StyledTitle>
{!readonly && onStartMilestone && (
<ReleasePlanMilestoneStatus
status={status}
onStartMilestone={() =>
onStartMilestone(milestone)
}
/>
)}
</StyledTitleContainer>
<StyledSecondaryLabel>No strategies</StyledSecondaryLabel>
</StyledAccordionSummary>
</StyledAccordion>
);
}
return (
<StyledAccordion
status={status}
onChange={(evt, expanded) => setExpanded(expanded)}
>
<StyledAccordionSummary expandIcon={<ExpandMore />}>
<StyledTitleContainer>
<StyledTitle>{milestone.name}</StyledTitle>
{!readonly && onStartMilestone && (
<ReleasePlanMilestoneStatus
status={status}
onStartMilestone={() => onStartMilestone(milestone)}
/>
)}
</StyledTitleContainer>
<StyledSecondaryLabel>
{milestone.strategies.length === 1
? `${expanded ? 'Hide' : 'View'} strategy`
: `${expanded ? 'Hide' : 'View'} ${milestone.strategies.length} strategies`}
</StyledSecondaryLabel>
</StyledAccordionSummary>
<StyledAccordionDetails>
{milestone.strategies.map((strategy, index) => (
<div key={strategy.id}>
<ConditionallyRender
condition={index > 0}
show={<StrategySeparator text='OR' />}
/>
<ReleasePlanMilestoneStrategy strategy={strategy} />
</div>
))}
</StyledAccordionDetails>
</StyledAccordion>
);
};

View File

@ -6,19 +6,23 @@ import {
styled,
} from '@mui/material';
import type { IReleasePlanMilestone } from 'interfaces/releasePlans';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ReleasePlanMilestoneStrategy } from './ReleasePlanMilestoneStrategy';
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
import {
ReleasePlanMilestoneStatus,
type MilestoneStatus,
} from './ReleasePlanMilestoneStatus';
import { useState } from 'react';
import {
StyledContentList,
StyledListItem,
} from '../../FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
import { StrategyItem } from '../../FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem';
const StyledAccordion = styled(Accordion, {
shouldForwardProp: (prop) => prop !== 'status',
})<{ status: MilestoneStatus }>(({ theme, status }) => ({
border: `1px solid ${status === 'active' ? theme.palette.success.border : theme.palette.divider}`,
overflow: 'hidden',
boxShadow: 'none',
margin: 0,
backgroundColor: theme.palette.background.paper,
@ -52,8 +56,7 @@ const StyledSecondaryLabel = styled('span')(({ theme }) => ({
}));
const StyledAccordionDetails = styled(AccordionDetails)(({ theme }) => ({
borderBottomLeftRadius: theme.shape.borderRadiusLarge,
borderBottomRightRadius: theme.shape.borderRadiusLarge,
padding: 0,
}));
interface IReleasePlanMilestoneProps {
@ -114,15 +117,23 @@ export const ReleasePlanMilestone = ({
</StyledSecondaryLabel>
</StyledAccordionSummary>
<StyledAccordionDetails>
{milestone.strategies.map((strategy, index) => (
<div key={strategy.id}>
<ConditionallyRender
condition={index > 0}
show={<StrategySeparator text='OR' />}
/>
<ReleasePlanMilestoneStrategy strategy={strategy} />
</div>
))}
<StyledContentList>
{milestone.strategies.map((strategy, index) => (
<StyledListItem key={strategy.id}>
{index > 0 ? <StrategySeparator /> : null}
<StrategyItem
strategy={{
...strategy,
name:
strategy.name ||
strategy.strategyName ||
'',
}}
/>
</StyledListItem>
))}
</StyledContentList>
</StyledAccordionDetails>
</StyledAccordion>
);

View File

@ -1,3 +1,4 @@
// 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';

View File

@ -38,6 +38,22 @@ export const MilestoneList = ({
}
if (dragIndex !== dropIndex) {
// todo! See if there's a way to make this snippet to stabilize dragging before removing flag `flagOverviewRedesign`
// We don't have a reference to `ref` or `event` here, but maybe we can make it work? Somehow?
// const { top, bottom } = ref.current.getBoundingClientRect();
// const overTargetTop = event.clientY - top < dragItem.height;
// const overTargetBottom =
// bottom - event.clientY < dragItem.height;
// const draggingUp = dragItem.index > targetIndex;
// // prevent oscillating by only reordering if there is sufficient space
// if (
// (overTargetTop && draggingUp) ||
// (overTargetBottom && !draggingUp)
// ) {
// // reorder here
// }
const oldMilestones = milestones || [];
const newMilestones = [...oldMilestones];
const movedMilestone = newMilestones.splice(dragIndex, 1)[0];