chore: add key hold handlers for conditionally external link - add market name ag-grid cell renderer

This commit is contained in:
maciek
2023-03-24 16:40:36 +01:00
parent 427baeb7aa
commit fcd70f68a7
5 changed files with 108 additions and 41 deletions
@@ -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