feat(trading): show successor banner if market is settled or successor market exists (#5056)
Co-authored-by: bwallacee <ben@vega.xyz>
This commit is contained in:
parent
abf84b9d45
commit
236e35a92b
@ -21,6 +21,7 @@ const market = {
|
|||||||
} as unknown as Market;
|
} as unknown as Market;
|
||||||
|
|
||||||
let mockDataSuccessorMarket: PartialDeep<Market> | null = null;
|
let mockDataSuccessorMarket: PartialDeep<Market> | null = null;
|
||||||
|
let mockDataMarketState: Market['state'] | null = null;
|
||||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/data-provider'),
|
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||||
useDataProvider: jest.fn().mockImplementation((args) => {
|
useDataProvider: jest.fn().mockImplementation((args) => {
|
||||||
@ -43,6 +44,12 @@ jest.mock('@vegaprotocol/utils', () => ({
|
|||||||
let mockCandles = {};
|
let mockCandles = {};
|
||||||
jest.mock('@vegaprotocol/markets', () => ({
|
jest.mock('@vegaprotocol/markets', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/markets'),
|
...jest.requireActual('@vegaprotocol/markets'),
|
||||||
|
useMarketState: (marketId: string) =>
|
||||||
|
marketId
|
||||||
|
? {
|
||||||
|
data: mockDataMarketState,
|
||||||
|
}
|
||||||
|
: { data: undefined },
|
||||||
useSuccessorMarket: (marketId: string) =>
|
useSuccessorMarket: (marketId: string) =>
|
||||||
marketId
|
marketId
|
||||||
? {
|
? {
|
||||||
@ -81,30 +88,6 @@ describe('MarketSuccessorBanner', () => {
|
|||||||
});
|
});
|
||||||
expect(container).toBeEmptyDOMElement();
|
expect(container).toBeEmptyDOMElement();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('successor market not in continuous mode', () => {
|
|
||||||
mockDataSuccessorMarket = {
|
|
||||||
...mockDataSuccessorMarket,
|
|
||||||
tradingMode: Types.MarketTradingMode.TRADING_MODE_NO_TRADING,
|
|
||||||
};
|
|
||||||
const { container } = render(<MarketSuccessorBanner market={market} />, {
|
|
||||||
wrapper: MockedProvider,
|
|
||||||
});
|
|
||||||
expect(container).toBeEmptyDOMElement();
|
|
||||||
expect(allUtils.getMarketExpiryDate).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('successor market is not active', () => {
|
|
||||||
mockDataSuccessorMarket = {
|
|
||||||
...mockDataSuccessorMarket,
|
|
||||||
state: Types.MarketState.STATE_PENDING,
|
|
||||||
};
|
|
||||||
const { container } = render(<MarketSuccessorBanner market={market} />, {
|
|
||||||
wrapper: MockedProvider,
|
|
||||||
});
|
|
||||||
expect(container).toBeEmptyDOMElement();
|
|
||||||
expect(allUtils.getMarketExpiryDate).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should be displayed', () => {
|
describe('should be displayed', () => {
|
||||||
@ -120,6 +103,17 @@ describe('MarketSuccessorBanner', () => {
|
|||||||
).toHaveAttribute('href', '/#/markets/successorMarketID');
|
).toHaveAttribute('href', '/#/markets/successorMarketID');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('no successor market data, market settled', () => {
|
||||||
|
mockDataSuccessorMarket = null;
|
||||||
|
mockDataMarketState = Types.MarketState.STATE_SETTLED;
|
||||||
|
render(<MarketSuccessorBanner market={market} />, {
|
||||||
|
wrapper: MockedProvider,
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
screen.getByText('This market has been settled')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it('should display optionally successor volume', () => {
|
it('should display optionally successor volume', () => {
|
||||||
mockDataSuccessorMarket = {
|
mockDataSuccessorMarket = {
|
||||||
...mockDataSuccessorMarket,
|
...mockDataSuccessorMarket,
|
||||||
@ -137,7 +131,9 @@ describe('MarketSuccessorBanner', () => {
|
|||||||
render(<MarketSuccessorBanner market={market} />, {
|
render(<MarketSuccessorBanner market={market} />, {
|
||||||
wrapper: MockedProvider,
|
wrapper: MockedProvider,
|
||||||
});
|
});
|
||||||
expect(screen.getByText('has 101.367 24h vol.')).toBeInTheDocument();
|
expect(
|
||||||
|
screen.getByText('has a 24h trading volume of 101.367')
|
||||||
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display optionally duration', () => {
|
it('should display optionally duration', () => {
|
||||||
|
@ -4,6 +4,7 @@ import type { Market } from '@vegaprotocol/markets';
|
|||||||
import {
|
import {
|
||||||
calcCandleVolume,
|
calcCandleVolume,
|
||||||
useCandles,
|
useCandles,
|
||||||
|
useMarketState,
|
||||||
useSuccessorMarket,
|
useSuccessorMarket,
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
@ -29,7 +30,9 @@ export const MarketSuccessorBanner = ({
|
|||||||
}: {
|
}: {
|
||||||
market: Market | null;
|
market: Market | null;
|
||||||
}) => {
|
}) => {
|
||||||
const { data: successorData } = useSuccessorMarket(market?.id);
|
const { data: marketState } = useMarketState(market?.id);
|
||||||
|
const isSettled = marketState === Types.MarketState.STATE_SETTLED;
|
||||||
|
const { data: successorData, loading } = useSuccessorMarket(market?.id);
|
||||||
|
|
||||||
const [visible, setVisible] = useState(true);
|
const [visible, setVisible] = useState(true);
|
||||||
|
|
||||||
@ -45,11 +48,6 @@ export const MarketSuccessorBanner = ({
|
|||||||
? intervalToDuration({ start: new Date(), end: expiry })
|
? intervalToDuration({ start: new Date(), end: expiry })
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const isInContinuesMode =
|
|
||||||
successorData?.state === Types.MarketState.STATE_ACTIVE &&
|
|
||||||
successorData?.tradingMode ===
|
|
||||||
Types.MarketTradingMode.TRADING_MODE_CONTINUOUS;
|
|
||||||
|
|
||||||
const { oneDayCandles } = useCandles({
|
const { oneDayCandles } = useCandles({
|
||||||
marketId: successorData?.id,
|
marketId: successorData?.id,
|
||||||
});
|
});
|
||||||
@ -66,7 +64,7 @@ export const MarketSuccessorBanner = ({
|
|||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (isInContinuesMode && visible) {
|
if (!loading && (isSettled || successorData) && visible) {
|
||||||
return (
|
return (
|
||||||
<NotificationBanner
|
<NotificationBanner
|
||||||
intent={Intent.Primary}
|
intent={Intent.Primary}
|
||||||
@ -74,34 +72,47 @@ export const MarketSuccessorBanner = ({
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="uppercase mb-1">
|
<div className="uppercase">
|
||||||
{t('This market has been succeeded')}
|
{successorData
|
||||||
</div>
|
? t('This market has been succeeded')
|
||||||
<div>
|
: t('This market has been settled')}
|
||||||
{duration && (
|
|
||||||
<span>
|
|
||||||
{t('This market expires in %s.', [
|
|
||||||
formatDuration(duration, {
|
|
||||||
format: [
|
|
||||||
'years',
|
|
||||||
'months',
|
|
||||||
'weeks',
|
|
||||||
'days',
|
|
||||||
'hours',
|
|
||||||
'minutes',
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
])}
|
|
||||||
</span>
|
|
||||||
)}{' '}
|
|
||||||
{t('The successor market')}{' '}
|
|
||||||
<ExternalLink href={`/#/markets/${successorData?.id}`}>
|
|
||||||
{successorData?.tradableInstrument.instrument.name}
|
|
||||||
</ExternalLink>
|
|
||||||
{successorVolume && (
|
|
||||||
<span> {t('has %s 24h vol.', [successorVolume])}</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
{(duration || successorData) && (
|
||||||
|
<div className="mt-1">
|
||||||
|
{duration && (
|
||||||
|
<span>
|
||||||
|
{t('This market expires in %s.', [
|
||||||
|
formatDuration(duration, {
|
||||||
|
format: [
|
||||||
|
'years',
|
||||||
|
'months',
|
||||||
|
'weeks',
|
||||||
|
'days',
|
||||||
|
'hours',
|
||||||
|
'minutes',
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{successorData && (
|
||||||
|
<>
|
||||||
|
{' '}
|
||||||
|
{t('The successor market')}
|
||||||
|
{!successorVolume ? ' is ' : ' '}
|
||||||
|
<ExternalLink href={`/#/markets/${successorData?.id}`}>
|
||||||
|
{successorData?.tradableInstrument.instrument.name}
|
||||||
|
</ExternalLink>
|
||||||
|
{successorVolume && (
|
||||||
|
<span>
|
||||||
|
{' '}
|
||||||
|
{t('has a 24h trading volume of %s', [successorVolume])}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</NotificationBanner>
|
</NotificationBanner>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,16 @@ export const marketTradingModeProvider = makeDerivedDataProvider<
|
|||||||
(parts[0] as ReturnType<typeof getData>)?.marketTradingMode
|
(parts[0] as ReturnType<typeof getData>)?.marketTradingMode
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const marketStateProvider = makeDerivedDataProvider<
|
||||||
|
MarketDataFieldsFragment['marketState'] | undefined,
|
||||||
|
never,
|
||||||
|
MarketDataQueryVariables
|
||||||
|
>(
|
||||||
|
[marketDataProvider],
|
||||||
|
(parts, variables, prevData) =>
|
||||||
|
(parts[0] as ReturnType<typeof getData>)?.marketState
|
||||||
|
);
|
||||||
|
|
||||||
export const fundingRateProvider = makeDerivedDataProvider<
|
export const fundingRateProvider = makeDerivedDataProvider<
|
||||||
string,
|
string,
|
||||||
never,
|
never,
|
||||||
@ -176,3 +186,11 @@ export const useMarketTradingMode = (marketId?: string, skip?: boolean) => {
|
|||||||
skip: skip || !marketId,
|
skip: skip || !marketId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useMarketState = (marketId?: string, skip?: boolean) => {
|
||||||
|
return useDataProvider({
|
||||||
|
dataProvider: marketStateProvider,
|
||||||
|
variables: { marketId: marketId || '' },
|
||||||
|
skip: skip || !marketId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user