1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: new in unleash tooltip (#7991)

This commit is contained in:
Mateusz Kwasniewski 2024-08-27 15:47:55 +02:00 committed by GitHub
parent 742504793c
commit c69036c5a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 245 additions and 48 deletions

View File

@ -105,7 +105,7 @@ const StageBox = styled(Box, {
const ColorFill = styled(Box)(({ theme }) => ({ const ColorFill = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.primary.light, backgroundColor: theme.palette.primary.light,
color: theme.palette.primary.contrastText, color: theme.palette.primary.contrastText,
borderRadius: theme.spacing(0, 0, 1, 1), // 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),
})); }));

View File

@ -12,7 +12,6 @@ import {
styled, styled,
} from '@mui/material'; } from '@mui/material';
import Signals from '@mui/icons-material/Sensors'; import Signals from '@mui/icons-material/Sensors';
import { useNavigate } from 'react-router-dom';
import type { NavigationMode } from 'component/layout/MainLayout/NavigationSidebar/NavigationMode'; import type { NavigationMode } from 'component/layout/MainLayout/NavigationSidebar/NavigationMode';
import { NewInUnleashItem } from './NewInUnleashItem'; import { NewInUnleashItem } from './NewInUnleashItem';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
@ -71,7 +70,9 @@ type NewItem = {
label: string; label: string;
icon: ReactNode; icon: ReactNode;
link: string; link: string;
docsLink: string;
show: boolean; show: boolean;
longDescription: ReactNode;
}; };
interface INewInUnleashProps { interface INewInUnleashProps {
@ -86,7 +87,6 @@ export const NewInUnleash = ({
onMiniModeClick, onMiniModeClick,
}: INewInUnleashProps) => { }: INewInUnleashProps) => {
const { trackEvent } = usePlausibleTracker(); const { trackEvent } = usePlausibleTracker();
const navigate = useNavigate();
const [seenItems, setSeenItems] = useLocalStorageState( const [seenItems, setSeenItems] = useLocalStorageState(
'new-in-unleash-seen:v1', 'new-in-unleash-seen:v1',
new Set(), new Set(),
@ -99,7 +99,35 @@ export const NewInUnleash = ({
label: 'Signals & Actions', label: 'Signals & Actions',
icon: <StyledSignalsIcon />, icon: <StyledSignalsIcon />,
link: '/integrations/signals', link: '/integrations/signals',
docsLink: 'https://docs.getunleash.io/reference/signals',
show: isEnterprise() && signalsEnabled, show: isEnterprise() && signalsEnabled,
longDescription: (
<>
<p>
It allows you to respond to events in your real-time
monitoring system by automating tasks such as disabling
a beta feature in response to an increase in errors or a
drop in conversion rates.
</p>
<p>
<ul>
<li>
<b>Signal endpoints</b> are used to send signals
to Unleash. This allows you to integrate Unleash
with any external tool.
</li>
<li>
<b>Actions</b>, which are configured inside
projects, allow you to react to those signals
and enable or disable flags based on certain
conditions.
</li>
</ul>
</p>
</>
),
}, },
]; ];
@ -135,18 +163,15 @@ export const NewInUnleash = ({
New in Unleash New in Unleash
</StyledNewInUnleashHeader> </StyledNewInUnleashHeader>
<StyledNewInUnleashList> <StyledNewInUnleashList>
{visibleItems.map(({ label, icon, link }) => ( {visibleItems.map(
({ label, icon, link, longDescription, docsLink }) => (
<NewInUnleashItem <NewInUnleashItem
key={label}
icon={icon}
onClick={() => { onClick={() => {
trackEvent('new-in-unleash-click', { trackEvent('new-in-unleash-click', {
props: { props: {
label, label,
}, },
}); });
navigate(link);
onItemClick?.();
}} }}
onDismiss={() => { onDismiss={() => {
trackEvent('new-in-unleash-dismiss', { trackEvent('new-in-unleash-dismiss', {
@ -156,10 +181,14 @@ export const NewInUnleash = ({
}); });
setSeenItems(new Set([...seenItems, label])); setSeenItems(new Set([...seenItems, label]));
}} }}
> label={label}
{label} icon={icon}
</NewInUnleashItem> link={link}
))} longDescription={longDescription}
docsLink={docsLink}
/>
),
)}
</StyledNewInUnleashList> </StyledNewInUnleashList>
</StyledNewInUnleash> </StyledNewInUnleash>
); );

View File

@ -1,12 +1,14 @@
import type { ReactNode } from 'react'; import type * as React from 'react';
import { type ReactNode, useState } from 'react';
import { import {
IconButton, IconButton,
ListItem, ListItem,
ListItemButton, ListItemButton,
Tooltip,
styled, styled,
Tooltip,
} from '@mui/material'; } from '@mui/material';
import Close from '@mui/icons-material/Close'; import Close from '@mui/icons-material/Close';
import { NewInUnleashTooltip } from './NewInUnleashTooltip';
const StyledItemButton = styled(ListItemButton)(({ theme }) => ({ const StyledItemButton = styled(ListItemButton)(({ theme }) => ({
justifyContent: 'space-between', justifyContent: 'space-between',
@ -30,26 +32,62 @@ interface INewInUnleashItemProps {
icon: ReactNode; icon: ReactNode;
onClick: () => void; onClick: () => void;
onDismiss: () => void; onDismiss: () => void;
children: ReactNode; label: string;
longDescription: ReactNode;
link: string;
docsLink: string;
} }
const useTooltip = () => {
const [open, setOpen] = useState(false);
const handleTooltipClose = () => {
setOpen(false);
};
const handleTooltipOpen = () => {
setOpen(true);
};
return { open, handleTooltipOpen, handleTooltipClose };
};
export const NewInUnleashItem = ({ export const NewInUnleashItem = ({
icon, icon,
onClick, onClick,
onDismiss, onDismiss,
children, label,
longDescription,
link,
docsLink,
}: INewInUnleashItemProps) => { }: INewInUnleashItemProps) => {
const { open, handleTooltipOpen, handleTooltipClose } = useTooltip();
const onDismissClick = (e: React.MouseEvent) => { const onDismissClick = (e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
onDismiss(); onDismiss();
}; };
return ( return (
<ListItem disablePadding onClick={onClick}> <ListItem
disablePadding
onClick={() => {
onClick();
handleTooltipOpen();
}}
>
<NewInUnleashTooltip
open={open}
onClose={handleTooltipClose}
title={label}
longDescription={longDescription}
link={link}
docsLink={docsLink}
>
<StyledItemButton> <StyledItemButton>
<StyledItemButtonContent> <StyledItemButtonContent>
{icon} {icon}
{children} {label}
</StyledItemButtonContent> </StyledItemButtonContent>
<Tooltip title='Dismiss' arrow> <Tooltip title='Dismiss' arrow>
<StyledItemButtonClose <StyledItemButtonClose
@ -61,6 +99,7 @@ export const NewInUnleashItem = ({
</StyledItemButtonClose> </StyledItemButtonClose>
</Tooltip> </Tooltip>
</StyledItemButton> </StyledItemButton>
</NewInUnleashTooltip>
</ListItem> </ListItem>
); );
}; };

View File

@ -0,0 +1,129 @@
import type * as React from 'react';
import type { FC, ReactNode } from 'react';
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
import {
Box,
Button,
Link,
styled,
Typography,
ClickAwayListener,
} from '@mui/material';
import { type Link as RouterLink, useNavigate } from 'react-router-dom';
import OpenInNew from '@mui/icons-material/OpenInNew';
import { ReactComponent as UnleashLogo } from 'assets/img/logoWithWhiteText.svg';
const Header = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.primary.light,
color: theme.palette.primary.contrastText,
borderRadius: `${theme.shape.borderRadiusMedium}px ${theme.shape.borderRadiusMedium}px 0 0`, // 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),
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: '120px',
}));
const Body = styled(Box)(({ theme }) => ({
padding: theme.spacing(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.fontWeight.medium,
'&:hover, &:focus': {
textDecoration: 'underline',
},
}));
const StyledOpenInNew = styled(OpenInNew)(({ theme }) => ({
fontSize: theme.spacing(2.25),
}));
const StyledUnleashLogo = styled(UnleashLogo)({ width: '150px' });
const LongDescription = styled(Box)(({ theme }) => ({
ul: {
paddingLeft: theme.spacing(2),
},
}));
const Title = styled(Typography)(({ theme }) => ({
margin: theme.spacing(2, 0),
}));
const ReadMore = styled(Box)(({ theme }) => ({
margin: theme.spacing(3, 0),
}));
export const NewInUnleashTooltip: FC<{
children: React.ReactElement<any, any>;
title: string;
longDescription: ReactNode;
docsLink: string;
link: string;
open: boolean;
onClose: () => void;
}> = ({ children, title, longDescription, link, docsLink, open, onClose }) => {
const navigate = useNavigate();
return (
<HtmlTooltip
disableFocusListener
disableHoverListener
disableTouchListener
onClose={onClose}
open={open}
maxHeight={800}
maxWidth={350}
arrow
tabIndex={0}
placement='right-end'
title={
<ClickAwayListener onClickAway={onClose}>
<Box>
<Header>
<StyledUnleashLogo />
</Header>
<Body>
<Title>{title}</Title>
<LongDescription>{longDescription}</LongDescription>
<ReadMore>
<StyledLink
component='a'
href={docsLink}
underline='hover'
rel='noopener noreferrer'
target='_blank'
>
<StyledOpenInNew /> Read more in our
documentation
</StyledLink>
</ReadMore>
<Button
variant='contained'
color='primary'
type='submit'
size='small'
onClick={(event) => {
event.stopPropagation();
onClose();
navigate(link);
}}
>
Check it out
</Button>
</Body>
</Box>
</ClickAwayListener>
}
>
{children}
</HtmlTooltip>
);
};