mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-17 01:17:29 +02:00
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 
This commit is contained in:
parent
1bd328f4e1
commit
01f3af4bda
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 60 KiB |
92
frontend/src/assets/img/releaseTemplatePreview.svg
Normal file
92
frontend/src/assets/img/releaseTemplatePreview.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 509 KiB |
@ -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: <StyledReleaseManagementIcon />,
|
||||
preview: <ReleaseManagementPreview />,
|
||||
onCheckItOut: () => navigate('/release-templates'),
|
||||
docsLink: 'https://docs.getunleash.io/reference/release-templates',
|
||||
show: isEnterprise() && releasePlansEnabled,
|
||||
beta: true,
|
||||
longDescription: (
|
||||
<>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
Once you have set it up, just apply your release plan to
|
||||
a flag, and you are ready to rollout!
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
beta: false,
|
||||
popout: true,
|
||||
},
|
||||
];
|
||||
|
||||
@ -229,6 +218,7 @@ export const NewInUnleash = ({
|
||||
preview,
|
||||
summary,
|
||||
beta = false,
|
||||
popout = false,
|
||||
}) => (
|
||||
<NewInUnleashItem
|
||||
key={label}
|
||||
@ -255,6 +245,7 @@ export const NewInUnleash = ({
|
||||
docsLink={docsLink}
|
||||
summary={summary}
|
||||
beta={beta}
|
||||
popout={popout}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
|
@ -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<typeof RouterLink | 'a'>)(({ 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,
|
||||
}) => (
|
||||
<StyledDialog open={open} onClose={onClose} maxWidth='lg' fullWidth>
|
||||
<ClickAwayListener onClickAway={onClose}>
|
||||
<DialogCard>
|
||||
<Tooltip title='Dismiss' arrow sx={{ marginLeft: 'auto' }}>
|
||||
<StyledItemButtonClose
|
||||
aria-label='dismiss'
|
||||
onClick={(e) => {
|
||||
onClose();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
size='small'
|
||||
>
|
||||
<Close fontSize='inherit' />
|
||||
</StyledItemButtonClose>
|
||||
</Tooltip>
|
||||
|
||||
<StyledPreHeader>New in Unleash</StyledPreHeader>
|
||||
|
||||
<StyledMainTitle>
|
||||
<StyledLargeHeader>{title}</StyledLargeHeader>
|
||||
<ConditionallyRender
|
||||
condition={beta}
|
||||
show={<Badge color='secondary'>Beta</Badge>}
|
||||
/>
|
||||
</StyledMainTitle>
|
||||
|
||||
<LongDescription>{longDescription}</LongDescription>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={Boolean(preview)}
|
||||
show={<CenteredPreview>{preview}</CenteredPreview>}
|
||||
/>
|
||||
|
||||
<BottomActions>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(docsLink)}
|
||||
show={
|
||||
<ReadMore>
|
||||
<StyledLink
|
||||
component='a'
|
||||
href={docsLink}
|
||||
underline='hover'
|
||||
rel='noopener noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
<StyledOpenInNew />
|
||||
Read more in our documentation
|
||||
</StyledLink>
|
||||
</ReadMore>
|
||||
}
|
||||
/>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={Boolean(onCheckItOut)}
|
||||
show={
|
||||
<StyledCheckItOutButton
|
||||
variant='contained'
|
||||
color='primary'
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
onClose();
|
||||
onCheckItOut?.();
|
||||
}}
|
||||
>
|
||||
Get Started
|
||||
</StyledCheckItOutButton>
|
||||
}
|
||||
/>
|
||||
</BottomActions>
|
||||
</DialogCard>
|
||||
</ClickAwayListener>
|
||||
</StyledDialog>
|
||||
);
|
@ -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<NewInUnleashItemDetails, 'show' | 'beta'> {
|
||||
onClick: () => void;
|
||||
@ -88,6 +49,7 @@ export const NewInUnleashItem = ({
|
||||
preview,
|
||||
summary,
|
||||
beta,
|
||||
popout,
|
||||
}: INewInUnleashItemProps) => {
|
||||
const { open, handleTooltipOpen, handleTooltipClose } = useTooltip();
|
||||
|
||||
@ -96,14 +58,35 @@ export const NewInUnleashItem = ({
|
||||
onDismiss();
|
||||
};
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
disablePadding
|
||||
onClick={() => {
|
||||
const onOpen = () => {
|
||||
onClick();
|
||||
handleTooltipOpen();
|
||||
}}
|
||||
>
|
||||
};
|
||||
|
||||
return (
|
||||
<ListItem disablePadding>
|
||||
{popout ? (
|
||||
<>
|
||||
<NewInUnleashDialog
|
||||
open={open}
|
||||
onClose={handleTooltipClose}
|
||||
title={label}
|
||||
longDescription={longDescription}
|
||||
onCheckItOut={onCheckItOut}
|
||||
docsLink={docsLink}
|
||||
preview={preview}
|
||||
beta={beta}
|
||||
/>
|
||||
<NewInUnleashSideBarItem
|
||||
label={label}
|
||||
summary={summary}
|
||||
icon={icon}
|
||||
beta={beta}
|
||||
onClick={onOpen}
|
||||
onDismiss={onDismissClick}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<NewInUnleashTooltip
|
||||
open={open}
|
||||
onClose={handleTooltipClose}
|
||||
@ -114,33 +97,16 @@ export const NewInUnleashItem = ({
|
||||
preview={preview}
|
||||
beta={beta}
|
||||
>
|
||||
<StyledItemButton>
|
||||
{icon}
|
||||
<LabelWithSummary>
|
||||
<StyledItemTitle>
|
||||
<Typography fontWeight='bold' fontSize='small'>
|
||||
<Truncator title={label} arrow>
|
||||
{label}
|
||||
</Truncator>
|
||||
</Typography>
|
||||
<ConditionallyRender
|
||||
condition={beta}
|
||||
show={<Badge color='secondary'>Beta</Badge>}
|
||||
<NewInUnleashSideBarItem
|
||||
label={label}
|
||||
summary={summary}
|
||||
icon={icon}
|
||||
beta={beta}
|
||||
onClick={onOpen}
|
||||
onDismiss={onDismissClick}
|
||||
/>
|
||||
</StyledItemTitle>
|
||||
<Typography fontSize='small'>{summary}</Typography>
|
||||
</LabelWithSummary>
|
||||
<Tooltip title='Dismiss' arrow sx={{ marginLeft: 'auto' }}>
|
||||
<StyledItemButtonClose
|
||||
aria-label='dismiss'
|
||||
onClick={onDismissClick}
|
||||
size='small'
|
||||
>
|
||||
<Close fontSize='inherit' />
|
||||
</StyledItemButtonClose>
|
||||
</Tooltip>
|
||||
</StyledItemButton>
|
||||
</NewInUnleashTooltip>
|
||||
)}
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
@ -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 (
|
||||
<StyledItemButton onClick={onClick}>
|
||||
{icon}
|
||||
<LabelWithSummary>
|
||||
<StyledItemTitle>
|
||||
<Typography fontWeight='bold' fontSize='small'>
|
||||
<Truncator title={label} arrow>
|
||||
{label}
|
||||
</Truncator>
|
||||
</Typography>
|
||||
<ConditionallyRender
|
||||
condition={beta}
|
||||
show={<Badge color='secondary'>Beta</Badge>}
|
||||
/>
|
||||
</StyledItemTitle>
|
||||
<Typography fontSize='small'>{summary}</Typography>
|
||||
</LabelWithSummary>
|
||||
<Tooltip title='Dismiss' arrow sx={{ marginLeft: 'auto' }}>
|
||||
<StyledItemButtonClose
|
||||
aria-label='dismiss'
|
||||
onClick={onDismiss}
|
||||
size='small'
|
||||
>
|
||||
<Close fontSize='inherit' />
|
||||
</StyledItemButtonClose>
|
||||
</Tooltip>
|
||||
</StyledItemButton>
|
||||
);
|
||||
};
|
@ -179,6 +179,6 @@ export const NewInUnleashTooltip: FC<{
|
||||
</ClickAwayListener>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
<div>{children}</div>
|
||||
</HtmlTooltip>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user