feat(trading): error boundaries for panes, sidebars, pages (#5438)

This commit is contained in:
Matthew Russell 2023-12-06 05:31:40 -08:00 committed by GitHub
parent f56d34fe6e
commit cf9f313e4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 302 additions and 72 deletions

View File

@ -77,6 +77,7 @@
"fixStyle": "inline-type-imports" "fixStyle": "inline-type-imports"
} }
], ],
"@typescript-eslint/no-useless-constructor": 0,
"curly": ["error", "multi-line"] "curly": ["error", "multi-line"]
} }
}, },

View File

@ -1,8 +1,9 @@
import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils';
import { ErrorBoundary } from '../../components/error-boundary';
import { FeesContainer } from '../../components/fees-container'; import { FeesContainer } from '../../components/fees-container';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { usePageTitleStore } from '../../stores'; import { usePageTitleStore } from '../../stores';
import { titlefy } from '@vegaprotocol/utils';
import { useEffect } from 'react';
export const Fees = () => { export const Fees = () => {
const t = useT(); const t = useT();
@ -10,13 +11,17 @@ export const Fees = () => {
const { updateTitle } = usePageTitleStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle, updateTitle: store.updateTitle,
})); }));
useEffect(() => { useEffect(() => {
updateTitle(titlefy([title])); updateTitle(titlefy([title]));
}, [updateTitle, title]); }, [updateTitle, title]);
return ( return (
<ErrorBoundary feature="fees">
<div className="container p-4 mx-auto"> <div className="container p-4 mx-auto">
<h1 className="px-4 pb-4 text-2xl">{title}</h1> <h1 className="px-4 pb-4 text-2xl">{title}</h1>
<FeesContainer /> <FeesContainer />
</div> </div>
</ErrorBoundary>
); );
}; };

View File

@ -6,6 +6,7 @@ import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { LiquidityContainer } from '../../components/liquidity-container'; import { LiquidityContainer } from '../../components/liquidity-container';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const enum LiquidityTabs { const enum LiquidityTabs {
Active = 'active', Active = 'active',
@ -58,19 +59,28 @@ export const LiquidityViewContainer = ({
name={t('My liquidity provision')} name={t('My liquidity provision')}
hidden={!pubKey} hidden={!pubKey}
> >
<ErrorBoundary feature="liquidity-party">
<LiquidityContainer <LiquidityContainer
marketId={marketId} marketId={marketId}
filter={{ partyId: pubKey || undefined }} filter={{ partyId: pubKey || undefined }}
/> />
</ErrorBoundary>
</Tab> </Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}> <Tab id={LiquidityTabs.Active} name={t('Active')}>
<LiquidityContainer marketId={marketId} filter={{ active: true }} /> <ErrorBoundary feature="liquidity-active">
<LiquidityContainer
marketId={marketId}
filter={{ active: true }}
/>
</ErrorBoundary>
</Tab> </Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}> <Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<ErrorBoundary feature="liquidity-inactive">
<LiquidityContainer <LiquidityContainer
marketId={marketId} marketId={marketId}
filter={{ active: false }} filter={{ active: false }}
/> />
</ErrorBoundary>
</Tab> </Tab>
</Tabs> </Tabs>
</div> </div>

View File

@ -20,6 +20,7 @@ import {
} from '../../components/market-banner'; } from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment'; import { FLAGS } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
interface TradeGridProps { interface TradeGridProps {
market: Market | null; market: Market | null;
@ -62,28 +63,38 @@ const MainGrid = memo(
name={t('Chart')} name={t('Chart')}
menu={<TradingViews.candles.menu />} menu={<TradingViews.candles.menu />}
> >
<ErrorBoundary feature="chart">
<TradingViews.candles.component marketId={marketId} /> <TradingViews.candles.component marketId={marketId} />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="depth" name={t('Depth')}> <Tab id="depth" name={t('Depth')}>
<ErrorBoundary feature="depth">
<TradingViews.depth.component marketId={marketId} /> <TradingViews.depth.component marketId={marketId} />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="liquidity" name={t('Liquidity')}> <Tab id="liquidity" name={t('Liquidity')}>
<ErrorBoundary feature="liquidity">
<TradingViews.liquidity.component marketId={marketId} /> <TradingViews.liquidity.component marketId={marketId} />
</ErrorBoundary>
</Tab> </Tab>
{market && {market &&
market.tradableInstrument.instrument.product.__typename === market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? ( 'Perpetual' ? (
<Tab id="funding-history" name={t('Funding history')}> <Tab id="funding-history" name={t('Funding history')}>
<ErrorBoundary feature="funding-history">
<TradingViews.funding.component marketId={marketId} /> <TradingViews.funding.component marketId={marketId} />
</ErrorBoundary>
</Tab> </Tab>
) : null} ) : null}
{market && {market &&
market.tradableInstrument.instrument.product.__typename === market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? ( 'Perpetual' ? (
<Tab id="funding-payments" name={t('Funding payments')}> <Tab id="funding-payments" name={t('Funding payments')}>
<ErrorBoundary feature="funding-payments">
<TradingViews.fundingPayments.component <TradingViews.fundingPayments.component
marketId={marketId} marketId={marketId}
/> />
</ErrorBoundary>
</Tab> </Tab>
) : null} ) : null}
</Tabs> </Tabs>
@ -96,10 +107,14 @@ const MainGrid = memo(
<TradeGridChild> <TradeGridChild>
<Tabs storageKey="console-trade-grid-main-right"> <Tabs storageKey="console-trade-grid-main-right">
<Tab id="orderbook" name={t('Orderbook')}> <Tab id="orderbook" name={t('Orderbook')}>
<ErrorBoundary feature="orderbook">
<TradingViews.orderbook.component marketId={marketId} /> <TradingViews.orderbook.component marketId={marketId} />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="trades" name={t('Trades')}> <Tab id="trades" name={t('Trades')}>
<ErrorBoundary feature="trades">
<TradingViews.trades.component marketId={marketId} /> <TradingViews.trades.component marketId={marketId} />
</ErrorBoundary>
</Tab> </Tab>
</Tabs> </Tabs>
</TradeGridChild> </TradeGridChild>
@ -118,31 +133,43 @@ const MainGrid = memo(
name={t('Positions')} name={t('Positions')}
menu={<TradingViews.positions.menu />} menu={<TradingViews.positions.menu />}
> >
<ErrorBoundary feature="positions">
<TradingViews.positions.component /> <TradingViews.positions.component />
</ErrorBoundary>
</Tab> </Tab>
<Tab <Tab
id="open-orders" id="open-orders"
name={t('Open')} name={t('Open')}
menu={<TradingViews.activeOrders.menu />} menu={<TradingViews.activeOrders.menu />}
> >
<ErrorBoundary feature="activeOrders">
<TradingViews.activeOrders.component /> <TradingViews.activeOrders.component />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="closed-orders" name={t('Closed')}> <Tab id="closed-orders" name={t('Closed')}>
<ErrorBoundary feature="closedOrders">
<TradingViews.closedOrders.component /> <TradingViews.closedOrders.component />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="rejected-orders" name={t('Rejected')}> <Tab id="rejected-orders" name={t('Rejected')}>
<ErrorBoundary feature="rejectedOrders">
<TradingViews.rejectedOrders.component /> <TradingViews.rejectedOrders.component />
</ErrorBoundary>
</Tab> </Tab>
<Tab <Tab
id="orders" id="orders"
name={t('All')} name={t('All')}
menu={<TradingViews.orders.menu />} menu={<TradingViews.orders.menu />}
> >
<ErrorBoundary feature="orders">
<TradingViews.orders.component /> <TradingViews.orders.component />
</ErrorBoundary>
</Tab> </Tab>
{FLAGS.STOP_ORDERS ? ( {FLAGS.STOP_ORDERS ? (
<Tab id="stop-orders" name={t('Stop orders')}> <Tab id="stop-orders" name={t('Stop orders')}>
<ErrorBoundary feature="stop-orders">
<TradingViews.stopOrders.component /> <TradingViews.stopOrders.component />
</ErrorBoundary>
</Tab> </Tab>
) : null} ) : null}
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
@ -153,7 +180,11 @@ const MainGrid = memo(
name={t('Collateral')} name={t('Collateral')}
menu={<TradingViews.collateral.menu />} menu={<TradingViews.collateral.menu />}
> >
<TradingViews.collateral.component pinnedAsset={pinnedAsset} /> <ErrorBoundary feature="collateral">
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
/>
</ErrorBoundary>
</Tab> </Tab>
</Tabs> </Tabs>
</TradeGridChild> </TradeGridChild>

View File

@ -1,19 +1,20 @@
import type { PinnedAsset } from '@vegaprotocol/accounts'; import { type PinnedAsset } from '@vegaprotocol/accounts';
import type { Market } from '@vegaprotocol/markets'; import { type Market } from '@vegaprotocol/markets';
import { OracleBanner } from '@vegaprotocol/markets'; import { OracleBanner } from '@vegaprotocol/markets';
import type { TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { useState } from 'react'; import { useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames'; import classNames from 'classnames';
import { FLAGS } from '@vegaprotocol/environment';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useT } from '../../lib/use-t';
import { import {
MarketSuccessorBanner, MarketSuccessorBanner,
MarketSuccessorProposalBanner, MarketSuccessorProposalBanner,
MarketTerminationBanner, MarketTerminationBanner,
} from '../../components/market-banner'; } from '../../components/market-banner';
import { FLAGS } from '@vegaprotocol/environment'; import { ErrorBoundary } from '../../components/error-boundary';
import { useT } from '../../lib/use-t'; import { type TradingView } from './trade-views';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { TradingViews } from './trade-views';
interface TradePanelsProps { interface TradePanelsProps {
market: Market | null; market: Market | null;
@ -34,7 +35,11 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
// Watch out here, we don't know what component is being rendered // Watch out here, we don't know what component is being rendered
// so watch out for clashes in props // so watch out for clashes in props
return <Component marketId={market?.id} pinnedAsset={pinnedAsset} />; return (
<ErrorBoundary feature={view}>
<Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
</ErrorBoundary>
);
}; };
const renderMenu = () => { const renderMenu = () => {

View File

@ -15,6 +15,7 @@ import {
useLinks, useLinks,
} from '@vegaprotocol/environment'; } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
export const MarketsPage = () => { export const MarketsPage = () => {
const t = useT(); const t = useT();
@ -34,7 +35,9 @@ export const MarketsPage = () => {
<div className="h-full my-1 border rounded-sm border-default"> <div className="h-full my-1 border rounded-sm border-default">
<Tabs storageKey="console-markets"> <Tabs storageKey="console-markets">
<Tab id="open-markets" name={t('Open markets')}> <Tab id="open-markets" name={t('Open markets')}>
<ErrorBoundary feature="markets-open">
<OpenMarkets /> <OpenMarkets />
</ErrorBoundary>
</Tab> </Tab>
<Tab <Tab
id="proposed-markets" id="proposed-markets"
@ -49,10 +52,14 @@ export const MarketsPage = () => {
</TradingAnchorButton> </TradingAnchorButton>
} }
> >
<ErrorBoundary feature="markets-proposed">
<Proposed /> <Proposed />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="closed-markets" name={t('Closed markets')}> <Tab id="closed-markets" name={t('Closed markets')}>
<ErrorBoundary feature="markets-closed">
<Closed /> <Closed />
</ErrorBoundary>
</Tab> </Tab>
</Tabs> </Tabs>
</div> </div>

View File

@ -25,6 +25,7 @@ import { DepositsMenu } from '../../components/deposits-menu';
import { WithdrawalsMenu } from '../../components/withdrawals-menu'; import { WithdrawalsMenu } from '../../components/withdrawals-menu';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const WithdrawalsIndicator = () => { const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals(); const { ready } = useIncompleteWithdrawals();
@ -72,19 +73,29 @@ export const Portfolio = () => {
name={t('Positions')} name={t('Positions')}
menu={<PositionsMenu />} menu={<PositionsMenu />}
> >
<ErrorBoundary feature="portfolio-positions">
<PositionsContainer allKeys /> <PositionsContainer allKeys />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="orders" name={t('Orders')}> <Tab id="orders" name={t('Orders')}>
<ErrorBoundary feature="portfolio-orders">
<OrdersContainer /> <OrdersContainer />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
<ErrorBoundary feature="portfolio-fills">
<FillsContainer /> <FillsContainer />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="funding-payments" name={t('Funding payments')}> <Tab id="funding-payments" name={t('Funding payments')}>
<ErrorBoundary feature="portfolio-funding-payments">
<FundingPaymentsContainer /> <FundingPaymentsContainer />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}> <Tab id="ledger-entries" name={t('Ledger entries')}>
<ErrorBoundary feature="portfolio-ledger">
<LedgerContainer /> <LedgerContainer />
</ErrorBoundary>
</Tab> </Tab>
</Tabs> </Tabs>
</PortfolioGridChild> </PortfolioGridChild>
@ -101,10 +112,14 @@ export const Portfolio = () => {
name={t('Collateral')} name={t('Collateral')}
menu={<AccountsMenu />} menu={<AccountsMenu />}
> >
<ErrorBoundary feature="portfolio-accounts">
<AccountsContainer /> <AccountsContainer />
</ErrorBoundary>
</Tab> </Tab>
<Tab id="deposits" name={t('Deposits')} menu={<DepositsMenu />}> <Tab id="deposits" name={t('Deposits')} menu={<DepositsMenu />}>
<ErrorBoundary feature="portfolio-deposit">
<DepositsContainer /> <DepositsContainer />
</ErrorBoundary>
</Tab> </Tab>
<Tab <Tab
id="withdrawals" id="withdrawals"
@ -112,7 +127,9 @@ export const Portfolio = () => {
indicator={<WithdrawalsIndicator />} indicator={<WithdrawalsIndicator />}
menu={<WithdrawalsMenu />} menu={<WithdrawalsMenu />}
> >
<ErrorBoundary feature="portfolio-deposit">
<WithdrawalsContainer /> <WithdrawalsContainer />
</ErrorBoundary>
</Tab> </Tab>
</Tabs> </Tabs>
</PortfolioGridChild> </PortfolioGridChild>

View File

@ -18,6 +18,7 @@ import { usePageTitleStore } from '../../stores';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils'; import { titlefy } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const Nav = () => { const Nav = () => {
const t = useT(); const t = useT();
@ -65,7 +66,7 @@ export const Referrals = () => {
}, [updateTitle, t]); }, [updateTitle, t]);
return ( return (
<> <ErrorBoundary feature="referrals">
<LandingBanner /> <LandingBanner />
{showNav && <Nav />} {showNav && <Nav />}
@ -107,6 +108,6 @@ export const Referrals = () => {
</TradingAnchorButton> </TradingAnchorButton>
</div> </div>
</div> </div>
</> </ErrorBoundary>
); );
}; };

View File

@ -1,8 +1,9 @@
import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { RewardsContainer } from '../../components/rewards-container'; import { RewardsContainer } from '../../components/rewards-container';
import { usePageTitleStore } from '../../stores'; import { usePageTitleStore } from '../../stores';
import { titlefy } from '@vegaprotocol/utils'; import { ErrorBoundary } from '../../components/error-boundary';
import { useEffect } from 'react';
export const Rewards = () => { export const Rewards = () => {
const t = useT(); const t = useT();
@ -14,9 +15,11 @@ export const Rewards = () => {
updateTitle(titlefy([title])); updateTitle(titlefy([title]));
}, [updateTitle, title]); }, [updateTitle, title]);
return ( return (
<ErrorBoundary feature="rewards">
<div className="container mx-auto p-4"> <div className="container mx-auto p-4">
<h1 className="px-4 pb-4 text-2xl">{title}</h1> <h1 className="px-4 pb-4 text-2xl">{title}</h1>
<RewardsContainer /> <RewardsContainer />
</div> </div>
</ErrorBoundary>
); );
}; };

View File

@ -0,0 +1,79 @@
import { render, screen } from '@testing-library/react';
import { ErrorBoundary } from './error-boundary';
import { localLoggerFactory } from '@vegaprotocol/logger';
jest.mock('@vegaprotocol/logger', () => ({
localLoggerFactory: jest.fn(),
}));
describe('ErrorBoundary', () => {
const mockLogError = jest.fn();
const originalConsoleError = console.error;
const mockLoggerFactory = localLoggerFactory as jest.Mock;
beforeAll(() => {
console.error = () => {};
});
afterAll(() => {
console.error = originalConsoleError;
});
beforeEach(() => {
mockLoggerFactory.mockImplementation(() => ({
error: mockLogError,
}));
});
afterEach(() => {
mockLogError.mockClear();
});
it('renders children', () => {
render(
<ErrorBoundary feature="feature">
<div data-testid="child" />
</ErrorBoundary>
);
expect(screen.getByTestId('child')).toBeInTheDocument();
});
it('renders fallback ui and logs an error', () => {
const error = new Error('bork!');
const BorkedComponent = () => {
throw error;
};
render(
<ErrorBoundary feature="test-feature">
<BorkedComponent />
</ErrorBoundary>
);
expect(screen.getByText('Something went wrong')).toBeInTheDocument();
expect(mockLogError).toHaveBeenCalledTimes(1);
expect(mockLogError).toHaveBeenCalledWith(
error.message,
expect.stringContaining('componentStack')
);
});
it('renders fallback render prop if error', () => {
const error = new Error('bork!');
const BorkedComponent = () => {
throw error;
};
render(
<ErrorBoundary
feature="test-feature"
fallback={<div data-testid="custom-ui" />}
>
<BorkedComponent />
</ErrorBoundary>
);
expect(screen.getByTestId('custom-ui')).toBeInTheDocument();
});
});

View File

@ -0,0 +1,53 @@
import { localLoggerFactory, type LocalLogger } from '@vegaprotocol/logger';
import { Component, type ErrorInfo, type ReactNode } from 'react';
import { useT } from '../../lib/use-t';
interface ErrorBoundaryProps {
children: ReactNode;
feature: string;
fallback?: ReactNode;
}
interface ErrorBoundaryState {
hasError: boolean;
}
export class ErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
logger: LocalLogger | null = null;
constructor(props: ErrorBoundaryProps) {
super(props);
this.logger = localLoggerFactory({ application: props.feature });
this.state = {
hasError: false,
};
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, info: ErrorInfo) {
if (this.logger) {
this.logger.error(error.message, JSON.stringify(info));
}
}
render() {
if (this.state.hasError) {
return this.props.fallback || <DefaultFallback />;
}
return this.props.children;
}
}
const DefaultFallback = () => {
const t = useT();
return <p className="text-xs">{t('Something went wrong')}</p>;
};

View File

@ -0,0 +1 @@
export { ErrorBoundary } from './error-boundary';

View File

@ -16,6 +16,7 @@ import { GetStarted } from '../welcome-dialog';
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet'; import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id'; import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t'; import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../error-boundary';
export enum ViewType { export enum ViewType {
Order = 'Order', Order = 'Order',
@ -163,12 +164,14 @@ export const SidebarContent = () => {
if (params.marketId) { if (params.marketId) {
return ( return (
<ContentWrapper> <ContentWrapper>
<ErrorBoundary feature="deal-ticket">
<DealTicketContainer <DealTicketContainer
marketId={params.marketId} marketId={params.marketId}
onDeposit={(assetId) => onDeposit={(assetId) =>
setViews({ type: ViewType.Deposit, assetId }, currentRouteId) setViews({ type: ViewType.Deposit, assetId }, currentRouteId)
} }
/> />
</ErrorBoundary>
<GetStarted /> <GetStarted />
</ContentWrapper> </ContentWrapper>
); );
@ -181,7 +184,9 @@ export const SidebarContent = () => {
if (params.marketId) { if (params.marketId) {
return ( return (
<ContentWrapper> <ContentWrapper>
<ErrorBoundary feature="market-info">
<MarketInfoAccordionContainer marketId={params.marketId} /> <MarketInfoAccordionContainer marketId={params.marketId} />
</ErrorBoundary>
</ContentWrapper> </ContentWrapper>
); );
} else { } else {
@ -192,7 +197,9 @@ export const SidebarContent = () => {
if (view.type === ViewType.Deposit) { if (view.type === ViewType.Deposit) {
return ( return (
<ContentWrapper title={t('Deposit')}> <ContentWrapper title={t('Deposit')}>
<ErrorBoundary feature="deposit">
<DepositContainer assetId={view.assetId} /> <DepositContainer assetId={view.assetId} />
</ErrorBoundary>
</ContentWrapper> </ContentWrapper>
); );
} }
@ -200,7 +207,9 @@ export const SidebarContent = () => {
if (view.type === ViewType.Withdraw) { if (view.type === ViewType.Withdraw) {
return ( return (
<ContentWrapper title={t('Withdraw')}> <ContentWrapper title={t('Withdraw')}>
<ErrorBoundary feature="withdraw">
<WithdrawContainer assetId={view.assetId} /> <WithdrawContainer assetId={view.assetId} />
</ErrorBoundary>
</ContentWrapper> </ContentWrapper>
); );
} }
@ -208,7 +217,9 @@ export const SidebarContent = () => {
if (view.type === ViewType.Transfer) { if (view.type === ViewType.Transfer) {
return ( return (
<ContentWrapper title={t('Transfer')}> <ContentWrapper title={t('Transfer')}>
<ErrorBoundary feature="transfer">
<TransferContainer assetId={view.assetId} /> <TransferContainer assetId={view.assetId} />
</ErrorBoundary>
</ContentWrapper> </ContentWrapper>
); );
} }
@ -216,7 +227,9 @@ export const SidebarContent = () => {
if (view.type === ViewType.Settings) { if (view.type === ViewType.Settings) {
return ( return (
<ContentWrapper title={t('Settings')}> <ContentWrapper title={t('Settings')}>
<ErrorBoundary feature="settings">
<Settings /> <Settings />
</ErrorBoundary>
</ContentWrapper> </ContentWrapper>
); );
} }

View File

@ -10,6 +10,7 @@ export interface LoggerProps extends LoggerConf {
export const useLogger = ({ dsn, env, ...props }: LoggerProps) => { export const useLogger = ({ dsn, env, ...props }: LoggerProps) => {
const logger = useRef<LocalLogger | null>(null); const logger = useRef<LocalLogger | null>(null);
if (!logger.current) { if (!logger.current) {
logger.current = localLoggerFactory(props); logger.current = localLoggerFactory(props);
if (dsn) { if (dsn) {

View File

@ -25,6 +25,7 @@ describe('LocalLogger', () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
it('logger should be properly instantiate', () => { it('logger should be properly instantiate', () => {
const logger = localLoggerFactory({}); const logger = localLoggerFactory({});
expect(logger).toBeInstanceOf(LocalLogger); expect(logger).toBeInstanceOf(LocalLogger);
@ -50,7 +51,7 @@ describe('LocalLogger', () => {
logger[method]('test', 'test2'); logger[method]('test', 'test2');
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
expect(console[consoleMethod]).toHaveBeenCalledWith( expect(console[consoleMethod]).toHaveBeenCalledWith(
`trading:${methodToLevel[i]}: `, `trading:${methodToLevel[i]}:`,
'test', 'test',
'test2' 'test2'
); );
@ -110,7 +111,7 @@ describe('LocalLogger', () => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
expect(console.debug).toHaveBeenCalledWith( expect(console.debug).toHaveBeenCalledWith(
'trading:debug: ', 'trading:debug:',
'test', 'test',
'test1' 'test1'
); );

View File

@ -62,6 +62,7 @@ export class LocalLogger {
} }
private tags: string[] = []; private tags: string[] = [];
private _application = 'trading'; private _application = 'trading';
constructor(conf: LoggerConf) { constructor(conf: LoggerConf) {
if (conf.application) { if (conf.application) {
this._application = conf.application; this._application = conf.application;
@ -69,6 +70,7 @@ export class LocalLogger {
this.tags = [...(conf.tags || [])]; this.tags = [...(conf.tags || [])];
this._logLevel = conf.logLevel || this._logLevel; this._logLevel = conf.logLevel || this._logLevel;
} }
public debug(...args: ConsoleArg[]) { public debug(...args: ConsoleArg[]) {
this._log('debug', 'debug', args); this._log('debug', 'debug', args);
} }
@ -101,7 +103,7 @@ export class LocalLogger {
) { ) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console[logMethod].apply(console, [ console[logMethod].apply(console, [
`${this._application}:${level}: `, `${this._application}:${level}:`,
...args, ...args,
]); ]);
} }