diff --git a/apps/trading-e2e/src/integration/home-node-network.cy.ts b/apps/trading-e2e/src/integration/home-node-network.cy.ts index 4bf521d71..21975f74d 100644 --- a/apps/trading-e2e/src/integration/home-node-network.cy.ts +++ b/apps/trading-e2e/src/integration/home-node-network.cy.ts @@ -1,20 +1,16 @@ -import { closeWelcomeDialog } from '../support/helpers'; - const dialogContent = 'dialog-content'; const nodeHealth = 'node-health'; -describe('home', { tags: '@regression' }, () => { +describe.skip('home', { tags: '@regression' }, () => { before(() => { cy.clearAllLocalStorage(); cy.mockTradingPage(); cy.mockSubscription(); cy.visit('/'); - closeWelcomeDialog(); }); describe('footer', () => { - it.skip('shows current block height', () => { - closeWelcomeDialog(); + it('shows current block height', () => { // 0006-NETW-004 // 0006-NETW-005 // 0006-NETW-008 diff --git a/apps/trading-e2e/src/integration/home.cy.ts b/apps/trading-e2e/src/integration/home.cy.ts index d60d8046e..c3292d0ef 100644 --- a/apps/trading-e2e/src/integration/home.cy.ts +++ b/apps/trading-e2e/src/integration/home.cy.ts @@ -1,9 +1,7 @@ import { aliasGQLQuery } from '@vegaprotocol/cypress'; import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals'; -import { marketsDataQuery } from '@vegaprotocol/mock'; import * as Schema from '@vegaprotocol/types'; -const selectMarketOverlay = 'select-market-list'; const dialogContent = 'dialog-content'; const generateProposal = (code: string): ProposalListFieldsFragment => ({ @@ -110,77 +108,12 @@ describe('home', { tags: '@regression' }, () => { cy.get('main[data-testid^="/markets/"]'); - // Overlay should be shown - cy.getByTestId(selectMarketOverlay).should('exist'); - cy.contains('Select a market to get started').should('be.visible'); - - // I expect the market overlay table to contain at least 3 rows (one header row) - cy.getByTestId(selectMarketOverlay) - .get('table tr') - .then((row) => { - expect(row.length >= 3).to.be.true; - }); - - // each market shown in overlay table contains content under the last price and change fields - cy.getByTestId(selectMarketOverlay) - .get('table tr') - .each(($element, index) => { - if (index > 0) { - // skip header row - cy.root().within(() => { - cy.getByTestId('price').should('not.be.empty'); - }); - } - }); - - cy.getByTestId('welcome-notice-proposed-markets') - .children('div.pt-1.flex.justify-between') - .should('have.length', 3) - .each((item) => { - cy.wrap(item).getByTestId('external-link').should('exist'); - }); - - cy.getByTestId('dialog-close').click(); - cy.getByTestId(selectMarketOverlay).should('not.exist'); - // the choose market overlay is no longer showing - cy.contains('Select a market to get started').should('not.exist'); cy.contains('Loading...').should('not.exist'); cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-0'); }); }); - describe('market table should be properly rendered', () => { - it('redirects to a default market with the landing dialog open', () => { - const override = { - marketsConnection: { - edges: [ - { - node: { - data: { - markPrice: '46126900581221212121212121212121212121212121212', - }, - }, - }, - ], - }, - }; - // @ts-ignore partial deep check failing - const data = marketsDataQuery(override); - cy.mockGQL((req) => { - aliasGQLQuery(req, 'MarketsData', data); - }); - cy.visit('/'); - cy.wait('@Markets'); - cy.getByTestId(selectMarketOverlay) - .get('table') - .invoke('outerWidth') - .then((value) => { - expect(value).to.be.closeTo(554, 10); - }); - }); - }); - describe('no markets found', () => { beforeEach(() => { cy.mockGQL((req) => { @@ -206,40 +139,13 @@ describe('home', { tags: '@regression' }, () => { cy.wait('@Markets'); cy.wait('@MarketsData'); }); + it('redirects to a the empty market page and displays welcome notice', () => { cy.url().should('eq', Cypress.config().baseUrl + `/#/markets`); cy.getByTestId('welcome-notice-title').should( 'contain.text', 'Welcome to Console' ); - cy.getByTestId('welcome-notice-proposed-markets').should( - 'contain.text', - 'AAAZZZ' - ); - }); - }); - - describe('no proposal found', () => { - it('there is a link to propose market', () => { - cy.mockGQL((req) => { - aliasGQLQuery(req, 'ProposalsList', { - proposalsConnection: { - __typename: 'ProposalsConnection', - edges: null, - }, - }); - }); - cy.visit('/'); - cy.wait('@Markets'); - cy.wait('@MarketsData'); - cy.getByTestId(selectMarketOverlay) - .get('table tr') - .then((row) => { - expect(row.length >= 3).to.be.true; - }); - cy.getByTestId('external-link') - .contains('Propose a market') - .should('exist'); }); }); diff --git a/apps/trading-e2e/src/integration/live-env.cy.ts b/apps/trading-e2e/src/integration/live-env.cy.ts index 5934b4676..e14acf2c4 100644 --- a/apps/trading-e2e/src/integration/live-env.cy.ts +++ b/apps/trading-e2e/src/integration/live-env.cy.ts @@ -1,4 +1,3 @@ -const selectMarketOverlay = 'select-market-list'; const marketInfoBtn = 'Info'; const marketInfoSubtitle = 'accordion-title'; const marketSummaryBlock = 'header-summary'; @@ -14,45 +13,6 @@ const itemHeader = 'item-header'; const itemValue = 'item-value'; const marketListContent = 'popover-content'; -describe( - 'Console - market list - live env', - { tags: '@live', testIsolation: true }, - () => { - beforeEach(() => { - cy.visit('/'); - }); - - it('shows the market list page', () => { - cy.get('main', { timeout: 20000 }); - - // Overlay should be shown - cy.getByTestId(selectMarketOverlay).should('exist'); - cy.contains('Select a market to get started').should('be.visible'); - - // I expect the market overlay table to contain at least one row - cy.getByTestId(selectMarketOverlay) - .get('table tr') - .should('have.length.greaterThan', 1); - - // each market shown in overlay table contains content under the last price and change fields - cy.getByTestId(selectMarketOverlay) - .get('table tr') - .getByTestId('price') - .should('not.be.empty'); - }); - - it('redirects to a default market', () => { - cy.getByTestId('dialog-close').click(); - cy.getByTestId(selectMarketOverlay).should('not.exist'); - - // the choose market overlay is no longer showing - cy.contains('Select a market to get started').should('not.exist'); - cy.contains('Loading...').should('not.exist'); - cy.getByTestId('popover-trigger').should('not.be.empty'); - }); - } -); - describe( 'Console - market info - live env', { tags: '@live', testIsolation: true }, diff --git a/apps/trading-e2e/src/integration/market-selector.cy.ts b/apps/trading-e2e/src/integration/market-selector.cy.ts index 0a6102d5e..6ef994263 100644 --- a/apps/trading-e2e/src/integration/market-selector.cy.ts +++ b/apps/trading-e2e/src/integration/market-selector.cy.ts @@ -38,28 +38,28 @@ describe('markets selector', { tags: '@smoke' }, () => { // TODO: load data from mocks in. Using alias and wrap intermittently fails const data = [ { - code: 'AAPL.MF21', - name: 'Apple Monthly (30 Jun 2022)', - markPrice: '46,126.90058', - change: '+200.00%', - }, - { - code: 'BTCUSD.MF21', - name: 'ACTIVE MARKET', - markPrice: '46,126.90058', + code: 'SOLUSD', + markPrice: '84.41XYZalpha', change: '+200.00%', + vol: '324h vol', }, { code: 'ETHBTC.QM21', - name: 'ETHBTC Quarterly (30 Jun 2022)', - markPrice: '46,126.90058', + markPrice: '46,126.90058tBTC', change: '+200.00%', + vol: '324h vol', }, { - code: 'SOLUSD', - name: 'SUSPENDED MARKET', - markPrice: '84.41', + code: 'BTCUSD.MF21', + markPrice: '46,126.90058tDAI', change: '+200.00%', + vol: '324h vol', + }, + { + code: 'AAPL.MF21', + markPrice: '46,126.90058tUSDC', + change: '+200.00%', + vol: '324h vol', }, ]; cy.getByTestId(list) @@ -68,12 +68,13 @@ describe('markets selector', { tags: '@smoke' }, () => { const market = data[i]; // 6001-MARK-021 expect(item.find('h3').text()).equals(market.code); - // 6001-MARK-022 - expect(item.find('h4').text()).equals(market.name); + expect( + item.find('[data-testid="market-selector-data-row"]').eq(0).text() + ).contains(market.vol); // 6001-MARK-024 - expect(item.find('[data-testid="market-item-price"]').text()).equals( - market.markPrice - ); + expect( + item.find('[data-testid="market-selector-data-row"]').eq(1).text() + ).contains(market.markPrice); // 6001-MARK-023 expect(item.find('[data-testid="market-item-change"]').text()).equals( market.change @@ -96,8 +97,8 @@ describe('markets selector', { tags: '@smoke' }, () => { // 6001-MARK-29 cy.getByTestId(searchInput).clear().type('btc'); cy.getByTestId(list).find('a').should('have.length', 2); - cy.getByTestId(list).find('a').eq(0).contains('BTCUSD.MF21'); - cy.getByTestId(list).find('a').eq(1).contains('ETHBTC.QM21'); + cy.getByTestId(list).find('a').eq(1).contains('BTCUSD.MF21'); + cy.getByTestId(list).find('a').eq(0).contains('ETHBTC.QM21'); cy.getByTestId(searchInput).clear(); cy.getByTestId(list).find('a').should('have.length', 4); diff --git a/apps/trading-e2e/src/integration/markets.cy.ts b/apps/trading-e2e/src/integration/markets.cy.ts index 92cbd2fbb..c44db07dc 100644 --- a/apps/trading-e2e/src/integration/markets.cy.ts +++ b/apps/trading-e2e/src/integration/markets.cy.ts @@ -3,8 +3,6 @@ import { aliasGQLQuery, checkSorting } from '@vegaprotocol/cypress'; import { marketsQuery } from '@vegaprotocol/mock'; import { getDateTimeFormat } from '@vegaprotocol/utils'; -const dialogCloseBtn = 'dialog-close'; - describe('markets table', { tags: '@smoke' }, () => { beforeEach(() => { cy.clearLocalStorage().then(() => { @@ -14,20 +12,18 @@ describe('markets table', { tags: '@smoke' }, () => { Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET ); cy.mockSubscription(); - cy.visit('/'); - cy.wait('@Markets'); - cy.wait('@MarketsData'); - cy.wait('@MarketsCandles'); + cy.visit('/#/markets/all'); }); }); it('renders markets correctly', () => { + cy.wait('@Markets'); + cy.wait('@MarketsData'); cy.get('[data-testid^="market-link-"]').should('not.be.empty'); cy.getByTestId('price').invoke('text').should('not.be.empty'); cy.getByTestId('settlement-asset').should('not.be.empty'); cy.getByTestId('price-change-percentage').should('not.be.empty'); cy.getByTestId('price-change').should('not.be.empty'); - cy.getByTestId('sparkline-svg').should('be.visible'); }); it('able to open and sort full market list - market page', () => { @@ -37,9 +33,6 @@ describe('markets table', { tags: '@smoke' }, () => { 'ETHBTC.QM21', 'SOLUSD', ]; - cy.getByTestId('view-market-list-link') - .should('have.attr', 'href', '#/markets/all') - .click(); cy.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all'); cy.contains('AAPL.MF21').should('be.visible'); cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name @@ -51,10 +44,6 @@ describe('markets table', { tags: '@smoke' }, () => { }); it('proposed markets tab should be rendered properly', () => { - cy.getByTestId('view-market-list-link') - .should('have.attr', 'href', '#/markets/all') - .click(); - cy.get('[data-testid="All markets"]').should( 'have.attr', 'data-state', @@ -87,8 +76,8 @@ describe('markets table', { tags: '@smoke' }, () => { `${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market` ); }); + it('proposed markets tab should be sorted properly', () => { - cy.getByTestId('view-market-list-link').click(); cy.get('[data-testid="Proposed markets"]').click(); const marketColDefault = [ 'ETHUSD', @@ -167,7 +156,7 @@ describe('markets table', { tags: '@smoke' }, () => { checkSorting('state', stateColDefault, stateColAsc, stateColDesc); }); - it('opening auction subsets should be properly displayed', () => { + it.skip('opening auction subsets should be properly displayed', () => { cy.mockTradingPage( Schema.MarketState.STATE_ACTIVE, Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION @@ -200,7 +189,6 @@ describe('markets table', { tags: '@smoke' }, () => { }); cy.visit('#/markets/market-0'); cy.url().should('contain', 'market-0'); - cy.getByTestId(dialogCloseBtn).click(); cy.getByTestId('item-value').contains('Opening auction').realHover(); cy.getByTestId('opening-auction-sub-status').should( 'contain.text', diff --git a/apps/trading-e2e/src/integration/navbar.cy.ts b/apps/trading-e2e/src/integration/navbar.cy.ts index 849bc1df7..65aeb8104 100644 --- a/apps/trading-e2e/src/integration/navbar.cy.ts +++ b/apps/trading-e2e/src/integration/navbar.cy.ts @@ -8,9 +8,6 @@ describe('Navbar', { tags: '@smoke' }, () => { cy.visit('/'); cy.wait('@Markets'); cy.wait('@MarketsData'); - cy.wait('@MarketsCandles'); - // close welcome dialog - cy.getByTestId('dialog-close').click(); }); const pages = [ diff --git a/apps/trading-e2e/src/integration/settings.cy.ts b/apps/trading-e2e/src/integration/settings.cy.ts index c4ed19e46..9d980db6d 100644 --- a/apps/trading-e2e/src/integration/settings.cy.ts +++ b/apps/trading-e2e/src/integration/settings.cy.ts @@ -1,12 +1,9 @@ -import { closeWelcomeDialog } from '../support/helpers'; - describe('Settings page', { tags: '@smoke' }, () => { beforeEach(() => { cy.clearLocalStorage().then(() => { cy.mockTradingPage(); cy.mockSubscription(); cy.visit('/'); - closeWelcomeDialog(); cy.get('[aria-label="cog icon"]').click(); }); }); diff --git a/apps/trading-e2e/src/support/helpers.ts b/apps/trading-e2e/src/support/helpers.ts index 19af30e7e..85fa22a68 100644 --- a/apps/trading-e2e/src/support/helpers.ts +++ b/apps/trading-e2e/src/support/helpers.ts @@ -9,8 +9,3 @@ export const selectAsset = (assetIndex: number) => { // eslint-disable-next-line cy.wait(100); }; - -export const closeWelcomeDialog = () => { - cy.getByTestId('select-market-list').should('exist'); - cy.getByTestId('dialog-close').click(); -}; diff --git a/apps/trading-e2e/src/support/trading.ts b/apps/trading-e2e/src/support/trading.ts index b4f3f671f..d8ae8b840 100644 --- a/apps/trading-e2e/src/support/trading.ts +++ b/apps/trading-e2e/src/support/trading.ts @@ -85,7 +85,7 @@ const mockTradingPage = ( trigger?: Schema.AuctionTrigger ) => { aliasGQLQuery(req, 'ChainId', chainIdQuery()); - aliasGQLQuery(req, 'Statistics', statisticsQuery()); + aliasGQLQuery(req, 'NodeCheck', statisticsQuery()); aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery()); aliasGQLQuery( req, diff --git a/apps/trading/client-pages/home/home.tsx b/apps/trading/client-pages/home/home.tsx index 2468f54c7..ce3830894 100644 --- a/apps/trading/client-pages/home/home.tsx +++ b/apps/trading/client-pages/home/home.tsx @@ -9,7 +9,7 @@ import { useGlobalStore } from '../../stores'; export const Home = () => { const navigate = useNavigate(); // The default market selected in the platform behind the overlay - // should be the oldest market that is currently trading in us mode(i.e. not in auction). + // should be the oldest market that is currently trading in continuous mode(i.e. not in auction). const { data, error, loading } = useDataProvider({ dataProvider: marketsWithDataProvider, variables: undefined, diff --git a/apps/trading/client-pages/market/market-selector-item.spec.tsx b/apps/trading/client-pages/market/market-selector-item.spec.tsx index 38a0a2e38..d4c7b824f 100644 --- a/apps/trading/client-pages/market/market-selector-item.spec.tsx +++ b/apps/trading/client-pages/market/market-selector-item.spec.tsx @@ -22,7 +22,19 @@ describe('MarketSelectorItem', () => { id: 'market-0', decimalPlaces: 2, // @ts-ignore fragment doesn't contain candles - candles: [{ close: '5' }, { close: '10' }], + candles: [ + { close: '5', volume: '50' }, + { close: '10', volume: '50' }, + ], + tradableInstrument: { + instrument: { + product: { + settlementAsset: { + symbol: 'SYM', + }, + }, + }, + }, }); const marketData: MarketDataUpdateFieldsFragment = { __typename: 'ObservableMarketData', @@ -83,6 +95,9 @@ describe('MarketSelectorItem', () => { }; it('renders market information', async () => { + const symbol = + market.tradableInstrument.instrument.product.settlementAsset.symbol; + renderJsx(); const link = screen.getByRole('link'); @@ -91,7 +106,8 @@ describe('MarketSelectorItem', () => { expect(link).toHaveClass('ring-1'); - expect(screen.getByTestId('market-item-price')).toHaveTextContent('-'); + expect(screen.getByTitle('24h vol')).toHaveTextContent('100'); + expect(screen.getByTitle(symbol)).toHaveTextContent('-'); // candles are loaded immediately expect(screen.getByTestId('market-item-change')).toHaveTextContent( @@ -99,7 +115,7 @@ describe('MarketSelectorItem', () => { ); await waitFor(() => { - expect(screen.getByTestId('market-item-price')).toHaveTextContent( + expect(screen.getByTitle(symbol)).toHaveTextContent( addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces) ); }); diff --git a/apps/trading/client-pages/market/market-selector-item.tsx b/apps/trading/client-pages/market/market-selector-item.tsx index 67ca496d3..99731e844 100644 --- a/apps/trading/client-pages/market/market-selector-item.tsx +++ b/apps/trading/client-pages/market/market-selector-item.tsx @@ -7,8 +7,14 @@ import { priceChangePercentage, } from '@vegaprotocol/utils'; import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets'; +import { calcCandleVolume } from '@vegaprotocol/markets'; import { useMarketDataUpdateSubscription } from '@vegaprotocol/markets'; import { Sparkline } from '@vegaprotocol/ui-toolkit'; +import { + MarketTradingMode, + MarketTradingModeMapping, +} from '@vegaprotocol/types'; +import { t } from '@vegaprotocol/i18n'; export const MarketSelectorItem = ({ market, @@ -38,13 +44,6 @@ export const MarketSelectorItem = ({ onSelect && onSelect(market.id); }} > -

{market.tradableInstrument.instrument.code}

-

- {market.tradableInstrument.instrument.name} -

@@ -68,30 +67,84 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => { ? addDecimalsFormatNumber(market.data.markPrice, market.decimalPlaces) : '-'; + const marketTradingMode = marketData + ? marketData.marketTradingMode + : market.tradingMode; + + const mode = [ + MarketTradingMode.TRADING_MODE_BATCH_AUCTION, + MarketTradingMode.TRADING_MODE_MONITORING_AUCTION, + MarketTradingMode.TRADING_MODE_OPENING_AUCTION, + ].includes(marketTradingMode) + ? MarketTradingModeMapping[marketTradingMode] + : ''; + + const instrument = market.tradableInstrument.instrument; + + const vol = market.candles ? calcCandleVolume(market.candles) : '0'; + const volume = + vol && vol !== '0' + ? addDecimalsFormatNumber(vol, market.positionDecimalPlaces) + : '0.00'; + return ( -
-
-
+
+

- {price} -

+ {market.tradableInstrument.instrument.code} + + {mode && ( +

+ {mode} +

+ )} +
+ + +
{market.candles && ( c.close)} /> )} + +
+ {market.candles && ( + Number(c.close))} + /> + )} +
-
- {market.candles ? ( - Number(c.close))} - /> - ) : ( - '-' - )} -
+ + ); +}; + +const DataRow = ({ value, label }: { value: string; label: string }) => { + return ( +
+ + {value} + + + {label} +
); }; diff --git a/apps/trading/client-pages/market/product-selector.tsx b/apps/trading/client-pages/market/product-selector.tsx index 035a3c49f..5ce6eb576 100644 --- a/apps/trading/client-pages/market/product-selector.tsx +++ b/apps/trading/client-pages/market/product-selector.tsx @@ -2,8 +2,8 @@ import classNames from 'classnames'; // Make sure these match the available __typename properties on product export const Product = { - Spot: 'Spot', Future: 'Future', + Spot: 'Spot', Perpetual: 'Perpetual', } as const; @@ -12,8 +12,8 @@ export type ProductType = keyof typeof Product; const ProductTypeMapping: { [key in ProductType]: string; } = { - [Product.Spot]: 'Spot', [Product.Future]: 'Futures', + [Product.Spot]: 'Spot', [Product.Perpetual]: 'Perpetuals', }; diff --git a/apps/trading/client-pages/market/trade-grid.tsx b/apps/trading/client-pages/market/trade-grid.tsx index 95efb5575..d726c57a6 100644 --- a/apps/trading/client-pages/market/trade-grid.tsx +++ b/apps/trading/client-pages/market/trade-grid.tsx @@ -339,24 +339,31 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => { return (
-
+
diff --git a/apps/trading/client-pages/market/use-market-selector-list.spec.tsx b/apps/trading/client-pages/market/use-market-selector-list.spec.tsx index e70520934..6faadaef3 100644 --- a/apps/trading/client-pages/market/use-market-selector-list.spec.tsx +++ b/apps/trading/client-pages/market/use-market-selector-list.spec.tsx @@ -315,39 +315,47 @@ describe('useMarketSelectorList', () => { ]); }); - it('sorts albphabetically', () => { + it('sorts by state and volume by default', () => { const markets = [ createMarketFragment({ id: 'market-0', - tradableInstrument: { - instrument: { - code: 'd', + state: MarketState.STATE_PENDING, + // @ts-ignore candles not on fragment + candles: [ + { + volume: '200', }, - }, + ], }), createMarketFragment({ id: 'market-1', - tradableInstrument: { - instrument: { - code: 'b', + state: MarketState.STATE_ACTIVE, + // @ts-ignore candles not on fragment + candles: [ + { + volume: '200', }, - }, + ], }), createMarketFragment({ id: 'market-2', - tradableInstrument: { - instrument: { - code: 'a', + state: MarketState.STATE_ACTIVE, + // @ts-ignore candles not on fragment + candles: [ + { + volume: '100', }, - }, + ], }), createMarketFragment({ + state: MarketState.STATE_PENDING, id: 'market-3', - tradableInstrument: { - instrument: { - code: 'c', + // @ts-ignore candles not on fragment + candles: [ + { + volume: '100', }, - }, + ], }), ]; @@ -364,10 +372,10 @@ describe('useMarketSelectorList', () => { assets: [], }); expect(result.current.markets).toEqual([ - markets[2], markets[1], - markets[3], + markets[2], markets[0], + markets[3], ]); }); diff --git a/apps/trading/client-pages/market/use-market-selector-list.ts b/apps/trading/client-pages/market/use-market-selector-list.ts index c631ffd53..fef89b188 100644 --- a/apps/trading/client-pages/market/use-market-selector-list.ts +++ b/apps/trading/client-pages/market/use-market-selector-list.ts @@ -1,11 +1,18 @@ import { useMemo } from 'react'; import orderBy from 'lodash/orderBy'; import { MarketState } from '@vegaprotocol/types'; -import { useMarketList } from '@vegaprotocol/markets'; +import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets'; import { priceChangePercentage } from '@vegaprotocol/utils'; import type { Filter } from './market-selector'; import { Sort } from './sort-dropdown'; +// Used for sort order and filter +const MARKET_TEMPLATE = [ + MarketState.STATE_ACTIVE, + MarketState.STATE_SUSPENDED, + MarketState.STATE_PENDING, +]; + export const useMarketSelectorList = ({ product, assets, @@ -46,7 +53,19 @@ export const useMarketSelectorList = ({ }); if (sort === Sort.None) { - return orderBy(markets, ['tradableInstrument.instrument.code'], ['asc']); + // Sort by market state primarilly and AtoZ secondarilly + return orderBy( + markets, + [ + (m) => MARKET_TEMPLATE.indexOf(m.state), + (m) => { + if (!m.candles?.length) return 0; + const vol = calcCandleVolume(m.candles); + return Number(vol || 0); + }, + ], + ['asc', 'desc'] + ); } if (sort === Sort.Gained || sort === Sort.Lost) { @@ -78,9 +97,5 @@ export const useMarketSelectorList = ({ }; export const isMarketActive = (state: MarketState) => { - return [ - MarketState.STATE_ACTIVE, - MarketState.STATE_SUSPENDED, - MarketState.STATE_PENDING, - ].includes(state); + return MARKET_TEMPLATE.includes(state); }; diff --git a/apps/trading/components/constants.ts b/apps/trading/components/constants.ts index 3aa00c63d..db2fa18b1 100644 --- a/apps/trading/components/constants.ts +++ b/apps/trading/components/constants.ts @@ -1,4 +1,5 @@ import { t } from '@vegaprotocol/i18n'; + export const THROTTLE_UPDATE_TIME = 500; export const RISK_ACCEPTED_KEY = 'vega_risk_accepted'; export const MAINNET_WELCOME_HEADER = t( diff --git a/apps/trading/components/header/header.tsx b/apps/trading/components/header/header.tsx index fa079597f..aae29ea64 100644 --- a/apps/trading/components/header/header.tsx +++ b/apps/trading/components/header/header.tsx @@ -73,10 +73,10 @@ export const HeaderTitle = ({ }) => { return (
-
+
{primaryContent}
-
+
{secondaryContent}
diff --git a/apps/trading/components/select-market/index.ts b/apps/trading/components/select-market/index.ts deleted file mode 100644 index 5330316ed..000000000 --- a/apps/trading/components/select-market/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './select-market-columns'; -export * from './select-market-table'; diff --git a/apps/trading/components/select-market/select-market-columns.tsx b/apps/trading/components/select-market/select-market-columns.tsx deleted file mode 100644 index 8ea8dc207..000000000 --- a/apps/trading/components/select-market/select-market-columns.tsx +++ /dev/null @@ -1,344 +0,0 @@ -import type { RefObject, MouseEvent } from 'react'; -import { - FeesCell, - calcCandleHigh, - calcCandleLow, - calcCandleVolume, - Last24hPriceChange, - Last24hVolume, -} from '@vegaprotocol/markets'; -import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; -import { t } from '@vegaprotocol/i18n'; -import { PriceCell } from '@vegaprotocol/datagrid'; -import { Link as UILink, Sparkline, Tooltip } from '@vegaprotocol/ui-toolkit'; -import isNil from 'lodash/isNil'; -import type { CandleClose } from '@vegaprotocol/types'; -import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets'; -import { Link } from 'react-router-dom'; -import { MarketMarkPrice } from '../market-mark-price'; -import { MarketTradingMode } from '../market-trading-mode'; -import { Links, Routes } from '../../pages/client-router'; - -const ellipsisClasses = 'whitespace-nowrap overflow-hidden text-ellipsis'; -export const cellClassNames = `py-1 first:text-left text-right ${ellipsisClasses}`; - -const FeesInfo = () => { - return ( - - {t( - 'Fees are paid by market takers on aggressive orders only. The fee displayed is made up of:' - )} -
    -
  • {t('An infrastructure fee')}
  • -
  • {t('A liquidity provision fee')}
  • -
  • {t('A maker fee')}
  • -
- - } - > - {t('Taker fee')} -
- ); -}; - -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: , - 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: MouseEvent, - kind: ColumnKind, - value: string -) => void; - -export const columns = ( - market: MarketMaybeWithDataAndCandles, - onSelect: (id: string, metaKey?: boolean) => void, - onCellClick: OnCellClickHandler, - inViewRoot?: RefObject -) => { - 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 selectMarketColumns: Column[] = [ - { - kind: ColumnKind.Market, - value: ( - { - e.preventDefault(); - e.stopPropagation(); - onSelect(market.id, e.metaKey || e.ctrlKey); - }} - > - {market.tradableInstrument.instrument.code} - - ), - className: `${cellClassNames} max-w-[110px]`, - onlyOnDetailed: false, - }, - { - kind: ColumnKind.ProductType, - value: market.tradableInstrument.instrument.product.__typename, - className: `py-2 text-left hidden sm:table-cell max-w-[50px] ${ellipsisClasses}`, - onlyOnDetailed: false, - }, - { - kind: ColumnKind.LastPrice, - value: ( - - ), - className: `${cellClassNames} max-w-[100px]`, - onlyOnDetailed: false, - }, - { - kind: ColumnKind.Change24, - value: ( - - ), - className: `${cellClassNames} max-w-[150px]`, - onlyOnDetailed: false, - }, - { - kind: ColumnKind.Sparkline, - value: market.candles && ( - Number(c)) || []} - /> - ), - className: `${cellClassNames} hidden lg:table-cell max-w-[80px]`, - onlyOnDetailed: false && candlesClose, - }, - { - kind: ColumnKind.Asset, - value: ( - - ), - dataTestId: 'settlement-asset', - className: `${cellClassNames} hidden sm:table-cell max-w-[100px]`, - onlyOnDetailed: false, - }, - { - kind: ColumnKind.High24, - value: candleHigh ? ( - - ) : ( - '-' - ), - className: `${cellClassNames} hidden xl:table-cell font-mono`, - onlyOnDetailed: true, - }, - { - kind: ColumnKind.Low24, - value: candleLow ? ( - - ) : ( - '-' - ), - className: `${cellClassNames} hidden xl:table-cell font-mono`, - onlyOnDetailed: true, - }, - { - kind: ColumnKind.Volume, - value: ( - - ), - className: `${cellClassNames} hidden lg:table-cell font-mono`, - onlyOnDetailed: true, - dataTestId: 'market-volume', - }, - { - kind: ColumnKind.TradingMode, - value: ( - - ), - className: `${cellClassNames} hidden lg:table-cell`, - onlyOnDetailed: true, - dataTestId: 'trading-mode-col', - }, - { - kind: ColumnKind.Fee, - value: , - 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; -}; diff --git a/apps/trading/components/select-market/select-market-table.tsx b/apps/trading/components/select-market/select-market-table.tsx deleted file mode 100644 index b7136ce3f..000000000 --- a/apps/trading/components/select-market/select-market-table.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { columnHeaders } from './select-market-columns'; -import classNames from 'classnames'; -import type { Column } from './select-market-columns'; - -export const SelectMarketTableHeader = ({ - detailed = false, - headers = columnHeaders, -}) => { - return ( - - {headers.map(({ kind, value, className, onlyOnDetailed }) => { - const thClass = classNames( - 'font-normal text-neutral-500 dark:text-neutral-400', - className - ); - - if (!onlyOnDetailed || detailed === onlyOnDetailed) { - return ( - - {value} - - ); - } - - return null; - })} - - ); -}; - -export const SelectMarketTableRow = ({ - detailed = false, - columns, - onSelect, - marketId, -}: { - detailed?: boolean; - columns: Column[]; - onSelect: (id: string, metaKey?: boolean) => void; - marketId: string; -}) => { - return ( - { - onSelect(marketId, ev.metaKey || ev.ctrlKey); - }} - data-testid={`market-link-${marketId}`} - > - {columns.map(({ kind, value, className, dataTestId, onlyOnDetailed }) => { - if (!onlyOnDetailed || detailed === onlyOnDetailed) { - const tdClass = classNames(className); - return ( - - {value} - - ); - } - return null; - })} - - ); -}; diff --git a/apps/trading/components/welcome-dialog/welcome-dialog-header.tsx b/apps/trading/components/welcome-dialog/welcome-dialog-header.tsx deleted file mode 100644 index de626e68e..000000000 --- a/apps/trading/components/welcome-dialog/welcome-dialog-header.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Networks, useEnvironment } from '@vegaprotocol/environment'; -import * as constants from '../constants'; - -export const WelcomeDialogHeader = () => { - const { VEGA_ENV } = useEnvironment(); - const header = - VEGA_ENV === Networks.MAINNET - ? constants.MAINNET_WELCOME_HEADER - : constants.TESTNET_WELCOME_HEADER; - return

{header}

; -}; diff --git a/apps/trading/components/welcome-dialog/welcome-dialog.tsx b/apps/trading/components/welcome-dialog/welcome-dialog.tsx index e00f00ad9..f57202282 100644 --- a/apps/trading/components/welcome-dialog/welcome-dialog.tsx +++ b/apps/trading/components/welcome-dialog/welcome-dialog.tsx @@ -8,7 +8,6 @@ import { activeMarketsProvider } from '@vegaprotocol/markets'; import * as constants from '../constants'; import { RiskNoticeDialog } from './risk-notice-dialog'; import { WelcomeNoticeDialog } from './welcome-notice-dialog'; -import { WelcomeLandingDialog } from './welcome-landing-dialog'; import { useGlobalStore } from '../../stores'; import { useEnvironment } from '@vegaprotocol/environment'; import { Networks } from '@vegaprotocol/environment'; @@ -43,6 +42,7 @@ export const WelcomeDialog = () => { shouldDisplayWelcomeDialog: isRiskDialogNeeded, }); }, [update, isRiskDialogNeeded]); + if (isRiskDialogNeeded) { dialogContent = ( @@ -52,9 +52,6 @@ export const WelcomeDialog = () => { } else if (isWelcomeDialogNeeded && data?.length === 0) { dialogContent = ; onClose = onCloseDialog; - } else if (isWelcomeDialogNeeded && (data?.length || 0) > 0) { - dialogContent = ; - onClose = onCloseDialog; } else { dialogContent = null as React.ReactNode; } diff --git a/apps/trading/components/welcome-dialog/welcome-landing-dialog.spec.tsx b/apps/trading/components/welcome-dialog/welcome-landing-dialog.spec.tsx deleted file mode 100644 index 7478fa3ae..000000000 --- a/apps/trading/components/welcome-dialog/welcome-landing-dialog.spec.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import { MockedProvider } from '@apollo/client/testing'; -import * as Schema from '@vegaprotocol/types'; -import type { - MarketMaybeWithCandles, - MarketMaybeWithData, - MarketData, -} from '@vegaprotocol/markets'; -import { SelectMarketLandingTable } from './welcome-landing-dialog'; -const mockMarketClickHandler = jest.fn(); -jest.mock('../../lib/hooks/use-market-click-handler', () => ({ - useMarketClickHandler: () => mockMarketClickHandler, -})); - -type Market = MarketMaybeWithCandles & MarketMaybeWithData; -type PartialMarket = Partial< - Omit & { data: Partial } ->; - -const MARKET_A: PartialMarket = { - __typename: 'Market', - id: '1', - decimalPlaces: 2, - tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, - tradableInstrument: { - __typename: 'TradableInstrument', - instrument: { - __typename: 'Instrument', - id: '1', - code: 'ABCDEF', - name: 'ABCDEF 1-Day', - product: { - __typename: 'Future', - quoteName: 'ABCDEF', - settlementAsset: { - __typename: 'Asset', - id: 'asset-ABC', - name: 'asset-ABC', - decimals: 2, - symbol: 'ABC', - }, - dataSourceSpecForTradingTermination: { - __typename: 'DataSourceSpec', - id: 'oracleId', - data: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionExternal', - sourceType: { - __typename: 'DataSourceSpecConfiguration', - }, - }, - }, - }, - dataSourceSpecForSettlementData: { - __typename: 'DataSourceSpec', - id: 'oracleId', - data: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionExternal', - sourceType: { - __typename: 'DataSourceSpecConfiguration', - }, - }, - }, - }, - dataSourceSpecBinding: { - __typename: 'DataSourceSpecToFutureBinding', - tradingTerminationProperty: 'trading-termination-property', - settlementDataProperty: 'settlement-data-property', - }, - }, - metadata: { - __typename: 'InstrumentMetadata', - tags: ['ABC', 'DEF'], - }, - }, - }, - fees: { - __typename: 'Fees', - factors: { - __typename: 'FeeFactors', - infrastructureFee: '0.01', - liquidityFee: '0.01', - makerFee: '0.01', - }, - }, - data: { - __typename: 'MarketData', - market: { - __typename: 'Market', - id: '1', - }, - markPrice: '90', - trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING, - marketState: Schema.MarketState.STATE_PENDING, - marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION, - indicativeVolume: '1000', - }, - candles: [ - { - __typename: 'Candle', - high: '100', - low: '10', - open: '10', - close: '80', - volume: '1000', - periodStart: '2022-11-01T15:49:00Z', - }, - { - __typename: 'Candle', - high: '10', - low: '1', - open: '1', - close: '100', - volume: '1000', - periodStart: '2022-11-01T15:50:00Z', - }, - ], -}; - -const MARKET_B: PartialMarket = { - __typename: 'Market', - id: '2', - decimalPlaces: 2, - positionDecimalPlaces: 0, - tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS, - tradableInstrument: { - __typename: 'TradableInstrument', - instrument: { - __typename: 'Instrument', - id: '2', - code: 'XYZ', - name: 'XYZ 1-Day', - product: { - __typename: 'Future', - quoteName: 'XYZ', - settlementAsset: { - __typename: 'Asset', - id: 'asset-XYZ', - name: 'asset-XYZ', - decimals: 2, - symbol: 'XYZ', - }, - dataSourceSpecForTradingTermination: { - __typename: 'DataSourceSpec', - id: 'oracleId', - data: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionExternal', - sourceType: { - __typename: 'DataSourceSpecConfiguration', - }, - }, - }, - }, - dataSourceSpecForSettlementData: { - __typename: 'DataSourceSpec', - id: 'oracleId', - data: { - __typename: 'DataSourceDefinition', - sourceType: { - __typename: 'DataSourceDefinitionExternal', - sourceType: { - __typename: 'DataSourceSpecConfiguration', - }, - }, - }, - }, - dataSourceSpecBinding: { - __typename: 'DataSourceSpecToFutureBinding', - tradingTerminationProperty: 'trading-termination-property', - settlementDataProperty: 'settlement-data-property', - }, - }, - metadata: { - __typename: 'InstrumentMetadata', - tags: ['XYZ'], - }, - }, - }, - fees: { - __typename: 'Fees', - factors: { - __typename: 'FeeFactors', - infrastructureFee: '0.01', - liquidityFee: '0.01', - makerFee: '0.01', - }, - }, - data: { - __typename: 'MarketData', - market: { - __typename: 'Market', - id: '2', - }, - markPrice: '123.123', - trigger: Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING, - marketState: Schema.MarketState.STATE_PENDING, - marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION, - indicativeVolume: '2000', - }, - candles: [ - { - __typename: 'Candle', - high: '100', - low: '10', - open: '10', - close: '80', - volume: '1000', - periodStart: '2022-11-01T15:49:00Z', - }, - ], -}; - -describe('WelcomeLandingDialog', () => { - it('should call onSelect callback on SelectMarketLandingTable', () => { - const onClose = jest.fn(); - - render( - - - , - { wrapper: MockedProvider } - ); - fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]); - expect(onClose).toHaveBeenCalled(); - fireEvent.click(screen.getAllByTestId(`market-link-2`)[0]); - expect(onClose).toHaveBeenCalled(); - }); - - it('should not call onClose when metaKey is held', () => { - const onClose = jest.fn(); - - render( - - - , - { wrapper: MockedProvider } - ); - fireEvent.click(screen.getAllByTestId(`market-link-1`)[0], { - metaKey: true, - }); - expect(mockMarketClickHandler).toHaveBeenCalled(); - expect(onClose).not.toHaveBeenCalled(); - fireEvent.click(screen.getAllByTestId(`market-link-1`)[0]); - expect(onClose).toHaveBeenCalled(); - }); -}); diff --git a/apps/trading/components/welcome-dialog/welcome-landing-dialog.tsx b/apps/trading/components/welcome-dialog/welcome-landing-dialog.tsx deleted file mode 100644 index 225af52be..000000000 --- a/apps/trading/components/welcome-dialog/welcome-landing-dialog.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import React, { useCallback } from 'react'; -import { useMarketList } from '@vegaprotocol/markets'; -import { t } from '@vegaprotocol/i18n'; -import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; -import { Link as UILink, TinyScroll } from '@vegaprotocol/ui-toolkit'; -import type { OnCellClickHandler } from '../select-market'; -import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets'; -import { - ColumnKind, - columns, - SelectMarketTableHeader, - SelectMarketTableRow, -} from '../select-market'; -import { WelcomeDialogHeader } from './welcome-dialog-header'; -import { Link } from 'react-router-dom'; -import { ProposedMarkets } from './proposed-markets'; -import { Links, Routes } from '../../pages/client-router'; -import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; -import { TelemetryApproval } from './telemetry-approval'; -import { Networks, useEnvironment } from '@vegaprotocol/environment'; - -export const SelectMarketLandingTable = ({ - markets, - onClose, -}: { - markets: MarketMaybeWithDataAndCandles[] | null; - onClose: () => void; -}) => { - const onSelect = useMarketClickHandler(); - const onSelectMarket = useCallback( - (id: string, metaKey?: boolean) => { - onSelect(id, metaKey); - if (!metaKey) { - onClose(); - } - }, - [onSelect, onClose] - ); - const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); - const onCellClick = useCallback( - (e, kind, value) => { - if (value && kind === ColumnKind.Asset) { - openAssetDetailsDialog(value, e.target as HTMLElement); - } - }, - [openAssetDetailsDialog] - ); - const showProposed = (markets?.length || 0) <= 5; - return ( - <> - -

- {t('Select a market to get started...')} -

- - - - - - {markets?.map((market, i) => ( - - ))} - -
-
-
- onClose()} - > - - {'Or view full market list'} - - -
- {showProposed && } - - ); -}; - -interface LandingDialogContainerProps { - onClose: () => void; -} - -export const WelcomeLandingDialog = ({ - onClose, -}: LandingDialogContainerProps) => { - const { data, loading, error } = useMarketList(); - const { VEGA_ENV } = useEnvironment(); - const isMainnet = VEGA_ENV === Networks.MAINNET; - if (error) { - return ( -
-

{t('Failed to load markets')}

-
- ); - } - - if (loading) { - return ( -
-

{t('Loading...')}

-
- ); - } - - return ( - <> - - - {isMainnet && ( - - )} - - ); -}; diff --git a/libs/ui-toolkit/src/components/sparkline/sparkline.tsx b/libs/ui-toolkit/src/components/sparkline/sparkline.tsx index 7bde6b25c..01ee9041f 100644 --- a/libs/ui-toolkit/src/components/sparkline/sparkline.tsx +++ b/libs/ui-toolkit/src/components/sparkline/sparkline.tsx @@ -92,10 +92,7 @@ export const SparklineView = ({ return (