Skip to content

Commit 3f62bec

Browse files
authored
feat: add toast notification provider (#1199)
1 parent 24b5352 commit 3f62bec

22 files changed

+1134
-8
lines changed

src/components/NotificationProvider/NotificationProvider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import {
1414
NotifyProviderProps,
1515
} from "./types";
1616
import { info, failure, success, queue } from "./messageBuilder";
17-
import Notification, { DefaultTitles } from "../Notification/Notification";
17+
import Notification from "../Notifications";
18+
import { DefaultTitles } from "../Notifications/Notification/Notification";
1819

1920
const NotifyContext = createContext<NotificationHelper>({
2021
notification: null,

src/components/NotificationProvider/messageBuilder.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
QueuedNotification,
55
} from "./types";
66
import React, { ReactNode } from "react";
7-
import { NotificationSeverity } from "../Notification";
7+
import { NotificationSeverity } from "../Notifications";
88

99
export const queue = (notification: NotificationType): QueuedNotification => {
1010
return { state: { queuedNotification: notification } };

src/components/NotificationProvider/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ReactNode } from "react";
22
import { ValueOf } from "types";
3-
import { NotificationSeverity } from "../Notification";
3+
import { NotificationSeverity } from "../Notifications";
44

55
export interface NotifyProviderProps {
66
state?: {
File renamed without changes.
File renamed without changes.

src/components/Notification/Notification.tsx renamed to src/components/Notifications/Notification/Notification.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import classNames from "classnames";
22
import React, { ElementType, useEffect, useRef } from "react";
33
import type { HTMLProps, ReactNode } from "react";
44

5-
import Button, { ButtonAppearance } from "../Button";
6-
import { IS_DEV, isReactNode } from "../../utils";
5+
import Button, { ButtonAppearance } from "../../Button";
6+
import { IS_DEV, isReactNode } from "../../../utils";
77

88
import type { ClassName, PropsWithSpread, ValueOf } from "types";
99

File renamed without changes.
File renamed without changes.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { FC, PropsWithChildren } from "react";
2+
import { useLayoutEffect, useRef, useState } from "react";
3+
import React from "react";
4+
import { usePrefersReducedMotion } from "../../../hooks";
5+
6+
interface Props {
7+
show: boolean;
8+
from: Keyframe;
9+
to: Keyframe;
10+
exitAnimation?: Keyframe[];
11+
options?: KeyframeAnimationOptions;
12+
className?: string;
13+
}
14+
15+
const Animate: FC<PropsWithChildren<Props>> = ({
16+
show,
17+
children,
18+
from,
19+
to,
20+
exitAnimation,
21+
options = { duration: 500, fill: "forwards" },
22+
className,
23+
}) => {
24+
const containerRef = useRef<HTMLDivElement>(null);
25+
const preferReducedMotion = usePrefersReducedMotion();
26+
27+
// This state is used so that we trigger a extra render cycle
28+
// to animate the child component when it is being unmounted
29+
const [removeState, setRemove] = useState(!show);
30+
31+
useLayoutEffect(() => {
32+
const element = containerRef.current;
33+
if (show) {
34+
setRemove(false);
35+
if (!element || preferReducedMotion) return;
36+
element.animate([from, to], options);
37+
} else {
38+
if (!element) return;
39+
if (preferReducedMotion) {
40+
setRemove(true);
41+
return;
42+
}
43+
const animation = element.animate(exitAnimation || [to, from], options);
44+
animation.onfinish = () => {
45+
setRemove(true);
46+
// This is important, else the next render cycle due to setRemove will cause flickering effect
47+
element.style.display = "none";
48+
};
49+
}
50+
}, [show, removeState]);
51+
52+
if (removeState) {
53+
return null;
54+
}
55+
56+
return (
57+
<div ref={containerRef} className={className}>
58+
{children}
59+
</div>
60+
);
61+
};
62+
63+
export default Animate;
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
@import "vanilla-framework/scss/settings";
2+
@import "vanilla-framework/scss/build";
3+
4+
.toast-animate {
5+
position: relative;
6+
z-index: 101;
7+
}
8+
9+
.toast-notification,
10+
.toast-notification-list {
11+
bottom: 3.5rem;
12+
margin: 0 1.5rem;
13+
position: absolute;
14+
right: 0;
15+
16+
@media screen and (min-width: $breakpoint-small) and (max-width: $breakpoint-large) {
17+
width: 500px;
18+
}
19+
20+
@media screen and (min-width: $breakpoint-large) {
21+
width: 600px;
22+
}
23+
}
24+
25+
.toast-notification {
26+
box-shadow: 0 0 calc($sp-unit * 4) $sp-unit rgb(0 0 0 / 15%);
27+
overflow: hidden;
28+
z-index: 101;
29+
30+
@media screen and (max-width: $breakpoint-small) {
31+
left: 0;
32+
margin: 1.5rem auto;
33+
max-width: 400px;
34+
width: 95vw;
35+
}
36+
}
37+
38+
.toast-notification-list {
39+
background-color: white;
40+
box-shadow: 0 0 calc($sp-unit * 4) $sp-unit rgb(0 0 0 / 22%);
41+
max-height: calc(100vh - 4.5rem);
42+
overflow: auto;
43+
padding: 0 0.5rem;
44+
z-index: 102;
45+
46+
.individual-notification {
47+
margin: 0.5rem 0;
48+
overflow: hidden;
49+
}
50+
51+
.dismiss {
52+
align-items: center;
53+
background-color: white;
54+
bottom: 0;
55+
display: flex;
56+
justify-content: space-between;
57+
padding: 0.5rem 0;
58+
padding-left: 0.8rem;
59+
position: sticky;
60+
z-index: 10;
61+
62+
.dismiss-button {
63+
align-items: center;
64+
display: flex;
65+
66+
.dismiss-text {
67+
align-items: center;
68+
display: flex;
69+
gap: $sph--x-small;
70+
}
71+
}
72+
73+
.filters {
74+
display: flex;
75+
76+
.filter-button {
77+
align-items: center;
78+
border: none;
79+
display: flex;
80+
gap: $sph--x-small;
81+
justify-content: center;
82+
margin-right: $sph--small !important;
83+
padding: calc(0.4rem - 1px);
84+
85+
/* stylelint-disable max-nesting-depth */
86+
/* stylelint-disable-next-line selector-max-type */
87+
span {
88+
padding: 0;
89+
text-align: center;
90+
}
91+
92+
/* stylelint-disable-next-line selector-class-pattern */
93+
.p-icon--info--notification {
94+
@extend %icon;
95+
@include vf-icon-info-coloured-themed;
96+
}
97+
/* stylelint-enable max-nesting-depth */
98+
}
99+
}
100+
101+
@media screen and (max-width: $breakpoint-small) {
102+
align-items: flex-start;
103+
flex-direction: column;
104+
gap: 0.5rem;
105+
margin-bottom: 0.5rem;
106+
}
107+
}
108+
109+
@media screen and (max-width: $breakpoint-small) {
110+
bottom: 2rem;
111+
left: 0;
112+
margin: 1.5rem auto;
113+
max-width: 400px;
114+
width: 95vw;
115+
}
116+
}

0 commit comments

Comments
 (0)