chore: add key hold handlers for conditionally external link - add market name ag-grid cell renderer
This commit is contained in:
@@ -2,6 +2,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 { MarketNameCell } from '../../components/market-name-cell';
|
||||
|
||||
export const Markets = () => {
|
||||
const navigate = useNavigate();
|
||||
@@ -12,5 +13,10 @@ export const Markets = () => {
|
||||
[navigate]
|
||||
);
|
||||
|
||||
return <MarketsContainer onSelect={handleOnSelect} />;
|
||||
return (
|
||||
<MarketsContainer
|
||||
onSelect={handleOnSelect}
|
||||
MarketNameCell={MarketNameCell}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { useGlobalStore } from '../stores';
|
||||
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
import type { MarketMaybeWithData } from '@vegaprotocol/market-list';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { ExternalLink, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { Links, Routes } from '../pages/client-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export const MarketNameCell = ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<
|
||||
MarketMaybeWithData,
|
||||
'tradableInstrument.instrument.code'
|
||||
>) => {
|
||||
const holdingKey = useGlobalStore((store) => store.holdingKey);
|
||||
const ref = useRef<HTMLDivElement | null>(null);
|
||||
const [on, setOn] = useState(false);
|
||||
const [externalKeyDown, setExternalKeyDown] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setExternalKeyDown(['Meta', 'Control'].includes(holdingKey) && on);
|
||||
if (on) {
|
||||
ref.current?.focus();
|
||||
} else {
|
||||
ref.current?.blur();
|
||||
}
|
||||
}, [on, holdingKey]);
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const linkContent = externalKeyDown ? (
|
||||
<ExternalLink
|
||||
href={`#${Links[Routes.MARKET](data.id)}`}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{value}
|
||||
</ExternalLink>
|
||||
) : (
|
||||
<Link to={Links[Routes.MARKET](data.id)}>{value}</Link>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
data-testid={`market-${data.id}`}
|
||||
ref={ref}
|
||||
role="link"
|
||||
tabIndex={0}
|
||||
onMouseEnter={() => setOn(true)}
|
||||
onMouseLeave={() => setOn(false)}
|
||||
>
|
||||
{linkContent}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ReactNode, RefObject, MouseEvent, KeyboardEvent } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { FeesCell } from '@vegaprotocol/market-info';
|
||||
import {
|
||||
calcCandleHigh,
|
||||
@@ -189,7 +189,6 @@ const MarketExternalLink = ({
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(e: MouseEvent<HTMLAnchorElement>) => {
|
||||
console.log('test');
|
||||
e.preventDefault();
|
||||
onSelect(marketId);
|
||||
},
|
||||
@@ -430,30 +429,14 @@ 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<HTMLSpanElement>,
|
||||
id: string
|
||||
) => {
|
||||
if (event.key === 'Enter' && onSelect) {
|
||||
return onSelect(id);
|
||||
}
|
||||
};
|
||||
const candleVolume = market.candles && calcCandleVolume(market.candles);
|
||||
const selectMarketColumns: Column[] = [
|
||||
{
|
||||
kind: ColumnKind.Market,
|
||||
value: (
|
||||
<Link
|
||||
to={Links[Routes.MARKET](market.id)}
|
||||
data-testid={`market-link-${market.id}`}
|
||||
onKeyPress={(event) => handleKeyPress(event, market.id)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
onSelect(market.id);
|
||||
}}
|
||||
>
|
||||
<MarketExternalLink marketId={market.id} onSelect={onSelect}>
|
||||
<UILink>{market.tradableInstrument.instrument.code}</UILink>
|
||||
</Link>
|
||||
</MarketExternalLink>
|
||||
),
|
||||
className: cellClassNames,
|
||||
onlyOnDetailed: false,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -17,15 +18,28 @@ import type { AgGridReact } from 'ag-grid-react';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { MarketMaybeWithData, MarketFieldsFragment } from '../../';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import type { MarketNameCellPropsRenderer } from './markets-container';
|
||||
|
||||
const { MarketTradingMode, AuctionTrigger } = Schema;
|
||||
|
||||
export const getRowId = ({ data }: { data: { id: string } }) => data.id;
|
||||
|
||||
const DefaultMarketNameCellRenderer = ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<
|
||||
MarketMaybeWithData,
|
||||
'tradableInstrument.instrument.code'
|
||||
>) => {
|
||||
if (!data) return null;
|
||||
return <span data-testid={`market-${data.id}`}>{value}</span>;
|
||||
};
|
||||
|
||||
export const MarketListTable = forwardRef<
|
||||
AgGridReact,
|
||||
TypedDataAgGrid<MarketMaybeWithData>
|
||||
>((props, ref) => {
|
||||
TypedDataAgGrid<MarketMaybeWithData> & MarketNameCellPropsRenderer
|
||||
>(({ MarketNameCell, ...props }, ref) => {
|
||||
MarketNameCell = MarketNameCell || DefaultMarketNameCellRenderer;
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
return (
|
||||
<AgGrid
|
||||
@@ -41,22 +55,13 @@ export const MarketListTable = forwardRef<
|
||||
filterParams: { buttons: ['reset'] },
|
||||
}}
|
||||
suppressCellFocus={true}
|
||||
components={{ PriceFlashCell }}
|
||||
components={{ PriceFlashCell, MarketNameCell }}
|
||||
{...props}
|
||||
>
|
||||
<AgGridColumn
|
||||
headerName={t('Market')}
|
||||
field="tradableInstrument.instrument.code"
|
||||
cellRenderer={({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<
|
||||
MarketMaybeWithData,
|
||||
'tradableInstrument.instrument.code'
|
||||
>) => {
|
||||
if (!data) return null;
|
||||
return <span data-testid={`market-${data.id}`}>{value}</span>;
|
||||
}}
|
||||
cellRenderer="MarketNameCell"
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Description')}
|
||||
|
||||
@@ -2,14 +2,29 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketListTable } from './market-list-table';
|
||||
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 type { MarketMaybeWithData } from '../../markets-provider';
|
||||
interface MarketsContainerProps {
|
||||
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
|
||||
export interface MarketNameCellPropsRenderer {
|
||||
MarketNameCell?: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<
|
||||
MarketMaybeWithData,
|
||||
'tradableInstrument.instrument.code'
|
||||
>) => JSX.Element | null;
|
||||
}
|
||||
|
||||
interface MarketsContainerProps extends MarketNameCellPropsRenderer {
|
||||
onSelect: (marketId: string) => void;
|
||||
}
|
||||
|
||||
export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
|
||||
export const MarketsContainer = ({
|
||||
onSelect,
|
||||
MarketNameCell,
|
||||
}: MarketsContainerProps) => {
|
||||
const { data, error, loading, reload } = useDataProvider({
|
||||
dataProvider,
|
||||
skipUpdates: true,
|
||||
@@ -21,16 +36,20 @@ 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 } = 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);
|
||||
}}
|
||||
MarketNameCell={MarketNameCell}
|
||||
/>
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
|
||||
Reference in New Issue
Block a user