Skip to content

Commit

Permalink
Merge pull request #27 from qoretechnologies/feature/notifications
Browse files Browse the repository at this point in the history
Feature/notifications
  • Loading branch information
Foxhoundn authored Jan 28, 2021
2 parents 4dc866f + 13e53fd commit 8a99aa7
Show file tree
Hide file tree
Showing 18 changed files with 1,025 additions and 49 deletions.
179 changes: 179 additions & 0 deletions __tests__/notifications.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { Button } from '@blueprintjs/core';
import { act, fireEvent, render, screen } from '@testing-library/react';
import React, { useContext } from 'react';
import { ReqoreNotificationsContext, ReqoreUIProvider } from '../src/index';

const AddButton = (props: any) => {
const { addNotification } = useContext(ReqoreNotificationsContext);

return (
<Button
id='add-notification'
onClick={() =>
addNotification({
title: 'Test Notification',
content: 'I am a notification in tests',
duration: 3000,
id: props.id || Date.now(),
...props,
})
}
>
Add Notification
</Button>
);
};

const UpdateButton = (props: any) => {
const { addNotification } = useContext(ReqoreNotificationsContext);

return (
<Button
id='update-notification'
onClick={() =>
addNotification({
title: 'Updated Notification',
content: 'I am an updated notification in tests',
duration: 5000,
...props,
})
}
>
Update Notification
</Button>
);
};

beforeAll(() => {
jest.useFakeTimers();
});

test('Adds notifications and dismisses them automatically', async () => {
act(() => {
render(
<ReqoreUIProvider>
<AddButton />
</ReqoreUIProvider>
);
});

fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));

expect(document.querySelectorAll('.reqore-notification').length).toBe(5);

act(() => jest.runAllTimers());

expect(document.querySelectorAll('.reqore-notification').length).toBe(0);
});

test('Adds a notification and updates it', async () => {
act(() => {
render(
<ReqoreUIProvider>
<AddButton id='test' />
<UpdateButton id='test' />
</ReqoreUIProvider>
);
});

fireEvent.click(screen.getByText('Add Notification'));

act(() => jest.advanceTimersByTime(1000));

expect(document.querySelectorAll('.reqore-notification').length).toBe(1);

fireEvent.click(screen.getByText('Update Notification'));

expect(document.querySelectorAll('.reqore-notification').length).toBe(1);

act(() => jest.runAllTimers());

expect(document.querySelectorAll('.reqore-notification').length).toBe(0);
});

test('Notification has a click event', async () => {
const clickFn = jest.fn();

act(() => {
render(
<ReqoreUIProvider>
<AddButton id='test' onClick={clickFn} />
</ReqoreUIProvider>
);
});

fireEvent.click(screen.getByText('Add Notification'));

act(() => jest.advanceTimersByTime(1000));

fireEvent.click(document.querySelector('.reqore-notification'));

expect(clickFn).toHaveBeenCalledWith('test');
});

test('Notification has a close event', async () => {
const closeFn = jest.fn();
const finishFn = jest.fn();

act(() => {
render(
<ReqoreUIProvider>
<AddButton id='test' onClose={closeFn} onFinish={finishFn} />
</ReqoreUIProvider>
);
});

fireEvent.click(screen.getByText('Add Notification'));

act(() => jest.advanceTimersByTime(1000));

fireEvent.click(document.querySelector('.reqore-notification-close'));

expect(closeFn).toHaveBeenCalledWith('test');
expect(finishFn).toHaveBeenCalledTimes(0);
});

test('Notification has a finish event', async () => {
const finishFn = jest.fn();

act(() => {
render(
<ReqoreUIProvider>
<AddButton id='test' onFinish={finishFn} />
</ReqoreUIProvider>
);
});

fireEvent.click(screen.getByText('Add Notification'));

act(() => jest.runAllTimers());

expect(finishFn).toHaveBeenCalledWith('test');
});

test('Maximum of 5 notifications is shown at once', async () => {
act(() => {
render(
<ReqoreUIProvider>
<AddButton />
</ReqoreUIProvider>
);
});

fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));
fireEvent.click(screen.getByText('Add Notification'));

expect(document.querySelectorAll('.reqore-notification').length).toBe(5);
});
95 changes: 59 additions & 36 deletions __tests__/sidebar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { ReqoreUIProvider } from '../src';
import QorusSidebar from '../src/components/Sidebar';
import { qorusSidebarItems } from '../src/mock/menu';

test('Renders sidebar', () => {
render(<QorusSidebar items={qorusSidebarItems} path='/' />);
render(
<ReqoreUIProvider>
<QorusSidebar items={qorusSidebarItems} path='/' />
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.sidebarItem').length).toBe(7);
expect(document.querySelectorAll('.sidebarSection').length).toBe(3);
Expand All @@ -14,11 +19,13 @@ test('Sidebar can be collapsed', () => {
const handleClick = jest.fn();

render(
<QorusSidebar
items={qorusSidebarItems}
path='/'
onCollapseChange={handleClick}
/>
<ReqoreUIProvider>
<QorusSidebar
items={qorusSidebarItems}
path='/'
onCollapseChange={handleClick}
/>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.expanded').length).toBe(1);
Expand All @@ -31,7 +38,11 @@ test('Sidebar can be collapsed', () => {
});

test('Can open submenu manually', () => {
render(<QorusSidebar items={qorusSidebarItems} path='/' />);
render(
<ReqoreUIProvider>
<QorusSidebar items={qorusSidebarItems} path='/' />
</ReqoreUIProvider>
);

fireEvent.click(screen.getByText('Menu item 3'));

Expand All @@ -40,7 +51,11 @@ test('Can open submenu manually', () => {
});

test('Submenu opens automatically if path matches', () => {
render(<QorusSidebar items={qorusSidebarItems} path='/item-3/item-1' />);
render(
<ReqoreUIProvider>
<QorusSidebar items={qorusSidebarItems} path='/item-3/item-1' />
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.sidebarItem').length).toBe(10);
expect(document.querySelectorAll('.sidebarSection').length).toBe(3);
Expand All @@ -51,11 +66,13 @@ test('Bookmarks can be added and removed', () => {
const handleBookmarksChange = jest.fn();

render(
<QorusSidebar
items={qorusSidebarItems}
path='/'
onBookmarksChange={handleBookmarksChange}
/>
<ReqoreUIProvider>
<QorusSidebar
items={qorusSidebarItems}
path='/'
onBookmarksChange={handleBookmarksChange}
/>
</ReqoreUIProvider>
);

const addBookmarkButton = document.querySelector('.favorite');
Expand All @@ -80,24 +97,26 @@ test('Renders item as <p> element with onClick', () => {
const handleItemClick = jest.fn();

render(
<QorusSidebar
items={{
...qorusSidebarItems,
TestItems: {
title: 'TestItems',
items: [
{
name: 'Test',
as: 'p',
onClick: handleItemClick,
id: 'test-item-1',
icon: 'add',
},
],
},
}}
path='/'
/>
<ReqoreUIProvider>
<QorusSidebar
items={{
...qorusSidebarItems,
TestItems: {
title: 'TestItems',
items: [
{
name: 'Test',
as: 'p',
onClick: handleItemClick,
id: 'test-item-1',
icon: 'add',
},
],
},
}}
path='/'
/>
</ReqoreUIProvider>
);

const menuItem = document.querySelector('p.sidebarItem');
Expand All @@ -114,11 +133,15 @@ test('Renders item as <p> element with onClick', () => {

test('Renders custom item at the top', () => {
render(
<QorusSidebar
items={qorusSidebarItems}
path='/'
customItems={[{ element: () => <span>Hello, I am a custom item!</span> }]}
/>
<ReqoreUIProvider>
<QorusSidebar
items={qorusSidebarItems}
path='/'
customItems={[
{ element: () => <span>Hello, I am a custom item!</span> },
]}
/>
</ReqoreUIProvider>
);

expect(document.querySelectorAll('.sidebarItem').length).toBe(7);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@qoretechnologies/reqore",
"version": "0.1.49",
"version": "0.2.0",
"description": "ReQore is a UI library of components for Qorus connected apps",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
58 changes: 58 additions & 0 deletions src/components/Notifications/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react';
import styled, { css } from 'styled-components';

export interface IReqoreNotificationsWrapperProps {
children: any;
position?: IReqoreNotificationsPosition;
}

export type IReqoreNotificationsPosition =
| 'TOP'
| 'BOTTOM'
| 'TOP LEFT'
| 'TOP RIGHT'
| 'BOTTOM LEFT'
| 'BOTTOM RIGHT';

export interface IReqoreNotificationsStyle {
positions: string[];
}
const StyledNotificationsWrapper = styled.div<IReqoreNotificationsStyle>`
position: fixed;
z-index: 9999;
padding: 0 20px 20px 20px;
${({ positions }) => {
const hasTwoPositions = positions.length > 1;
if (hasTwoPositions) {
return css`
${positions[0]}: 30px;
${positions[1]}: 30px;
`;
}
return css`
${positions[0]}: 30px;
left: 50%;
transform: translateX(-50%);
`;
}}
`;

const ReqoreNotificationsWrapper: React.FC<IReqoreNotificationsWrapperProps> = ({
children,
position = 'TOP',
}) => (
<StyledNotificationsWrapper
positions={position
.split(' ')
.map((p: IReqoreNotificationsPosition | string): string =>
p.toLowerCase()
)}
>
{children}
</StyledNotificationsWrapper>
);

export default ReqoreNotificationsWrapper;
Loading

0 comments on commit 8a99aa7

Please sign in to comment.