mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-27 13:49:10 +02:00
feat: update lifecycle tooltip style (#9107)
New tooltips for lifecycle indicators. - removed "timeline" lifecycle explanation - new descriptions - changed tooltip footer colors - refactored "environments" section
This commit is contained in:
parent
013ddd348d
commit
4b3b98f263
@ -54,7 +54,7 @@ test('render initial stage', async () => {
|
|||||||
await screen.findByText('Define');
|
await screen.findByText('Define');
|
||||||
await screen.findByText('2 minutes');
|
await screen.findByText('2 minutes');
|
||||||
await screen.findByText(
|
await screen.findByText(
|
||||||
'This feature flag is currently in the initial phase of its lifecycle.',
|
'Feature flag has been created, but we have not seen any metrics yet.',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ test('render completed stage with still active', async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await screen.findByText('Cleanup');
|
await screen.findByText('Cleanup');
|
||||||
await screen.findByText('production');
|
await screen.findByText(/production/);
|
||||||
await screen.findByText('2 hours ago');
|
await screen.findByText('2 hours ago');
|
||||||
expect(screen.queryByText('Archive feature')).not.toBeInTheDocument();
|
expect(screen.queryByText('Archive feature')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,6 @@ import {
|
|||||||
DELETE_FEATURE,
|
DELETE_FEATURE,
|
||||||
UPDATE_FEATURE,
|
UPDATE_FEATURE,
|
||||||
} from 'component/providers/AccessProvider/permissions';
|
} from 'component/providers/AccessProvider/permissions';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import { isSafeToArchive } from './isSafeToArchive';
|
import { isSafeToArchive } from './isSafeToArchive';
|
||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||||
import { formatDateYMDHMS } from 'utils/formatDate';
|
import { formatDateYMDHMS } from 'utils/formatDate';
|
||||||
@ -27,6 +26,7 @@ const TimeLabel = styled('span')(({ theme }) => ({
|
|||||||
|
|
||||||
const InfoText = styled('p')(({ theme }) => ({
|
const InfoText = styled('p')(({ theme }) => ({
|
||||||
paddingBottom: theme.spacing(1),
|
paddingBottom: theme.spacing(1),
|
||||||
|
color: theme.palette.text.primary,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const MainLifecycleRow = styled(Box)(({ theme }) => ({
|
const MainLifecycleRow = styled(Box)(({ theme }) => ({
|
||||||
@ -39,69 +39,21 @@ const TimeLifecycleRow = styled(Box)(({ theme }) => ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
marginBottom: theme.spacing(1.5),
|
marginBottom: theme.spacing(1.5),
|
||||||
|
gap: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const IconsRow = styled(Box)(({ theme }) => ({
|
const StyledFooter = styled('footer')(({ theme }) => ({
|
||||||
display: 'flex',
|
background: theme.palette.neutral.light,
|
||||||
alignItems: 'center',
|
|
||||||
marginTop: theme.spacing(4),
|
|
||||||
marginBottom: theme.spacing(6),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const Line = styled(Box)(({ theme }) => ({
|
|
||||||
height: '1px',
|
|
||||||
background: theme.palette.divider,
|
|
||||||
flex: 1,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StageBox = styled(Box, {
|
|
||||||
shouldForwardProp: (prop) => prop !== 'active',
|
|
||||||
})<{
|
|
||||||
active?: boolean;
|
|
||||||
}>(({ theme, active }) => ({
|
|
||||||
position: 'relative',
|
|
||||||
// speech bubble triangle for active stage
|
|
||||||
...(active && {
|
|
||||||
'&:before': {
|
|
||||||
content: '""',
|
|
||||||
position: 'absolute',
|
|
||||||
display: 'block',
|
|
||||||
borderStyle: 'solid',
|
|
||||||
borderColor: `${theme.palette.primary.light} transparent`,
|
|
||||||
borderWidth: '0 6px 6px',
|
|
||||||
top: theme.spacing(3.25),
|
|
||||||
left: theme.spacing(1.75),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
// stage name text
|
|
||||||
'&:after': {
|
|
||||||
content: 'attr(data-after-content)',
|
|
||||||
display: 'block',
|
|
||||||
position: 'absolute',
|
|
||||||
top: theme.spacing(4),
|
|
||||||
left: theme.spacing(-1.25),
|
|
||||||
right: theme.spacing(-1.25),
|
|
||||||
textAlign: 'center',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
fontSize: theme.spacing(1.25),
|
|
||||||
padding: theme.spacing(0.25, 0),
|
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
// active wrapper for stage name text
|
|
||||||
...(active && {
|
|
||||||
backgroundColor: theme.palette.primary.light,
|
|
||||||
color: theme.palette.primary.contrastText,
|
|
||||||
fontWeight: theme.typography.fontWeightBold,
|
|
||||||
borderRadius: theme.spacing(0.5),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const ColorFill = styled(Box)(({ theme }) => ({
|
|
||||||
backgroundColor: theme.palette.primary.light,
|
|
||||||
color: theme.palette.primary.contrastText,
|
|
||||||
borderRadius: `0 0 ${theme.shape.borderRadiusMedium}px ${theme.shape.borderRadiusMedium}px`, // has to match the parent tooltip container
|
borderRadius: `0 0 ${theme.shape.borderRadiusMedium}px ${theme.shape.borderRadiusMedium}px`, // has to match the parent tooltip container
|
||||||
margin: theme.spacing(-1, -1.5), // has to match the parent tooltip container
|
margin: theme.spacing(-1, -1.5), // has to match the parent tooltip container
|
||||||
padding: theme.spacing(2, 3),
|
padding: theme.spacing(2, 3.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledEnvironmentUsageIcon = styled(StyledIconWrapper)(({ theme }) => ({
|
||||||
|
width: theme.spacing(2),
|
||||||
|
height: theme.spacing(2),
|
||||||
|
marginRight: theme.spacing(0.75),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const LastSeenIcon: FC<{
|
const LastSeenIcon: FC<{
|
||||||
@ -111,76 +63,9 @@ const LastSeenIcon: FC<{
|
|||||||
const { text, background } = getColor(lastSeen);
|
const { text, background } = getColor(lastSeen);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledIconWrapper style={{ background }}>
|
<StyledEnvironmentUsageIcon style={{ background }}>
|
||||||
<UsageRate stroke={text} />
|
<UsageRate stroke={text} />
|
||||||
</StyledIconWrapper>
|
</StyledEnvironmentUsageIcon>
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const InitialStageDescription: FC = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<InfoText>
|
|
||||||
This feature flag is currently in the initial phase of its
|
|
||||||
lifecycle.
|
|
||||||
</InfoText>
|
|
||||||
<InfoText>
|
|
||||||
This means that the flag has been created, but it has not yet
|
|
||||||
been seen in any environment.
|
|
||||||
</InfoText>
|
|
||||||
<InfoText>
|
|
||||||
Once we detect metrics for a non-production environment it will
|
|
||||||
move into pre-live.
|
|
||||||
</InfoText>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const StageTimeline: FC<{
|
|
||||||
stage: LifecycleStage;
|
|
||||||
}> = ({ stage }) => {
|
|
||||||
return (
|
|
||||||
<IconsRow>
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Initial'
|
|
||||||
active={stage.name === 'initial'}
|
|
||||||
>
|
|
||||||
<FeatureLifecycleStageIcon stage={{ name: 'initial' }} />
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Pre-live'
|
|
||||||
active={stage.name === 'pre-live'}
|
|
||||||
>
|
|
||||||
<FeatureLifecycleStageIcon stage={{ name: 'pre-live' }} />
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox data-after-content='Live' active={stage.name === 'live'}>
|
|
||||||
<FeatureLifecycleStageIcon stage={{ name: 'live' }} />
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Completed'
|
|
||||||
active={stage.name === 'completed'}
|
|
||||||
>
|
|
||||||
<FeatureLifecycleStageIcon stage={{ name: 'completed' }} />
|
|
||||||
</StageBox>
|
|
||||||
|
|
||||||
<Line />
|
|
||||||
|
|
||||||
<StageBox
|
|
||||||
data-after-content='Archived'
|
|
||||||
active={stage.name === 'archived'}
|
|
||||||
>
|
|
||||||
<FeatureLifecycleStageIcon stage={{ name: 'archived' }} />
|
|
||||||
</StageBox>
|
|
||||||
</IconsRow>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -189,13 +74,30 @@ const EnvironmentLine = styled(Box)(({ theme }) => ({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
marginTop: theme.spacing(1),
|
marginTop: theme.spacing(1),
|
||||||
marginBottom: theme.spacing(2),
|
marginBottom: theme.spacing(1),
|
||||||
|
marginLeft: theme.spacing(3.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledEnvironmentsTitle = styled(Box)(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: theme.spacing(1),
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledEnvironmentIcon = styled(CloudCircle)(({ theme }) => ({
|
||||||
|
color: theme.palette.primary.main,
|
||||||
|
width: theme.spacing(2.5),
|
||||||
|
display: 'block',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const CenteredBox = styled(Box)(({ theme }) => ({
|
const CenteredBox = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: theme.spacing(1),
|
}));
|
||||||
|
|
||||||
|
const StyledStageAction = styled(Box)(({ theme }) => ({
|
||||||
|
marginTop: theme.spacing(2),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const Environments: FC<{
|
const Environments: FC<{
|
||||||
@ -210,12 +112,11 @@ const Environments: FC<{
|
|||||||
return (
|
return (
|
||||||
<EnvironmentLine key={environment.name}>
|
<EnvironmentLine key={environment.name}>
|
||||||
<CenteredBox>
|
<CenteredBox>
|
||||||
<CloudCircle />
|
|
||||||
<Box>{environment.name}</Box>
|
<Box>{environment.name}</Box>
|
||||||
</CenteredBox>
|
</CenteredBox>
|
||||||
<CenteredBox>
|
<CenteredBox>
|
||||||
<TimeAgo date={environment.lastSeenAt} />
|
|
||||||
<LastSeenIcon lastSeen={environment.lastSeenAt} />
|
<LastSeenIcon lastSeen={environment.lastSeenAt} />
|
||||||
|
<TimeAgo date={environment.lastSeenAt} />
|
||||||
</CenteredBox>
|
</CenteredBox>
|
||||||
</EnvironmentLine>
|
</EnvironmentLine>
|
||||||
);
|
);
|
||||||
@ -224,54 +125,32 @@ const Environments: FC<{
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PreLiveStageDescription: FC<{ children?: React.ReactNode }> = ({
|
const StyledStageActionTitle = styled(Typography)(({ theme }) => ({
|
||||||
children,
|
paddingTop: theme.spacing(0.5),
|
||||||
}) => {
|
marginBottom: theme.spacing(0.5),
|
||||||
return (
|
color: theme.palette.text.primary,
|
||||||
<>
|
fontSize: theme.fontSizes.smallerBody,
|
||||||
<InfoText>
|
|
||||||
We've seen the feature flag in the following environments:
|
|
||||||
</InfoText>
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ArchivedStageDescription = () => {
|
|
||||||
return (
|
|
||||||
<InfoText>
|
|
||||||
Your feature has been archived, it is now safe to delete it.
|
|
||||||
</InfoText>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const BoldTitle = styled(Typography)(({ theme }) => ({
|
|
||||||
marginTop: theme.spacing(1),
|
|
||||||
marginBottom: theme.spacing(1),
|
|
||||||
fontSize: theme.typography.body2.fontSize,
|
|
||||||
fontWeight: theme.typography.fontWeightBold,
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const LiveStageDescription: FC<{
|
const LiveStageAction: FC<{
|
||||||
onComplete: () => void;
|
onComplete: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
project: string;
|
project: string;
|
||||||
}> = ({ children, onComplete, loading, project }) => {
|
}> = ({ onComplete, loading, project }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<StyledStageAction>
|
||||||
<BoldTitle>Is this feature complete?</BoldTitle>
|
<StyledStageActionTitle>
|
||||||
|
Is this feature complete?
|
||||||
|
</StyledStageActionTitle>
|
||||||
<InfoText sx={{ mb: 1 }}>
|
<InfoText sx={{ mb: 1 }}>
|
||||||
Marking the feature flag as complete does not affect any
|
Marking the feature flag as complete does not affect any
|
||||||
configuration; however, it moves the feature flag to its next
|
configuration; however, it moves the flag to its next lifecycle
|
||||||
lifecycle stage and indicates that you have learned what you
|
stage and indicates that you have learned what you needed in
|
||||||
needed in order to progress with the feature. It serves as a
|
order to progress.
|
||||||
reminder to start cleaning up the feature flag and removing it
|
|
||||||
from the code.
|
|
||||||
</InfoText>
|
</InfoText>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
color='inherit'
|
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
permission={UPDATE_FEATURE}
|
permission={UPDATE_FEATURE}
|
||||||
size='small'
|
size='small'
|
||||||
@ -281,13 +160,7 @@ const LiveStageDescription: FC<{
|
|||||||
>
|
>
|
||||||
Mark completed
|
Mark completed
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<InfoText sx={{ mt: 3 }}>
|
</StyledStageAction>
|
||||||
Users have been exposed to this feature in the following
|
|
||||||
production environments:
|
|
||||||
</InfoText>
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -298,27 +171,22 @@ const SafeToArchive: FC<{
|
|||||||
project: string;
|
project: string;
|
||||||
}> = ({ onArchive, onUncomplete, loading, project }) => {
|
}> = ({ onArchive, onUncomplete, loading, project }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<StyledStageAction>
|
||||||
<BoldTitle>Safe to archive</BoldTitle>
|
<StyledStageActionTitle>Safe to archive</StyledStageActionTitle>
|
||||||
<InfoText
|
<InfoText>
|
||||||
sx={{
|
|
||||||
mt: 2,
|
|
||||||
mb: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
We haven’t seen this feature flag in any environment for at
|
We haven’t seen this feature flag in any environment for at
|
||||||
least two days. It’s likely that it’s safe to archive this flag.
|
least two days. It’s likely that it’s safe to archive this flag.
|
||||||
</InfoText>
|
</InfoText>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={(theme) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
gap: 2,
|
gap: theme.spacing(2),
|
||||||
}}
|
marginTop: theme.spacing(1),
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
color='inherit'
|
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
permission={UPDATE_FEATURE}
|
permission={UPDATE_FEATURE}
|
||||||
size='small'
|
size='small'
|
||||||
@ -329,7 +197,6 @@ const SafeToArchive: FC<{
|
|||||||
Revert to live
|
Revert to live
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
color='inherit'
|
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
permission={DELETE_FEATURE}
|
permission={DELETE_FEATURE}
|
||||||
size='small'
|
size='small'
|
||||||
@ -340,38 +207,25 @@ const SafeToArchive: FC<{
|
|||||||
Archive feature
|
Archive feature
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</StyledStageAction>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ActivelyUsed: FC<{
|
const ActivelyUsed: FC<{
|
||||||
onUncomplete: () => void;
|
onUncomplete: () => void;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
children?: React.ReactNode;
|
}> = ({ onUncomplete, loading }) => (
|
||||||
}> = ({ children, onUncomplete, loading }) => (
|
<StyledStageAction>
|
||||||
<>
|
<InfoText>
|
||||||
<InfoText
|
|
||||||
sx={{
|
|
||||||
mt: 1,
|
|
||||||
mb: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
This feature has been successfully completed, but we are still
|
This feature has been successfully completed, but we are still
|
||||||
seeing usage. Clean up the feature flag from your code before
|
seeing usage. Clean up the feature flag from your code before
|
||||||
archiving it:
|
archiving it.
|
||||||
</InfoText>
|
</InfoText>
|
||||||
{children}
|
<InfoText>
|
||||||
<InfoText
|
|
||||||
sx={{
|
|
||||||
mt: 1,
|
|
||||||
mb: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
If you think this feature was completed too early you can revert to
|
If you think this feature was completed too early you can revert to
|
||||||
the live stage:
|
the live stage.
|
||||||
</InfoText>
|
</InfoText>
|
||||||
<PermissionButton
|
<PermissionButton
|
||||||
color='inherit'
|
|
||||||
variant='outlined'
|
variant='outlined'
|
||||||
permission={UPDATE_FEATURE}
|
permission={UPDATE_FEATURE}
|
||||||
size='small'
|
size='small'
|
||||||
@ -381,7 +235,7 @@ const ActivelyUsed: FC<{
|
|||||||
>
|
>
|
||||||
Revert to live
|
Revert to live
|
||||||
</PermissionButton>
|
</PermissionButton>
|
||||||
</>
|
</StyledStageAction>
|
||||||
);
|
);
|
||||||
|
|
||||||
const CompletedStageDescription: FC<{
|
const CompletedStageDescription: FC<{
|
||||||
@ -392,34 +246,20 @@ const CompletedStageDescription: FC<{
|
|||||||
name: string;
|
name: string;
|
||||||
lastSeenAt: string;
|
lastSeenAt: string;
|
||||||
}>;
|
}>;
|
||||||
children?: React.ReactNode;
|
|
||||||
project: string;
|
project: string;
|
||||||
}> = ({
|
}> = ({ environments, onArchive, onUncomplete, loading, project }) => {
|
||||||
children,
|
if (isSafeToArchive(environments)) {
|
||||||
environments,
|
|
||||||
onArchive,
|
|
||||||
onUncomplete,
|
|
||||||
loading,
|
|
||||||
project,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<ConditionallyRender
|
|
||||||
condition={isSafeToArchive(environments)}
|
|
||||||
show={
|
|
||||||
<SafeToArchive
|
<SafeToArchive
|
||||||
onArchive={onArchive}
|
onArchive={onArchive}
|
||||||
onUncomplete={onUncomplete}
|
onUncomplete={onUncomplete}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
project={project}
|
project={project}
|
||||||
/>
|
/>
|
||||||
}
|
|
||||||
elseShow={
|
|
||||||
<ActivelyUsed onUncomplete={onUncomplete} loading={loading}>
|
|
||||||
{children}
|
|
||||||
</ActivelyUsed>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ActivelyUsed onUncomplete={onUncomplete} loading={loading} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FormatTime: FC<{
|
const FormatTime: FC<{
|
||||||
@ -438,6 +278,72 @@ const FormatElapsedTime: FC<{
|
|||||||
return <span>{elapsedTime}</span>;
|
return <span>{elapsedTime}</span>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const StageInfo: FC<{ stage: LifecycleStage['name'] }> = ({ stage }) => {
|
||||||
|
if (stage === 'initial') {
|
||||||
|
return (
|
||||||
|
<InfoText>
|
||||||
|
Feature flag has been created, but we have not seen any metrics
|
||||||
|
yet.
|
||||||
|
</InfoText>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (stage === 'pre-live') {
|
||||||
|
return (
|
||||||
|
<InfoText>
|
||||||
|
Feature is being developed and tested in controlled
|
||||||
|
environments.
|
||||||
|
</InfoText>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (stage === 'live') {
|
||||||
|
return (
|
||||||
|
<InfoText>
|
||||||
|
Feature is being rolled out in production according to an
|
||||||
|
activation strategy.
|
||||||
|
</InfoText>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (stage === 'completed') {
|
||||||
|
return (
|
||||||
|
<InfoText>
|
||||||
|
When a flag is no longer needed, clean up the code to minimize
|
||||||
|
technical debt and archive the flag for future reference.
|
||||||
|
</InfoText>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (stage === 'archived') {
|
||||||
|
return (
|
||||||
|
<InfoText>
|
||||||
|
Flag is archived in Unleash for future reference.
|
||||||
|
</InfoText>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EnvironmentsInfo: FC<{
|
||||||
|
stage: {
|
||||||
|
name: LifecycleStage['name'];
|
||||||
|
environments?: Array<{
|
||||||
|
name: string;
|
||||||
|
lastSeenAt: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
}> = ({ stage }) => (
|
||||||
|
<>
|
||||||
|
<StyledEnvironmentsTitle>
|
||||||
|
<StyledEnvironmentIcon />{' '}
|
||||||
|
{stage.environments && stage.environments.length > 0
|
||||||
|
? `Seen in environment${stage.environments.length > 1 ? 's' : ''}`
|
||||||
|
: 'Not seen in any environments'}
|
||||||
|
</StyledEnvironmentsTitle>
|
||||||
|
{stage.environments && stage.environments.length > 0 ? (
|
||||||
|
<Environments environments={stage.environments!} />
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
export const FeatureLifecycleTooltip: FC<{
|
export const FeatureLifecycleTooltip: FC<{
|
||||||
children: React.ReactElement<any, any>;
|
children: React.ReactElement<any, any>;
|
||||||
stage: LifecycleStage;
|
stage: LifecycleStage;
|
||||||
@ -478,9 +384,11 @@ export const FeatureLifecycleTooltip: FC<{
|
|||||||
<FeatureLifecycleStageIcon stage={stage} />
|
<FeatureLifecycleStageIcon stage={stage} />
|
||||||
</Box>
|
</Box>
|
||||||
</MainLifecycleRow>
|
</MainLifecycleRow>
|
||||||
|
|
||||||
|
<StageInfo stage={stage.name} />
|
||||||
|
|
||||||
<TimeLifecycleRow>
|
<TimeLifecycleRow>
|
||||||
<TimeLabel>Stage entered at</TimeLabel>
|
<TimeLabel>Stage entered at</TimeLabel>
|
||||||
|
|
||||||
<FormatTime time={stage.enteredStageAt} />
|
<FormatTime time={stage.enteredStageAt} />
|
||||||
</TimeLifecycleRow>
|
</TimeLifecycleRow>
|
||||||
<TimeLifecycleRow>
|
<TimeLifecycleRow>
|
||||||
@ -488,35 +396,31 @@ export const FeatureLifecycleTooltip: FC<{
|
|||||||
<FormatElapsedTime time={stage.enteredStageAt} />
|
<FormatElapsedTime time={stage.enteredStageAt} />
|
||||||
</TimeLifecycleRow>
|
</TimeLifecycleRow>
|
||||||
</Box>
|
</Box>
|
||||||
<ColorFill>
|
{stage.name !== 'archived' ? (
|
||||||
{stage.name === 'initial' && <InitialStageDescription />}
|
<StyledFooter>
|
||||||
{stage.name === 'pre-live' && (
|
<EnvironmentsInfo stage={stage} />
|
||||||
<PreLiveStageDescription>
|
|
||||||
<Environments environments={stage.environments} />
|
|
||||||
</PreLiveStageDescription>
|
|
||||||
)}
|
|
||||||
{stage.name === 'live' && (
|
{stage.name === 'live' && (
|
||||||
<LiveStageDescription
|
<LiveStageAction
|
||||||
onComplete={onComplete}
|
onComplete={onComplete}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
project={project}
|
project={project}
|
||||||
>
|
>
|
||||||
<Environments environments={stage.environments} />
|
<Environments
|
||||||
</LiveStageDescription>
|
environments={stage.environments!}
|
||||||
|
/>
|
||||||
|
</LiveStageAction>
|
||||||
)}
|
)}
|
||||||
{stage.name === 'completed' && (
|
{stage.name === 'completed' && (
|
||||||
<CompletedStageDescription
|
<CompletedStageDescription
|
||||||
environments={stage.environments}
|
environments={stage.environments!}
|
||||||
onArchive={onArchive}
|
onArchive={onArchive}
|
||||||
onUncomplete={onUncomplete}
|
onUncomplete={onUncomplete}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
project={project}
|
project={project}
|
||||||
>
|
/>
|
||||||
<Environments environments={stage.environments} />
|
|
||||||
</CompletedStageDescription>
|
|
||||||
)}
|
)}
|
||||||
{stage.name === 'archived' && <ArchivedStageDescription />}
|
</StyledFooter>
|
||||||
</ColorFill>
|
) : null}
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user