From 969bfd694564292550182ead8ab69dddceb42eaa Mon Sep 17 00:00:00 2001 From: Art Date: Wed, 15 Feb 2023 15:20:01 +0100 Subject: [PATCH] feat(explorer): markets and market details pages (#2914) --- .../src/integration/network.cy.js | 2 +- apps/explorer/src/app/app.tsx | 30 +-- .../components/dialogs/json-viewer-dialog.tsx | 56 ++++ .../src/app/components/header/header.spec.tsx | 2 +- .../src/app/components/header/header.tsx | 14 +- .../src/app/components/main/index.tsx | 2 +- .../app/components/markets/market-details.tsx | 246 ++++++++++++++++++ .../app/components/markets/markets-table.tsx | 132 ++++++++++ .../explorer/src/app/components/nav/index.tsx | 46 +--- apps/explorer/src/app/components/nav/nav.tsx | 181 +++++++++++++ .../src/app/routes/markets/Markets.graphql | 140 ---------- .../routes/markets/__generated__/Markets.ts | 180 ------------- .../src/app/routes/markets/index.spec.tsx | 48 ---- .../explorer/src/app/routes/markets/index.tsx | 46 +--- .../src/app/routes/markets/market-page.tsx | 68 +++++ .../src/app/routes/markets/markets-page.tsx | 31 +++ .../explorer/src/app/routes/router-config.tsx | 33 ++- libs/apollo-client/src/cache-config.ts | 52 ++++ libs/apollo-client/src/index.ts | 1 + .../market-info/MarketInfoNoCandles.graphql | 147 +++++++++++ .../__generated__/MarketInfoNoCandles.ts | 190 ++++++++++++++ .../src/components/market-info/index.ts | 2 + .../market-info/info-key-value-table.tsx | 7 +- .../market-info/market-info-data-provider.ts | 12 + 24 files changed, 1163 insertions(+), 505 deletions(-) create mode 100644 apps/explorer/src/app/components/dialogs/json-viewer-dialog.tsx create mode 100644 apps/explorer/src/app/components/markets/market-details.tsx create mode 100644 apps/explorer/src/app/components/markets/markets-table.tsx create mode 100644 apps/explorer/src/app/components/nav/nav.tsx delete mode 100644 apps/explorer/src/app/routes/markets/Markets.graphql delete mode 100644 apps/explorer/src/app/routes/markets/__generated__/Markets.ts delete mode 100644 apps/explorer/src/app/routes/markets/index.spec.tsx create mode 100644 apps/explorer/src/app/routes/markets/market-page.tsx create mode 100644 apps/explorer/src/app/routes/markets/markets-page.tsx create mode 100644 libs/apollo-client/src/cache-config.ts create mode 100644 libs/market-info/src/components/market-info/MarketInfoNoCandles.graphql create mode 100644 libs/market-info/src/components/market-info/__generated__/MarketInfoNoCandles.ts diff --git a/apps/explorer-e2e/src/integration/network.cy.js b/apps/explorer-e2e/src/integration/network.cy.js index 5afd69d36..cea6b65f9 100644 --- a/apps/explorer-e2e/src/integration/network.cy.js +++ b/apps/explorer-e2e/src/integration/network.cy.js @@ -247,7 +247,7 @@ context('Network parameters page', { tags: '@smoke' }, function () { .and('include', darkThemeSideMenuBackgroundColor); }); - it('should be able to see network parameters - on mobile', function () { + it.skip('should be able to see network parameters - on mobile', function () { cy.common_switch_to_mobile_and_click_toggle(); cy.get(networkParametersNavigation).click(); cy.get_network_parameters().then((network_parameters) => { diff --git a/apps/explorer/src/app/app.tsx b/apps/explorer/src/app/app.tsx index 7621aada8..440ff3756 100644 --- a/apps/explorer/src/app/app.tsx +++ b/apps/explorer/src/app/app.tsx @@ -1,18 +1,16 @@ import classnames from 'classnames'; -import { useState, useEffect } from 'react'; -import { useLocation } from 'react-router-dom'; import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment'; import { Nav } from './components/nav'; import { Header } from './components/header'; import { Main } from './components/main'; import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider'; -import type { InMemoryCacheConfig } from '@apollo/client'; import { Footer } from './components/footer/footer'; import { AnnouncementBanner, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { AssetDetailsDialog, useAssetDetailsDialogStore, } from '@vegaprotocol/assets'; +import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client'; const DialogsContainer = () => { const { isOpen, id, trigger, asJson, setOpen } = useAssetDetailsDialogStore(); @@ -28,36 +26,18 @@ const DialogsContainer = () => { }; function App() { - const [menuOpen, setMenuOpen] = useState(false); - - const location = useLocation(); - - useEffect(() => { - setMenuOpen(false); - }, [location]); - - const cacheConfig: InMemoryCacheConfig = { - typePolicies: { - statistics: { - keyFields: false, - }, - }, - }; - const layoutClasses = classnames( 'grid grid-rows-[auto_1fr_auto] grid-cols-[1fr] md:grid-rows-[auto_minmax(700px,_1fr)_auto] md:grid-cols-[300px_1fr]', 'min-h-[100vh] mx-auto my-0', 'border-neutral-700 dark:border-neutral-300 lg:border-l lg:border-r', 'bg-white dark:bg-black', 'antialiased text-black dark:text-white', - { - 'h-[100vh] min-h-auto overflow-hidden': menuOpen, - } + 'overflow-hidden relative' ); return ( - +
Mainnet sim 2 coming in March! @@ -68,8 +48,8 @@ function App() {
-
-
diff --git a/apps/explorer/src/app/components/dialogs/json-viewer-dialog.tsx b/apps/explorer/src/app/components/dialogs/json-viewer-dialog.tsx new file mode 100644 index 000000000..a3c032f4e --- /dev/null +++ b/apps/explorer/src/app/components/dialogs/json-viewer-dialog.tsx @@ -0,0 +1,56 @@ +import { t } from '@vegaprotocol/react-helpers'; +import { + Button, + Dialog, + Icon, + SyntaxHighlighter, +} from '@vegaprotocol/ui-toolkit'; + +type JsonViewerDialogProps = { + title: string; + content: unknown; + open: boolean; + onChange: (isOpen: boolean) => void; + trigger?: HTMLElement; +}; +export const JsonViewerDialog = ({ + title, + content, + open, + onChange, + trigger, +}: JsonViewerDialogProps) => { + return ( + } + open={open} + onChange={(isOpen) => onChange(isOpen)} + onCloseAutoFocus={(e) => { + /** + * This mimics radix's default behaviour that focuses the dialog's + * trigger after closing itself + */ + if (trigger) { + e.preventDefault(); + trigger.focus(); + } + }} + > +
+ +
+
+ +
+
+ ); +}; diff --git a/apps/explorer/src/app/components/header/header.spec.tsx b/apps/explorer/src/app/components/header/header.spec.tsx index 36c727f5f..b6a1cb184 100644 --- a/apps/explorer/src/app/components/header/header.spec.tsx +++ b/apps/explorer/src/app/components/header/header.spec.tsx @@ -14,7 +14,7 @@ jest.mock('../search', () => ({ const renderComponent = () => ( -
+
); diff --git a/apps/explorer/src/app/components/header/header.tsx b/apps/explorer/src/app/components/header/header.tsx index 4d0b3b1f1..67cc12488 100644 --- a/apps/explorer/src/app/components/header/header.tsx +++ b/apps/explorer/src/app/components/header/header.tsx @@ -4,15 +4,11 @@ import { ThemeSwitcher, Icon } from '@vegaprotocol/ui-toolkit'; import { t } from '@vegaprotocol/react-helpers'; import { Search } from '../search'; import { Routes } from '../../routes/route-names'; -import type { Dispatch, SetStateAction } from 'react'; import { NetworkSwitcher } from '@vegaprotocol/environment'; +import { useNavStore } from '../nav'; -interface ThemeToggleProps { - menuOpen: boolean; - setMenuOpen: Dispatch>; -} - -export const Header = ({ menuOpen, setMenuOpen }: ThemeToggleProps) => { +export const Header = () => { + const [open, toggle] = useNavStore((state) => [state.open, state.toggle]); const headerClasses = classnames( 'md:col-span-2', 'grid grid-rows-2 md:grid-rows-1 grid-cols-[1fr_auto] md:grid-cols-[auto_1fr_auto] items-center', @@ -36,9 +32,9 @@ export const Header = ({ menuOpen, setMenuOpen }: ThemeToggleProps) => { diff --git a/apps/explorer/src/app/components/main/index.tsx b/apps/explorer/src/app/components/main/index.tsx index 15fa742ad..022d5f98c 100644 --- a/apps/explorer/src/app/components/main/index.tsx +++ b/apps/explorer/src/app/components/main/index.tsx @@ -2,7 +2,7 @@ import { AppRouter } from '../../routes'; export const Main = () => { return ( -
+
); diff --git a/apps/explorer/src/app/components/markets/market-details.tsx b/apps/explorer/src/app/components/markets/market-details.tsx new file mode 100644 index 000000000..27fffab12 --- /dev/null +++ b/apps/explorer/src/app/components/markets/market-details.tsx @@ -0,0 +1,246 @@ +import { + addDecimalsFormatNumber, + formatNumberPercentage, + getMarketExpiryDateFormatted, + t, +} from '@vegaprotocol/react-helpers'; +import type { MarketInfoNoCandlesQuery } from '@vegaprotocol/market-info'; +import { MarketInfoTable } from '@vegaprotocol/market-info'; +import pick from 'lodash/pick'; +import { + MarketStateMapping, + MarketTradingModeMapping, +} from '@vegaprotocol/types'; +import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets'; +import { Splash } from '@vegaprotocol/ui-toolkit'; +import BigNumber from 'bignumber.js'; +import { useMemo } from 'react'; +import { Link } from 'react-router-dom'; + +export const MarketDetails = ({ + market, +}: { + market: MarketInfoNoCandlesQuery['market']; +}) => { + const assetSymbol = + market?.tradableInstrument.instrument.product?.settlementAsset.symbol; + const assetId = useMemo( + () => market?.tradableInstrument.instrument.product?.settlementAsset.id, + [market] + ); + const { data: asset } = useAssetDataProvider(assetId ?? ''); + + if (!market) return null; + + const keyDetails = { + ...pick(market, 'decimalPlaces', 'positionDecimalPlaces', 'tradingMode'), + state: MarketStateMapping[market.state], + }; + const assetDecimals = + market.tradableInstrument.instrument.product.settlementAsset.decimals; + + const panels = [ + { + title: t('Key details'), + content: ( + + ), + }, + { + title: t('Instrument'), + content: ( + + ), + }, + { + title: t('Settlement asset'), + content: asset ? ( + + ) : ( + {t('No data')} + ), + }, + { + title: t('Metadata'), + content: ( + { + const [key, value] = tag.split(':'); + return { [key]: value }; + }) + .reduce((acc, curr) => ({ ...acc, ...curr }), {}), + }} + /> + ), + }, + { + title: t('Risk model'), + content: ( + + ), + }, + { + title: t('Risk parameters'), + content: ( + + ), + }, + { + title: t('Risk factors'), + content: ( + + ), + }, + ...(market.priceMonitoringSettings?.parameters?.triggers || []).map( + (trigger, i) => ({ + title: t(`Price monitoring trigger ${i + 1}`), + content: , + }) + ), + ...(market.data?.priceMonitoringBounds || []).map((trigger, i) => ({ + title: t(`Price monitoring bound ${i + 1}`), + content: ( + <> + + + + ), + })), + { + title: t('Liquidity monitoring parameters'), + content: ( + + ), + }, + { + title: t('Liquidity price range'), + content: ( + + ), + }, + { + title: t('Oracle'), + content: ( + + + {t('View settlement data oracle specification')} + + + {t('View termination oracle specification')} + + + ), + }, + ]; + + return ( + <> + {panels.map((p) => ( +
+

{p.title}

+ {p.content} +
+ ))} + + ); +}; diff --git a/apps/explorer/src/app/components/markets/markets-table.tsx b/apps/explorer/src/app/components/markets/markets-table.tsx new file mode 100644 index 000000000..680cbaa61 --- /dev/null +++ b/apps/explorer/src/app/components/markets/markets-table.tsx @@ -0,0 +1,132 @@ +import type { MarketFieldsFragment } from '@vegaprotocol/market-list'; +import { t } from '@vegaprotocol/react-helpers'; +import type { + VegaICellRendererParams, + VegaValueGetterParams, +} from '@vegaprotocol/ui-toolkit'; +import { ButtonLink } from '@vegaprotocol/ui-toolkit'; +import type { AgGridReact } from 'ag-grid-react'; +import { AgGridColumn } from 'ag-grid-react'; +import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit'; +import { useRef, useLayoutEffect } from 'react'; +import { BREAKPOINT_MD } from '../../config/breakpoints'; +import { MarketStateMapping } from '@vegaprotocol/types'; +import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; +import type { RowClickedEvent } from 'ag-grid-community'; +import { Link, useNavigate } from 'react-router-dom'; + +type MarketsTableProps = { + data: MarketFieldsFragment[] | null; +}; +export const MarketsTable = ({ data }: MarketsTableProps) => { + const openAssetDetailsDialog = useAssetDetailsDialogStore( + (state) => state.open + ); + + const navigate = useNavigate(); + + const gridRef = useRef(null); + useLayoutEffect(() => { + const showColumnsOnDesktop = () => { + gridRef.current?.columnApi.setColumnsVisible( + ['id', 'state', 'asset'], + window.innerWidth > BREAKPOINT_MD + ); + }; + window.addEventListener('resize', showColumnsOnDesktop); + return () => { + window.removeEventListener('resize', showColumnsOnDesktop); + }; + }, []); + + return ( + data.id} + overlayNoRowsTemplate={t('This chain has no markets')} + domLayout="autoHeight" + defaultColDef={{ + flex: 1, + resizable: true, + sortable: true, + filter: true, + filterParams: { buttons: ['reset'] }, + autoHeight: true, + }} + suppressCellFocus={true} + onRowClicked={({ data, event }: RowClickedEvent) => { + if ((event?.target as HTMLElement).tagName.toUpperCase() !== 'BUTTON') { + navigate(data.id); + } + }} + > + + + ) => { + return data?.state ? MarketStateMapping[data?.state] : '-'; + }} + /> + ) => + value ? ( + { + openAssetDetailsDialog(value.id, e.target as HTMLElement); + }} + > + {value.symbol} + + ) : ( + '' + ) + } + /> + + ) => + value ? ( + + {t('View details')} + + ) : ( + '' + ) + } + /> + + ); +}; diff --git a/apps/explorer/src/app/components/nav/index.tsx b/apps/explorer/src/app/components/nav/index.tsx index 0b2c88330..38d674589 100644 --- a/apps/explorer/src/app/components/nav/index.tsx +++ b/apps/explorer/src/app/components/nav/index.tsx @@ -1,45 +1 @@ -import { NavLink } from 'react-router-dom'; -import routerConfig from '../../routes/router-config'; -import classnames from 'classnames'; - -interface NavProps { - menuOpen: boolean; -} - -export const Nav = ({ menuOpen }: NavProps) => { - return ( - - ); -}; +export * from './nav'; diff --git a/apps/explorer/src/app/components/nav/nav.tsx b/apps/explorer/src/app/components/nav/nav.tsx new file mode 100644 index 000000000..8b511e9a2 --- /dev/null +++ b/apps/explorer/src/app/components/nav/nav.tsx @@ -0,0 +1,181 @@ +import { NavLink, useLocation } from 'react-router-dom'; +import type { Navigable } from '../../routes/router-config'; +import routerConfig from '../../routes/router-config'; +import classnames from 'classnames'; +import { create } from 'zustand'; +import { + useCallback, + useEffect, + useLayoutEffect, + useMemo, + useRef, +} from 'react'; +import { Icon } from '@vegaprotocol/ui-toolkit'; +import first from 'lodash/first'; +import last from 'lodash/last'; +import { BREAKPOINT_MD } from '../../config/breakpoints'; + +type NavStore = { + open: boolean; + toggle: () => void; + hide: () => void; +}; + +export const useNavStore = create((set, get) => ({ + open: false, + toggle: () => set({ open: !get().open }), + hide: () => set({ open: false }), +})); + +const NavLinks = ({ links }: { links: Navigable[] }) => { + const navLinks = links.map((r) => ( +
  • + + classnames( + 'block mb-2 px-2', + 'text-lg hover:bg-vega-pink dark:hover:bg-vega-yellow hover:text-white dark:hover:text-black', + { + 'bg-vega-pink text-white dark:bg-vega-yellow dark:text-black': + isActive, + } + ) + } + > + {r.text} + +
  • + )); + + return
      {navLinks}
    ; +}; + +export const Nav = () => { + const [open, hide] = useNavStore((state) => [state.open, state.hide]); + const location = useLocation(); + + const navRef = useRef(null); + const btnRef = useRef(null); + + const focusable = useMemo( + () => + navRef.current + ? [ + ...(navRef.current.querySelectorAll( + 'a, button' + ) as NodeListOf), + ] + : [], + // eslint-disable-next-line react-hooks/exhaustive-deps + [navRef.current] // do not remove `navRef.current` from deps + ); + + const closeNav = useCallback(() => { + hide(); + console.log(focusable); + focusable.forEach((fe) => + fe.setAttribute( + 'tabindex', + window.innerWidth > BREAKPOINT_MD ? '0' : '-1' + ) + ); + }, [focusable, hide]); + + // close navigation when location changes + useEffect(() => { + closeNav(); + }, [closeNav, location]); + + useLayoutEffect(() => { + if (open) { + focusable.forEach((fe) => fe.setAttribute('tabindex', '0')); + } + + document.body.style.overflow = open ? 'hidden' : ''; + const offset = + document.querySelector('header')?.getBoundingClientRect().top || 0; + if (navRef.current) { + navRef.current.style.height = `calc(100vh - ${offset}px)`; + } + + // focus current by default + if (navRef.current && open) { + (navRef.current.querySelector('a[aria-current]') as HTMLElement)?.focus(); + } + + const closeOnEsc = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + closeNav(); + } + }; + + // tabbing loop + const focusLast = (e: FocusEvent) => { + e.preventDefault(); + const isNavElement = + e.relatedTarget && navRef.current?.contains(e.relatedTarget as Node); + if (!isNavElement && open) { + last(focusable)?.focus(); + } + }; + const focusFirst = (e: FocusEvent) => { + e.preventDefault(); + const isNavElement = + e.relatedTarget && navRef.current?.contains(e.relatedTarget as Node); + if (!isNavElement && open) { + first(focusable)?.focus(); + } + }; + + const resetOnDesktop = () => { + focusable.forEach((fe) => + fe.setAttribute( + 'tabindex', + window.innerWidth > BREAKPOINT_MD ? '0' : '-1' + ) + ); + }; + + window.addEventListener('resize', resetOnDesktop); + + first(focusable)?.addEventListener('focusout', focusLast); + last(focusable)?.addEventListener('focusout', focusFirst); + + document.addEventListener('keydown', closeOnEsc); + return () => { + window.removeEventListener('resize', resetOnDesktop); + document.removeEventListener('keydown', closeOnEsc); + first(focusable)?.removeEventListener('focusout', focusLast); + last(focusable)?.removeEventListener('focusout', focusFirst); + }; + }, [closeNav, focusable, open]); + + return ( + + ); +}; diff --git a/apps/explorer/src/app/routes/markets/Markets.graphql b/apps/explorer/src/app/routes/markets/Markets.graphql deleted file mode 100644 index 89fd5e170..000000000 --- a/apps/explorer/src/app/routes/markets/Markets.graphql +++ /dev/null @@ -1,140 +0,0 @@ -query ExplorerMarkets { - marketsConnection { - edges { - node { - id - fees { - factors { - makerFee - infrastructureFee - liquidityFee - } - } - tradableInstrument { - instrument { - name - metadata { - tags - } - code - product { - ... on Future { - settlementAsset { - id - name - decimals - globalRewardPoolAccount { - balance - } - } - } - } - } - riskModel { - ... on LogNormalRiskModel { - tau - riskAversionParameter - params { - r - sigma - mu - } - } - ... on SimpleRiskModel { - params { - factorLong - factorShort - } - } - } - marginCalculator { - scalingFactors { - searchLevel - initialMargin - collateralRelease - } - } - } - decimalPlaces - openingAuction { - durationSecs - volume - } - priceMonitoringSettings { - parameters { - triggers { - horizonSecs - probability - auctionExtensionSecs - } - } - } - liquidityMonitoringParameters { - triggeringRatio - targetStakeParameters { - timeWindow - scalingFactor - } - } - tradingMode - state - proposal { - id - } - state - accountsConnection { - edges { - node { - asset { - id - name - } - balance - type - } - } - } - data { - markPrice - bestBidPrice - bestBidVolume - bestOfferPrice - bestOfferVolume - bestStaticBidPrice - bestStaticBidVolume - bestStaticOfferPrice - bestStaticOfferVolume - midPrice - staticMidPrice - timestamp - openInterest - auctionEnd - auctionStart - indicativePrice - indicativeVolume - trigger - extensionTrigger - targetStake - suppliedStake - priceMonitoringBounds { - minValidPrice - maxValidPrice - trigger { - auctionExtensionSecs - probability - } - referencePrice - } - marketValueProxy - liquidityProviderFeeShare { - party { - id - } - equityLikeShare - averageEntryValuation - } - } - } - } - } -} diff --git a/apps/explorer/src/app/routes/markets/__generated__/Markets.ts b/apps/explorer/src/app/routes/markets/__generated__/Markets.ts deleted file mode 100644 index 3ef1a43f3..000000000 --- a/apps/explorer/src/app/routes/markets/__generated__/Markets.ts +++ /dev/null @@ -1,180 +0,0 @@ -import * as Types from '@vegaprotocol/types'; - -import { gql } from '@apollo/client'; -import * as Apollo from '@apollo/client'; -const defaultOptions = {} as const; -export type ExplorerMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>; - - -export type ExplorerMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, tradingMode: Types.MarketTradingMode, state: Types.MarketState, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', id: string, name: string, decimals: number, globalRewardPoolAccount?: { __typename?: 'AccountBalance', balance: string } | null } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, proposal?: { __typename?: 'Proposal', id?: string | null } | null, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', balance: string, type: Types.AccountType, asset: { __typename?: 'Asset', id: string, name: string } } } | null> | null } | null, data?: { __typename?: 'MarketData', markPrice: string, bestBidPrice: string, bestBidVolume: string, bestOfferPrice: string, bestOfferVolume: string, bestStaticBidPrice: string, bestStaticBidVolume: string, bestStaticOfferPrice: string, bestStaticOfferVolume: string, midPrice: string, staticMidPrice: string, timestamp: any, openInterest: string, auctionEnd?: string | null, auctionStart?: string | null, indicativePrice: string, indicativeVolume: string, trigger: Types.AuctionTrigger, extensionTrigger: Types.AuctionTrigger, targetStake?: string | null, suppliedStake?: string | null, marketValueProxy: string, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', auctionExtensionSecs: number, probability: number } }> | null, liquidityProviderFeeShare?: Array<{ __typename?: 'LiquidityProviderFeeShare', equityLikeShare: string, averageEntryValuation: string, party: { __typename?: 'Party', id: string } }> | null } | null } }> } | null }; - - -export const ExplorerMarketsDocument = gql` - query ExplorerMarkets { - marketsConnection { - edges { - node { - id - fees { - factors { - makerFee - infrastructureFee - liquidityFee - } - } - tradableInstrument { - instrument { - name - metadata { - tags - } - code - product { - ... on Future { - settlementAsset { - id - name - decimals - globalRewardPoolAccount { - balance - } - } - } - } - } - riskModel { - ... on LogNormalRiskModel { - tau - riskAversionParameter - params { - r - sigma - mu - } - } - ... on SimpleRiskModel { - params { - factorLong - factorShort - } - } - } - marginCalculator { - scalingFactors { - searchLevel - initialMargin - collateralRelease - } - } - } - decimalPlaces - openingAuction { - durationSecs - volume - } - priceMonitoringSettings { - parameters { - triggers { - horizonSecs - probability - auctionExtensionSecs - } - } - } - liquidityMonitoringParameters { - triggeringRatio - targetStakeParameters { - timeWindow - scalingFactor - } - } - tradingMode - state - proposal { - id - } - state - accountsConnection { - edges { - node { - asset { - id - name - } - balance - type - } - } - } - data { - markPrice - bestBidPrice - bestBidVolume - bestOfferPrice - bestOfferVolume - bestStaticBidPrice - bestStaticBidVolume - bestStaticOfferPrice - bestStaticOfferVolume - midPrice - staticMidPrice - timestamp - openInterest - auctionEnd - auctionStart - indicativePrice - indicativeVolume - trigger - extensionTrigger - targetStake - suppliedStake - priceMonitoringBounds { - minValidPrice - maxValidPrice - trigger { - auctionExtensionSecs - probability - } - referencePrice - } - marketValueProxy - liquidityProviderFeeShare { - party { - id - } - equityLikeShare - averageEntryValuation - } - } - } - } - } -} - `; - -/** - * __useExplorerMarketsQuery__ - * - * To run a query within a React component, call `useExplorerMarketsQuery` and pass it any options that fit your needs. - * When your component renders, `useExplorerMarketsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useExplorerMarketsQuery({ - * variables: { - * }, - * }); - */ -export function useExplorerMarketsQuery(baseOptions?: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(ExplorerMarketsDocument, options); - } -export function useExplorerMarketsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(ExplorerMarketsDocument, options); - } -export type ExplorerMarketsQueryHookResult = ReturnType; -export type ExplorerMarketsLazyQueryHookResult = ReturnType; -export type ExplorerMarketsQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/apps/explorer/src/app/routes/markets/index.spec.tsx b/apps/explorer/src/app/routes/markets/index.spec.tsx deleted file mode 100644 index 7786261a6..000000000 --- a/apps/explorer/src/app/routes/markets/index.spec.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { MockedProvider } from '@apollo/client/testing'; -import { render } from '@testing-library/react'; -import { MemoryRouter } from 'react-router-dom'; -import Markets from './index'; -import type { MockedResponse } from '@apollo/client/testing'; -import { ExplorerMarketsDocument } from './__generated__/Markets'; - -function renderComponent(mock: MockedResponse[]) { - return ( - - - - - - ); -} - -describe('Markets index', () => { - it('Renders loader when loading', async () => { - const mock = { - request: { - query: ExplorerMarketsDocument, - }, - result: { - data: { - marketsConnection: [], - }, - }, - }; - const res = render(renderComponent([mock])); - expect(await res.findByTestId('loader')).toBeInTheDocument(); - }); - - it('Renders EmptyList when loading completes and there are no results', async () => { - const mock = { - request: { - query: ExplorerMarketsDocument, - }, - result: { - data: { - marketsConnection: [], - }, - }, - }; - const res = render(renderComponent([mock])); - expect(await res.findByTestId('emptylist')).toBeInTheDocument(); - }); -}); diff --git a/apps/explorer/src/app/routes/markets/index.tsx b/apps/explorer/src/app/routes/markets/index.tsx index 3c87feb68..23da6fdc1 100644 --- a/apps/explorer/src/app/routes/markets/index.tsx +++ b/apps/explorer/src/app/routes/markets/index.tsx @@ -1,44 +1,2 @@ -import React from 'react'; -import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit'; -import { RouteTitle } from '../../components/route-title'; -import { SubHeading } from '../../components/sub-heading'; -import { t } from '@vegaprotocol/react-helpers'; -import { useExplorerMarketsQuery } from './__generated__/Markets'; -import { useScrollToLocation } from '../../hooks/scroll-to-location'; -import { useDocumentTitle } from '../../hooks/use-document-title'; -import EmptyList from '../../components/empty-list/empty-list'; - -const Markets = () => { - const { data, loading } = useExplorerMarketsQuery(); - - useScrollToLocation(); - useDocumentTitle(['Markets']); - - const m = data?.marketsConnection?.edges; - - return ( -
    - {t('Markets')} - - {m ? ( - m.map((e) => ( - - - {e.node.tradableInstrument.instrument.name} - - - - )) - ) : loading ? ( - - ) : ( - - )} -
    - ); -}; - -export default Markets; +export * from './markets-page'; +export * from './market-page'; diff --git a/apps/explorer/src/app/routes/markets/market-page.tsx b/apps/explorer/src/app/routes/markets/market-page.tsx new file mode 100644 index 000000000..7c90251e5 --- /dev/null +++ b/apps/explorer/src/app/routes/markets/market-page.tsx @@ -0,0 +1,68 @@ +import { t, useDataProvider } from '@vegaprotocol/react-helpers'; +import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit'; +import { useMemo, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { MarketDetails } from '../../components/markets/market-details'; +import { RouteTitle } from '../../components/route-title'; +import { useScrollToLocation } from '../../hooks/scroll-to-location'; +import { useDocumentTitle } from '../../hooks/use-document-title'; +import compact from 'lodash/compact'; +import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog'; +import { marketInfoNoCandlesDataProvider } from '@vegaprotocol/market-info'; + +export const MarketPage = () => { + useScrollToLocation(); + + const { marketId } = useParams<{ marketId: string }>(); + + const variables = useMemo( + () => ({ + marketId, + }), + [marketId] + ); + + const { data, loading, error } = useDataProvider({ + dataProvider: marketInfoNoCandlesDataProvider, + skipUpdates: true, + variables, + }); + + useDocumentTitle( + compact([ + 'Market details', + data?.market?.tradableInstrument.instrument.name, + ]) + ); + + const [dialogOpen, setDialogOpen] = useState(false); + + return ( + <> +
    + + {data?.market?.tradableInstrument.instrument.name} + + +
    + +
    + +
    +
    + setDialogOpen(isOpen)} + title={data?.market?.tradableInstrument.instrument.name || ''} + content={data?.market} + /> + + ); +}; diff --git a/apps/explorer/src/app/routes/markets/markets-page.tsx b/apps/explorer/src/app/routes/markets/markets-page.tsx new file mode 100644 index 000000000..04033c957 --- /dev/null +++ b/apps/explorer/src/app/routes/markets/markets-page.tsx @@ -0,0 +1,31 @@ +import { useScrollToLocation } from '../../hooks/scroll-to-location'; +import { useDocumentTitle } from '../../hooks/use-document-title'; +import { marketsProvider } from '@vegaprotocol/market-list'; +import { RouteTitle } from '../../components/route-title'; +import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; +import { t, useDataProvider } from '@vegaprotocol/react-helpers'; +import { MarketsTable } from '../../components/markets/markets-table'; + +export const MarketsPage = () => { + useDocumentTitle(['Markets']); + useScrollToLocation(); + + const { data, loading, error } = useDataProvider({ + dataProvider: marketsProvider, + skipUpdates: true, + }); + + return ( +
    + {t('Markets')} + + + +
    + ); +}; diff --git a/apps/explorer/src/app/routes/router-config.tsx b/apps/explorer/src/app/routes/router-config.tsx index b55b5bdae..aa6fdb1e1 100644 --- a/apps/explorer/src/app/routes/router-config.tsx +++ b/apps/explorer/src/app/routes/router-config.tsx @@ -2,7 +2,6 @@ import { Assets } from './assets'; import BlockPage from './blocks'; import Governance from './governance'; import Home from './home'; -import Markets from './markets'; import OraclePage from './oracles'; import Oracles from './oracles/home'; import { Oracle } from './oracles/id'; @@ -21,8 +20,13 @@ import flags from '../config/flags'; import { t } from '@vegaprotocol/react-helpers'; import { Routes } from './route-names'; import { NetworkParameters } from './network-parameters'; +import type { RouteObject } from 'react-router-dom'; +import { MarketPage, MarketsPage } from './markets'; -const partiesRoutes = flags.parties +export type Navigable = { path: string; name: string; text: string }; +type Route = RouteObject & Navigable; + +const partiesRoutes: Route[] = flags.parties ? [ { path: Routes.PARTIES, @@ -43,7 +47,7 @@ const partiesRoutes = flags.parties ] : []; -const assetsRoutes = flags.assets +const assetsRoutes: Route[] = flags.assets ? [ { path: Routes.ASSETS, @@ -54,7 +58,7 @@ const assetsRoutes = flags.assets ] : []; -const genesisRoutes = flags.genesis +const genesisRoutes: Route[] = flags.genesis ? [ { path: Routes.GENESIS, @@ -65,7 +69,7 @@ const genesisRoutes = flags.genesis ] : []; -const governanceRoutes = flags.governance +const governanceRoutes: Route[] = flags.governance ? [ { path: Routes.GOVERNANCE, @@ -76,18 +80,27 @@ const governanceRoutes = flags.governance ] : []; -const marketsRoutes = flags.markets +const marketsRoutes: Route[] = flags.markets ? [ { path: Routes.MARKETS, name: 'Markets', text: t('Markets'), - element: , + children: [ + { + index: true, + element: , + }, + { + path: ':marketId', + element: , + }, + ], }, ] : []; -const networkParametersRoutes = flags.networkParameters +const networkParametersRoutes: Route[] = flags.networkParameters ? [ { path: Routes.NETWORK_PARAMETERS, @@ -97,7 +110,7 @@ const networkParametersRoutes = flags.networkParameters }, ] : []; -const validators = flags.validators +const validators: Route[] = flags.validators ? [ { path: Routes.VALIDATORS, @@ -108,7 +121,7 @@ const validators = flags.validators ] : []; -const routerConfig = [ +const routerConfig: Route[] = [ { path: Routes.HOME, name: 'Home', diff --git a/libs/apollo-client/src/cache-config.ts b/libs/apollo-client/src/cache-config.ts new file mode 100644 index 000000000..789d2096a --- /dev/null +++ b/libs/apollo-client/src/cache-config.ts @@ -0,0 +1,52 @@ +import type { InMemoryCacheConfig } from '@apollo/client'; + +export const DEFAULT_CACHE_CONFIG: InMemoryCacheConfig = { + typePolicies: { + Account: { + keyFields: false, + fields: { + balanceFormatted: {}, + }, + }, + Instrument: { + keyFields: false, + }, + TradableInstrument: { + keyFields: ['instrument'], + }, + Product: { + keyFields: ['settlementAsset', ['id']], + }, + MarketData: { + keyFields: ['market', ['id']], + }, + Node: { + keyFields: false, + }, + Withdrawal: { + fields: { + pendingOnForeignChain: { + read: (isPending = false) => isPending, + }, + }, + }, + ERC20: { + keyFields: ['contractAddress'], + }, + PositionUpdate: { + keyFields: false, + }, + AccountUpdate: { + keyFields: false, + }, + Party: { + keyFields: false, + }, + Fees: { + keyFields: false, + }, + statistics: { + keyFields: false, + }, + }, +}; diff --git a/libs/apollo-client/src/index.ts b/libs/apollo-client/src/index.ts index bc8a1d463..867adb98a 100644 --- a/libs/apollo-client/src/index.ts +++ b/libs/apollo-client/src/index.ts @@ -1 +1,2 @@ export * from './lib/apollo-client'; +export * from './cache-config'; diff --git a/libs/market-info/src/components/market-info/MarketInfoNoCandles.graphql b/libs/market-info/src/components/market-info/MarketInfoNoCandles.graphql new file mode 100644 index 000000000..179ed4b53 --- /dev/null +++ b/libs/market-info/src/components/market-info/MarketInfoNoCandles.graphql @@ -0,0 +1,147 @@ +query MarketInfoNoCandles($marketId: ID!) { + market(id: $marketId) { + id + decimalPlaces + positionDecimalPlaces + state + tradingMode + lpPriceRange + proposal { + id + rationale { + title + description + } + } + marketTimestamps { + open + close + } + openingAuction { + durationSecs + volume + } + accountsConnection { + edges { + node { + type + asset { + id + } + balance + } + } + } + tradingMode + fees { + factors { + makerFee + infrastructureFee + liquidityFee + } + } + priceMonitoringSettings { + parameters { + triggers { + horizonSecs + probability + auctionExtensionSecs + } + } + } + riskFactors { + market + short + long + } + data { + market { + id + } + markPrice + midPrice + bestBidVolume + bestOfferVolume + bestStaticBidVolume + bestStaticOfferVolume + bestBidPrice + bestOfferPrice + trigger + openInterest + suppliedStake + openInterest + targetStake + marketValueProxy + priceMonitoringBounds { + minValidPrice + maxValidPrice + trigger { + horizonSecs + probability + auctionExtensionSecs + } + referencePrice + } + } + liquidityMonitoringParameters { + triggeringRatio + targetStakeParameters { + timeWindow + scalingFactor + } + } + tradableInstrument { + instrument { + id + name + code + metadata { + tags + } + product { + ... on Future { + quoteName + settlementAsset { + id + symbol + name + decimals + } + dataSourceSpecForSettlementData { + id + } + dataSourceSpecForTradingTermination { + id + } + dataSourceSpecBinding { + settlementDataProperty + tradingTerminationProperty + } + } + } + } + riskModel { + ... on LogNormalRiskModel { + tau + riskAversionParameter + params { + r + sigma + mu + } + } + ... on SimpleRiskModel { + params { + factorLong + factorShort + } + } + } + } + depth { + lastTrade { + price + } + } + } +} diff --git a/libs/market-info/src/components/market-info/__generated__/MarketInfoNoCandles.ts b/libs/market-info/src/components/market-info/__generated__/MarketInfoNoCandles.ts new file mode 100644 index 000000000..e2d7e2c52 --- /dev/null +++ b/libs/market-info/src/components/market-info/__generated__/MarketInfoNoCandles.ts @@ -0,0 +1,190 @@ +import * as Types from '@vegaprotocol/types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type MarketInfoNoCandlesQueryVariables = Types.Exact<{ + marketId: Types.Scalars['ID']; +}>; + + +export type MarketInfoNoCandlesQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, data?: { __typename?: 'MarketData', markPrice: string, midPrice: string, bestBidVolume: string, bestOfferVolume: string, bestStaticBidVolume: string, bestStaticOfferVolume: string, bestBidPrice: string, bestOfferPrice: string, trigger: Types.AuctionTrigger, openInterest: string, suppliedStake?: string | null, targetStake?: string | null, marketValueProxy: string, market: { __typename?: 'Market', id: string }, priceMonitoringBounds?: Array<{ __typename?: 'PriceMonitoringBounds', minValidPrice: string, maxValidPrice: string, referencePrice: string, trigger: { __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number } }> | null } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } } }, depth: { __typename?: 'MarketDepth', lastTrade?: { __typename?: 'Trade', price: string } | null } } | null }; + + +export const MarketInfoNoCandlesDocument = gql` + query MarketInfoNoCandles($marketId: ID!) { + market(id: $marketId) { + id + decimalPlaces + positionDecimalPlaces + state + tradingMode + lpPriceRange + proposal { + id + rationale { + title + description + } + } + marketTimestamps { + open + close + } + openingAuction { + durationSecs + volume + } + accountsConnection { + edges { + node { + type + asset { + id + } + balance + } + } + } + tradingMode + fees { + factors { + makerFee + infrastructureFee + liquidityFee + } + } + priceMonitoringSettings { + parameters { + triggers { + horizonSecs + probability + auctionExtensionSecs + } + } + } + riskFactors { + market + short + long + } + data { + market { + id + } + markPrice + midPrice + bestBidVolume + bestOfferVolume + bestStaticBidVolume + bestStaticOfferVolume + bestBidPrice + bestOfferPrice + trigger + openInterest + suppliedStake + openInterest + targetStake + marketValueProxy + priceMonitoringBounds { + minValidPrice + maxValidPrice + trigger { + horizonSecs + probability + auctionExtensionSecs + } + referencePrice + } + } + liquidityMonitoringParameters { + triggeringRatio + targetStakeParameters { + timeWindow + scalingFactor + } + } + tradableInstrument { + instrument { + id + name + code + metadata { + tags + } + product { + ... on Future { + quoteName + settlementAsset { + id + symbol + name + decimals + } + dataSourceSpecForSettlementData { + id + } + dataSourceSpecForTradingTermination { + id + } + dataSourceSpecBinding { + settlementDataProperty + tradingTerminationProperty + } + } + } + } + riskModel { + ... on LogNormalRiskModel { + tau + riskAversionParameter + params { + r + sigma + mu + } + } + ... on SimpleRiskModel { + params { + factorLong + factorShort + } + } + } + } + depth { + lastTrade { + price + } + } + } +} + `; + +/** + * __useMarketInfoNoCandlesQuery__ + * + * To run a query within a React component, call `useMarketInfoNoCandlesQuery` and pass it any options that fit your needs. + * When your component renders, `useMarketInfoNoCandlesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useMarketInfoNoCandlesQuery({ + * variables: { + * marketId: // value for 'marketId' + * }, + * }); + */ +export function useMarketInfoNoCandlesQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(MarketInfoNoCandlesDocument, options); + } +export function useMarketInfoNoCandlesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(MarketInfoNoCandlesDocument, options); + } +export type MarketInfoNoCandlesQueryHookResult = ReturnType; +export type MarketInfoNoCandlesLazyQueryHookResult = ReturnType; +export type MarketInfoNoCandlesQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/libs/market-info/src/components/market-info/index.ts b/libs/market-info/src/components/market-info/index.ts index 6144ab2bb..261a271ec 100644 --- a/libs/market-info/src/components/market-info/index.ts +++ b/libs/market-info/src/components/market-info/index.ts @@ -2,3 +2,5 @@ export * from './info-key-value-table'; export * from './info-market'; export * from './tooltip-mapping'; export * from './__generated__/MarketInfo'; +export * from './__generated__/MarketInfoNoCandles'; +export * from './market-info-data-provider'; diff --git a/libs/market-info/src/components/market-info/info-key-value-table.tsx b/libs/market-info/src/components/market-info/info-key-value-table.tsx index 14cc16f71..fc41af6a5 100644 --- a/libs/market-info/src/components/market-info/info-key-value-table.tsx +++ b/libs/market-info/src/components/market-info/info-key-value-table.tsx @@ -22,6 +22,7 @@ interface RowProps { asPercentage?: boolean; unformatted?: boolean; assetSymbol?: string; + noBorder?: boolean; } const Row = ({ @@ -31,6 +32,7 @@ const Row = ({ asPercentage, unformatted, assetSymbol = '', + noBorder = true, }: RowProps) => { const className = 'text-black dark:text-white text-sm !px-0'; @@ -55,7 +57,7 @@ const Row = ({ @@ -75,6 +77,7 @@ export interface MarketInfoTableProps { omits?: string[]; children?: ReactNode; assetSymbol?: string; + noBorder?: boolean; } export const MarketInfoTable = ({ @@ -85,6 +88,7 @@ export const MarketInfoTable = ({ omits = ['__typename'], children, assetSymbol, + noBorder, }: MarketInfoTableProps) => { if (!data || typeof data !== 'object') { return null; @@ -103,6 +107,7 @@ export const MarketInfoTable = ({ assetSymbol={assetSymbol} asPercentage={asPercentage} unformatted={unformatted || key.toLowerCase().includes('volume')} + noBorder={noBorder} /> ))} diff --git a/libs/market-info/src/components/market-info/market-info-data-provider.ts b/libs/market-info/src/components/market-info/market-info-data-provider.ts index ae477fa44..87cbcf7b1 100644 --- a/libs/market-info/src/components/market-info/market-info-data-provider.ts +++ b/libs/market-info/src/components/market-info/market-info-data-provider.ts @@ -1,6 +1,8 @@ import { makeDataProvider } from '@vegaprotocol/react-helpers'; import type { MarketInfoQuery } from './__generated__/MarketInfo'; import { MarketInfoDocument } from './__generated__/MarketInfo'; +import type { MarketInfoNoCandlesQuery } from './__generated__/MarketInfoNoCandles'; +import { MarketInfoNoCandlesDocument } from './__generated__/MarketInfoNoCandles'; export const marketInfoDataProvider = makeDataProvider< MarketInfoQuery, @@ -11,3 +13,13 @@ export const marketInfoDataProvider = makeDataProvider< query: MarketInfoDocument, getData: (responseData: MarketInfoQuery | null) => responseData, }); + +export const marketInfoNoCandlesDataProvider = makeDataProvider< + MarketInfoNoCandlesQuery, + MarketInfoNoCandlesQuery, + never, + never +>({ + query: MarketInfoNoCandlesDocument, + getData: (responseData: MarketInfoNoCandlesQuery | null) => responseData, +});