mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
chore(unl-204): make toasts smaller (#8935)
This PR makes toasts smaller and less intrusive, and gives them a new color scheme. Changes include: - new color scheme - no description, only title - new padding - removes confetti code (even when rendered, they're invisible; UX also says to cut it) - use warning triangle for error messages I've also set a max height on the container and made it scrollable if it's too tall to deal with super long messages. I'll remove the description and confetti props in a separate PR to keep this one cleaner. Light mode:   Dark mode:   With line break (min-width):  With line break (max-width):  With very long message on phone in landscape mode: 
This commit is contained in:
parent
7ff6a9c5c8
commit
2f7beceb21
@ -1,5 +1,5 @@
|
||||
import Check from '@mui/icons-material/Check';
|
||||
import Close from '@mui/icons-material/Close';
|
||||
import Check from '@mui/icons-material/CheckCircle';
|
||||
import Warning from '@mui/icons-material/Warning';
|
||||
import { styled } from '@mui/material';
|
||||
|
||||
interface ICheckMarkBadgeProps {
|
||||
@ -7,40 +7,25 @@ interface ICheckMarkBadgeProps {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const StyledBatch = styled('div')(({ theme }) => ({
|
||||
backgroundColor: theme.palette.background.alternative,
|
||||
width: '75px',
|
||||
height: '75px',
|
||||
borderRadius: '50px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledClose = styled(Close)(({ theme }) => ({
|
||||
color: theme.palette.common.white,
|
||||
width: '35px',
|
||||
height: '35px',
|
||||
}));
|
||||
const StyledCheck = styled(Check)(({ theme }) => ({
|
||||
color: theme.palette.common.white,
|
||||
width: '35px',
|
||||
height: '35px',
|
||||
color:
|
||||
theme.mode === 'light'
|
||||
? theme.palette.secondary.border
|
||||
: theme.palette.primary.main,
|
||||
}));
|
||||
|
||||
const CheckMarkBadge = ({ type, className }: ICheckMarkBadgeProps) => {
|
||||
return (
|
||||
<StyledBatch className={className}>
|
||||
{type === 'error' ? (
|
||||
<StyledClose titleAccess='Error' />
|
||||
) : (
|
||||
<StyledCheck />
|
||||
)}
|
||||
</StyledBatch>
|
||||
const StyledCancel = styled(Warning)(({ theme }) => ({
|
||||
color:
|
||||
theme.mode === 'light'
|
||||
? theme.palette.warning.border
|
||||
: theme.palette.warning.main,
|
||||
}));
|
||||
|
||||
const CheckMarkBadge = ({ type }: ICheckMarkBadgeProps) => {
|
||||
return type === 'error' ? (
|
||||
<StyledCancel titleAccess='Error' />
|
||||
) : (
|
||||
<StyledCheck />
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -2,56 +2,49 @@ import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()((theme) => ({
|
||||
container: {
|
||||
maxWidth: '450px',
|
||||
background: theme.palette.background.paper,
|
||||
alignItems: 'center',
|
||||
background:
|
||||
// the background color for this doesn't exist in the theme yet and it's not synchronized across dark/light modes yet.
|
||||
theme.mode === 'light' ? '#201E42' : theme.palette.background.paper,
|
||||
|
||||
borderRadius: theme.shape.borderRadiusMedium,
|
||||
boxShadow: theme.boxShadows.popup,
|
||||
zIndex: 500,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: theme.spacing(2),
|
||||
margin: '0 0.8rem',
|
||||
borderRadius: '12.5px',
|
||||
padding: '2rem',
|
||||
},
|
||||
innerContainer: {
|
||||
position: 'relative',
|
||||
maxWidth: '450px',
|
||||
padding: theme.spacing(1),
|
||||
paddingLeft: theme.spacing(2),
|
||||
zIndex: theme.zIndex.snackbar,
|
||||
color: theme.palette.common.white,
|
||||
},
|
||||
starting: {
|
||||
opacity: 0,
|
||||
},
|
||||
headerContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
confettiContainer: {
|
||||
position: 'relative',
|
||||
maxWidth: '600px',
|
||||
margin: '0 auto',
|
||||
display: 'flex',
|
||||
},
|
||||
textContainer: {
|
||||
marginLeft: '1rem',
|
||||
wordBreak: 'break-word',
|
||||
},
|
||||
headerStyles: {
|
||||
fontSize: theme.typography.body1.fontSize,
|
||||
fontWeight: 'normal',
|
||||
margin: 0,
|
||||
marginBottom: '0.5rem',
|
||||
},
|
||||
createdContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
maxHeight: '75vh',
|
||||
overflowY: 'auto',
|
||||
'&::first-letter': {
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
},
|
||||
anim: {
|
||||
animation: `$drop 10s 3s`,
|
||||
},
|
||||
checkMark: {
|
||||
width: '65px',
|
||||
height: '65px',
|
||||
marginLeft: theme.spacing(1),
|
||||
},
|
||||
buttonStyle: {
|
||||
position: 'absolute',
|
||||
top: '-33px',
|
||||
right: '-33px',
|
||||
color: theme.palette.common.white,
|
||||
svg: {
|
||||
fontSize: '1em',
|
||||
},
|
||||
},
|
||||
|
||||
'@keyframes drop': {
|
||||
'0%': {
|
||||
opacity: '0%',
|
||||
|
@ -4,52 +4,13 @@ import { useContext } from 'react';
|
||||
import { IconButton, Tooltip } from '@mui/material';
|
||||
import CheckMarkBadge from 'component/common/CheckmarkBadge/CheckMarkBadge';
|
||||
import UIContext from 'contexts/UIContext';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import Close from '@mui/icons-material/Close';
|
||||
import type { IToast } from 'interfaces/toast';
|
||||
import { TOAST_TEXT } from 'utils/testIds';
|
||||
|
||||
const Toast = ({ title, text, type, confetti }: IToast) => {
|
||||
const Toast = ({ title, type }: IToast) => {
|
||||
const { setToast } = useContext(UIContext);
|
||||
|
||||
const { classes: styles } = useStyles();
|
||||
const confettiColors = ['#d13447', '#ffbf00', '#263672'];
|
||||
const confettiAmount = 200;
|
||||
|
||||
const getRandomNumber = (input: number) => {
|
||||
return Math.floor(Math.random() * input) + 1;
|
||||
};
|
||||
|
||||
const renderConfetti = () => {
|
||||
const elements = new Array(confettiAmount).fill(1);
|
||||
|
||||
const styledElements = elements.map((el, index) => {
|
||||
const width = getRandomNumber(8);
|
||||
const length = getRandomNumber(100);
|
||||
|
||||
const style = {
|
||||
position: 'absolute' as const,
|
||||
width: `${width}px`,
|
||||
height: `${width * 0.4}px`,
|
||||
backgroundColor: confettiColors[getRandomNumber(2)],
|
||||
left: `${length}%`,
|
||||
transform: `rotate(${getRandomNumber(101)}deg)`,
|
||||
animationDelay: `${getRandomNumber(5)}s`,
|
||||
animationDuration: `${getRandomNumber(3)}s`,
|
||||
animationEase: `${getRandomNumber(2)}s`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
style={style}
|
||||
className={classnames(styles.starting, styles.anim)}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return styledElements;
|
||||
};
|
||||
|
||||
const hide = () => {
|
||||
setToast((prev: IToast) => ({ ...prev, show: false }));
|
||||
@ -57,40 +18,19 @@ const Toast = ({ title, text, type, confetti }: IToast) => {
|
||||
|
||||
return (
|
||||
<div className={classnames(styles.container, 'dropdown-outline')}>
|
||||
<div className={styles.innerContainer}>
|
||||
<div className={styles.confettiContainer}>
|
||||
{confetti && renderConfetti()}
|
||||
<div className={styles.createdContainer}>
|
||||
<div className={styles.headerContainer}>
|
||||
<div>
|
||||
<CheckMarkBadge
|
||||
type={type}
|
||||
className={styles.checkMark}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.textContainer}>
|
||||
<h3 className={styles.headerStyles}>{title}</h3>
|
||||
<CheckMarkBadge type={type} className={styles.checkMark} />
|
||||
|
||||
<ConditionallyRender
|
||||
condition={Boolean(text)}
|
||||
show={
|
||||
<p data-testid={TOAST_TEXT}>{text}</p>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip title='Close' arrow>
|
||||
<IconButton
|
||||
onClick={hide}
|
||||
className={styles.buttonStyle}
|
||||
size='large'
|
||||
>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className={styles.headerStyles}>{title}</h3>
|
||||
|
||||
<Tooltip title='Close' arrow>
|
||||
<IconButton
|
||||
onClick={hide}
|
||||
className={styles.buttonStyle}
|
||||
size='small'
|
||||
>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -22,7 +22,6 @@ const ToastRenderer = () => {
|
||||
const timeout = setTimeout(() => {
|
||||
hide();
|
||||
}, toastData.autoHideDuration);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
@ -36,7 +35,7 @@ const ToastRenderer = () => {
|
||||
right: 0,
|
||||
left: 0,
|
||||
margin: '0 auto',
|
||||
maxWidth: '450px',
|
||||
width: 'fit-content',
|
||||
},
|
||||
enter: fadeInBottomEnter,
|
||||
leave: fadeInBottomLeave,
|
||||
|
@ -47,9 +47,6 @@ test('should show deleted stale sessions info for Password Auth', async () => {
|
||||
button.click();
|
||||
|
||||
await screen.findByText('Maximum Session Limit Reached');
|
||||
await screen.findByText(
|
||||
'You can have up to 3 active sessions at a time. To enhance your account security, we’ve ended 1 session(s) on other browsers.',
|
||||
);
|
||||
});
|
||||
|
||||
test('should show deleted stale sessions info for Hosted Auth', async () => {
|
||||
@ -76,7 +73,4 @@ test('should show deleted stale sessions info for Hosted Auth', async () => {
|
||||
button.click();
|
||||
|
||||
await screen.findByText('Maximum Session Limit Reached');
|
||||
await screen.findByText(
|
||||
'You can have up to 3 active sessions at a time. To enhance your account security, we’ve ended 1 session(s) on other browsers.',
|
||||
);
|
||||
});
|
||||
|
@ -100,6 +100,6 @@ test('handle error', async () => {
|
||||
|
||||
checkbox.click();
|
||||
|
||||
await screen.findByText('user error');
|
||||
await screen.findByText('Something went wrong');
|
||||
expect(changed).toBe(true);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user