Compare commits

...

14 Commits

Author SHA1 Message Date
Ben
7cb08df480
Merge branch 'develop' into fix/add-missing-funding-panel 2024-02-15 07:03:38 +00:00
Matthew Russell
25a8749212
chore: revert to preview 10 2024-02-13 21:49:53 -08:00
Matthew Russell
f0df1b2d81 chore: try preview 11 2024-02-13 18:35:45 -08:00
Matthew Russell
63c25eb458 fix: update market change header stat now it renders correctly 2024-02-13 16:54:31 -08:00
Matthew Russell
e049477e37
fix: use candles test 2024-02-13 13:44:30 -08:00
Matthew Russell
25f8290b6d
chore: format markets.json 2024-02-13 13:37:34 -08:00
Matthew Russell
0a25fdbdb8
chore: fix lint 2024-02-13 13:37:12 -08:00
Matthew Russell
1d9ee2382c fix: 24 hour volume change 2024-02-13 13:14:46 -08:00
Matthew Russell
1c3871865f
chore: make fallback optional 2024-02-13 12:58:23 -08:00
Matthew Russell
988489b4b0
fix: add fallback data if market is young 2024-02-13 12:57:33 -08:00
Matthew Russell
39b5a6c4ae fix: fallback state for 24hr price change 2024-02-13 12:48:26 -08:00
Matthew Russell
224ecf30f5 fix: incorrect price change values for less than 24h old markets 2024-02-13 12:37:39 -08:00
Matthew Russell
feddc6d4e1 fix: funding panel wrapping and mobile overflow 2024-02-13 11:29:13 -08:00
Matthew Russell
844870913b
fix: add missing funding panel 2024-02-12 09:24:25 -08:00
11 changed files with 181 additions and 79 deletions

View File

@ -56,7 +56,6 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
<Last24hPriceChange
marketId={market.id}
decimalPlaces={market.decimalPlaces}
fallback={<span>-</span>}
/>
</HeaderStat>
<HeaderStat heading={t('Volume (24h)')} testId="market-volume">

View File

@ -72,7 +72,14 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
<div className="h-full flex flex-col lg:grid grid-rows-[min-content_min-content_1fr_min-content]">
<div className="flex flex-col w-full overflow-hidden">
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
{['chart', 'orderbook', 'trades', 'liquidity', 'fundingPayments']
{[
'chart',
'orderbook',
'trades',
'liquidity',
'fundingPayments',
'funding',
]
// filter to control available views for the current market
// e.g. only perpetuals should get the funding views
.filter((_key) => {
@ -163,9 +170,12 @@ const ViewButton = ({
onClick: () => void;
}) => {
const label = useViewLabel(view);
const className = classNames('py-2 px-4 min-w-[100px] capitalize text-sm', {
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive,
});
const className = classNames(
'py-2 px-4 capitalize text-sm whitespace-nowrap',
{
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive,
}
);
return (
<button data-testid={view} onClick={onClick} className={className}>
@ -181,8 +191,8 @@ const useViewLabel = (view: TradingView) => {
chart: t('Chart'),
depth: t('Depth'),
liquidity: t('Liquidity'),
funding: t('Funding'),
fundingPayments: t('Funding'),
funding: t('Funding history'),
fundingPayments: t('Funding payments'),
orderbook: t('Orderbook'),
trades: t('Trades'),
positions: t('Positions'),

View File

@ -29,28 +29,35 @@ export const MobileMarketHeader = () => {
if (!marketId) return null;
return (
<div className="pl-3 pr-2 flex justify-between gap-2 h-10 bg-vega-clight-700 dark:bg-vega-cdark-700">
<div className="pl-3 pr-2 grid grid-cols-2 h-10 bg-vega-clight-700 dark:bg-vega-cdark-700">
<FullScreenPopover
open={openMarket}
onOpenChange={(x) => {
setOpenMarket(x);
}}
trigger={
<h1 className="flex gap-1 sm:gap-2 md:gap-4 items-center text-base leading-3 md:text-lg whitespace-nowrap">
{data
? data.tradableInstrument.instrument.code
: t('Select market')}
<span
<button
data-testid="popover-trigger"
className="min-w-0 flex gap-1 items-center"
>
<h1 className="whitespace-nowrap overflow-hidden text-ellipsis items-center">
<span className="">
{data
? data.tradableInstrument.instrument.code
: t('Select market')}
</span>
</h1>
<VegaIcon
name={VegaIconNames.CHEVRON_DOWN}
size={16}
className={classNames(
'transition-transform ease-in-out duration-300 flex',
'origin-center transition-transform ease-in-out duration-300 flex',
{
'rotate-180': openMarket,
}
)}
>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={16} />
</span>
</h1>
/>
</button>
}
>
<MarketSelector
@ -64,34 +71,40 @@ export const MobileMarketHeader = () => {
setOpenPrice(x);
}}
trigger={
<span className="flex gap-2 items-end md:text-md whitespace-nowrap leading-3">
<button
data-testid="popover-trigger"
className="min-w-0 flex gap-2 items-center justify-end"
>
{data && (
<>
<span className="text-xs">
<Last24hPriceChange
marketId={data.id}
decimalPlaces={data.decimalPlaces}
/>
</span>
<span className="flex items-center gap-1">
<MarketMarkPrice
marketId={data.id}
decimalPlaces={data.decimalPlaces}
/>
<VegaIcon
name={VegaIconNames.CHEVRON_DOWN}
size={16}
className={classNames(
'transition-transform ease-in-out duration-300',
{
'rotate-180': openPrice,
}
)}
/>
<span className="min-w-0 flex flex-col items-end gap-0">
<span className="text-sm">
<MarketMarkPrice
marketId={data.id}
decimalPlaces={data.decimalPlaces}
/>
</span>
<span className="text-xs">
<Last24hPriceChange
marketId={data.id}
decimalPlaces={data.decimalPlaces}
fallback={<span />} // dont render anything so price is vertically centered
/>
</span>
</span>
<VegaIcon
name={VegaIconNames.CHEVRON_DOWN}
size={16}
className={classNames(
'min-w-0 transition-transform ease-in-out duration-300',
{
'rotate-180': openPrice,
}
)}
/>
</>
)}
</span>
</button>
}
>
{data && (
@ -104,11 +117,11 @@ export const MobileMarketHeader = () => {
);
};
export interface PopoverProps extends PopoverPrimitive.PopoverProps {
interface PopoverProps extends PopoverPrimitive.PopoverProps {
trigger: React.ReactNode | string;
}
export const FullScreenPopover = ({
const FullScreenPopover = ({
trigger,
children,
open,
@ -116,7 +129,7 @@ export const FullScreenPopover = ({
}: PopoverProps) => {
return (
<PopoverPrimitive.Root open={open} onOpenChange={onOpenChange}>
<PopoverPrimitive.Trigger data-testid="popover-trigger">
<PopoverPrimitive.Trigger asChild={true}>
{trigger}
</PopoverPrimitive.Trigger>
<PopoverPrimitive.Portal>

View File

@ -35,9 +35,9 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
# 6002-MDET-003
expect(page.get_by_test_id("market-price")).to_have_text("Mark Price0.00")
# 6002-MDET-004
expect(page.get_by_test_id("market-change")).to_have_text("Change (24h)0.00%0.00")
expect(page.get_by_test_id("market-change")).to_have_text("Change (24h)-")
# 6002-MDET-005
expect(page.get_by_test_id("market-volume")).to_have_text("Volume (24h)- (- BTC)")
expect(page.get_by_test_id("market-volume")).to_have_text("Volume (24h)-")
# 6002-MDET-008
expect(page.get_by_test_id("market-settlement-asset")).to_have_text(
"Settlement assettDAI"

View File

@ -7,7 +7,7 @@ import {
type FirstDataRenderedEvent,
type SortChangedEvent,
type GridReadyEvent,
GridApi,
type GridApi,
} from 'ag-grid-community';
import { useCallback, useEffect, useRef } from 'react';

View File

@ -2,7 +2,7 @@
"{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price",
"{{probability}} probability price bounds": "{{probability}} probability price bounds",
"24 hour change is unavailable at this time. The price change in the last 120 hours is:": "24 hour change is unavailable at this time. The price change in the last 120 hours is:",
"24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}}": "24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}}",
"24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
"A concept derived from traditional markets. It is a calculated value for the current market price on a market.": "A concept derived from traditional markets. It is a calculated value for the current market price on a market.",
"A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.": "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.",
"A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.": "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.",
@ -49,6 +49,9 @@
"Market": "Market",
"Market data": "Market data",
"Market governance": "Market governance",
"Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:",
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:",
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
"Market ID": "Market ID",
"Market price": "Market price",
"Market specification": "Market specification",

View File

@ -1,7 +1,8 @@
import { type ReactNode } from 'react';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
isNumeric,
getDateTimeFormat,
priceChange,
priceChangePercentage,
} from '@vegaprotocol/utils';
@ -14,29 +15,57 @@ import { useT } from '../../use-t';
interface Props {
marketId?: string;
decimalPlaces?: number;
initialValue?: string[];
isHeader?: boolean;
noUpdate?: boolean;
// render prop for no price change
fallback?: React.ReactNode;
decimalPlaces: number;
fallback?: ReactNode;
}
export const Last24hPriceChange = ({
marketId,
decimalPlaces,
initialValue,
fallback,
}: Props) => {
const t = useT();
const { oneDayCandles, error, fiveDaysCandles } = useCandles({
const { oneDayCandles, fiveDaysCandles, error } = useCandles({
marketId,
});
if (
fiveDaysCandles &&
fiveDaysCandles.length > 0 &&
(!oneDayCandles || oneDayCandles?.length === 0)
) {
const nonIdeal = fallback || <span>{'-'}</span>;
if (error || !oneDayCandles || !fiveDaysCandles) {
return nonIdeal;
}
if (fiveDaysCandles.length < 24) {
return (
<Tooltip
description={
<span className="justify-start">
{t(
'Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:',
{
start: getDateTimeFormat().format(
new Date(fiveDaysCandles[0].periodStart)
),
end: getDateTimeFormat().format(
new Date(
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
)
),
}
)}
<PriceChangeCell
candles={fiveDaysCandles.map((c) => c.close) || []}
decimalPlaces={decimalPlaces}
/>
</span>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
if (oneDayCandles.length < 24) {
return (
<Tooltip
description={
@ -51,16 +80,12 @@ export const Last24hPriceChange = ({
</span>
}
>
<span>{fallback}</span>
<span>{nonIdeal}</span>
</Tooltip>
);
}
if (error || !isNumeric(decimalPlaces)) {
return <span>{fallback}</span>;
}
const candles = oneDayCandles?.map((c) => c.close) || initialValue || [];
const candles = oneDayCandles?.map((c) => c.close) || [];
const change = priceChange(candles);
const changePercentage = priceChangePercentage(candles);

View File

@ -2,6 +2,7 @@ import { calcCandleVolume, calcCandleVolumePrice } from '../../market-utils';
import {
addDecimalsFormatNumber,
formatNumber,
getDateTimeFormat,
isNumeric,
} from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
@ -26,15 +27,17 @@ export const Last24hVolume = ({
quoteUnit,
}: Props) => {
const t = useT();
const { oneDayCandles, fiveDaysCandles } = useCandles({
const { oneDayCandles, fiveDaysCandles, error } = useCandles({
marketId,
});
if (
fiveDaysCandles &&
fiveDaysCandles.length > 0 &&
(!oneDayCandles || oneDayCandles?.length === 0)
) {
const nonIdeal = <span>{'-'}</span>;
if (error || !oneDayCandles || !fiveDaysCandles) {
return nonIdeal;
}
if (fiveDaysCandles.length < 24) {
const candleVolume = calcCandleVolume(fiveDaysCandles);
const candleVolumePrice = calcCandleVolumePrice(
fiveDaysCandles,
@ -55,14 +58,59 @@ export const Last24hVolume = ({
<div>
<span className="flex flex-col">
{t(
'24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} ({{candleVolumePrice}} {{quoteUnit}})',
'Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}',
{
start: getDateTimeFormat().format(
new Date(fiveDaysCandles[0].periodStart)
),
end: getDateTimeFormat().format(
new Date(
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
)
),
candleVolumeValue,
candleVolumePrice,
quoteUnit,
}
)}
</span>
</div>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
if (oneDayCandles.length < 24) {
const candleVolume = calcCandleVolume(fiveDaysCandles);
const candleVolumePrice = calcCandleVolumePrice(
fiveDaysCandles,
marketDecimals,
positionDecimalPlaces
);
const candleVolumeValue =
candleVolume && isNumeric(positionDecimalPlaces)
? addDecimalsFormatNumber(
candleVolume,
positionDecimalPlaces,
formatDecimals
)
: '-';
return (
<Tooltip
description={
<div>
<span className="flex flex-col">
{t(
'24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of ({{candleVolumePrice}} {{quoteUnit}})',
{ candleVolumeValue, candleVolumePrice, quoteUnit }
)}
</span>
</div>
}
>
<span>-</span>
<span>{nonIdeal}</span>
</Tooltip>
);
}

View File

@ -15,7 +15,6 @@ const mockData = [
periodStart: today.toISOString(),
__typename: 'Candle',
},
null,
{
high: '6309988',
low: '6296335',

View File

@ -18,7 +18,12 @@ export const useCandles = ({ marketId }: { marketId?: string }) => {
skip: !marketId,
});
const fiveDaysCandles = data?.filter(Boolean);
const fiveDaysCandles = data?.filter((c) => {
if (c.open === '' || c.close === '' || c.high === '' || c.close === '') {
return false;
}
return true;
});
const oneDayCandles = fiveDaysCandles?.filter((candle) =>
isCandleLessThan24hOld(candle, yesterday)

View File

@ -1,7 +1,7 @@
import { useState, useCallback } from 'react';
import {
OpenVolumeData,
openVolumeDataProvider,
type OpenVolumeData,
} from './positions-data-providers';
import { useDataProvider } from '@vegaprotocol/data-provider';