1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-29 01:15:48 +02:00
unleash.unleash/frontend/src/component/messageBanners/MessageBanner/MessageBanner.tsx
Nuno Góis d30e059ffc
fix: message banner internal link assumption (#5011)
https://linear.app/unleash/issue/2-1504/fix-message-banner-internal-link-assumption

Fixes the internal link assumption in message banner to
`.startsWith('/')` - Any other links will be treated as external (normal
`href`).
2023-10-12 15:07:55 +01:00

181 lines
4.5 KiB
TypeScript

import { Check, Close, InfoOutlined, WarningAmber } from '@mui/icons-material';
import { styled, Icon, Link } from '@mui/material';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { useNavigate } from 'react-router-dom';
import { MessageBannerDialog } from './MessageBannerDialog/MessageBannerDialog';
import { useState } from 'react';
import ReactMarkdown from 'react-markdown';
const StyledBar = styled('aside', {
shouldForwardProp: (prop) => prop !== 'variant' && prop !== 'sticky',
})<{ variant: BannerVariant; sticky?: boolean }>(
({ theme, variant, sticky }) => ({
position: sticky ? 'sticky' : 'relative',
zIndex: 1,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: theme.spacing(1),
gap: theme.spacing(1),
borderBottom: '1px solid',
borderColor: theme.palette[variant].border,
background: theme.palette[variant].light,
color: theme.palette[variant].dark,
fontSize: theme.fontSizes.smallBody,
...(sticky && {
top: 0,
zIndex: theme.zIndex.sticky,
}),
}),
);
const StyledIcon = styled('div', {
shouldForwardProp: (prop) => prop !== 'variant',
})<{ variant: BannerVariant }>(({ theme, variant }) => ({
display: 'flex',
alignItems: 'center',
color: theme.palette[variant].main,
}));
type BannerVariant =
| 'warning'
| 'info'
| 'error'
| 'success'
| 'neutral'
| 'secondary';
export interface IMessageBanner {
message: string;
variant?: BannerVariant;
sticky?: boolean;
icon?: string;
link?: string;
linkText?: string;
plausibleEvent?: string;
dialogTitle?: string;
dialog?: string;
}
interface IMessageBannerProps {
messageBanner: IMessageBanner;
}
export const MessageBanner = ({ messageBanner }: IMessageBannerProps) => {
const [open, setOpen] = useState(false);
const {
message,
variant = 'neutral',
sticky,
icon,
link,
linkText = 'More info',
plausibleEvent,
dialogTitle,
dialog,
} = messageBanner;
return (
<StyledBar variant={variant} sticky={sticky}>
<StyledIcon variant={variant}>
<BannerIcon icon={icon} variant={variant} />
</StyledIcon>
<ReactMarkdown>{message}</ReactMarkdown>
<BannerButton
link={link}
plausibleEvent={plausibleEvent}
openDialog={() => setOpen(true)}
>
{linkText}
</BannerButton>
<MessageBannerDialog
open={open}
setOpen={setOpen}
title={dialogTitle || linkText}
>
{dialog!}
</MessageBannerDialog>
</StyledBar>
);
};
const VariantIcons = {
warning: <WarningAmber />,
info: <InfoOutlined />,
error: <Close />,
success: <Check />,
neutral: <InfoOutlined />,
secondary: <InfoOutlined />,
};
interface IBannerIconProps {
variant: BannerVariant;
icon?: string;
}
const BannerIcon = ({ icon, variant }: IBannerIconProps) => {
if (icon === 'none') return null;
if (icon) return <Icon>{icon}</Icon>;
return VariantIcons[variant] ?? <InfoOutlined />;
};
interface IBannerButtonProps {
link?: string;
plausibleEvent?: string;
openDialog: () => void;
children: React.ReactNode;
}
const BannerButton = ({
link,
plausibleEvent,
openDialog,
children,
}: IBannerButtonProps) => {
const navigate = useNavigate();
const tracker = usePlausibleTracker();
if (!link) return null;
const dialog = link === 'dialog';
const internal = link.startsWith('/');
const trackEvent = () => {
if (!plausibleEvent) return;
tracker.trackEvent('message_banner', {
props: { event: plausibleEvent },
});
};
if (dialog)
return (
<Link
onClick={() => {
trackEvent();
openDialog();
}}
>
{children}
</Link>
);
if (internal)
return (
<Link
onClick={() => {
trackEvent();
navigate(link);
}}
>
{children}
</Link>
);
return (
<Link href={link} target='_blank' rel='noreferrer' onClick={trackEvent}>
{children}
</Link>
);
};