Skip to content

Commit

Permalink
[UPM-1153]/evgeniy/network status component replace to quill-ui compo…
Browse files Browse the repository at this point in the history
…nent (#15958)

* chore: [UPM-1153]/evgeniy/network status component replace to quill-ui component

* chore: remove test button

* chore: text with localize

* chore: refactor toast component

* chore: added tests

* chore: added tests for dynamic status

* fix: failing positions tests

* chore: update package-lock

* chore: update quill-ui

* chore: update package-lock

* chore: update package-lock

* chore: move settimeout to useeffect

* chore: remove ^ for quill version

* chore: fix tests, remove snackbar for tablet

* chore: update styles to center snackbar, update tests

* chore: update package lock

* chore: update package-lock
  • Loading branch information
yauheni-deriv authored Aug 14, 2024
1 parent 44100bf commit 3d4cc26
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 64 deletions.
4 changes: 2 additions & 2 deletions packages/account/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Routes from './Containers/routes';
import ResetTradingPassword from './Containers/reset-trading-password';
import { NetworkStatusToastErrorPopup } from './Containers/toast-popup';
import NetworkStatusToastPopup from './Components/network-status-toast-popup/network-status-toast-popup';
import { APIProvider } from '@deriv/api';
import { StoreProvider } from '@deriv/stores';
import { TCoreStores } from '@deriv/stores/types';
Expand All @@ -21,14 +21,14 @@ const App = ({ passthrough }: TAppProps) => {

return (
<StoreProvider store={root_store}>
<NetworkStatusToastErrorPopup />
<APIProvider>
<POIProvider>
{Notifications && <Notifications />}
<Routes />
<ResetTradingPassword />
</POIProvider>
</APIProvider>
<NetworkStatusToastPopup />
</StoreProvider>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { act, render, screen } from '@testing-library/react';
import { mockStore, StoreProvider } from '@deriv/stores';
import { useDevice } from '@deriv-com/ui';
import NetworkStatusToastPopup from '../network-status-toast-popup';

jest.mock('@deriv-com/ui', () => ({
...jest.requireActual('@deriv-com/ui'),
useDevice: jest.fn(() => ({ isMobile: true })),
}));

jest.mock('@deriv-com/quill-ui', () => ({
Snackbar: jest.fn(({ message, isVisible }) => (isVisible ? <div>{message}</div> : null)),
}));

describe('NetworkStatusToastPopup', () => {
const offline = 'Offline';
const online = 'Online';
const connecting = 'Connecting to server';

let popup_root_el: Element;

beforeAll(() => {
popup_root_el = document.createElement('div');
popup_root_el.setAttribute('id', 'popup_root');
document.body.appendChild(popup_root_el);
});

afterAll(() => {
document.body.removeChild(popup_root_el);
});

const mock_store = mockStore({
common: { network_status: { class: offline.toLowerCase(), tooltip: offline } },
});

afterEach(() => {
jest.clearAllMocks();
});

const wrapper = ({ children }: { children: JSX.Element }) => (
<StoreProvider store={mock_store}>{children}</StoreProvider>
);

it('should render NetworkStatusToastPopup when offline', () => {
render(<NetworkStatusToastPopup />, { wrapper });

expect(screen.queryByText(connecting)).not.toBeInTheDocument();
expect(screen.queryByText(online)).not.toBeInTheDocument();
expect(screen.getByText(offline)).toBeInTheDocument();
});

it('should render NetworkStatusToastPopup when trying to connect', async () => {
jest.useFakeTimers();

mock_store.common.network_status.class = connecting.toLowerCase();
mock_store.common.network_status.tooltip = connecting;

const { rerender } = render(
<StoreProvider store={mock_store}>
<NetworkStatusToastPopup />
</StoreProvider>
);

expect(screen.queryByText(offline)).not.toBeInTheDocument();
expect(screen.queryByText(online)).not.toBeInTheDocument();
expect(screen.getByText(connecting)).toBeInTheDocument();

const mock_store_updated = mockStore({
common: { network_status: { class: online.toLowerCase(), tooltip: online } },
});

rerender(
<StoreProvider store={mock_store_updated}>
<NetworkStatusToastPopup />
</StoreProvider>
);

expect(screen.queryByText(offline)).not.toBeInTheDocument();
expect(screen.queryByText(connecting)).not.toBeInTheDocument();
expect(screen.getByText(online)).toBeInTheDocument();

act(() => {
jest.advanceTimersByTime(1500);
});

expect(screen.queryByText(offline)).not.toBeInTheDocument();
expect(screen.queryByText(connecting)).not.toBeInTheDocument();
expect(screen.queryByText(online)).not.toBeInTheDocument();

jest.useRealTimers();
});

it('should not render NetworkStatusToastPopup when no message', () => {
mock_store.common.network_status.class = offline.toLowerCase();
mock_store.common.network_status.tooltip = '';

render(<NetworkStatusToastPopup />, { wrapper });

expect(screen.queryByText(offline)).not.toBeInTheDocument();
expect(screen.queryByText(connecting)).not.toBeInTheDocument();
expect(screen.queryByText(online)).not.toBeInTheDocument();
});

it('should not render NetworkStatusToastPopup for desktop', () => {
(useDevice as jest.Mock).mockReturnValue({
isDesktop: true,
isTablet: false,
isMobile: false,
});
mock_store.common.network_status.class = offline.toLowerCase();
mock_store.common.network_status.tooltip = offline;

render(<NetworkStatusToastPopup />, { wrapper });

expect(screen.queryByText(offline)).not.toBeInTheDocument();
expect(screen.queryByText(connecting)).not.toBeInTheDocument();
expect(screen.queryByText(online)).not.toBeInTheDocument();
});

it('should not render NetworkStatusToastPopup for tablet', () => {
(useDevice as jest.Mock).mockReturnValue({
isDesktop: false,
isTablet: true,
isMobile: false,
});
mock_store.common.network_status.class = offline.toLowerCase();
mock_store.common.network_status.tooltip = offline;

render(<NetworkStatusToastPopup />, { wrapper });

expect(screen.queryByText(offline)).not.toBeInTheDocument();
expect(screen.queryByText(connecting)).not.toBeInTheDocument();
expect(screen.queryByText(online)).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import NetworkStatusToastPopup from './network-status-toast-popup';

export default NetworkStatusToastPopup;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.network-status {
&__container {
.snackbar {
//center fixed element with dynamic width
left: 50%;
transform: translateX(-50%);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useEffect, useLayoutEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { observer, useStore } from '@deriv/stores';
import { useDevice } from '@deriv-com/ui';
import { Snackbar } from '@deriv-com/quill-ui';
import './network-status-toast-popup.scss';

type TNetworkStatusToast = {
status: string;
portal_id: string;
message: string;
};

const NetworkStatusToast = ({ status, portal_id, message }: TNetworkStatusToast) => {
const [is_visible, setIsVisible] = useState(false);
const { isMobile } = useDevice();
const new_portal_id = document.getElementById(portal_id);

useEffect(() => {
if (!new_portal_id || !message || !isMobile) return;
if (!is_visible && status !== 'online') {
setIsVisible(true);
} else if (is_visible && status === 'online') {
setTimeout(() => {
setIsVisible(false);
}, 1500);
}
}, [isMobile, is_visible, message, new_portal_id, status]);

if (!new_portal_id || !message || !isMobile) return null;

return createPortal(
<div className='network-status__container'>
<Snackbar hasCloseButton={false} message={message} isVisible={is_visible} />
</div>,
new_portal_id
);
};

const NetworkStatusToastPopup = observer(() => {
const {
common: { network_status },
} = useStore();
return <NetworkStatusToast portal_id='popup_root' message={network_status.tooltip} status={network_status.class} />;
});

export default NetworkStatusToastPopup;
60 changes: 0 additions & 60 deletions packages/account/src/Containers/toast-popup.tsx

This file was deleted.

5 changes: 3 additions & 2 deletions packages/account/src/Sections/Security/Passkeys/passkeys.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { Fragment, useEffect, useRef, useState } from 'react';
import { Redirect, useHistory } from 'react-router-dom';
import { InlineMessage, Loading } from '@deriv/components';
import { useGetPasskeysList, useRegisterPasskey, useRenamePasskey } from '@deriv/hooks';
Expand Down Expand Up @@ -39,7 +39,7 @@ const Passkeys = observer(() => {

const error_modal_timeout = useRef<ReturnType<typeof setTimeout> | null>(null);
const snackbar_timeout = useRef<ReturnType<typeof setTimeout> | null>(null);
const prev_passkey_status = React.useRef<TPasskeysStatus>(PASSKEY_STATUS_CODES.LIST);
const prev_passkey_status = useRef<TPasskeysStatus>(PASSKEY_STATUS_CODES.LIST);

const history = useHistory();

Expand Down Expand Up @@ -72,6 +72,7 @@ const Passkeys = observer(() => {
} else {
setPasskeyStatus(PASSKEY_STATUS_CODES.LIST);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [is_passkeys_list_loading, passkeys_list?.length]);

useEffect(() => {
Expand Down

0 comments on commit 3d4cc26

Please sign in to comment.