1
0
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:

![image](https://github.com/user-attachments/assets/05666259-bc40-4c87-8e51-9900bc67310e)

![image](https://github.com/user-attachments/assets/25dfca09-af5f-4a2a-8490-1169f6d8accb)


Dark mode:

![image](https://github.com/user-attachments/assets/8fa199aa-3cb5-47b4-acaa-1b0fcfd668eb)

![image](https://github.com/user-attachments/assets/eca7d26e-f695-43f9-b281-a64315544212)


With line break (min-width):

![image](https://github.com/user-attachments/assets/2ebd9117-a7c2-4a96-8b4b-c217ba12993b)

With line break (max-width):

![image](https://github.com/user-attachments/assets/8015c761-fc1e-4ff9-992d-a0e9ec27a4f9)

With very long message on phone in landscape mode:


![image](https://github.com/user-attachments/assets/7dc34d25-026c-46c3-9906-dc1348daf208)
This commit is contained in:
Thomas Heartman 2024-12-10 14:10:04 +01:00 committed by GitHub
parent 7ff6a9c5c8
commit 2f7beceb21
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 59 additions and 148 deletions

View File

@ -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 />
);
};

View File

@ -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%',

View File

@ -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>
);
};

View File

@ -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,

View File

@ -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, weve 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, weve ended 1 session(s) on other browsers.',
);
});

View File

@ -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);
});