Skip to content

Commit

Permalink
Update Toasts style
Browse files Browse the repository at this point in the history
  • Loading branch information
Anboias committed Jan 2, 2025
1 parent 7f89632 commit 08eb5a2
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 109 deletions.
96 changes: 71 additions & 25 deletions src/components/notifications/notifications.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@import '../../styles/variables.module.scss';
@import '../../styles/fonts.module.scss';

/*
* NOTE: Use these classes to style notification content.
Expand All @@ -7,43 +8,88 @@
* This would include styling for things like backgrounds and containers
*/

.notificationBody {
width: 100%;
.notification {
display: flex;
align-items: flex-start;
padding: $space-lg;
gap: 8px;
padding: 32px 28px 34px 28px;
box-sizing: border-box;

&.url {
padding-bottom: $space-md;
}
}

.notificationContent {
display: flex;
flex-direction: column;
gap: 16px;
flex: 1;
margin-left: $space-sm;
margin-top: $space-xxs;

.notificationMessage {
@include font-body-8;
}
}

.transactionButtonContainer {
width: 100%;
display: flex;
justify-content: flex-end;
}

.transactionButton {
display: flex;
gap: 4px;
padding: 0 !important;
}

.icon {
width: 24px;
height: 24px;

&.success {
color: $color-action-success-500;
}

&.error {
color: $color-action-error-500;
}

&.warning {
color: $color-action-warning-500;
}

&.info {
color: $color-action-info-500;
}
}

.closeButton {
position: absolute;
top: $space-sm;
right: $space-sm;
}
top: 12px;
right: 12px;
background-color: transparent;
border: none;
cursor: pointer;
color: var(--color-gray-200);
transition: color 0.2s;

&:hover {
color: var(--color-base-light);
}

svg {
width: 20px;
height: 20px;

.notificationButton {
background-color: $black-color;
color: $primary-color;
padding: 6px 20px;
font-size: $text-xsmall;
line-height: 20px;
font-weight: 600;
border-radius: 16px;
text-decoration: none;
@media (min-width: $md) {
width: 24px;
height: 24px;
}
}
}

.notificationUrl {
display: flex;
justify-content: flex-end;
margin-top: $space-md;
.progressBarBackground {
position: absolute;
height: 6px;
bottom: 0;
left: 0;
right: 0;
background-color: $color-dark-blue-100;
}
114 changes: 76 additions & 38 deletions src/components/notifications/notifications.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,94 @@
import { ReactNode } from 'react';
import throttle from 'lodash/throttle';
import classNames from 'classnames';
import { toast, Slide, ToastOptions } from 'react-toastify';
import NotificationLinkButton from './notification-link-button';
import { images } from '../../utils';
import * as Sentry from '@sentry/browser';
import Button from '../button';
import styles from './notifications.module.scss';
import {
CheckCircleFillIcon,
CrossIcon,
ExclamationTriangleFillIcon,
ExternalLinkIcon,
InfoCircleFillIcon,
WarningCircleFillIcon,
} from '../icons';
import { ClearStorageButton } from './clear-storage-button';

import 'react-toastify/dist/ReactToastify.css';
// Use these static classes to style react-toastify defaults
import './react-toastify-overrides.scss';
// Use these classes to style content
import styles from './notifications.module.scss';
import * as Sentry from '@sentry/browser';

const THROTTLE_MS = 500;

interface CloseButtonProps {
closeToast: () => void;
}

export const CloseButton = ({ closeToast }: CloseButtonProps) => (
<div className={styles.closeButton} onClick={() => closeToast()}>
<img src={images.notificationClose} alt="notification close button" />
</div>
);
type ToastProps =
| {
isClearStorage?: never;
message: string;
url?: string;
}
| {
isClearStorage: true;
message: string;
url?: never;
};

type ErrorToastProps = {
errorOrMessage?: Error | string;
sendToSentry?: boolean;
} & ToastProps;

interface ToastProps {
message: ReactNode;
url?: string;
}
const GenericIcon = (props: { type: Partial<ToastOptions['type']> }) => {
const icons = {
info: <InfoCircleFillIcon className={classNames(styles.icon, styles.info)} />,
success: <CheckCircleFillIcon className={classNames(styles.icon, styles.success)} />,
error: <ExclamationTriangleFillIcon className={classNames(styles.icon, styles.error)} />,
warning: <WarningCircleFillIcon className={classNames(styles.icon, styles.warning)} />,
};

interface ErrorToastProps extends ToastProps {
errorOrMessage: Error | string;
sendToSentry?: boolean;
}
return icons[props.type as keyof typeof icons];
};

interface ToastPropsWithType extends ToastProps {
type: 'info' | 'success' | 'warning' | 'error';
}
const CustomToast = (props: ToastProps & ToastOptions) => {
const { isClearStorage, message, type, url } = props;

const CustomToast = ({ message, type, url }: ToastPropsWithType) => {
return (
<div className={classNames(styles.notificationBody, { [styles.url]: url })}>
<img src={`/${type}.svg`} alt={`${type} icon`} />
<div className={styles.notification}>
<GenericIcon type={type} />

<div className={styles.notificationContent}>
<div>{message}</div>
<span className={styles.notificationMessage}>{message}</span>

{url && (
<div className={styles.notificationUrl}>
<NotificationLinkButton href={url}>View transaction</NotificationLinkButton>
<div className={styles.transactionButtonContainer}>
<Button type="text-gray" size="xs" className={styles.transactionButton} href={url}>
<span>View transaction</span>
<ExternalLinkIcon />
</Button>
</div>
)}

{isClearStorage && <ClearStorageButton />}
</div>

<div className={styles.progressBarBackground} />
</div>
);
};

// https://fkhadra.github.io/react-toastify/api/toast
const BASE_OPTIONS: ToastOptions = {
transition: Slide,
closeButton: CloseButton,
hideProgressBar: false,
closeButton: (props) => (
<button className={styles.closeButton} onClick={props.closeToast}>
<CrossIcon />
</button>
),
};

const CLEAR_STORAGE_OPTIONS: ToastOptions = {
toastId: 'storage-warning',
bodyClassName: 'cursor-auto',
closeOnClick: false,
draggable: false,
};

// NOTE: toasts are throttled to prevent duplicate notifications being displayed.
Expand All @@ -72,15 +103,22 @@ export const info = throttle(

export const success = throttle(
(props: ToastProps, overrides?: ToastOptions) => {
return toast.info(<CustomToast {...props} type="success" />, { ...BASE_OPTIONS, ...overrides });
return toast.success(<CustomToast {...props} type="success" />, {
...BASE_OPTIONS,
...overrides,
});
},
THROTTLE_MS,
{ trailing: false }
);

export const warning = throttle(
(props: ToastProps, overrides?: ToastOptions) => {
return toast.info(<CustomToast {...props} type="warning" />, { ...BASE_OPTIONS, ...overrides });
return toast.warning(<CustomToast {...props} type="warning" />, {
...BASE_OPTIONS,
...(props.isClearStorage && CLEAR_STORAGE_OPTIONS),
...overrides,
});
},
THROTTLE_MS,
{ trailing: false }
Expand All @@ -100,7 +138,7 @@ export const error = throttle(
console.error('[DEV: Caught error]:', errorOrMessage);
}

return toast.info(<CustomToast {...other} type="error" />, { ...BASE_OPTIONS, ...overrides });
return toast.error(<CustomToast {...other} type="error" />, { ...BASE_OPTIONS, ...overrides });
},
THROTTLE_MS,
{ trailing: false }
Expand Down
67 changes: 21 additions & 46 deletions src/components/notifications/react-toastify-overrides.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,79 +11,54 @@
* components using the notifications.module.scss
*/

/** Used to define container behavior: width, position: fixed etc... **/
.Toastify__toast,
.Toastify__toast-body,
.Toastify__toast-container {
width: 340px;
border-radius: 24px;
padding: 0;
}

@media (max-width: $max-sm) {
width: 240px;
}
.Toastify__toast {
margin-bottom: 1rem;
}

@media only screen and (max-width: 480px) {
left: auto;
@media only screen and (min-width: 478px) {
.Toastify__toast-container {
width: 480px;
}
}

/** Used to define the position of the ToastContainer **/
.Toastify__toast-container--top-right {
@media only screen and (max-width: 480px) {
top: 1em;
}
top: 1em;
right: 1rem;
}

/** Classes for the displayed toast **/
.Toastify__toast {
background: linear-gradient(76.31deg, #f3f3f3 36.47%, #c3c4c3 99.02%);
background-color: $color-dark-blue-700;
box-shadow: 0px 9px 28px 8px rgba(0, 0, 0, 0.05), 0px 6px 16px rgba(0, 0, 0, 0.08),
0px 3px 6px -4px rgba(0, 0, 0, 0.12);
border-radius: 0;
padding: 0;
font-size: $text-small;
line-height: $lh-small;
color: $black-color;
}

.Toastify__toast-body {
padding: 0;
}

/** Classes for the progress bar **/
.Toastify__progress-bar {
width: $space-xxs;
height: 100%;
transform-origin: bottom;
}

.Toastify__progress-bar--error {
background-color: transparent;
background: linear-gradient(0deg, #7ce3cb 0%, #7963b2 135%);
.Toastify__progress-bar,
.Toastify__progress-bar--bg {
height: 6px;
}

.Toastify__progress-bar--info {
background-color: $progressbar-info;
background-color: $color-action-info-500;
}

.Toastify__progress-bar--success {
background-color: $progressbar-success;
background-color: $color-action-success-500;
}

.Toastify__progress-bar--warning {
background-color: $progressbar-warning;
background-color: $color-action-warning-500;
}

.Toastify__progress-bar--error {
background-color: $progressbar-error;
}

@keyframes Toastify__trackProgress {
0% {
transform: scaleY(1);
}
100% {
transform: scaleY(0);
}
}

.Toastify__progress-bar--animated {
animation: Toastify__trackProgress linear 1 forwards;
background-color: $color-action-error-500;
}

0 comments on commit 08eb5a2

Please sign in to comment.