Skip to content

Commit

Permalink
fix: conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
niloofar-deriv committed May 13, 2024
2 parents a33c511 + 8a80f8e commit 5605124
Show file tree
Hide file tree
Showing 10 changed files with 528 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/components/AppLayout/Notifications/Notification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Text } from "../../Text";
import { TNotificationObject } from "./types";

export const Notification = ({
icon,
title,
message,
buttonAction,
actionText,
}: TNotificationObject) => {
return (
<div className="notification">
<div className="notification__container">
<div className="notification__icon">{icon}</div>
<div className="notification__text">
<Text
as="h3" className="notification__title">{title}</Text>
<div className="notification__message">
<Text as="p">{message}</Text>
</div>
</div>
</div>
<div className="notification__button-container">
<button className="notification__button" onClick={buttonAction}>
{actionText}
</button>
</div>
</div>
);
};
Notification.displayName = 'Notification'
1 change: 1 addition & 0 deletions src/components/AppLayout/Notifications/ic-box.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
138 changes: 138 additions & 0 deletions src/components/AppLayout/Notifications/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
.notifications {
border-radius: 9px;
background: var(--system-light-8-primary-background, #fff);
box-shadow:
0px 0px 16px 0px rgba(0, 0, 0, 0.08),
0px 16px 16px 0px rgba(0, 0, 0, 0.08);
min-height: 525px !important;
display: flex;
flex-direction: column;
&__empty {
width: 264px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: var(--text-less-prominent, #999);
flex-grow: 1;

@include mobile {
width: unset;
}
p {
justify-content: center;
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: 20px;
color: var(--text-less-prominent, #999);
}
span {
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 18px;
color: var(--text-less-prominent, #999);
}
}
.notification {
background: var(--light-8-primary-background, #fff);
box-shadow: 0px -1px 0px 0px #f2f3f4 inset;
padding: 8px 16px;
font-size: 12px;

&__container {
display: flex;
flex-direction: row;
}
&__text {
margin-left: 8px;
}
&__icon {
width: 16px;
height: 16px;
padding: 2px 0;
}
&__title {
color: var(--system-light-1-prominent-text, #333);
font-family: "IBM Plex Sans";
font-size: 14px;
font-style: normal;
font-weight: 700;
line-height: 20px;
}
&__message {
color: var(--system-light-2-general-text, #333);
font-family: "IBM Plex Sans";
font-style: normal;
font-weight: 400;
line-height: 18px;
}
&__button {
&-container {
margin-top: 8px;
display: flex;
justify-content: flex-end;
}
display: flex;
align-items: center;
border-radius: 4px;
border: 1px solid var(--light-3-less-prominent-text, #999);
height: 24px;
padding: 3px 8px;
font-size: 12px;
font-style: normal;
font-weight: 700;
line-height: 18px;
}
}
&__header {
&-desktop {
display: flex;
justify-content: center;
margin-bottom: 7px;
font-size: 14px;
font-style: normal;
font-weight: 700;
margin: 8px 0;
}
}
&__footer {
align-items: flex-end;
font-size: 12px;
font-style: normal;
font-weight: 700;
display: flex;
justify-content: flex-end;
margin-top: auto;
min-height: unset !important;
&-box {
width: 100%;
box-shadow: 0px 1px 0px 0px #f2f3f4 inset;
justify-content: flex-end;
display: flex;
}

// width: 100vw;
@include mobile {
font-size: 14px;
padding: 8px 16px !important;
align-items: flex-start !important;
}
&__clear-button {
margin: 8px 16px 8px 0;
display: inline-flex;
height: 32px;
padding: 6px 16px;
justify-content: flex-end;
align-items: center;
flex-shrink: 0;
border-radius: 4px;
border: 1px solid var(--system-light-3-less-prominent-text, #999);
@include mobile {
margin: 0;
}
}
}
}
132 changes: 132 additions & 0 deletions src/components/AppLayout/Notifications/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Fragment, useRef } from "react";
import { Notification } from "./Notification";
import { TNotificationsProps } from "./types";
import "./index.scss";
import { useDevice } from "../../../hooks";
import { ContextMenu } from "../../ContextMenu";
import { Modal } from "../../Modal";
import { Text } from "../../Text";
import { useOnClickOutside } from "usehooks-ts";
import Icon from "./ic-box.svg";

export const Notifications = ({
notifications,
clearNotificationsCallback = () => {},
isOpen,
setIsOpen,
componentConfig,
}: TNotificationsProps) => {
const { isMobile } = useDevice();
const notificationsRef = useRef(null);

useOnClickOutside(notificationsRef, (e) => {
e.stopPropagation();
setIsOpen(!isOpen);
});

return (
<Fragment>
{isMobile && (
<Modal
className="notifications"
isOpen={isOpen}
onRequestClose={() => {
setIsOpen(false);
}}
>
<Modal.Header
onRequestClose={() => {
setIsOpen(false);
}}
>
<Text
as="div"
weight="bold"
className="deriv-modal__header-title"
>
{componentConfig.modalTitle}
</Text>
</Modal.Header>
{notifications.length === 0 && (
<div className="notifications__empty">
<img src={Icon} />
<Text
as="p"
align="center"
className="notifications__empty-text"
>
{componentConfig.noNotificationsTitle}
</Text>
<Text as="span" align="center">
{componentConfig.noNotificationsMessage}
</Text>
</div>
)}
{notifications.map((notification) => (
<Notification
key={notification.title}
{...notification}
/>
))}
<Modal.Footer className="notifications__footer">
<button
className="notifications__footer__clear-button"
onClick={() => {
clearNotificationsCallback();
setIsOpen(false);
}}
>
{componentConfig.clearButtonText}
</button>
</Modal.Footer>
</Modal>
)}
{!isMobile && (
<ContextMenu
ref={notificationsRef}
isOpen={isOpen}
className="notifications"
>
<Text as="span" className="notifications__header-desktop">
{componentConfig.modalTitle}
</Text>
{notifications.length === 0 && (
<div className="notifications__empty">
<img src={Icon} />
<Text
as="p"
align="center"
className="notifications__empty-text"
>
{componentConfig.noNotificationsTitle}
</Text>
<Text as="span" align="center">
{componentConfig.noNotificationsMessage}
</Text>
</div>
)}
{notifications.map((notification) => (
<Notification
key={notification.title}
{...notification}
/>
))}
<div className="notifications__footer">
<div className="notifications__footer-box">
<button
className="notifications__footer__clear-button"
onClick={() => {
setIsOpen(false);
clearNotificationsCallback();
}}
>
{componentConfig.clearButtonText}
</button>
</div>
</div>
</ContextMenu>
)}
</Fragment>
);
};
Notifications.displayName = "Notifications";
21 changes: 21 additions & 0 deletions src/components/AppLayout/Notifications/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ReactNode } from "react";

export type TNotificationObject = {
icon: ReactNode;
title: string;
message: string;
buttonAction: () => void;
actionText: string;
};
export type TNotificationsProps = {
notifications: TNotificationObject[];
clearNotificationsCallback: () => void;
setIsOpen: (state: boolean) => void;
isOpen: boolean;
componentConfig: {
clearButtonText: string;
modalTitle: string;
noNotificationsTitle: string;
noNotificationsMessage: string;
};
};
28 changes: 28 additions & 0 deletions src/components/AppLayout/__test__/notification.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import { render } from "@testing-library/react";
import { Notification } from "../Notifications/Notification";
import userEvent from "@testing-library/user-event";

describe("Notification Component", () => {
it("renders the notification with title, message, and button", async () => {
const mockAction = jest.fn();
const { getByText, getByRole } = render(
<Notification
icon={<span>Icon</span>}
title="Test Title"
message="Test message"
buttonAction={mockAction}
actionText="Click Me"
/>,
);

// Check if the title and message are in the document
expect(getByText("Test Title")).toBeInTheDocument();
expect(getByText("Test message")).toBeInTheDocument();

// Check if the button is rendered and clickable
const button = getByRole("button", { name: "Click Me" });
await userEvent.click(button);
expect(mockAction).toHaveBeenCalled(); // Ensure button action is triggered
});
});
Loading

0 comments on commit 5605124

Please sign in to comment.