Compare commits

..
Author SHA1 Message Date
asiaznik b0a97c3fde chore(ui-tookit): notification component redesign 2023-07-18 18:28:26 +02:00
Bartłomiej Głownia 4af2ad9a13 feat(ui-toolkit): add trading button component 2023-07-18 18:28:18 +02:00
Matthew Russell 10f35a317b chore: fix lint 2023-07-18 13:23:04 +01:00
Matthew Russell 1d4ccf590d chore: remove commented out code 2023-07-18 13:16:13 +01:00
Matthew Russell abd2768110 feat: market selector changes 2023-07-18 13:16:13 +01:00
Matthew Russell f474672d03 fix: move default bg and text classes to body tag 2023-07-18 13:16:13 +01:00
Matthew Russell 97d34fd660 fix: add volume separator border 2023-07-18 13:16:13 +01:00
Matthew Russell 7e5b9d58bb fix: dropdown-menu 2023-07-18 13:16:13 +01:00
Matthew Russell 0636564a28 chore: move settings to bottom 2023-07-18 13:16:13 +01:00
Matthew Russell aee48cac31 chore: tweak dark grayscale 2023-07-18 13:16:13 +01:00
Matthew Russell a8463d1e32 chore: adjust cdark colors 2023-07-18 13:16:13 +01:00
Matthew Russell 56dd5062e1 test: fix settings test 2023-07-18 13:16:13 +01:00
Matthew Russell 9355f3b672 chore: re add init flag 2023-07-18 13:16:13 +01:00
Matthew Russell df757dc7a3 chore: make select only have additional padding to accomodate dropdown chevron 2023-07-18 13:16:13 +01:00
Matthew Russell b8fa6ff783 chore: fix test after removal of side bar init flag 2023-07-18 13:16:13 +01:00
Matthew Russell 198cbd98c0 feat: remove init flag in sidebar store, use persist 2023-07-18 13:16:13 +01:00
Matthew Russell 98b244d1fc chore: fix build-spec 2023-07-18 13:16:13 +01:00
Matthew Russell 50e845c3ac fix: transfer tests 2023-07-18 13:16:13 +01:00
Matthew Russell 4d94c0a8de chore: lint 2023-07-18 13:16:13 +01:00
Matthew Russell 1bd9fbbc36 feat: reduce size of chevron dropdown 2023-07-18 13:16:13 +01:00
Matthew Russell 87d15bbaa0 feat: adjust separators in header and sidebar 2023-07-18 13:16:13 +01:00
Matthew Russell e44d205c7f feat: adjust sidebar layout 2023-07-18 13:16:13 +01:00
Matthew Russell ecf705ddb2 chore: reduce dropdown spacing 2023-07-18 13:16:13 +01:00
Matthew Russell d4398a9e99 chore: fix long text in default select element 2023-07-18 13:16:13 +01:00
Matthew Russell 99db852687 feat: adjust padding and add same scroll element for all sidebar children 2023-07-18 13:16:12 +01:00
Matthew Russell 582f9757f4 chore: remove stray apostrophe in render 2023-07-18 13:07:34 +01:00
Matthew Russell 59f20f6d4d feat: align nav dropdowns 2023-07-18 13:07:34 +01:00
Matthew Russell e8a3f9fdc7 feat: reduce tab font size 2023-07-18 13:07:34 +01:00
Matthew Russell 64568777f5 chore: disable some tests add wait 2023-07-18 13:07:33 +01:00
Matthew Russell 37b03d884e test: disable flakey trades test and move mocks for transfer test 2023-07-18 13:07:14 +01:00
Matthew Russell 58d91f231e chore: tidy up dstylesheet 2023-07-18 13:07:14 +01:00
Matthew Russell 36d8a773aa chore: lint 2023-07-18 13:07:14 +01:00
Matthew Russell ca66dfc626 test: fix out of sync ethereum address with form 2023-07-18 13:07:14 +01:00
Matthew Russell 524a82cbf9 test: e2e test updates 2023-07-18 13:07:14 +01:00
Matthew Russell 1af9bb900b chore: lint 2023-07-18 13:07:14 +01:00
Matthew Russell 719ffbbd05 test: fix node health tests 2023-07-18 13:07:14 +01:00
Matthew Russell 3c88cf48ee test: updates to ensure e2e tests run 2023-07-18 13:07:13 +01:00
Matthew Russell 19ea8ff25f feat: ensure floating buttons stay within their container 2023-07-18 13:06:25 +01:00
Matthew Russell 3b76a95fbf chore: lint 2023-07-18 13:06:25 +01:00
Matthew Russell 8060e068a3 fix: ts errors in test files 2023-07-18 13:06:25 +01:00
Matthew Russell 7cf3eac6f8 chore: tidy up 2023-07-18 13:06:25 +01:00
Matthew Russell f415523240 test: add unit test for sidebar components 2023-07-18 13:06:25 +01:00
Matthew Russell 2405d1d6e1 feat: re org layout so that all pages get navbar 2023-07-18 13:06:25 +01:00
Matthew Russell 3233651178 feat: update to use links from router config 2023-07-18 13:06:25 +01:00
Matthew Russell 3c8d735a1c fix: remove padding from toast container so no clash with indicator 2023-07-18 13:06:25 +01:00
Matthew Russell 3d2463c18d feat: add 0 delay duration for side bar buttons 2023-07-18 13:06:25 +01:00
Matthew Russell 4da20fd502 feat: add ticket icon 2023-07-18 13:06:25 +01:00
Matthew Russell 41240ff640 feat: spacing for liquidity and portfolio pages 2023-07-18 13:06:25 +01:00
Matthew Russell 97951cd6fb feat: move settings cog to top 2023-07-18 13:06:25 +01:00
Matthew Russell f311bedf5e feat: move sidebar to right 2023-07-18 13:06:25 +01:00
Matthew Russell 5246b7bace feat: adjust table overlay bg color 2023-07-18 13:06:25 +01:00
Matthew Russell b29ea07dfa feat: adjust market selector 2023-07-18 13:06:25 +01:00
Matthew Russell a154ac1b2d feat: adjust padding of sidebar content 2023-07-18 13:06:25 +01:00
Matthew Russell 86fd0d7fe6 feat: add notification for transfer form 2023-07-18 13:06:25 +01:00
Matthew Russell e49fe6014c feat: make sidebar open by default for trading/portfolio 2023-07-18 13:06:25 +01:00
Matthew Russell 15398cc23f feat: add titles to some sidebars 2023-07-18 13:06:25 +01:00
Matthew Russell 9a24d6437a feat: tidy up dialogs 2023-07-18 13:06:25 +01:00
Matthew Russell 5e3ea30046 chore: adjust layout to work for mobile 2023-07-18 13:06:25 +01:00
Matthew Russell f011bcb543 feat: add rounded border for panes 2023-07-18 13:06:25 +01:00
Matthew Russell 16937ecacf feat: adjust tab indicator 2023-07-18 13:06:25 +01:00
Matthew Russell 31116a9cd6 feat: add console tooltip for sidebar 2023-07-18 13:06:25 +01:00
Matthew Russell 75a5c9649a feat: update colors 2023-07-18 13:06:25 +01:00
Matthew Russell 9f0b3f0de6 feat: set default view for trading and portfolio page 2023-07-18 13:06:24 +01:00
Matthew Russell 3841fac411 test: fix market selector tests 2023-07-18 13:05:02 +01:00
Matthew Russell d96322a525 test: fix node health tests 2023-07-18 13:05:02 +01:00
Matthew Russell 0c238c4517 feat: adjust node health text size 2023-07-18 13:05:02 +01:00
Matthew Russell 1f3b683ebc feat: adjust settings page to work for sidebar 2023-07-18 13:05:02 +01:00
Matthew Russell f2fdeeee44 feat: adjust padding in sidebar, colors for chart controls 2023-07-18 13:05:02 +01:00
Matthew Russell 2ae477260b feat: make appName and therefore logo for navigation component optional 2023-07-18 13:05:02 +01:00
Matthew Russell 7162ac1303 chore: re add logo for now 2023-07-18 13:05:02 +01:00
Matthew Russell ccb23ca1a2 feat: adjust styles for small screens and add sidebar as overlay 2023-07-18 13:05:02 +01:00
Matthew Russell ad5d4801d1 feat: tidy up header styles 2023-07-18 13:05:02 +01:00
Matthew Russell 28b819b995 feat: add spacing to portfolio page 2023-07-18 13:05:02 +01:00
Matthew Russell 6b2b502498 feat: add market info 2023-07-18 13:05:02 +01:00
Matthew Russell 9aec3b9881 feat: adjust tabs and table styles add cog 2023-07-18 13:05:02 +01:00
Matthew Russell 28dbdf2951 chore: re add home to serve as redirect to default market 2023-07-18 13:05:02 +01:00
Matthew Russell 24a799a369 feat: adapt market-selector for popover 2023-07-18 13:05:02 +01:00
Matthew Russell 27fa8ebb9b chore: keep using dialogs along side sidebar for now 2023-07-18 13:05:02 +01:00
Matthew Russell 48f9f32fe9 feat: open transfer sidebar, adjust trade-grid 2023-07-18 13:05:02 +01:00
Matthew Russell 97ccc5cbd0 feat: refactor routing and add sidebar 2023-07-18 13:05:02 +01:00
122 changed files with 891 additions and 1626 deletions
+6 -6
View File
@@ -28,7 +28,7 @@ jobs:
echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELASE=false >> $GITHUB_ENV
- name: Is PR
if: ${{ github.event_name == 'pull_request' }}
@@ -51,9 +51,9 @@ jobs:
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
- name: Is S3 Release
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' }}
if: ${{ env.IS_IPFS_RELASE == 'false' && github.event_name == 'push' }}
run: |
echo IS_S3_RELEASE=true >> $GITHUB_ENV
echo IS_S3_RELASE=true >> $GITHUB_ENV
- name: Set up QEMU
id: quemu
@@ -251,7 +251,7 @@ jobs:
- name: Publish dist to s3
uses: jakejarvis/s3-sync-action@master
# s3 releases are not happening for trading on mainnet - it's IPFS
if: ${{ env.IS_S3_RELEASE == 'true' }}
if: ${{ env.IS_S3_RELASE == 'true' }}
with:
args: --acl private --follow-symlinks --delete
env:
@@ -262,11 +262,11 @@ jobs:
SOURCE_DIR: 'dist-result'
- name: Install aws CLI
if: ${{ env.IS_S3_RELEASE == 'true' }}
if: ${{ env.IS_S3_RELASE == 'true' }}
uses: unfor19/install-aws-cli-action@master
- name: Perform cache invalidation
if: ${{ env.IS_S3_RELEASE == 'true' }}
if: ${{ env.IS_S3_RELASE == 'true' }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
@@ -40,7 +40,7 @@ context.skip('Transactions page', function () {
.first()
.click({ force: true });
} else {
cy.log('Unable to find any transactions on page');
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
@@ -78,7 +78,7 @@ context.skip('Transactions page', function () {
}
});
} else {
cy.log('Unable to find any transactions on page');
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
@@ -137,7 +137,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Enactment date'),
field: 'terms.enactmentDatetime',
valueFormatter: ({
valueFormatte: ({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
@@ -1,52 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { components } from '../../../../../types/explorer';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
export interface TxDetailsOrderIcebergDetailsProps {
iceberg: components['schemas']['v1IcebergOpts'];
size: components['schemas']['v1OrderSubmission']['size'];
}
/**
.iceberg .sub {
vertical-align: bottom;
color: #954F01;
}
.iceberg .sup {
vertical-align: top;
color: #515E1E;
}
*/
/**
* Visualises the iceberg details of an order, showing the minimum visible size,
* the full size and the peak size.
*/
export const TxOrderIcebergDetails = ({
iceberg,
size,
}: TxDetailsOrderIcebergDetailsProps) => {
return (
<div
className="inline-block iceberg bg-vega-blue-300 border-vega-blue-350 border-[1px] text-[70%] font-mono py-[2px] pl-2 pr-3 height-[1em] leading-[1em] cursor-pointer"
style={{ clipPath: 'polygon(10% 0%, 100% 0%, 90% 100%, 0% 100%)' }}
>
<Tooltip description={t('Iceberg: Minimum visible size')}>
<span className="align-bottom text-vega-orange-650">
{iceberg.minimumVisibleSize || '-'}
</span>
</Tooltip>
<Tooltip description={t('Iceberg: Total size')}>
<span className="text-sm text-vega-blue-600 mx-3">{size}</span>
</Tooltip>
<Tooltip description={t('Iceberg: Visible peak')}>
<span className="align-top text-vega-yellow-600">
{iceberg.peakSize || '-'}
</span>
</Tooltip>
</div>
);
};
@@ -8,7 +8,6 @@ import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import DeterministicOrderDetails from '../../order-details/deterministic-order-details';
import Hash from '../../links/hash';
import { TxOrderPeggedReferenceRow } from './order/tx-order-peg';
import { TxOrderIcebergDetails } from './order/tx-order-iceberg';
interface TxDetailsOrderProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -33,8 +32,6 @@ export const TxDetailsOrder = ({
const marketId = txData.command.orderSubmission.marketId || '-';
const reference = txData.command.orderSubmission.peggedOrder;
const side = txData.command.orderSubmission.side;
const iceberg = txData.command.orderSubmission.icebergOpts;
const size = txData.command.orderSubmission.size;
let deterministicId = '';
@@ -77,14 +74,6 @@ export const TxDetailsOrder = ({
marketId={marketId}
/>
) : null}
{iceberg ? (
<TableRow modifier="bordered">
<TableCell>{t('Iceberg details')}</TableCell>
<TableCell>
<TxOrderIcebergDetails iceberg={iceberg} size={size} />
</TableCell>
</TableRow>
) : null}
</TableWithTbody>
{deterministicId.length > 0 ? (
@@ -166,7 +166,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
// This will get unwieldy and should probably produce a different colour of tag
// Note that colours are currently arbitrary
if (type === 'Chain Event' && !!command?.chainEvent) {
type = getLabelForChainEvent(command.chainEvent);
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
@@ -178,9 +177,6 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
type = getLabelForProposal(command.proposalSubmission);
}
colours = 'text-black bg-vega-yellow';
} else if (type === 'Order' && command) {
type = getLabelForOrderType(orderType, command);
colours = 'text-white dark-text-white bg-vega-blue dark:bg-vega-blue';
}
if (type === 'Vote on Proposal') {
@@ -1,5 +1,7 @@
import { Link, useParams } from 'react-router-dom';
import { DATA_SOURCES } from '../../../config';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import type { TendermintBlocksResponse } from '../tendermint-blocks-response';
import { RouteTitle } from '../../../components/route-title';
import { TimeAgo } from '../../../components/time-ago';
import {
@@ -12,7 +14,7 @@ import { TxsPerBlock } from '../../../components/txs/txs-per-block';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { Routes } from '../../route-names';
import { t } from '@vegaprotocol/i18n';
import { useBlockInfo } from '@vegaprotocol/tendermint';
import { useFetch } from '@vegaprotocol/react-helpers';
import { NodeLink } from '../../../components/links';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import EmptyList from '../../../components/empty-list/empty-list';
@@ -22,7 +24,10 @@ const Block = () => {
useDocumentTitle(['Blocks', `Block #${block}`]);
const {
state: { data: blockData, loading, error },
} = useBlockInfo(Number(block));
} = useFetch<TendermintBlocksResponse>(
`${DATA_SOURCES.tendermintUrl}/block?height=${block}`,
{ cache: 'force-cache' }
);
return (
<section>
-4
View File
@@ -18,10 +18,6 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
#Test configuration variables
CYPRESS_FAIRGROUND=false
LC_ALL="en_US.UTF-8"
-3
View File
@@ -19,9 +19,6 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
#Test configuration variables
CYPRESS_FAIRGROUND=false
-3
View File
@@ -13,6 +13,3 @@ NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
-3
View File
@@ -14,6 +14,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
-3
View File
@@ -13,6 +13,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
-3
View File
@@ -9,6 +9,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
-3
View File
@@ -14,6 +14,3 @@ NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
-3
View File
@@ -11,6 +11,3 @@ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
+1 -12
View File
@@ -18,10 +18,6 @@ import { VegaWallet } from '../vega-wallet';
import { useLocation, useMatch } from 'react-router-dom';
import { useEffect } from 'react';
import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog';
import {
ProtocolUpgradeCountdown,
ProtocolUpgradeCountdownMode,
} from '@vegaprotocol/proposals';
export const SettingsLink = () => {
const { open, isOpen, close } = useTelemetryDialog();
@@ -65,14 +61,7 @@ export const Nav = ({ theme }: Pick<NavigationProps, 'theme'>) => {
appName="Governance"
theme={theme}
breakpoints={[458, 959]}
actions={
<>
<SettingsLink />
<ProtocolUpgradeCountdown
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
</>
}
actions={<SettingsLink />}
>
<NavigationList
className="[.drawer-content_&]:border-b [.drawer-content_&]:border-b-vega-light-200 dark:[.drawer-content_&]:border-b-vega-dark-200 [.drawer-content_&]:pb-8 [.drawer-content_&]:mb-2"
@@ -4,11 +4,7 @@ import type { ReactNode } from 'react';
import { AnnouncementBanner } from '@vegaprotocol/announcements';
import { Nav } from '../nav';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import {
ProtocolUpgradeCountdownMode,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import React from 'react';
interface AppLayoutProps {
children: ReactNode;
@@ -35,23 +31,8 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
/>
)}
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
<NotificationsContainer />
</div>
<div className={AppLayoutClasses}>{children}</div>
</div>
);
};
const NotificationsContainer = () => {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<div data-testid="banners">
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
{isReadOnly ? (
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
) : null}
</div>
);
};
@@ -1,3 +1,5 @@
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react';
export interface TemplateSidebarProps {
@@ -6,18 +8,24 @@ export interface TemplateSidebarProps {
}
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
{children}
</main>
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
{sidebar.map((Component, i) => (
<section className="mb-4 last:mb-0" key={i}>
{Component}
</section>
))}
</aside>
</div>
<>
{isReadOnly ? (
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
) : null}
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
{children}
</main>
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
{sidebar.map((Component, i) => (
<section className="mb-4 last:mb-0" key={i}>
{Component}
</section>
))}
</aside>
</div>
</>
);
}
@@ -837,7 +837,5 @@
"RejectedProposals": "Rejected proposals",
"networkGovernance": "Network governance",
"networkUpgrades": "Network upgrades",
"assetSpecification": "Asset specification",
"Estimated time to upgrade": "Estimated time to upgrade",
"Upgraded at": "Upgraded at"
"assetSpecification": "Asset specification"
}
@@ -31,7 +31,6 @@ import {
orderByDate,
orderByUpgradeBlockHeight,
} from '../proposals/components/proposals-list/proposals-list';
import { BigNumber } from '../../lib/bignumber';
const nodesToShow = 6;
@@ -249,8 +248,6 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
const activeNodes = removePaginationWrapper(
validatorsData?.nodesConnection.edges
).filter((node) =>
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
);
const trimmedActiveNodes = activeNodes?.slice(0, nodesToShow);
@@ -4,20 +4,16 @@ import { render } from '@testing-library/react';
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
import { ProtocolUpgradeProposalDetailInfo } from './protocol-upgrade-proposal-detail-info';
const PROPOSAL = {
vegaReleaseTag: 'v0.1.234',
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
upgradeBlockHeight: '12345',
approvers: [],
};
const renderComponent = (lastBlockHeight?: string, time?: string) =>
const renderComponent = () =>
render(
<ProtocolUpgradeProposalDetailInfo
proposal={PROPOSAL}
lastBlockHeight={lastBlockHeight}
time={time}
proposal={{
vegaReleaseTag: 'v0.1.234',
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
upgradeBlockHeight: '12345',
approvers: [],
}}
/>
);
@@ -47,28 +43,4 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
'v0.1.234'
);
});
it('should display estimated time if proposal is still pending', () => {
const { getByTestId } = renderComponent('12', 'time');
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
'Estimated time to upgrade'
);
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
});
it('should display upgraded at if proposal is done', () => {
const { getByTestId } = renderComponent('123456', 'time');
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
'Upgraded at'
);
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
});
it('should not display time if none provided', () => {
const { queryByTestId } = renderComponent('123456', undefined);
expect(
queryByTestId('protocol-upgrade-time-label')
).not.toBeInTheDocument();
expect(queryByTestId('protocol-upgrade-time')).not.toBeInTheDocument();
});
});
@@ -5,33 +5,19 @@ import {
RoundedWrapper,
} from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import * as Types from '@vegaprotocol/types';
import type { ReactNode } from 'react';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
export interface ProtocolUpgradeProposalDetailInfoProps {
proposal: ProtocolUpgradeProposalFieldsFragment;
lastBlockHeight?: string;
time?: string | ReactNode;
}
export const ProtocolUpgradeProposalDetailInfo = ({
proposal,
lastBlockHeight,
time,
}: ProtocolUpgradeProposalDetailInfoProps) => {
const { t } = useTranslation();
const shouldShowUpgradeTime =
![
Types.ProtocolUpgradeProposalStatus
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
Types.ProtocolUpgradeProposalStatus
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
].includes(proposal.status) && time;
const pending = Number(proposal.upgradeBlockHeight) > Number(lastBlockHeight);
return (
<div className="mb-10">
<SubHeading title={t('proposal')} />
@@ -43,26 +29,13 @@ export const ProtocolUpgradeProposalDetailInfo = ({
<span data-testid="protocol-upgrade-block-height">
{proposal.upgradeBlockHeight}{' '}
{lastBlockHeight && (
<span className="text-vega-light-300 dark:text-vega-dark-300">
<>
({t('currently')} {lastBlockHeight})
</span>
</>
)}
</span>
</KeyValueTableRow>
{shouldShowUpgradeTime && (
<KeyValueTableRow>
<span
data-testid="protocol-upgrade-time-label"
className="uppercase"
>
{pending ? t('Estimated time to upgrade') : t('Upgraded at')}
</span>
<span data-testid="protocol-upgrade-time">{time}</span>
</KeyValueTableRow>
)}
<KeyValueTableRow>
<span className="uppercase">{t('state')}</span>
@@ -1,23 +1,14 @@
import { useEffect, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import {
convertToCountdownString,
formatDateWithLocalTimezone,
removePaginationWrapper,
stripFullStops,
} from '@vegaprotocol/utils';
import { removePaginationWrapper, stripFullStops } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import { ProtocolUpgradeProposal } from './protocol-upgrade-proposal';
import { ProposalNotFound } from '../components/proposal-not-found';
import { useNodesQuery } from '../../staking/home/__generated__/Nodes';
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import {
useProtocolUpgradeProposalsQuery,
useTimeToUpgrade,
} from '@vegaprotocol/proposals';
import { useBlockInfo } from '@vegaprotocol/tendermint';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
export const ProtocolUpgradeProposalContainer = () => {
const params = useParams<{ proposalReleaseTag: string }>();
@@ -67,20 +58,6 @@ export const ProtocolUpgradeProposalContainer = () => {
);
}, [nodesData]);
const pending =
Number(protocolUpgradeProposal?.upgradeBlockHeight) >
Number(data?.lastBlockHeight);
const {
state: { data: blockInfo },
} = useBlockInfo(
Number(protocolUpgradeProposal?.upgradeBlockHeight),
!pending
);
const time = useTimeToUpgrade(
Number(protocolUpgradeProposal?.upgradeBlockHeight)
);
return (
<AsyncRenderer
loading={loading || nodesLoading}
@@ -92,19 +69,6 @@ export const ProtocolUpgradeProposalContainer = () => {
proposal={protocolUpgradeProposal}
lastBlockHeight={data?.lastBlockHeight}
consensusValidators={consensusValidators}
time={
pending && time ? (
convertToCountdownString(time, '0:00:00:00')
) : blockInfo?.result ? (
<span title={blockInfo.result.block.header.time}>
{formatDateWithLocalTimezone(
new Date(blockInfo.result.block.header.time)
)}
</span>
) : (
'-'
)
}
/>
) : (
<ProposalNotFound />
@@ -1,11 +1,10 @@
import { useMemo } from 'react';
import type { ReactNode } from 'react';
import { ProtocolUpgradeProposalDetailHeader } from '../components/protocol-upgrade-proposal-detail-header';
import { ProtocolUpdateProposalDetailApprovals } from '../components/protocol-upgrade-proposal-detail-approvals';
import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrade-proposal-detail-info';
import { getNormalisedVotingPower } from '../../staking/shared';
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useVegaRelease } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
@@ -14,7 +13,6 @@ export interface ProtocolUpgradeProposalProps {
proposal: ProtocolUpgradeProposalFieldsFragment;
consensusValidators: NodesFragmentFragment[] | null;
lastBlockHeight?: string;
time?: string | ReactNode;
}
export const getConsensusApprovals = (
@@ -46,7 +44,6 @@ export const ProtocolUpgradeProposal = ({
proposal,
lastBlockHeight,
consensusValidators,
time,
}: ProtocolUpgradeProposalProps) => {
const { t } = useTranslation();
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
@@ -76,7 +73,6 @@ export const ProtocolUpgradeProposal = ({
<ProtocolUpgradeProposalDetailInfo
proposal={proposal}
lastBlockHeight={lastBlockHeight}
time={time}
/>
{consensusValidators && consensusApprovals && (
@@ -54,7 +54,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
it('can deposit', function () {
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
// 1001-DEPO-001
// 1001-DEPO-002
@@ -117,7 +117,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
it('can key to key transfers', function () {
// 1003-TRAN-023
// 1003-TRAN-006
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(collateralTab).click();
cy.getByTestId('open-transfer').click();
@@ -344,7 +344,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId('Withdrawals').click();
cy.getByTestId('withdraw-dialog-button').click();
@@ -431,7 +431,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
// 1001-DEPO-006
// 1001-DEPO-007
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId(depositsTab).click();
cy.getByTestId('deposit-button').click();
@@ -453,7 +453,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
// 1002-WITH-007
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]', txTimeout).should('exist');
cy.get('main[data-testid="/portfolio"]', txTimeout).should('exist');
cy.getByTestId(toastCloseBtn, txTimeout).click();
cy.getByTestId(depositsTab).click();
cy.getByTestId('deposit-button').click();
@@ -351,6 +351,12 @@ describe('Closed markets', { tags: '@smoke' }, () => {
)
);
// 6001-MARK-017
cy.get(rowSelector)
.first()
.find('[col-id="realisedPNL"]')
.should('have.text', '-');
// 6001-MARK-018
cy.get(rowSelector)
.first()
@@ -17,7 +17,7 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
cy.mockTradingPage();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Deposits').click();
cy.getByTestId('deposit-button').click();
connectEthereumWallet('MetaMask');
+1 -1
View File
@@ -106,7 +106,7 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.get('[data-testid^="pathname-/markets/"]');
cy.get('main[data-testid^="/markets/"]');
// the choose market overlay is no longer showing
cy.contains('Loading...').should('not.exist');
@@ -37,23 +37,27 @@ describe('markets selector', { tags: '@smoke' }, () => {
const data = [
{
code: 'SOLUSD',
markPrice: '84.41',
vol: '0.00',
markPrice: 'XYZalpha84.41',
change: '',
vol: '24h vol0.00',
},
{
code: 'ETHBTC.QM21',
markPrice: '46,126.90058',
vol: '0.00',
markPrice: 'tBTC46,126.90058',
change: '',
vol: '24h vol0.00',
},
{
code: 'BTCUSD.MF21',
markPrice: '46,126.90058',
vol: '0.00',
markPrice: 'tDAI46,126.90058',
change: '',
vol: '24h vol0.00',
},
{
code: 'AAPL.MF21',
markPrice: '46,126.90058',
vol: '0.00',
markPrice: 'tUSDC46,126.90058',
change: '',
vol: '24h vol0.00',
},
];
cy.getByTestId('header-title').should('be.visible').click();
@@ -65,12 +69,16 @@ describe('markets selector', { tags: '@smoke' }, () => {
// 6001-MARK-022
expect(item.find('h3').text()).equals(market.code);
expect(
item.find('[data-testid="market-selector-volume"]').text()
item.find('[data-testid="market-selector-data-row"]').eq(0).text()
).contains(market.vol);
// 6001-MARK-024
expect(
item.find('[data-testid="market-selector-price"]').text()
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
);
// 6001-MARK-025
expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist;
});
@@ -101,14 +101,13 @@ describe('order book', { tags: '@smoke' }, () => {
'1,000',
'10,000',
];
cy.getByTestId(priceResolution).click();
cy.get('[role="menu"]')
.find('[role="menuitem"]')
cy.getByTestId(priceResolution)
.find('option')
.each(($el, index) => {
expect($el.text()).to.equal(resolutions[index]);
});
cy.get('[role="menuitem"]').eq(4).click();
cy.getByTestId(priceResolution).select('0.0');
cy.getByTestId(resPrice).should('have.text', '99.0');
cy.getByTestId(askPrice).should('not.exist');
cy.getByTestId(bidPrice).should('not.exist');
@@ -14,12 +14,6 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
});
beforeEach(() => {
@@ -59,12 +59,6 @@ describe(
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
if (!$form.length) {
cy.getByTestId('Order').click();
}
});
});
it('should display info and button for deposit', () => {
@@ -37,7 +37,7 @@ describe('fills', { tags: '@regression' }, () => {
it('renders fills on portfolio page', () => {
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Fills').click();
validateFillsDisplayed();
});
@@ -10,7 +10,7 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId('Withdrawals').click();
});
@@ -17,7 +17,7 @@ describe(
cy.visit('/#/portfolio');
cy.mockTradingPage();
cy.mockSubscription();
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
});
it('can connect', () => {
@@ -122,7 +122,7 @@ describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.visit('/#/portfolio');
cy.mockTradingPage();
cy.mockSubscription();
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
cy.get('main[data-testid="/portfolio"]').should('exist');
});
it('can connect', () => {
@@ -1,5 +1,5 @@
import { selectAsset } from '../support/helpers';
// #region consts
const amountField = 'input[name="amount"]';
const amountShortName = 'input[name="amount"] + div + span.text-xs';
const assetSelection = 'select-asset';
@@ -26,6 +26,7 @@ const ASSET_SEPOLIA_TBTC = 2;
const collateralTab = 'Collateral';
const toastCloseBtn = 'toast-close';
const toastContent = 'toast-content';
// #endregion
describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
beforeEach(() => {
@@ -36,17 +37,17 @@ describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
cy.visit('/');
cy.wait('@Assets');
cy.wait('@Accounts');
cy.mockVegaWalletTransaction();
// Only click if not already active otherwise sidebar will close
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
if ($sidebarContent.find('h2').text() !== 'Transfer') {
cy.get('[data-testid="sidebar"] [data-testid="Transfer"]').click();
}
});
cy.wait('@Assets');
cy.wait('@Accounts');
cy.mockVegaWalletTransaction();
});
it('transfer fees tooltips', () => {
+137 -26
View File
@@ -1,10 +1,29 @@
import { matchFilter, lpAggregatedDataProvider } from '@vegaprotocol/liquidity';
import {
matchFilter,
lpAggregatedDataProvider,
useCheckLiquidityStatus,
} from '@vegaprotocol/liquidity';
import { tooltipMapping } from '@vegaprotocol/markets';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { Tab, Tabs } from '@vegaprotocol/ui-toolkit';
import { Tab, Tabs, Indicator, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useEffect, useState } from 'react';
import { memo, useEffect, useState } from 'react';
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
import { useParams } from 'react-router-dom';
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
import { DocsLinks } from '@vegaprotocol/environment';
import { LiquidityContainer } from '../../components/liquidity-container';
const enum LiquidityTabs {
@@ -19,6 +38,92 @@ export const Liquidity = () => {
return <LiquidityViewContainer marketId={marketId} />;
};
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
const targetStake = marketData?.targetStake;
const suppliedStake = marketData?.suppliedStake;
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const triggeringRatio =
params.market_liquidity_targetstake_triggering_ratio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
targetStake: targetStake || 0,
triggeringRatio,
});
return (
<Header
title={
market?.tradableInstrument.instrument.code &&
marketId && (
<HeaderTitle>
{market.tradableInstrument.instrument.code &&
t(
'%s liquidity provision',
market.tradableInstrument.instrument.code
)}
</HeaderTitle>
)
}
>
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
testId="target-stake"
>
<div>
{targetStake
? `${addDecimalsFormatNumber(
targetStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
testId="supplied-stake"
>
<div>
{suppliedStake
? `${addDecimalsFormatNumber(
suppliedStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat heading={t('Liquidity supplied')} testId="liquidity-supplied">
<Indicator variant={status} /> {formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')} testId="liquidity-market-id">
<div className="break-word">{marketId}</div>
</HeaderStat>
<HeaderStat heading={t('Learn more')} testId="liquidity-learn-more">
{DocsLinks ? (
<ExternalLink href={DocsLinks.LIQUIDITY}>
{t('Providing liquidity')}
</ExternalLink>
) : (
(null as React.ReactNode)
)}
</HeaderStat>
</Header>
);
});
LiquidityViewHeader.displayName = 'LiquidityViewHeader';
export const LiquidityViewContainer = ({
marketId,
}: {
@@ -49,29 +154,35 @@ export const LiquidityViewContainer = ({
}, [data, pubKey]);
return (
<div className="h-full p-1.5">
<div className="h-full border border-default">
<Tabs value={tab || LiquidityTabs.Active} onValueChange={setTab}>
<Tab
id={LiquidityTabs.MyLiquidityProvision}
name={t('My liquidity provision')}
hidden={!pubKey}
>
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
</Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}>
<LiquidityContainer marketId={marketId} filter={{ active: true }} />
</Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
</Tab>
</Tabs>
<div className="h-full grid grid-rows-[min-content_1fr]">
<LiquidityViewHeader marketId={marketId} />
<div className="p-1">
<div className="h-full border border-default">
<Tabs value={tab || LiquidityTabs.Active} onValueChange={setTab}>
<Tab
id={LiquidityTabs.MyLiquidityProvision}
name={t('My liquidity provision')}
hidden={!pubKey}
>
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
</Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}>
<LiquidityContainer
marketId={marketId}
filter={{ active: true }}
/>
</Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
</Tab>
</Tabs>
</div>
</div>
</div>
);
@@ -19,6 +19,7 @@ describe('AssetDropdown', () => {
checkedAssets={[]}
assets={assets}
onSelect={mockOnSelect}
onReset={jest.fn()}
/>
);
await userEvent.click(screen.getByRole('button'));
@@ -38,6 +39,7 @@ describe('AssetDropdown', () => {
checkedAssets={[assets[0].id]}
assets={assets}
onSelect={mockOnSelect}
onReset={jest.fn()}
/>
);
await userEvent.click(screen.getByRole('button'));
@@ -46,9 +48,35 @@ describe('AssetDropdown', () => {
expect(mockOnSelect).toHaveBeenCalledWith(assets[0].id, false);
});
it('can be reset clearing all assets', async () => {
const mockOnSelect = jest.fn();
const mockOnReset = jest.fn();
render(
<AssetDropdown
checkedAssets={assets.map((a) => a.id)}
assets={assets}
onSelect={mockOnSelect}
onReset={mockOnReset}
/>
);
await userEvent.click(screen.getByRole('button'));
const items = screen.getAllByRole('menuitemcheckbox');
// all should be checked
items.forEach((item) => {
expect(item).toBeChecked();
});
await userEvent.click(screen.getByText('Reset'));
expect(mockOnReset).toHaveBeenCalled();
});
it('doesnt render if no assets provided', async () => {
const { container } = render(
<AssetDropdown checkedAssets={[]} assets={[]} onSelect={jest.fn()} />
<AssetDropdown
checkedAssets={[]}
assets={[]}
onSelect={jest.fn()}
onReset={jest.fn()}
/>
);
expect(container).toBeEmptyDOMElement();
});
@@ -3,10 +3,10 @@ import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIndicator,
DropdownMenuTrigger,
VegaIcon,
VegaIconNames,
DropdownMenuSeparator,
} from '@vegaprotocol/ui-toolkit';
type Assets = Array<{ id: string; symbol: string }>;
@@ -15,10 +15,12 @@ export const AssetDropdown = ({
assets,
checkedAssets,
onSelect,
onReset,
}: {
assets: Assets | undefined;
checkedAssets: string[];
onSelect: (id: string, checked: boolean) => void;
onReset: () => void;
}) => {
if (!assets?.length) {
return null;
@@ -33,6 +35,8 @@ export const AssetDropdown = ({
}
>
<DropdownMenuContent>
<DropdownMenuItem onClick={onReset}>{t('Reset')}</DropdownMenuItem>
<DropdownMenuSeparator />
{assets?.map((a) => {
return (
<DropdownMenuCheckboxItem
@@ -62,19 +66,15 @@ const TriggerText = ({
assets: Assets;
checkedAssets: string[];
}) => {
let text = t('Assets');
let text = t('Asset');
if (checkedAssets.length === 1) {
const assetId = checkedAssets[0];
const asset = assets.find((a) => a.id === assetId);
text = asset ? asset.symbol : t('Asset (1)');
} else if (checkedAssets.length > 1) {
text = t(`${checkedAssets.length} Assets`);
text = t(`Asset (${checkedAssets.length})`);
}
return (
<span className="flex justify-between items-center">
{text} <VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</span>
);
return <span className="px-1">{text}</span>;
};
@@ -169,7 +169,7 @@ describe('MarketSelectorItem', () => {
// link renders and is styled
expect(link).toHaveAttribute('href', '/markets/' + market.id);
expect(link).toHaveClass('bg-vega-clight-600');
expect(link.parentNode).toHaveClass('bg-vega-light-100');
expect(screen.getByTitle('24h vol')).toHaveTextContent('0.00');
expect(screen.getByTitle(symbol)).toHaveTextContent('-');
@@ -79,7 +79,7 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
return (
<>
<div className="w-2/5" role="gridcell">
<div className="w-1/4" role="gridcell">
<h3 className="text-ellipsis whitespace-nowrap overflow-hidden">
{market.tradableInstrument.instrument.code}
</h3>
@@ -90,25 +90,25 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
)}
</div>
<div
className="w-1/5 text-sm whitespace-nowrap text-ellipsis overflow-hidden"
className="w-1/4 text-sm"
title={instrument.product.settlementAsset.symbol}
data-testid="market-selector-price"
data-testid="market-selector-data-row"
role="gridcell"
>
{price} {instrument.product.settlementAsset.symbol}
</div>
<div
className="w-1/5 text-sm text-right whitespace-nowrap text-ellipsis overflow-hidden"
className="w-1/4 text-sm text-right"
title={t('24h vol')}
data-testid="market-selector-volume"
data-testid="market-selector-data-row"
role="gridcell"
>
{volume}
</div>
<div className="w-1/5 flex justify-end" role="gridcell">
<div className="w-1/4" role="gridcell">
{oneDayCandles && (
<Sparkline
width={64}
width={70}
height={15}
data={oneDayCandles.map((c) => Number(c.close))}
/>
@@ -240,7 +240,7 @@ describe('MarketSelector', () => {
await userEvent.click(screen.getByTestId('sort-trigger'));
const options = screen.getAllByTestId(/sort-item/);
expect(options.map((o) => o.textContent?.trim())).toEqual(
expect(options.map((o) => o.textContent)).toEqual(
Object.entries(Sort)
.filter(([key]) => key !== Sort.None)
.map(([key]) => SortTypeMapping[key as SortType])
@@ -8,7 +8,7 @@ import {
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import type { CSSProperties } from 'react';
import { useCallback, useState, useMemo, useRef } from 'react';
import { useCallback, useState, useMemo } from 'react';
import { FixedSizeList } from 'react-window';
import { useMarketSelectorList } from './use-market-selector-list';
import type { ProductType } from './product-selector';
@@ -66,7 +66,20 @@ export const MarketSelector = ({
placeholder={t('Search')}
data-testid="search-term"
className="w-full"
prependElement={<VegaIcon name={VegaIconNames.SEARCH} />}
appendElement={
filter.searchTerm.length ? (
<button
onClick={() =>
setFilter((curr) => ({ ...curr, searchTerm: '' }))
}
className="text-secondary"
>
<VegaIcon name={VegaIconNames.CROSS} />
</button>
) : (
<span />
)
}
/>
</div>
<AssetDropdown
@@ -96,6 +109,7 @@ export const MarketSelector = ({
return curr;
});
}}
onReset={() => setFilter((curr) => ({ ...curr, assets: [] }))}
/>
<SortDropdown
currentSort={filter.sort}
@@ -110,6 +124,7 @@ export const MarketSelector = ({
};
});
}}
onReset={() => setFilter((curr) => ({ ...curr, sort: Sort.None }))}
/>
</div>
</div>
@@ -150,18 +165,9 @@ const MarketList = ({
onSelect?: (marketId: string) => void;
noItems: string;
}) => {
const itemSize = 45;
const listRef = useRef<HTMLDivElement | null>(null);
const rect = listRef.current?.getBoundingClientRect();
// allow virtualized list to grow until it runs out of space
const height = rect
? Math.min(data.length * itemSize, window.innerHeight - rect.y)
: 400;
if (error) {
return <div>{error.message}</div>;
}
return (
<TinyScroll>
<div
@@ -171,28 +177,25 @@ const MarketList = ({
'p-2 mx-2 border-b border-default text-xs text-secondary'
)}
>
<div className="w-2/5" role="columnheader">
<div className="w-1/4" role="columnheader">
{t('Name')}
</div>
<div className="w-1/5" role="columnheader">
<div className="w-1/4" role="columnheader">
{t('Price')}
</div>
<div className="w-1/5 text-right" role="columnheader">
<div className="w-1/4 text-right" role="columnheader">
{t('24h volume')}
</div>
<div className="w-1/5" role="columnheader" />
</div>
<div ref={listRef}>
<List
data={data}
loading={loading}
height={height}
itemSize={itemSize}
currentMarketId={currentMarketId}
onSelect={onSelect}
noItems={noItems}
/>
<div className="w-1/4" role="columnheader" />
</div>
<List
data={data}
loading={loading}
height={400}
currentMarketId={currentMarketId}
onSelect={onSelect}
noItems={noItems}
/>
</TinyScroll>
);
};
@@ -223,14 +226,12 @@ const List = ({
data,
loading,
height,
itemSize,
onSelect,
noItems,
currentMarketId,
}: ListItemData & {
loading: boolean;
height: number;
itemSize: number;
noItems: string;
}) => {
const itemKey = useCallback(
@@ -263,7 +264,7 @@ const List = ({
className="vega-scrollbar"
itemCount={data.length}
itemData={itemData}
itemSize={itemSize}
itemSize={45}
itemKey={itemKey}
width="100%"
height={height}
+10 -3
View File
@@ -2,9 +2,12 @@ import React, { useEffect, useMemo } from 'react';
import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useThrottledDataProvider } from '@vegaprotocol/data-provider';
import {
useDataProvider,
useThrottledDataProvider,
} from '@vegaprotocol/data-provider';
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import { marketDataProvider, useMarket } from '@vegaprotocol/markets';
import { marketProvider, marketDataProvider } from '@vegaprotocol/markets';
import { useGlobalStore, usePageTitleStore } from '../../stores';
import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels';
@@ -67,7 +70,11 @@ export const MarketPage = () => {
const onSelect = useMarketClickHandler();
const { data, error, loading } = useMarket(marketId);
const { data, error, loading } = useDataProvider({
dataProvider: marketProvider,
variables: { marketId: marketId || '' },
skip: !marketId,
});
useEffect(() => {
if (data?.id && data.id !== lastMarketId) {
@@ -2,12 +2,12 @@ import { t } from '@vegaprotocol/i18n';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuItemIndicator,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
VegaIcon,
VegaIconNames,
DropdownMenuSeparator,
} from '@vegaprotocol/ui-toolkit';
export const Sort = {
@@ -28,32 +28,22 @@ export const SortTypeMapping: {
[Sort.New]: 'New markets',
};
const SortIconMapping: {
[key in SortType]: VegaIconNames;
} = {
[Sort.None]: null as unknown as VegaIconNames, // not shown in list
[Sort.Gained]: VegaIconNames.TREND_UP,
[Sort.Lost]: VegaIconNames.TREND_DOWN,
[Sort.New]: VegaIconNames.STAR,
};
export const SortDropdown = ({
currentSort,
onSelect,
onReset,
}: {
currentSort: SortType;
onSelect: (sort: SortType) => void;
onReset: () => void;
}) => {
return (
<DropdownMenu
trigger={
<DropdownMenuTrigger data-testid="sort-trigger">
<span className="flex justify-between items-center">
{currentSort === SortTypeMapping.None
? t('Sort')
: SortTypeMapping[currentSort]}{' '}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</span>
{currentSort === SortTypeMapping.None
? t('Sort')
: SortTypeMapping[currentSort]}
</DropdownMenuTrigger>
}
>
@@ -62,6 +52,8 @@ export const SortDropdown = ({
value={currentSort}
onValueChange={(value) => onSelect(value as SortType)}
>
<DropdownMenuItem onClick={onReset}>{t('Reset')}</DropdownMenuItem>
<DropdownMenuSeparator />
{Object.keys(Sort)
.filter((s) => s !== Sort.None)
.map((key) => {
@@ -72,10 +64,7 @@ export const SortDropdown = ({
value={key}
data-testid={`sort-item-${key}`}
>
<span className="flex gap-2">
<VegaIcon name={SortIconMapping[key as SortType]} />{' '}
{SortTypeMapping[key as SortType]}
</span>
{SortTypeMapping[key as SortType]}
<DropdownMenuItemIndicator />
</DropdownMenuRadioItem>
);
@@ -8,9 +8,16 @@ import { t } from '@vegaprotocol/i18n';
import { OracleBanner } from '@vegaprotocol/markets';
import type { Market } from '@vegaprotocol/markets';
import { Filter } from '@vegaprotocol/orders';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import {
Popover,
Tab,
LocalStoragePersistTabs as Tabs,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { Header, HeaderTitle } from '../../components/header';
import {
ResizableGrid,
ResizableGridPanel,
@@ -18,6 +25,8 @@ import {
} from '../../components/resizable-grid';
import { TradingViews } from './trade-views';
import { MarketSuccessorBanner } from '../../components/market-banner';
import { MarketSelector } from './market-selector';
import { MarketHeaderStats } from './market-header-stats';
interface TradeGridProps {
market: Market | null;
onSelect: (marketId: string, metaKey?: boolean) => void;
@@ -154,16 +163,32 @@ MainGrid.displayName = 'MainGrid';
export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
const wrapperClasses = classNames(
'h-full grid',
'grid-rows-[min-content_1fr]'
'grid-rows-[min-content_min-content_1fr]'
);
return (
<div className={wrapperClasses}>
<div>
<Header
title={
<Popover
trigger={
<HeaderTitle>
{market?.tradableInstrument.instrument.code}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</HeaderTitle>
}
>
<MarketSelector currentMarketId={market?.id} />
</Popover>
}
>
<MarketHeaderStats market={market} />
</Header>
<div className="bg-vega-green">
<MarketSuccessorBanner market={market} />
<OracleBanner marketId={market?.id || ''} />
</div>
<div className="min-h-0 p-0.5">
<div className="min-h-0 py-0.5">
<MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} />
</div>
</div>
@@ -8,11 +8,19 @@ import {
import type { TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { memo, useState } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit';
import {
Popover,
Splash,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { NO_MARKET } from './constants';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames';
import { Header, HeaderTitle } from '../../components/header';
import { MarketSelector } from './market-selector';
import { MarketSuccessorBanner } from '../../components/market-banner';
import { MarketHeaderStats } from './market-header-stats';
interface TradePanelsProps {
market: Market | null;
@@ -62,7 +70,23 @@ export const TradePanels = ({
};
return (
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
<Header
title={
<Popover
trigger={
<HeaderTitle>
{market?.tradableInstrument.instrument.code}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
</HeaderTitle>
}
>
<MarketSelector currentMarketId={market?.id} />
</Popover>
}
>
<MarketHeaderStats market={market} />
</Header>
<div>
<MarketSuccessorBanner market={market} />
<OracleBanner marketId={market?.id || ''} />
@@ -3,9 +3,9 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
import { TradesContainer } from '@vegaprotocol/trades';
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { OrderbookContainer } from '@vegaprotocol/market-depth';
import { Filter } from '@vegaprotocol/orders';
import { NO_MARKET } from './constants';
import { OrderbookContainer } from '../../components/orderbook-container';
import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container';
import { AccountsContainer } from '../../components/accounts-container';
@@ -4,12 +4,12 @@ import {
isMarketActive,
useMarketSelectorList,
} from './use-market-selector-list';
import { Product } from '../../components/market-selector/product-selector';
import { Product } from './product-selector';
import { Sort } from './sort-dropdown';
import { createMarketFragment } from '@vegaprotocol/mock';
import { MarketState } from '@vegaprotocol/types';
import { useMarketList } from '@vegaprotocol/markets';
import type { Filter } from '../../components/market-selector';
import type { Filter } from './market-selector';
import { subDays } from 'date-fns';
jest.mock('@vegaprotocol/markets');
@@ -3,7 +3,7 @@ import orderBy from 'lodash/orderBy';
import { MarketState } from '@vegaprotocol/types';
import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
import { priceChangePercentage } from '@vegaprotocol/utils';
import type { Filter } from '../../components/market-selector/market-selector';
import type { Filter } from './market-selector';
import { Sort } from './sort-dropdown';
// Used for sort order and filter
@@ -2,6 +2,7 @@ import { act, render, screen, within, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
import { PositionStatus } from '@vegaprotocol/types';
import { MarketState } from '@vegaprotocol/types';
import { subDays } from 'date-fns';
import type { MockedResponse } from '@apollo/client/testing';
@@ -18,6 +19,11 @@ import {
} from '@vegaprotocol/markets';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import type {
PositionsQuery,
PositionFieldsFragment,
} from '@vegaprotocol/positions';
import { PositionsDocument } from '@vegaprotocol/positions';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import {
createMarketFragment,
@@ -155,6 +161,45 @@ describe('Closed', () => {
},
};
// Create mock position
const createPosition = (): PositionFieldsFragment => {
return {
__typename: 'Position' as const,
realisedPNL: '1000',
unrealisedPNL: '2000',
openVolume: '3000',
averageEntryPrice: '100',
updatedAt: new Date().toISOString(),
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
lossSocializationAmount: '1000',
market: {
__typename: 'Market',
id: marketId,
},
party: {
__typename: 'Party',
id: pubKey,
},
};
};
const position = createPosition();
const positionsMock: MockedResponse<PositionsQuery> = {
request: {
query: PositionsDocument,
variables: {
partyIds: [pubKey],
},
},
result: {
data: {
positions: {
__typename: 'PositionConnection',
edges: [{ __typename: 'PositionEdge', node: position }],
},
},
},
};
beforeAll(() => {
originalNow = Date.now;
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
@@ -169,7 +214,12 @@ describe('Closed', () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[marketsMock, marketsDataMock, oracleDataMock]}
mocks={[
marketsMock,
marketsDataMock,
positionsMock,
oracleDataMock,
]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
@@ -193,6 +243,7 @@ describe('Closed', () => {
'Best offer',
'Mark price',
'Settlement price',
'Realised PNL',
'Settlement asset',
'', // actions row
];
@@ -215,6 +266,7 @@ describe('Closed', () => {
addDecimalsFormatNumber(marketsData!.markPrice, market.decimalPlaces),
/* eslint-enable @typescript-eslint/no-non-null-assertion */
addDecimalsFormatNumber(property.value, market.decimalPlaces),
addDecimalsFormatNumber(position.realisedPNL, market.decimalPlaces),
market.tradableInstrument.instrument.product.settlementAsset.symbol,
'', // actions row
];
@@ -275,7 +327,12 @@ describe('Closed', () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
mocks={[
mixedMarketsMock,
marketsDataMock,
positionsMock,
oracleDataMock,
]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
@@ -362,7 +419,12 @@ describe('Closed', () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
mocks={[
mixedMarketsMock,
marketsDataMock,
positionsMock,
oracleDataMock,
]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
+37 -2
View File
@@ -16,6 +16,7 @@ import {
addDecimalsFormatNumber,
getMarketExpiryDate,
} from '@vegaprotocol/utils';
import { usePositionsQuery } from '@vegaprotocol/positions';
import type {
DataSourceFilterFragment,
MarketMaybeWithData,
@@ -25,6 +26,7 @@ import {
closedMarketsWithDataProvider,
marketProvider,
} from '@vegaprotocol/markets';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import type { ColDef } from 'ag-grid-community';
import { SettlementDateCell } from './settlement-date-cell';
@@ -51,18 +53,34 @@ interface Row {
setlementDataSourceFilter: DataSourceFilterFragment | undefined;
tradingTerminationOracleId: string;
settlementAsset: SettlementAsset;
realisedPNL: string | undefined;
successorMarketID: string | undefined | null;
}
export const Closed = () => {
const { data: marketData, error } = useDataProvider({
const { pubKey } = useVegaWallet();
const {
data: marketData,
error,
reload,
} = useDataProvider({
dataProvider: closedMarketsWithDataProvider,
variables: undefined,
});
const { data: positionData } = usePositionsQuery({
variables: {
partyIds: pubKey ? [pubKey] : [],
},
skip: !pubKey,
});
// find a position for each market and add the realised pnl to
// a normalized object
const rowData = compact(marketData).map((market) => {
const position = positionData?.positions?.edges?.find((edge) => {
return edge.node.market.id === market.id;
});
const instrument = market.tradableInstrument.instrument;
const spec =
@@ -97,6 +115,7 @@ export const Closed = () => {
tradingTerminationOracleId:
instrument.product.dataSourceSpecForTradingTermination.id,
settlementAsset: instrument.product.settlementAsset,
realisedPNL: position?.node.realisedPNL,
successorMarketID: market.successorMarketID,
};
@@ -104,7 +123,7 @@ export const Closed = () => {
});
return (
<div className="h-full relative">
<ClosedMarketsDataGrid rowData={rowData} error={error} />
<ClosedMarketsDataGrid rowData={rowData} error={error} reload={reload} />
</div>
);
};
@@ -134,9 +153,11 @@ export const SuccessorMarketRenderer = ({
const ClosedMarketsDataGrid = ({
rowData,
error,
reload,
}: {
rowData: Row[];
error: Error | undefined;
reload: () => void;
}) => {
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
const colDefs = useMemo(() => {
@@ -268,11 +289,25 @@ const ClosedMarketsDataGrid = ({
/>
),
},
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
cellClass: 'font-mono ag-right-aligned-cell',
type: 'numericColumn',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Row, 'realisedPNL'>) => {
if (!value || !data) return '-';
return addDecimalsFormatNumber(value, data.decimalPlaces);
},
},
{
headerName: t('Settlement asset'),
field: 'settlementAsset',
cellRenderer: ({
value,
data,
}: VegaValueFormatterParams<Row, 'settlementAsset'>) => (
<button
className="underline"
@@ -15,7 +15,7 @@ export const MarketsPage = () => {
updateTitle(titlefy(['Markets']));
}, [updateTitle]);
return (
<div className="h-full pt-0.5 pb-3 px-1.5">
<div className="h-full pt-0.5 pb-3 px-1">
<div className="h-full my-1 border border-default rounded-sm">
<Tabs storageKey="console-markets">
<Tab id="all-markets" name={t('All markets')}>
@@ -54,7 +54,7 @@ export const Portfolio = () => {
const onMarketClick = useMarketClickHandler(true);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
const wrapperClasses = 'py-0.5 h-full max-h-full flex flex-col';
return (
<div className={wrapperClasses}>
<ResizableGrid vertical onChange={handleOnLayoutChange}>
+10 -8
View File
@@ -12,17 +12,19 @@ export const Header = ({ title, children }: TradeMarketHeaderProps) => {
'grid',
'grid-rows-[min-content_min-content]',
'xl:grid-cols-[min-content_1fr]',
'border-b border-default',
'lg:border-x border-b border-default',
'bg-vega-clight-800 dark:bg-vega-cdark-800'
);
return (
<header className={headerClasses}>
<div className="flex flex-col justify-center items-start pl-3 lg:pl-4 pt-2 xl:pb-2 pb-0">
{title}
</div>
<div data-testid="header-summary" className="min-w-0">
<div className="px-3 lg:px-4 py-2 flex flex-nowrap gap-4 items-center text-xs overflow-x-auto">
{children}
<header className="lg:px-1">
<div className={headerClasses}>
<div className="flex flex-col justify-center items-start pl-3 lg:pl-4 pt-2 xl:pb-2 pb-0">
{title}
</div>
<div data-testid="header-summary" className="min-w-0">
<div className="px-3 lg:px-4 py-2 flex flex-nowrap gap-4 items-center text-xs overflow-x-auto">
{children}
</div>
</div>
</div>
</header>
@@ -1,9 +1,6 @@
import { Outlet, Routes, Route } from 'react-router-dom';
import { Outlet } from 'react-router-dom';
import { Sidebar, SidebarContent, useSidebar } from '../sidebar';
import classNames from 'classnames';
import { Routes as AppRoutes } from '../../pages/client-router';
import { MarketHeader } from '../market-header';
import { LiquidityHeader } from '../liquidity-header';
export const LayoutWithSidebar = () => {
const sidebarView = useSidebar((store) => store.view);
@@ -11,35 +8,28 @@ export const LayoutWithSidebar = () => {
const gridClasses = classNames(
'h-full relative z-0 grid',
'grid-rows-[min-content_1fr]',
'grid-cols-[1fr_45px]',
'lg:grid-cols-[1fr_350px_45px]'
);
return (
<div className={gridClasses}>
<div className="col-span-full">
<Routes>
<Route path={AppRoutes.MARKET} element={<MarketHeader />} />
<Route path={AppRoutes.LIQUIDITY} element={<LiquidityHeader />} />
</Routes>
</div>
<main
<section
className={classNames('col-start-1 col-end-1', {
'lg:col-end-3': !sidebarOpen,
'hidden lg:block lg:col-end-2': sidebarOpen,
})}
>
<Outlet />
</main>
<aside
</section>
<div
// min-h-0 is needed as this element is part of a grid, we want the content to be scrollable, without it it will push the grid element taller
className={classNames('col-start-1 lg:col-start-2 min-h-0', {
hidden: !sidebarOpen,
})}
>
<SidebarContent />
</aside>
</div>
<div className="col-start-2 lg:col-start-3 bg-vega-clight-800 dark:bg-vega-cdark-800 border-l border-default">
<Sidebar />
</div>
@@ -1 +0,0 @@
export * from './liquidity-header';
@@ -1,107 +0,0 @@
import {
tooltipMapping,
useMarket,
useStaticMarketData,
} from '@vegaprotocol/markets';
import { Header, HeaderStat, HeaderTitle } from '../header';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { ExternalLink, Indicator } from '@vegaprotocol/ui-toolkit';
import { DocsLinks } from '@vegaprotocol/environment';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useCheckLiquidityStatus } from '@vegaprotocol/liquidity';
import { useParams } from 'react-router-dom';
export const LiquidityHeader = () => {
const { marketId } = useParams();
const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId);
const targetStake = marketData?.targetStake;
const suppliedStake = marketData?.suppliedStake;
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
NetworkParams.market_liquidity_targetstake_triggering_ratio,
]);
const triggeringRatio =
params.market_liquidity_targetstake_triggering_ratio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
targetStake: targetStake || 0,
triggeringRatio,
});
console.log(market);
return (
<Header
title={
market?.tradableInstrument.instrument.code &&
marketId && (
<HeaderTitle>
{market.tradableInstrument.instrument.code &&
t(
'%s liquidity provision',
market.tradableInstrument.instrument.code
)}
</HeaderTitle>
)
}
>
<HeaderStat
heading={t('Target stake')}
description={tooltipMapping['targetStake']}
testId="target-stake"
>
<div>
{targetStake
? `${addDecimalsFormatNumber(
targetStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat
heading={t('Supplied stake')}
description={tooltipMapping['suppliedStake']}
testId="supplied-stake"
>
<div>
{suppliedStake
? `${addDecimalsFormatNumber(
suppliedStake,
assetDecimalPlaces ?? 0
)} ${symbol}`
: '-'}
</div>
</HeaderStat>
<HeaderStat heading={t('Liquidity supplied')} testId="liquidity-supplied">
<Indicator variant={status} /> {formatNumberPercentage(percentage, 2)}
</HeaderStat>
<HeaderStat heading={t('Market ID')} testId="liquidity-market-id">
<div className="break-word">{marketId}</div>
</HeaderStat>
<HeaderStat heading={t('Learn more')} testId="liquidity-learn-more">
{DocsLinks ? (
<ExternalLink href={DocsLinks.LIQUIDITY}>
{t('Providing liquidity')}
</ExternalLink>
) : (
(null as React.ReactNode)
)}
</HeaderStat>
</Header>
);
};
@@ -1 +0,0 @@
export * from './market-header';
@@ -1,33 +0,0 @@
import { Popover, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Header, HeaderTitle } from '../header';
import { useParams } from 'react-router-dom';
import { MarketSelector } from '../../components/market-selector/market-selector';
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
import { useMarket } from '@vegaprotocol/markets';
export const MarketHeader = () => {
const { marketId } = useParams();
const { data } = useMarket(marketId);
if (!data) return null;
return (
<Header
title={
<Popover
trigger={
<HeaderTitle>
{data.tradableInstrument.instrument.code}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
</HeaderTitle>
}
alignOffset={-10}
>
<MarketSelector currentMarketId={marketId} />
</Popover>
}
>
<MarketHeaderStats market={data} />
</Header>
);
};
@@ -1,2 +0,0 @@
export * from './market-selector';
export * from './market-selector-item';
@@ -16,7 +16,7 @@ export const NodeHealthContainer = () => {
<Tooltip
description={
<div
className="flex flex-col gap-2 p-2 text-sm"
className="flex flex-col gap-2 p-4 text-sm"
data-testid="node-health"
>
<div className="flex items-center gap-2">
@@ -37,9 +37,8 @@ export const NodeHealthContainer = () => {
</div>
}
align="end"
side="left"
sideOffset={18}
arrow={false}
side="right"
sideOffset={10}
>
<button
className="flex justify-center items-center p-2 rounded hover:bg-vega-light-200 hover:dark:bg-vega-dark-200"
@@ -1 +0,0 @@
export * from './orderbook-container';
@@ -1,23 +0,0 @@
import { OrderbookManager } from '@vegaprotocol/market-depth';
import { useCreateOrderStore } from '@vegaprotocol/orders';
import { ViewType, useSidebar } from '../sidebar';
export const OrderbookContainer = ({ marketId }: { marketId: string }) => {
const useOrderStoreRef = useCreateOrderStore();
const updateOrder = useOrderStoreRef((store) => store.update);
const setView = useSidebar((store) => store.setView);
return (
<OrderbookManager
marketId={marketId}
onClick={({ price, size }) => {
if (price) {
updateOrder(marketId, { price });
}
if (size) {
updateOrder(marketId, { size });
}
setView({ type: ViewType.Order });
}}
/>
);
};
+5 -9
View File
@@ -20,7 +20,6 @@ export interface TooltipProps {
side?: 'top' | 'right' | 'bottom' | 'left';
sideOffset?: number;
delayDuration?: number;
arrow?: boolean;
}
// Conditionally rendered tooltip if description content is provided.
@@ -32,7 +31,6 @@ export const Tooltip = ({
align = 'start',
side = 'bottom',
delayDuration = 200,
arrow = true,
}: TooltipProps) =>
description ? (
<Provider delayDuration={delayDuration} skipDelayDuration={100}>
@@ -55,13 +53,11 @@ export const Tooltip = ({
<div className="relative z-0" data-testid="tooltip-content">
{description}
</div>
{arrow && (
<Arrow
width={16}
height={8}
className="fill-vega-clight-500 dark:fill-vega-cdark-500"
/>
)}
<Arrow
width={16}
height={8}
className="fill-vega-clight-500 dark:fill-vega-cdark-500"
/>
</Content>
</Portal>
)}
+2 -2
View File
@@ -95,9 +95,9 @@ function AppBody({ Component }: AppProps) {
<ViewingBanner />
<UpgradeBanner showVersionChange={true} />
</div>
<div data-testid={`pathname-${location.pathname}`}>
<main data-testid={location.pathname}>
<Component />
</div>
</main>
</div>
<DialogsContainer />
<ToastsManager />
+21 -21
View File
@@ -61,18 +61,18 @@ html.dark {
html [data-theme='dark'],
html [data-theme='light'] {
/* sell candles only use stroke as the candle is solid (without border) */
--pennant-color-sell-stroke: theme(colors.market.red.DEFAULT);
--pennant-color-sell-stroke: theme('colors.market.red.500');
/* studies */
--pennant-color-eldar-ray-bear-power: theme(colors.market.red.DEFAULT);
--pennant-color-eldar-ray-bull-power: theme(colors.market.green.600);
--pennant-color-eldar-ray-bear-power: theme('colors.market.red.500');
--pennant-color-eldar-ray-bull-power: theme('colors.market.green.600');
--pennant-color-macd-divergence-buy: theme(colors.market.green.600);
--pennant-color-macd-divergence-sell: theme(colors.market.red.DEFAULT);
--pennant-color-macd-signal: theme(colors.vega.blue.DEFAULT);
--pennant-color-macd-macd: theme(colors.vega.yellow.500);
--pennant-color-macd-divergence-buy: theme('colors.market.green.600');
--pennant-color-macd-divergence-sell: theme('colors.market.red.500');
--pennant-color-macd-signal: theme('colors.vega.blue.500');
--pennant-color-macd-macd: theme('colors.vega.yellow.500');
--pennant-color-volume-sell: theme(colors.market.red.DEFAULT);
--pennant-color-volume-sell: theme('colors.market.red.500');
}
html [data-theme='light'] {
@@ -80,20 +80,20 @@ html [data-theme='light'] {
--pennant-background-surface-color: theme(colors.vega.clight.900);
/* candles */
--pennant-color-buy-fill: theme(colors.market.green.DEFAULT);
--pennant-color-buy-fill: theme(colors.market.green.500);
--pennant-color-buy-stroke: theme(colors.market.green.600);
/* sell uses stroke for fill and stroke */
--pennant-color-sell-stroke: theme(colors.market.red.DEFAULT);
--pennant-color-sell-stroke: theme(colors.market.red.500);
/* depth chart */
--pennant-color-depth-buy-fill: theme(colors.market.green.DEFAULT);
--pennant-color-depth-buy-fill: theme(colors.market.green.500);
--pennant-color-depth-buy-stroke: theme(colors.market.green.600);
--pennant-color-depth-sell-fill: theme(colors.market.red.DEFAULT);
--pennant-color-depth-sell-stroke: theme(colors.market.red.650);
--pennant-color-depth-sell-fill: theme(colors.market.red.500);
--pennant-color-depth-sell-stroke: theme(colors.market.red.600);
--pennant-color-volume-buy: theme(colors.market.green.300);
--pennant-color-volume-sell: theme(colors.market.red.300);
--pennant-color-volume-buy: theme(colors.market.green.400);
--pennant-color-volume-sell: theme(colors.market.red.400);
}
html [data-theme='dark'] {
@@ -102,19 +102,19 @@ html [data-theme='dark'] {
/* candles */
--pennant-color-buy-fill: theme(colors.market.green.600);
--pennant-color-buy-stroke: theme(colors.market.green.DEFAULT);
--pennant-color-buy-stroke: theme(colors.market.green.500);
/* sell uses stroke for fill and stroke */
--pennant-color-sell-stroke: theme(colors.market.red.DEFAULT);
--pennant-color-sell-stroke: theme(colors.market.red.500);
/* depth chart */
--pennant-color-depth-buy-fill: theme(colors.market.green.600);
--pennant-color-depth-buy-stroke: theme(colors.market.green.DEFAULT);
--pennant-color-depth-sell-fill: theme(colors.market.red.650);
--pennant-color-depth-sell-stroke: theme(colors.market.red.DEFAULT);
--pennant-color-depth-buy-stroke: theme(colors.market.green.500);
--pennant-color-depth-sell-fill: theme(colors.market.red.600);
--pennant-color-depth-sell-stroke: theme(colors.market.red.500);
--pennant-color-volume-buy: theme(colors.market.green.600);
--pennant-color-volume-sell: theme(colors.market.red.650);
--pennant-color-volume-sell: theme(colors.market.red.600);
}
/**
+6 -11
View File
@@ -3,6 +3,7 @@ import { addMockGQLCommand } from './lib/mock-gql';
import { addMockSubscription } from './lib/mock-ws';
import { addMockWalletCommand } from './lib/mock-rest';
import { addMockWeb3ProviderCommand } from './lib/commands/mock-web3-provider';
import { addSlackCommand } from './lib/commands/slack';
import { addHighlightLog } from './lib/commands/highlight-log';
import { addGetAssets } from './lib/commands/get-assets';
import { addVegaWalletReceiveFaucetedAsset } from './lib/commands/vega-wallet-receive-fauceted-asset';
@@ -25,6 +26,7 @@ import { addVegaWalletTopUpRewardsPool } from './lib/commands/vega-wallet-top-up
import { addAssociateTokensToVegaWallet } from './lib/commands/associate-tokens-to-vega-wallet';
addGetTestIdcommand();
addSlackCommand();
addMockGQLCommand();
addMockSubscription();
addMockWalletCommand();
@@ -57,14 +59,7 @@ export { aliasGQLQuery } from './lib/mock-gql';
export { aliasWalletQuery } from './lib/mock-rest';
export * from './lib/utils';
Cypress.on('uncaught:exception', (err) => {
if (
err.message.includes('ResizeObserver loop limit exceeded') ||
err.message.includes(
'ResizeObserver loop completed with undelivered notifications'
)
) {
return false;
}
return true;
});
Cypress.on(
'uncaught:exception',
(err) => !err.message.includes('ResizeObserver loop limit exceeded')
);
+28
View File
@@ -0,0 +1,28 @@
declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
slack(message: string): void;
}
}
}
export function addSlackCommand() {
// @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command
Cypress.Commands.add('slack', (message) => {
const text = `${message}: ${JSON.stringify(Cypress.spec)}`;
cy.log('NOTIFYING SLACK');
const webhook = Cypress.env('SLACK_WEBHOOK');
if (!webhook) {
return;
}
cy.request('POST', webhook, {
text,
});
});
}
@@ -35,9 +35,7 @@ export const ZeroBalanceError = ({
}
buttonProps={{
text: t(`Make a deposit`),
action: () => {
onDeposit(asset.id);
},
action: () => onDeposit(asset.id),
dataTestId: 'deal-ticket-deposit-dialog-button',
size: 'small',
}}
@@ -10,7 +10,7 @@ export const DealTicketButton = ({ side }: Props) => {
const buttonClasses = classNames(
'px-10 py-2 uppercase rounded-md text-white w-full',
{
'bg-market-red': side === Side.SIDE_SELL,
'bg-market-red-500': side === Side.SIDE_SELL,
'bg-market-green-550': side === Side.SIDE_BUY,
}
);
@@ -270,8 +270,8 @@ export const DealTicket = ({
return (
<form
onSubmit={isReadOnly ? undefined : handleSubmit(onSubmit)}
className="p-4"
noValidate
data-testid="deal-ticket-form"
>
<Controller
name="type"
+9 -4
View File
@@ -1,6 +1,7 @@
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { DepositManager } from './deposit-manager';
import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { enabledAssetsProvider } from '@vegaprotocol/assets';
@@ -15,14 +16,18 @@ export const DepositContainer = ({ assetId }: { assetId?: string }) => {
});
return (
<AsyncRendererInline data={data} loading={loading} error={error}>
{data && data.length && (
<AsyncRenderer data={data} loading={loading} error={error}>
{data && data.length ? (
<DepositManager
assetId={assetId}
assets={data}
isFaucetable={VEGA_ENV !== Networks.MAINNET}
/>
) : (
<Splash>
<p>{t('No assets on this network')}</p>
</Splash>
)}
</AsyncRendererInline>
</AsyncRenderer>
);
};
+1
View File
@@ -90,6 +90,7 @@ export const DepositForm = ({
const [approveNotificationIntent, setApproveNotificationIntent] =
useState<Intent>(Intent.Warning);
const [persistedDeposit] = usePersistentDeposit(selectedAsset?.id);
console.log('deposit-form', account);
const {
register,
handleSubmit,
@@ -359,14 +359,6 @@ function compileEnvVars() {
),
APP_VERSION: windowOrDefault('APP_VERSION', process.env['NX_APP_VERSION']),
SENTRY_DSN: windowOrDefault('SENTRY_DSN', process.env['NX_SENTRY_DSN']),
TENDERMINT_URL: windowOrDefault(
'NX_TENDERMINT_URL',
process.env['NX_TENDERMINT_URL']
),
TENDERMINT_WEBSOCKET_URL: windowOrDefault(
'NX_TENDERMINT_WEBSOCKET_URL',
process.env['NX_TENDERMINT_WEBSOCKET_URL']
),
};
return env;
@@ -54,8 +54,6 @@ const schemaObject = {
VEGA_INCIDENT_URL: z.optional(z.string()),
APP_VERSION: z.optional(z.string()),
SENTRY_DSN: z.optional(z.string()),
TENDERMINT_URL: z.optional(z.string()),
TENDERMINT_WEBSOCKET_URL: z.optional(z.string()),
};
// combine schema above with custom rule to ensure either
+1
View File
@@ -1,6 +1,7 @@
export * from './__generated__/MarketDepth';
export * from './depth-chart';
export * from './market-depth-provider';
export * from './orderbook-container';
export * from './orderbook-data';
export * from './orderbook-manager';
export * from './orderbook-row';
@@ -0,0 +1,5 @@
import { OrderbookManager } from './orderbook-manager';
export const OrderbookContainer = ({ marketId }: { marketId: string }) => (
<OrderbookManager marketId={marketId} />
);
@@ -9,6 +9,7 @@ import type {
MarketDepthUpdateSubscription,
PriceLevelFieldsFragment,
} from './__generated__/MarketDepth';
import { useCreateOrderStore } from '@vegaprotocol/orders';
export type OrderbookData = {
asks: PriceLevelFieldsFragment[];
@@ -17,13 +18,9 @@ export type OrderbookData = {
interface OrderbookManagerProps {
marketId: string;
onClick?: (args: { price?: string; size?: string }) => void;
}
export const OrderbookManager = ({
marketId,
onClick,
}: OrderbookManagerProps) => {
export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
const variables = { marketId };
const { data, error, loading, reload } = useDataProvider<
@@ -53,6 +50,8 @@ export const OrderbookManager = ({
dataProvider: marketDataProvider,
variables,
});
const useOrderStoreRef = useCreateOrderStore();
const updateOrder = useOrderStoreRef((store) => store.update);
return (
<AsyncRenderer
@@ -67,7 +66,14 @@ export const OrderbookManager = ({
decimalPlaces={market?.decimalPlaces ?? 0}
positionDecimalPlaces={market?.positionDecimalPlaces ?? 0}
assetSymbol={market?.tradableInstrument.instrument.product.quoteName}
onClick={onClick}
onClick={({ price, size }) => {
if (price) {
updateOrder(marketId, { price });
}
if (size) {
updateOrder(marketId, { size });
}
}}
midPrice={marketData?.midPrice}
/>
</AsyncRenderer>
+30 -44
View File
@@ -13,12 +13,8 @@ interface OrderbookRowProps {
price: string;
onClick?: (args: { price?: string; size?: string }) => void;
type: VolumeType;
width: number;
}
const HIDE_VOL_WIDTH = 150;
const HIDE_CUMULATIVE_VOL_WIDTH = 220;
const CumulationBar = ({
cumulativeValue = 0,
type,
@@ -30,7 +26,7 @@ const CumulationBar = ({
<div
data-testid={`${VolumeType.bid === type ? 'bid' : 'ask'}-bar`}
className={classNames(
'absolute top-0 left-0 h-full',
'absolute top-0 left-0 h-full transition-all',
type === VolumeType.bid
? 'bg-market-green-300 dark:bg-market-green/50'
: 'bg-market-red-300 dark:bg-market-red/30'
@@ -94,18 +90,12 @@ export const OrderbookRow = React.memo(
price,
onClick,
type,
width,
}: OrderbookRowProps) => {
const txtId = type === VolumeType.bid ? 'bid' : 'ask';
const cols =
width >= HIDE_CUMULATIVE_VOL_WIDTH ? 3 : width >= HIDE_VOL_WIDTH ? 2 : 1;
return (
<div className="relative pr-1">
<div className="relative">
<CumulationBar cumulativeValue={cumulativeRelativeValue} type={type} />
<div
data-testid={`${txtId}-rows-container`}
className={classNames('grid gap-1 text-right', `grid-cols-${cols}`)}
>
<div className="grid gap-1 text-right grid-cols-3">
<PriceCell
testId={`price-${price}`}
value={BigInt(price)}
@@ -119,37 +109,33 @@ export const OrderbookRow = React.memo(
: 'text-market-green-600 dark:text-market-green'
}
/>
{width >= HIDE_VOL_WIDTH && (
<PriceCell
testId={`${txtId}-vol-${price}`}
onClick={(value) =>
onClick &&
value &&
onClick({
size: addDecimal(value, positionDecimalPlaces),
})
}
value={value}
valueFormatted={addDecimalsFixedFormatNumber(
value,
positionDecimalPlaces
)}
/>
)}
{width >= HIDE_CUMULATIVE_VOL_WIDTH && (
<CumulativeVol
testId={`cumulative-vol-${price}`}
onClick={() =>
onClick &&
cumulativeValue &&
onClick({
size: addDecimal(cumulativeValue, positionDecimalPlaces),
})
}
positionDecimalPlaces={positionDecimalPlaces}
cumulativeValue={cumulativeValue}
/>
)}
<PriceCell
testId={`${txtId}-vol-${price}`}
onClick={(value) =>
onClick &&
value &&
onClick({
size: addDecimal(value, positionDecimalPlaces),
})
}
value={value}
valueFormatted={addDecimalsFixedFormatNumber(
value,
positionDecimalPlaces
)}
/>
<CumulativeVol
testId={`cumulative-vol-${price}`}
onClick={() =>
onClick &&
cumulativeValue &&
onClick({
size: addDecimal(cumulativeValue, positionDecimalPlaces),
})
}
positionDecimalPlaces={positionDecimalPlaces}
cumulativeValue={cumulativeValue}
/>
</div>
</div>
);
+7 -95
View File
@@ -1,5 +1,4 @@
import { render, waitFor, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render, fireEvent, waitFor, screen } from '@testing-library/react';
import { generateMockData, VolumeType } from './orderbook-data';
import { Orderbook } from './orderbook';
import * as orderbookData from './orderbook-data';
@@ -34,7 +33,6 @@ describe('Orderbook', () => {
const decimalPlaces = 3;
beforeEach(() => {
jest.clearAllMocks();
mockOffsetSize(800, 768);
});
it('markPrice should be in the middle', async () => {
@@ -71,17 +69,12 @@ describe('Orderbook', () => {
await screen.findByTestId(`middle-mark-price-${params.midPrice}`)
).toBeInTheDocument();
// Before resolution change the price is 122.934
await userEvent.click(await screen.getByTestId('price-122901'));
await fireEvent.click(await screen.getByTestId('price-122901'));
expect(onClickSpy).toBeCalledWith({ price: '122.901' });
await userEvent.click(screen.getByTestId('resolution'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
await userEvent.click(screen.getAllByRole('menuitem')[1]);
const resolutionSelect = screen.getByTestId(
'resolution'
) as HTMLSelectElement;
await fireEvent.change(resolutionSelect, { target: { value: '10' } });
expect(orderbookData.compactRows).toHaveBeenCalledWith(
mockedData.bids,
VolumeType.bid,
@@ -92,88 +85,7 @@ describe('Orderbook', () => {
VolumeType.ask,
10
);
await userEvent.click(await screen.getByTestId('price-12294'));
await fireEvent.click(await screen.getByTestId('price-12294'));
expect(onClickSpy).toBeCalledWith({ price: '122.94' });
});
it('plus - minus buttons should change resolution', async () => {
const onClickSpy = jest.fn();
jest.spyOn(orderbookData, 'compactRows');
const mockedData = generateMockData(params);
render(
<Orderbook
decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
onClick={onClickSpy}
{...mockedData}
assetSymbol="USD"
/>
);
expect((orderbookData.compactRows as jest.Mock).mock.lastCall[2]).toEqual(
1
);
expect(screen.getByTestId('minus-button')).toBeDisabled();
userEvent.click(screen.getByTestId('plus-button'));
await waitFor(() => {
expect((orderbookData.compactRows as jest.Mock).mock.lastCall[2]).toEqual(
10
);
});
expect(screen.getByTestId('minus-button')).not.toBeDisabled();
userEvent.click(screen.getByTestId('minus-button'));
await waitFor(() => {
expect((orderbookData.compactRows as jest.Mock).mock.lastCall[2]).toEqual(
1
);
});
expect(screen.getByTestId('minus-button')).toBeDisabled();
await userEvent.click(screen.getByTestId('resolution'));
await waitFor(() => {
expect(screen.getByRole('menu')).toBeInTheDocument();
});
await userEvent.click(screen.getAllByRole('menuitem')[5]);
await waitFor(() => {
expect((orderbookData.compactRows as jest.Mock).mock.lastCall[2]).toEqual(
100000
);
});
expect(screen.getByTestId('plus-button')).toBeDisabled();
});
it('two columns', () => {
mockOffsetSize(200, 768);
const onClickSpy = jest.fn();
const mockedData = generateMockData(params);
render(
<Orderbook
decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
onClick={onClickSpy}
{...mockedData}
assetSymbol="USD"
/>
);
screen.getAllByTestId('bid-rows-container').forEach((item) => {
expect(item).toHaveClass('grid-cols-2');
});
});
it('one column', () => {
mockOffsetSize(140, 768);
const onClickSpy = jest.fn();
const mockedData = generateMockData(params);
render(
<Orderbook
decimalPlaces={decimalPlaces}
positionDecimalPlaces={0}
onClick={onClickSpy}
{...mockedData}
assetSymbol="USD"
/>
);
screen.getAllByTestId('ask-rows-container').forEach((item) => {
expect(item).toHaveClass('grid-cols-1');
});
});
});
+24 -123
View File
@@ -1,24 +1,16 @@
import { useMemo, useRef, useState } from 'react';
import { useMemo } from 'react';
import ReactVirtualizedAutoSizer from 'react-virtualized-auto-sizer';
import {
addDecimalsFormatNumber,
formatNumberFixed,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { usePrevious } from '@vegaprotocol/react-helpers';
import { OrderbookRow } from './orderbook-row';
import type { OrderbookRowData } from './orderbook-data';
import { compactRows, VolumeType } from './orderbook-data';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Splash,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { Splash } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import { useState } from 'react';
import type { PriceLevelFieldsFragment } from './__generated__/MarketDepth';
// Sets row height, will be used to calculate number of rows that can be
@@ -27,19 +19,6 @@ export const rowHeight = 17;
const rowGap = 1;
const midHeight = 30;
type PriceChange = 'up' | 'down' | 'none';
const PRICE_CHANGE_ICON_MAP: Readonly<Record<PriceChange, VegaIconNames>> = {
up: VegaIconNames.ARROW_UP,
down: VegaIconNames.ARROW_DOWN,
none: VegaIconNames.BULLET,
};
const PRICE_CHANGE_CLASS_MAP: Readonly<Record<PriceChange, string>> = {
up: 'text-market-green-600 dark:text-market-green',
down: 'text-market-red dark:text-market-red',
none: 'text-vega-blue-500',
};
const OrderbookTable = ({
rows,
resolution,
@@ -47,7 +26,6 @@ const OrderbookTable = ({
decimalPlaces,
positionDecimalPlaces,
onClick,
width,
}: {
rows: OrderbookRowData[];
resolution: number;
@@ -55,7 +33,6 @@ const OrderbookTable = ({
positionDecimalPlaces: number;
type: VolumeType;
onClick?: (args: { price?: string; size?: string }) => void;
width: number;
}) => {
return (
<div
@@ -82,7 +59,6 @@ const OrderbookTable = ({
cumulativeValue={data.cumulativeVol.value}
cumulativeRelativeValue={data.cumulativeVol.relativeValue}
type={type}
width={width}
/>
))}
</div>
@@ -123,50 +99,12 @@ export const Orderbook = ({
const groupedBids = useMemo(() => {
return compactRows(bids, VolumeType.bid, resolution);
}, [bids, resolution]);
const [isOpen, setOpen] = useState(false);
const previousMidPrice = usePrevious(midPrice);
const priceChangeRef = useRef<'up' | 'down' | 'none'>('none');
if (midPrice && previousMidPrice !== midPrice) {
priceChangeRef.current =
(previousMidPrice || '') > midPrice ? 'down' : 'up';
}
const priceChangeIcon = (
<span
className={classNames(PRICE_CHANGE_CLASS_MAP[priceChangeRef.current])}
>
<VegaIcon name={PRICE_CHANGE_ICON_MAP[priceChangeRef.current]} />
</span>
);
const formatResolution = (r: number) => {
return formatNumberFixed(
Math.log10(r) - decimalPlaces > 0
? Math.pow(10, Math.log10(r) - decimalPlaces)
: 0,
decimalPlaces - Math.log10(r)
);
};
const increaseResolution = () => {
const index = resolutions.indexOf(resolution);
if (index < resolutions.length - 1) {
setResolution(resolutions[index + 1]);
}
};
const decreaseResolution = () => {
const index = resolutions.indexOf(resolution);
if (index > 0) {
setResolution(resolutions[index - 1]);
}
};
return (
<div className="h-full text-xs grid grid-rows-[1fr_min-content]">
<div>
<ReactVirtualizedAutoSizer>
{({ width, height }) => {
<ReactVirtualizedAutoSizer disableWidth>
{({ height }) => {
const limit = Math.max(
1,
Math.floor((height - midHeight) / 2 / (rowHeight + rowGap))
@@ -178,7 +116,6 @@ export const Orderbook = ({
className="overflow-hidden grid"
data-testid="orderbook-grid-element"
style={{
width: width + 'px',
height: height + 'px',
gridTemplateRows: `1fr ${midHeight}px 1fr`, // cannot use tailwind here as tailwind will not parse a class string with interpolation
}}
@@ -192,7 +129,6 @@ export const Orderbook = ({
decimalPlaces={decimalPlaces}
positionDecimalPlaces={positionDecimalPlaces}
onClick={onClick}
width={width}
/>
<div className="flex items-center justify-center gap-2">
{midPrice && (
@@ -204,7 +140,6 @@ export const Orderbook = ({
{addDecimalsFormatNumber(midPrice, decimalPlaces)}
</span>
<span className="text-base">{assetSymbol}</span>
{priceChangeIcon}
</>
)}
</div>
@@ -215,7 +150,6 @@ export const Orderbook = ({
decimalPlaces={decimalPlaces}
positionDecimalPlaces={positionDecimalPlaces}
onClick={onClick}
width={width}
/>
</>
) : (
@@ -228,59 +162,26 @@ export const Orderbook = ({
}}
</ReactVirtualizedAutoSizer>
</div>
<div className="border-t border-default flex">
<button
onClick={increaseResolution}
disabled={resolutions.indexOf(resolution) >= resolutions.length - 1}
className="flex items-center border-r border-default px-2 cursor-pointer"
data-testid="plus-button"
<div className="border-t border-default bg-vega-clight-700 dark:bg-vega-cdark-700">
<select
onChange={(e) => {
setResolution(Number(e.currentTarget.value));
}}
value={resolution}
className="block bg-transparent font-mono text-right"
data-testid="resolution"
>
<VegaIcon size={12} name={VegaIconNames.PLUS} />
</button>
<DropdownMenu
open={isOpen}
onOpenChange={(open) => setOpen(open)}
trigger={
<DropdownMenuTrigger
data-testid="resolution"
className="flex justify-between px-1 items-center"
style={{
width: `${
Math.max.apply(
null,
resolutions.map((item) => formatResolution(item).length)
) + 3
}ch`,
}}
>
<VegaIcon
size={12}
name={
isOpen ? VegaIconNames.CHEVRON_UP : VegaIconNames.CHEVRON_DOWN
}
/>
<div className="text-xs text-left">
{formatResolution(resolution)}
</div>
</DropdownMenuTrigger>
}
>
<DropdownMenuContent align="start">
{resolutions.map((r) => (
<DropdownMenuItem key={r} onClick={() => setResolution(r)}>
{formatResolution(r)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<button
onClick={decreaseResolution}
disabled={resolutions.indexOf(resolution) <= 0}
className="flex items-center border-x border-default px-2 cursor-pointer"
data-testid="minus-button"
>
<VegaIcon size={12} name={VegaIconNames.MINUS} />
</button>
{resolutions.map((r) => (
<option key={r} value={r}>
{formatNumberFixed(
Math.log10(r) - decimalPlaces > 0
? Math.pow(10, Math.log10(r) - decimalPlaces)
: 0,
decimalPlaces - Math.log10(r)
)}
</option>
))}
</select>
</div>
</div>
);
@@ -168,6 +168,7 @@ export const MarketInfoDocument = gql`
}
}
}
parentMarketID
}
}
${DataSourceFragmentDoc}`;
-1
View File
@@ -82,7 +82,6 @@ export const marketProvider = makeDerivedDataProvider<
);
export const useMarket = (marketId?: string) => {
console.log(marketId);
const variables = useMemo(() => ({ marketId: marketId || '' }), [marketId]);
return useDataProvider({
dataProvider: marketProvider,
@@ -6,7 +6,6 @@ import type { Position } from './positions-data-providers';
import * as Schema from '@vegaprotocol/types';
import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types';
import type { ICellRendererParams } from 'ag-grid-community';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
jest.mock('./liquidation-price', () => ({
LiquidationPrice: () => (
@@ -20,7 +19,7 @@ const singleRow: Position = {
assetSymbol: 'BTC',
averageEntryPrice: '133',
currentLeverage: 1.1,
decimals: 2, // this is settlementAsset.decimals
decimals: 2,
quantum: '0.1',
lossSocializationAmount: '0',
marginAccountBalance: '12345600',
@@ -178,23 +177,12 @@ it('displays allocated margin', async () => {
});
it('displays realised and unrealised PNL', async () => {
// pnl cells should be rendered with asset dps
const expectedRealised = addDecimalsFormatNumber(
singleRow.realisedPNL,
singleRow.decimals
);
const expectedUnrealised = addDecimalsFormatNumber(
singleRow.unrealisedPNL,
singleRow.decimals
);
await act(async () => {
render(<PositionsTable rowData={singleRowData} isReadOnly={false} />);
});
const cells = screen.getAllByRole('gridcell');
expect(cells[9].textContent).toEqual(expectedRealised);
expect(cells[10].textContent).toEqual(expectedUnrealised);
expect(cells[9].textContent).toEqual('12.3');
expect(cells[10].textContent).toEqual('45.6');
});
it('displays close button', async () => {
+16 -4
View File
@@ -365,14 +365,20 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.realisedPNL, data.decimals).toNumber();
: toBigNum(
data.realisedPNL,
data.marketDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? ''
: addDecimalsFormatNumber(data.realisedPNL, data.decimals);
: addDecimalsFormatNumber(
data.realisedPNL,
data.marketDecimalPlaces
);
},
headerTooltip: t(
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
@@ -390,14 +396,20 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(data.unrealisedPNL, data.decimals).toNumber();
: toBigNum(
data.unrealisedPNL,
data.marketDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumber(data.unrealisedPNL, data.decimals),
: addDecimalsFormatNumber(
data.unrealisedPNL,
data.marketDecimalPlaces
),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
@@ -44,10 +44,7 @@ export const ProtocolUpgradeProposalNotification = ({
case ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING:
countdown =
time !== undefined ? (
<span
title={t('estimated time to protocol upgrade')}
className="text-vega-orange-500"
>
<span className="text-vega-orange-500">
{convertToCountdownString(time, '0:00:00:00')}
</span>
) : (
+1 -1
View File
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useReducer, useRef } from 'react';
export interface State<T> {
interface State<T> {
data?: T;
error?: Error;
loading?: boolean;
-12
View File
@@ -1,12 +0,0 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
-18
View File
@@ -1,18 +0,0 @@
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
-7
View File
@@ -1,7 +0,0 @@
# tendermint
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test tendermint` to execute the unit tests via [Jest](https://jestjs.io).
-11
View File
@@ -1,11 +0,0 @@
/* eslint-disable */
export default {
displayName: 'tendermint',
preset: '../../jest.preset.js',
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/tendermint',
};
-4
View File
@@ -1,4 +0,0 @@
{
"name": "tendermint",
"version": "0.0.1"
}
-50
View File
@@ -1,50 +0,0 @@
{
"name": "tendermint",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/tendermint/src",
"projectType": "library",
"tags": [],
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/tendermint/**/*.{ts,tsx,js,jsx}"]
}
},
"build": {
"executor": "@nx/rollup:rollup",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/tendermint",
"tsConfig": "libs/tendermint/tsconfig.lib.json",
"project": "libs/tendermint/package.json",
"entryFile": "libs/tendermint/src/index.ts",
"external": ["react", "react-dom", "react/jsx-runtime"],
"rollupConfig": "@nx/react/plugins/bundle-rollup",
"compiler": "babel",
"assets": [
{
"glob": "libs/tendermint/README.md",
"input": ".",
"output": "."
}
]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/tendermint/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
}
}
-2
View File
@@ -1,2 +0,0 @@
export * from './lib/use-block-info';
export * from './types';
-20
View File
@@ -1,20 +0,0 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { useFetch } from '@vegaprotocol/react-helpers';
import type { TendermintBlockResponse } from '../types';
export const useBlockInfo = (blockHeight?: number, canFetch = true) => {
const { TENDERMINT_URL } = useEnvironment();
const url = `${TENDERMINT_URL}/block?height=${blockHeight}`;
const canFetchData = Boolean(
TENDERMINT_URL && blockHeight && !isNaN(blockHeight) && canFetch
);
const { state, refetch } = useFetch<TendermintBlockResponse>(
url,
{ cache: 'force-cache' },
canFetchData
);
return { state, refetch };
};
-57
View File
@@ -1,57 +0,0 @@
export type TendermintBlockResponse = {
jsonrpc: string;
id: number;
result: {
block_id: Id;
block: Block;
};
};
type Id = {
hash: string;
parts: {
total: number;
hash: string;
};
};
type Header = {
version: {
block: string;
};
chain_id: string;
height: string;
time: string;
last_block_id: Id;
last_commit_hash: string;
data_hash: string;
validators_hash: string;
next_validators_hash: string;
consensus_hash: string;
app_hash: string;
last_results_hash: string;
evidence_hash: string;
proposer_address: string;
};
type Block = {
header: Header;
data: {
txs: string[];
};
evidence: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
evidence: any[];
};
last_commit: {
height: string;
round: number;
block_id: Id;
signatures: {
block_id_flag: number;
validator_address: string;
timestamp: string;
signature: string;
}[];
};
};
-25
View File
@@ -1,25 +0,0 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../tsconfig.base.json"
}
-23
View File
@@ -1,23 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
],
"exclude": [
"jest.config.ts",
"src/**/*.spec.ts",
"src/**/*.test.ts",
"src/**/*.spec.tsx",
"src/**/*.test.tsx",
"src/**/*.spec.js",
"src/**/*.test.js",
"src/**/*.spec.jsx",
"src/**/*.test.jsx"
],
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
}

Some files were not shown because too many files have changed in this diff Show More