From 01f3af4bda51aa70d949b1a3e13ccff3c52d88e4 Mon Sep 17 00:00:00 2001 From: Simon Hornby Date: Thu, 27 Mar 2025 11:30:24 +0200 Subject: [PATCH] chore: whats new dialog (#9622) Adds a new dialog option for whats in new in Unleash items. This can be tiggerred by setting `popout` to true when configuring the items. To do this without setting fire to the code, I've also needed to refactor the NewInUnleash components: - NewInUnleashItem becomes a dumb item that decides if a dialog or tooltip should be rendered and controls that render state - The child item in NewInUnleashItem has been moved out into NewInUnleashSideBarItem, which feels a bit better since that is a distinct UI element from the popup - NewInUnleashDialog now exists, which is a dialog version of the popup. Meaningfully different to ask for a new component ## Screenshots ![image](https://github.com/user-attachments/assets/33d3e7f5-9178-4d2d-9355-866814e58164) --- .../assets/img/releaseManagementPreview.svg | 22 -- .../src/assets/img/releaseTemplatePreview.svg | 92 ++++++++ .../NewInUnleash/NewInUnleash.tsx | 23 +- .../NewInUnleash/NewInUnleashDialog.tsx | 222 ++++++++++++++++++ .../NewInUnleash/NewInUnleashItem.tsx | 142 +++++------ .../NewInUnleash/NewInUnleashSideBarItem.tsx | 90 +++++++ .../NewInUnleash/NewInUnleashTooltip.tsx | 2 +- 7 files changed, 466 insertions(+), 127 deletions(-) delete mode 100644 frontend/src/assets/img/releaseManagementPreview.svg create mode 100644 frontend/src/assets/img/releaseTemplatePreview.svg create mode 100644 frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashDialog.tsx create mode 100644 frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashSideBarItem.tsx diff --git a/frontend/src/assets/img/releaseManagementPreview.svg b/frontend/src/assets/img/releaseManagementPreview.svg deleted file mode 100644 index 4416fd606c..0000000000 --- a/frontend/src/assets/img/releaseManagementPreview.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/releaseTemplatePreview.svg b/frontend/src/assets/img/releaseTemplatePreview.svg new file mode 100644 index 0000000000..59980382f5 --- /dev/null +++ b/frontend/src/assets/img/releaseTemplatePreview.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleash.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleash.tsx index da6a0369d6..37f7cbb2ce 100644 --- a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleash.tsx +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleash.tsx @@ -23,7 +23,7 @@ import MonitorHeartIcon from '@mui/icons-material/MonitorHeartOutlined'; import { useNavigate } from 'react-router-dom'; import { formatAssetPath } from 'utils/formatPath'; import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined'; -import { ReactComponent as ReleaseManagementPreview } from 'assets/img/releaseManagementPreview.svg'; +import { ReactComponent as ReleaseManagementPreview } from 'assets/img/releaseTemplatePreview.svg'; const StyledNewInUnleash = styled('div')(({ theme }) => ({ margin: theme.spacing(2, 0, 1, 0), @@ -165,25 +165,14 @@ export const NewInUnleash = ({ }, { label: 'Release templates', - summary: 'Save time with release plans', + summary: 'Save time and optimize your process', icon: , preview: , onCheckItOut: () => navigate('/release-templates'), + docsLink: 'https://docs.getunleash.io/reference/release-templates', show: isEnterprise() && releasePlansEnabled, - beta: true, - longDescription: ( - <> -

- Instead of having to set up the same strategies again - and again, you can now create templates with milestones - of how you want to rollout features to your users. -

-

- Once you have set it up, just apply your release plan to - a flag, and you are ready to rollout! -

- - ), + beta: false, + popout: true, }, ]; @@ -229,6 +218,7 @@ export const NewInUnleash = ({ preview, summary, beta = false, + popout = false, }) => ( ), )} diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashDialog.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashDialog.tsx new file mode 100644 index 0000000000..d14408bb68 --- /dev/null +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashDialog.tsx @@ -0,0 +1,222 @@ +import OpenInNew from '@mui/icons-material/OpenInNew'; +import { + Badge, + Box, + Button, + ClickAwayListener, + Dialog, + IconButton, + Link, + styled, + Tooltip, + Typography, +} from '@mui/material'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import type { Link as RouterLink } from 'react-router-dom'; +import type { FC, ReactNode } from 'react'; +import Close from '@mui/icons-material/Close'; + +const StyledLargeHeader = styled(Typography)(({ theme }) => ({ + fontFamily: theme.typography.fontFamily, + fontSize: theme.typography.h1.fontSize, + fontWeight: theme.typography.fontWeightLight, + color: theme.palette.text.primary, + margin: 0, +})); + +const StyledPreHeader = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: theme.spacing(1), + color: theme.palette.neutral.main, + fontFamily: theme.typography.fontFamily, + fontSize: theme.typography.h3.fontSize, + fontWeight: theme.typography.fontWeightBold, + padding: 0, + marginBottom: theme.spacing(0.5), +})); + +const StyledMainTitle = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: theme.spacing(1), + padding: 0, + lineHeight: 1.2, +})); + +const StyledLink = styled(Link)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + padding: 0, + color: theme.palette.links, + fontWeight: theme.typography.fontWeightBold, + '&:hover, &:focus': { + textDecoration: 'underline', + }, +})); + +const StyledOpenInNew = styled(OpenInNew)(({ theme }) => ({ + fontSize: theme.spacing(2.25), +})); + +const CenteredPreview = styled(Box)(({ theme }) => ({ + padding: theme.spacing(3), + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + '> svg': { display: 'block', width: '100%', height: 'auto' }, +})); + +const LongDescription = styled(Box)(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(1.5), + ul: { + margin: 0, + paddingLeft: theme.spacing(2), + }, +})); + +const ReadMore = styled(Box)(({ theme }) => ({ + paddingTop: theme.spacing(3), + paddingBottom: theme.spacing(1), +})); + +const StyledCheckItOutButton = styled(Button)(({ theme }) => ({ + marginTop: theme.spacing(2), +})); + +const DialogCard = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.paper, + borderRadius: theme.shape.borderRadiusLarge, + padding: theme.spacing(4), + margin: theme.spacing(2), + display: 'flex', + flexDirection: 'column', + flex: 1, + boxShadow: theme.shadows[5], + maxWidth: theme.spacing(150), + width: '100%', + height: '100%', + overflow: 'auto', +})); + +const StyledDialog = styled(Dialog)(() => ({ + '& .MuiDialog-paper': { + backgroundColor: 'transparent', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + boxShadow: 'none', + overflow: 'visible', + }, +})); + +const BottomActions = styled(Box)(({ theme }) => ({ + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', + paddingRight: theme.spacing(3), + gap: theme.spacing(2), + flexWrap: 'wrap', +})); + +const StyledItemButtonClose = styled(IconButton)(({ theme }) => ({ + padding: theme.spacing(0.25), +})); + +export const NewInUnleashDialog: FC<{ + title: string; + longDescription: ReactNode; + docsLink?: string; + onCheckItOut?: () => void; + open: boolean; + preview?: ReactNode; + onClose: () => void; + beta: boolean; +}> = ({ + title, + longDescription, + onCheckItOut, + docsLink, + preview, + open, + onClose, + beta, +}) => ( + + + + + { + onClose(); + e.stopPropagation(); + }} + size='small' + > + + + + + New in Unleash + + + {title} + Beta} + /> + + + {longDescription} + + {preview}} + /> + + + + + + Read more in our documentation + + + } + /> + + { + event.stopPropagation(); + onClose(); + onCheckItOut?.(); + }} + > + Get Started + + } + /> + + + + +); diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashItem.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashItem.tsx index ce95e916ee..2abf4d1a01 100644 --- a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashItem.tsx +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashItem.tsx @@ -1,18 +1,8 @@ -import type * as React from 'react'; import { type ReactNode, useState } from 'react'; -import { - IconButton, - ListItem, - ListItemButton, - styled, - Tooltip, - Typography, -} from '@mui/material'; -import Close from '@mui/icons-material/Close'; +import { ListItem } from '@mui/material'; import { NewInUnleashTooltip } from './NewInUnleashTooltip'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -import { Badge } from 'component/common/Badge/Badge'; -import { Truncator } from 'component/common/Truncator/Truncator'; +import { NewInUnleashDialog } from './NewInUnleashDialog'; +import { NewInUnleashSideBarItem } from './NewInUnleashSideBarItem'; export type NewInUnleashItemDetails = { label: string; @@ -21,41 +11,12 @@ export type NewInUnleashItemDetails = { onCheckItOut?: () => void; docsLink?: string; show: boolean; - longDescription: ReactNode; + longDescription?: ReactNode; preview?: ReactNode; beta?: boolean; + popout?: boolean; }; -const StyledItemButton = styled(ListItemButton)(({ theme }) => ({ - outline: `1px solid ${theme.palette.divider}`, - borderRadius: theme.shape.borderRadiusMedium, - padding: theme.spacing(1), - width: '100%', - display: 'flex', - alignItems: 'start', - gap: theme.spacing(1), - fontSize: theme.fontSizes.smallBody, - '& > svg': { - width: theme.spacing(3), - height: theme.spacing(3), - }, -})); - -const LabelWithSummary = styled('div')(({ theme }) => ({ - flex: 1, -})); - -const StyledItemTitle = styled('div')(({ theme }) => ({ - display: 'flex', - gap: theme.spacing(1), - alignItems: 'center', - height: theme.spacing(3), -})); - -const StyledItemButtonClose = styled(IconButton)(({ theme }) => ({ - padding: theme.spacing(0.25), -})); - interface INewInUnleashItemProps extends Omit { onClick: () => void; @@ -88,6 +49,7 @@ export const NewInUnleashItem = ({ preview, summary, beta, + popout, }: INewInUnleashItemProps) => { const { open, handleTooltipOpen, handleTooltipClose } = useTooltip(); @@ -96,51 +58,55 @@ export const NewInUnleashItem = ({ onDismiss(); }; + const onOpen = () => { + onClick(); + handleTooltipOpen(); + }; + return ( - { - onClick(); - handleTooltipOpen(); - }} - > - - - {icon} - - - - - {label} - - - Beta} - /> - - {summary} - - - - - - - - + + {popout ? ( + <> + + + + ) : ( + + + + )} ); }; diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashSideBarItem.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashSideBarItem.tsx new file mode 100644 index 0000000000..95de0bb6b4 --- /dev/null +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashSideBarItem.tsx @@ -0,0 +1,90 @@ +import type * as React from 'react'; +import type { ReactNode } from 'react'; +import { + IconButton, + ListItemButton, + styled, + Tooltip, + Typography, +} from '@mui/material'; +import Close from '@mui/icons-material/Close'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import { Badge } from 'component/common/Badge/Badge'; +import { Truncator } from 'component/common/Truncator/Truncator'; + +const StyledItemButton = styled(ListItemButton)(({ theme }) => ({ + outline: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadiusMedium, + padding: theme.spacing(1), + width: '100%', + display: 'flex', + alignItems: 'start', + gap: theme.spacing(1), + fontSize: theme.fontSizes.smallBody, + '& > svg': { + width: theme.spacing(3), + height: theme.spacing(3), + }, +})); + +const LabelWithSummary = styled('div')(({ theme }) => ({ + flex: 1, +})); + +const StyledItemButtonClose = styled(IconButton)(({ theme }) => ({ + padding: theme.spacing(0.25), +})); + +const StyledItemTitle = styled('div')(({ theme }) => ({ + display: 'flex', + gap: theme.spacing(1), + alignItems: 'center', + height: theme.spacing(3), +})); + +interface NewInUnleashSideBarItemProps { + label: string; + summary: string; + icon: ReactNode; + beta?: boolean; + onDismiss: (e: React.MouseEvent) => void; + onClick: (e: React.MouseEvent) => void; +} + +export const NewInUnleashSideBarItem = ({ + icon, + label, + summary, + beta = false, + onDismiss, + onClick, +}: NewInUnleashSideBarItemProps) => { + return ( + + {icon} + + + + + {label} + + + Beta} + /> + + {summary} + + + + + + + + ); +}; diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashTooltip.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashTooltip.tsx index 9233ece2ce..f2e7523cc1 100644 --- a/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashTooltip.tsx +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleashTooltip.tsx @@ -179,6 +179,6 @@ export const NewInUnleashTooltip: FC<{ } > - {children} +
{children}
);