diff --git a/ui/src/Components/notification/index.js b/ui/src/Components/notification/index.js new file mode 100644 index 0000000..632731c --- /dev/null +++ b/ui/src/Components/notification/index.js @@ -0,0 +1,32 @@ +import { NOTIFICATION_TEXT_COLOR } from "../../Constant/constant"; +import { useNotificationContext } from "../../Context/NotificationContext"; + +const NotificationContainer = () => { + const { notificationQueue } = useNotificationContext(); + + return ( +
+ {notificationQueue.map((notification, index) => ( +
+

{notification.message}

+
+ ))} +
+ ); +}; + +export default NotificationContainer; diff --git a/ui/src/Constant/constant.js b/ui/src/Constant/constant.js index c9355c4..f9f47f0 100644 --- a/ui/src/Constant/constant.js +++ b/ui/src/Constant/constant.js @@ -1,5 +1,13 @@ export const FOOTER = { - "COPYRIGHT": "Copyright 2022-2023", - "RDS_WEBSITE": "RDS Website", - "DISCORD_SERVER":"Discord server" -} \ No newline at end of file + COPYRIGHT: "Copyright 2022-2023", + RDS_WEBSITE: "RDS Website", + DISCORD_SERVER: "Discord server", +}; + +export const NOTIFICATION_TEXT_COLOR = { + SUCCESS: "rgb(95, 214, 95)", + WARNING: "rgb(238, 241, 41)", + ERROR: "rgb(253, 81, 81)", +}; + +export const NOTIFICATION_TIMEOUT = 2000; diff --git a/ui/src/Context/NotificationContext.js b/ui/src/Context/NotificationContext.js new file mode 100644 index 0000000..d89a32f --- /dev/null +++ b/ui/src/Context/NotificationContext.js @@ -0,0 +1,38 @@ +import { createContext, useContext, useState } from "react"; +import { NOTIFICATION_TIMEOUT } from "../Constant/constant"; + +export const NotificationContext = createContext({ + createNotifcation() {}, + notificationQueue: [], +}); + +export const useNotificationContext = () => useContext(NotificationContext); + +const NotificationProvider = ({ children }) => { + const [notificationQueue, setNotificationQueue] = useState([]); + + /** + * + * @param {type : string, message : string} notificationContent + */ + const createNotifcation = (notificationContent) => { + setNotificationQueue((prev) => [...prev, notificationContent]); + + setTimeout(() => { + setNotificationQueue((prev) => { + let [, ...data] = prev; + return data; + }); + }, NOTIFICATION_TIMEOUT); + }; + + return ( + + {children} + + ); +}; + +export default NotificationProvider; diff --git a/ui/src/index.js b/ui/src/index.js index d563c0f..73b6ae8 100644 --- a/ui/src/index.js +++ b/ui/src/index.js @@ -1,13 +1,16 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import React from "react"; +import ReactDOM from "react-dom/client"; +import "./index.css"; +import App from "./App"; +import reportWebVitals from "./reportWebVitals"; +import NotificationProvider from "./Context/NotificationContext"; -const root = ReactDOM.createRoot(document.getElementById('root')); +const root = ReactDOM.createRoot(document.getElementById("root")); root.render( - + + + ); diff --git a/ui/src/test/Notification.test.js b/ui/src/test/Notification.test.js new file mode 100644 index 0000000..040ba6a --- /dev/null +++ b/ui/src/test/Notification.test.js @@ -0,0 +1,82 @@ +import { render, screen, waitFor } from "@testing-library/react"; +import "@testing-library/jest-dom"; + +import NotificationContainer from "../Components/notification"; +import { + NOTIFICATION_TIMEOUT, + NOTIFICATION_TEXT_COLOR, +} from "../Constant/constant"; +import { NotificationContext } from "../Context/NotificationContext"; +import React from "react"; + +const customRender = (ui, { value, ...options }) => + render( + + {ui} + , + options + ); + +let notificationContent = { message: "Hello World", type: "success" }; + +let mockNotificationContext = {}; + +beforeEach(() => { + mockNotificationContext = { + notificationQueue: [], + createNotifcation(data) { + this.notificationQueue = [data, ...this.notificationQueue]; + + setTimeout(() => { + let [, ...data] = this.notificationQueue; + this.notificationQueue = data; + }, NOTIFICATION_TIMEOUT); + }, + }; +}); + +afterEach(() => { + mockNotificationContext = {}; +}); + +describe("Notification Component", () => { + it("should check if the notification wrapper renders", async () => { + render(); + expect(screen.getByTestId("notificationcontainer")).toBeInTheDocument(); + }); + + it("should check the function cycle of the notification component", async () => { + mockNotificationContext.createNotifcation(notificationContent); + customRender(, { value: mockNotificationContext }); + + let notificationChild = await waitFor(() => + screen.getByTestId("notificationchild") + ); + + expect(notificationChild).toHaveTextContent(notificationContent.message); + }); + + it("should check if the color rendered is the same one as passed", async () => { + mockNotificationContext.createNotifcation(notificationContent); + customRender(, { value: mockNotificationContext }); + + let notificationChild = await waitFor(() => + screen.getByTestId("notificationchild") + ); + expect(notificationChild.style.backgroundColor).toEqual( + NOTIFICATION_TEXT_COLOR[notificationContent.type.toUpperCase()] + ); + }); + it("should check if the heading rendered as h3", async () => { + mockNotificationContext.createNotifcation(notificationContent); + customRender(, { value: mockNotificationContext }); + + let notificationChild = await waitFor(() => + screen.getByTestId("notificationchild") + ); + expect(screen.getByRole("alert").nodeName).toEqual('H3') + expect(screen.getByRole("alert")).toHaveTextContent( + notificationContent.message + ); + }); +});