Skip to content

Commit

Permalink
Maryia/DTRA-403/feat: migrate Routes files in Trader package to TS (#38)
Browse files Browse the repository at this point in the history
* feat: migrate routes files to ts

* fix: routes extension

* refactor: for consistency

* refactor: import order

* refactor: renamed prop types

* refactor: type for consistency

* refactor: remove index as key
  • Loading branch information
maryia-deriv committed Sep 15, 2023
1 parent a6cd500 commit 729947f
Show file tree
Hide file tree
Showing 17 changed files with 159 additions and 103 deletions.
2 changes: 1 addition & 1 deletion packages/components/src/components/dialog/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type TDialog = {
onConfirm: () => void;
onEscapeButtonCancel?: () => void;
portal_element_id?: string;
title?: string;
title?: React.ReactNode;
};

const Dialog = ({
Expand Down
2 changes: 2 additions & 0 deletions packages/stores/src/mockStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ const mock = (): TStores & { is_mock: boolean } => {
setHasOnlyForwardingContracts: jest.fn(),
setIsClosingCreateRealAccountModal: jest.fn(),
setRealAccountSignupEnd: jest.fn(),
setPromptHandler: jest.fn(),
setPurchaseState: jest.fn(),
setAppContentsScrollRef: jest.fn(),
shouldNavigateAfterChooseCrypto: jest.fn(),
Expand Down Expand Up @@ -441,6 +442,7 @@ const mock = (): TStores & { is_mock: boolean } => {
onClickCancel: jest.fn(),
onClickSell: jest.fn(),
onMount: jest.fn(),
onUnmount: jest.fn(),
positions: [],
removePositionById: jest.fn(),
},
Expand Down
5 changes: 5 additions & 0 deletions packages/stores/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ type TUiStore = {
setRealAccountSignupEnd: (status: boolean) => void;
setPurchaseState: (index: number) => void;
sub_section_index: number;
setPromptHandler: (
condition: boolean,
cb?: (() => void) | ((route_to: RouteComponentProps['location'], action: string) => boolean)
) => void;
setSubSectionIndex: (index: number) => void;
shouldNavigateAfterChooseCrypto: (value: Omit<string, TRoutes> | TRoutes) => void;
toggleAccountsDialog: () => void;
Expand Down Expand Up @@ -550,6 +554,7 @@ type TPortfolioStore = {
onClickCancel: (contract_id?: number) => void;
onClickSell: (contract_id?: number) => void;
onMount: () => void;
onUnmount: () => void;
positions: TPortfolioPosition[];
removePositionById: (id: number) => void;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { routes } from '@deriv/shared';
import { localize } from '@deriv/translations';

type TErrorComponent = {
header: string;
header: React.ReactNode;
message: React.ReactNode;
is_dialog: boolean;
redirect_label: string;
redirectOnClick: () => void;
redirectOnClick: (() => void) | null;
should_show_refresh: boolean;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import * as Helpers from '../helpers';
import { routes } from '@deriv/shared';
import getRoutesConfig from '../../../Constants/routes-config';
Expand All @@ -23,9 +22,9 @@ describe('Helpers', () => {
});
it('should return route_info when path is in routes_config and is not nested', () => {
const result = Helpers.findRouteByPath(routes.trade, getRoutesConfig());
expect(result.path).toBe(routes.trade);
expect(result.exact).toBe(true);
expect(result.component).toBe(Trade);
expect(result?.path).toBe(routes.trade);
expect(result?.exact).toBe(true);
expect(result?.component).toBe(Trade);
});
});

Expand All @@ -46,7 +45,7 @@ describe('Helpers', () => {

describe('getPath', () => {
it('should return param values in params as a part of path', () => {
expect(Helpers.getPath('/contract/:contract_id', { contract_id: 37511105068 })).toBe(
expect(Helpers.getPath('/contract/:contract_id', { contract_id: '37511105068' })).toBe(
'/contract/37511105068'
);
expect(
Expand All @@ -63,7 +62,7 @@ describe('Helpers', () => {

describe('getContractPath', () => {
it('should return the path of contract with contract_id passed', () => {
expect(Helpers.getContractPath(1234)).toBe('/contract/1234');
expect(Helpers.getContractPath('1234')).toBe('/contract/1234');
});
});
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import PropTypes from 'prop-types';
import React from 'react';
import { NavLink } from 'react-router-dom';
import { findRouteByPath, normalizePath } from './helpers';
import getRoutesConfig from '../../Constants/routes-config';

type TBinaryLinkProps = React.PropsWithChildren<{
active_class?: string;
to?: string;
}>;

// TODO: solve circular dependency problem
// when binary link is imported into components present in routes config
// or into their descendants
const BinaryLink = ({ active_class, to, children, ...props }) => {
const BinaryLink = ({ active_class, to, children, ...props }: TBinaryLinkProps) => {
const path = normalizePath(to);
const route = findRouteByPath(path, getRoutesConfig());

Expand All @@ -32,10 +36,4 @@ const BinaryLink = ({ active_class, to, children, ...props }) => {
);
};

BinaryLink.propTypes = {
active_class: PropTypes.string,
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array, PropTypes.string]),
to: PropTypes.string,
};

export default BinaryLink;
16 changes: 0 additions & 16 deletions packages/trader/src/App/Components/Routes/binary-routes.jsx

This file was deleted.

17 changes: 17 additions & 0 deletions packages/trader/src/App/Components/Routes/binary-routes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { Switch } from 'react-router-dom';
import getRoutesConfig from 'App/Constants/routes-config';
import { TBinaryRoutesProps, TRouteConfig } from 'Types';
import RouteWithSubRoutes from './route-with-sub-routes';

const BinaryRoutes = (props: TBinaryRoutesProps) => (
<React.Suspense fallback={<div />}>
<Switch>
{getRoutesConfig().map((route: TRouteConfig) => (
<RouteWithSubRoutes key={route.getTitle?.()} {...route} {...props} />
))}
</Switch>
</React.Suspense>
);

export default BinaryRoutes;
37 changes: 0 additions & 37 deletions packages/trader/src/App/Components/Routes/helpers.js

This file was deleted.

39 changes: 39 additions & 0 deletions packages/trader/src/App/Components/Routes/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { matchPath, RouteProps } from 'react-router';
import { routes } from '@deriv/shared';
import { TRouteConfig } from 'Types';

export const normalizePath = (path = '') => (/^\//.test(path) ? path : `/${path || ''}`); // Default to '/'

export const findRouteByPath = (path: string, routes_config?: TRouteConfig[]): RouteProps | undefined => {
let result: RouteProps | undefined;

routes_config?.some(route_info => {
let match_path;
try {
match_path = matchPath(path, route_info);
} catch (e: unknown) {
if (/undefined/.test((e as Error).message)) {
return undefined;
}
}

if (match_path) {
result = route_info;
return true;
} else if (route_info.routes) {
result = findRouteByPath(path, route_info.routes);
return result;
}
return false;
});

return result;
};

export const isRouteVisible = (route?: TRouteConfig, is_logged_in?: boolean) =>
!(route && route.is_authenticated && !is_logged_in);

export const getPath = (route_path: string, params: { [key: string]: string } = {}) =>
Object.keys(params).reduce((p, name) => p.replace(`:${name}`, params[name]), route_path);

export const getContractPath = (contract_id = '') => getPath(routes.contract, { contract_id });
7 changes: 0 additions & 7 deletions packages/trader/src/App/Components/Routes/index.js

This file was deleted.

7 changes: 7 additions & 0 deletions packages/trader/src/App/Components/Routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import BinaryLink from './binary-link';
import RouteWithSubRoutes from './route-with-sub-routes';
import BinaryRoutes from './binary-routes';

export * from './helpers';
export { BinaryLink, RouteWithSubRoutes };
export default BinaryRoutes;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { Redirect, Route, RouteComponentProps } from 'react-router-dom';
import {
alternateLinkTagChange,
canonicalLinkTagChange,
Expand All @@ -11,9 +11,12 @@ import {
} from '@deriv/shared';
import { getLanguage } from '@deriv/translations';
import Page404 from 'Modules/Page404';
import { TBinaryRoutesProps, TRouteConfig } from 'Types';

const RouteWithSubRoutes = route => {
const validateRoute = pathname => {
type TRouteWithSubRoutesProps = TRouteConfig & TBinaryRoutesProps;

const RouteWithSubRoutes = (route: TRouteWithSubRoutesProps) => {
const validateRoute = (pathname: string) => {
if (pathname === '') return true;
if (route.path?.includes(':')) {
const static_pathname = pathname.substring(0, pathname.lastIndexOf('/') + 1);
Expand All @@ -22,7 +25,7 @@ const RouteWithSubRoutes = route => {
return route.path === pathname || !!(route.routes && route.routes.find(r => pathname === r.path));
};

const renderFactory = props => {
const renderFactory = (props: RouteComponentProps) => {
let result = null;

const pathname = removeBranchName(location.pathname).replace(/\/$/, '');
Expand All @@ -42,16 +45,15 @@ const RouteWithSubRoutes = route => {
} else {
const default_subroute = route.routes ? route.routes.find(r => r.default) : {};
const has_default_subroute = !isEmptyObject(default_subroute);

const RouteComponent = route.component as React.ElementType;
result = (
<React.Fragment>
{has_default_subroute && pathname === route.path && <Redirect to={default_subroute.path} />}
{is_valid_route ? <route.component {...props} routes={route.routes} /> : <Page404 />}
{has_default_subroute && pathname === route.path && <Redirect to={default_subroute?.path} />}
{is_valid_route ? <RouteComponent {...props} routes={route.routes} /> : <Page404 />}
</React.Fragment>
);
}

// eslint-disable-next-line no-nested-ternary
const title = route.getTitle?.() || '';
document.title = `${title} | ${default_title}`;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import React from 'react';
import { RouteComponentProps } from 'react-router';
import { routes, moduleLoader } from '@deriv/shared';
import { localize } from '@deriv/translations';
import Trade from 'Modules/Trading';
import { TRouteConfig } from 'Types';

const ContractDetails = React.lazy(() =>
moduleLoader(() => import(/* webpackChunkName: "contract" */ 'Modules/Contract'))
const ContractDetails = React.lazy(
() =>
moduleLoader(() => import(/* webpackChunkName: "contract" */ 'Modules/Contract')) as Promise<{
default: React.ComponentType<RouteComponentProps>;
}>
);

// Error Routes
const Page404 = React.lazy(() => moduleLoader(() => import(/* webpackChunkName: "404" */ 'Modules/Page404')));
const Page404 = React.lazy(
() =>
moduleLoader(() => import(/* webpackChunkName: "404" */ 'Modules/Page404')) as Promise<{
default: React.ComponentType<Record<string, never>>;
}>
);

// Order matters
const initRoutesConfig = () => {
Expand All @@ -24,7 +34,7 @@ const initRoutesConfig = () => {
];
};

let routesConfig;
let routesConfig: TRouteConfig[] | undefined;

// For default page route if page/path is not found, must be kept at the end of routes_config array
const route_default = { component: Page404, getTitle: () => localize('Error 404') };
Expand Down
Loading

0 comments on commit 729947f

Please sign in to comment.