vega-frontend-monorepo/apps/trading/components/select-market/select-market-columns.tsx
Matthew Russell c576037b58
chore(#1873): trading hash router (#1921)
* chore: make liquidity page client side only

* chore: switch to hash based router

* chore: add index files for each page

* chore: tidy up _app

* chore: convert to use useRoutes

* fix: active state with react-router NavLink

* feat: add routes enum

* chore: restrict link and router imports from next

* chore: update testing navigation to use hash routes

* fix: typoe in eslint rule message

* chore: remove unnecessary getInitialProps function definition

* chore: wrap tests with memory router

* chore: delete unused index.page file

* chore: update suspense fallback state

* chore: add comment for link component span usage, update link to use toolkit styles

* chore: fix lint issues

* chore: delete client deposit page

* chore: revert title in _app so title gets set correctly without rerender

* revert: removal of deposit page so deposit e2e tests still pass

* chore: move client router to index page so valid status codes are still sent

* fix: wrong route path for markets page, cypress tests
2022-11-08 08:23:38 +01:00

565 lines
14 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
import { TradingModeTooltip } from '@vegaprotocol/deal-ticket';
import { FeesCell } from '@vegaprotocol/market-info';
import {
calcCandleHigh,
calcCandleLow,
calcCandleVolume,
} from '@vegaprotocol/market-list';
import {
addDecimalsNormalizeNumber,
PriceCell,
signedNumberCssClass,
t,
} from '@vegaprotocol/react-helpers';
import {
AuctionTrigger,
AuctionTriggerMapping,
MarketTradingMode,
MarketTradingModeMapping,
} from '@vegaprotocol/types';
import {
Link as UILink,
PriceCellChange,
Sparkline,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import isNil from 'lodash/isNil';
import type { CandleClose } from '@vegaprotocol/types';
import type {
MarketWithData,
MarketWithCandles,
} from '@vegaprotocol/market-list';
import { Link } from 'react-router-dom';
type Market = MarketWithData & MarketWithCandles;
export const cellClassNames = 'py-1 first:text-left text-right';
const TradingMode = ({ market }: { market: Market }) => {
return (
<Tooltip
description={
market && (
<TradingModeTooltip
tradingMode={market.tradingMode}
trigger={market.data?.trigger || null}
/>
)
}
>
<span>
{market.tradingMode ===
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
market.data?.trigger &&
market.data.trigger !== AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
? `${MarketTradingModeMapping[market.tradingMode]}
- ${AuctionTriggerMapping[market.data.trigger]}`
: MarketTradingModeMapping[market.tradingMode]}
</span>
</Tooltip>
);
};
const FeesInfo = () => {
return (
<Tooltip
description={
<span>
{t(
'Fees are paid by market takers on aggressive orders only. The fee displayed is made up of:'
)}
<ul className="list-disc ml-4">
<li>{t('An infrastructure fee')}</li>
<li>{t('A liquidity provision fee')}</li>
<li>{t('A maker fee')}</li>
</ul>
</span>
}
>
<span>{t('Taker fee')}</span>
</Tooltip>
);
};
export enum ColumnKind {
Market,
LastPrice,
Change24,
Asset,
ProductType,
Sparkline,
High24,
Low24,
TradingMode,
Volume,
Fee,
Position,
FullName,
}
export interface Column {
kind: ColumnKind;
value: string | React.ReactNode;
className: string;
onlyOnDetailed: boolean;
dataTestId?: string;
}
const headers: Column[] = [
{
kind: ColumnKind.Market,
value: t('Market'),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.ProductType,
value: t('Type'),
className: `py-2 text-left hidden sm:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.LastPrice,
value: t('Last price'),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Change24,
value: t('Change (24h)'),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Sparkline,
value: t(''),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Asset,
value: t('Settlement asset'),
className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.High24,
value: t('24h High'),
className: `${cellClassNames} hidden xl:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Low24,
value: t('24h Low'),
className: `${cellClassNames} hidden xl:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Volume,
value: t('24h Volume'),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.TradingMode,
value: t('Trading mode'),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Fee,
value: <FeesInfo />,
className: `${cellClassNames} hidden xl:table-cell`,
onlyOnDetailed: true,
},
];
export const columnHeadersPositionMarkets: Column[] = [
...headers,
{
kind: ColumnKind.Position,
value: t('Position'),
className: `${cellClassNames} hidden xxl:table-cell`,
onlyOnDetailed: true,
},
];
export const columnHeaders: Column[] = [
...headers,
{
kind: ColumnKind.FullName,
value: t('Full name'),
className: `${cellClassNames} hidden xxl:block`,
onlyOnDetailed: true,
},
];
export type OnCellClickHandler = (
e: React.MouseEvent,
kind: ColumnKind,
value: string
) => void;
export const columns = (
market: Market,
onSelect: (id: string) => void,
onCellClick: OnCellClickHandler
) => {
const candlesClose = market.candles
?.map((candle) => candle?.close)
.filter((c: string | undefined): c is CandleClose => !isNil(c));
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<HTMLAnchorElement>,
id: string
) => {
if (event.key === 'Enter' && onSelect) {
return onSelect(id);
}
};
const selectMarketColumns: Column[] = [
{
kind: ColumnKind.Market,
value: (
<Link
to={`/markets/${market.id}`}
data-testid={`market-link-${market.id}`}
onKeyPress={(event) => handleKeyPress(event, market.id)}
onClick={(e) => {
e.preventDefault();
onSelect(market.id);
}}
>
<UILink>{market.tradableInstrument.instrument.code}</UILink>
</Link>
),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.ProductType,
value: market.tradableInstrument.instrument.product.__typename,
className: `py-2 text-left hidden sm:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.LastPrice,
value: market.data?.markPrice ? (
<PriceCell
value={Number(market.data?.markPrice)}
valueFormatted={addDecimalsNormalizeNumber(
market.data?.markPrice.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Change24,
value: candlesClose && (
<PriceCellChange
candles={candlesClose}
decimalPlaces={market.decimalPlaces}
/>
),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Sparkline,
value: market.candles && (
<Sparkline
width={100}
height={20}
muted={false}
data={candlesClose?.map((c: string) => Number(c)) || []}
/>
),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false && candlesClose,
},
{
kind: ColumnKind.Asset,
value: (
<button
data-dialog-trigger
className="inline underline"
onClick={(e) => {
e.stopPropagation();
onCellClick(
e,
ColumnKind.Asset,
market.tradableInstrument.instrument.product.settlementAsset
.symbol
);
}}
>
{market.tradableInstrument.instrument.product.settlementAsset.symbol}
</button>
),
dataTestId: 'settlement-asset',
className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.High24,
value: candleHigh ? (
<PriceCell
value={Number(candleHigh)}
valueFormatted={addDecimalsNormalizeNumber(
candleHigh.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Low24,
value: candleLow ? (
<PriceCell
value={Number(candleLow)}
valueFormatted={addDecimalsNormalizeNumber(
candleLow.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Volume,
value: candleVolume
? addDecimalsNormalizeNumber(
candleVolume.toString(),
market.positionDecimalPlaces,
2
)
: '-',
className: `${cellClassNames} hidden lg:table-cell font-mono`,
onlyOnDetailed: true,
dataTestId: 'market-volume',
},
{
kind: ColumnKind.TradingMode,
value: <TradingMode market={market} />,
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: true,
dataTestId: 'trading-mode-col',
},
{
kind: ColumnKind.Fee,
value: <FeesCell feeFactors={market.fees.factors} />,
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
dataTestId: 'taker-fee',
},
{
kind: ColumnKind.FullName,
value: market.tradableInstrument.instrument.name,
className: `${cellClassNames} hidden xxl:block`,
onlyOnDetailed: true,
dataTestId: 'market-name',
},
];
return selectMarketColumns;
};
export const columnsPositionMarkets = (
market: Market,
onSelect: (id: string) => void,
openVolume?: string,
onCellClick?: OnCellClickHandler
) => {
const candlesClose = market.candles
?.map((candle) => candle?.close)
.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={`/markets/${market.id}`}
data-testid={`market-link-${market.id}`}
onKeyPress={(event) => handleKeyPress(event, market.id)}
onClick={(e) => {
e.preventDefault();
onSelect(market.id);
}}
>
<UILink>{market.tradableInstrument.instrument.code}</UILink>
</Link>
),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.ProductType,
value: market.tradableInstrument.instrument.product.__typename,
className: `py-2 first:text-left hidden sm:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.LastPrice,
value: market.data?.markPrice ? (
<PriceCell
value={Number(market.data.markPrice)}
valueFormatted={addDecimalsNormalizeNumber(
market.data.markPrice.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Change24,
value: candlesClose && (
<PriceCellChange
candles={candlesClose}
decimalPlaces={market.decimalPlaces}
/>
),
className: cellClassNames,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Sparkline,
value: candlesClose && (
<Sparkline
width={100}
height={20}
muted={false}
data={candlesClose.map((c: string) => Number(c))}
/>
),
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.Asset,
value: (
<button
data-dialog-trigger
className="inline underline"
onClick={(e) => {
e.stopPropagation();
if (!onCellClick) return;
onCellClick(
e,
ColumnKind.Asset,
market.tradableInstrument.instrument.product.settlementAsset
.symbol
);
}}
>
{market.tradableInstrument.instrument.product.settlementAsset.symbol}
</button>
),
className: `${cellClassNames} hidden sm:table-cell`,
onlyOnDetailed: false,
},
{
kind: ColumnKind.High24,
value: candleHigh ? (
<PriceCell
value={Number(candleHigh)}
valueFormatted={addDecimalsNormalizeNumber(
candleHigh.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Low24,
value: candleLow ? (
<PriceCell
value={Number(candleLow)}
valueFormatted={addDecimalsNormalizeNumber(
candleLow.toString(),
market.decimalPlaces,
2
)}
/>
) : (
'-'
),
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Volume,
value: candleVolume
? addDecimalsNormalizeNumber(
candleVolume.toString(),
market.positionDecimalPlaces,
2
)
: '-',
className: `${cellClassNames} hidden lg:table-cell font-mono`,
onlyOnDetailed: true,
dataTestId: 'market-volume',
},
{
kind: ColumnKind.TradingMode,
value: <TradingMode market={market} />,
className: `${cellClassNames} hidden lg:table-cell`,
onlyOnDetailed: true,
dataTestId: 'trading-mode-col',
},
{
kind: ColumnKind.Fee,
value: <FeesCell feeFactors={market.fees.factors} />,
className: `${cellClassNames} hidden xl:table-cell font-mono`,
onlyOnDetailed: true,
},
{
kind: ColumnKind.Position,
value: (
<p className={signedNumberCssClass(openVolume || '')}>{openVolume}</p>
),
className: `${cellClassNames} hidden xxl:table-cell font-mono`,
onlyOnDetailed: true,
},
];
return selectMarketColumns;
};