Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 🎨 migrated account component to TSX #47

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import { MemoryRouter, BrowserRouter } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import { StoreProvider, mockStore } from '@deriv/stores';
import { routes } from '@deriv/shared';
import { TRoute } from 'Types';
import Account from '../account';

jest.mock('../../Account/page-overlay-wrapper', () => jest.fn(() => <div>MockPageOverlayWrapper</div>));

jest.mock('@deriv/components', () => ({
...jest.requireActual('@deriv/components'),
Loading: () => <div>MockLoading</div>,
}));

describe('Account', () => {
const store = mockStore({
ui: {
is_account_settings_visible: true,
},
});

const route_list: Array<TRoute> = [
{
getTitle: () => 'Profile',
icon: 'mockIcon',
subroutes: [
{
path: routes.personal_details,
component: () => <div>MockPersonalDetails</div>,
getTitle: () => 'Personal details',
default: true,
},
{
path: routes.trading_assessment,
component: () => <div>MockTradeAssessment</div>,
getTitle: () => 'Trade assessment',
},
],
},
];

const mock_props: React.ComponentProps<typeof Account> = {
routes: route_list,
};

const mock_route = routes.personal_details;

const renderComponent = ({ store_config = store, route = mock_route, props = mock_props }) =>
render(
<MemoryRouter initialEntries={[route]}>
<BrowserRouter>
<StoreProvider store={store_config}>
<Account {...props} />
</StoreProvider>
</BrowserRouter>
</MemoryRouter>
);

it('should render account page', () => {
renderComponent({});
expect(screen.getByText('MockPageOverlayWrapper')).toBeInTheDocument();
});

it('should render loader while the client is still logging in', () => {
const new_store_config = mockStore({
client: {
is_logging_in: true,
},
});

renderComponent({ store_config: new_store_config });
expect(screen.getByText('MockLoading')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import TradingHubLogout from '../tradinghub-logout';

describe('TradingHubLogout', () => {
const mock_props: React.ComponentProps<typeof TradingHubLogout> = {
handleOnLogout: jest.fn(),
};

it('should render logout tab', () => {
render(<TradingHubLogout {...mock_props} />);
expect(screen.getByText('Log out')).toBeInTheDocument();
});

it('should invoke handleOnLogout when logout tab is clicked', () => {
render(<TradingHubLogout {...mock_props} />);
const el_tab = screen.getByTestId('dt_logout_tab');
userEvent.click(el_tab);
expect(mock_props.handleOnLogout).toBeCalledTimes(1);
});
});
87 changes: 87 additions & 0 deletions packages/account/src/Containers/Account/account.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import { FadeWrapper, Loading } from '@deriv/components';
import { matchRoute, routes as shared_routes } from '@deriv/shared';
import { observer, useStore } from '@deriv/stores';
import PageOverlayWrapper from './page-overlay-wrapper';
import { TRoute } from '../../Types';
import 'Styles/account.scss';

type TAccountProps = RouteComponentProps & {
routes: Array<TRoute>;
};

/**
* Component that renders the account section
* @name Account
* @param history - history object passed from react-router-dom
* @param location - location object passed from react-router-dom
* @param routes - routes object passed from react-router-dom
* @returns React component
*/
const Account = observer(({ history, location, routes }: TAccountProps) => {
const { client, ui } = useStore();
const {
is_virtual,
is_logged_in,
is_logging_in,
is_pending_proof_of_ownership,
landing_company_shortcode,
should_allow_authentication,
} = client;
const { toggleAccountSettings, is_account_settings_visible } = ui;
const subroutes = routes.map(i => i.subroutes);
let selected_content = subroutes.find(r => matchRoute(r, location.pathname));

React.useEffect(() => {
toggleAccountSettings(true);
}, [toggleAccountSettings]);

routes.forEach(menu_item => {
if (menu_item?.subroutes?.length) {
menu_item.subroutes.forEach(route => {
if (route.path === shared_routes.financial_assessment) {
route.is_disabled = is_virtual || landing_company_shortcode === 'maltainvest';
}

if (route.path === shared_routes.trading_assessment) {
route.is_disabled = is_virtual || landing_company_shortcode !== 'maltainvest';
}

if (route.path === shared_routes.proof_of_identity || route.path === shared_routes.proof_of_address) {
route.is_disabled = !should_allow_authentication;
}

if (route.path === shared_routes.proof_of_ownership) {
route.is_disabled = is_virtual || !is_pending_proof_of_ownership;
}
});
}
});

if (!selected_content) {
// fallback
selected_content = subroutes[0];
history.push(shared_routes.personal_details);
}

if (!is_logged_in && is_logging_in) {
return <Loading is_fullscreen className='account__initial-loader' />;
}

return (
<FadeWrapper
is_visible={is_account_settings_visible}
className='account-page-wrapper'
keyname='account-page-wrapper'
>
<div className='account'>
<PageOverlayWrapper routes={routes} subroutes={subroutes} />
</div>
</FadeWrapper>
);
});

Account.displayName = 'Account';

export default withRouter(Account);
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
import { PageOverlay, VerticalTab } from '@deriv/components';
import { getSelectedRoute, getStaticUrl, routes as shared_routes } from '@deriv/shared';
import { observer, useStore } from '@deriv/stores';
import { Localize } from '@deriv/translations';
import TradingHubLogout from './tradinghub-logout';
import { TRoute } from '../../Types';

type RouteItems = React.ComponentProps<typeof VerticalTab>['list'];

type PageOverlayWrapperProps = {
routes: Array<TRoute>;
subroutes: RouteItems;
};

/**
* @name PageOverlayWrapper
* @param routes - routes object pased by react-router-dom
* @param subroutes - list of subroutes
*/
const PageOverlayWrapper = observer(({ routes, subroutes }: PageOverlayWrapperProps) => {
const history = useHistory();
const { client, common, ui } = useStore();
const { is_mobile } = ui;
const { logout } = client;
const { is_from_derivgo, routeBackInApp } = common;

const list_groups = routes.map(route_group => ({
icon: route_group.icon,
label: route_group?.getTitle(),
subitems: route_group?.subroutes?.length ? route_group.subroutes.map(sub => subroutes.indexOf(sub)) : [],
}));

const onClickClose = React.useCallback(() => routeBackInApp(history), [routeBackInApp, history]);

const selected_route = getSelectedRoute({ routes: subroutes as Array<TRoute>, pathname: location.pathname });

const onClickLogout = () => {
history.push(shared_routes.index);
logout().then(() => (window.location.href = getStaticUrl('/')));
};

if (is_mobile && selected_route) {
const RouteComponent = selected_route.component as React.ElementType<{ component_icon: string | undefined }>;
return (
<PageOverlay header={selected_route?.getTitle()} onClickClose={onClickClose} is_from_app={is_from_derivgo}>
<RouteComponent component_icon={selected_route.icon_component} />
</PageOverlay>
);
}
return (
<PageOverlay
header={<Localize i18n_default_text='Settings' />}
onClickClose={onClickClose}
is_from_app={is_from_derivgo}
>
<VerticalTab
is_floating
current_path={location.pathname}
is_routed
is_full_width
list={subroutes}
list_groups={list_groups}
extra_content={<TradingHubLogout handleOnLogout={onClickLogout} />}
/>
</PageOverlay>
);
});

PageOverlayWrapper.displayName = 'PageOverlayWrapper';

export default PageOverlayWrapper;
22 changes: 22 additions & 0 deletions packages/account/src/Containers/Account/tradinghub-logout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { Text, Icon } from '@deriv/components';
import { Localize } from '@deriv/translations';

/**
* Content to be displayed in the side bar
* @name TradingHubLogout
* @param handleOnLogout - function to handle action when user click on logout
* @returns React Component
*/
const TradingHubLogout = ({ handleOnLogout }: { handleOnLogout: () => void }) => (
<div className='dc-vertical-tab__header-account__logout-tab' onClick={handleOnLogout} data-testid='dt_logout_tab'>
<div className='dc-vertical-tab__header-account__logout'>
<Icon icon='IcLogout' className='dc-vertical-tab__header-account__logout--icon' />
<Text size='xs' weight='bold'>
<Localize i18n_default_text='Log out' />
</Text>
</div>
</div>
);

export default TradingHubLogout;
Loading
Loading