diff --git a/apps/trading/client-pages/market/market.tsx b/apps/trading/client-pages/market/market.tsx index 75bedefbd..a98849681 100644 --- a/apps/trading/client-pages/market/market.tsx +++ b/apps/trading/client-pages/market/market.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; import { @@ -13,6 +13,7 @@ import { useGlobalStore, usePageTitleStore } from '../../stores'; import { TradeGrid, TradePanels } from './trade-grid'; import { useNavigate, useParams } from 'react-router-dom'; import { Links, Routes } from '../../pages/client-router'; +import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; const calculatePrice = (markPrice?: string, decimalPlaces?: number) => { return markPrice && decimalPlaces @@ -66,14 +67,7 @@ export const MarketPage = () => { const update = useGlobalStore((store) => store.update); const lastMarketId = useGlobalStore((store) => store.marketId); - const onSelect = useCallback( - (id: string) => { - if (id && id !== marketId) { - navigate(Links[Routes.MARKET](id)); - } - }, - [marketId, navigate] - ); + const onSelect = useMarketClickHandler(); const { data, error, loading } = useDataProvider({ dataProvider: marketProvider, diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index f8383d1f0..580fb6518 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -8,7 +8,7 @@ import { TradesContainer } from '@vegaprotocol/trades'; import { LayoutPriority } from 'allotment'; import classNames from 'classnames'; 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 { DepthChartContainer } from '@vegaprotocol/market-depth'; import { CandlesChartContainer } from '@vegaprotocol/candles-chart'; @@ -27,9 +27,9 @@ import { TradeMarketHeader } from './trade-market-header'; import { NO_MARKET } from './constants'; import { LiquidityContainer } from '../liquidity/liquidity'; import { useNavigate } from 'react-router-dom'; -import { Links, Routes } from '../../pages/client-router'; import type { PinnedAsset } from '@vegaprotocol/accounts'; import { useScreenDimensions } from '@vegaprotocol/react-helpers'; +import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; type MarketDependantView = | typeof CandlesChartContainer @@ -66,7 +66,7 @@ type TradingView = keyof typeof TradingViews; interface TradeGridProps { market: Market | null; - onSelect: (marketId: string) => void; + onSelect: (marketId: string, metaKey?: boolean) => void; pinnedAsset?: PinnedAsset; } @@ -78,15 +78,7 @@ interface BottomPanelProps { const MarketBottomPanel = memo( ({ marketId, pinnedAsset }: BottomPanelProps) => { const { screenSize } = useScreenDimensions(); - const navigate = useNavigate(); - const onMarketClick = useCallback( - (marketId: string) => { - navigate(Links[Routes.MARKET](marketId), { - replace: true, - }); - }, - [navigate] - ); + const onMarketClick = useMarketClickHandler(true); return 'xxxl' === screenSize ? ( @@ -189,7 +181,7 @@ const MainGrid = memo( pinnedAsset, }: { marketId: string; - onSelect?: (marketId: string) => void; + onSelect: (marketId: string, metaKey?: boolean) => void; pinnedAsset?: PinnedAsset; }) => { const navigate = useNavigate(); @@ -230,12 +222,7 @@ const MainGrid = memo( /> - { - onSelect?.(id); - }} - /> + @@ -304,7 +291,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => { interface TradePanelsProps { market: Market | null; - onSelect: (marketId: string) => void; + onSelect: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string) => void; onClickCollateral: () => void; pinnedAsset?: PinnedAsset; @@ -320,7 +307,7 @@ export const TradePanels = ({ const renderView = () => { const Component = memo<{ marketId: string; - onSelect: (marketId: string) => void; + onSelect: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string) => void; onClickCollateral: () => void; pinnedAsset?: PinnedAsset; diff --git a/apps/trading/client-pages/market/trade-market-header.tsx b/apps/trading/client-pages/market/trade-market-header.tsx index c9e4c814b..96ebae4f3 100644 --- a/apps/trading/client-pages/market/trade-market-header.tsx +++ b/apps/trading/client-pages/market/trade-market-header.tsx @@ -22,7 +22,7 @@ import { MarketState as State } from '@vegaprotocol/types'; interface TradeMarketHeaderProps { market: Market | null; - onSelect: (marketId: string) => void; + onSelect: (marketId: string, metaKey?: boolean) => void; } export const TradeMarketHeader = ({ @@ -91,7 +91,6 @@ export const TradeMarketHeader = ({ diff --git a/apps/trading/client-pages/markets/markets.tsx b/apps/trading/client-pages/markets/markets.tsx index ffdf3eb94..7a172583c 100644 --- a/apps/trading/client-pages/markets/markets.tsx +++ b/apps/trading/client-pages/markets/markets.tsx @@ -1,16 +1,7 @@ -import { useCallback } from 'react'; import { MarketsContainer } from '@vegaprotocol/market-list'; -import { useNavigate } from 'react-router-dom'; -import { Links, Routes } from '../../pages/client-router'; +import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; export const Markets = () => { - const navigate = useNavigate(); - const handleOnSelect = useCallback( - (marketId: string) => { - navigate(Links[Routes.MARKET](marketId)); - }, - [navigate] - ); - + const handleOnSelect = useMarketClickHandler(); return ; }; diff --git a/apps/trading/client-pages/portfolio/portfolio.tsx b/apps/trading/client-pages/portfolio/portfolio.tsx index 099d4e0d1..a8e9bcf8c 100644 --- a/apps/trading/client-pages/portfolio/portfolio.tsx +++ b/apps/trading/client-pages/portfolio/portfolio.tsx @@ -19,25 +19,18 @@ import { usePageTitleStore } from '../../stores'; import { LedgerContainer } from '@vegaprotocol/ledger'; import { AccountsContainer } from '../../components/accounts-container'; import { AccountHistoryContainer } from './account-history-container'; -import { useNavigate } from 'react-router-dom'; -import { Links, Routes } from '../../pages/client-router'; +import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; export const Portfolio = () => { const { updateTitle } = usePageTitleStore((store) => ({ updateTitle: store.updateTitle, })); - const navigate = useNavigate(); - useEffect(() => { updateTitle(titlefy([t('Portfolio')])); }, [updateTitle]); - const onMarketClick = (marketId: string) => { - navigate(Links[Routes.MARKET](marketId), { - replace: true, - }); - }; + const onMarketClick = useMarketClickHandler(true); const wrapperClasses = 'h-full max-h-full flex flex-col'; return ( diff --git a/apps/trading/components/market-trading-mode/market-trading-mode.tsx b/apps/trading/components/market-trading-mode/market-trading-mode.tsx index c3c1a50e3..05be7298f 100644 --- a/apps/trading/components/market-trading-mode/market-trading-mode.tsx +++ b/apps/trading/components/market-trading-mode/market-trading-mode.tsx @@ -25,7 +25,7 @@ const getTradingModeLabel = ( interface HeaderStatMarketTradingModeProps { marketId?: string; - onSelect?: (marketId: string) => void; + onSelect?: (marketId: string, metaKey?: boolean) => void; initialTradingMode?: Schema.MarketTradingMode; initialTrigger?: Schema.AuctionTrigger; } @@ -66,7 +66,9 @@ export const MarketTradingMode = ({ return ( } + description={ + + } > {getTradingModeLabel( diff --git a/apps/trading/components/select-market/select-market-columns.tsx b/apps/trading/components/select-market/select-market-columns.tsx index 817992b94..78d241ece 100644 --- a/apps/trading/components/select-market/select-market-columns.tsx +++ b/apps/trading/components/select-market/select-market-columns.tsx @@ -1,4 +1,4 @@ -import type { RefObject } from 'react'; +import type { RefObject, MouseEvent } from 'react'; import { FeesCell } from '@vegaprotocol/market-info'; import { calcCandleHigh, @@ -157,14 +157,14 @@ export const columnHeaders: Column[] = [ ]; export type OnCellClickHandler = ( - e: React.MouseEvent, + e: MouseEvent, kind: ColumnKind, value: string ) => void; export const columns = ( market: MarketMaybeWithDataAndCandles, - onSelect: (id: string) => void, + onSelect: (id: string, metaKey?: boolean) => void, onCellClick: OnCellClickHandler, inViewRoot?: RefObject ) => { @@ -174,14 +174,7 @@ export const columns = ( const candleLow = market.candles && calcCandleLow(market.candles); const candleHigh = market.candles && calcCandleHigh(market.candles); const candleVolume = market.candles && calcCandleVolume(market.candles); - const handleKeyPress = ( - event: React.KeyboardEvent, - id: string - ) => { - if (event.key === 'Enter' && onSelect) { - return onSelect(id); - } - }; + const selectMarketColumns: Column[] = [ { kind: ColumnKind.Market, @@ -189,10 +182,10 @@ export const columns = ( handleKeyPress(event, market.id)} onClick={(e) => { e.preventDefault(); - onSelect(market.id); + e.stopPropagation(); + onSelect(market.id, e.metaKey); }} > {market.tradableInstrument.instrument.code} @@ -352,7 +345,7 @@ export const columns = ( export const columnsPositionMarkets = ( market: MarketMaybeWithDataAndCandles, - onSelect: (id: string) => void, + onSelect: (id: string, metaKey?: boolean) => void, inViewRoot?: RefObject, openVolume?: string, onCellClick?: OnCellClickHandler @@ -362,14 +355,6 @@ export const columnsPositionMarkets = ( .filter((c: string | undefined): c is CandleClose => !isNil(c)); const candleLow = market.candles && calcCandleLow(market.candles); const candleHigh = market.candles && calcCandleHigh(market.candles); - const handleKeyPress = ( - event: React.KeyboardEvent, - id: string - ) => { - if (event.key === 'Enter' && onSelect) { - return onSelect(id); - } - }; const candleVolume = market.candles && calcCandleVolume(market.candles); const selectMarketColumns: Column[] = [ { @@ -378,10 +363,10 @@ export const columnsPositionMarkets = ( handleKeyPress(event, market.id)} onClick={(e) => { e.preventDefault(); - onSelect(market.id); + e.stopPropagation(); + onSelect(market.id, e.metaKey); }} > {market.tradableInstrument.instrument.code} diff --git a/apps/trading/components/select-market/select-market-table.tsx b/apps/trading/components/select-market/select-market-table.tsx index 36a3e5539..4c3684d47 100644 --- a/apps/trading/components/select-market/select-market-table.tsx +++ b/apps/trading/components/select-market/select-market-table.tsx @@ -37,14 +37,14 @@ export const SelectMarketTableRow = ({ }: { detailed?: boolean; columns: Column[]; - onSelect: (id: string) => void; + onSelect: (id: string, metaKey?: boolean) => void; marketId: string; }) => { return ( { - onSelect(marketId); + onClick={(ev) => { + onSelect(marketId, ev.metaKey); }} data-testid={`market-link-${marketId}`} > diff --git a/apps/trading/components/select-market/select-market.spec.tsx b/apps/trading/components/select-market/select-market.spec.tsx index b87eb9938..e7fec9a86 100644 --- a/apps/trading/components/select-market/select-market.spec.tsx +++ b/apps/trading/components/select-market/select-market.spec.tsx @@ -178,6 +178,6 @@ describe('SelectMarket', () => { expect(screen.getByText('25.00%')).toBeTruthy(); // price change expect(container).toHaveTextContent(/1,000/); // volume fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]); - expect(onSelect).toHaveBeenCalledWith('1'); + expect(onSelect).toHaveBeenCalledWith('1', false); }); }); diff --git a/apps/trading/components/select-market/select-market.tsx b/apps/trading/components/select-market/select-market.tsx index 24a2d66b1..2a0ede834 100644 --- a/apps/trading/components/select-market/select-market.tsx +++ b/apps/trading/components/select-market/select-market.tsx @@ -40,7 +40,7 @@ export const SelectAllMarketsTableBody = ({ markets?: MarketMaybeWithDataAndCandles[] | null; positions?: PositionFieldsFragment[]; title?: string; - onSelect: (id: string) => void; + onSelect: (id: string, metaKey?: boolean) => void; onCellClick: OnCellClickHandler; headers?: Column[]; tableColumns?: ( @@ -95,7 +95,7 @@ export const SelectMarketPopover = ({ }: { marketCode: string; marketName: string; - onSelect: (id: string) => void; + onSelect: (id: string, metaKey?: boolean) => void; onCellClick: OnCellClickHandler; }) => { const { pubKey } = useVegaWallet(); @@ -116,8 +116,8 @@ export const SelectMarketPopover = ({ skip: !pubKey, }); const onSelectMarket = useCallback( - (marketId: string) => { - onSelect(marketId); + (marketId: string, metaKey?: boolean) => { + onSelect(marketId, metaKey); setOpen(false); }, [onSelect] diff --git a/apps/trading/lib/hooks/use-market-click-handler.ts b/apps/trading/lib/hooks/use-market-click-handler.ts new file mode 100644 index 000000000..946a27889 --- /dev/null +++ b/apps/trading/lib/hooks/use-market-click-handler.ts @@ -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] + ); +}; diff --git a/apps/trading/pages/_app.page.tsx b/apps/trading/pages/_app.page.tsx index 30f99079c..1eeea5019 100644 --- a/apps/trading/pages/_app.page.tsx +++ b/apps/trading/pages/_app.page.tsx @@ -1,3 +1,5 @@ +import { useMemo, useState } from 'react'; +import classNames from 'classnames'; import Head from 'next/head'; import type { AppProps } from 'next/app'; import { t } from '@vegaprotocol/i18n'; @@ -23,14 +25,12 @@ import { import './styles.css'; import { useGlobalStore, usePageTitleStore } from '../stores'; import { Footer } from '../components/footer'; -import { useMemo, useState } from 'react'; import DialogsContainer from './dialogs-container'; import ToastsManager from './toasts-manager'; import { HashRouter, useLocation, useSearchParams } from 'react-router-dom'; import { Connectors } from '../lib/vega-connectors'; import { ViewingBanner } from '../components/viewing-banner'; import { Banner } from '../components/banner'; -import classNames from 'classnames'; import { AppLoader, DynamicLoader } from '../components/app-loader'; import { Navbar } from '../components/navbar'; @@ -57,7 +57,7 @@ const Title = () => { ); }; -const TransactionsHandler = () => { +const InitializeHandlers = () => { useVegaTransactionManager(); useVegaTransactionUpdater(); useEthTransactionManager(); @@ -93,7 +93,7 @@ function AppBody({ Component }: AppProps) { - + ); diff --git a/libs/datagrid/src/index.ts b/libs/datagrid/src/index.ts index b33afd315..f84344412 100644 --- a/libs/datagrid/src/index.ts +++ b/libs/datagrid/src/index.ts @@ -9,6 +9,7 @@ export * from './lib/cells/price-change-cell'; export * from './lib/cells/price-flash-cell'; export * from './lib/cells/vol-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/set-filter'; diff --git a/libs/datagrid/src/lib/cells/market-name-cell.tsx b/libs/datagrid/src/lib/cells/market-name-cell.tsx new file mode 100644 index 000000000..808273f7f --- /dev/null +++ b/libs/datagrid/src/lib/cells/market-name-cell.tsx @@ -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) => { + ev.preventDefault(); + ev.stopPropagation(); + if (onMarketClick) { + onMarketClick(id, ev.metaKey); + } + }, + [id, onMarketClick] + ); + if (!data) return null; + return ( + + ); +}; diff --git a/libs/deal-ticket/src/components/trading-mode-tooltip/compile-grid-data.tsx b/libs/deal-ticket/src/components/trading-mode-tooltip/compile-grid-data.tsx index 0d383edb1..1aeb7e3eb 100644 --- a/libs/deal-ticket/src/components/trading-mode-tooltip/compile-grid-data.tsx +++ b/libs/deal-ticket/src/components/trading-mode-tooltip/compile-grid-data.tsx @@ -26,7 +26,7 @@ export const compileGridData = ( | 'targetStake' | 'trigger' > | null, - onSelect?: (id: string) => void + onSelect?: (id: string, metaKey?: boolean) => void ): { label: ReactNode; value?: ReactNode }[] => { const grid: SimpleGridProps['grid'] = []; const isLiquidityMonitoringAuction = @@ -78,7 +78,7 @@ export const compileGridData = ( label: ( onSelect && onSelect(market.id)} + onClick={(ev) => onSelect && onSelect(market.id, ev.metaKey)} > {t('Current liquidity')} diff --git a/libs/deal-ticket/src/components/trading-mode-tooltip/trading-mode-tooltip.tsx b/libs/deal-ticket/src/components/trading-mode-tooltip/trading-mode-tooltip.tsx index 50ce68be6..e0de874da 100644 --- a/libs/deal-ticket/src/components/trading-mode-tooltip/trading-mode-tooltip.tsx +++ b/libs/deal-ticket/src/components/trading-mode-tooltip/trading-mode-tooltip.tsx @@ -12,14 +12,16 @@ import { useMarket, useStaticMarketData } from '@vegaprotocol/market-list'; type TradingModeTooltipProps = { marketId?: string; - onSelect?: (marketId: string) => void; + onSelect?: (marketId: string, metaKey?: boolean) => void; skip?: boolean; + skipGrid?: boolean; }; export const TradingModeTooltip = ({ marketId, onSelect, skip, + skipGrid, }: TradingModeTooltipProps) => { const { VEGA_DOCS_URL } = useEnvironment(); const { data: market } = useMarket(marketId); @@ -42,7 +44,7 @@ export const TradingModeTooltip = ({ ); const compiledGrid = - onSelect && compileGridData(market, marketData, onSelect); + !skipGrid && compileGridData(market, marketData, onSelect); switch (marketTradingMode) { case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS: { @@ -103,6 +105,7 @@ export const TradingModeTooltip = ({ {VEGA_DOCS_URL && ( {t('Find out more')} @@ -129,6 +132,7 @@ export const TradingModeTooltip = ({ createDocsLinks(VEGA_DOCS_URL) .AUCTION_TYPE_LIQUIDITY_MONITORING } + className="ml-1" > {t('Find out more')} @@ -153,6 +157,7 @@ export const TradingModeTooltip = ({ createDocsLinks(VEGA_DOCS_URL) .AUCTION_TYPE_LIQUIDITY_MONITORING } + className="ml-1" > {t('Find out more')} @@ -175,6 +180,7 @@ export const TradingModeTooltip = ({ createDocsLinks(VEGA_DOCS_URL) .AUCTION_TYPE_PRICE_MONITORING } + className="ml-1" > {t('Find out more')} diff --git a/libs/fills/src/lib/fills-container.tsx b/libs/fills/src/lib/fills-container.tsx index df759df4a..84a79f462 100644 --- a/libs/fills/src/lib/fills-container.tsx +++ b/libs/fills/src/lib/fills-container.tsx @@ -8,7 +8,7 @@ export const FillsContainer = ({ onMarketClick, }: { marketId?: string; - onMarketClick?: (marketId: string) => void; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; }) => { const { pubKey } = useVegaWallet(); diff --git a/libs/fills/src/lib/fills-manager.tsx b/libs/fills/src/lib/fills-manager.tsx index 51e3dc5a8..8ca9d30af 100644 --- a/libs/fills/src/lib/fills-manager.tsx +++ b/libs/fills/src/lib/fills-manager.tsx @@ -11,7 +11,7 @@ import { useBottomPlaceholder } from '@vegaprotocol/react-helpers'; interface FillsManagerProps { partyId: string; marketId?: string; - onMarketClick?: (marketId: string) => void; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; } export const FillsManager = ({ diff --git a/libs/fills/src/lib/fills-table.spec.tsx b/libs/fills/src/lib/fills-table.spec.tsx index f1ee45630..c68567ea4 100644 --- a/libs/fills/src/lib/fills-table.spec.tsx +++ b/libs/fills/src/lib/fills-table.spec.tsx @@ -7,6 +7,7 @@ import type { Trade } from './fills-data-provider'; import { FillsTable, getFeesBreakdown } from './fills-table'; import { generateFill } from './test-helpers'; +import { MemoryRouter } from 'react-router-dom'; describe('FillsTable', () => { let defaultFill: PartialDeep; @@ -36,7 +37,11 @@ describe('FillsTable', () => { it('correct columns are rendered', async () => { await act(async () => { - render(); + render( + + + + ); }); const headers = screen.getAllByRole('columnheader'); @@ -67,7 +72,11 @@ describe('FillsTable', () => { liquidityFee: '2', }, }); - render(); + render( + + + + ); const cells = screen.getAllByRole('gridcell'); const expectedValues = [ buyerFill.market?.tradableInstrument.instrument.name || '', @@ -100,7 +109,11 @@ describe('FillsTable', () => { liquidityFee: '1', }, }); - render(); + render( + + + + ); const cells = screen.getAllByRole('gridcell'); const expectedValues = [ @@ -129,7 +142,9 @@ describe('FillsTable', () => { aggressor: Schema.Side.SIDE_SELL, }); const { rerender } = render( - + + + ); expect( screen @@ -143,7 +158,11 @@ describe('FillsTable', () => { }, aggressor: Schema.Side.SIDE_BUY, }); - rerender(); + rerender( + + + + ); expect( screen @@ -160,7 +179,11 @@ describe('FillsTable', () => { }, aggressor: Schema.Side.SIDE_SELL, }); - render(); + render( + + + + ); const feeCell = screen .getAllByRole('gridcell') diff --git a/libs/fills/src/lib/fills-table.tsx b/libs/fills/src/lib/fills-table.tsx index dbfe59d33..eb3090225 100644 --- a/libs/fills/src/lib/fills-table.tsx +++ b/libs/fills/src/lib/fills-table.tsx @@ -14,16 +14,13 @@ import { import { t } from '@vegaprotocol/i18n'; import * as Schema from '@vegaprotocol/types'; import { AgGridColumn } from 'ag-grid-react'; -import { Link } from '@vegaprotocol/ui-toolkit'; import { AgGridDynamic as AgGrid, positiveClassNames, negativeClassNames, + MarketNameCell, } from '@vegaprotocol/datagrid'; -import type { - VegaICellRendererParams, - VegaValueFormatterParams, -} from '@vegaprotocol/datagrid'; +import type { VegaValueFormatterParams } from '@vegaprotocol/datagrid'; import { forwardRef } from 'react'; import BigNumber from 'bignumber.js'; import type { Trade } from './fills-data-provider'; @@ -34,7 +31,7 @@ const MAKER = 'MAKER'; export type Props = (AgGridReactProps | AgReactUiProps) & { partyId: string; - onMarketClick?: (marketId: string) => void; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; }; export const FillsTable = forwardRef( @@ -48,30 +45,14 @@ export const FillsTable = forwardRef( getRowId={({ data }) => data?.id} tooltipShowDelay={0} tooltipHideDelay={2000} + components={{ MarketNameCell }} {...props} > ) => - onMarketClick ? ( - - data?.market?.id && onMarketClick(data?.market?.id) - } - > - {value} - - ) : ( - value - ) - } + cellRenderer="MarketNameCell" + cellRendererParams={{ idPath: 'market.id', onMarketClick }} /> void; + onSelect?: (id: string, metaKey?: boolean) => void; } export interface MarketInfoContainerProps { marketId: string; - onSelect?: (id: string) => void; + onSelect?: (id: string, metaKey?: boolean) => void; } export const MarketInfoContainer = ({ marketId, @@ -73,7 +73,7 @@ export const MarketInfoContainer = ({ {data ? ( - onSelect?.(id)} /> + ) : ( @@ -169,7 +169,7 @@ export const Info = ({ market, onSelect }: InfoProps) => { onSelect(market.id)} + onClick={(ev) => onSelect?.(market.id, ev.metaKey)} data-testid="view-liquidity-link" > {t('View liquidity provision table')} diff --git a/libs/market-list/src/lib/components/markets-container/market-list-table.tsx b/libs/market-list/src/lib/components/markets-container/market-list-table.tsx index 358c1daa3..faa6fe978 100644 --- a/libs/market-list/src/lib/components/markets-container/market-list-table.tsx +++ b/libs/market-list/src/lib/components/markets-container/market-list-table.tsx @@ -10,6 +10,7 @@ import type { import { AgGridDynamic as AgGrid, PriceFlashCell, + MarketNameCell, } from '@vegaprotocol/datagrid'; import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { AgGridColumn } from 'ag-grid-react'; @@ -24,8 +25,10 @@ export const getRowId = ({ data }: { data: { id: string } }) => data.id; export const MarketListTable = forwardRef< AgGridReact, - TypedDataAgGrid ->((props, ref) => { + TypedDataAgGrid & { + onMarketClick: (marketId: string, metaKey?: boolean) => void; + } +>(({ onMarketClick, ...props }, ref) => { const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); return ( ) => { - if (!data) return null; - return {value}; - }} + cellRenderer="MarketNameCell" + cellRendererParams={{ onMarketClick }} /> void; + onSelect: (marketId: string, metaKey?: boolean) => void; } export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => { @@ -21,16 +23,23 @@ export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => { rowData={error ? [] : data} suppressLoadingOverlay suppressNoRowsOverlay - onRowClicked={(rowEvent: RowClickedEvent) => { - const { data, event } = rowEvent; - // filters out clicks on the symbol column because it should display asset details + onCellClicked={(cellEvent: CellClickedEvent) => { + const { data, column, event } = cellEvent; + const colId = column.getColId(); if ( - (event?.target as HTMLElement).tagName.toUpperCase() === 'BUTTON' + [ + 'tradableInstrument.instrument.code', + 'tradableInstrument.instrument.product.settlementAsset', + ].includes(colId) ) { return; } - onSelect((data as MarketMaybeWithData).id); + onSelect( + (data as MarketMaybeWithData).id, + (event as unknown as MouseEvent)?.metaKey + ); }} + onMarketClick={onSelect} />
void; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; enforceBottomPlaceholder?: boolean; }) => { const { pubKey, isReadOnly } = useVegaWallet(); diff --git a/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx b/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx index 6b2706e34..4be809922 100644 --- a/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx +++ b/libs/orders/src/lib/components/order-list-manager/order-list-manager.tsx @@ -23,7 +23,7 @@ import type { Order, OrderEdge } from '../order-data-provider'; export interface OrderListManagerProps { partyId: string; marketId?: string; - onMarketClick?: (marketId: string) => void; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; isReadOnly: boolean; enforceBottomPlaceholder?: boolean; } diff --git a/libs/orders/src/lib/components/order-list/order-list.spec.tsx b/libs/orders/src/lib/components/order-list/order-list.spec.tsx index 20f66106e..3d4f56538 100644 --- a/libs/orders/src/lib/components/order-list/order-list.spec.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.spec.tsx @@ -13,6 +13,7 @@ import { limitOrder, marketOrder, } from '../mocks/generate-orders'; +import { MemoryRouter } from 'react-router-dom'; // Mock theme switcher to get around inconsistent mocking of zustand // stores @@ -36,9 +37,11 @@ const generateJsx = ( ) => { return ( - - - + + + + + ); }; diff --git a/libs/orders/src/lib/components/order-list/order-list.tsx b/libs/orders/src/lib/components/order-list/order-list.tsx index c1d2e7ec6..b190e9287 100644 --- a/libs/orders/src/lib/components/order-list/order-list.tsx +++ b/libs/orders/src/lib/components/order-list/order-list.tsx @@ -5,7 +5,7 @@ import { } from '@vegaprotocol/utils'; import { t } from '@vegaprotocol/i18n'; 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 BigNumber from 'bignumber.js'; import { memo, forwardRef } from 'react'; @@ -15,6 +15,7 @@ import { DateRangeFilter, negativeClassNames, positiveClassNames, + MarketNameCell, } from '@vegaprotocol/datagrid'; import type { TypedDataAgGrid, @@ -29,7 +30,7 @@ type OrderListProps = TypedDataAgGrid & { marketId?: string }; export type OrderListTableProps = OrderListProps & { cancel: (order: Order) => void; setEditOrder: (order: Order) => void; - onMarketClick?: (marketId: string) => void; + onMarketClick?: (marketId: string, metaKey?: boolean) => void; isReadOnly: boolean; }; @@ -50,30 +51,14 @@ export const OrderListTable = memo( height: '100%', }} getRowId={({ data }) => data.id} + components={{ MarketNameCell }} {...props} > ) => - onMarketClick ? ( - - data?.market?.id && onMarketClick(data?.market?.id) - } - > - {value} - - ) : ( - value - ) - } + cellRenderer="MarketNameCell" + cellRendererParams={{ idPath: 'market.id', onMarketClick }} minWidth={150} /> { await act(async () => { const { baseElement } = render( - + + + ); expect(baseElement).toBeTruthy(); }); @@ -45,7 +48,11 @@ it('should render successfully', async () => { it('render correct columns', async () => { await act(async () => { - render(); + render( + + + + ); }); const headers = screen.getAllByRole('columnheader'); @@ -69,7 +76,11 @@ it('render correct columns', async () => { it('renders market name', async () => { await act(async () => { - render(); + render( + + + + ); }); 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 }), ]; await act(async () => { - render(); + render( + + + + ); }); expect(screen.getByText(breakingMarketName)).toBeTruthy(); @@ -90,7 +105,9 @@ it('add color and sign to amount, displays positive notional value', async () => let result: RenderResult; await act(async () => { result = render( - + + + ); }); 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'); await act(async () => { result.rerender( - + + + ); }); cells = screen.getAllByRole('gridcell'); @@ -118,7 +137,9 @@ it('displays mark price', async () => { let result: RenderResult; await act(async () => { result = render( - + + + ); }); @@ -127,16 +148,18 @@ it('displays mark price', async () => { await act(async () => { result.rerender( - + + + ); }); @@ -146,7 +169,11 @@ it('displays mark price', async () => { it('displays leverage', async () => { await act(async () => { - render(); + render( + + + + ); }); const cells = screen.getAllByRole('gridcell'); expect(cells[6].textContent).toEqual('1.1'); @@ -154,7 +181,11 @@ it('displays leverage', async () => { it('displays allocated margin', async () => { await act(async () => { - render(); + render( + + + + ); }); const cells = screen.getAllByRole('gridcell'); const cell = cells[7]; @@ -163,7 +194,11 @@ it('displays allocated margin', async () => { it('displays realised and unrealised PNL', async () => { await act(async () => { - render(); + render( + + + + ); }); const cells = screen.getAllByRole('gridcell'); expect(cells[9].textContent).toEqual('4.56'); @@ -172,13 +207,15 @@ it('displays realised and unrealised PNL', async () => { it('displays close button', async () => { await act(async () => { render( - { - return; - }} - isReadOnly={false} - /> + + { + return; + }} + isReadOnly={false} + /> + ); }); 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 () => { await act(async () => { render( - { - return; - }} - isReadOnly={false} - /> + + { + return; + }} + isReadOnly={false} + /> + ); }); const cells = screen.getAllByRole('gridcell'); diff --git a/libs/positions/src/lib/positions-table.tsx b/libs/positions/src/lib/positions-table.tsx index 8732e42d9..9ac49f2fd 100644 --- a/libs/positions/src/lib/positions-table.tsx +++ b/libs/positions/src/lib/positions-table.tsx @@ -14,12 +14,12 @@ import { PriceFlashCell, signedNumberCssClass, signedNumberCssClassRules, + MarketNameCell, } from '@vegaprotocol/datagrid'; import { ButtonLink, Tooltip, TooltipCellComponent, - Link, ExternalLink, Icon, ProgressBarCell, @@ -43,7 +43,7 @@ import { useEnvironment } from '@vegaprotocol/environment'; interface Props extends TypedDataAgGrid { onClose?: (data: Position) => void; - onMarketClick?: (id: string) => void; + onMarketClick?: (id: string, metaKey?: boolean) => void; style?: CSSProperties; isReadOnly: boolean; } @@ -96,26 +96,19 @@ export const PositionsTable = forwardRef( filterParams: { buttons: ['reset'] }, tooltipComponent: TooltipCellComponent, }} - components={{ AmountCell, PriceFlashCell, ProgressBarCell }} + components={{ + AmountCell, + PriceFlashCell, + ProgressBarCell, + MarketNameCell, + }} {...props} > ) => - onMarketClick ? ( - data?.marketId && onMarketClick(data?.marketId)} - > - {value} - - ) : ( - value - ) - } + cellRenderer="MarketNameCell" + cellRendererParams={{ idPath: 'marketId', onMarketClick }} minWidth={190} />