1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-06-18 01:18:23 +02:00

feat: update strategy window styles, extract old and new components (#9730)

This commit is contained in:
Jaanus Sellin 2025-04-09 13:41:18 +03:00 committed by GitHub
parent a92c79e2dd
commit 02aadfe1bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 304 additions and 69 deletions

View File

@ -12,7 +12,7 @@ import { useChangeRequestAddStrategy } from 'hooks/useChangeRequestAddStrategy';
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
import { CopyStrategiesMessage } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/CopyStrategiesMessage';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { FeatureStrategyMenu } from '../FeatureStrategyMenu/FeatureStrategyMenu';
import { FeatureStrategyMenuWrapper } from '../FeatureStrategyMenu/FeatureStrategyMenu';
interface IFeatureStrategyEmptyProps {
projectId: string;
@ -161,7 +161,7 @@ export const FeatureStrategyEmpty = ({
justifyContent: 'center',
}}
>
<FeatureStrategyMenu
<FeatureStrategyMenuWrapper
label='Add your first strategy'
projectId={projectId}
featureId={featureId}

View File

@ -5,7 +5,7 @@ import PermissionButton, {
type IPermissionButtonProps,
} from 'component/common/PermissionButton/PermissionButton';
import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
import { Popover, styled } from '@mui/material';
import { Dialog, styled } from '@mui/material';
import { FeatureStrategyMenuCards } from './FeatureStrategyMenuCards/FeatureStrategyMenuCards';
import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate';
import MoreVert from '@mui/icons-material/MoreVert';
@ -20,9 +20,8 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { OldFeatureStrategyMenuCards } from './FeatureStrategyMenuCards/OldFeatureStrategyMenuCards';
import { ReleasePlanReviewDialog } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanReviewDialog';
import { ReleasePlanAddDialog } from '../../FeatureView/FeatureOverview/ReleasePlan/ReleasePlanAddDialog';
import { OldFeatureStrategyMenu } from './OldFeatureStrategyMenu';
interface IFeatureStrategyMenuProps {
label: string;
@ -49,6 +48,18 @@ const StyledAdditionalMenuButton = styled(PermissionButton)(({ theme }) => ({
paddingBlock: 0,
}));
export const FeatureStrategyMenuWrapper = (
props: IFeatureStrategyMenuProps,
) => {
const newStrategyDropdownEnabled = useUiFlag('newStrategyDropdown');
if (newStrategyDropdownEnabled) {
return <FeatureStrategyMenu {...props} />;
}
return <OldFeatureStrategyMenu {...props} />;
};
export const FeatureStrategyMenu = ({
label,
projectId,
@ -77,7 +88,6 @@ export const FeatureStrategyMenu = ({
const { addReleasePlanToFeature } = useReleasePlansApi();
const { isOss } = useUiConfig();
const releasePlansEnabled = useUiFlag('releasePlans');
const newStrategyDropdownEnabled = useUiFlag('newStrategyDropdown');
const displayReleasePlanButton = !isOss() && releasePlansEnabled;
const crProtected =
releasePlansEnabled && isChangeRequestConfigured(environmentId);
@ -211,22 +221,17 @@ export const FeatureStrategyMenu = ({
>
<MoreVert />
</StyledAdditionalMenuButton>
<Popover
id={popoverId}
<Dialog
open={isPopoverOpen}
onClose={onClose}
onClick={onClose}
disableScrollLock={true}
sx={{
'& .MuiPopover-paper': {
position: 'fixed',
top: '50% !important',
left: '50% !important',
transform: 'translate(-50%, -50%) !important',
maxWidth='md'
PaperProps={{
sx: {
borderRadius: (theme) => '12px',
},
}}
>
{newStrategyDropdownEnabled ? (
{
<FeatureStrategyMenuCards
projectId={projectId}
featureId={featureId}
@ -242,21 +247,9 @@ export const FeatureStrategyMenu = ({
}}
onClose={onClose}
/>
) : (
<OldFeatureStrategyMenuCards
projectId={projectId}
featureId={featureId}
environmentId={environmentId}
onlyReleasePlans={onlyReleasePlans}
onAddReleasePlan={(template) => {
setSelectedTemplate(template);
setAddReleasePlanOpen(true);
}}
/>
)}
</Popover>
{selectedTemplate &&
(newStrategyDropdownEnabled ? (
}
</Dialog>
{selectedTemplate && (
<ReleasePlanReviewDialog
open={addReleasePlanOpen}
setOpen={setAddReleasePlanOpen}
@ -269,19 +262,7 @@ export const FeatureStrategyMenu = ({
environment={environmentId}
crProtected={crProtected}
/>
) : (
<ReleasePlanAddDialog
open={addReleasePlanOpen}
setOpen={setAddReleasePlanOpen}
onConfirm={() => {
addReleasePlan(selectedTemplate);
}}
template={selectedTemplate}
projectId={projectId}
featureName={featureId}
environment={environmentId}
/>
))}
)}
</StyledStrategyMenu>
);
};

View File

@ -29,7 +29,7 @@ interface IFeatureStrategyMenuCardsProps {
const StyledTypography = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
padding: theme.spacing(1, 2),
padding: theme.spacing(1, 4),
width: '100%',
}));
@ -48,14 +48,14 @@ const ScrollableContent = styled(Box)(({ theme }) => ({
width: '100%',
maxHeight: '70vh',
overflowY: 'auto',
padding: theme.spacing(0, 0, 1, 0),
padding: theme.spacing(1, 0, 1, 0),
}));
const GridSection = styled(Box)(({ theme }) => ({
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: theme.spacing(1.5),
padding: theme.spacing(0, 2),
padding: theme.spacing(0, 4),
marginBottom: theme.spacing(3),
width: '100%',
}));
@ -69,7 +69,7 @@ const TitleRow = styled(Box)(({ theme }) => ({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: theme.spacing(1, 2),
padding: theme.spacing(4, 4, 2, 4),
}));
const TitleText = styled(Typography)(({ theme }) => ({
@ -82,7 +82,7 @@ const SectionTitle = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(0.5),
padding: theme.spacing(1, 2),
padding: theme.spacing(0, 4, 1, 4),
width: '100%',
}));
@ -111,7 +111,7 @@ const EmptyStateContainer = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.neutral.light,
borderRadius: theme.shape.borderRadiusMedium,
padding: theme.spacing(3),
margin: theme.spacing(0, 2),
margin: theme.spacing(0, 4),
width: 'auto',
}));

View File

@ -0,0 +1,254 @@
import type React from 'react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import PermissionButton, {
type IPermissionButtonProps,
} from 'component/common/PermissionButton/PermissionButton';
import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
import { Popover, styled } from '@mui/material';
import { formatCreateStrategyPath } from '../FeatureStrategyCreate/FeatureStrategyCreate';
import MoreVert from '@mui/icons-material/MoreVert';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import type { IReleasePlanTemplate } from 'interfaces/releasePlans';
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
import { usePendingChangeRequests } from 'hooks/api/getters/usePendingChangeRequests/usePendingChangeRequests';
import useToast from 'hooks/useToast';
import { ReleasePlanAddDialog } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanAddDialog';
import { useReleasePlansApi } from 'hooks/api/actions/useReleasePlansApi/useReleasePlansApi';
import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useUiFlag } from 'hooks/useUiFlag';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { OldFeatureStrategyMenuCards } from './FeatureStrategyMenuCards/OldFeatureStrategyMenuCards';
interface IFeatureStrategyMenuProps {
label: string;
projectId: string;
featureId: string;
environmentId: string;
variant?: IPermissionButtonProps['variant'];
matchWidth?: boolean;
size?: IPermissionButtonProps['size'];
disableReason?: string;
}
const StyledStrategyMenu = styled('div')(({ theme }) => ({
display: 'flex',
flexFlow: 'row',
justifyContent: 'flex-end',
gap: theme.spacing(1),
}));
const StyledAdditionalMenuButton = styled(PermissionButton)(({ theme }) => ({
minWidth: 0,
width: theme.spacing(4.5),
alignSelf: 'stretch',
paddingBlock: 0,
}));
export const OldFeatureStrategyMenu = ({
label,
projectId,
featureId,
environmentId,
variant,
size,
matchWidth,
disableReason,
}: IFeatureStrategyMenuProps) => {
const [anchor, setAnchor] = useState<Element>();
const [onlyReleasePlans, setOnlyReleasePlans] = useState<boolean>(false);
const navigate = useNavigate();
const { trackEvent } = usePlausibleTracker();
const [selectedTemplate, setSelectedTemplate] =
useState<IReleasePlanTemplate>();
const [addReleasePlanOpen, setAddReleasePlanOpen] = useState(false);
const isPopoverOpen = Boolean(anchor);
const popoverId = isPopoverOpen ? 'FeatureStrategyMenuPopover' : undefined;
const { setToastApiError, setToastData } = useToast();
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const { addChange } = useChangeRequestApi();
const { refetch: refetchChangeRequests } =
usePendingChangeRequests(projectId);
const { refetch } = useReleasePlans(projectId, featureId, environmentId);
const { addReleasePlanToFeature } = useReleasePlansApi();
const { isOss } = useUiConfig();
const releasePlansEnabled = useUiFlag('releasePlans');
const displayReleasePlanButton = !isOss() && releasePlansEnabled;
const crProtected =
releasePlansEnabled && isChangeRequestConfigured(environmentId);
const onClose = () => {
setAnchor(undefined);
};
const openDefaultStrategyCreationModal = (event: React.SyntheticEvent) => {
trackEvent('strategy-add', {
props: {
buttonTitle: label,
},
});
navigate(createStrategyPath);
};
const openMoreStrategies = (event: React.SyntheticEvent) => {
setOnlyReleasePlans(false);
setAnchor(event.currentTarget);
};
const openReleasePlans = (event: React.SyntheticEvent) => {
setOnlyReleasePlans(true);
setAnchor(event.currentTarget);
};
const addReleasePlan = async () => {
if (!selectedTemplate) return;
try {
if (crProtected) {
await addChange(projectId, environmentId, {
feature: featureId,
action: 'addReleasePlan',
payload: {
templateId: selectedTemplate.id,
},
});
setToastData({
type: 'success',
text: 'Added to draft',
});
refetchChangeRequests();
} else {
await addReleasePlanToFeature(
featureId,
selectedTemplate.id,
projectId,
environmentId,
);
setToastData({
type: 'success',
text: 'Release plan added',
});
refetch();
}
trackEvent('release-management', {
props: {
eventType: 'add-plan',
plan: selectedTemplate.name,
},
});
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
} finally {
setAddReleasePlanOpen(false);
setSelectedTemplate(undefined);
}
};
const createStrategyPath = formatCreateStrategyPath(
projectId,
featureId,
environmentId,
'flexibleRollout',
true,
);
return (
<StyledStrategyMenu onClick={(event) => event.stopPropagation()}>
{displayReleasePlanButton ? (
<PermissionButton
data-testid='ADD_TEMPLATE_BUTTON'
permission={CREATE_FEATURE_STRATEGY}
projectId={projectId}
environmentId={environmentId}
onClick={openReleasePlans}
aria-labelledby={popoverId}
variant='outlined'
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
disabled={Boolean(disableReason)}
tooltipProps={{
title: disableReason ? disableReason : undefined,
}}
>
Use template
</PermissionButton>
) : null}
<PermissionButton
data-testid='ADD_STRATEGY_BUTTON'
permission={CREATE_FEATURE_STRATEGY}
projectId={projectId}
environmentId={environmentId}
onClick={openDefaultStrategyCreationModal}
aria-labelledby={popoverId}
variant={variant}
sx={{ minWidth: matchWidth ? '282px' : 'auto' }}
disabled={Boolean(disableReason)}
tooltipProps={{
title: disableReason ? disableReason : undefined,
}}
>
{label}
</PermissionButton>
<StyledAdditionalMenuButton
permission={CREATE_FEATURE_STRATEGY}
projectId={projectId}
environmentId={environmentId}
onClick={openMoreStrategies}
variant='outlined'
hideLockIcon
disabled={Boolean(disableReason)}
tooltipProps={{
title: disableReason ? disableReason : 'More strategies',
}}
>
<MoreVert />
</StyledAdditionalMenuButton>
<Popover
id={popoverId}
open={isPopoverOpen}
anchorEl={anchor}
onClose={onClose}
onClick={onClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
PaperProps={{
sx: (theme) => ({
paddingBottom: theme.spacing(1),
}),
}}
>
<OldFeatureStrategyMenuCards
projectId={projectId}
featureId={featureId}
environmentId={environmentId}
onlyReleasePlans={onlyReleasePlans}
onAddReleasePlan={(template) => {
setSelectedTemplate(template);
setAddReleasePlanOpen(true);
}}
/>
</Popover>
{selectedTemplate && (
<ReleasePlanAddDialog
open={addReleasePlanOpen}
setOpen={setAddReleasePlanOpen}
onConfirm={addReleasePlan}
template={selectedTemplate}
projectId={projectId}
featureName={featureId}
environment={environmentId}
crProtected={crProtected}
/>
)}
</StyledStrategyMenu>
);
};

View File

@ -3,7 +3,7 @@ import type {
IFeatureEnvironment,
IFeatureEnvironmentMetrics,
} from 'interfaces/featureToggle';
import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu';
import { FeatureStrategyMenuWrapper } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu';
import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { UpgradeChangeRequests } from './UpgradeChangeRequests/UpgradeChangeRequests';
@ -103,7 +103,7 @@ export const FeatureOverviewEnvironment = ({
environment={environment}
/>
{!hasActivations ? (
<FeatureStrategyMenu
<FeatureStrategyMenuWrapper
label='Add strategy'
projectId={projectId}
featureId={featureId}
@ -129,7 +129,7 @@ export const FeatureOverviewEnvironment = ({
<Box sx={{ display: 'flex', flexDirection: 'row' }}>
<ReleaseTemplatesFeedback />
<Box ml='auto'>
<FeatureStrategyMenu
<FeatureStrategyMenuWrapper
label='Add strategy'
projectId={projectId}
featureId={featureId}

View File

@ -16,7 +16,7 @@ import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import EnvironmentAccordionBody from './EnvironmentAccordionBody/LegacyEnvironmentAccordionBody';
import { EnvironmentFooter } from './EnvironmentFooter/EnvironmentFooter';
import FeatureOverviewEnvironmentMetrics from './EnvironmentHeader/FeatureOverviewEnvironmentMetrics/LegacyFeatureOverviewEnvironmentMetrics';
import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu';
import { FeatureStrategyMenuWrapper } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu';
import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { FeatureStrategyIcons } from 'component/feature/FeatureStrategy/FeatureStrategyIcons/FeatureStrategyIcons';
@ -179,7 +179,7 @@ const FeatureOverviewEnvironment = ({
/>
</StyledHeaderTitle>
<StyledButtonContainer>
<FeatureStrategyMenu
<FeatureStrategyMenuWrapper
label='Add strategy'
projectId={projectId}
featureId={featureId}
@ -223,7 +223,7 @@ const FeatureOverviewEnvironment = ({
py: 1,
}}
>
<FeatureStrategyMenu
<FeatureStrategyMenuWrapper
label='Add strategy'
projectId={projectId}
featureId={featureId}