Skip to content

Commit 4b91137

Browse files
feat(notifications): revamp contract (#48)
* feat(notifications): revamp contract * fix: notification references
1 parent 262dbfc commit 4b91137

File tree

5 files changed

+135
-105
lines changed

5 files changed

+135
-105
lines changed
Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { useRef } from "react";
2+
23
import Icon from "../Icon";
3-
import { ShowNotification, NotificationRef } from "../Notification";
4+
import { NotificationProvider, useNotification } from "../Notification";
45
import { Container, CopyBtn, Viewer } from "./Codeblock.styles";
56

67
interface CodeblockProps {
@@ -9,18 +10,18 @@ interface CodeblockProps {
910
copy?: boolean;
1011
className?: string;
1112
}
12-
1313
const Codeblock = ({ lang = "text", children = "", copy = false, className = "" }: CodeblockProps) => {
14-
const notificationRef = useRef<NotificationRef>();
14+
const { showSuccess, showError } = useNotification()
15+
1516
const codeRef = useRef(null);
1617
function fallbackCopyTextToClipboard() {
1718
const node = codeRef?.current;
1819
if (window.getSelection && node) {
1920
window.getSelection()?.selectAllChildren(node);
2021
document.execCommand("Copy");
21-
notificationRef?.current?.showSuccess("Copied to Clipboard");
22+
showSuccess("Copied to Clipboard");
2223
} else {
23-
notificationRef?.current?.showError("Unable to Copy");
24+
showError("Unable to Copy");
2425
}
2526
}
2627

@@ -29,28 +30,34 @@ const Codeblock = ({ lang = "text", children = "", copy = false, className = ""
2930
navigator.clipboard
3031
.writeText(children)
3132
.then(() => {
32-
notificationRef?.current?.showSuccess("Copied to Clipboard");
33+
showSuccess("Copied to Clipboard");
3334
})
3435
.catch(() => {
35-
notificationRef?.current?.showError("Unable to Copy");
36+
showError("Unable to Copy");
3637
});
3738
} else {
3839
fallbackCopyTextToClipboard();
3940
}
4041
};
42+
4143
return (
42-
<Container className={className}>
43-
{copy ? (
44-
<CopyBtn onClick={handleCopy}>
45-
<Icon name="copy2" styleOverride={{ color: "white" }} />
46-
</CopyBtn>
47-
) : null}
48-
<Viewer lang={lang} ref={codeRef}>
49-
{children}
50-
</Viewer>
51-
<ShowNotification ref={notificationRef} />
52-
</Container>
44+
<NotificationProvider>
45+
<Container className={className}>
46+
{copy ? (
47+
<CopyBtn onClick={handleCopy}>
48+
<Icon name="copy2" styleOverride={{ color: "white" }} />
49+
</CopyBtn>
50+
) : null}
51+
<Viewer lang={lang} ref={codeRef}>
52+
{children}
53+
</Viewer>
54+
</Container>
55+
</NotificationProvider>
5356
);
5457
};
5558

56-
export default Codeblock;
59+
export default (props: CodeblockProps) => (
60+
<NotificationProvider>
61+
<Codeblock {...props} />
62+
</NotificationProvider>
63+
);

packages/apsara-ui/src/Notification/Notification.stories.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,47 @@
1+
import React from "react";
2+
13
import Button from "../Button";
2-
import React, { useRef } from "react";
34
import Icon from "../Icon";
4-
import { NotificationRef, ShowNotification } from "./Notification";
5+
import { NotificationProvider, useNotification } from "./Notification";
56

67
export default {
78
title: "Feedback/Notifications",
8-
component: ShowNotification,
99
};
1010

1111
export const notifications = () => {
12-
const toastRef = useRef<NotificationRef>();
12+
return (
13+
<NotificationProvider>
14+
<_Notifications />
15+
</NotificationProvider>
16+
)
17+
}
18+
19+
const _Notifications = () => {
20+
const {
21+
showError,
22+
showNotification,
23+
showSuccess,
24+
} = useNotification()
25+
1326
return (
1427
<div style={{ display: "flex" }}>
1528
<Button
1629
onClick={() => {
17-
toastRef?.current?.showSuccess("Success", "this is a test");
30+
showSuccess("Success", "this is a test");
1831
}}
1932
>
2033
show Success
2134
</Button>
2235
<Button
2336
onClick={() => {
24-
toastRef?.current?.showError("Error", "this is a test");
37+
showError("Error", "this is a test");
2538
}}
2639
>
2740
show Error
2841
</Button>
2942
<Button
3043
onClick={() => {
31-
toastRef?.current?.showNotification({
44+
showNotification({
3245
title: "Alerts Configured",
3346
icon: <Icon name="copy2" />,
3447
content:
@@ -57,7 +70,6 @@ export const notifications = () => {
5770
>
5871
custom notification with action
5972
</Button>
60-
<ShowNotification ref={toastRef} />
6173
</div>
6274
);
6375
};

packages/apsara-ui/src/Notification/Notification.tsx

Lines changed: 87 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/* eslint-disable react/display-name */
2-
import React, { forwardRef, Ref, useImperativeHandle, useState } from "react";
1+
import React, { createContext, useContext, useState } from "react";
2+
33
import Icon from "../Icon";
44
import {
55
Toast,
@@ -12,22 +12,102 @@ import {
1212
IconTitleWrapper,
1313
} from "./Notification.styles";
1414

15-
const CrossIcon = <Icon name="cross" />;
16-
17-
interface NotificationProps {
15+
export interface Notification {
1816
title: string;
1917
content?: React.ReactNode;
2018
icon?: React.ReactNode;
2119
footer?: React.ReactNode;
2220
id?: string;
2321
}
2422

25-
export interface NotificationRef {
26-
showNotification: (toast: NotificationProps) => void;
23+
export interface Notifier {
24+
showNotification: (toast: Notification) => void;
2725
showSuccess: (title: string, content?: string) => void;
2826
showError: (title: string, content?: string) => void;
2927
}
3028

29+
export const useNotification = () => {
30+
return useContext(NotificationContext)
31+
}
32+
33+
export const NotificationProvider = ({ children }: any) => {
34+
const [toasts, setToasts] = useState<Notification[]>([]);
35+
36+
const showNotification = (toast: Notification) => {
37+
setToasts([...toasts, { ...toast, id: uuid() }]);
38+
}
39+
40+
const showSuccess = (title: string, content?: string) => {
41+
setToasts([
42+
...toasts,
43+
{
44+
title: title,
45+
content: content,
46+
id: uuid(),
47+
icon: <Icon name="checkcircle" color="green" size={32} />,
48+
},
49+
]);
50+
}
51+
52+
const showError = (title: string, content?: string) => {
53+
setToasts([
54+
...toasts,
55+
{
56+
title: title,
57+
content: content,
58+
id: uuid(),
59+
icon: <Icon name="error" color="red" size={32} />,
60+
},
61+
]);
62+
}
63+
64+
return (
65+
<NotificationContext.Provider value={{
66+
showNotification,
67+
showSuccess,
68+
showError,
69+
}}>
70+
{children}
71+
<ToastProvider swipeDirection="right">
72+
{toasts.map((toast) => {
73+
return (
74+
<Toast
75+
key={toast.id}
76+
onOpenChange={() => {
77+
setToasts(toasts.filter((t) => t.id !== toast.id));
78+
}}
79+
duration={3000}
80+
>
81+
<ToastTitle>
82+
<IconTitleWrapper>
83+
{toast.icon || defaultIcon}
84+
{toast.title}
85+
</IconTitleWrapper>
86+
</ToastTitle>
87+
<ToastDescription asChild>
88+
<DescriptionWrapper>
89+
{toast.content}
90+
{toast.footer}
91+
</DescriptionWrapper>
92+
</ToastDescription>
93+
<ToastAction asChild altText="Goto schedule to undo">
94+
<Icon name="cross" />
95+
</ToastAction>
96+
</Toast>
97+
);
98+
})}
99+
<ToastViewport />
100+
</ToastProvider>
101+
</NotificationContext.Provider>
102+
);
103+
};
104+
105+
const NotificationContext = createContext<Notifier>({
106+
showNotification: (_toast: Notification) => null,
107+
showSuccess: (_title: string, _content?: string) => null,
108+
showError: (_title: string, _content?: string) => null,
109+
})
110+
31111
const uuid = () => {
32112
let dt = new Date().getTime();
33113
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
@@ -38,70 +118,3 @@ const uuid = () => {
38118
};
39119

40120
const defaultIcon = <Icon name="checkcircle" color="green" size={32} />;
41-
42-
export const ShowNotification = forwardRef((_, ref?: Ref<NotificationRef | undefined>) => {
43-
const [toasts, setToasts] = useState<NotificationProps[]>([]);
44-
45-
useImperativeHandle(ref, () => ({
46-
showNotification(toast: NotificationProps) {
47-
setToasts([...toasts, { ...toast, id: uuid() }]);
48-
},
49-
50-
showSuccess(title: string, content?: string) {
51-
setToasts([
52-
...toasts,
53-
{
54-
title: title,
55-
content: content,
56-
id: uuid(),
57-
icon: <Icon name="checkcircle" color="green" size={32} />,
58-
},
59-
]);
60-
},
61-
62-
showError(title: string, content?: string) {
63-
setToasts([
64-
...toasts,
65-
{
66-
title: title,
67-
content: content,
68-
id: uuid(),
69-
icon: <Icon name="error" color="red" size={32} />,
70-
},
71-
]);
72-
},
73-
}));
74-
75-
return (
76-
<ToastProvider swipeDirection="right">
77-
{toasts.map((toast) => {
78-
return (
79-
<Toast
80-
key={toast.id}
81-
onOpenChange={() => {
82-
setToasts(toasts.filter((t) => t.id !== toast.id));
83-
}}
84-
duration={3000}
85-
>
86-
<ToastTitle>
87-
<IconTitleWrapper>
88-
{toast.icon || defaultIcon}
89-
{toast.title}
90-
</IconTitleWrapper>
91-
</ToastTitle>
92-
<ToastDescription asChild>
93-
<DescriptionWrapper>
94-
{toast.content}
95-
{toast.footer}
96-
</DescriptionWrapper>
97-
</ToastDescription>
98-
<ToastAction asChild altText="Goto schedule to undo">
99-
{CrossIcon}
100-
</ToastAction>
101-
</Toast>
102-
);
103-
})}
104-
<ToastViewport />
105-
</ToastProvider>
106-
);
107-
});
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { ShowNotification, NotificationRef } from "./Notification";
1+
export * from "./Notification";

packages/apsara-ui/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import VTable from "./Table/VirtualisedTable";
77
import Text from "./Text";
88
import Tag from "./Tag";
99
import States from "./States";
10-
import { ShowNotification, NotificationRef } from "./Notification";
1110
import FormBuilder from "./FormBuilder";
1211
import Learn from "./Learn";
1312
import Markdown from "./Markdown";
@@ -40,6 +39,7 @@ import Themes from "./Provider/theme";
4039
export { default as DiffTimeline } from "./DiffTimeline";
4140
export { DynamicList } from "./DynamicList";
4241

42+
export * from "./Notification";
4343
export {
4444
Button,
4545
Icon,
@@ -54,8 +54,6 @@ export {
5454
Tag,
5555
FormBuilder,
5656
States,
57-
ShowNotification,
58-
NotificationRef,
5957
Learn,
6058
Markdown,
6159
ContentLayout,

0 commit comments

Comments
 (0)