mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: archive feature from lifecycle (#6938)
This commit is contained in:
parent
3fb53737c6
commit
675e1a9f8b
@ -4,17 +4,19 @@ import { render } from 'utils/testRenderer';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { vi } from 'vitest';
|
||||
import type { LifecycleStage } from './LifecycleStage';
|
||||
import { DELETE_FEATURE } from 'component/providers/AccessProvider/permissions';
|
||||
|
||||
const currentTime = '2024-04-25T08:05:00.000Z';
|
||||
const twoMinutesAgo = '2024-04-25T08:03:00.000Z';
|
||||
const oneHourAgo = '2024-04-25T07:05:00.000Z';
|
||||
const twoHoursAgo = '2024-04-25T06:05:00.000Z';
|
||||
|
||||
const renderOpenTooltip = (stage: LifecycleStage) => {
|
||||
const renderOpenTooltip = (stage: LifecycleStage, onArchive = () => {}) => {
|
||||
render(
|
||||
<FeatureLifecycleTooltip stage={stage}>
|
||||
<FeatureLifecycleTooltip stage={stage} onArchive={onArchive}>
|
||||
<span>child</span>
|
||||
</FeatureLifecycleTooltip>,
|
||||
{ permissions: [{ permission: DELETE_FEATURE }] },
|
||||
);
|
||||
|
||||
const child = screen.getByText('child');
|
||||
@ -89,14 +91,25 @@ test('render completed stage with still active', async () => {
|
||||
test('render completed stage safe to archive', async () => {
|
||||
vi.setSystemTime(currentTime);
|
||||
const enteredStageAt = twoMinutesAgo;
|
||||
let onArchiveInvoked = false;
|
||||
const onArchive = () => {
|
||||
onArchiveInvoked = true;
|
||||
};
|
||||
|
||||
renderOpenTooltip({
|
||||
name: 'completed',
|
||||
status: 'kept',
|
||||
environments: [],
|
||||
enteredStageAt,
|
||||
});
|
||||
renderOpenTooltip(
|
||||
{
|
||||
name: 'completed',
|
||||
status: 'kept',
|
||||
environments: [],
|
||||
enteredStageAt,
|
||||
},
|
||||
onArchive,
|
||||
);
|
||||
|
||||
await screen.findByText('completed');
|
||||
await screen.findByText('Archive feature');
|
||||
const button = await screen.findByText('Archive feature');
|
||||
|
||||
button.click();
|
||||
|
||||
expect(onArchiveInvoked).toBe(true);
|
||||
});
|
||||
|
@ -287,7 +287,7 @@ const LiveStageDescription: FC = ({ children }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const SafeToArchive: FC = () => {
|
||||
const SafeToArchive: FC<{ onArchive: () => void }> = ({ onArchive }) => {
|
||||
return (
|
||||
<>
|
||||
<BoldTitle>Safe to archive</BoldTitle>
|
||||
@ -301,6 +301,7 @@ const SafeToArchive: FC = () => {
|
||||
permission={DELETE_FEATURE}
|
||||
size='small'
|
||||
sx={{ mb: 2 }}
|
||||
onClick={onArchive}
|
||||
>
|
||||
Archive feature
|
||||
</PermissionButton>
|
||||
@ -322,12 +323,13 @@ const ActivelyUsed: FC = ({ children }) => {
|
||||
};
|
||||
|
||||
const CompletedStageDescription: FC<{
|
||||
onArchive: () => void;
|
||||
environments: Array<{ name: string; lastSeenAt: string }>;
|
||||
}> = ({ children, environments }) => {
|
||||
}> = ({ children, environments, onArchive }) => {
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={isSafeToArchive(environments)}
|
||||
show={<SafeToArchive />}
|
||||
show={<SafeToArchive onArchive={onArchive} />}
|
||||
elseShow={<ActivelyUsed>{children}</ActivelyUsed>}
|
||||
/>
|
||||
);
|
||||
@ -348,7 +350,8 @@ const FormatElapsedTime: FC<{ time: string }> = ({ time }) => {
|
||||
export const FeatureLifecycleTooltip: FC<{
|
||||
children: React.ReactElement<any, any>;
|
||||
stage: LifecycleStage;
|
||||
}> = ({ children, stage }) => (
|
||||
onArchive: () => void;
|
||||
}> = ({ children, stage, onArchive }) => (
|
||||
<HtmlTooltip
|
||||
maxHeight={800}
|
||||
maxWidth={350}
|
||||
@ -397,6 +400,7 @@ export const FeatureLifecycleTooltip: FC<{
|
||||
{stage.name === 'completed' && (
|
||||
<CompletedStageDescription
|
||||
environments={stage.environments}
|
||||
onArchive={onArchive}
|
||||
>
|
||||
<Environments environments={stage.environments} />
|
||||
</CompletedStageDescription>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { capitalize, styled } from '@mui/material';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
|
||||
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
@ -10,6 +10,9 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { FeatureLifecycleTooltip } from '../FeatureLifecycle/FeatureLifecycleTooltip';
|
||||
import { FeatureLifecycleStageIcon } from '../FeatureLifecycle/FeatureLifecycleStageIcon';
|
||||
import { FeatureArchiveDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveDialog';
|
||||
import { useState } from 'react';
|
||||
import { FeatureArchiveNotAllowedDialog } from 'component/common/FeatureArchiveDialog/FeatureArchiveNotAllowedDialog';
|
||||
import { populateCurrentStage } from '../FeatureLifecycle/populateCurrentStage';
|
||||
|
||||
const StyledContainer = styled('div')(({ theme }) => ({
|
||||
@ -79,6 +82,8 @@ const FeatureOverviewMetaData = () => {
|
||||
const { feature } = useFeature(projectId, featureId);
|
||||
const { project, description, type } = feature;
|
||||
const featureLifecycleEnabled = useUiFlag('featureLifecycle');
|
||||
const navigate = useNavigate();
|
||||
const [showDelDialog, setShowDelDialog] = useState(false);
|
||||
|
||||
const IconComponent = getFeatureTypeIcons(type);
|
||||
|
||||
@ -108,20 +113,20 @@ const FeatureOverviewMetaData = () => {
|
||||
<span>{project}</span>
|
||||
</StyledRow>
|
||||
<ConditionallyRender
|
||||
condition={featureLifecycleEnabled}
|
||||
condition={
|
||||
featureLifecycleEnabled && Boolean(currentStage)
|
||||
}
|
||||
show={
|
||||
<StyledRow data-loading>
|
||||
<StyledLabel>Lifecycle:</StyledLabel>
|
||||
|
||||
{currentStage && (
|
||||
<FeatureLifecycleTooltip
|
||||
stage={currentStage}
|
||||
>
|
||||
<FeatureLifecycleStageIcon
|
||||
stage={currentStage}
|
||||
/>
|
||||
</FeatureLifecycleTooltip>
|
||||
)}
|
||||
<FeatureLifecycleTooltip
|
||||
stage={currentStage!}
|
||||
onArchive={() => setShowDelDialog(true)}
|
||||
>
|
||||
<FeatureLifecycleStageIcon
|
||||
stage={currentStage!}
|
||||
/>
|
||||
</FeatureLifecycleTooltip>
|
||||
</StyledRow>
|
||||
}
|
||||
/>
|
||||
@ -170,6 +175,28 @@ const FeatureOverviewMetaData = () => {
|
||||
/>
|
||||
</StyledBody>
|
||||
</StyledPaddingContainerTop>
|
||||
<ConditionallyRender
|
||||
condition={feature.children.length > 0}
|
||||
show={
|
||||
<FeatureArchiveNotAllowedDialog
|
||||
features={feature.children}
|
||||
project={projectId}
|
||||
isOpen={showDelDialog}
|
||||
onClose={() => setShowDelDialog(false)}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<FeatureArchiveDialog
|
||||
isOpen={showDelDialog}
|
||||
onConfirm={() => {
|
||||
navigate(`/projects/${projectId}`);
|
||||
}}
|
||||
onClose={() => setShowDelDialog(false)}
|
||||
projectId={projectId}
|
||||
featureIds={[featureId]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user