Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0316cbd9a | ||
|
|
015e0188ec | ||
|
|
228236c152 | ||
|
|
cd6e906dc6 | ||
|
|
8a47a92dbd | ||
|
|
af14c27df9 | ||
|
|
38edcd88c0 | ||
|
|
a74cf02030 | ||
|
|
6686755763 | ||
|
|
79cf8163be |
@@ -1,7 +1,6 @@
|
||||
---
|
||||
name: Feature Epic
|
||||
about:
|
||||
A template to capture and scope user requirements, high level process, and basic mockups for an upcoming feature as part of the initial core spec review process.
|
||||
about: A template to capture and scope user requirements, high level process, and basic mockups for an upcoming feature as part of the initial core spec review process.
|
||||
title: 'Epic: '
|
||||
labels: feature-epic
|
||||
---
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"changes": {
|
||||
"decimalPlaces": "5",
|
||||
"positionDecimalPlaces": "5",
|
||||
"linearSlippageFactor": "0.001",
|
||||
"quadraticSlippageFactor": "0",
|
||||
"lpPriceRange": "10",
|
||||
"instrument": {
|
||||
"name": "Token test market",
|
||||
"code": "Token.24h",
|
||||
"future": {
|
||||
"settlementAsset": "816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc",
|
||||
"quoteName": "fUSDC",
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"external": {
|
||||
"oracle": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "prices.BTC.value",
|
||||
"type": "TYPE_INTEGER",
|
||||
"numberDecimalPlaces": "0"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecForTradingTermination": {
|
||||
"external": {
|
||||
"oracle": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "trading.terminated.ETH5",
|
||||
"type": "TYPE_BOOLEAN"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_EQUALS",
|
||||
"value": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecBinding": {
|
||||
"settlementDataProperty": "prices.BTC.value",
|
||||
"tradingTerminationProperty": "trading.terminated.ETH5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadata": ["sector:food", "sector:materials", "source:docs.vega.xyz"],
|
||||
"priceMonitoringParameters": {
|
||||
"triggers": [
|
||||
{
|
||||
"horizon": "43200",
|
||||
"probability": "0.9999999",
|
||||
"auctionExtension": "600"
|
||||
}
|
||||
]
|
||||
},
|
||||
"liquidityMonitoringParameters": {
|
||||
"targetStakeParameters": {
|
||||
"timeWindow": "3600",
|
||||
"scalingFactor": 10
|
||||
},
|
||||
"triggeringRatio": "0.7",
|
||||
"auctionExtension": "1"
|
||||
},
|
||||
"logNormal": {
|
||||
"tau": 0.0001140771161,
|
||||
"riskAversionParameter": 0.001,
|
||||
"params": {
|
||||
"mu": 0,
|
||||
"r": 0.016,
|
||||
"sigma": 0.8
|
||||
}
|
||||
},
|
||||
"successor": {
|
||||
"parentMarketId": "",
|
||||
"insurancePoolFraction": "0.75"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ context(
|
||||
function () {
|
||||
const proposalTitle = 'Test new market proposal';
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.getByTestId(newProposalTitle).type('Test new market proposal');
|
||||
cy.getByTestId(newProposalTitle).type(proposalTitle);
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
@@ -606,6 +606,94 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
it('able to submit successor market proposal', function () {
|
||||
const proposalTitle = 'Test successor market proposal';
|
||||
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.getByTestId('closed-proposals').within(() => {
|
||||
cy.contains('Add Lorem Ipsum market')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
});
|
||||
getProposalInformationFromTable('ID').invoke('text').as('parentMarketId');
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.getByTestId(newProposalTitle).type(proposalTitle);
|
||||
cy.getByTestId(newProposalDescription).type(
|
||||
'E2E test for successor market'
|
||||
);
|
||||
cy.fixture('/proposals/successor-market').then((newMarketProposal) => {
|
||||
newMarketProposal.changes.successor.parentMarketId =
|
||||
this.parentMarketId;
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(proposalTitle).within(() => {
|
||||
// 3003-PMAN-008
|
||||
cy.getByTestId('proposal-successor-info')
|
||||
.should('have.text', 'Successor market to: TEST.24h')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', this.parentMarketId);
|
||||
cy.getByTestId('view-proposal-btn').click();
|
||||
});
|
||||
// #3003-PMAN-010
|
||||
cy.getByTestId(proposalJsonToggle).click();
|
||||
cy.get('.language-json').within(() => {
|
||||
cy.get('.hljs-attr').should('contain.text', 'parentMarketId');
|
||||
cy.get('.hljs-string').should('contain.text', this.parentMarketId);
|
||||
cy.get('.hljs-attr').should('contain.text', 'insurancePoolFraction');
|
||||
cy.get('.hljs-string').should('contain.text', '0.75');
|
||||
});
|
||||
cy.getByTestId('proposal-market-data').within(() => {
|
||||
cy.getByTestId('proposal-market-data-toggle').click();
|
||||
cy.contains('Key details').click();
|
||||
// 3003-PMAN-009
|
||||
getMarketProposalDetailsFromTable('Parent Market ID').should(
|
||||
'have.text',
|
||||
this.parentMarketId
|
||||
);
|
||||
getMarketProposalDetailsFromTable('Insurance Pool Fraction').should(
|
||||
'have.text',
|
||||
'0.75'
|
||||
);
|
||||
getMarketProposalDetailsFromTable('Trading Mode').should(
|
||||
'have.text',
|
||||
'No trading'
|
||||
);
|
||||
});
|
||||
// 3003-PMAN-011
|
||||
cy.contains('Parent Market ID').realHover();
|
||||
cy.getByTestId('tooltip-content').should(
|
||||
'contain.text',
|
||||
'The ID of the market this market succeeds.'
|
||||
);
|
||||
cy.contains('Insurance Pool Fraction').realHover();
|
||||
cy.getByTestId('tooltip-content').should(
|
||||
'contain.text',
|
||||
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
|
||||
);
|
||||
});
|
||||
|
||||
after('Disassociate from second wallet key if present', function () {
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
|
||||
@@ -96,12 +96,14 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
.within(() => {
|
||||
cy.get('h2').first().should('contain.text', 'EPOCH');
|
||||
cy.getByTestId('individual-rewards-asset').should('have.text', 'Vega');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut)
|
||||
.should('contain.text', '0.4415')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')
|
||||
.should('contain.text', '0.0004')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut).should(
|
||||
'contain.text',
|
||||
'0.4415'
|
||||
);
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE').should(
|
||||
'contain.text',
|
||||
'0.0004'
|
||||
);
|
||||
cy.getByTestId('total').should('have.text', '0.4419');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -339,7 +339,7 @@ context(
|
||||
cy.getByTestId(vegaWalletCurrencyTitle)
|
||||
.contains(name)
|
||||
.parent()
|
||||
.siblings()
|
||||
.siblings(txTimeout)
|
||||
.should((elementAmount) => {
|
||||
const displayedAmount = parseFloat(elementAmount.text());
|
||||
expect(displayedAmount).be.gte(expectedAmount);
|
||||
|
||||
@@ -107,7 +107,7 @@ export function dissociateFromSecondWalletKey() {
|
||||
cy.getByTestId('vega-in-wallet')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('eth-wallet-associated-balances')
|
||||
cy.getByTestId('eth-wallet-associated-balances', txTimeout)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId('associated-key')
|
||||
|
||||
@@ -78,13 +78,21 @@ export function stakingPageAssociateTokens(
|
||||
}
|
||||
cy.get(tokenAmountInputBox, epochTimeout).type(amount);
|
||||
if (approve) {
|
||||
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
|
||||
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains('Approve $VEGA Tokens for staking on Vega', txTimeout).should(
|
||||
'not.exist'
|
||||
);
|
||||
cy.getByTestId('wallet-associate').then((walletAssociateField) => {
|
||||
if (
|
||||
walletAssociateField.find('[data-testid="token-input-approve-button"]')
|
||||
.length
|
||||
) {
|
||||
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
|
||||
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains(
|
||||
'Approve $VEGA Tokens for staking on Vega',
|
||||
txTimeout
|
||||
).should('not.exist');
|
||||
}
|
||||
});
|
||||
}
|
||||
cy.get(tokenSubmitButton, txTimeout).should('be.enabled').click();
|
||||
|
||||
|
||||
@@ -18,5 +18,5 @@ NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DepositsTable } from '@vegaprotocol/deposits';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -17,6 +17,9 @@ export const DepositsContainer = () => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<div className="h-full">
|
||||
<DepositsTable
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
withdrawalProvider,
|
||||
WithdrawalsTable,
|
||||
@@ -18,7 +18,9 @@ export const WithdrawalsContainer = () => {
|
||||
});
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const { ready, delayed } = useIncompleteWithdrawals();
|
||||
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="h-full relative">
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { Sidebar, SidebarContent, ViewType, useSidebar } from './sidebar';
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarButton,
|
||||
SidebarContent,
|
||||
ViewType,
|
||||
useSidebar,
|
||||
} from './sidebar';
|
||||
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
|
||||
jest.mock('../node-health', () => ({
|
||||
NodeHealthContainer: () => <span data-testid="node-health" />,
|
||||
@@ -28,11 +36,14 @@ describe('Sidebar', () => {
|
||||
'does not render ticket and info',
|
||||
(path) => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={[path]}>
|
||||
<Sidebar />
|
||||
</MemoryRouter>
|
||||
<VegaWalletProvider>
|
||||
<MemoryRouter initialEntries={[path]}>
|
||||
<Sidebar />
|
||||
</MemoryRouter>
|
||||
</VegaWalletProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
|
||||
@@ -47,11 +58,14 @@ describe('Sidebar', () => {
|
||||
|
||||
it('renders ticket and info on market pages', () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Sidebar />
|
||||
</MemoryRouter>
|
||||
<VegaWalletProvider>
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Sidebar />
|
||||
</MemoryRouter>
|
||||
</VegaWalletProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
|
||||
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
|
||||
@@ -65,9 +79,11 @@ describe('Sidebar', () => {
|
||||
|
||||
it('renders selected state', async () => {
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Sidebar />
|
||||
</MemoryRouter>
|
||||
<VegaWalletProvider>
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Sidebar />
|
||||
</MemoryRouter>
|
||||
</VegaWalletProvider>
|
||||
);
|
||||
|
||||
const settingsButton = screen.getByTestId(ViewType.Settings);
|
||||
@@ -91,11 +107,13 @@ describe('Sidebar', () => {
|
||||
describe('SidebarContent', () => {
|
||||
it('renders the correct content', () => {
|
||||
const { container } = render(
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Routes>
|
||||
<Route path="/markets/:marketId" element={<SidebarContent />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
<VegaWalletProvider>
|
||||
<MemoryRouter initialEntries={['/markets/ABC']}>
|
||||
<Routes>
|
||||
<Route path="/markets/:marketId" element={<SidebarContent />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</VegaWalletProvider>
|
||||
);
|
||||
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
@@ -115,11 +133,13 @@ describe('SidebarContent', () => {
|
||||
|
||||
it('closes sidebar if market id is required but not present', () => {
|
||||
const { container } = render(
|
||||
<MemoryRouter initialEntries={['/portfolio']}>
|
||||
<Routes>
|
||||
<Route path="/portfolio" element={<SidebarContent />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
<VegaWalletProvider>
|
||||
<MemoryRouter initialEntries={['/portfolio']}>
|
||||
<Routes>
|
||||
<Route path="/portfolio" element={<SidebarContent />} />
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
</VegaWalletProvider>
|
||||
);
|
||||
|
||||
act(() => {
|
||||
@@ -141,3 +161,25 @@ describe('SidebarContent', () => {
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
|
||||
describe('SidebarButton', () => {
|
||||
it.each([ViewType.Info, ViewType.Deposit, ViewType.ViewAs])(
|
||||
'runs given callback regardless of requested view (%s)',
|
||||
async (view) => {
|
||||
const onClick = jest.fn();
|
||||
render(
|
||||
<SidebarButton
|
||||
icon={VegaIconNames.INFO}
|
||||
tooltip="INFO"
|
||||
onClick={onClick}
|
||||
view={view}
|
||||
/>
|
||||
);
|
||||
|
||||
const btn = screen.getByTestId(view);
|
||||
await userEvent.click(btn);
|
||||
|
||||
expect(onClick).toBeCalled();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ import { WithdrawContainer } from '../withdraw-container';
|
||||
import { Routes as AppRoutes } from '../../pages/client-router';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { GetStarted } from '../welcome-dialog';
|
||||
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
|
||||
|
||||
const STORAGE_KEY = 'vega_sidebar_store';
|
||||
|
||||
@@ -26,6 +27,7 @@ export enum ViewType {
|
||||
Withdraw = 'Withdraw',
|
||||
Transfer = 'Transfer',
|
||||
Settings = 'Settings',
|
||||
ViewAs = 'ViewAs',
|
||||
}
|
||||
|
||||
type SidebarView =
|
||||
@@ -53,6 +55,8 @@ type SidebarView =
|
||||
|
||||
export const Sidebar = () => {
|
||||
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
|
||||
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
|
||||
const { pubKeys } = useVegaWallet();
|
||||
return (
|
||||
<div className="flex lg:flex-col gap-2 h-full p-1" data-testid="sidebar">
|
||||
<nav className={navClasses}>
|
||||
@@ -105,6 +109,16 @@ export const Sidebar = () => {
|
||||
</Routes>
|
||||
</nav>
|
||||
<nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}>
|
||||
<SidebarButton
|
||||
view={ViewType.ViewAs}
|
||||
onClick={() => {
|
||||
setViewAsDialogOpen(true);
|
||||
}}
|
||||
icon={VegaIconNames.EYE}
|
||||
tooltip={t('View as party')}
|
||||
disabled={Boolean(pubKeys)}
|
||||
/>
|
||||
|
||||
<SidebarButton
|
||||
view={ViewType.Settings}
|
||||
icon={VegaIconNames.COG}
|
||||
@@ -116,25 +130,43 @@ export const Sidebar = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const SidebarButton = ({
|
||||
export const SidebarButton = ({
|
||||
view,
|
||||
icon,
|
||||
tooltip,
|
||||
disabled = false,
|
||||
onClick,
|
||||
}: {
|
||||
view: ViewType;
|
||||
view?: ViewType;
|
||||
icon: VegaIconNames;
|
||||
tooltip: string;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
}) => {
|
||||
const { currView, setView } = useSidebar((store) => ({
|
||||
currView: store.view,
|
||||
setView: store.setView,
|
||||
}));
|
||||
const buttonClasses = classNames('flex items-center p-1 rounded', {
|
||||
'text-vega-clight-200 dark:text-vega-cdark-200 hover:bg-vega-clight-500 dark:hover:bg-vega-cdark-500':
|
||||
view !== currView?.type,
|
||||
'bg-vega-yellow hover:bg-vega-yellow-550 text-black':
|
||||
view === currView?.type,
|
||||
});
|
||||
|
||||
const onSelect = (view: SidebarView['type']) => {
|
||||
if (view === currView?.type) {
|
||||
setView(null);
|
||||
} else {
|
||||
setView({ type: view });
|
||||
}
|
||||
};
|
||||
|
||||
const buttonClasses = classNames(
|
||||
'flex items-center p-1 rounded',
|
||||
'disabled:cursor-not-allowed disabled:text-vega-clight-500 dark:disabled:text-vega-cdark-500',
|
||||
{
|
||||
'text-vega-clight-200 dark:text-vega-cdark-200 enabled:hover:bg-vega-clight-500 dark:enabled:hover:bg-vega-cdark-500':
|
||||
!view || view !== currView?.type,
|
||||
'bg-vega-yellow enabled:hover:bg-vega-yellow-550 text-black':
|
||||
view && view === currView?.type,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
description={tooltip}
|
||||
@@ -146,13 +178,8 @@ const SidebarButton = ({
|
||||
<button
|
||||
className={buttonClasses}
|
||||
data-testid={view}
|
||||
onClick={() => {
|
||||
if (view === currView?.type) {
|
||||
setView(null);
|
||||
} else {
|
||||
setView({ type: view });
|
||||
}
|
||||
}}
|
||||
onClick={onClick || (() => onSelect(view as SidebarView['type']))}
|
||||
disabled={disabled}
|
||||
>
|
||||
<VegaIcon name={icon} size={20} />
|
||||
</button>
|
||||
@@ -282,7 +309,7 @@ export const useSidebar = create<{
|
||||
view: null,
|
||||
setView: (x) =>
|
||||
set(() => {
|
||||
if (x === null) {
|
||||
if (x == null) {
|
||||
return { view: null, init: false };
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Links, Routes } from '../../pages/client-router';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import * as constants from '../constants';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export const WelcomeDialogContent = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
@@ -25,206 +26,134 @@ export const WelcomeDialogContent = () => {
|
||||
'Free from the risks of real trading, Fairground is a safe and fun place to try out Vega yourself with virtual assets.'
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-col pb-2">
|
||||
<div className="flex gap-8">
|
||||
<div className="w-1/2 flex flex-col justify-between pt-3">
|
||||
<ul className="ml-0">
|
||||
<li className="my-3 flex gap-3 text-default">
|
||||
<div className="shrink-0 pt-1.5">
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 30 30"
|
||||
fill="currentColor"
|
||||
>
|
||||
<rect x="14" y="20" width="2" height="2" />
|
||||
<rect x="12" y="18" width="2" height="2" />
|
||||
<rect x="10" y="8" width="2" height="10" />
|
||||
<rect x="16" y="18" width="2" height="2" />
|
||||
<rect x="20" y="16" width="2" height="2" />
|
||||
<rect x="18" y="8" width="2" height="8" />
|
||||
<rect x="28" y="2" width="2" height="26" />
|
||||
<rect y="2" width="2" height="26" />
|
||||
<rect
|
||||
x="28"
|
||||
width="2"
|
||||
height="26"
|
||||
transform="rotate(90 28 0)"
|
||||
/>
|
||||
<rect
|
||||
x="28"
|
||||
y="28"
|
||||
width="2"
|
||||
height="26"
|
||||
transform="rotate(90 28 28)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg">{t('Trade with no KYC')}</h3>
|
||||
<span className="text-sm text-vega-clight-100 dark:text-vega-cdark-100 leading-4">
|
||||
{t(
|
||||
'Pseudonomously trade Futures markets. Spot and perps comming soon'
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="my-3 flex gap-3 text-vega-clight-50 dark:text-vega-cdark-50">
|
||||
<div className="shrink-0 pt-1.5">
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 30 30"
|
||||
fill="currentColor"
|
||||
>
|
||||
<g clip-path="url(#clip0_5168_49089)">
|
||||
<path d="M22 7H18V9H22V7Z" />
|
||||
<path d="M22 13H18V15H22V13Z" />
|
||||
<path d="M24 17H16V19H24V17Z" />
|
||||
<path d="M24 9H22V13H24V9Z" />
|
||||
<path d="M18 9H16V13H18V9Z" />
|
||||
<path d="M6 19H4V23H6V19Z" />
|
||||
<path d="M16 19H14V23H16V19Z" />
|
||||
<path d="M26 19H24V23H26V19Z" />
|
||||
<path d="M12 7H8V9H12V7Z" />
|
||||
<path d="M12 13H8V15H12V13Z" />
|
||||
<path d="M14 17H6V19H14V17Z" />
|
||||
<path d="M14 9H12V13H14V9Z" />
|
||||
<path d="M8 9H6V13H8V9Z" />
|
||||
<path d="M30 2H28V28H30V2Z" />
|
||||
<path d="M2 2H0V28H2V2Z" />
|
||||
<path d="M28 0H2V2H28V0Z" />
|
||||
<path d="M28 28H2V30H28V28Z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5168_49089">
|
||||
<rect width="30" height="30" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg">
|
||||
{t('Community generated trading pairs')}
|
||||
</h3>
|
||||
<span className="text-sm text-vega-clight-100 dark:text-vega-cdark-100 leading-4">
|
||||
{t('All markets are proposed and enacted by the community')}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="my-3 flex gap-3 text-vega-clight-50 dark:text-vega-cdark-50">
|
||||
<div className="shrink-0 pt-1.5">
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 30 30"
|
||||
fill="currentColor"
|
||||
>
|
||||
<rect x="6" y="16" width="4" height="2" />
|
||||
<rect x="10" y="14" width="2" height="2" />
|
||||
<rect
|
||||
x="12"
|
||||
y="24"
|
||||
width="4"
|
||||
height="2"
|
||||
transform="rotate(-90 12 24)"
|
||||
/>
|
||||
<rect
|
||||
x="10"
|
||||
y="20"
|
||||
width="2"
|
||||
height="2"
|
||||
transform="rotate(-90 10 20)"
|
||||
/>
|
||||
<rect
|
||||
x="20"
|
||||
y="18"
|
||||
width="4"
|
||||
height="2"
|
||||
transform="rotate(-180 20 18)"
|
||||
/>
|
||||
<rect
|
||||
x="16"
|
||||
y="20"
|
||||
width="2"
|
||||
height="2"
|
||||
transform="rotate(-180 16 20)"
|
||||
/>
|
||||
<rect
|
||||
x="16"
|
||||
y="14"
|
||||
width="2"
|
||||
height="2"
|
||||
transform="rotate(90 16 14)"
|
||||
/>
|
||||
<rect
|
||||
x="22"
|
||||
y="10"
|
||||
width="2"
|
||||
height="2"
|
||||
transform="rotate(90 22 10)"
|
||||
/>
|
||||
<rect
|
||||
x="20"
|
||||
y="8"
|
||||
width="2"
|
||||
height="2"
|
||||
transform="rotate(90 20 8)"
|
||||
/>
|
||||
<rect
|
||||
x="24"
|
||||
y="8"
|
||||
width="2"
|
||||
height="2"
|
||||
transform="rotate(90 24 8)"
|
||||
/>
|
||||
<rect
|
||||
x="22"
|
||||
y="6"
|
||||
width="2"
|
||||
height="2"
|
||||
transform="rotate(90 22 6)"
|
||||
/>
|
||||
<rect x="12" y="10" width="2" height="4" />
|
||||
<rect x="28" y="2" width="2" height="26" />
|
||||
<rect y="2" width="2" height="26" />
|
||||
<rect
|
||||
x="28"
|
||||
width="2"
|
||||
height="26"
|
||||
transform="rotate(90 28 0)"
|
||||
/>
|
||||
<rect
|
||||
x="28"
|
||||
y="28"
|
||||
width="2"
|
||||
height="26"
|
||||
transform="rotate(90 28 28)"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg">{t('Rewards')}</h3>
|
||||
<span className="text-sm text-vega-clight-100 dark:text-vega-cdark-100 leading-4">
|
||||
{t(
|
||||
'Earn rewards for trading, market making and providing liquidity'
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<TradingButton
|
||||
onClick={browseMarkets}
|
||||
className="block w-full"
|
||||
data-testid="browse-markets-button"
|
||||
>
|
||||
{t('Browse the markets')}
|
||||
</TradingButton>
|
||||
</div>
|
||||
<div className="w-1/2 -mr-3 flex flex-grow">
|
||||
<GetStarted lead={lead} />
|
||||
</div>
|
||||
<div className="flex flex-col sm:flex-row gap-8">
|
||||
<div className="sm:w-1/2 flex flex-col justify-between pt-3">
|
||||
<ul className="ml-0">
|
||||
<ListItemContent
|
||||
icon={<NonCustodialIcon />}
|
||||
title={t('Non-cutodial and pseudonymous')}
|
||||
text={t('No third party has access to your funds.')}
|
||||
/>
|
||||
<ListItemContent
|
||||
icon={<PurposeBuiltIcon />}
|
||||
title={t('Purpose built proof of stake blockhain')}
|
||||
text={t(
|
||||
'Fully decentralised high performance peer-to-network trading.'
|
||||
)}
|
||||
/>
|
||||
<ListItemContent
|
||||
icon={<RewardIcon />}
|
||||
title={t('Low fees and no cost to place orders')}
|
||||
text={t(
|
||||
'Fees work like a CEX with no per-transaction gas for orders'
|
||||
)}
|
||||
/>
|
||||
</ul>
|
||||
<TradingButton
|
||||
onClick={browseMarkets}
|
||||
className="block w-full"
|
||||
data-testid="browse-markets-button"
|
||||
>
|
||||
{t('Browse the markets')}
|
||||
</TradingButton>
|
||||
</div>
|
||||
<div className="sm:w-1/2">
|
||||
<GetStarted lead={lead} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ListItemContent = ({
|
||||
icon,
|
||||
title,
|
||||
text,
|
||||
}: {
|
||||
icon: ReactNode;
|
||||
title: string;
|
||||
text: string;
|
||||
}) => {
|
||||
return (
|
||||
<li className="my-4 flex gap-3">
|
||||
<div className="shrink-0 pt-1">{icon}</div>
|
||||
<div>
|
||||
<h3 className="text-lg leading-snug mb-2">{title}</h3>
|
||||
<p className="text-sm text-secondary">{text}</p>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
const PurposeBuiltIcon = () => {
|
||||
return (
|
||||
<svg width="40" height="40" viewBox="0 0 30 30" fill="currentColor">
|
||||
<rect x="14" y="20" width="2" height="2" />
|
||||
<rect x="12" y="18" width="2" height="2" />
|
||||
<rect x="10" y="8" width="2" height="10" />
|
||||
<rect x="16" y="18" width="2" height="2" />
|
||||
<rect x="20" y="16" width="2" height="2" />
|
||||
<rect x="18" y="8" width="2" height="8" />
|
||||
<rect x="28" y="2" width="2" height="26" />
|
||||
<rect y="2" width="2" height="26" />
|
||||
<rect x="28" width="2" height="26" transform="rotate(90 28 0)" />
|
||||
<rect x="28" y="28" width="2" height="26" transform="rotate(90 28 28)" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const NonCustodialIcon = () => {
|
||||
return (
|
||||
<svg width="40" height="40" viewBox="0 0 30 30" className="fill-current">
|
||||
<rect x="8" y="22" width="14" height="2" />
|
||||
<rect x="8" y="12" width="14" height="2" />
|
||||
<rect
|
||||
x="22"
|
||||
y="24"
|
||||
width="2"
|
||||
height="12"
|
||||
transform="rotate(-180 22 24)"
|
||||
/>
|
||||
<rect
|
||||
x="10"
|
||||
y="24"
|
||||
width="2"
|
||||
height="12"
|
||||
transform="rotate(-180 10 24)"
|
||||
/>
|
||||
<rect x="12" y="12" width="2" height="2" transform="rotate(-180 12 12)" />
|
||||
<rect x="20" y="12" width="2" height="2" transform="rotate(-180 20 12)" />
|
||||
<rect x="12" y="10" width="2" height="2" transform="rotate(-180 12 10)" />
|
||||
<rect x="20" y="10" width="2" height="2" transform="rotate(-180 20 10)" />
|
||||
<rect x="18" y="8" width="2" height="2" transform="rotate(-180 18 8)" />
|
||||
<rect x="14" y="8" width="2" height="2" transform="rotate(-180 14 8)" />
|
||||
<rect x="16" y="8" width="2" height="2" transform="rotate(-180 16 8)" />
|
||||
<rect x="28" y="2" width="2" height="26" />
|
||||
<rect y="2" width="2" height="26" />
|
||||
<rect x="28" width="2" height="26" transform="rotate(90 28 0)" />
|
||||
<rect x="28" y="28" width="2" height="26" transform="rotate(90 28 28)" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
const RewardIcon = () => {
|
||||
return (
|
||||
<svg width="40" height="40" viewBox="0 0 30 30" fill="currentColor">
|
||||
<rect x="6" y="16" width="4" height="2" />
|
||||
<rect x="10" y="14" width="2" height="2" />
|
||||
<rect x="12" y="24" width="4" height="2" transform="rotate(-90 12 24)" />
|
||||
<rect x="10" y="20" width="2" height="2" transform="rotate(-90 10 20)" />
|
||||
<rect x="20" y="18" width="4" height="2" transform="rotate(-180 20 18)" />
|
||||
<rect x="16" y="20" width="2" height="2" transform="rotate(-180 16 20)" />
|
||||
<rect x="16" y="14" width="2" height="2" transform="rotate(90 16 14)" />
|
||||
<rect x="22" y="10" width="2" height="2" transform="rotate(90 22 10)" />
|
||||
<rect x="20" y="8" width="2" height="2" transform="rotate(90 20 8)" />
|
||||
<rect x="24" y="8" width="2" height="2" transform="rotate(90 24 8)" />
|
||||
<rect x="22" y="6" width="2" height="2" transform="rotate(90 22 6)" />
|
||||
<rect x="12" y="10" width="2" height="4" />
|
||||
<rect x="28" y="2" width="2" height="26" />
|
||||
<rect y="2" width="2" height="26" />
|
||||
<rect x="28" width="2" height="26" transform="rotate(90 28 0)" />
|
||||
<rect x="28" y="28" width="2" height="26" transform="rotate(90 28 28)" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
AssetDetailsDialog,
|
||||
useAssetDetailsDialogStore,
|
||||
} from '@vegaprotocol/assets';
|
||||
import { VegaConnectDialog } from '@vegaprotocol/wallet';
|
||||
import { VegaConnectDialog, ViewAsDialog } from '@vegaprotocol/wallet';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import {
|
||||
Web3ConnectUncontrolledDialog,
|
||||
@@ -19,6 +19,7 @@ const DialogsContainer = () => {
|
||||
connectors={Connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
/>
|
||||
<ViewAsDialog connector={Connectors.view} />
|
||||
<AssetDetailsDialog
|
||||
assetId={id}
|
||||
trigger={trigger || null}
|
||||
|
||||
@@ -488,19 +488,7 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
</InputError>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2 pb-2 justify-end">
|
||||
<Checkbox
|
||||
name="reduce-only"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
label={
|
||||
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
|
||||
<span className="text-xs">{t('Reduce only')}</span>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<div className="flex gap-2 pb-2 justify-between">
|
||||
<Controller
|
||||
name="expire"
|
||||
control={control}
|
||||
@@ -511,11 +499,21 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
onCheckedChange={onCheckedChange}
|
||||
checked={value}
|
||||
name="expire"
|
||||
label={'Expire'}
|
||||
label={<span className="text-xs">{t('Expire')}</span>}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Checkbox
|
||||
name="reduce-only"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
label={
|
||||
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
|
||||
<span className="text-xs">{t('Reduce only')}</span>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{expire && (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
Icon,
|
||||
InputError,
|
||||
SimpleGrid,
|
||||
Tooltip,
|
||||
@@ -10,6 +9,8 @@ import {
|
||||
TradingDropdownRadioGroup,
|
||||
TradingDropdownRadioItem,
|
||||
TradingDropdownTrigger,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { Market, StaticMarketData } from '@vegaprotocol/markets';
|
||||
@@ -81,11 +82,11 @@ export const TypeToggle = ({
|
||||
}
|
||||
)}
|
||||
>
|
||||
<button>
|
||||
<button className="flex gap-1">
|
||||
<span className="text-ellipsis whitespace-nowrap shrink overflow-hidden">
|
||||
{t(selectedOption ? selectedOption.label : 'Stop')}
|
||||
</span>
|
||||
<Icon name="chevron-down" className="ml-1" />
|
||||
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={14} />
|
||||
</button>
|
||||
</TradingDropdownTrigger>
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './order-actions-dropdown';
|
||||
@@ -1,13 +0,0 @@
|
||||
import {
|
||||
ActionsDropdown,
|
||||
TradingDropdownCopyItem,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const OrderActionsDropdown = ({ id }: { id: string }) => {
|
||||
return (
|
||||
<ActionsDropdown data-testid="order-actions-content">
|
||||
<TradingDropdownCopyItem value={id} text={t('Copy order ID')} />
|
||||
</ActionsDropdown>
|
||||
);
|
||||
};
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
|
||||
import { OrderEditDialog } from '../order-list/order-edit-dialog';
|
||||
import type { Order } from '../order-data-provider';
|
||||
import { OrderViewDialog } from '../order-list/order-view-dialog';
|
||||
|
||||
export enum Filter {
|
||||
'Open' = 'Open',
|
||||
@@ -42,6 +43,7 @@ export const OrderListManager = ({
|
||||
}: OrderListManagerProps) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const [editOrder, setEditOrder] = useState<Order | null>(null);
|
||||
const [viewOrder, setViewOrder] = useState<Order | null>(null);
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const hasAmendableOrder = useHasAmendableOrder(marketId);
|
||||
const variables =
|
||||
@@ -89,6 +91,7 @@ export const OrderListManager = ({
|
||||
filter={filter}
|
||||
onCancel={cancel}
|
||||
onEdit={setEditOrder}
|
||||
onView={setViewOrder}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
isReadOnly={isReadOnly}
|
||||
@@ -134,6 +137,14 @@ export const OrderListManager = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{viewOrder && (
|
||||
<OrderViewDialog
|
||||
isOpen={Boolean(viewOrder)}
|
||||
order={viewOrder}
|
||||
onChange={() => setViewOrder(null)}
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@ const defaultProps: OrderListTableProps = {
|
||||
rowData: [],
|
||||
onEdit: jest.fn(),
|
||||
onCancel: jest.fn(),
|
||||
onView: jest.fn(),
|
||||
isReadOnly: false,
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ const Template: Story = (args) => {
|
||||
onEdit={() => {
|
||||
return;
|
||||
}}
|
||||
onView={() => {
|
||||
return;
|
||||
}}
|
||||
isReadOnly={false}
|
||||
/>
|
||||
</div>
|
||||
@@ -49,6 +52,9 @@ const Template2: Story = (args) => {
|
||||
rowData={args.data}
|
||||
onCancel={cancel}
|
||||
onEdit={setEditOrder}
|
||||
onView={() => {
|
||||
return;
|
||||
}}
|
||||
isReadOnly={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,14 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ActionsDropdown,
|
||||
ButtonLink,
|
||||
TradingDropdownCopyItem,
|
||||
DropdownMenuItem,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { memo, forwardRef, useMemo } from 'react';
|
||||
import {
|
||||
@@ -27,7 +34,6 @@ import type {
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { Order } from '../order-data-provider';
|
||||
import { OrderActionsDropdown } from '../order-actions-dropdown';
|
||||
import { Filter } from '../order-list-manager';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
|
||||
@@ -35,6 +41,7 @@ export type OrderListTableProps = TypedDataAgGrid<Order> & {
|
||||
marketId?: string;
|
||||
onCancel: (order: Order) => void;
|
||||
onEdit: (order: Order) => void;
|
||||
onView: (order: Order) => void;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
filter?: Filter;
|
||||
@@ -46,7 +53,15 @@ export const OrderListTable = memo<
|
||||
>(
|
||||
forwardRef<AgGridReact, OrderListTableProps>(
|
||||
(
|
||||
{ onCancel, onEdit, onMarketClick, onOrderTypeClick, filter, ...props },
|
||||
{
|
||||
onCancel,
|
||||
onEdit,
|
||||
onView,
|
||||
onMarketClick,
|
||||
onOrderTypeClick,
|
||||
filter,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const showAllActions = props.isReadOnly
|
||||
@@ -295,7 +310,20 @@ export const OrderListTable = memo<
|
||||
</ButtonLink>
|
||||
</>
|
||||
)}
|
||||
<OrderActionsDropdown id={data?.id} />
|
||||
<ActionsDropdown data-testid="market-actions-content">
|
||||
<TradingDropdownCopyItem
|
||||
value={data.id}
|
||||
text={t('Copy order ID')}
|
||||
/>
|
||||
<DropdownMenuItem
|
||||
key={'view-order'}
|
||||
data-testid="view-order"
|
||||
onClick={() => onView(data)}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.INFO} size={16} />
|
||||
{t('View order details')}
|
||||
</DropdownMenuItem>
|
||||
</ActionsDropdown>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -305,6 +333,7 @@ export const OrderListTable = memo<
|
||||
filter,
|
||||
onCancel,
|
||||
onEdit,
|
||||
onView,
|
||||
onMarketClick,
|
||||
onOrderTypeClick,
|
||||
props.isReadOnly,
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { OrderViewDialog } from './order-view-dialog';
|
||||
import type { Order } from '../order-data-provider';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import {
|
||||
MarketState,
|
||||
MarketTradingMode,
|
||||
OrderStatus,
|
||||
OrderTimeInForce,
|
||||
OrderType,
|
||||
Side,
|
||||
} from '@vegaprotocol/types';
|
||||
|
||||
describe('OrderViewDialog', () => {
|
||||
it('should render the order view dialog if the order is provided', async () => {
|
||||
const order: Order = {
|
||||
id: '7f2a78f370062e41683d26a0fcc4521b2bd4b747530b78bc6bc86195db0e5fb3',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: 'b66cd4be223dfd900a4750bb5175e17d8f678996877d262be4c749a99e22a970',
|
||||
decimalPlaces: 5,
|
||||
positionDecimalPlaces: 3,
|
||||
state: MarketState.STATE_ACTIVE,
|
||||
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
fees: {
|
||||
__typename: 'Fees',
|
||||
factors: {
|
||||
__typename: 'FeeFactors',
|
||||
makerFee: '0.0002',
|
||||
infrastructureFee: '0.0005',
|
||||
liquidityFee: '0.001',
|
||||
},
|
||||
},
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
id: '',
|
||||
name: 'Tesla Quarterly (Sep 2023)',
|
||||
code: 'TSLA.QM21',
|
||||
metadata: {
|
||||
__typename: 'InstrumentMetadata',
|
||||
tags: [
|
||||
'formerly:5A86B190C384997F',
|
||||
'quote:EURO',
|
||||
'ticker:TSLA',
|
||||
'class:equities/single-stock-futures',
|
||||
'sector:tech',
|
||||
'listing_venue:NASDAQ',
|
||||
'country:US',
|
||||
'auto:tsla',
|
||||
],
|
||||
},
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
id: '177e8f6c25a955bd18475084b99b2b1d37f28f3dec393fab7755a7e69c3d8c3b',
|
||||
symbol: 'tEURO',
|
||||
name: 'tEURO Fairground',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
},
|
||||
quoteName: 'EURO',
|
||||
dataSourceSpecForTradingTermination: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: '3f5941ba047dabb3bec23f112db0a8cc8aecbbbc6b34d1366d9383ed1b0f39df',
|
||||
data: {
|
||||
__typename: 'DataSourceDefinition',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration',
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
},
|
||||
},
|
||||
],
|
||||
filters: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: '05ca5485678aa7c705aa6a683fd062714cbcfdac2d1a27641cd27b66168d9c1d',
|
||||
data: {
|
||||
__typename: 'DataSourceDefinition',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration',
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
},
|
||||
},
|
||||
],
|
||||
filters: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
settlementDataProperty: 'prices.TSLA.value',
|
||||
tradingTerminationProperty: 'termination.TSLA.value',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
marketTimestamps: {
|
||||
__typename: 'MarketTimestamps',
|
||||
open: '2023-07-19T12:05:25.822854221Z',
|
||||
close: null,
|
||||
},
|
||||
},
|
||||
type: OrderType.TYPE_LIMIT,
|
||||
side: Side.SIDE_BUY,
|
||||
size: '10000',
|
||||
status: OrderStatus.STATUS_ACTIVE,
|
||||
rejectionReason: null,
|
||||
price: '15000000',
|
||||
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTT,
|
||||
remaining: '5000',
|
||||
expiresAt: null,
|
||||
createdAt: '2023-08-01T14:47:34.977742Z',
|
||||
updatedAt: null,
|
||||
postOnly: false,
|
||||
reduceOnly: false,
|
||||
liquidityProvision: null,
|
||||
peggedOrder: null,
|
||||
icebergOrder: {
|
||||
__typename: 'IcebergOrder',
|
||||
peakSize: '5000',
|
||||
minimumVisibleSize: '2000',
|
||||
reservedRemaining: '5000',
|
||||
},
|
||||
__typename: 'Order',
|
||||
};
|
||||
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<OrderViewDialog order={order} onChange={jest.fn()} isOpen={true} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('order-market-label')).toHaveTextContent(
|
||||
'Market'
|
||||
);
|
||||
expect(screen.getByTestId('order-market-value')).toHaveTextContent(
|
||||
'Tesla Quarterly (Sep 2023)'
|
||||
);
|
||||
expect(screen.getByTestId('order-type-label')).toHaveTextContent('Type');
|
||||
expect(screen.getByTestId('order-type-value')).toHaveTextContent('Limit');
|
||||
expect(screen.getByTestId('order-price-label')).toHaveTextContent('Price');
|
||||
expect(screen.getByTestId('order-price-value')).toHaveTextContent('150.00');
|
||||
expect(screen.getByTestId('order-size-label')).toHaveTextContent('Size');
|
||||
expect(screen.getByTestId('order-size-value')).toHaveTextContent('+10.00');
|
||||
expect(screen.getByTestId('order-remaining-label')).toHaveTextContent(
|
||||
'Remaining'
|
||||
);
|
||||
expect(screen.getByTestId('order-remaining-value')).toHaveTextContent(
|
||||
'+5.00'
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId('order-iceberg-order-reserved-remaining-value')
|
||||
).toHaveTextContent('5.00');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,280 @@
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
getDateTimeFormat,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Size } from '@vegaprotocol/datagrid';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
ButtonLink,
|
||||
Dialog,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
Tooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
truncateMiddle,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { Order } from '../order-data-provider';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { useCopyTimeout } from '@vegaprotocol/react-helpers';
|
||||
|
||||
interface OrderViewDialogProps {
|
||||
isOpen: boolean;
|
||||
order: Order;
|
||||
onChange: (open: boolean) => void;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
}
|
||||
|
||||
export const OrderViewDialog = ({
|
||||
isOpen,
|
||||
order,
|
||||
onChange,
|
||||
onMarketClick,
|
||||
}: OrderViewDialogProps) => {
|
||||
const [, setCopied] = useCopyTimeout();
|
||||
return (
|
||||
<Dialog open={isOpen} title={t('Order details')} onChange={onChange}>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow key={'order-market'}>
|
||||
<div data-testid={'order-market-label'}>{t('Market')}</div>
|
||||
<div data-testid={`order-market-value`}>
|
||||
{onMarketClick ? (
|
||||
<ButtonLink
|
||||
onClick={() => order.market && onMarketClick(order.market?.id)}
|
||||
>
|
||||
{order.market?.tradableInstrument.instrument.name}
|
||||
</ButtonLink>
|
||||
) : (
|
||||
order.market?.tradableInstrument.instrument.name
|
||||
)}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow key={'order-type'}>
|
||||
<div data-testid={'order-type-label'}>{t('Type')}</div>
|
||||
<div data-testid={`order-type-value`}>
|
||||
{Schema.OrderTypeMapping[order.type as Schema.OrderType]}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
{order.market && (
|
||||
<KeyValueTableRow key={'order-price'}>
|
||||
<div data-testid={'order-price-label'}>{t('Price')}</div>
|
||||
<div data-testid={`order-price-value`}>
|
||||
{addDecimalsFormatNumber(
|
||||
order.price,
|
||||
order.market.decimalPlaces as number
|
||||
)}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
{order.market && (
|
||||
<KeyValueTableRow key={'order-size'}>
|
||||
<div data-testid={'order-size-label'}>{t('Size')}</div>
|
||||
<div data-testid={`order-size-value`}>
|
||||
<Size
|
||||
value={order.size}
|
||||
side={order.side}
|
||||
positionDecimalPlaces={
|
||||
order.market.positionDecimalPlaces as number
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
{order.market && (
|
||||
<KeyValueTableRow key={'order-remaining'} className="mb-4">
|
||||
<div data-testid={'order-remaining-label'}>{t('Remaining')}</div>
|
||||
<div data-testid={`order-remaining-value`}>
|
||||
<Size
|
||||
value={order.remaining}
|
||||
side={order.side}
|
||||
positionDecimalPlaces={
|
||||
order.market.positionDecimalPlaces as number
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow key={'order-status'}>
|
||||
<div data-testid={'order-status-label'}>{t('Status')}</div>
|
||||
<div data-testid={`order-status-value`}>
|
||||
{Schema.OrderStatusMapping[order.status as Schema.OrderStatus]}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
{order.rejectionReason && (
|
||||
<KeyValueTableRow key={'order-rejection-reason'}>
|
||||
<div data-testid={'order-rejection-reason-label'}>
|
||||
{t('rejection reason')}
|
||||
</div>
|
||||
<div data-testid={`order-rejection-reason-value`}>
|
||||
{
|
||||
Schema.OrderRejectionReasonMapping[
|
||||
order.rejectionReason as Schema.OrderRejectionReason
|
||||
]
|
||||
}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow key={'order-id'}>
|
||||
<div data-testid={'order-id-label'}>{t('Order ID')}</div>
|
||||
<div data-testid={`order-id-value`}>
|
||||
{truncateMiddle(order.id, 10)}
|
||||
<CopyToClipboard text={order.id} onCopy={() => setCopied(true)}>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="copy-order-id"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<span className="sr-only">{t('Copy')}</span>
|
||||
<VegaIcon name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow key={'order-created'}>
|
||||
<div data-testid={'order-created-label'}>{t('Created')}</div>
|
||||
<div data-testid={`order-created-value`}>
|
||||
{getDateTimeFormat().format(new Date(order.createdAt))}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
{order.updatedAt && (
|
||||
<KeyValueTableRow key={'order-updated'}>
|
||||
<div data-testid={'order-updated-label'}>{t('Updated')}</div>
|
||||
<div data-testid={`order-updated-value`}>
|
||||
{getDateTimeFormat().format(new Date(order.updatedAt))}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
{order.expiresAt && (
|
||||
<KeyValueTableRow key={'order-expires'}>
|
||||
<div data-testid={'order-expires-label'}>{t('Expires')}</div>
|
||||
<div data-testid={`order-expires-value`}>
|
||||
{getDateTimeFormat().format(new Date(order.expiresAt))}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
|
||||
<KeyValueTableRow key={'order-time-in-force'} className="mt-4">
|
||||
<div data-testid={'order-time-in-force-label'}>
|
||||
{t('Time in force')}
|
||||
</div>
|
||||
<div data-testid={`order-time-in-force-value`}>
|
||||
{
|
||||
Schema.OrderTimeInForceMapping[
|
||||
order.timeInForce as Schema.OrderTimeInForce
|
||||
]
|
||||
}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
|
||||
<KeyValueTableRow key={'order-post-only'}>
|
||||
<div data-testid={'order-post-only-label'}>{t('Post only')}</div>
|
||||
<div data-testid={`order-post-only-value`}>
|
||||
{order.postOnly ? t('Yes') : t('-')}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
|
||||
<KeyValueTableRow key={'order-reduce-only'}>
|
||||
<div data-testid={'order-reduce-only-label'}>{t('Reduce only')}</div>
|
||||
<div data-testid={`order-reduce-only-value`}>
|
||||
{order.reduceOnly ? t('Yes') : t('-')}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
|
||||
<KeyValueTableRow key={'order-pegged'}>
|
||||
<div data-testid={'order-pegged-label'}>{t('Pegged')}</div>
|
||||
<div data-testid={`order-pegged-value`}>
|
||||
{order.peggedOrder ? t('Yes') : t('-')}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
|
||||
<KeyValueTableRow key={'order-liquidity-provision'}>
|
||||
<div data-testid={'order-liquidity-provision-label'}>
|
||||
{t('Liquidity provision')}
|
||||
</div>
|
||||
<div data-testid={`order-liquidity-provision-value`}>
|
||||
{order.liquidityProvision ? t('Yes') : t('-')}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
|
||||
<KeyValueTableRow key={'order-iceberg-order'}>
|
||||
<div data-testid={'order-iceberg-order-label'}>
|
||||
{t('Iceberg order')}
|
||||
</div>
|
||||
<div data-testid={`order-iceberg-order-value`}>
|
||||
{order.icebergOrder ? t('Yes') : t('-')}
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
{order.icebergOrder && (
|
||||
<KeyValueTableRow
|
||||
key={'order-iceberg-order-peak-size'}
|
||||
className="ml-4"
|
||||
>
|
||||
<div data-testid={'order-iceberg-order-peak-size-label'}>
|
||||
<Tooltip
|
||||
description={t(
|
||||
'The maximum volume that can be traded at once. Must be less than the total size of the order.'
|
||||
)}
|
||||
>
|
||||
<span>{t('Peak size')}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div data-testid={`order-iceberg-order-peak-size-value`}>
|
||||
<Size
|
||||
value={order.icebergOrder.peakSize}
|
||||
side={order.side}
|
||||
positionDecimalPlaces={
|
||||
order.market?.positionDecimalPlaces as number
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
{order.icebergOrder && (
|
||||
<KeyValueTableRow
|
||||
key={'order-iceberg-order-minimum-visible-size'}
|
||||
className="ml-4"
|
||||
>
|
||||
<div data-testid={'order-iceberg-order-minimum-visible-size-label'}>
|
||||
<Tooltip
|
||||
description={t(
|
||||
'When the order trades and its size falls below this threshold, it will be reset to the peak size and moved to the back of the priority order. Must be less than or equal to peak size, and greater than 0.'
|
||||
)}
|
||||
>
|
||||
<span>{t('Minimum size')}</span>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div data-testid={`order-iceberg-order-minimum-visible-size-value`}>
|
||||
<Size
|
||||
value={order.icebergOrder.minimumVisibleSize}
|
||||
side={order.side}
|
||||
positionDecimalPlaces={
|
||||
order.market?.positionDecimalPlaces as number
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
{order.icebergOrder && (
|
||||
<KeyValueTableRow
|
||||
key={'order-iceberg-order-reserved-remaining'}
|
||||
className="ml-4"
|
||||
>
|
||||
<div data-testid={'order-iceberg-order-reserved-remaining-label'}>
|
||||
{t('Reserved remaining')}
|
||||
</div>
|
||||
<div data-testid={`order-iceberg-order-reserved-remaining-value`}>
|
||||
<Size
|
||||
value={order.icebergOrder.reservedRemaining}
|
||||
side={order.side}
|
||||
positionDecimalPlaces={
|
||||
order.market?.positionDecimalPlaces as number
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
export const IconEye = ({ size = 16 }: { size: number }) => {
|
||||
return (
|
||||
<svg width={size} height={size} viewBox="0 0 16 16">
|
||||
<path d="M0.678395 7.44128C2.71173 4.52663 5.34817 2.99829 7.9894 2.99829C11.0914 2.99829 13.7635 5.01915 15.34 7.45404L15.3408 7.45519C15.4451 7.61729 15.5006 7.80599 15.5006 7.99876C15.5006 8.19118 15.4453 8.37954 15.3414 8.54144C13.7662 11.0077 11.1114 12.9983 7.9894 12.9983C4.8342 12.9983 2.23054 11.0118 0.659261 8.55277C0.552495 8.38698 0.497119 8.19332 0.500115 7.99613C0.503119 7.79845 0.564651 7.6061 0.676935 7.44338L0.67839 7.44127L0.678395 7.44128ZM1.5 8.01133L1.50139 8.01348L1.50139 8.01349C2.95267 10.2852 5.27875 11.9983 7.9894 11.9983C10.6697 11.9983 13.0459 10.278 14.4989 8.00262L14.4999 8.00113C14.5001 8.00073 14.5003 8.0003 14.5005 7.99985C14.5005 7.9995 14.5006 7.99913 14.5006 7.99876C14.5006 7.99792 14.5003 7.9971 14.4999 7.99639L14.9203 7.72579L14.5006 7.99754C13.0421 5.74493 10.6461 3.99829 7.9894 3.99829C5.75997 3.99829 3.40017 5.28866 1.5 8.01133Z" />
|
||||
<path d="M8 5.99951C6.89543 5.99951 6 6.89494 6 7.99951C6 9.10408 6.89543 9.99951 8 9.99951C9.10457 9.99951 10 9.10408 10 7.99951C10 6.89494 9.10457 5.99951 8 5.99951ZM5 7.99951C5 6.34266 6.34315 4.99951 8 4.99951C9.65685 4.99951 11 6.34266 11 7.99951C11 9.65637 9.65685 10.9995 8 10.9995C6.34315 10.9995 5 9.65637 5 7.99951Z" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ import { IconCross } from './svg-icons/icon-cross';
|
||||
import { IconDeposit } from './svg-icons/icon-deposit';
|
||||
import { IconEdit } from './svg-icons/icon-edit';
|
||||
import { IconExclaimationMark } from './svg-icons/icon-exclaimation-mark';
|
||||
import { IconEye } from './svg-icons/icon-eye';
|
||||
import { IconForum } from './svg-icons/icon-forum';
|
||||
import { IconGlobe } from './svg-icons/icon-globe';
|
||||
import { IconInfo } from './svg-icons/icon-info';
|
||||
@@ -48,6 +49,7 @@ export enum VegaIconNames {
|
||||
DEPOSIT = 'deposit',
|
||||
EDIT = 'edit',
|
||||
EXCLAIMATION_MARK = 'exclaimation-mark',
|
||||
EYE = 'eye',
|
||||
FORUM = 'forum',
|
||||
GLOBE = 'globe',
|
||||
INFO = 'info',
|
||||
@@ -88,6 +90,7 @@ export const VegaIconNameMap: Record<
|
||||
deposit: IconDeposit,
|
||||
edit: IconEdit,
|
||||
'exclaimation-mark': IconExclaimationMark,
|
||||
eye: IconEye,
|
||||
forum: IconForum,
|
||||
globe: IconGlobe,
|
||||
info: IconInfo,
|
||||
|
||||
@@ -21,7 +21,7 @@ export const NotificationBanner = ({
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'flex items-center p-3 border-b min-h-[56px]',
|
||||
'flex items-center px-1 py-3 border-b min-h-[56px]',
|
||||
'text-[12px] leading-[16px] font-normal',
|
||||
{
|
||||
'bg-vega-light-100 dark:bg-vega-dark-100 ': intent === Intent.None,
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
import { Intent } from '../../utils/intent';
|
||||
|
||||
type TradingButtonProps = {
|
||||
size?: 'large' | 'medium' | 'small';
|
||||
size?: 'large' | 'medium' | 'small' | 'extra-small';
|
||||
intent?: Intent;
|
||||
children?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
@@ -31,6 +31,7 @@ const getClassName = (
|
||||
'h-10': !subLabel && (!size || size === 'medium'),
|
||||
'h-8': !subLabel && size === 'small',
|
||||
'px-3 text-sm': !subLabel && size === 'small',
|
||||
'h-6 px-2 text-xs': !subLabel && size === 'extra-small',
|
||||
'px-4 text-base': !subLabel && size !== 'small',
|
||||
'flex-col items-center justify-center px-3 pt-2.5 pb-2': subLabel,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { NotificationBanner } from '../notification-banner';
|
||||
import { Intent } from '../../utils/intent';
|
||||
import { TradingButton } from '../trading-button';
|
||||
|
||||
export function truncateMiddle(address: string, start = 6, end = 4) {
|
||||
if (address.length < 11) return address;
|
||||
@@ -24,19 +25,20 @@ export const ViewingAsBanner = ({
|
||||
<NotificationBanner
|
||||
data-testid="view-banner"
|
||||
intent={Intent.None}
|
||||
className="py-2 min-h-fit"
|
||||
className="!px-1 !py-1 min-h-fit"
|
||||
>
|
||||
<div className="flex justify-between items-baseline">
|
||||
<span>
|
||||
{t('Viewing as Vega user:')} {pubKey && truncateMiddle(pubKey)}{' '}
|
||||
</span>
|
||||
<button
|
||||
className="p-2 bg-light-dark-150 dark:bg-vega-dark-150 rounded uppercase"
|
||||
<TradingButton
|
||||
intent={Intent.None}
|
||||
size="extra-small"
|
||||
data-testid="exit-view"
|
||||
onClick={disconnect}
|
||||
>
|
||||
{t('Exit view as')}
|
||||
</button>
|
||||
</TradingButton>
|
||||
</div>
|
||||
</NotificationBanner>
|
||||
);
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './connect-dialog';
|
||||
export * from './view-as-dialog';
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { Dialog } from '@vegaprotocol/ui-toolkit';
|
||||
import { ViewConnectorForm } from './view-connector-form';
|
||||
import type { ViewConnector } from '../connectors';
|
||||
import { create } from 'zustand';
|
||||
|
||||
type ViewAsDialogStore = {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export const useViewAsDialog = create<ViewAsDialogStore>()((set) => ({
|
||||
open: false,
|
||||
setOpen: (open) => set({ open }),
|
||||
}));
|
||||
|
||||
type ViewAsDialogProps = {
|
||||
connector: ViewConnector;
|
||||
};
|
||||
|
||||
export const ViewAsDialog = ({ connector }: ViewAsDialogProps) => {
|
||||
const open = useViewAsDialog((state) => state.open);
|
||||
const setOpen = useViewAsDialog((state) => state.setOpen);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
size="small"
|
||||
onChange={(open) => {
|
||||
setOpen(open);
|
||||
}}
|
||||
>
|
||||
<ViewConnectorForm
|
||||
connector={connector}
|
||||
onConnect={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -2,9 +2,10 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
Icon,
|
||||
Input,
|
||||
InputError,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import type { ViewConnector } from '../connectors';
|
||||
@@ -17,7 +18,7 @@ interface FormFields {
|
||||
interface RestConnectorFormProps {
|
||||
connector: ViewConnector;
|
||||
onConnect: (connector: ViewConnector) => void;
|
||||
reset: () => void;
|
||||
reset?: () => void;
|
||||
}
|
||||
|
||||
export function ViewConnectorForm({
|
||||
@@ -50,13 +51,19 @@ export function ViewConnectorForm({
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={reset}
|
||||
className="absolute p-2 top-0 left-0 md:top-2 md:left-2"
|
||||
data-testid="back-button"
|
||||
>
|
||||
<Icon name={'chevron-left'} ariaLabel="back" size={4} />
|
||||
</button>
|
||||
{reset && (
|
||||
<button
|
||||
onClick={reset}
|
||||
className="absolute p-2 top-0 left-0 md:top-2 md:left-2"
|
||||
data-testid="back-button"
|
||||
>
|
||||
<VegaIcon
|
||||
name={VegaIconNames.CHEVRON_LEFT}
|
||||
aria-label="back"
|
||||
size={16}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
<form onSubmit={handleSubmit(onSubmit)} data-testid="view-connector-form">
|
||||
<h1 className="text-2xl uppercase mb-6 text-center font-alpha calt">
|
||||
{t('VIEW AS VEGA USER')}
|
||||
|
||||
Reference in New Issue
Block a user