chore(#2004): add market status to trade header (#2269)

* feat: add status header stat, move header to own component

* chore: fix alignment of price change values when arrow is not rendered

* test: add check for market state display

* feat: add market state component and adjust queries to handle sub updates to market state

* test: update mocks to include data.marketState fields

* chore: add missing market state to console-lite mock

* chore: test adjustment

* fix: botched conflict resolution

* chore: update select market columns test

* chore: add missing fields to test helper functions

Co-authored-by: Rado <szpiechrados@gmail.com>
This commit is contained in:
Matthew Russell 2022-12-01 18:33:30 -06:00 committed by GitHub
parent 9b356e53b4
commit 9ddedd0b78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 368 additions and 212 deletions

View File

@ -1413,6 +1413,7 @@ export const generateMarketData = (): MarketDataQuery => {
}, },
marketTradingMode: marketTradingMode:
Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0', staticMidPrice: '0',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '0', bestStaticBidPrice: '0',

View File

@ -81,6 +81,7 @@ const market: MarketDealTicket = {
trigger: Types.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED, trigger: Types.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED,
staticMidPrice: '1606156850', staticMidPrice: '1606156850',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
indicativeVolume: '0', indicativeVolume: '0',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '1605489971', bestStaticBidPrice: '1605489971',
@ -92,8 +93,9 @@ const market: MarketDealTicket = {
market: { __typename: 'Market', id: 'market-id' }, market: { __typename: 'Market', id: 'market-id' },
}, },
marketTimestamps: { marketTimestamps: {
open: null, __typename: 'MarketTimestamps',
close: null, close: null,
open: null,
}, },
}; };

View File

@ -1,4 +1,4 @@
import { Schema, MarketTradingModeMapping } from '@vegaprotocol/types'; import { MarketTradingModeMapping } from '@vegaprotocol/types';
const marketInfoBtn = 'Info'; const marketInfoBtn = 'Info';
const row = 'key-value-table-row'; const row = 'key-value-table-row';
@ -210,42 +210,3 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
}); });
} }
}); });
describe('market states', { tags: '@smoke' }, function () {
//7002-SORD-062
//7002-SORD-063
//7002-SORD-066
const states = [
Schema.MarketState.STATE_REJECTED,
Schema.MarketState.STATE_CANCELLED,
Schema.MarketState.STATE_CLOSED,
Schema.MarketState.STATE_SETTLED,
Schema.MarketState.STATE_TRADING_TERMINATED,
];
states.forEach((marketState) => {
describe(marketState, function () {
beforeEach(function () {
cy.mockTradingPage(marketState);
cy.mockGQLSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
});
it.skip('must display correct market state');
//7002-/SORD-/061 no state displayed
it('must display that market is not accepting orders', function () {
cy.getByTestId('place-order').click();
cy.getByTestId('dealticket-error-message-summary').should(
'have.text',
`This market is ${marketState
.split('_')
.pop()
?.toLowerCase()} and not accepting orders`
);
cy.getByTestId('place-order').should('be.disabled');
});
});
});
});

View File

@ -11,6 +11,7 @@ const marketPrice = 'market-price';
const marketChange = 'market-change'; const marketChange = 'market-change';
const marketVolume = 'market-volume'; const marketVolume = 'market-volume';
const marketMode = 'market-trading-mode'; const marketMode = 'market-trading-mode';
const marketState = 'market-state';
const marketSettlement = 'market-settlement-asset'; const marketSettlement = 'market-settlement-asset';
const percentageValue = 'price-change-percentage'; const percentageValue = 'price-change-percentage';
const priceChangeValue = 'price-change'; const priceChangeValue = 'price-change';
@ -36,6 +37,7 @@ describe('Market proposal notification', { tags: '@smoke' }, () => {
cy.wait('@MarketData'); cy.wait('@MarketData');
cy.getByTestId(marketSummaryBlock).should('be.visible'); cy.getByTestId(marketSummaryBlock).should('be.visible');
}); });
it('should display market proposal notification if proposal found', () => { it('should display market proposal notification if proposal found', () => {
cy.getByTestId(marketSummaryBlock).within(() => { cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId('market-proposal-notification').should( cy.getByTestId('market-proposal-notification').should(
@ -119,6 +121,16 @@ describe('Market trading page', () => {
}); });
}); });
it('must see market state', () => {
//7002-SORD-061
cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketState).within(() => {
cy.getByTestId(itemHeader).should('have.text', 'Status');
cy.getByTestId(itemValue).should('not.be.empty');
});
});
});
it('must see market settlement', () => { it('must see market settlement', () => {
cy.getByTestId(marketSummaryBlock).within(() => { cy.getByTestId(marketSummaryBlock).within(() => {
cy.getByTestId(marketSettlement).within(() => { cy.getByTestId(marketSettlement).within(() => {
@ -203,3 +215,40 @@ describe('Market trading page', () => {
}); });
}); });
}); });
describe('market states not accepting orders', { tags: '@smoke' }, function () {
//7002-SORD-062
//7002-SORD-063
//7002-SORD-066
const states = [
Schema.MarketState.STATE_REJECTED,
Schema.MarketState.STATE_CANCELLED,
Schema.MarketState.STATE_CLOSED,
Schema.MarketState.STATE_SETTLED,
Schema.MarketState.STATE_TRADING_TERMINATED,
];
states.forEach((marketState) => {
describe(marketState, function () {
beforeEach(function () {
cy.mockTradingPage(marketState);
cy.mockGQLSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
cy.connectVegaWallet();
});
it('must display that market is not accepting orders', function () {
cy.getByTestId('place-order').click();
cy.getByTestId('dealticket-error-message-summary').should(
'have.text',
`This market is ${marketState
.split('_')
.pop()
?.toLowerCase()} and not accepting orders`
);
cy.getByTestId('place-order').should('be.disabled');
});
});
});
});

View File

@ -91,6 +91,7 @@ export const generateMarketData = (
targetStake: '1000000', targetStake: '1000000',
suppliedStake: '1000', suppliedStake: '1000',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0', staticMidPrice: '0',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '0', bestStaticBidPrice: '0',

View File

@ -218,6 +218,7 @@ export const generateMarketsData = (
__typename: 'Market', __typename: 'Market',
}, },
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0', staticMidPrice: '0',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '0', bestStaticBidPrice: '0',
@ -235,6 +236,7 @@ export const generateMarketsData = (
__typename: 'Market', __typename: 'Market',
}, },
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0', staticMidPrice: '0',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '0', bestStaticBidPrice: '0',
@ -252,6 +254,7 @@ export const generateMarketsData = (
__typename: 'Market', __typename: 'Market',
}, },
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0', staticMidPrice: '0',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '0', bestStaticBidPrice: '0',
@ -269,6 +272,7 @@ export const generateMarketsData = (
__typename: 'Market', __typename: 'Market',
}, },
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
staticMidPrice: '0', staticMidPrice: '0',
indicativePrice: '0', indicativePrice: '0',
bestStaticBidPrice: '0', bestStaticBidPrice: '0',

View File

@ -0,0 +1,3 @@
import { t } from '@vegaprotocol/react-helpers';
export const NO_MARKET = t('No market');

View File

@ -1,5 +1,5 @@
import { DealTicketContainer } from '@vegaprotocol/deal-ticket'; import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
import { MarketInfoContainer, getExpiryDate } from '@vegaprotocol/market-info'; import { MarketInfoContainer } from '@vegaprotocol/market-info';
import { OrderbookContainer } from '@vegaprotocol/market-depth'; import { OrderbookContainer } from '@vegaprotocol/market-depth';
import { OrderListContainer } from '@vegaprotocol/orders'; import { OrderListContainer } from '@vegaprotocol/orders';
import { FillsContainer } from '@vegaprotocol/fills'; import { FillsContainer } from '@vegaprotocol/fills';
@ -17,29 +17,14 @@ import {
Tabs, Tabs,
ResizableGrid, ResizableGrid,
ResizableGridPanel, ResizableGridPanel,
ButtonLink,
Link,
Splash, Splash,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers'; import { t } from '@vegaprotocol/react-helpers';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useEnvironment } from '@vegaprotocol/environment';
import { Header, HeaderStat } from '../../components/header';
import { AccountsContainer } from '../../components/accounts-container'; import { AccountsContainer } from '../../components/accounts-container';
import {
ColumnKind,
SelectMarketPopover,
} from '../../components/select-market';
import type { OnCellClickHandler } from '../../components/select-market';
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list'; import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
import { Last24hPriceChange } from '../../components/last-24h-price-change';
import { MarketMarkPrice } from '../../components/market-mark-price';
import { MarketTradingModeComponent } from '../../components/market-trading-mode';
import { Last24hVolume } from '../../components/last-24h-volume';
import { MarketProposalNotification } from '@vegaprotocol/governance';
import { VegaWalletContainer } from '../../components/vega-wallet-container'; import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { TradeMarketHeader } from './trade-market-header';
const NO_MARKET = t('No market'); import { NO_MARKET } from './constants';
type MarketDependantView = type MarketDependantView =
| typeof CandlesChartContainer | typeof CandlesChartContainer
@ -73,133 +58,6 @@ const TradingViews = {
type TradingView = keyof typeof TradingViews; type TradingView = keyof typeof TradingViews;
type ExpiryLabelProps = {
market: SingleMarketFieldsFragment | null;
};
const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
const content = market ? getExpiryDate(market) : '-';
return <div data-testid="trading-expiry">{content}</div>;
};
type ExpiryTooltipContentProps = {
market: SingleMarketFieldsFragment;
explorerUrl?: string;
};
const ExpiryTooltipContent = ({
market,
explorerUrl,
}: ExpiryTooltipContentProps) => {
if (market?.marketTimestamps.close === null) {
const oracleId =
market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination?.id;
return (
<section data-testid="expiry-tool-tip">
<p className="mb-2">
{t(
'This market expires when triggered by its oracle, not on a set date.'
)}
</p>
{explorerUrl && oracleId && (
<Link href={`${explorerUrl}/oracles#${oracleId}`} target="_blank">
{t('View oracle specification')}
</Link>
)}
</section>
);
}
return null;
};
interface TradeMarketHeaderProps {
market: SingleMarketFieldsFragment | null;
onSelect: (marketId: string) => void;
}
export const TradeMarketHeader = ({
market,
onSelect,
}: TradeMarketHeaderProps) => {
const { VEGA_EXPLORER_URL } = useEnvironment();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const asset = market?.tradableInstrument.instrument.product?.settlementAsset;
const onCellClick: OnCellClickHandler = (e, kind, value) => {
if (value && kind === ColumnKind.Asset) {
openAssetDetailsDialog(value, e.target as HTMLElement);
}
};
return (
<Header
title={
<SelectMarketPopover
marketName={market?.tradableInstrument.instrument.name || NO_MARKET}
onSelect={onSelect}
onCellClick={onCellClick}
/>
}
>
<HeaderStat
heading={t('Expiry')}
description={
market && (
<ExpiryTooltipContent
market={market}
explorerUrl={VEGA_EXPLORER_URL}
/>
)
}
testId="market-expiry"
>
<ExpiryLabel market={market} />
</HeaderStat>
<MarketMarkPrice
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
isHeader
/>
<Last24hPriceChange
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
isHeader
/>
<Last24hVolume
marketId={market?.id}
positionDecimalPlaces={market?.positionDecimalPlaces}
isHeader
/>
<MarketTradingModeComponent
marketId={market?.id}
onSelect={onSelect}
isHeader
/>
{asset ? (
<HeaderStat
heading={t('Settlement asset')}
testId="market-settlement-asset"
>
<div>
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(asset.id, e.target as HTMLElement);
}}
>
{asset.symbol}
</ButtonLink>
</div>
</HeaderStat>
) : null}
<MarketProposalNotification marketId={market?.id} />
</Header>
);
};
interface TradeGridProps { interface TradeGridProps {
market: SingleMarketFieldsFragment | null; market: SingleMarketFieldsFragment | null;
onSelect: (marketId: string) => void; onSelect: (marketId: string) => void;

View File

@ -0,0 +1,143 @@
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useEnvironment } from '@vegaprotocol/environment';
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
import { MarketProposalNotification } from '@vegaprotocol/governance';
import { getExpiryDate } from '@vegaprotocol/market-info';
import { t } from '@vegaprotocol/react-helpers';
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
import {
ColumnKind,
SelectMarketPopover,
} from '../../components/select-market';
import type { OnCellClickHandler } from '../../components/select-market';
import { Header, HeaderStat } from '../../components/header';
import { NO_MARKET } from './constants';
import { MarketMarkPrice } from '../../components/market-mark-price';
import { Last24hPriceChange } from '../../components/last-24h-price-change';
import { Last24hVolume } from '../../components/last-24h-volume';
import { MarketState } from '../../components/market-state';
import { MarketTradingMode } from '../../components/market-trading-mode';
interface TradeMarketHeaderProps {
market: SingleMarketFieldsFragment | null;
onSelect: (marketId: string) => void;
}
export const TradeMarketHeader = ({
market,
onSelect,
}: TradeMarketHeaderProps) => {
const { VEGA_EXPLORER_URL } = useEnvironment();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const asset = market?.tradableInstrument.instrument.product?.settlementAsset;
const onCellClick: OnCellClickHandler = (e, kind, value) => {
if (value && kind === ColumnKind.Asset) {
openAssetDetailsDialog(value, e.target as HTMLElement);
}
};
return (
<Header
title={
<SelectMarketPopover
marketName={market?.tradableInstrument.instrument.name || NO_MARKET}
onSelect={onSelect}
onCellClick={onCellClick}
/>
}
>
<HeaderStat
heading={t('Expiry')}
description={
market && (
<ExpiryTooltipContent
market={market}
explorerUrl={VEGA_EXPLORER_URL}
/>
)
}
testId="market-expiry"
>
<ExpiryLabel market={market} />
</HeaderStat>
<MarketMarkPrice
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
isHeader
/>
<Last24hPriceChange
marketId={market?.id}
decimalPlaces={market?.decimalPlaces}
isHeader
/>
<Last24hVolume
marketId={market?.id}
positionDecimalPlaces={market?.positionDecimalPlaces}
isHeader
/>
<MarketTradingMode marketId={market?.id} onSelect={onSelect} isHeader />
<MarketState market={market} />
{asset ? (
<HeaderStat
heading={t('Settlement asset')}
testId="market-settlement-asset"
>
<div>
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(asset.id, e.target as HTMLElement);
}}
>
{asset.symbol}
</ButtonLink>
</div>
</HeaderStat>
) : null}
<MarketProposalNotification marketId={market?.id} />
</Header>
);
};
type ExpiryLabelProps = {
market: SingleMarketFieldsFragment | null;
};
const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
const content = market ? getExpiryDate(market) : '-';
return <div data-testid="trading-expiry">{content}</div>;
};
type ExpiryTooltipContentProps = {
market: SingleMarketFieldsFragment;
explorerUrl?: string;
};
const ExpiryTooltipContent = ({
market,
explorerUrl,
}: ExpiryTooltipContentProps) => {
if (market?.marketTimestamps.close === null) {
const oracleId =
market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination?.id;
return (
<section data-testid="expiry-tool-tip">
<p className="mb-2">
{t(
'This market expires when triggered by its oracle, not on a set date.'
)}
</p>
{explorerUrl && oracleId && (
<Link href={`${explorerUrl}/oracles#${oracleId}`} target="_blank">
{t('View oracle specification')}
</Link>
)}
</section>
);
}
return null;
};

View File

@ -0,0 +1 @@
export * from './market-state';

View File

@ -0,0 +1,105 @@
import throttle from 'lodash/throttle';
import type {
MarketData,
MarketDataUpdateFieldsFragment,
SingleMarketFieldsFragment,
} from '@vegaprotocol/market-list';
import { marketDataProvider } from '@vegaprotocol/market-list';
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
import { MarketStateMapping, Schema } from '@vegaprotocol/types';
import { HeaderStat } from '../header';
import { useCallback, useMemo, useRef, useState } from 'react';
import * as constants from '../constants';
export const MarketState = ({
market,
}: {
market: SingleMarketFieldsFragment | null;
}) => {
const [marketState, setMarketState] = useState<Schema.MarketState | null>(
null
);
const throttledSetMarketState = useRef(
throttle((state: Schema.MarketState) => {
setMarketState(state);
}, constants.DEBOUNCE_UPDATE_TIME)
).current;
const update = useCallback(
({ data: marketData }: { data: MarketData | null }) => {
if (marketData) {
throttledSetMarketState(marketData.marketState);
}
return true;
},
[throttledSetMarketState]
);
const variables = useMemo(
() => ({ marketId: market?.id || '' }),
[market?.id]
);
useDataProvider<MarketData, MarketDataUpdateFieldsFragment>({
dataProvider: marketDataProvider,
update,
variables,
skip: !market?.id,
});
return (
<HeaderStat
heading={t('Status')}
description={getMarketStateTooltip(marketState)}
testId="market-state"
>
{marketState ? MarketStateMapping[marketState] : '-'}
</HeaderStat>
);
};
const getMarketStateTooltip = (state: Schema.MarketState | null) => {
if (state === Schema.MarketState.STATE_ACTIVE) {
return t('Enactment date reached and usual auction exit checks pass');
}
if (state === Schema.MarketState.STATE_CANCELLED) {
return t(
'Market triggers cancellation or governance vote has passed to cancel'
);
}
if (state === Schema.MarketState.STATE_CLOSED) {
return t('Governance vote passed to close the market');
}
if (state === Schema.MarketState.STATE_PENDING) {
return t(
'Governance vote has passed and market is awaiting opening auction exit'
);
}
if (state === Schema.MarketState.STATE_PROPOSED) {
return t('Governance vote for this market is valid and has been accepted');
}
if (state === Schema.MarketState.STATE_REJECTED) {
return t('Governance vote for this market has been rejected');
}
if (state === Schema.MarketState.STATE_SETTLED) {
return t('Settlement defined by product has been triggered and completed');
}
if (state === Schema.MarketState.STATE_SUSPENDED) {
return t('Suspended due to price or liquidity monitoring trigger');
}
if (state === Schema.MarketState.STATE_TRADING_TERMINATED) {
return t(
'Trading has been terminated as a result of the product definition'
);
}
return undefined;
};

View File

@ -26,7 +26,7 @@ interface Props {
initialTrigger?: Types.AuctionTrigger; initialTrigger?: Types.AuctionTrigger;
} }
export const MarketTradingModeComponent = ({ export const MarketTradingMode = ({
marketId, marketId,
onSelect, onSelect,
isHeader = false, isHeader = false,

View File

@ -20,7 +20,7 @@ import type {
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { MarketMarkPrice } from '../market-mark-price'; import { MarketMarkPrice } from '../market-mark-price';
import { Last24hPriceChange } from '../last-24h-price-change'; import { Last24hPriceChange } from '../last-24h-price-change';
import { MarketTradingModeComponent } from '../market-trading-mode'; import { MarketTradingMode } from '../market-trading-mode';
import { Last24hVolume } from '../last-24h-volume'; import { Last24hVolume } from '../last-24h-volume';
type Market = MarketWithData & MarketWithCandles; type Market = MarketWithData & MarketWithCandles;
@ -325,7 +325,7 @@ export const columns = (
{ {
kind: ColumnKind.TradingMode, kind: ColumnKind.TradingMode,
value: ( value: (
<MarketTradingModeComponent <MarketTradingMode
marketId={market?.id} marketId={market?.id}
noUpdate={noUpdate} noUpdate={noUpdate}
initialMode={market.tradingMode} initialMode={market.tradingMode}
@ -513,7 +513,7 @@ export const columnsPositionMarkets = (
{ {
kind: ColumnKind.TradingMode, kind: ColumnKind.TradingMode,
value: ( value: (
<MarketTradingModeComponent <MarketTradingMode
marketId={market?.id} marketId={market?.id}
noUpdate={noUpdate} noUpdate={noUpdate}
initialMode={market.tradingMode} initialMode={market.tradingMode}

View File

@ -64,6 +64,8 @@ const MARKET_A: PartialMarket = {
}, },
markPrice: '90', markPrice: '90',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING, trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING,
marketState: Schema.MarketState.STATE_PENDING,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
indicativeVolume: '1000', indicativeVolume: '1000',
}, },
candles: [ candles: [
@ -134,6 +136,8 @@ const MARKET_B: PartialMarket = {
}, },
markPrice: '123.123', markPrice: '123.123',
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING, trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING,
marketState: Schema.MarketState.STATE_PENDING,
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
indicativeVolume: '2000', indicativeVolume: '2000',
}, },
candles: [ candles: [

View File

@ -52,6 +52,7 @@ export function generateMarket(
trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_BATCH, trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_BATCH,
staticMidPrice: '100', staticMidPrice: '100',
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
marketState: Schema.MarketState.STATE_ACTIVE,
indicativePrice: '100', indicativePrice: '100',
indicativeVolume: '10', indicativeVolume: '10',
bestStaticBidPrice: '100', bestStaticBidPrice: '100',

View File

@ -3,23 +3,23 @@ import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string }; export type MarketDataUpdateFieldsFragment = { __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string };
export type MarketDataUpdateSubscriptionVariables = Types.Exact<{ export type MarketDataUpdateSubscriptionVariables = Types.Exact<{
marketId: Types.Scalars['ID']; marketId: Types.Scalars['ID'];
}>; }>;
export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string }> }; export type MarketDataUpdateSubscription = { __typename?: 'Subscription', marketsData: Array<{ __typename?: 'ObservableMarketData', marketId: string, bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string }> };
export type MarketDataFieldsFragment = { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } }; export type MarketDataFieldsFragment = { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } };
export type MarketDataQueryVariables = Types.Exact<{ export type MarketDataQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID']; marketId: Types.Scalars['ID'];
}>; }>;
export type MarketDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } } | null } }> } | null }; export type MarketDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, marketState: Types.MarketState, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } } | null } }> } | null };
export const MarketDataUpdateFieldsFragmentDoc = gql` export const MarketDataUpdateFieldsFragmentDoc = gql`
fragment MarketDataUpdateFields on ObservableMarketData { fragment MarketDataUpdateFields on ObservableMarketData {
@ -30,6 +30,7 @@ export const MarketDataUpdateFieldsFragmentDoc = gql`
trigger trigger
staticMidPrice staticMidPrice
marketTradingMode marketTradingMode
marketState
indicativeVolume indicativeVolume
indicativePrice indicativePrice
bestStaticBidPrice bestStaticBidPrice
@ -47,6 +48,7 @@ export const MarketDataFieldsFragmentDoc = gql`
trigger trigger
staticMidPrice staticMidPrice
marketTradingMode marketTradingMode
marketState
indicativeVolume indicativeVolume
indicativePrice indicativePrice
bestStaticBidPrice bestStaticBidPrice

View File

@ -1,28 +1,49 @@
import { Schema as Types } from '@vegaprotocol/types'; import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import { MarketDataFieldsFragmentDoc } from './market-data';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type MarketsDataFieldsFragment = { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } };
export type MarketsDataQueryVariables = Types.Exact<{ [key: string]: never; }>; export type MarketsDataQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type MarketsDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } } | null } }> } | null }; export type MarketsDataQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', data?: { __typename?: 'MarketData', bestBidPrice: string, bestOfferPrice: string, markPrice: string, trigger: Types.AuctionTrigger, staticMidPrice: string, marketTradingMode: Types.MarketTradingMode, indicativeVolume: string, indicativePrice: string, bestStaticBidPrice: string, bestStaticOfferPrice: string, targetStake?: string | null, suppliedStake?: string | null, auctionStart?: string | null, auctionEnd?: string | null, market: { __typename?: 'Market', id: string } } | null } }> } | null };
export const MarketsDataFieldsFragmentDoc = gql`
fragment MarketsDataFields on MarketData {
market {
id
}
bestBidPrice
bestOfferPrice
markPrice
trigger
staticMidPrice
marketTradingMode
indicativeVolume
indicativePrice
bestStaticBidPrice
bestStaticOfferPrice
targetStake
suppliedStake
auctionStart
auctionEnd
}
`;
export const MarketsDataDocument = gql` export const MarketsDataDocument = gql`
query MarketsData { query MarketsData {
marketsConnection { marketsConnection {
edges { edges {
node { node {
data { data {
...MarketDataFields ...MarketsDataFields
} }
} }
} }
} }
} }
${MarketDataFieldsFragmentDoc}`; ${MarketsDataFieldsFragmentDoc}`;
/** /**
* __useMarketsDataQuery__ * __useMarketsDataQuery__

View File

@ -6,6 +6,7 @@ fragment MarketDataUpdateFields on ObservableMarketData {
trigger trigger
staticMidPrice staticMidPrice
marketTradingMode marketTradingMode
marketState
indicativeVolume indicativeVolume
indicativePrice indicativePrice
bestStaticBidPrice bestStaticBidPrice
@ -28,6 +29,7 @@ fragment MarketDataFields on MarketData {
trigger trigger
staticMidPrice staticMidPrice
marketTradingMode marketTradingMode
marketState
indicativeVolume indicativeVolume
indicativePrice indicativePrice
bestStaticBidPrice bestStaticBidPrice

View File

@ -1,4 +1,4 @@
fragment MarketDataFields on MarketData { fragment MarketsDataFields on MarketData {
market { market {
id id
} }
@ -23,7 +23,7 @@ query MarketsData {
edges { edges {
node { node {
data { data {
...MarketDataFields ...MarketsDataFields
} }
} }
} }

View File

@ -44,20 +44,18 @@ export const PriceCellChange = React.memo(
<span <span
className={`${signedNumberCssClass( className={`${signedNumberCssClass(
change change
)} flex items-center gap-2 justify-end`} )} flex items-center gap-2 font-mono text-ui-small`}
> >
<Arrow value={change} /> <Arrow value={change} />
<span className="flex items-center gap-2 font-mono text-ui-small"> <span data-testid="price-change-percentage">
<span data-testid="price-change-percentage"> {formatNumberPercentage(
{formatNumberPercentage( new BigNumber(changePercentage.toString()),
new BigNumber(changePercentage.toString()), 2
2 )}
)} &nbsp;
&nbsp; </span>
</span> <span data-testid="price-change">
<span data-testid="price-change"> {addDecimalsFormatNumber(change.toString(), decimalPlaces ?? 0, 3)}
{addDecimalsFormatNumber(change.toString(), decimalPlaces ?? 0, 3)}
</span>
</span> </span>
</span> </span>
); );