From b87c47d7c4f8de0dee4dc07763857ec5e8522286 Mon Sep 17 00:00:00 2001
From: Thomas Heartman
Date: Wed, 13 Nov 2024 10:16:35 +0100
Subject: [PATCH] 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:

---
.../component/common/HelpIcon/HelpIcon.tsx | 28 +-
.../ProjectStatus/ProjectLifecycleSummary.tsx | 260 +++++++++++-------
2 files changed, 189 insertions(+), 99 deletions(-)
diff --git a/frontend/src/component/common/HelpIcon/HelpIcon.tsx b/frontend/src/component/common/HelpIcon/HelpIcon.tsx
index 1c54473b76..70177f828a 100644
--- a/frontend/src/component/common/HelpIcon/HelpIcon.tsx
+++ b/frontend/src/component/common/HelpIcon/HelpIcon.tsx
@@ -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 (
-
+
{children ?? }
diff --git a/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx b/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx
index ecf58ea9b1..89a122a053 100644
--- a/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx
+++ b/frontend/src/component/project/Project/ProjectStatus/ProjectLifecycleSummary.tsx
@@ -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 (
+
+
+ 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.
+
+
+
+
+ Read more in our documentation
+
+
+
+ }
+ />
+ );
+};
+
export const ProjectLifecycleSummary = () => {
const projectId = useRequiredPathParam('projectId');
const { data, loading } = useProjectStatus(projectId);
+ const { isEnterprise } = useUiConfig();
const loadingRef = useLoading(
loading,
@@ -99,103 +156,118 @@ export const ProjectLifecycleSummary = () => {
}
};
return (
-
-
-
-
-
+
+
+ Flag lifecycle
+
+
+
+
+
+
+
-
-
- {flagWord('initial')} in initial
-
-
-
-
-
-
-
+
+
+ {flagWord('initial')} in initial
+
+
+
+
+
+
+
-
-
- {flagWord('preLive')} in pre-live
-
-
-
-
-
-
-
+
+
+ {flagWord('preLive')} in pre-live
+
+
+
+
+
+
+
-
-
- {flagWord('live')} in live
-
-
-
-
-
-
-
+
+
+ {flagWord('live')} in live
+
+
+
+
+
+
+
-
-
- {flagWord('completed')} in cleanup
-
-
-
-
-
-
-
+
+
+ {flagWord('completed')} in cleanup
+
+
+
+
+
+
+
-
-
- {flagWord('archived')} in archived
-
-
- This month
-
- {data?.lifecycleSummary.archived.currentFlags ?? 0}{' '}
- flags archived
-
-
-
-
+
+
+ {flagWord('archived')} in archived
+
+
+ This month
+
+ {data?.lifecycleSummary.archived.currentFlags ?? 0}{' '}
+ flags archived
+
+
+
+
+
);
};