Skip to content

Commit

Permalink
Merge pull request #60 from farrah-deriv/FEQ-2190/app-layout
Browse files Browse the repository at this point in the history
farrah/FEQ-2190/Header and footer integration
  • Loading branch information
farrah-deriv authored May 10, 2024
2 parents d2b55dc + bc14f8d commit 9651798
Show file tree
Hide file tree
Showing 31 changed files with 333 additions and 102 deletions.
28 changes: 15 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"@babel/preset-env": "^7.24.5",
"@deriv-com/api-hooks": "^0.1.19",
"@deriv-com/translations": "^1.2.0",
"@deriv-com/ui": "latest",
"@deriv-com/ui": "^1.21.1",
"@deriv-com/utils": "latest",
"@deriv/deriv-api": "^1.0.15",
"@deriv/quill-design": "^1.2.24",
Expand Down
8 changes: 6 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { BrowserRouter } from 'react-router-dom';
import { QueryParamProvider } from 'use-query-params';
import { ReactRouter5Adapter } from 'use-query-params/adapters/react-router-5';
import { Header } from '@/components';
import { AppFooter, AppHeader } from '@/components';
import { initializeI18n, TranslationProvider } from '@deriv-com/translations';
import { useDevice } from '@deriv-com/ui';
import AppContent from './routes/AppContent';

//TODO: replace with ${process.env.VITE_PROJECT_NAME}/${process.env.VITE_CROWDIN_BRANCH_NAME}
Expand All @@ -11,12 +12,15 @@ const i18nInstance = initializeI18n({
});

const App = () => {
const { isDesktop } = useDevice();

return (
<BrowserRouter>
<QueryParamProvider adapter={ReactRouter5Adapter}>
<TranslationProvider defaultLang='ID' i18nInstance={i18nInstance}>
<Header />
<AppHeader />
<AppContent />
{isDesktop && <AppFooter />}
</TranslationProvider>
</QueryParamProvider>
</BrowserRouter>
Expand Down
13 changes: 13 additions & 0 deletions src/components/AppFooter/AppFooter.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.app-footer {
padding: 2.4rem;

&__language-btn {
&:hover {
background-color: transparent !important;

span {
color: #000 !important;
}
}
}
}
32 changes: 32 additions & 0 deletions src/components/AppFooter/AppFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { LanguagesModal } from '@/components/Modals';
import { LANGUAGES } from '@/constants';
import { useModalManager } from '@/hooks/custom-hooks';
import { IconTypes } from '@deriv/quill-icons';
import { useTranslations } from '@deriv-com/translations';
import { Button, Footer } from '@deriv-com/ui';
import './AppFooter.scss';

// TODO: handle local storage values not updating after changing local storage values
const AppFooter = () => {
const { currentLang } = useTranslations();
const { hideModal, isModalOpenFor, showModal } = useModalManager();
const CountryIcon = LANGUAGES.find(lang => lang.code === currentLang)?.icon as IconTypes;

return (
<Footer className='app-footer'>
<Button
className='app-footer__language-btn'
color='black'
icon={<CountryIcon iconSize='sm' />}
onClick={() => showModal('LanguagesModal')}
size='sm'
variant='ghost'
>
{currentLang}
</Button>
{isModalOpenFor('LanguagesModal') && <LanguagesModal isModalOpen onClose={hideModal} />}
</Footer>
);
};

export default AppFooter;
23 changes: 23 additions & 0 deletions src/components/AppFooter/__tests__/AppFooter.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { render, screen } from '@testing-library/react';
import AppFooter from '../AppFooter';

jest.mock('use-query-params', () => ({
...jest.requireActual('use-query-params'),
useQueryParams: jest.fn().mockReturnValue([{}, jest.fn()]),
}));

jest.mock('@deriv-com/translations', () => ({
useTranslations: jest.fn(() => ({ currentLang: 'EN' })),
}));

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

describe('<AppFooter/>', () => {
it('should render the footer', () => {
render(<AppFooter />);
expect(screen.getByRole('button', { name: 'EN' })).toBeInTheDocument();
});
});
1 change: 1 addition & 0 deletions src/components/AppFooter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as AppFooter } from './AppFooter';
7 changes: 7 additions & 0 deletions src/components/AppHeader/AppHeader.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.app-header {
padding: 2.4rem;

@include mobile {
padding-left: 0;
}
}
93 changes: 93 additions & 0 deletions src/components/AppHeader/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { useState } from 'react';
import {
LabelPairedHouseBlankMdRegularIcon,
LegacyChevronRight2pxIcon,
LegacyMenuHamburger1pxIcon,
} from '@deriv/quill-icons';
import { useAuthData } from '@deriv-com/api-hooks';
import { Button, DerivLogo, Drawer, Header, MenuItem, Text, useDevice, Wrapper } from '@deriv-com/ui';
import { LocalStorageConstants, LocalStorageUtils, URLUtils } from '@deriv-com/utils';
import './AppHeader.scss';

// TODO: handle local storage values not updating after changing local storage values
const AppHeader = () => {
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const { isDesktop } = useDevice();
const { activeLoginid, logout } = useAuthData();
const appId = LocalStorageUtils.getValue(LocalStorageConstants.configAppId);
const serverUrl = localStorage.getItem(LocalStorageConstants.configServerURL.toString());
const oauthUrl =
appId && serverUrl
? `https://${serverUrl}/oauth2/authorize?app_id=${appId}&l=EN&&brand=deriv`
: URLUtils.getOauthURL();

return (
<Header className='app-header'>
{isDesktop ? (
<Wrapper variant='left'>
<DerivLogo
href='https://deriv.com'
logoHeight={30}
logoWidth={30}
target='_blank'
variant='wallets'
/>
<MenuItem
as='button'
className='flex gap-2 p-5'
leftComponent={<LabelPairedHouseBlankMdRegularIcon />}
>
<Text>Trader&apos;s Hub</Text>
</MenuItem>
</Wrapper>
) : (
<Wrapper variant='left'>
<Drawer
isOpen={isDrawerOpen}
onCloseDrawer={() => {
setIsDrawerOpen(false);
}}
width='300px'
>
<Drawer.Header
onCloseDrawer={() => {
setIsDrawerOpen(false);
}}
>
<Text>Menu</Text>
</Drawer.Header>
<Drawer.Content>
<div className='flex'>
<MenuItem
as='button'
className='flex gap-5 p-5'
leftComponent={<LabelPairedHouseBlankMdRegularIcon />}
rightComponent={<LegacyChevronRight2pxIcon iconSize='xs' />}
>
<Text>Trader&apos;s Hub</Text>
</MenuItem>
</div>
</Drawer.Content>
</Drawer>
<Button
icon={<LegacyMenuHamburger1pxIcon iconSize='sm' />}
onClick={() => setIsDrawerOpen(true)}
variant='ghost'
/>
</Wrapper>
)}
{activeLoginid ? (
<Button onClick={logout}>Logout</Button>
) : (
<a
className='bg-solid-coral-800 text-body-sm text-opacity-white-800 rounded-200 px-800 py-300 font-bold'
href={oauthUrl}
>
<Text>Log in</Text>
</a>
)}
</Header>
);
};

export default AppHeader;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useAuthData } from '@deriv-com/api-hooks';
import { URLUtils } from '@deriv-com/utils';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Header from '../Header';
import AppHeader from '../AppHeader';

const mockUseAuthData = useAuthData as jest.Mock;
jest.mock('@deriv-com/api-hooks', () => ({
Expand All @@ -17,14 +17,14 @@ jest.mock('@deriv-com/ui', () => ({
),
}));

describe('<Header/>', () => {
describe('<AppHeader/>', () => {
it('should render the header', () => {
render(<Header />);
render(<AppHeader />);
expect(screen.getByRole('link', { name: 'Login' })).toHaveAttribute('href', URLUtils.getOauthURL());
});
it('should handle the logout functionality if there is an active login id', async () => {
mockUseAuthData.mockReturnValue({ activeLoginid: '12345', logout: jest.fn() });
render(<Header />);
render(<AppHeader />);

const logoutButton = screen.getByRole('button', { name: 'Logout' });
const { logout } = mockUseAuthData();
Expand Down
1 change: 1 addition & 0 deletions src/components/AppHeader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as AppHeader } from './AppHeader';
16 changes: 0 additions & 16 deletions src/components/Header/Header.scss

This file was deleted.

34 changes: 0 additions & 34 deletions src/components/Header/Header.tsx

This file was deleted.

1 change: 0 additions & 1 deletion src/components/Header/index.ts

This file was deleted.

23 changes: 23 additions & 0 deletions src/components/Modals/LanguagesModal/LanguagesModal.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.languages-modal {
&__body {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 1rem;
padding: 2rem;

&-button {
height: auto;
display: flex;
align-items: center;
flex-direction: column;

&:hover {
background-color: transparent !important;

span {
color: #000 !important;
}
}
}
}
}
Loading

0 comments on commit 9651798

Please sign in to comment.