chore(trading): add "holding CMD + click" external link (#3273)

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
This commit is contained in:
Maciek 2023-03-31 15:23:44 +02:00 committed by GitHub
parent 2bfc3abd15
commit 82e5128ba1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 273 additions and 231 deletions

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils'; import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { import {
@ -13,6 +13,7 @@ import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid, TradePanels } from './trade-grid'; import { TradeGrid, TradePanels } from './trade-grid';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router'; import { Links, Routes } from '../../pages/client-router';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => { const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
return markPrice && decimalPlaces return markPrice && decimalPlaces
@ -66,14 +67,7 @@ export const MarketPage = () => {
const update = useGlobalStore((store) => store.update); const update = useGlobalStore((store) => store.update);
const lastMarketId = useGlobalStore((store) => store.marketId); const lastMarketId = useGlobalStore((store) => store.marketId);
const onSelect = useCallback( const onSelect = useMarketClickHandler();
(id: string) => {
if (id && id !== marketId) {
navigate(Links[Routes.MARKET](id));
}
},
[marketId, navigate]
);
const { data, error, loading } = useDataProvider({ const { data, error, loading } = useDataProvider({
dataProvider: marketProvider, dataProvider: marketProvider,

View File

@ -8,7 +8,7 @@ import { TradesContainer } from '@vegaprotocol/trades';
import { LayoutPriority } from 'allotment'; import { LayoutPriority } from 'allotment';
import classNames from 'classnames'; import classNames from 'classnames';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
import { memo, useCallback, useState } from 'react'; import { memo, useState } from 'react';
import type { ReactNode, ComponentProps } from 'react'; import type { ReactNode, ComponentProps } from 'react';
import { DepthChartContainer } from '@vegaprotocol/market-depth'; import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart'; import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
@ -27,9 +27,9 @@ import { TradeMarketHeader } from './trade-market-header';
import { NO_MARKET } from './constants'; import { NO_MARKET } from './constants';
import { LiquidityContainer } from '../liquidity/liquidity'; import { LiquidityContainer } from '../liquidity/liquidity';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import type { PinnedAsset } from '@vegaprotocol/accounts'; import type { PinnedAsset } from '@vegaprotocol/accounts';
import { useScreenDimensions } from '@vegaprotocol/react-helpers'; import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
type MarketDependantView = type MarketDependantView =
| typeof CandlesChartContainer | typeof CandlesChartContainer
@ -66,7 +66,7 @@ type TradingView = keyof typeof TradingViews;
interface TradeGridProps { interface TradeGridProps {
market: Market | null; market: Market | null;
onSelect: (marketId: string) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
} }
@ -78,15 +78,7 @@ interface BottomPanelProps {
const MarketBottomPanel = memo( const MarketBottomPanel = memo(
({ marketId, pinnedAsset }: BottomPanelProps) => { ({ marketId, pinnedAsset }: BottomPanelProps) => {
const { screenSize } = useScreenDimensions(); const { screenSize } = useScreenDimensions();
const navigate = useNavigate(); const onMarketClick = useMarketClickHandler(true);
const onMarketClick = useCallback(
(marketId: string) => {
navigate(Links[Routes.MARKET](marketId), {
replace: true,
});
},
[navigate]
);
return 'xxxl' === screenSize ? ( return 'xxxl' === screenSize ? (
<ResizableGrid proportionalLayout minSize={200}> <ResizableGrid proportionalLayout minSize={200}>
@ -189,7 +181,7 @@ const MainGrid = memo(
pinnedAsset, pinnedAsset,
}: { }: {
marketId: string; marketId: string;
onSelect?: (marketId: string) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
}) => { }) => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -230,12 +222,7 @@ const MainGrid = memo(
/> />
</Tab> </Tab>
<Tab id="info" name={t('Info')}> <Tab id="info" name={t('Info')}>
<TradingViews.Info <TradingViews.Info marketId={marketId} />
marketId={marketId}
onSelect={(id: string) => {
onSelect?.(id);
}}
/>
</Tab> </Tab>
</Tabs> </Tabs>
</TradeGridChild> </TradeGridChild>
@ -304,7 +291,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => {
interface TradePanelsProps { interface TradePanelsProps {
market: Market | null; market: Market | null;
onSelect: (marketId: string) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string) => void;
onClickCollateral: () => void; onClickCollateral: () => void;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
@ -320,7 +307,7 @@ export const TradePanels = ({
const renderView = () => { const renderView = () => {
const Component = memo<{ const Component = memo<{
marketId: string; marketId: string;
onSelect: (marketId: string) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string) => void;
onClickCollateral: () => void; onClickCollateral: () => void;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;

View File

@ -22,7 +22,7 @@ import { MarketState as State } from '@vegaprotocol/types';
interface TradeMarketHeaderProps { interface TradeMarketHeaderProps {
market: Market | null; market: Market | null;
onSelect: (marketId: string) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
} }
export const TradeMarketHeader = ({ export const TradeMarketHeader = ({
@ -91,7 +91,6 @@ export const TradeMarketHeader = ({
</HeaderStat> </HeaderStat>
<HeaderStatMarketTradingMode <HeaderStatMarketTradingMode
marketId={market?.id} marketId={market?.id}
onSelect={onSelect}
initialTradingMode={market?.tradingMode} initialTradingMode={market?.tradingMode}
/> />
<MarketState market={market} /> <MarketState market={market} />

View File

@ -1,16 +1,7 @@
import { useCallback } from 'react';
import { MarketsContainer } from '@vegaprotocol/market-list'; import { MarketsContainer } from '@vegaprotocol/market-list';
import { useNavigate } from 'react-router-dom'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { Links, Routes } from '../../pages/client-router';
export const Markets = () => { export const Markets = () => {
const navigate = useNavigate(); const handleOnSelect = useMarketClickHandler();
const handleOnSelect = useCallback(
(marketId: string) => {
navigate(Links[Routes.MARKET](marketId));
},
[navigate]
);
return <MarketsContainer onSelect={handleOnSelect} />; return <MarketsContainer onSelect={handleOnSelect} />;
}; };

View File

@ -19,25 +19,18 @@ import { usePageTitleStore } from '../../stores';
import { LedgerContainer } from '@vegaprotocol/ledger'; import { LedgerContainer } from '@vegaprotocol/ledger';
import { AccountsContainer } from '../../components/accounts-container'; import { AccountsContainer } from '../../components/accounts-container';
import { AccountHistoryContainer } from './account-history-container'; import { AccountHistoryContainer } from './account-history-container';
import { useNavigate } from 'react-router-dom'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { Links, Routes } from '../../pages/client-router';
export const Portfolio = () => { export const Portfolio = () => {
const { updateTitle } = usePageTitleStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle, updateTitle: store.updateTitle,
})); }));
const navigate = useNavigate();
useEffect(() => { useEffect(() => {
updateTitle(titlefy([t('Portfolio')])); updateTitle(titlefy([t('Portfolio')]));
}, [updateTitle]); }, [updateTitle]);
const onMarketClick = (marketId: string) => { const onMarketClick = useMarketClickHandler(true);
navigate(Links[Routes.MARKET](marketId), {
replace: true,
});
};
const wrapperClasses = 'h-full max-h-full flex flex-col'; const wrapperClasses = 'h-full max-h-full flex flex-col';
return ( return (

View File

@ -25,7 +25,7 @@ const getTradingModeLabel = (
interface HeaderStatMarketTradingModeProps { interface HeaderStatMarketTradingModeProps {
marketId?: string; marketId?: string;
onSelect?: (marketId: string) => void; onSelect?: (marketId: string, metaKey?: boolean) => void;
initialTradingMode?: Schema.MarketTradingMode; initialTradingMode?: Schema.MarketTradingMode;
initialTrigger?: Schema.AuctionTrigger; initialTrigger?: Schema.AuctionTrigger;
} }
@ -66,7 +66,9 @@ export const MarketTradingMode = ({
return ( return (
<Tooltip <Tooltip
description={<TradingModeTooltip marketId={marketId} skip={!inView} />} description={
<TradingModeTooltip marketId={marketId} skip={!inView} skipGrid />
}
> >
<span ref={ref}> <span ref={ref}>
{getTradingModeLabel( {getTradingModeLabel(

View File

@ -1,4 +1,4 @@
import type { RefObject } from 'react'; import type { RefObject, MouseEvent } from 'react';
import { FeesCell } from '@vegaprotocol/market-info'; import { FeesCell } from '@vegaprotocol/market-info';
import { import {
calcCandleHigh, calcCandleHigh,
@ -157,14 +157,14 @@ export const columnHeaders: Column[] = [
]; ];
export type OnCellClickHandler = ( export type OnCellClickHandler = (
e: React.MouseEvent, e: MouseEvent,
kind: ColumnKind, kind: ColumnKind,
value: string value: string
) => void; ) => void;
export const columns = ( export const columns = (
market: MarketMaybeWithDataAndCandles, market: MarketMaybeWithDataAndCandles,
onSelect: (id: string) => void, onSelect: (id: string, metaKey?: boolean) => void,
onCellClick: OnCellClickHandler, onCellClick: OnCellClickHandler,
inViewRoot?: RefObject<HTMLElement> inViewRoot?: RefObject<HTMLElement>
) => { ) => {
@ -174,14 +174,7 @@ export const columns = (
const candleLow = market.candles && calcCandleLow(market.candles); const candleLow = market.candles && calcCandleLow(market.candles);
const candleHigh = market.candles && calcCandleHigh(market.candles); const candleHigh = market.candles && calcCandleHigh(market.candles);
const candleVolume = market.candles && calcCandleVolume(market.candles); const candleVolume = market.candles && calcCandleVolume(market.candles);
const handleKeyPress = (
event: React.KeyboardEvent<HTMLAnchorElement>,
id: string
) => {
if (event.key === 'Enter' && onSelect) {
return onSelect(id);
}
};
const selectMarketColumns: Column[] = [ const selectMarketColumns: Column[] = [
{ {
kind: ColumnKind.Market, kind: ColumnKind.Market,
@ -189,10 +182,10 @@ export const columns = (
<Link <Link
to={Links[Routes.MARKET](market.id)} to={Links[Routes.MARKET](market.id)}
data-testid={`market-link-${market.id}`} data-testid={`market-link-${market.id}`}
onKeyPress={(event) => handleKeyPress(event, market.id)}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
onSelect(market.id); e.stopPropagation();
onSelect(market.id, e.metaKey);
}} }}
> >
<UILink>{market.tradableInstrument.instrument.code}</UILink> <UILink>{market.tradableInstrument.instrument.code}</UILink>
@ -352,7 +345,7 @@ export const columns = (
export const columnsPositionMarkets = ( export const columnsPositionMarkets = (
market: MarketMaybeWithDataAndCandles, market: MarketMaybeWithDataAndCandles,
onSelect: (id: string) => void, onSelect: (id: string, metaKey?: boolean) => void,
inViewRoot?: RefObject<HTMLElement>, inViewRoot?: RefObject<HTMLElement>,
openVolume?: string, openVolume?: string,
onCellClick?: OnCellClickHandler onCellClick?: OnCellClickHandler
@ -362,14 +355,6 @@ export const columnsPositionMarkets = (
.filter((c: string | undefined): c is CandleClose => !isNil(c)); .filter((c: string | undefined): c is CandleClose => !isNil(c));
const candleLow = market.candles && calcCandleLow(market.candles); const candleLow = market.candles && calcCandleLow(market.candles);
const candleHigh = market.candles && calcCandleHigh(market.candles); const candleHigh = market.candles && calcCandleHigh(market.candles);
const handleKeyPress = (
event: React.KeyboardEvent<HTMLSpanElement>,
id: string
) => {
if (event.key === 'Enter' && onSelect) {
return onSelect(id);
}
};
const candleVolume = market.candles && calcCandleVolume(market.candles); const candleVolume = market.candles && calcCandleVolume(market.candles);
const selectMarketColumns: Column[] = [ const selectMarketColumns: Column[] = [
{ {
@ -378,10 +363,10 @@ export const columnsPositionMarkets = (
<Link <Link
to={Links[Routes.MARKET](market.id)} to={Links[Routes.MARKET](market.id)}
data-testid={`market-link-${market.id}`} data-testid={`market-link-${market.id}`}
onKeyPress={(event) => handleKeyPress(event, market.id)}
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
onSelect(market.id); e.stopPropagation();
onSelect(market.id, e.metaKey);
}} }}
> >
<UILink>{market.tradableInstrument.instrument.code}</UILink> <UILink>{market.tradableInstrument.instrument.code}</UILink>

View File

@ -37,14 +37,14 @@ export const SelectMarketTableRow = ({
}: { }: {
detailed?: boolean; detailed?: boolean;
columns: Column[]; columns: Column[];
onSelect: (id: string) => void; onSelect: (id: string, metaKey?: boolean) => void;
marketId: string; marketId: string;
}) => { }) => {
return ( return (
<tr <tr
className={`hover:bg-neutral-200 dark:hover:bg-neutral-700 cursor-pointer relative h-[34px]`} className={`hover:bg-neutral-200 dark:hover:bg-neutral-700 cursor-pointer relative h-[34px]`}
onClick={() => { onClick={(ev) => {
onSelect(marketId); onSelect(marketId, ev.metaKey);
}} }}
data-testid={`market-link-${marketId}`} data-testid={`market-link-${marketId}`}
> >

View File

@ -178,6 +178,6 @@ describe('SelectMarket', () => {
expect(screen.getByText('25.00%')).toBeTruthy(); // price change expect(screen.getByText('25.00%')).toBeTruthy(); // price change
expect(container).toHaveTextContent(/1,000/); // volume expect(container).toHaveTextContent(/1,000/); // volume
fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]); fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]);
expect(onSelect).toHaveBeenCalledWith('1'); expect(onSelect).toHaveBeenCalledWith('1', false);
}); });
}); });

View File

@ -40,7 +40,7 @@ export const SelectAllMarketsTableBody = ({
markets?: MarketMaybeWithDataAndCandles[] | null; markets?: MarketMaybeWithDataAndCandles[] | null;
positions?: PositionFieldsFragment[]; positions?: PositionFieldsFragment[];
title?: string; title?: string;
onSelect: (id: string) => void; onSelect: (id: string, metaKey?: boolean) => void;
onCellClick: OnCellClickHandler; onCellClick: OnCellClickHandler;
headers?: Column[]; headers?: Column[];
tableColumns?: ( tableColumns?: (
@ -95,7 +95,7 @@ export const SelectMarketPopover = ({
}: { }: {
marketCode: string; marketCode: string;
marketName: string; marketName: string;
onSelect: (id: string) => void; onSelect: (id: string, metaKey?: boolean) => void;
onCellClick: OnCellClickHandler; onCellClick: OnCellClickHandler;
}) => { }) => {
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();
@ -116,8 +116,8 @@ export const SelectMarketPopover = ({
skip: !pubKey, skip: !pubKey,
}); });
const onSelectMarket = useCallback( const onSelectMarket = useCallback(
(marketId: string) => { (marketId: string, metaKey?: boolean) => {
onSelect(marketId); onSelect(marketId, metaKey);
setOpen(false); setOpen(false);
}, },
[onSelect] [onSelect]

View File

@ -0,0 +1,21 @@
import { useNavigate, useParams, useLocation } from 'react-router-dom';
import { useCallback } from 'react';
import { Links, Routes } from '../../pages/client-router';
export const useMarketClickHandler = (replace = false) => {
const navigate = useNavigate();
const { marketId } = useParams();
const { pathname } = useLocation();
const isMarketPage = pathname.match(/^\/markets\/(.+)/);
return useCallback(
(selectedId: string, metaKey?: boolean) => {
const link = Links[Routes.MARKET](selectedId);
if (metaKey) {
window.open(`/#${link}`, '_blank');
} else if (selectedId !== marketId || !isMarketPage) {
navigate(link, { replace });
}
},
[navigate, marketId, replace, isMarketPage]
);
};

View File

@ -1,3 +1,5 @@
import { useMemo, useState } from 'react';
import classNames from 'classnames';
import Head from 'next/head'; import Head from 'next/head';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
@ -23,14 +25,12 @@ import {
import './styles.css'; import './styles.css';
import { useGlobalStore, usePageTitleStore } from '../stores'; import { useGlobalStore, usePageTitleStore } from '../stores';
import { Footer } from '../components/footer'; import { Footer } from '../components/footer';
import { useMemo, useState } from 'react';
import DialogsContainer from './dialogs-container'; import DialogsContainer from './dialogs-container';
import ToastsManager from './toasts-manager'; import ToastsManager from './toasts-manager';
import { HashRouter, useLocation, useSearchParams } from 'react-router-dom'; import { HashRouter, useLocation, useSearchParams } from 'react-router-dom';
import { Connectors } from '../lib/vega-connectors'; import { Connectors } from '../lib/vega-connectors';
import { ViewingBanner } from '../components/viewing-banner'; import { ViewingBanner } from '../components/viewing-banner';
import { Banner } from '../components/banner'; import { Banner } from '../components/banner';
import classNames from 'classnames';
import { AppLoader, DynamicLoader } from '../components/app-loader'; import { AppLoader, DynamicLoader } from '../components/app-loader';
import { Navbar } from '../components/navbar'; import { Navbar } from '../components/navbar';
@ -57,7 +57,7 @@ const Title = () => {
); );
}; };
const TransactionsHandler = () => { const InitializeHandlers = () => {
useVegaTransactionManager(); useVegaTransactionManager();
useVegaTransactionUpdater(); useVegaTransactionUpdater();
useEthTransactionManager(); useEthTransactionManager();
@ -93,7 +93,7 @@ function AppBody({ Component }: AppProps) {
</div> </div>
<DialogsContainer /> <DialogsContainer />
<ToastsManager /> <ToastsManager />
<TransactionsHandler /> <InitializeHandlers />
<MaybeConnectEagerly /> <MaybeConnectEagerly />
</div> </div>
); );

View File

@ -9,6 +9,7 @@ export * from './lib/cells/price-change-cell';
export * from './lib/cells/price-flash-cell'; export * from './lib/cells/price-flash-cell';
export * from './lib/cells/vol-cell'; export * from './lib/cells/vol-cell';
export * from './lib/cells/centered-grid-cell'; export * from './lib/cells/centered-grid-cell';
export * from './lib/cells/market-name-cell';
export * from './lib/filters/date-range-filter'; export * from './lib/filters/date-range-filter';
export * from './lib/filters/set-filter'; export * from './lib/filters/set-filter';

View File

@ -0,0 +1,35 @@
import type { MouseEvent } from 'react';
import { useCallback } from 'react';
import get from 'lodash/get';
interface MarketNameCellProps {
value?: string;
data?: { id?: string; marketId?: string; market?: { id: string } };
idPath?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}
export const MarketNameCell = ({
value,
data,
idPath,
onMarketClick,
}: MarketNameCellProps) => {
const id = data ? get(data, idPath ?? 'id', 'all') : '';
const handleOnClick = useCallback(
(ev: MouseEvent<HTMLButtonElement>) => {
ev.preventDefault();
ev.stopPropagation();
if (onMarketClick) {
onMarketClick(id, ev.metaKey);
}
},
[id, onMarketClick]
);
if (!data) return null;
return (
<button onClick={handleOnClick} tabIndex={0}>
{value}
</button>
);
};

View File

@ -26,7 +26,7 @@ export const compileGridData = (
| 'targetStake' | 'targetStake'
| 'trigger' | 'trigger'
> | null, > | null,
onSelect?: (id: string) => void onSelect?: (id: string, metaKey?: boolean) => void
): { label: ReactNode; value?: ReactNode }[] => { ): { label: ReactNode; value?: ReactNode }[] => {
const grid: SimpleGridProps['grid'] = []; const grid: SimpleGridProps['grid'] = [];
const isLiquidityMonitoringAuction = const isLiquidityMonitoringAuction =
@ -78,7 +78,7 @@ export const compileGridData = (
label: ( label: (
<Link <Link
to={`/liquidity/${market.id}`} to={`/liquidity/${market.id}`}
onClick={() => onSelect && onSelect(market.id)} onClick={(ev) => onSelect && onSelect(market.id, ev.metaKey)}
> >
<UILink>{t('Current liquidity')}</UILink> <UILink>{t('Current liquidity')}</UILink>
</Link> </Link>

View File

@ -12,14 +12,16 @@ import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list';
type TradingModeTooltipProps = { type TradingModeTooltipProps = {
marketId?: string; marketId?: string;
onSelect?: (marketId: string) => void; onSelect?: (marketId: string, metaKey?: boolean) => void;
skip?: boolean; skip?: boolean;
skipGrid?: boolean;
}; };
export const TradingModeTooltip = ({ export const TradingModeTooltip = ({
marketId, marketId,
onSelect, onSelect,
skip, skip,
skipGrid,
}: TradingModeTooltipProps) => { }: TradingModeTooltipProps) => {
const { VEGA_DOCS_URL } = useEnvironment(); const { VEGA_DOCS_URL } = useEnvironment();
const { data: market } = useMarket(marketId); const { data: market } = useMarket(marketId);
@ -42,7 +44,7 @@ export const TradingModeTooltip = ({
); );
const compiledGrid = const compiledGrid =
onSelect && compileGridData(market, marketData, onSelect); !skipGrid && compileGridData(market, marketData, onSelect);
switch (marketTradingMode) { switch (marketTradingMode) {
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: { case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: {
@ -103,6 +105,7 @@ export const TradingModeTooltip = ({
{VEGA_DOCS_URL && ( {VEGA_DOCS_URL && (
<ExternalLink <ExternalLink
href={createDocsLinks(VEGA_DOCS_URL).AUCTION_TYPE_OPENING} href={createDocsLinks(VEGA_DOCS_URL).AUCTION_TYPE_OPENING}
className="ml-1"
> >
{t('Find out more')} {t('Find out more')}
</ExternalLink> </ExternalLink>
@ -129,6 +132,7 @@ export const TradingModeTooltip = ({
createDocsLinks(VEGA_DOCS_URL) createDocsLinks(VEGA_DOCS_URL)
.AUCTION_TYPE_LIQUIDITY_MONITORING .AUCTION_TYPE_LIQUIDITY_MONITORING
} }
className="ml-1"
> >
{t('Find out more')} {t('Find out more')}
</ExternalLink> </ExternalLink>
@ -153,6 +157,7 @@ export const TradingModeTooltip = ({
createDocsLinks(VEGA_DOCS_URL) createDocsLinks(VEGA_DOCS_URL)
.AUCTION_TYPE_LIQUIDITY_MONITORING .AUCTION_TYPE_LIQUIDITY_MONITORING
} }
className="ml-1"
> >
{t('Find out more')} {t('Find out more')}
</ExternalLink> </ExternalLink>
@ -175,6 +180,7 @@ export const TradingModeTooltip = ({
createDocsLinks(VEGA_DOCS_URL) createDocsLinks(VEGA_DOCS_URL)
.AUCTION_TYPE_PRICE_MONITORING .AUCTION_TYPE_PRICE_MONITORING
} }
className="ml-1"
> >
{t('Find out more')} {t('Find out more')}
</ExternalLink> </ExternalLink>

View File

@ -8,7 +8,7 @@ export const FillsContainer = ({
onMarketClick, onMarketClick,
}: { }: {
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}) => { }) => {
const { pubKey } = useVegaWallet(); const { pubKey } = useVegaWallet();

View File

@ -11,7 +11,7 @@ import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
interface FillsManagerProps { interface FillsManagerProps {
partyId: string; partyId: string;
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
} }
export const FillsManager = ({ export const FillsManager = ({

View File

@ -7,6 +7,7 @@ import type { Trade } from './fills-data-provider';
import { FillsTable, getFeesBreakdown } from './fills-table'; import { FillsTable, getFeesBreakdown } from './fills-table';
import { generateFill } from './test-helpers'; import { generateFill } from './test-helpers';
import { MemoryRouter } from 'react-router-dom';
describe('FillsTable', () => { describe('FillsTable', () => {
let defaultFill: PartialDeep<Trade>; let defaultFill: PartialDeep<Trade>;
@ -36,7 +37,11 @@ describe('FillsTable', () => {
it('correct columns are rendered', async () => { it('correct columns are rendered', async () => {
await act(async () => { await act(async () => {
render(<FillsTable partyId="party-id" rowData={[generateFill()]} />); render(
<MemoryRouter>
<FillsTable partyId="party-id" rowData={[generateFill()]} />
</MemoryRouter>
);
}); });
const headers = screen.getAllByRole('columnheader'); const headers = screen.getAllByRole('columnheader');
@ -67,7 +72,11 @@ describe('FillsTable', () => {
liquidityFee: '2', liquidityFee: '2',
}, },
}); });
render(<FillsTable partyId={partyId} rowData={[{ ...buyerFill }]} />); render(
<MemoryRouter>
<FillsTable partyId={partyId} rowData={[{ ...buyerFill }]} />
</MemoryRouter>
);
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
const expectedValues = [ const expectedValues = [
buyerFill.market?.tradableInstrument.instrument.name || '', buyerFill.market?.tradableInstrument.instrument.name || '',
@ -100,7 +109,11 @@ describe('FillsTable', () => {
liquidityFee: '1', liquidityFee: '1',
}, },
}); });
render(<FillsTable partyId={partyId} rowData={[buyerFill]} />); render(
<MemoryRouter>
<FillsTable partyId={partyId} rowData={[buyerFill]} />
</MemoryRouter>
);
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
const expectedValues = [ const expectedValues = [
@ -129,7 +142,9 @@ describe('FillsTable', () => {
aggressor: Schema.Side.SIDE_SELL, aggressor: Schema.Side.SIDE_SELL,
}); });
const { rerender } = render( const { rerender } = render(
<FillsTable partyId={partyId} rowData={[takerFill]} /> <MemoryRouter>
<FillsTable partyId={partyId} rowData={[takerFill]} />
</MemoryRouter>
); );
expect( expect(
screen screen
@ -143,7 +158,11 @@ describe('FillsTable', () => {
}, },
aggressor: Schema.Side.SIDE_BUY, aggressor: Schema.Side.SIDE_BUY,
}); });
rerender(<FillsTable partyId={partyId} rowData={[makerFill]} />); rerender(
<MemoryRouter>
<FillsTable partyId={partyId} rowData={[makerFill]} />
</MemoryRouter>
);
expect( expect(
screen screen
@ -160,7 +179,11 @@ describe('FillsTable', () => {
}, },
aggressor: Schema.Side.SIDE_SELL, aggressor: Schema.Side.SIDE_SELL,
}); });
render(<FillsTable partyId={partyId} rowData={[takerFill]} />); render(
<MemoryRouter>
<FillsTable partyId={partyId} rowData={[takerFill]} />
</MemoryRouter>
);
const feeCell = screen const feeCell = screen
.getAllByRole('gridcell') .getAllByRole('gridcell')

View File

@ -14,16 +14,13 @@ import {
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { AgGridColumn } from 'ag-grid-react'; import { AgGridColumn } from 'ag-grid-react';
import { Link } from '@vegaprotocol/ui-toolkit';
import { import {
AgGridDynamic as AgGrid, AgGridDynamic as AgGrid,
positiveClassNames, positiveClassNames,
negativeClassNames, negativeClassNames,
MarketNameCell,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import type { import type { VegaValueFormatterParams } from '@vegaprotocol/datagrid';
VegaICellRendererParams,
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { forwardRef } from 'react'; import { forwardRef } from 'react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import type { Trade } from './fills-data-provider'; import type { Trade } from './fills-data-provider';
@ -34,7 +31,7 @@ const MAKER = 'MAKER';
export type Props = (AgGridReactProps | AgReactUiProps) & { export type Props = (AgGridReactProps | AgReactUiProps) & {
partyId: string; partyId: string;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}; };
export const FillsTable = forwardRef<AgGridReact, Props>( export const FillsTable = forwardRef<AgGridReact, Props>(
@ -48,30 +45,14 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
getRowId={({ data }) => data?.id} getRowId={({ data }) => data?.id}
tooltipShowDelay={0} tooltipShowDelay={0}
tooltipHideDelay={2000} tooltipHideDelay={2000}
components={{ MarketNameCell }}
{...props} {...props}
> >
<AgGridColumn <AgGridColumn
headerName={t('Market')} headerName={t('Market')}
field="market.tradableInstrument.instrument.name" field="market.tradableInstrument.instrument.name"
cellRenderer={({ cellRenderer="MarketNameCell"
value, cellRendererParams={{ idPath: 'market.id', onMarketClick }}
data,
}: VegaICellRendererParams<
Trade,
'market.tradableInstrument.instrument.name'
>) =>
onMarketClick ? (
<Link
onClick={() =>
data?.market?.id && onMarketClick(data?.market?.id)
}
>
{value}
</Link>
) : (
value
)
}
/> />
<AgGridColumn <AgGridColumn
headerName={t('Size')} headerName={t('Size')}

View File

@ -39,12 +39,12 @@ import {
export interface InfoProps { export interface InfoProps {
market: MarketInfoWithDataAndCandles; market: MarketInfoWithDataAndCandles;
onSelect: (id: string) => void; onSelect?: (id: string, metaKey?: boolean) => void;
} }
export interface MarketInfoContainerProps { export interface MarketInfoContainerProps {
marketId: string; marketId: string;
onSelect?: (id: string) => void; onSelect?: (id: string, metaKey?: boolean) => void;
} }
export const MarketInfoContainer = ({ export const MarketInfoContainer = ({
marketId, marketId,
@ -73,7 +73,7 @@ export const MarketInfoContainer = ({
<AsyncRenderer data={data} loading={loading} error={error} reload={reload}> <AsyncRenderer data={data} loading={loading} error={error} reload={reload}>
{data ? ( {data ? (
<TinyScroll className="h-full overflow-auto"> <TinyScroll className="h-full overflow-auto">
<Info market={data} onSelect={(id) => onSelect?.(id)} /> <Info market={data} onSelect={onSelect} />
</TinyScroll> </TinyScroll>
) : ( ) : (
<Splash> <Splash>
@ -169,7 +169,7 @@ export const Info = ({ market, onSelect }: InfoProps) => {
<LiquidityInfoPanel market={market}> <LiquidityInfoPanel market={market}>
<Link <Link
to={`/liquidity/${market.id}`} to={`/liquidity/${market.id}`}
onClick={() => onSelect(market.id)} onClick={(ev) => onSelect?.(market.id, ev.metaKey)}
data-testid="view-liquidity-link" data-testid="view-liquidity-link"
> >
<UILink>{t('View liquidity provision table')}</UILink> <UILink>{t('View liquidity provision table')}</UILink>

View File

@ -10,6 +10,7 @@ import type {
import { import {
AgGridDynamic as AgGrid, AgGridDynamic as AgGrid,
PriceFlashCell, PriceFlashCell,
MarketNameCell,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react'; import { AgGridColumn } from 'ag-grid-react';
@ -24,8 +25,10 @@ export const getRowId = ({ data }: { data: { id: string } }) => data.id;
export const MarketListTable = forwardRef< export const MarketListTable = forwardRef<
AgGridReact, AgGridReact,
TypedDataAgGrid<MarketMaybeWithData> TypedDataAgGrid<MarketMaybeWithData> & {
>((props, ref) => { onMarketClick: (marketId: string, metaKey?: boolean) => void;
}
>(({ onMarketClick, ...props }, ref) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
return ( return (
<AgGrid <AgGrid
@ -41,22 +44,14 @@ export const MarketListTable = forwardRef<
filterParams: { buttons: ['reset'] }, filterParams: { buttons: ['reset'] },
}} }}
suppressCellFocus={true} suppressCellFocus={true}
components={{ PriceFlashCell }} components={{ PriceFlashCell, MarketNameCell }}
{...props} {...props}
> >
<AgGridColumn <AgGridColumn
headerName={t('Market')} headerName={t('Market')}
field="tradableInstrument.instrument.code" field="tradableInstrument.instrument.code"
cellRenderer={({ cellRenderer="MarketNameCell"
value, cellRendererParams={{ onMarketClick }}
data,
}: VegaICellRendererParams<
MarketMaybeWithData,
'tradableInstrument.instrument.code'
>) => {
if (!data) return null;
return <span data-testid={`market-${data.id}`}>{value}</span>;
}}
/> />
<AgGridColumn <AgGridColumn
headerName={t('Description')} headerName={t('Description')}

View File

@ -1,12 +1,14 @@
import type { MouseEvent } from 'react';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { MarketListTable } from './market-list-table'; import { MarketListTable } from './market-list-table';
import { useDataProvider } from '@vegaprotocol/react-helpers'; import { useDataProvider } from '@vegaprotocol/react-helpers';
import type { RowClickedEvent } from 'ag-grid-community'; import type { CellClickedEvent } from 'ag-grid-community';
import { marketsWithDataProvider as dataProvider } from '../../markets-provider'; import { marketsWithDataProvider as dataProvider } from '../../markets-provider';
import type { MarketMaybeWithData } from '../../markets-provider'; import type { MarketMaybeWithData } from '../../markets-provider';
interface MarketsContainerProps { interface MarketsContainerProps {
onSelect: (marketId: string) => void; onSelect: (marketId: string, metaKey?: boolean) => void;
} }
export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => { export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
@ -21,16 +23,23 @@ export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
rowData={error ? [] : data} rowData={error ? [] : data}
suppressLoadingOverlay suppressLoadingOverlay
suppressNoRowsOverlay suppressNoRowsOverlay
onRowClicked={(rowEvent: RowClickedEvent) => { onCellClicked={(cellEvent: CellClickedEvent) => {
const { data, event } = rowEvent; const { data, column, event } = cellEvent;
// filters out clicks on the symbol column because it should display asset details const colId = column.getColId();
if ( if (
(event?.target as HTMLElement).tagName.toUpperCase() === 'BUTTON' [
'tradableInstrument.instrument.code',
'tradableInstrument.instrument.product.settlementAsset',
].includes(colId)
) { ) {
return; return;
} }
onSelect((data as MarketMaybeWithData).id); onSelect(
(data as MarketMaybeWithData).id,
(event as unknown as MouseEvent)?.metaKey
);
}} }}
onMarketClick={onSelect}
/> />
<div className="pointer-events-none absolute inset-0"> <div className="pointer-events-none absolute inset-0">
<AsyncRenderer <AsyncRenderer

View File

@ -9,7 +9,7 @@ export const OrderListContainer = ({
enforceBottomPlaceholder, enforceBottomPlaceholder,
}: { }: {
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
enforceBottomPlaceholder?: boolean; enforceBottomPlaceholder?: boolean;
}) => { }) => {
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();

View File

@ -23,7 +23,7 @@ import type { Order, OrderEdge } from '../order-data-provider';
export interface OrderListManagerProps { export interface OrderListManagerProps {
partyId: string; partyId: string;
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean; isReadOnly: boolean;
enforceBottomPlaceholder?: boolean; enforceBottomPlaceholder?: boolean;
} }

View File

@ -13,6 +13,7 @@ import {
limitOrder, limitOrder,
marketOrder, marketOrder,
} from '../mocks/generate-orders'; } from '../mocks/generate-orders';
import { MemoryRouter } from 'react-router-dom';
// Mock theme switcher to get around inconsistent mocking of zustand // Mock theme switcher to get around inconsistent mocking of zustand
// stores // stores
@ -36,9 +37,11 @@ const generateJsx = (
) => { ) => {
return ( return (
<MockedProvider> <MockedProvider>
<VegaWalletContext.Provider value={context as VegaWalletContextShape}> <MemoryRouter>
<OrderListTable {...defaultProps} {...props} /> <VegaWalletContext.Provider value={context as VegaWalletContextShape}>
</VegaWalletContext.Provider> <OrderListTable {...defaultProps} {...props} />
</VegaWalletContext.Provider>
</MemoryRouter>
</MockedProvider> </MockedProvider>
); );
}; };

View File

@ -5,7 +5,7 @@ import {
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react'; import { AgGridColumn } from 'ag-grid-react';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import { memo, forwardRef } from 'react'; import { memo, forwardRef } from 'react';
@ -15,6 +15,7 @@ import {
DateRangeFilter, DateRangeFilter,
negativeClassNames, negativeClassNames,
positiveClassNames, positiveClassNames,
MarketNameCell,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import type { import type {
TypedDataAgGrid, TypedDataAgGrid,
@ -29,7 +30,7 @@ type OrderListProps = TypedDataAgGrid<Order> & { marketId?: string };
export type OrderListTableProps = OrderListProps & { export type OrderListTableProps = OrderListProps & {
cancel: (order: Order) => void; cancel: (order: Order) => void;
setEditOrder: (order: Order) => void; setEditOrder: (order: Order) => void;
onMarketClick?: (marketId: string) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean; isReadOnly: boolean;
}; };
@ -50,30 +51,14 @@ export const OrderListTable = memo(
height: '100%', height: '100%',
}} }}
getRowId={({ data }) => data.id} getRowId={({ data }) => data.id}
components={{ MarketNameCell }}
{...props} {...props}
> >
<AgGridColumn <AgGridColumn
headerName={t('Market')} headerName={t('Market')}
field="market.tradableInstrument.instrument.code" field="market.tradableInstrument.instrument.code"
cellRenderer={({ cellRenderer="MarketNameCell"
value, cellRendererParams={{ idPath: 'market.id', onMarketClick }}
data,
}: VegaICellRendererParams<
Order,
'market.tradableInstrument.instrument.code'
>) =>
onMarketClick ? (
<Link
onClick={() =>
data?.market?.id && onMarketClick(data?.market?.id)
}
>
{value}
</Link>
) : (
value
)
}
minWidth={150} minWidth={150}
/> />
<AgGridColumn <AgGridColumn

View File

@ -6,6 +6,7 @@ import type { Position } from './positions-data-providers';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types'; import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types';
import type { ICellRendererParams } from 'ag-grid-community'; import type { ICellRendererParams } from 'ag-grid-community';
import { MemoryRouter } from 'react-router-dom';
const singleRow: Position = { const singleRow: Position = {
marketName: 'ETH/BTC (31 july 2022)', marketName: 'ETH/BTC (31 july 2022)',
@ -37,7 +38,9 @@ const singleRowData = [singleRow];
it('should render successfully', async () => { it('should render successfully', async () => {
await act(async () => { await act(async () => {
const { baseElement } = render( const { baseElement } = render(
<PositionsTable rowData={[]} isReadOnly={false} /> <MemoryRouter>
<PositionsTable rowData={[]} isReadOnly={false} />
</MemoryRouter>
); );
expect(baseElement).toBeTruthy(); expect(baseElement).toBeTruthy();
}); });
@ -45,7 +48,11 @@ it('should render successfully', async () => {
it('render correct columns', async () => { it('render correct columns', async () => {
await act(async () => { await act(async () => {
render(<PositionsTable rowData={singleRowData} isReadOnly={true} />); render(
<MemoryRouter>
<PositionsTable rowData={singleRowData} isReadOnly={true} />
</MemoryRouter>
);
}); });
const headers = screen.getAllByRole('columnheader'); const headers = screen.getAllByRole('columnheader');
@ -69,7 +76,11 @@ it('render correct columns', async () => {
it('renders market name', async () => { it('renders market name', async () => {
await act(async () => { await act(async () => {
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />); render(
<MemoryRouter>
<PositionsTable rowData={singleRowData} isReadOnly={false} />
</MemoryRouter>
);
}); });
expect(screen.getByText('ETH/BTC (31 july 2022)')).toBeTruthy(); expect(screen.getByText('ETH/BTC (31 july 2022)')).toBeTruthy();
}); });
@ -80,7 +91,11 @@ it('Does not fail if the market name does not match the split pattern', async ()
Object.assign({}, singleRow, { marketName: breakingMarketName }), Object.assign({}, singleRow, { marketName: breakingMarketName }),
]; ];
await act(async () => { await act(async () => {
render(<PositionsTable rowData={row} isReadOnly={false} />); render(
<MemoryRouter>
<PositionsTable rowData={row} isReadOnly={false} />
</MemoryRouter>
);
}); });
expect(screen.getByText(breakingMarketName)).toBeTruthy(); expect(screen.getByText(breakingMarketName)).toBeTruthy();
@ -90,7 +105,9 @@ it('add color and sign to amount, displays positive notional value', async () =>
let result: RenderResult; let result: RenderResult;
await act(async () => { await act(async () => {
result = render( result = render(
<PositionsTable rowData={singleRowData} isReadOnly={false} /> <MemoryRouter>
<PositionsTable rowData={singleRowData} isReadOnly={false} />
</MemoryRouter>
); );
}); });
let cells = screen.getAllByRole('gridcell'); let cells = screen.getAllByRole('gridcell');
@ -101,10 +118,12 @@ it('add color and sign to amount, displays positive notional value', async () =>
expect(cells[1].textContent).toEqual('1,230.0'); expect(cells[1].textContent).toEqual('1,230.0');
await act(async () => { await act(async () => {
result.rerender( result.rerender(
<PositionsTable <MemoryRouter>
rowData={[{ ...singleRow, openVolume: '-100' }]} <PositionsTable
isReadOnly={false} rowData={[{ ...singleRow, openVolume: '-100' }]}
/> isReadOnly={false}
/>
</MemoryRouter>
); );
}); });
cells = screen.getAllByRole('gridcell'); cells = screen.getAllByRole('gridcell');
@ -118,7 +137,9 @@ it('displays mark price', async () => {
let result: RenderResult; let result: RenderResult;
await act(async () => { await act(async () => {
result = render( result = render(
<PositionsTable rowData={singleRowData} isReadOnly={false} /> <MemoryRouter>
<PositionsTable rowData={singleRowData} isReadOnly={false} />
</MemoryRouter>
); );
}); });
@ -127,16 +148,18 @@ it('displays mark price', async () => {
await act(async () => { await act(async () => {
result.rerender( result.rerender(
<PositionsTable <MemoryRouter>
rowData={[ <PositionsTable
{ rowData={[
...singleRow, {
marketTradingMode: ...singleRow,
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION, marketTradingMode:
}, Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
]} },
isReadOnly={false} ]}
/> isReadOnly={false}
/>
</MemoryRouter>
); );
}); });
@ -146,7 +169,11 @@ it('displays mark price', async () => {
it('displays leverage', async () => { it('displays leverage', async () => {
await act(async () => { await act(async () => {
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />); render(
<MemoryRouter>
<PositionsTable rowData={singleRowData} isReadOnly={false} />
</MemoryRouter>
);
}); });
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
expect(cells[6].textContent).toEqual('1.1'); expect(cells[6].textContent).toEqual('1.1');
@ -154,7 +181,11 @@ it('displays leverage', async () => {
it('displays allocated margin', async () => { it('displays allocated margin', async () => {
await act(async () => { await act(async () => {
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />); render(
<MemoryRouter>
<PositionsTable rowData={singleRowData} isReadOnly={false} />
</MemoryRouter>
);
}); });
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
const cell = cells[7]; const cell = cells[7];
@ -163,7 +194,11 @@ it('displays allocated margin', async () => {
it('displays realised and unrealised PNL', async () => { it('displays realised and unrealised PNL', async () => {
await act(async () => { await act(async () => {
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />); render(
<MemoryRouter>
<PositionsTable rowData={singleRowData} isReadOnly={false} />
</MemoryRouter>
);
}); });
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
expect(cells[9].textContent).toEqual('4.56'); expect(cells[9].textContent).toEqual('4.56');
@ -172,13 +207,15 @@ it('displays realised and unrealised PNL', async () => {
it('displays close button', async () => { it('displays close button', async () => {
await act(async () => { await act(async () => {
render( render(
<PositionsTable <MemoryRouter>
rowData={singleRowData} <PositionsTable
onClose={() => { rowData={singleRowData}
return; onClose={() => {
}} return;
isReadOnly={false} }}
/> isReadOnly={false}
/>
</MemoryRouter>
); );
}); });
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');
@ -188,13 +225,15 @@ it('displays close button', async () => {
it('do not display close button if openVolume is zero', async () => { it('do not display close button if openVolume is zero', async () => {
await act(async () => { await act(async () => {
render( render(
<PositionsTable <MemoryRouter>
rowData={[{ ...singleRow, openVolume: '0' }]} <PositionsTable
onClose={() => { rowData={[{ ...singleRow, openVolume: '0' }]}
return; onClose={() => {
}} return;
isReadOnly={false} }}
/> isReadOnly={false}
/>
</MemoryRouter>
); );
}); });
const cells = screen.getAllByRole('gridcell'); const cells = screen.getAllByRole('gridcell');

View File

@ -14,12 +14,12 @@ import {
PriceFlashCell, PriceFlashCell,
signedNumberCssClass, signedNumberCssClass,
signedNumberCssClassRules, signedNumberCssClassRules,
MarketNameCell,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import { import {
ButtonLink, ButtonLink,
Tooltip, Tooltip,
TooltipCellComponent, TooltipCellComponent,
Link,
ExternalLink, ExternalLink,
Icon, Icon,
ProgressBarCell, ProgressBarCell,
@ -43,7 +43,7 @@ import { useEnvironment } from '@vegaprotocol/environment';
interface Props extends TypedDataAgGrid<Position> { interface Props extends TypedDataAgGrid<Position> {
onClose?: (data: Position) => void; onClose?: (data: Position) => void;
onMarketClick?: (id: string) => void; onMarketClick?: (id: string, metaKey?: boolean) => void;
style?: CSSProperties; style?: CSSProperties;
isReadOnly: boolean; isReadOnly: boolean;
} }
@ -96,26 +96,19 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
filterParams: { buttons: ['reset'] }, filterParams: { buttons: ['reset'] },
tooltipComponent: TooltipCellComponent, tooltipComponent: TooltipCellComponent,
}} }}
components={{ AmountCell, PriceFlashCell, ProgressBarCell }} components={{
AmountCell,
PriceFlashCell,
ProgressBarCell,
MarketNameCell,
}}
{...props} {...props}
> >
<AgGridColumn <AgGridColumn
headerName={t('Market')} headerName={t('Market')}
field="marketName" field="marketName"
cellRenderer={({ cellRenderer="MarketNameCell"
value, cellRendererParams={{ idPath: 'marketId', onMarketClick }}
data,
}: VegaICellRendererParams<Position, 'marketName'>) =>
onMarketClick ? (
<Link
onClick={() => data?.marketId && onMarketClick(data?.marketId)}
>
{value}
</Link>
) : (
value
)
}
minWidth={190} minWidth={190}
/> />
<AgGridColumn <AgGridColumn