mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
1-3083: add remaining lifecycle header + tooltip (#8722)
This PR adds a header and a tooltip to the lifecycle widget. Most of the changes in ProjectLifecycleSummary is indentation changes due to wrapping the component in another row container. Additionally, this PR touches the `HelpIcon` component because we'd like the tooltip to be wider than what we currently set as the default for the help icon. The help icon uses the html tooltip component, which has a maxWidth prop, but it does not expose that. So I've adjusted it to let you do that. Header with tooltip: 
This commit is contained in:
parent
bb0403d551
commit
b87c47d7c4
@ -1,6 +1,9 @@
|
||||
import { styled, Tooltip, type TooltipProps } from '@mui/material';
|
||||
import HelpOutline from '@mui/icons-material/HelpOutline';
|
||||
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||
import {
|
||||
HtmlTooltip,
|
||||
type IHtmlTooltipProps,
|
||||
} from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||
|
||||
const StyledContainer = styled('span')<{ size: string | undefined }>(
|
||||
({ theme, size }) => ({
|
||||
@ -24,13 +27,18 @@ const StyledContainer = styled('span')<{ size: string | undefined }>(
|
||||
}),
|
||||
);
|
||||
|
||||
interface IHelpIconProps {
|
||||
type IHelpIconProps = {
|
||||
tooltip: React.ReactNode;
|
||||
htmlTooltip?: boolean;
|
||||
placement?: TooltipProps['placement'];
|
||||
children?: React.ReactNode;
|
||||
size?: string;
|
||||
}
|
||||
} & (
|
||||
| {
|
||||
htmlTooltip: true;
|
||||
htmlTooltipMaxWidth?: IHtmlTooltipProps['maxWidth'];
|
||||
}
|
||||
| { htmlTooltip?: false }
|
||||
);
|
||||
|
||||
export const HelpIcon = ({
|
||||
tooltip,
|
||||
@ -38,10 +46,20 @@ export const HelpIcon = ({
|
||||
placement,
|
||||
children,
|
||||
size,
|
||||
...props
|
||||
}: IHelpIconProps) => {
|
||||
if (htmlTooltip) {
|
||||
const { htmlTooltipMaxWidth } = props as {
|
||||
htmlTooltipMaxWidth?: IHtmlTooltipProps['maxWidth'];
|
||||
};
|
||||
|
||||
return (
|
||||
<HtmlTooltip title={tooltip} placement={placement} arrow>
|
||||
<HtmlTooltip
|
||||
title={tooltip}
|
||||
placement={placement}
|
||||
arrow
|
||||
maxWidth={htmlTooltipMaxWidth}
|
||||
>
|
||||
<StyledContainer size={size} tabIndex={0} aria-label='Help'>
|
||||
{children ?? <HelpOutline />}
|
||||
</StyledContainer>
|
||||
|
@ -6,6 +6,24 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import type { FC } from 'react';
|
||||
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
|
||||
import type { ProjectStatusSchemaLifecycleSummary } from 'openapi';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||
|
||||
const LifecycleRow = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexFlow: 'column',
|
||||
gap: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const HeaderRow = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flex: 'auto',
|
||||
'& > *': {
|
||||
marginBlock: 0,
|
||||
fontWeight: 'normal',
|
||||
},
|
||||
}));
|
||||
|
||||
const LifecycleBox = styled('li')(({ theme }) => ({
|
||||
padding: theme.spacing(2),
|
||||
borderRadius: theme.shape.borderRadiusExtraLarge,
|
||||
@ -16,13 +34,14 @@ const LifecycleBox = styled('li')(({ theme }) => ({
|
||||
justifyContent: 'space-between',
|
||||
}));
|
||||
|
||||
const Wrapper = styled('ul')(({ theme }) => ({
|
||||
const LifecycleList = styled('ul')(({ theme }) => ({
|
||||
display: 'grid',
|
||||
listStyle: 'none',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(180px, 1fr))',
|
||||
gap: theme.spacing(1),
|
||||
justifyContent: 'center',
|
||||
padding: 0,
|
||||
flex: 'auto',
|
||||
}));
|
||||
|
||||
const Counter = styled('span')({
|
||||
@ -83,9 +102,47 @@ const BigNumber: FC<{ value?: number }> = ({ value }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const TooltipContent = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(0.5),
|
||||
}));
|
||||
|
||||
const TooltipText = styled('p')(({ theme }) => ({
|
||||
fontSize: theme.typography.body1.fontSize,
|
||||
'& + p': {
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
const LifecycleTooltip: FC = () => {
|
||||
return (
|
||||
<HelpIcon
|
||||
htmlTooltip
|
||||
htmlTooltipMaxWidth='550px'
|
||||
tooltip={
|
||||
<TooltipContent>
|
||||
<TooltipText>
|
||||
Based on usage metrics and interactions with Unleash,
|
||||
feature flags can go through five distinct lifecycle
|
||||
stages. These stages mirror the typical software
|
||||
development process and allow you to identify
|
||||
bottlenecks at any stage of the lifecycle.
|
||||
</TooltipText>
|
||||
|
||||
<TooltipText>
|
||||
<a href='https://docs.getunleash.io/reference/feature-toggles#feature-flag-lifecycle'>
|
||||
Read more in our documentation
|
||||
</a>
|
||||
</TooltipText>
|
||||
</TooltipContent>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const ProjectLifecycleSummary = () => {
|
||||
const projectId = useRequiredPathParam('projectId');
|
||||
const { data, loading } = useProjectStatus(projectId);
|
||||
const { isEnterprise } = useUiConfig();
|
||||
|
||||
const loadingRef = useLoading<HTMLUListElement>(
|
||||
loading,
|
||||
@ -99,103 +156,118 @@ export const ProjectLifecycleSummary = () => {
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Wrapper ref={loadingRef}>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={data?.lifecycleSummary.initial.currentFlags}
|
||||
/>
|
||||
<LifecycleRow>
|
||||
<HeaderRow>
|
||||
<h4>Flag lifecycle</h4>
|
||||
<LifecycleTooltip />
|
||||
</HeaderRow>
|
||||
<LifecycleList ref={loadingRef}>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={
|
||||
data?.lifecycleSummary.initial.currentFlags
|
||||
}
|
||||
/>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'initial' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('initial')} in initial</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.initial.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={data?.lifecycleSummary.preLive.currentFlags}
|
||||
/>
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'initial' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('initial')} in initial</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.initial.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={
|
||||
data?.lifecycleSummary.preLive.currentFlags
|
||||
}
|
||||
/>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'pre-live' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('preLive')} in pre-live</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.preLive.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={data?.lifecycleSummary.live.currentFlags}
|
||||
/>
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'pre-live' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('preLive')} in pre-live</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.preLive.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={data?.lifecycleSummary.live.currentFlags}
|
||||
/>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'live' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('live')} in live</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.live.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={
|
||||
data?.lifecycleSummary.completed.currentFlags
|
||||
}
|
||||
/>
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'live' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('live')} in live</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.live.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={
|
||||
data?.lifecycleSummary.completed
|
||||
.currentFlags
|
||||
}
|
||||
/>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'completed' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('completed')} in cleanup</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={data?.lifecycleSummary.completed.averageDays}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={data?.lifecycleSummary.archived.currentFlags}
|
||||
/>
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'completed' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('completed')} in cleanup</span>
|
||||
</p>
|
||||
<AverageDaysStat
|
||||
averageDays={
|
||||
data?.lifecycleSummary.completed.averageDays
|
||||
}
|
||||
/>
|
||||
</LifecycleBox>
|
||||
<LifecycleBox>
|
||||
<p>
|
||||
<Counter>
|
||||
<BigNumber
|
||||
value={
|
||||
data?.lifecycleSummary.archived.currentFlags
|
||||
}
|
||||
/>
|
||||
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'archived' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('archived')} in archived</span>
|
||||
</p>
|
||||
<Stats>
|
||||
<dt>This month</dt>
|
||||
<dd data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.archived.currentFlags ?? 0}{' '}
|
||||
flags archived
|
||||
</dd>
|
||||
</Stats>
|
||||
</LifecycleBox>
|
||||
</Wrapper>
|
||||
<FeatureLifecycleStageIcon
|
||||
aria-hidden='true'
|
||||
stage={{ name: 'archived' }}
|
||||
/>
|
||||
</Counter>
|
||||
<span>{flagWord('archived')} in archived</span>
|
||||
</p>
|
||||
<Stats>
|
||||
<dt>This month</dt>
|
||||
<dd data-loading-project-lifecycle-summary>
|
||||
{data?.lifecycleSummary.archived.currentFlags ?? 0}{' '}
|
||||
flags archived
|
||||
</dd>
|
||||
</Stats>
|
||||
</LifecycleBox>
|
||||
</LifecycleList>
|
||||
</LifecycleRow>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user