Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f2105710c | ||
|
|
570472b739 | ||
|
|
28f7bd36e7 | ||
|
|
0f3e5595ba | ||
|
|
d3dbdd2bd5 | ||
|
|
0767139712 | ||
|
|
250492a02c | ||
|
|
29f3374c61 | ||
|
|
ba7b574a07 | ||
|
|
afd8650657 | ||
|
|
78414b4429 | ||
|
|
e0a91b3850 | ||
|
|
80f7a08765 | ||
|
|
3e627ff849 | ||
|
|
d2854b6e90 | ||
|
|
6d130c9cfc |
@@ -125,7 +125,7 @@ jobs:
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
working-directory: ./console-test
|
||||
run: poetry run pytest -s --numprocesses auto
|
||||
run: poetry run pytest -s --numprocesses auto --dist loadfile
|
||||
- name: Check files
|
||||
run: |
|
||||
ls -al .
|
||||
|
||||
@@ -29,37 +29,6 @@ fragment ExplorerPartyAssetsAccounts on AccountBalance {
|
||||
}
|
||||
}
|
||||
|
||||
fragment ExplorerPartyLinks on AccountBalance {
|
||||
asset {
|
||||
name
|
||||
id
|
||||
decimals
|
||||
symbol
|
||||
source {
|
||||
__typename
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
type
|
||||
balance
|
||||
market {
|
||||
id
|
||||
decimalPlaces
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
product {
|
||||
... on Future {
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ExplorerPartyAssets($partyId: ID!) {
|
||||
partiesConnection(id: $partyId) {
|
||||
edges {
|
||||
|
||||
@@ -64,7 +64,7 @@ export const ExplorerPartyAssetsDocument = gql`
|
||||
}
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
linkings(pagination: {first: 100}) {
|
||||
linkings(pagination: {last: 100}) {
|
||||
edges {
|
||||
node {
|
||||
type
|
||||
@@ -113,4 +113,4 @@ export function useExplorerPartyAssetsLazyQuery(baseOptions?: Apollo.LazyQueryHo
|
||||
}
|
||||
export type ExplorerPartyAssetsQueryHookResult = ReturnType<typeof useExplorerPartyAssetsQuery>;
|
||||
export type ExplorerPartyAssetsLazyQueryHookResult = ReturnType<typeof useExplorerPartyAssetsLazyQuery>;
|
||||
export type ExplorerPartyAssetsQueryResult = Apollo.QueryResult<ExplorerPartyAssetsQuery, ExplorerPartyAssetsQueryVariables>;
|
||||
export type ExplorerPartyAssetsQueryResult = Apollo.QueryResult<ExplorerPartyAssetsQuery, ExplorerPartyAssetsQueryVariables>;
|
||||
@@ -42,14 +42,14 @@ export const PartyBlockStake = ({
|
||||
linkedLength && linkedLength > 0
|
||||
? p?.stakingSummary?.linkings?.edges
|
||||
?.reduce((total, e) => {
|
||||
const accumulator = new BigNumber(total)
|
||||
const diff = new BigNumber(e?.node.amount || 0)
|
||||
const accumulator = new BigNumber(total);
|
||||
const diff = new BigNumber(e?.node.amount || 0);
|
||||
if (e?.node.type === 'TYPE_LINK') {
|
||||
return accumulator.plus(diff);
|
||||
} else if (e?.node.type === 'TYPE_UNLINK') {
|
||||
return accumulator.minus(diff);
|
||||
} else {
|
||||
return accumulator
|
||||
return accumulator;
|
||||
}
|
||||
}, new BigNumber(0))
|
||||
.toString()
|
||||
|
||||
@@ -361,6 +361,34 @@ describe(
|
||||
stakingPageDisassociateAllTokens();
|
||||
});
|
||||
|
||||
it('Error message should be displayed if error returned from wallet when voting', function () {
|
||||
const errorMsg =
|
||||
'Application error: party has already submitted the maximum number of transactions of this type per epoch (3)';
|
||||
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
cy.intercept('POST', '/api/v2/requests', {
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: 2001,
|
||||
message: 'Application error',
|
||||
data: 'party has already submitted the maximum number of transactions of this type per epoch (3)',
|
||||
},
|
||||
id: '-PK5EGmErnjLhAmzMeclC',
|
||||
});
|
||||
cy.contains('Vote breakdown').should('be.visible', { timeout: 10000 });
|
||||
cy.getByTestId('vote-buttons').contains('for').click();
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'have.text',
|
||||
'Transaction failed'
|
||||
);
|
||||
cy.getByTestId('Error').should('have.text', errorMsg);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to see successor market details with new and updated values', function () {
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
|
||||
@@ -119,6 +119,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-001 0006-NETW-002
|
||||
it('should display network data', function () {
|
||||
cy.getByTestId('git-network-data')
|
||||
.should('contain.text', 'Reading network data from')
|
||||
@@ -130,6 +131,37 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
|
||||
it('should have option to switch to different network node', function () {
|
||||
cy.getByTestId('git-network-data').within(() => {
|
||||
cy.getByTestId('link').click();
|
||||
});
|
||||
cy.getByTestId('node-row').within(() => {
|
||||
cy.getByTestId('node-url-0')
|
||||
.parent()
|
||||
.should('have.text', 'http://localhost:3008/graphql');
|
||||
cy.getByTestId('response-time-cell')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.and('not.eq', 'Checking');
|
||||
cy.getByTestId('block-height-cell')
|
||||
.invoke('text')
|
||||
.should('not.be.empty')
|
||||
.then((currentBlockHeight) => {
|
||||
// Check that block height updates automatically
|
||||
cy.getByTestId('block-height-cell')
|
||||
.invoke('text')
|
||||
.should('not.eq', currentBlockHeight);
|
||||
});
|
||||
cy.getByTestId('subscription-cell').should('have.text', 'Yes');
|
||||
});
|
||||
cy.getByTestId('connect').should('be.disabled');
|
||||
cy.getByTestId('node-url-custom').click();
|
||||
cy.get('input').should('exist');
|
||||
cy.getByTestId('connect').should('be.disabled');
|
||||
cy.getByTestId('icon-cross').click();
|
||||
});
|
||||
|
||||
it('should display eth data', function () {
|
||||
cy.getByTestId('git-eth-data')
|
||||
.should('contain.text', 'Reading Ethereum data from')
|
||||
@@ -138,6 +170,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
// 0006-NETW-011
|
||||
it('should contain link for known issues on Github', function () {
|
||||
cy.getByTestId('git-info').within(() => {
|
||||
cy.contains('Known issues and feedback on')
|
||||
|
||||
@@ -183,9 +183,8 @@ export function clickOnValidatorFromList(
|
||||
cy.get(`[row-id="${validatorNumber}"]`)
|
||||
.should('be.visible')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(stakeValidatorListName).click();
|
||||
});
|
||||
.as('validatorOnList');
|
||||
cy.get('@validatorOnList').click();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ 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
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
@@ -19,6 +19,9 @@ 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_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
|
||||
@@ -26,4 +29,4 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
CYPRESS_FAIRGROUND=false
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -14,8 +14,11 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -14,9 +14,11 @@ 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_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -13,9 +13,11 @@ 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_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -10,8 +10,11 @@ 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_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -15,8 +15,11 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -12,8 +12,11 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
|
||||
@@ -36,4 +36,4 @@ CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -259,6 +259,12 @@ describe('Closed markets', { tags: '@smoke' }, () => {
|
||||
.find('[data-testid="market-code"]')
|
||||
.should('have.text', settledMarket.tradableInstrument.instrument.code);
|
||||
|
||||
// 6001-MARK-071
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[title="Future"]')
|
||||
.should('have.text', 'Futr');
|
||||
|
||||
// 6001-MARK-002
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
|
||||
@@ -69,6 +69,12 @@ describe('markets all table', { tags: '@smoke' }, () => {
|
||||
.find(colInstrumentCode)
|
||||
.should('have.text', 'SOLUSD');
|
||||
|
||||
// 6001-MARK-073
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[title="Future"]')
|
||||
.should('have.text', 'Futr');
|
||||
|
||||
// 6001-MARK-036
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
|
||||
@@ -1,233 +0,0 @@
|
||||
import { aliasGQLQuery, checkSorting } from '@vegaprotocol/cypress';
|
||||
import type { ProposalsListQuery } from '@vegaprotocol/proposals';
|
||||
|
||||
const rowSelector =
|
||||
'[data-testid="tab-proposed-markets"] .ag-center-cols-container .ag-row';
|
||||
const colMarketId = '[col-id="market"] [data-testid="market-code"]';
|
||||
|
||||
describe('markets proposed table', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
});
|
||||
|
||||
it('can see table headers', () => {
|
||||
const headers = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Settlement asset',
|
||||
'State',
|
||||
'Parent market',
|
||||
'Voting',
|
||||
'Closing date',
|
||||
'Enactment date',
|
||||
'',
|
||||
];
|
||||
cy.getByTestId('tab-proposed-markets').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
// 6001-MARK-049
|
||||
cy.get(rowSelector).first().find(colMarketId).should('have.text', 'ETHUSD');
|
||||
|
||||
// 6001-MARK-050
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="description"]')
|
||||
.should('have.text', 'ETHUSD');
|
||||
|
||||
// 6001-MARK-051
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="asset"]')
|
||||
.should('have.text', 'tDAI TEST');
|
||||
|
||||
// 6001-MARK-052
|
||||
// 6001-MARK-053
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', 'Open');
|
||||
|
||||
// 6001-MARK-054
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="voting"]')
|
||||
.should('have.text', '');
|
||||
|
||||
// 6001-MARK-056
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="closing-date"]')
|
||||
.should('not.be.empty');
|
||||
|
||||
// 6001-MARK-057
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="enactment-date"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('can open row actions', () => {
|
||||
// 6001-MARK-058
|
||||
cy.get('.ag-pinned-right-cols-container')
|
||||
.find('[col-id="proposal-actions"]')
|
||||
.first()
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
const dropdownContent = '[data-testid="proposal-actions-content"]';
|
||||
const dropdownContentItem = '[role="menuitem"]';
|
||||
|
||||
// 6001-MARK-059
|
||||
cy.get(dropdownContent)
|
||||
.find(dropdownContentItem)
|
||||
.eq(0)
|
||||
.find('a')
|
||||
.should('have.text', 'View proposal')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env(
|
||||
'VEGA_TOKEN_URL'
|
||||
)}/proposals/e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829`
|
||||
);
|
||||
});
|
||||
|
||||
// 6001-MARK-060
|
||||
it('can see proposed market link', () => {
|
||||
cy.getByTestId('tab-proposed-markets')
|
||||
.find('[data-testid="external-link"]')
|
||||
.should('have.length', 11)
|
||||
.last()
|
||||
.should('have.text', 'Propose a new market')
|
||||
.and(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market`
|
||||
);
|
||||
});
|
||||
it('proposed markets tab should be sorted properly', () => {
|
||||
// 6001-MARK-062
|
||||
cy.get('[data-testid="Proposed markets"]').click({ force: true });
|
||||
const marketColDefault = [
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'TSLA.QM21',
|
||||
'AAVEDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColAsc = [
|
||||
'AAPL.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'BTCUSD.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'ETHDAI.MF21',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'LINKUSD',
|
||||
'TSLA.QM21',
|
||||
'UNIDAI.MF21',
|
||||
];
|
||||
const marketColDesc = [
|
||||
'UNIDAI.MF21',
|
||||
'TSLA.QM21',
|
||||
'LINKUSD',
|
||||
'ETHUSD',
|
||||
'ETHUSD',
|
||||
'ETHDAI.MF21',
|
||||
'ETHBTC.QM21',
|
||||
'BTCUSD.MF21',
|
||||
'AAVEDAI.MF21',
|
||||
'AAPL.MF21',
|
||||
];
|
||||
checkSorting(
|
||||
'market',
|
||||
marketColDefault,
|
||||
marketColAsc,
|
||||
marketColDesc,
|
||||
' [data-testid="market-code"]'
|
||||
);
|
||||
|
||||
const stateColDefault = [
|
||||
'Open',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Waiting for Node Vote',
|
||||
'Open',
|
||||
];
|
||||
const stateColAsc = [
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
];
|
||||
const stateColDesc = [
|
||||
'Waiting for Node Vote',
|
||||
'Waiting for Node Vote',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Passed',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
'Open',
|
||||
];
|
||||
checkSorting('state', stateColDefault, stateColAsc, stateColDesc);
|
||||
});
|
||||
|
||||
it('can drag and drop columns', () => {
|
||||
// 6001-MARK-063
|
||||
cy.get(colMarketId).realMouseDown().realMouseMove(700, 15).realMouseUp();
|
||||
cy.get(colMarketId).should(($element) => {
|
||||
const attributeValue = $element.attr('aria-colindex');
|
||||
expect(attributeValue).not.to.equal('1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('no markets proposed', { tags: '@smoke', testIsolation: true }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
const proposal: ProposalsListQuery = {};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ProposalsList', proposal);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
});
|
||||
|
||||
it.skip('can see no markets message', () => {
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
|
||||
// 6001-MARK-061
|
||||
cy.getByTestId('tab-proposed-markets').should('contain.text', 'No markets');
|
||||
});
|
||||
});
|
||||
@@ -23,7 +23,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
// 0003-WTXN-001
|
||||
cy.getByTestId('connect-vega-wallet'); // Not connected
|
||||
cy.getByTestId(placeOrderBtn).should('exist');
|
||||
cy.getByTestId('get-started-button').should('exist');
|
||||
cy.getByTestId('order-connect-wallet').should('exist');
|
||||
});
|
||||
|
||||
it('must be able to select order direction - long/short', function () {
|
||||
@@ -44,7 +44,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
mockConnectWallet();
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
cy.getByTestId(orderPriceField).clear().type('101');
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.getByTestId('order-connect-wallet').click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
@@ -54,6 +54,15 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId(toggleLimit).next('input').should('be.checked');
|
||||
cy.getByTestId(orderPriceField).should('have.value', '101');
|
||||
});
|
||||
|
||||
it('sidebar should be open after reload', () => {
|
||||
cy.mockTradingPage();
|
||||
cy.getByTestId('deal-ticket-form').should('be.visible');
|
||||
cy.getByTestId('Order').click();
|
||||
cy.getByTestId('deal-ticket-form').should('not.exist');
|
||||
cy.reload();
|
||||
cy.getByTestId('deal-ticket-form').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe(
|
||||
|
||||
@@ -222,12 +222,17 @@ describe('subscribe orders', { tags: '@smoke' }, () => {
|
||||
|
||||
it('must see a filled order', () => {
|
||||
// 7002-SORD-046
|
||||
// 7003-MORD-020
|
||||
// NOT COVERED: Must be able to see/link to all trades that were created from this order
|
||||
updateOrder({
|
||||
id: orderId,
|
||||
status: Schema.OrderStatus.STATUS_FILLED,
|
||||
});
|
||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled');
|
||||
cy.get('[col-id="market.tradableInstrument.instrument.code"]').contains(
|
||||
'[title="Future"]',
|
||||
'Futr'
|
||||
);
|
||||
});
|
||||
|
||||
it('must see a rejected order', () => {
|
||||
|
||||
+1
-2
@@ -12,8 +12,7 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -12,6 +12,8 @@ NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_DOCS_URL=#
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet-mainnet
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.20.21-core-0.71.6
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.mainnet-mirror.vega.rocks
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-mainnet/codfcglpplgmmlokgilfkpcjnmkbfiel
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet-mainnet
|
||||
# TAG name of the current app version - TODO: bump to the latest upon release
|
||||
NX_APP_VERSION=v0.20.19-core-0.71.6
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
@@ -14,6 +14,8 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
@@ -22,4 +24,4 @@ NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
@@ -15,6 +15,9 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
|
||||
|
||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/pl/firefox/addon/vega-wallet
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
NX_STOP_ORDERS=false
|
||||
|
||||
@@ -94,7 +94,11 @@ const MainGrid = memo(
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-bottom">
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<Tab
|
||||
id="positions"
|
||||
name={t('Positions')}
|
||||
menu={<TradingViews.positions.menu />}
|
||||
>
|
||||
<TradingViews.positions.component />
|
||||
</Tab>
|
||||
<Tab
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { OrderContainerProps } from '../../components/orders-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
||||
import { AccountsMenu } from '../../components/accounts-menu';
|
||||
import { PositionsMenu } from '../../components/positions-menu';
|
||||
|
||||
type MarketDependantView =
|
||||
| typeof CandlesChartContainer
|
||||
@@ -57,7 +58,11 @@ export const TradingViews = {
|
||||
label: 'Trades',
|
||||
component: requiresMarket(TradesContainer),
|
||||
},
|
||||
positions: { label: 'Positions', component: PositionsContainer },
|
||||
positions: {
|
||||
label: 'Positions',
|
||||
component: PositionsContainer,
|
||||
menu: PositionsMenu,
|
||||
},
|
||||
activeOrders: {
|
||||
label: 'Active',
|
||||
component: (props: OrderContainerProps) => (
|
||||
|
||||
@@ -1,10 +1,2 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const THROTTLE_UPDATE_TIME = 500;
|
||||
export const ONBOARDING_VIEWED_KEY = 'vega_onboarding_viewed';
|
||||
export const MAINNET_WELCOME_HEADER = t(
|
||||
'Trade cash settled futures on the fully decentralised Vega network.'
|
||||
);
|
||||
export const TESTNET_WELCOME_HEADER = t(
|
||||
'Try out trading cash settled futures on the fully decentralised Vega network (Testnet).'
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import type {
|
||||
SuccessorProposalListFieldsFragment,
|
||||
NewMarketSuccessorFieldsFragment,
|
||||
@@ -52,7 +52,7 @@ export const MarketSuccessorProposalBanner = ({
|
||||
TOKEN_PROPOSAL.replace(':id', item.id || '')
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Fragment key={i}>
|
||||
<ExternalLink href={externalLink} key={i}>
|
||||
{
|
||||
(item.terms?.change as NewMarketSuccessorFieldsFragment)
|
||||
@@ -60,7 +60,7 @@ export const MarketSuccessorProposalBanner = ({
|
||||
}
|
||||
</ExternalLink>
|
||||
{i < successors.length - 1 && ', '}
|
||||
</>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ 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';
|
||||
import { useMarket, useMarketList } from '@vegaprotocol/markets';
|
||||
import { useState } from 'react';
|
||||
|
||||
export const MarketHeader = () => {
|
||||
@@ -11,6 +11,10 @@ export const MarketHeader = () => {
|
||||
const { data } = useMarket(marketId);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Ensure that markets are kept cached so opening the list
|
||||
// shows all markets instantly
|
||||
useMarketList();
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -132,6 +132,7 @@ describe('MarketSelector', () => {
|
||||
data: markets,
|
||||
loading: false,
|
||||
error: undefined,
|
||||
reload: jest.fn(),
|
||||
});
|
||||
|
||||
it('Button "All" should be selected by default', () => {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import uniqBy from 'lodash/uniqBy';
|
||||
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
|
||||
import { type MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
|
||||
import {
|
||||
Input,
|
||||
TradingInput,
|
||||
TinyScroll,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useCallback, useState, useMemo, useRef } from 'react';
|
||||
import { useCallback, useState, useMemo, useRef, useEffect } from 'react';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import { useMarketSelectorList } from './use-market-selector-list';
|
||||
import type { ProductType } from './product-selector';
|
||||
@@ -44,7 +44,12 @@ export const MarketSelector = ({
|
||||
assets: [],
|
||||
});
|
||||
const allProducts = filter.product === Product.All;
|
||||
const { markets, data, loading, error } = useMarketSelectorList(filter);
|
||||
const { markets, data, loading, error, reload } =
|
||||
useMarketSelectorList(filter);
|
||||
|
||||
useEffect(() => {
|
||||
reload();
|
||||
}, [reload]);
|
||||
|
||||
return (
|
||||
<div data-testid="market-selector">
|
||||
@@ -57,7 +62,7 @@ export const MarketSelector = ({
|
||||
/>
|
||||
<div className="text-sm grid grid-cols-[2fr_1fr_1fr] gap-1 ">
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
<TradingInput
|
||||
onChange={(e) =>
|
||||
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export const Sort = {
|
||||
Gained: 'Gained',
|
||||
Lost: 'Lost',
|
||||
New: 'New',
|
||||
TopTraded: 'TopTraded',
|
||||
} as const;
|
||||
|
||||
export type SortType = keyof typeof Sort;
|
||||
@@ -26,6 +27,7 @@ export const SortTypeMapping: {
|
||||
[Sort.Gained]: 'Top gaining',
|
||||
[Sort.Lost]: 'Top losing',
|
||||
[Sort.New]: 'New markets',
|
||||
[Sort.TopTraded]: 'Top traded',
|
||||
};
|
||||
|
||||
const SortIconMapping: {
|
||||
@@ -35,6 +37,7 @@ const SortIconMapping: {
|
||||
[Sort.Gained]: VegaIconNames.TREND_UP,
|
||||
[Sort.Lost]: VegaIconNames.TREND_DOWN,
|
||||
[Sort.New]: VegaIconNames.STAR,
|
||||
[Sort.TopTraded]: VegaIconNames.ARROW_UP,
|
||||
};
|
||||
|
||||
export const SortDropdown = ({
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { useMemo } from 'react';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
|
||||
import {
|
||||
calcCandleVolume,
|
||||
calcTradedFactor,
|
||||
useMarketList,
|
||||
} from '@vegaprotocol/markets';
|
||||
import { priceChangePercentage } from '@vegaprotocol/utils';
|
||||
import type { Filter } from '../../components/market-selector/market-selector';
|
||||
import { Sort } from './sort-dropdown';
|
||||
@@ -20,7 +24,7 @@ export const useMarketSelectorList = ({
|
||||
sort,
|
||||
searchTerm,
|
||||
}: Filter) => {
|
||||
const { data, loading, error } = useMarketList();
|
||||
const { data, loading, error, reload } = useMarketList();
|
||||
|
||||
const markets = useMemo(() => {
|
||||
if (!data?.length) return [];
|
||||
@@ -94,10 +98,14 @@ export const useMarketSelectorList = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (sort === Sort.TopTraded) {
|
||||
return orderBy(markets, [(m) => calcTradedFactor(m)], ['desc']);
|
||||
}
|
||||
|
||||
return markets;
|
||||
}, [data, product, searchTerm, assets, sort]);
|
||||
|
||||
return { markets, data, loading, error };
|
||||
return { markets, data, loading, error, reload };
|
||||
};
|
||||
|
||||
export const isMarketActive = (state: MarketState) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketSelector } from '../market-selector';
|
||||
import { useMarket } from '@vegaprotocol/markets';
|
||||
import { useMarket, useMarketList } from '@vegaprotocol/markets';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||
@@ -14,6 +14,10 @@ export const NavHeader = () => {
|
||||
const { data } = useMarket(marketId);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
// Ensure that markets are kept cached so opening the list
|
||||
// shows all markets instantly
|
||||
useMarketList();
|
||||
|
||||
if (!marketId) return null;
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import type { StateCreator } from 'zustand';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
@@ -13,6 +14,7 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
||||
|
||||
const showClosed = usePositionsStore((store) => store.showClosedMarkets);
|
||||
const gridStore = usePositionsStore((store) => store.gridStore);
|
||||
const updateGridStore = usePositionsStore((store) => store.updateGridStore);
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, updateGridStore);
|
||||
@@ -40,12 +42,35 @@ export const PositionsContainer = ({ allKeys }: { allKeys?: boolean }) => {
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
gridProps={gridStoreCallbacks}
|
||||
showClosed={showClosed}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const usePositionsStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_positions_store',
|
||||
})
|
||||
type PositionsStoreSlice = {
|
||||
showClosedMarkets: boolean;
|
||||
toggleClosedMarkets: () => void;
|
||||
};
|
||||
|
||||
const createPositionStoreSlice: StateCreator<PositionsStoreSlice> = (set) => ({
|
||||
showClosedMarkets: false,
|
||||
toggleClosedMarkets: () => {
|
||||
set((curr) => {
|
||||
return {
|
||||
showClosedMarkets: !curr.showClosedMarkets,
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const usePositionsStore = create<PositionsStoreSlice & DataGridSlice>()(
|
||||
persist(
|
||||
(...args) => ({
|
||||
...createPositionStoreSlice(...args),
|
||||
...createDataGridSlice(...args),
|
||||
}),
|
||||
{
|
||||
name: 'vega_positions_store',
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './positions-menu';
|
||||
@@ -0,0 +1,18 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { usePositionsStore } from '../positions-container';
|
||||
|
||||
export const PositionsMenu = () => {
|
||||
const showClosed = usePositionsStore((store) => store.showClosedMarkets);
|
||||
const toggle = usePositionsStore((store) => store.toggleClosedMarkets);
|
||||
return (
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
size="extra-small"
|
||||
data-testid="open-transfer"
|
||||
onClick={toggle}
|
||||
>
|
||||
{showClosed ? t('Hide closed markets') : t('Show closed markets')}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
@@ -14,12 +14,9 @@ import { Settings } from '../settings';
|
||||
import { Tooltip } from '../../components/tooltip';
|
||||
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';
|
||||
|
||||
export enum ViewType {
|
||||
Order = 'Order',
|
||||
Info = 'Info',
|
||||
@@ -302,22 +299,14 @@ export const useSidebar = create<{
|
||||
init: boolean;
|
||||
view: SidebarView | null;
|
||||
setView: (view: SidebarView | null) => void;
|
||||
}>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
init: true,
|
||||
view: null,
|
||||
setView: (x) =>
|
||||
set(() => {
|
||||
if (x == null) {
|
||||
return { view: null, init: false };
|
||||
}
|
||||
|
||||
return { view: x, init: false };
|
||||
}),
|
||||
}>()((set) => ({
|
||||
init: true,
|
||||
view: null,
|
||||
setView: (x) =>
|
||||
set(() => {
|
||||
if (x == null) {
|
||||
return { view: null, init: false };
|
||||
}
|
||||
return { view: x, init: false };
|
||||
}),
|
||||
{
|
||||
name: STORAGE_KEY,
|
||||
}
|
||||
)
|
||||
);
|
||||
}));
|
||||
|
||||
@@ -1,16 +1,36 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { GetStarted } from './get-started';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
let mockStep = 1;
|
||||
jest.mock('./use-get-onboarding-step', () => ({
|
||||
...jest.requireActual('./use-get-onboarding-step'),
|
||||
useGetOnboardingStep: jest.fn(() => mockStep),
|
||||
}));
|
||||
|
||||
describe('GetStarted', () => {
|
||||
const renderComponent = (context: Partial<VegaWalletContextShape> = {}) => {
|
||||
return render(
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
const checkTicks = (elements: Element[]) => {
|
||||
elements.forEach((item, i) => {
|
||||
if (i + 1 < mockStep) {
|
||||
expect(item.querySelector('[data-testid="icon-tick"]')).toBeTruthy();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders full get started content if not connected and no browser wallet detected', () => {
|
||||
renderComponent();
|
||||
@@ -20,12 +40,71 @@ describe('GetStarted', () => {
|
||||
it('renders connect prompt if no pubKey but wallet installed', () => {
|
||||
globalThis.window.vega = {} as Vega;
|
||||
renderComponent();
|
||||
expect(screen.getByTestId('order-connect-wallet')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('get-started-banner')).toBeInTheDocument();
|
||||
globalThis.window.vega = undefined as unknown as Vega;
|
||||
});
|
||||
|
||||
it('renders nothing if connected', () => {
|
||||
mockStep = 0;
|
||||
const { container } = renderComponent({ pubKey: 'my-pubkey' });
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
|
||||
it('steps should be ticked', () => {
|
||||
const navigatorGetter: jest.SpyInstance = jest.spyOn(
|
||||
window.navigator,
|
||||
'userAgent',
|
||||
'get'
|
||||
);
|
||||
navigatorGetter.mockReturnValue('Chrome');
|
||||
mockStep = 1;
|
||||
const { rerender, container } = renderComponent();
|
||||
expect(screen.queryByTestId('icon-tick')).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('get-wallet-button')).toBeInTheDocument();
|
||||
|
||||
mockStep = 2;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Connect' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 3;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Deposit' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 4;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider value={{} as VegaWalletContextShape}>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
checkTicks(screen.getAllByRole('listitem'));
|
||||
expect(screen.getByRole('button', { name: 'Dismiss' })).toBeInTheDocument();
|
||||
|
||||
mockStep = 5;
|
||||
rerender(
|
||||
<MemoryRouter>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey: 'my-pubkey' } as VegaWalletContextShape}
|
||||
>
|
||||
<GetStarted />
|
||||
</VegaWalletContext.Provider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,96 @@
|
||||
import classNames from 'classnames';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { ExternalLink, Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
ExternalLink,
|
||||
Intent,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
GetWalletButton,
|
||||
useVegaWallet,
|
||||
useVegaWalletDialogStore,
|
||||
isBrowserWalletInstalled,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
OnboardingStep,
|
||||
useGetOnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useSidebar, ViewType } from '../sidebar';
|
||||
import * as constants from '../constants';
|
||||
|
||||
interface Props {
|
||||
lead?: string;
|
||||
}
|
||||
|
||||
export const GetStarted = ({ lead }: Props) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||
|
||||
const GetStartedButton = ({ step }: { step: OnboardingStep }) => {
|
||||
const navigate = useNavigate();
|
||||
const [, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
const link = marketId ? Links[Routes.MARKET](marketId) : Links[Routes.HOME]();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
let buttonText = t('Get started');
|
||||
let onClickHandle = () => {
|
||||
openVegaWalletDialog();
|
||||
};
|
||||
if (step === OnboardingStep.ONBOARDING_WALLET_STEP) {
|
||||
return <GetWalletButton className="justify-between" />;
|
||||
} else if (step === OnboardingStep.ONBOARDING_CONNECT_STEP) {
|
||||
buttonText = t('Connect');
|
||||
} else if (step === OnboardingStep.ONBOARDING_DEPOSIT_STEP) {
|
||||
buttonText = t('Deposit');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Deposit });
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
} else if (step === OnboardingStep.ONBOARDING_ORDER_STEP) {
|
||||
buttonText = t('Dismiss');
|
||||
onClickHandle = () => {
|
||||
navigate(link);
|
||||
setView({ type: ViewType.Order });
|
||||
setOnboardingViewed('true');
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<TradingButton
|
||||
onClick={onClickHandle}
|
||||
size="small"
|
||||
data-testid="get-started-button"
|
||||
intent={Intent.Info}
|
||||
>
|
||||
{buttonText}
|
||||
</TradingButton>
|
||||
);
|
||||
};
|
||||
|
||||
export const GetStarted = ({ lead }: Props) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
|
||||
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
|
||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||
const currentStep = useGetOnboardingStep();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
|
||||
const onButtonClick = () => {
|
||||
openVegaWalletDialog();
|
||||
setOnboardingViewed('true');
|
||||
};
|
||||
const getStartedNeeded =
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP;
|
||||
|
||||
const wrapperClasses = classNames(
|
||||
'flex flex-col py-4 px-6 gap-4 rounded',
|
||||
@@ -39,27 +99,48 @@ export const GetStarted = ({ lead }: Props) => {
|
||||
{ 'mt-8': !lead }
|
||||
);
|
||||
|
||||
if (!pubKey && !isBrowserWalletInstalled()) {
|
||||
if (getStartedNeeded) {
|
||||
return (
|
||||
<div className={wrapperClasses} data-testid="get-started-banner">
|
||||
{lead && <h2>{lead}</h2>}
|
||||
<h3 className="text-lg">{t('Get started')}</h3>
|
||||
<div>
|
||||
<ul className="list-decimal list-inside">
|
||||
<li>{t('Get a Vega wallet')}</li>
|
||||
<li>{t('Connect')}</li>
|
||||
<li>{t('Deposit funds')}</li>
|
||||
<li>{t('Open a position')}</li>
|
||||
<ul className="list-inside -ml-5" role="list">
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_WALLET_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">1. {t('Get a Vega wallet')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{(currentStep > OnboardingStep.ONBOARDING_CONNECT_STEP ||
|
||||
pubKey) && <VegaIcon name={VegaIconNames.TICK} size={20} />}
|
||||
</div>
|
||||
<div className="ml-1">2. {t('Connect')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_DEPOSIT_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">3. {t('Deposit funds')}</div>
|
||||
</li>
|
||||
<li className="flex">
|
||||
<div className="w-5">
|
||||
{currentStep > OnboardingStep.ONBOARDING_ORDER_STEP && (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-1">4. {t('Open a position')}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<TradingButton
|
||||
intent={Intent.Info}
|
||||
onClick={onButtonClick}
|
||||
data-testid="get-started-button"
|
||||
>
|
||||
{t('Get started')}
|
||||
</TradingButton>
|
||||
<GetStartedButton step={currentStep} />
|
||||
</div>
|
||||
{VEGA_ENV === Networks.MAINNET && (
|
||||
<p className="text-sm">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Checkbox } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingCheckbox } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
||||
|
||||
@@ -7,7 +7,7 @@ export const TelemetryApproval = ({ helpText }: { helpText: string }) => {
|
||||
return (
|
||||
<div className="flex flex-col py-3">
|
||||
<div className="mr-4" role="form">
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
label={<span className="text-lg pl-1">{t('Share usage data')}</span>}
|
||||
checked={isApproved}
|
||||
name="telemetry-approval"
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
OnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { ordersWithMarketProvider } from '@vegaprotocol/orders';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
|
||||
let mockData: object[] | null = [{ id: 'item-id' }];
|
||||
jest.mock('@vegaprotocol/data-provider', () => ({
|
||||
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||
useDataProvider: jest.fn(() => ({ data: mockData })),
|
||||
}));
|
||||
|
||||
let mockContext: Partial<VegaWalletContextShape> = { pubKey: 'test-pubkey' };
|
||||
|
||||
describe('useGetOnboardingStep', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockData = [{ id: 'item-id' }];
|
||||
mockContext = { pubKey: 'test-pubkey' };
|
||||
globalThis.window.vega = {} as Vega;
|
||||
});
|
||||
|
||||
const wrapper = ({ children }: { children: ReactNode }) => (
|
||||
<VegaWalletContext.Provider
|
||||
value={mockContext as unknown as VegaWalletContextShape}
|
||||
>
|
||||
{children}
|
||||
</VegaWalletContext.Provider>
|
||||
);
|
||||
|
||||
it('should return properly ONBOARDING_UNKNOWN_STEP', () => {
|
||||
mockData = null;
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_UNKNOWN_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_WALLET_STEP', () => {
|
||||
// @ts-ignore test only purpose
|
||||
globalThis.window.vega = undefined;
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_WALLET_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_CONNECT_STEP', () => {
|
||||
mockContext = { pubKey: null };
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
expect(result.current).toEqual(OnboardingStep.ONBOARDING_CONNECT_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_DEPOSIT_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation((args) => {
|
||||
if (
|
||||
args.dataProvider === depositsProvider ||
|
||||
args.dataProvider === aggregatedAccountsDataProvider
|
||||
) {
|
||||
return { data: [] };
|
||||
}
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(
|
||||
OnboardingStep.ONBOARDING_DEPOSIT_STEP
|
||||
);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_ORDER_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation((args) => {
|
||||
if (
|
||||
args.dataProvider === ordersWithMarketProvider ||
|
||||
args.dataProvider === positionsDataProvider
|
||||
) {
|
||||
return { data: [] };
|
||||
}
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(OnboardingStep.ONBOARDING_ORDER_STEP);
|
||||
});
|
||||
|
||||
it('should return properly ONBOARDING_COMPLETE_STEP', async () => {
|
||||
(useDataProvider as jest.Mock).mockImplementation(() => {
|
||||
return { data: mockData };
|
||||
});
|
||||
const { result } = renderHook(() => useGetOnboardingStep(), { wrapper });
|
||||
await expect(result.current).toEqual(
|
||||
OnboardingStep.ONBOARDING_COMPLETE_STEP
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,82 @@
|
||||
import { isBrowserWalletInstalled, useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { ordersWithMarketProvider } from '@vegaprotocol/orders';
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
|
||||
import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export enum OnboardingStep {
|
||||
ONBOARDING_UNKNOWN_STEP,
|
||||
ONBOARDING_WALLET_STEP,
|
||||
ONBOARDING_CONNECT_STEP,
|
||||
ONBOARDING_DEPOSIT_STEP,
|
||||
ONBOARDING_ORDER_STEP,
|
||||
ONBOARDING_COMPLETE_STEP,
|
||||
}
|
||||
|
||||
export const useGetOnboardingStep = () => {
|
||||
const connecting = useGlobalStore((store) => store.eagerConnecting);
|
||||
const { pubKey = '', pubKeys } = useVegaWallet();
|
||||
const { data: depositsData } = useDataProvider({
|
||||
dataProvider: depositsProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const { data: collateralData } = useDataProvider({
|
||||
dataProvider: aggregatedAccountsDataProvider,
|
||||
variables: { partyId: pubKey || '' },
|
||||
skip: !pubKey,
|
||||
});
|
||||
const collaterals = Boolean(collateralData?.length);
|
||||
const deposits =
|
||||
depositsData?.some(
|
||||
(item) => item.status === Types.DepositStatus.STATUS_FINALIZED
|
||||
) || false;
|
||||
const { data: ordersData } = useDataProvider({
|
||||
dataProvider: ordersWithMarketProvider,
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
pagination: {
|
||||
first: 1,
|
||||
},
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
const orders = Boolean(ordersData?.length);
|
||||
|
||||
const partyIds = pubKeys?.map((item) => item.publicKey) || [];
|
||||
const { data: positionsData } = useDataProvider({
|
||||
dataProvider: positionsDataProvider,
|
||||
variables: {
|
||||
partyIds,
|
||||
},
|
||||
skip: !partyIds?.length,
|
||||
});
|
||||
const positions = Boolean(positionsData?.length);
|
||||
|
||||
const isLoading = Boolean(
|
||||
(connecting || pubKey) &&
|
||||
(depositsData === null ||
|
||||
ordersData === null ||
|
||||
collateralData === null ||
|
||||
positionsData === null)
|
||||
);
|
||||
if (isLoading) {
|
||||
return OnboardingStep.ONBOARDING_UNKNOWN_STEP;
|
||||
}
|
||||
if (!isBrowserWalletInstalled()) {
|
||||
return OnboardingStep.ONBOARDING_WALLET_STEP;
|
||||
}
|
||||
if (!pubKey) {
|
||||
return OnboardingStep.ONBOARDING_CONNECT_STEP;
|
||||
}
|
||||
if (!deposits && !collaterals) {
|
||||
return OnboardingStep.ONBOARDING_DEPOSIT_STEP;
|
||||
}
|
||||
if (!orders && !positions) {
|
||||
return OnboardingStep.ONBOARDING_ORDER_STEP;
|
||||
}
|
||||
return OnboardingStep.ONBOARDING_COMPLETE_STEP;
|
||||
};
|
||||
@@ -3,21 +3,19 @@ import { GetStarted } from './get-started';
|
||||
import { TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
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';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
export const WelcomeDialogContent = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const navigate = useNavigate();
|
||||
const browseMarkets = () => {
|
||||
const link = Links[Routes.MARKETS]();
|
||||
navigate(link);
|
||||
setOnboardingViewed('true');
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
const lead =
|
||||
VEGA_ENV === Networks.MAINNET
|
||||
@@ -57,7 +55,7 @@ export const WelcomeDialogContent = () => {
|
||||
{t('Browse the markets')}
|
||||
</TradingButton>
|
||||
</div>
|
||||
<div className="sm:w-1/2">
|
||||
<div className="sm:w-1/2 flex grow">
|
||||
<GetStarted lead={lead} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,31 +2,38 @@ import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { isBrowserWalletInstalled } from '@vegaprotocol/wallet';
|
||||
import * as constants from '../constants';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { WelcomeDialogContent } from './welcome-dialog-content';
|
||||
import { getConfig } from '@vegaprotocol/wallet';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import {
|
||||
useGetOnboardingStep,
|
||||
OnboardingStep,
|
||||
} from './use-get-onboarding-step';
|
||||
import * as constants from '../constants';
|
||||
|
||||
export const WelcomeDialog = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const [onBoardingViewed, setOnboardingViewed] = useLocalStorage(
|
||||
constants.ONBOARDING_VIEWED_KEY
|
||||
);
|
||||
const [onBoardingViewed] = useLocalStorage(constants.ONBOARDING_VIEWED_KEY);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const dismissed = useGlobalStore((store) => store.onBoardingDismissed);
|
||||
const currentStep = useGetOnboardingStep();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const isOnboardingDialogNeeded =
|
||||
onBoardingViewed !== 'true' && !isBrowserWalletInstalled() && !getConfig();
|
||||
onBoardingViewed !== 'true' &&
|
||||
currentStep &&
|
||||
currentStep < OnboardingStep.ONBOARDING_COMPLETE_STEP &&
|
||||
!dismissed;
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
const onClose = () => {
|
||||
setOnboardingViewed('true');
|
||||
const link = marketId
|
||||
? Links[Routes.MARKET](marketId)
|
||||
: Links[Routes.HOME]();
|
||||
navigate(link);
|
||||
update({ onBoardingDismissed: true });
|
||||
};
|
||||
const title = (
|
||||
<span className="font-alpha calt" data-testid="welcome-title">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
useNodeSwitcherStore,
|
||||
} from '@vegaprotocol/environment';
|
||||
import './styles.css';
|
||||
import { usePageTitleStore } from '../stores';
|
||||
import { useGlobalStore, usePageTitleStore } from '../stores';
|
||||
import DialogsContainer from './dialogs-container';
|
||||
import ToastsManager from './toasts-manager';
|
||||
import {
|
||||
@@ -170,7 +170,8 @@ const PartyData = () => {
|
||||
|
||||
const MaybeConnectEagerly = () => {
|
||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||
useVegaEagerConnect(Connectors);
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const eagerConnecting = useVegaEagerConnect(Connectors);
|
||||
const [isTelemetryApproved] = useTelemetryApproval();
|
||||
useEthereumEagerConnect(
|
||||
isTelemetryApproved ? { dsn: SENTRY_DSN, env: VEGA_ENV } : {}
|
||||
@@ -182,5 +183,8 @@ const MaybeConnectEagerly = () => {
|
||||
if (query && !pubKey) {
|
||||
connect(Connectors['view']);
|
||||
}
|
||||
useEffect(() => {
|
||||
update({ eagerConnecting });
|
||||
}, [update, eagerConnecting]);
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,40 +1,10 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { Head, Html, Main, NextScript } from 'next/document';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html>
|
||||
<>
|
||||
<Head>
|
||||
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charSet="utf-8" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Vega Protocol - VEGA Console" />
|
||||
<meta name="og:type" content="website" />
|
||||
<meta name="og:url" content="https://console.vega.xyz/" />
|
||||
<meta name="og:title" content="Vega Protocol - Console" />
|
||||
<meta name="og:site_name" content="Vega Protocol - Console" />
|
||||
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
|
||||
|
||||
<meta
|
||||
name="twitter:card"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="twitter:title" content="Vega Protocol - Console" />
|
||||
<meta name="twitter:description" content="Vega Protocol - Console" />
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="twitter:image:alt" content="VEGA logo" />
|
||||
<meta name="twitter:site" content="@vegaprotocol" />
|
||||
|
||||
<meta name="description" content="Vega Protocol - Console" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
@@ -45,8 +15,6 @@ export default function Document() {
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
/>
|
||||
{/* eslint-disable-next-line @next/next/no-css-tags */}
|
||||
<link rel="stylesheet" href="/preloader.css" media="all" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
@@ -54,10 +22,12 @@ export default function Document() {
|
||||
/>
|
||||
<script src="/theme-setter.js" type="text/javascript" async />
|
||||
</Head>
|
||||
<body className="bg-white dark:bg-vega-cdark-900 text-default font-alpha">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
<Html>
|
||||
<body className="bg-white dark:bg-vega-cdark-900 text-default font-alpha">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Head from 'next/head';
|
||||
import { ClientRouter } from './client-router';
|
||||
|
||||
/**
|
||||
@@ -6,5 +7,60 @@ import { ClientRouter } from './client-router';
|
||||
* have to serve a static site via next export
|
||||
*/
|
||||
export default function Index() {
|
||||
return <ClientRouter />;
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charSet="utf-8" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Vega Protocol - VEGA Console" />
|
||||
<meta name="og:type" content="website" />
|
||||
<meta name="og:url" content="https://console.vega.xyz/" />
|
||||
<meta name="og:title" content="Vega Protocol - Console" />
|
||||
<meta name="og:site_name" content="Vega Protocol - Console" />
|
||||
<meta name="og:image" content="https://static.vega.xyz/favicon.ico" />
|
||||
|
||||
<meta
|
||||
name="twitter:card"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="twitter:title" content="Vega Protocol - Console" />
|
||||
<meta name="twitter:description" content="Vega Protocol - Console" />
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<meta name="twitter:image:alt" content="VEGA logo" />
|
||||
<meta name="twitter:site" content="@vegaprotocol" />
|
||||
|
||||
<meta name="description" content="Vega Protocol - Console" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
content="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<link
|
||||
rel="preload"
|
||||
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
||||
as="font"
|
||||
type="font/woff2"
|
||||
/>
|
||||
{/* eslint-disable-next-line @next/next/no-css-tags */}
|
||||
<link rel="stylesheet" href="/preloader.css" media="all" />
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/x-icon"
|
||||
href="https://static.vega.xyz/favicon.ico"
|
||||
/>
|
||||
<script src="/theme-setter.js" type="text/javascript" async />
|
||||
</Head>
|
||||
<ClientRouter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import produce from 'immer';
|
||||
|
||||
interface GlobalStore {
|
||||
marketId: string | null;
|
||||
onBoardingDismissed: boolean;
|
||||
eagerConnecting: boolean;
|
||||
update: (store: Partial<Omit<GlobalStore, 'update'>>) => void;
|
||||
}
|
||||
|
||||
@@ -14,6 +16,8 @@ interface PageTitleStore {
|
||||
|
||||
export const useGlobalStore = create<GlobalStore>()((set) => ({
|
||||
marketId: LocalStorage.getItem('marketId') || null,
|
||||
onBoardingDismissed: false,
|
||||
eagerConnecting: false,
|
||||
update: (newState) => {
|
||||
set(
|
||||
produce((state: GlobalStore) => {
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
RichSelect,
|
||||
Select,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
TradingRichSelect,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
Checkbox,
|
||||
TradingCheckbox,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { Transfer } from '@vegaprotocol/wallet';
|
||||
import { normalizeTransfer } from '@vegaprotocol/wallet';
|
||||
@@ -130,12 +130,16 @@ export const TransferForm = ({
|
||||
className="text-sm"
|
||||
data-testid="transfer-form"
|
||||
>
|
||||
<FormGroup label="Vega key" labelFor="to-address">
|
||||
<TradingFormGroup label="Vega key" labelFor="to-address">
|
||||
<AddressField
|
||||
pubKeys={pubKeys}
|
||||
onChange={() => setValue('toAddress', '')}
|
||||
select={
|
||||
<Select {...register('toAddress')} id="to-address" defaultValue="">
|
||||
<TradingSelect
|
||||
{...register('toAddress')}
|
||||
id="to-address"
|
||||
defaultValue=""
|
||||
>
|
||||
<option value="" disabled={true}>
|
||||
{t('Please select')}
|
||||
</option>
|
||||
@@ -147,10 +151,10 @@ export const TransferForm = ({
|
||||
{pk}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
}
|
||||
input={
|
||||
<Input
|
||||
<TradingInput
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus={true} // focus input immediately after is shown
|
||||
id="to-address"
|
||||
@@ -171,12 +175,12 @@ export const TransferForm = ({
|
||||
}
|
||||
/>
|
||||
{errors.toAddress?.message && (
|
||||
<InputError forInput="to-address">
|
||||
<TradingInputError forInput="to-address">
|
||||
{errors.toAddress.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label="Asset" labelFor="asset">
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label="Asset" labelFor="asset">
|
||||
<Controller
|
||||
control={control}
|
||||
name="asset"
|
||||
@@ -186,7 +190,7 @@ export const TransferForm = ({
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<RichSelect
|
||||
<TradingRichSelect
|
||||
data-testid="select-asset"
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
@@ -208,15 +212,17 @@ export const TransferForm = ({
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</RichSelect>
|
||||
</TradingRichSelect>
|
||||
)}
|
||||
/>
|
||||
{errors.asset?.message && (
|
||||
<InputError forInput="asset">{errors.asset.message}</InputError>
|
||||
<TradingInputError forInput="asset">
|
||||
{errors.asset.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label="Amount" labelFor="amount">
|
||||
<Input
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label="Amount" labelFor="amount">
|
||||
<TradingInput
|
||||
id="amount"
|
||||
autoComplete="off"
|
||||
appendElement={
|
||||
@@ -239,11 +245,13 @@ export const TransferForm = ({
|
||||
})}
|
||||
/>
|
||||
{errors.amount?.message && (
|
||||
<InputError forInput="amount">{errors.amount.message}</InputError>
|
||||
<TradingInputError forInput="amount">
|
||||
{errors.amount.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<div className="mb-4">
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="include-transfer-fee"
|
||||
disabled={!transferAmount}
|
||||
label={
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"name": "@vegaprotocol/announcements",
|
||||
"version": "0.0.2"
|
||||
"version": "0.0.2",
|
||||
"peerDependencies": {
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Option } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingOption } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AssetFieldsFragment } from './__generated__/Asset';
|
||||
import classNames from 'classnames';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -28,7 +28,7 @@ export const Balance = ({
|
||||
|
||||
export const AssetOption = ({ asset, balance }: AssetOptionProps) => {
|
||||
return (
|
||||
<Option key={asset.id} value={asset.id}>
|
||||
<TradingOption key={asset.id} value={asset.id}>
|
||||
<div className="flex flex-col items-start">
|
||||
<div className="flex flex-row align-baseline gap-2">
|
||||
<span>{asset.name}</span>{' '}
|
||||
@@ -49,6 +49,6 @@ export const AssetOption = ({ asset, balance }: AssetOptionProps) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Option>
|
||||
</TradingOption>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from 'date-fns';
|
||||
import { formatForInput } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingInputError } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
const defaultValue: Schema.DateRange = {};
|
||||
export interface DateRangeFilterProps extends IFilterParams {
|
||||
@@ -195,7 +195,7 @@ export const DateRangeFilter = forwardRef(
|
||||
}, [value, props]);
|
||||
|
||||
const notification = useMemo(() => {
|
||||
const not = error ? <InputError>{error}</InputError> : null;
|
||||
const not = error ? <TradingInputError>{error}</TradingInputError> : null;
|
||||
return (
|
||||
<div className="ag-filter-apply-panel flex min-h-[2rem]">{not}</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||
@@ -22,17 +26,17 @@ export const DealTicketLimitAmount = ({
|
||||
const renderError = () => {
|
||||
if (sizeError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-error-message-size-limit">
|
||||
<TradingInputError testId="deal-ticket-error-message-size-limit">
|
||||
{sizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
if (priceError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-error-message-price-limit">
|
||||
<TradingInputError testId="deal-ticket-error-message-price-limit">
|
||||
{priceError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,7 +47,7 @@ export const DealTicketLimitAmount = ({
|
||||
<div className="mb-2">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Size')}
|
||||
labelFor="input-order-size-limit"
|
||||
className="!mb-0"
|
||||
@@ -59,8 +63,8 @@ export const DealTicketLimitAmount = ({
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingInput
|
||||
id="input-order-size-limit"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@@ -68,15 +72,16 @@ export const DealTicketLimitAmount = ({
|
||||
min={sizeStep}
|
||||
data-testid="order-size"
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
<div className="pt-7 leading-10">@</div>
|
||||
<div className="pt-5 leading-10">@</div>
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
labelFor="input-price-quote"
|
||||
label={t(`Price (${quoteName})`)}
|
||||
labelAlign="right"
|
||||
@@ -93,19 +98,20 @@ export const DealTicketLimitAmount = ({
|
||||
},
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingInput
|
||||
id="input-price-quote"
|
||||
className="w-full"
|
||||
type="number"
|
||||
step={priceStep}
|
||||
data-testid="order-price"
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
</div>
|
||||
{renderError()}
|
||||
|
||||
@@ -4,7 +4,11 @@ import {
|
||||
validateAmount,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Input, InputError, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { isMarketInAuction } from '@vegaprotocol/markets';
|
||||
import type { DealTicketAmountProps } from './deal-ticket-amount';
|
||||
import { Controller } from 'react-hook-form';
|
||||
@@ -33,7 +37,7 @@ export const DealTicketMarketAmount = ({
|
||||
<div className="mb-2">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="flex-1">
|
||||
<div className="mb-2 text-sm">{t('Size')}</div>
|
||||
<div className="mb-2 text-xs">{t('Size')}</div>
|
||||
<Controller
|
||||
name="size"
|
||||
control={control}
|
||||
@@ -45,8 +49,8 @@ export const DealTicketMarketAmount = ({
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingInput
|
||||
id="input-order-size-market"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@@ -54,12 +58,13 @@ export const DealTicketMarketAmount = ({
|
||||
min={sizeStep}
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
data-testid="order-size"
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="pt-7 leading-10">@</div>
|
||||
<div className="pt-5 leading-10">@</div>
|
||||
<div className="flex-1 text-sm text-right">
|
||||
{inAuction && (
|
||||
<Tooltip
|
||||
@@ -72,7 +77,7 @@ export const DealTicketMarketAmount = ({
|
||||
)}
|
||||
<div
|
||||
data-testid="last-price"
|
||||
className={classNames('leading-10', { 'pt-7': !inAuction })}
|
||||
className={classNames('leading-10', { 'pt-5': !inAuction })}
|
||||
>
|
||||
{priceFormatted && quoteName ? (
|
||||
<>
|
||||
@@ -85,12 +90,12 @@ export const DealTicketMarketAmount = ({
|
||||
</div>
|
||||
</div>
|
||||
{sizeError && (
|
||||
<InputError
|
||||
<TradingInputError
|
||||
intent="danger"
|
||||
testId="deal-ticket-error-message-size-market"
|
||||
>
|
||||
{sizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -4,9 +4,9 @@ import type { OrderFormValues } from '../../hooks/use-form-values';
|
||||
import { toDecimal, validateAmount } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
@@ -32,9 +32,9 @@ export const DealTicketSizeIceberg = ({
|
||||
const renderPeakSizeError = () => {
|
||||
if (peakSizeError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-peak-error-message-size-limit">
|
||||
<TradingInputError testId="deal-ticket-peak-error-message-size-limit">
|
||||
{peakSizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ export const DealTicketSizeIceberg = ({
|
||||
const renderMinimumSizeError = () => {
|
||||
if (minimumVisibleSizeError) {
|
||||
return (
|
||||
<InputError testId="deal-ticket-minimum-error-message-size-limit">
|
||||
<TradingInputError testId="deal-ticket-minimum-error-message-size-limit">
|
||||
{minimumVisibleSizeError}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export const DealTicketSizeIceberg = ({
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={
|
||||
<Tooltip
|
||||
description={
|
||||
@@ -93,7 +93,7 @@ export const DealTicketSizeIceberg = ({
|
||||
validate: validateAmount(sizeStep, 'peakSize'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="input-order-peak-size"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@@ -106,14 +106,14 @@ export const DealTicketSizeIceberg = ({
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
<div className="flex-0 items-center">
|
||||
<div className="flex"></div>
|
||||
<div className="flex"></div>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={
|
||||
<Tooltip
|
||||
description={
|
||||
@@ -151,7 +151,7 @@ export const DealTicketSizeIceberg = ({
|
||||
validate: validateAmount(sizeStep, 'minimumVisibleSize'),
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="input-order-minimum-size"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@@ -164,7 +164,7 @@ export const DealTicketSizeIceberg = ({
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
</div>
|
||||
{renderPeakSizeError()}
|
||||
|
||||
@@ -10,13 +10,13 @@ import {
|
||||
import { useForm, Controller, useController } from 'react-hook-form';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Input,
|
||||
Checkbox,
|
||||
FormGroup,
|
||||
InputError,
|
||||
Select,
|
||||
TradingRadio,
|
||||
TradingRadioGroup,
|
||||
TradingInput,
|
||||
TradingCheckbox,
|
||||
TradingFormGroup,
|
||||
TradingInputError,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { getDerivedPrice, type Market } from '@vegaprotocol/markets';
|
||||
@@ -187,9 +187,9 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
}}
|
||||
/>
|
||||
{errors.type && (
|
||||
<InputError testId="stop-order-error-message-type">
|
||||
<TradingInputError testId="stop-order-error-message-type">
|
||||
{errors.type.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
|
||||
<Controller
|
||||
@@ -199,21 +199,21 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
<SideSelector value={field.value} onValueChange={field.onChange} />
|
||||
)}
|
||||
/>
|
||||
<FormGroup label={t('Trigger')} compact={true} labelFor="">
|
||||
<TradingFormGroup label={t('Trigger')} compact={true} labelFor="">
|
||||
<Controller
|
||||
name="triggerDirection"
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
const { onChange, value } = field;
|
||||
return (
|
||||
<RadioGroup
|
||||
<TradingRadioGroup
|
||||
name="triggerDirection"
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
orientation="horizontal"
|
||||
className="mb-2"
|
||||
>
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderTriggerDirection
|
||||
.TRIGGER_DIRECTION_RISES_ABOVE
|
||||
@@ -221,7 +221,7 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
id="triggerDirection-risesAbove"
|
||||
label={'Rises above'}
|
||||
/>
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderTriggerDirection
|
||||
.TRIGGER_DIRECTION_FALLS_BELOW
|
||||
@@ -229,7 +229,7 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
id="triggerDirection-fallsBelow"
|
||||
label={'Falls below'}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
@@ -246,16 +246,17 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<Input
|
||||
<TradingInput
|
||||
data-testid="triggerPrice"
|
||||
type="number"
|
||||
step={priceStep}
|
||||
appendElement={asset.symbol}
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@@ -263,9 +264,9 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
}}
|
||||
/>
|
||||
{errors.triggerPrice && (
|
||||
<InputError testId="stop-order-error-message-trigger-price">
|
||||
<TradingInputError testId="stop-order-error-message-trigger-price">
|
||||
{errors.triggerPrice.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -294,16 +295,17 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
'Trailing percentage offset'
|
||||
),
|
||||
}}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<Input
|
||||
<TradingInput
|
||||
type="number"
|
||||
step={trailingPercentOffsetStep}
|
||||
appendElement="%"
|
||||
data-testid="triggerTrailingPercentOffset"
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@@ -311,9 +313,9 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
}}
|
||||
/>
|
||||
{errors.triggerTrailingPercentOffset && (
|
||||
<InputError testId="stop-order-error-message-trigger-trailing-percent-offset">
|
||||
<TradingInputError testId="stop-order-error-message-trigger-trailing-percent-offset">
|
||||
{errors.triggerTrailingPercentOffset.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -324,25 +326,29 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
render={({ field }) => {
|
||||
const { onChange, value } = field;
|
||||
return (
|
||||
<RadioGroup
|
||||
<TradingRadioGroup
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
orientation="horizontal"
|
||||
>
|
||||
<Radio value="price" id="triggerType-price" label={'Price'} />
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value="price"
|
||||
id="triggerType-price"
|
||||
label={'Price'}
|
||||
/>
|
||||
<TradingRadio
|
||||
value="trailingPercentOffset"
|
||||
id="triggerType-trailingPercentOffset"
|
||||
label={'Trailing Percent Offset'}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<div className="mb-2">
|
||||
<div className="flex items-start gap-4">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
labelFor="input-price-quote"
|
||||
label={t(`Size`)}
|
||||
className="!mb-0 flex-1"
|
||||
@@ -358,10 +364,10 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
},
|
||||
validate: validateAmount(sizeStep, 'Size'),
|
||||
}}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="order-size"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@@ -370,16 +376,17 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
data-testid="order-size"
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
<div className="pt-7 leading-10">@</div>
|
||||
</TradingFormGroup>
|
||||
<div className="pt-5 leading-10">@</div>
|
||||
<div className="flex-1">
|
||||
{type === Schema.OrderType.TYPE_LIMIT ? (
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
labelFor="input-price-quote"
|
||||
label={t(`Price (${quoteName})`)}
|
||||
labelAlign="right"
|
||||
@@ -397,10 +404,10 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
},
|
||||
validate: validateAmount(priceStep, 'Price'),
|
||||
}}
|
||||
render={({ field }) => {
|
||||
render={({ field, fieldState }) => {
|
||||
const { value, ...props } = field;
|
||||
return (
|
||||
<Input
|
||||
<TradingInput
|
||||
id="input-price-quote"
|
||||
className="w-full"
|
||||
type="number"
|
||||
@@ -408,15 +415,16 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
data-testid="order-price"
|
||||
onWheel={(e) => e.currentTarget.blur()}
|
||||
value={value || ''}
|
||||
hasError={!!fieldState.error}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
) : (
|
||||
<div
|
||||
className="text-sm text-right pt-7 leading-10"
|
||||
className="text-sm text-right pt-5 leading-10"
|
||||
data-testid="price"
|
||||
>
|
||||
{priceFormatted && quoteName
|
||||
@@ -427,21 +435,21 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
</div>
|
||||
</div>
|
||||
{errors.size && (
|
||||
<InputError testId="stop-order-error-message-size">
|
||||
<TradingInputError testId="stop-order-error-message-size">
|
||||
{errors.size.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
|
||||
{!errors.size &&
|
||||
errors.price &&
|
||||
type === Schema.OrderType.TYPE_LIMIT && (
|
||||
<InputError testId="stop-order-error-message-price">
|
||||
<TradingInputError testId="stop-order-error-message-price">
|
||||
{errors.price.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-2">
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Time in force')}
|
||||
labelFor="select-time-in-force"
|
||||
compact={true}
|
||||
@@ -449,11 +457,12 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
<Controller
|
||||
name="timeInForce"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Select
|
||||
render={({ field, fieldState }) => (
|
||||
<TradingSelect
|
||||
id="select-time-in-force"
|
||||
className="w-full"
|
||||
data-testid="order-tif"
|
||||
hasError={!!fieldState.error}
|
||||
{...field}
|
||||
>
|
||||
<option
|
||||
@@ -468,14 +477,14 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
>
|
||||
{timeInForceLabel(Schema.OrderTimeInForce.TIME_IN_FORCE_FOK)}
|
||||
</option>
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
{errors.timeInForce && (
|
||||
<InputError testId="stop-error-message-tif">
|
||||
<TradingInputError testId="stop-error-message-tif">
|
||||
{errors.timeInForce.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2 pb-2 justify-between">
|
||||
@@ -485,29 +494,29 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
render={({ field }) => {
|
||||
const { onChange: onCheckedChange, value } = field;
|
||||
return (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
onCheckedChange={onCheckedChange}
|
||||
checked={value}
|
||||
name="expire"
|
||||
label={<span className="text-xs">{t('Expire')}</span>}
|
||||
label={t('Expire')}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="reduce-only"
|
||||
checked={true}
|
||||
disabled={true}
|
||||
label={
|
||||
<Tooltip description={<span>{t(REDUCE_ONLY_TOOLTIP)}</span>}>
|
||||
<span className="text-xs">{t('Reduce only')}</span>
|
||||
<>{t('Reduce only')}</>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{expire && (
|
||||
<>
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Strategy')}
|
||||
labelFor="expiryStrategy"
|
||||
compact={true}
|
||||
@@ -517,26 +526,26 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<RadioGroup orientation="horizontal" {...field}>
|
||||
<Radio
|
||||
<TradingRadioGroup orientation="horizontal" {...field}>
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_SUBMIT
|
||||
}
|
||||
id="expiryStrategy-submit"
|
||||
label={'Submit'}
|
||||
/>
|
||||
<Radio
|
||||
<TradingRadio
|
||||
value={
|
||||
Schema.StopOrderExpiryStrategy.EXPIRY_STRATEGY_CANCELS
|
||||
}
|
||||
id="expiryStrategy-cancel"
|
||||
label={'Cancel'}
|
||||
/>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<div className="mb-2">
|
||||
<Controller
|
||||
name="expiresAt"
|
||||
|
||||
@@ -17,8 +17,8 @@ import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { mapFormValuesToOrderSubmission } from '../../utils/map-form-values-to-submission';
|
||||
import {
|
||||
Checkbox,
|
||||
InputError,
|
||||
TradingCheckbox,
|
||||
TradingInputError,
|
||||
Intent,
|
||||
Notification,
|
||||
Tooltip,
|
||||
@@ -417,7 +417,7 @@ export const DealTicket = ({
|
||||
name="postOnly"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="post-only"
|
||||
checked={!disablePostOnlyCheckbox && field.value}
|
||||
disabled={disablePostOnlyCheckbox}
|
||||
@@ -449,7 +449,7 @@ export const DealTicket = ({
|
||||
name="reduceOnly"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="reduce-only"
|
||||
checked={!disableReduceOnlyCheckbox && field.value}
|
||||
disabled={disableReduceOnlyCheckbox}
|
||||
@@ -483,7 +483,7 @@ export const DealTicket = ({
|
||||
name="iceberg"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
<TradingCheckbox
|
||||
name="iceberg"
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
@@ -572,11 +572,11 @@ export const NoWalletWarning = ({
|
||||
if (isReadOnly) {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<InputError testId="deal-ticket-error-message-summary">
|
||||
<TradingInputError testId="deal-ticket-error-message-summary">
|
||||
{
|
||||
'You need to connect your own wallet to start trading on this market'
|
||||
}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -613,9 +613,9 @@ const SummaryMessage = memo(
|
||||
if (error?.message) {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<InputError testId="deal-ticket-error-message-summary">
|
||||
<TradingInputError testId="deal-ticket-error-message-summary">
|
||||
{error?.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { formatForInput } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useRef } from 'react';
|
||||
@@ -19,24 +23,25 @@ export const ExpirySelector = ({
|
||||
const dateFormatted = formatForInput(date);
|
||||
const minDate = formatForInput(date);
|
||||
return (
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Expiry time/date')}
|
||||
labelFor="expiration"
|
||||
compact={true}
|
||||
>
|
||||
<Input
|
||||
<TradingInput
|
||||
data-testid="date-picker-field"
|
||||
id="expiration"
|
||||
type="datetime-local"
|
||||
value={dateFormatted}
|
||||
onChange={(e) => onSelect(e.target.value)}
|
||||
min={minDate}
|
||||
hasError={!!errorMessage}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<InputError testId="deal-ticket-error-message-expiry">
|
||||
<TradingInputError testId="deal-ticket-error-message-expiry">
|
||||
{errorMessage}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
FormGroup,
|
||||
InputError,
|
||||
Select,
|
||||
TradingFormGroup,
|
||||
TradingInputError,
|
||||
TradingSelect,
|
||||
Tooltip,
|
||||
SimpleGrid,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
@@ -90,12 +90,12 @@ export const TimeInForceSelector = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('Time in force')}
|
||||
labelFor="select-time-in-force"
|
||||
compact={true}
|
||||
>
|
||||
<Select
|
||||
<TradingSelect
|
||||
id="select-time-in-force"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
@@ -103,18 +103,19 @@ export const TimeInForceSelector = ({
|
||||
}}
|
||||
className="w-full"
|
||||
data-testid="order-tif"
|
||||
hasError={!!errorMessage}
|
||||
>
|
||||
{options.map(([key, value]) => (
|
||||
<option key={key} value={value}>
|
||||
{timeInForceLabel(value)}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
{errorMessage && (
|
||||
<InputError testId="deal-ticket-error-message-tif">
|
||||
<TradingInputError testId="deal-ticket-error-message-tif">
|
||||
{renderError(errorMessage)}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
InputError,
|
||||
TradingInputError,
|
||||
SimpleGrid,
|
||||
Tooltip,
|
||||
TradingDropdown,
|
||||
@@ -178,9 +178,9 @@ export const TypeSelector = ({
|
||||
value={value}
|
||||
/>
|
||||
{errorMessage && (
|
||||
<InputError testId="deal-ticket-error-message-type">
|
||||
<TradingInputError testId="deal-ticket-error-message-type">
|
||||
{renderError(errorMessage as MarketModeValidationType)}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -14,14 +14,14 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
Button,
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
RichSelect,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
TradingRichSelect,
|
||||
Notification,
|
||||
Intent,
|
||||
ButtonLink,
|
||||
Select,
|
||||
TradingSelect,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
@@ -151,7 +151,7 @@ export const DepositForm = ({
|
||||
noValidate={true}
|
||||
data-testid="deposit-form"
|
||||
>
|
||||
<FormGroup
|
||||
<TradingFormGroup
|
||||
label={t('From (Ethereum address)')}
|
||||
labelFor="ethereum-address"
|
||||
>
|
||||
@@ -197,15 +197,17 @@ export const DepositForm = ({
|
||||
}}
|
||||
/>
|
||||
{errors.from?.message && (
|
||||
<InputError intent="danger">{errors.from.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.from.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label={t('To (Vega key)')} labelFor="to">
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label={t('To (Vega key)')} labelFor="to">
|
||||
<AddressField
|
||||
pubKeys={pubKeys}
|
||||
onChange={() => setValue('to', '')}
|
||||
select={
|
||||
<Select {...register('to')} id="to" defaultValue="">
|
||||
<TradingSelect {...register('to')} id="to" defaultValue="">
|
||||
<option value="" disabled>
|
||||
{t('Please select')}
|
||||
</option>
|
||||
@@ -215,10 +217,10 @@ export const DepositForm = ({
|
||||
{pk}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</TradingSelect>
|
||||
}
|
||||
input={
|
||||
<Input
|
||||
<TradingInput
|
||||
// eslint-disable-next-line jsx-a11y/no-autofocus
|
||||
autoFocus={true} // focus input immediately after is shown
|
||||
id="to"
|
||||
@@ -233,12 +235,12 @@ export const DepositForm = ({
|
||||
}
|
||||
/>
|
||||
{errors.to?.message && (
|
||||
<InputError intent="danger" forInput="to">
|
||||
<TradingInputError intent="danger" forInput="to">
|
||||
{errors.to.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label={t('Asset')} labelFor="asset">
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label={t('Asset')} labelFor="asset">
|
||||
<Controller
|
||||
control={control}
|
||||
name="asset"
|
||||
@@ -248,7 +250,7 @@ export const DepositForm = ({
|
||||
},
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<RichSelect
|
||||
<TradingRichSelect
|
||||
data-testid="select-asset"
|
||||
id={field.name}
|
||||
name={field.name}
|
||||
@@ -271,13 +273,13 @@ export const DepositForm = ({
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</RichSelect>
|
||||
</TradingRichSelect>
|
||||
)}
|
||||
/>
|
||||
{errors.asset?.message && (
|
||||
<InputError intent="danger" forInput="asset">
|
||||
<TradingInputError intent="danger" forInput="asset">
|
||||
{errors.asset.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
{isActive && isFaucetable && selectedAsset && (
|
||||
<UseButton onClick={submitFaucet}>
|
||||
@@ -296,7 +298,7 @@ export const DepositForm = ({
|
||||
{t('View asset details')}
|
||||
</button>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
<FaucetNotification
|
||||
isActive={isActive}
|
||||
selectedAsset={selectedAsset}
|
||||
@@ -308,8 +310,8 @@ export const DepositForm = ({
|
||||
</div>
|
||||
)}
|
||||
{approved && (
|
||||
<FormGroup label={t('Amount')} labelFor="amount">
|
||||
<Input
|
||||
<TradingFormGroup label={t('Amount')} labelFor="amount">
|
||||
<TradingInput
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
id="amount"
|
||||
@@ -374,9 +376,9 @@ export const DepositForm = ({
|
||||
})}
|
||||
/>
|
||||
{errors.amount?.message && (
|
||||
<InputError intent="danger" forInput="amount">
|
||||
<TradingInputError intent="danger" forInput="amount">
|
||||
{errors.amount.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
{selectedAsset && balances && (
|
||||
<UseButton
|
||||
@@ -390,7 +392,7 @@ export const DepositForm = ({
|
||||
{t('Use maximum')}
|
||||
</UseButton>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
)}
|
||||
<ApproveNotification
|
||||
isActive={isActive}
|
||||
|
||||
@@ -4,10 +4,10 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
Button,
|
||||
ButtonLink,
|
||||
Input,
|
||||
TradingInput,
|
||||
Loader,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TradingRadio,
|
||||
TradingRadioGroup,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '../../hooks';
|
||||
import { CUSTOM_NODE_KEY } from '../../types';
|
||||
@@ -76,7 +76,7 @@ export const NodeSwitcher = ({ closeDialog }: { closeDialog: () => void }) => {
|
||||
`This app will only work on ${VEGA_ENV}. Select a node to connect to.`
|
||||
)}
|
||||
</p>
|
||||
<RadioGroup
|
||||
<TradingRadioGroup
|
||||
value={nodeRadio}
|
||||
onChange={(value) => setNodeRadio(value)}
|
||||
>
|
||||
@@ -112,7 +112,7 @@ export const NodeSwitcher = ({ closeDialog }: { closeDialog: () => void }) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</TradingRadioGroup>
|
||||
<div className="mt-4">
|
||||
<Button
|
||||
fill={true}
|
||||
@@ -161,7 +161,7 @@ const CustomRowWrapper = ({
|
||||
<LayoutRow dataTestId="custom-row">
|
||||
<div className="flex w-full mb-2">
|
||||
{nodes.length > 0 && (
|
||||
<Radio
|
||||
<TradingRadio
|
||||
id="node-url-custom"
|
||||
value={CUSTOM_NODE_KEY}
|
||||
label={nodeRadio === CUSTOM_NODE_KEY ? '' : t('Other')}
|
||||
@@ -172,7 +172,7 @@ const CustomRowWrapper = ({
|
||||
data-testid="custom-node"
|
||||
className="flex items-center w-full gap-2"
|
||||
>
|
||||
<Input
|
||||
<TradingInput
|
||||
placeholder="https://"
|
||||
value={inputText}
|
||||
hasError={Boolean(error)}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ApolloError } from '@apollo/client';
|
||||
import { useHeaderStore } from '@vegaprotocol/apollo-client';
|
||||
import { isValidUrl } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Radio } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradingRadio } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CUSTOM_NODE_KEY } from '../../types';
|
||||
import {
|
||||
@@ -138,7 +138,7 @@ export const RowData = ({
|
||||
<>
|
||||
{id !== CUSTOM_NODE_KEY && (
|
||||
<div className="break-all" data-testid="node">
|
||||
<Radio id={`node-url-${id}`} value={url} label={url} />
|
||||
<TradingRadio id={`node-url-${id}`} value={url} label={url} />
|
||||
</div>
|
||||
)}
|
||||
<LayoutCell
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { Market } from './markets-provider';
|
||||
import { filterAndSortMarkets, totalFeesPercentage } from './market-utils';
|
||||
import type { Market, MarketMaybeWithDataAndCandles } from './markets-provider';
|
||||
import {
|
||||
calcTradedFactor,
|
||||
filterAndSortMarkets,
|
||||
totalFeesPercentage,
|
||||
} from './market-utils';
|
||||
const { MarketState, MarketTradingMode } = Schema;
|
||||
|
||||
const MARKET_A: Partial<Market> = {
|
||||
@@ -77,3 +81,52 @@ describe('totalFees', () => {
|
||||
expect(totalFeesPercentage(i)).toEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcTradedFactor', () => {
|
||||
const marketA = {
|
||||
data: {
|
||||
markPrice: '10',
|
||||
},
|
||||
candles: [
|
||||
{
|
||||
volume: '1000',
|
||||
},
|
||||
],
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
settlementAsset: {
|
||||
decimals: 18,
|
||||
quantum: '1000000000000000000', // 1
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const marketB = {
|
||||
data: {
|
||||
markPrice: '10',
|
||||
},
|
||||
candles: [
|
||||
{
|
||||
volume: '1000',
|
||||
},
|
||||
],
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
settlementAsset: {
|
||||
decimals: 18,
|
||||
quantum: '1', // 0.0000000000000000001
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
it('a is "traded" more than b', () => {
|
||||
const fa = calcTradedFactor(marketA as MarketMaybeWithDataAndCandles);
|
||||
const fb = calcTradedFactor(marketB as MarketMaybeWithDataAndCandles);
|
||||
// it should be true because market a's asset is "more valuable" than b's
|
||||
expect(fa > fb).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import { formatNumberPercentage, toBigNum } from '@vegaprotocol/utils';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import type { Market, Candle, MarketMaybeWithData } from '../';
|
||||
import type {
|
||||
Market,
|
||||
Candle,
|
||||
MarketMaybeWithData,
|
||||
MarketMaybeWithDataAndCandles,
|
||||
} from '../';
|
||||
const { MarketState, MarketTradingMode } = Schema;
|
||||
|
||||
export const totalFees = (fees: Market['fees']['factors']) => {
|
||||
@@ -86,3 +91,18 @@ export const calcCandleHigh = (candles: Candle[]): string | undefined => {
|
||||
export const calcCandleVolume = (candles: Candle[]): string | undefined =>
|
||||
candles &&
|
||||
candles.reduce((acc, c) => new BigNumber(acc).plus(c.volume).toString(), '0');
|
||||
|
||||
export const calcTradedFactor = (m: MarketMaybeWithDataAndCandles) => {
|
||||
const volume = Number(calcCandleVolume(m.candles || []) || 0);
|
||||
const price = m.data?.markPrice ? Number(m.data.markPrice) : 0;
|
||||
const quantum = Number(
|
||||
m.tradableInstrument.instrument.product.settlementAsset.quantum
|
||||
);
|
||||
const decimals = Number(
|
||||
m.tradableInstrument.instrument.product.settlementAsset.decimals
|
||||
);
|
||||
const fp = toBigNum(price, decimals);
|
||||
const fq = toBigNum(quantum, decimals);
|
||||
const factor = fq.multipliedBy(fp).multipliedBy(volume);
|
||||
return factor.toNumber();
|
||||
};
|
||||
|
||||
@@ -128,6 +128,9 @@ fragment StopOrderFields on StopOrder {
|
||||
updatedAt
|
||||
partyId
|
||||
marketId
|
||||
order {
|
||||
...OrderFields
|
||||
}
|
||||
trigger {
|
||||
... on StopOrderPrice {
|
||||
price
|
||||
|
||||
+37
-33
@@ -34,22 +34,51 @@ export type OrdersUpdateSubscription = { __typename?: 'Subscription', orders?: A
|
||||
|
||||
export type OrderSubmissionFieldsFragment = { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null };
|
||||
|
||||
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
|
||||
export type StopOrderFieldsFragment = { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } };
|
||||
|
||||
export type StopOrdersQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
|
||||
export type StopOrdersQuery = { __typename?: 'Query', stopOrders?: { __typename?: 'StopOrderConnection', edges?: Array<{ __typename?: 'StopOrderEdge', node?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null }> | null } | null };
|
||||
|
||||
export type StopOrderByIdQueryVariables = Types.Exact<{
|
||||
stopOrderId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
|
||||
export type StopOrderByIdQuery = { __typename?: 'Query', stopOrder?: { __typename?: 'StopOrder', id: string, ocoLinkId?: string | null, expiresAt?: any | null, expiryStrategy?: Types.StopOrderExpiryStrategy | null, triggerDirection: Types.StopOrderTriggerDirection, status: Types.StopOrderStatus, createdAt: any, updatedAt?: any | null, partyId: string, marketId: string, order?: { __typename?: 'Order', id: string, type?: Types.OrderType | null, side: Types.Side, size: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, price: string, timeInForce: Types.OrderTimeInForce, remaining: string, expiresAt?: any | null, createdAt: any, updatedAt?: any | null, postOnly?: boolean | null, reduceOnly?: boolean | null, market: { __typename?: 'Market', id: string }, liquidityProvision?: { __typename: 'LiquidityProvision' } | null, peggedOrder?: { __typename: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null, icebergOrder?: { __typename: 'IcebergOrder', peakSize: string, minimumVisibleSize: string, reservedRemaining: string } | null } | null, trigger: { __typename?: 'StopOrderPrice', price: string } | { __typename?: 'StopOrderTrailingPercentOffset', trailingPercentOffset: string }, submission: { __typename?: 'OrderSubmission', marketId: string, price: string, size: string, side: Types.Side, timeInForce: Types.OrderTimeInForce, expiresAt: any, type: Types.OrderType, reference?: string | null, postOnly?: boolean | null, reduceOnly?: boolean | null, peggedOrder?: { __typename?: 'PeggedOrder', reference: Types.PeggedReference, offset: string } | null } } | null };
|
||||
|
||||
export const OrderUpdateFieldsFragmentDoc = gql`
|
||||
fragment OrderUpdateFields on OrderUpdate {
|
||||
id
|
||||
marketId
|
||||
type
|
||||
side
|
||||
size
|
||||
status
|
||||
rejectionReason
|
||||
price
|
||||
timeInForce
|
||||
remaining
|
||||
expiresAt
|
||||
createdAt
|
||||
updatedAt
|
||||
liquidityProvisionId
|
||||
peggedOrder {
|
||||
__typename
|
||||
reference
|
||||
offset
|
||||
}
|
||||
icebergOrder {
|
||||
__typename
|
||||
peakSize
|
||||
minimumVisibleSize
|
||||
reservedRemaining
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const OrderFieldsFragmentDoc = gql`
|
||||
fragment OrderFields on Order {
|
||||
id
|
||||
@@ -85,35 +114,6 @@ export const OrderFieldsFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const OrderUpdateFieldsFragmentDoc = gql`
|
||||
fragment OrderUpdateFields on OrderUpdate {
|
||||
id
|
||||
marketId
|
||||
type
|
||||
side
|
||||
size
|
||||
status
|
||||
rejectionReason
|
||||
price
|
||||
timeInForce
|
||||
remaining
|
||||
expiresAt
|
||||
createdAt
|
||||
updatedAt
|
||||
liquidityProvisionId
|
||||
peggedOrder {
|
||||
__typename
|
||||
reference
|
||||
offset
|
||||
}
|
||||
icebergOrder {
|
||||
__typename
|
||||
peakSize
|
||||
minimumVisibleSize
|
||||
reservedRemaining
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const OrderSubmissionFieldsFragmentDoc = gql`
|
||||
fragment OrderSubmissionFields on OrderSubmission {
|
||||
marketId
|
||||
@@ -144,6 +144,9 @@ export const StopOrderFieldsFragmentDoc = gql`
|
||||
updatedAt
|
||||
partyId
|
||||
marketId
|
||||
order {
|
||||
...OrderFields
|
||||
}
|
||||
trigger {
|
||||
... on StopOrderPrice {
|
||||
price
|
||||
@@ -156,7 +159,8 @@ export const StopOrderFieldsFragmentDoc = gql`
|
||||
...OrderSubmissionFields
|
||||
}
|
||||
}
|
||||
${OrderSubmissionFieldsFragmentDoc}`;
|
||||
${OrderFieldsFragmentDoc}
|
||||
${OrderSubmissionFieldsFragmentDoc}`;
|
||||
export const OrderByIdDocument = gql`
|
||||
query OrderById($orderId: ID!) {
|
||||
orderByID(id: $orderId) {
|
||||
|
||||
@@ -9,9 +9,9 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { Size } from '@vegaprotocol/datagrid';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
FormGroup,
|
||||
Input,
|
||||
InputError,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
Button,
|
||||
Dialog,
|
||||
Icon,
|
||||
@@ -102,8 +102,12 @@ export const OrderEditDialog = ({
|
||||
noValidate
|
||||
>
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<FormGroup label={t('Price')} labelFor="limitPrice" className="grow">
|
||||
<Input
|
||||
<TradingFormGroup
|
||||
label={t('Price')}
|
||||
labelFor="limitPrice"
|
||||
className="grow"
|
||||
>
|
||||
<TradingInput
|
||||
type="number"
|
||||
step={step}
|
||||
{...register('limitPrice', {
|
||||
@@ -119,13 +123,13 @@ export const OrderEditDialog = ({
|
||||
id="limitPrice"
|
||||
/>
|
||||
{errors.limitPrice?.message && (
|
||||
<InputError intent="danger">
|
||||
<TradingInputError intent="danger">
|
||||
{errors.limitPrice.message}
|
||||
</InputError>
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label={t('Size')} labelFor="size" className="grow">
|
||||
<Input
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup label={t('Size')} labelFor="size" className="grow">
|
||||
<TradingInput
|
||||
type="number"
|
||||
step={stepSize}
|
||||
{...register('size', {
|
||||
@@ -139,9 +143,11 @@ export const OrderEditDialog = ({
|
||||
id="size"
|
||||
/>
|
||||
{errors.size?.message && (
|
||||
<InputError intent="danger">{errors.size.message}</InputError>
|
||||
<TradingInputError intent="danger">
|
||||
{errors.size.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</TradingFormGroup>
|
||||
</div>
|
||||
<Button variant="primary" size="md" type="submit">
|
||||
{t('Update')}
|
||||
|
||||
@@ -296,7 +296,7 @@ export const OrderListTable = memo<
|
||||
</ButtonLink>
|
||||
</>
|
||||
)}
|
||||
<ActionsDropdown data-testid="market-actions-content">
|
||||
<ActionsDropdown data-testid="order-actions-content">
|
||||
<TradingDropdownCopyItem
|
||||
value={data.id}
|
||||
text={t('Copy order ID')}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { StopOrdersTable } from '../stop-orders-table/stop-orders-table';
|
||||
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
|
||||
import type { StopOrder } from '../order-data-provider/stop-orders-data-provider';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { stopOrdersWithMarketProvider } from '../order-data-provider/stop-orders-data-provider';
|
||||
import { OrderViewDialog } from '../order-list/order-view-dialog';
|
||||
import type { Order } from '../order-data-provider';
|
||||
|
||||
export interface StopOrdersManagerProps {
|
||||
partyId: string;
|
||||
@@ -23,6 +25,7 @@ export const StopOrdersManager = ({
|
||||
gridProps,
|
||||
}: StopOrdersManagerProps) => {
|
||||
const create = useVegaTransactionStore((state) => state.create);
|
||||
const [viewOrder, setViewOrder] = useState<Order | null>(null);
|
||||
const variables = { partyId };
|
||||
|
||||
const { data, error, reload } = useDataProvider({
|
||||
@@ -53,14 +56,25 @@ export const StopOrdersManager = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<StopOrdersTable
|
||||
rowData={data}
|
||||
onCancel={cancel}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
suppressAutoSize
|
||||
overlayNoRowsTemplate={error ? error.message : t('No stop orders')}
|
||||
{...gridProps}
|
||||
/>
|
||||
<>
|
||||
<StopOrdersTable
|
||||
rowData={data}
|
||||
onCancel={cancel}
|
||||
onView={setViewOrder}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
suppressAutoSize
|
||||
overlayNoRowsTemplate={error ? error.message : t('No stop orders')}
|
||||
{...gridProps}
|
||||
/>
|
||||
{viewOrder && (
|
||||
<OrderViewDialog
|
||||
isOpen={Boolean(viewOrder)}
|
||||
order={viewOrder}
|
||||
onChange={() => setViewOrder(null)}
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { PartialDeep } from 'type-fest';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import {
|
||||
StopOrdersTable,
|
||||
type StopOrdersTableProps,
|
||||
@@ -27,6 +28,7 @@ jest.mock('@vegaprotocol/utils', () => ({
|
||||
}));
|
||||
|
||||
const defaultProps: StopOrdersTableProps = {
|
||||
onView: jest.fn(),
|
||||
rowData: [],
|
||||
onCancel: jest.fn(),
|
||||
isReadOnly: false,
|
||||
@@ -104,6 +106,7 @@ const rowData = [
|
||||
generateStopOrder({
|
||||
id: 'stop-order-6',
|
||||
status: Schema.StopOrderStatus.STATUS_TRIGGERED,
|
||||
order: { id: 'order-id' },
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -234,4 +237,37 @@ describe('StopOrdersTable', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows actions dropdown only for triggered stop orders', async () => {
|
||||
await act(async () => {
|
||||
render(generateJsx({ rowData }));
|
||||
});
|
||||
const dropdownMenuButtons = screen.getAllByTestId('dropdown-menu');
|
||||
expect(dropdownMenuButtons).toHaveLength(1);
|
||||
dropdownMenuButtons.forEach((dropdownMenuButton) => {
|
||||
const id = dropdownMenuButton
|
||||
.closest('[role="row"]')
|
||||
?.getAttribute('row-id');
|
||||
expect(rowData.find((row) => row.id === id)?.status).toEqual(
|
||||
Schema.StopOrderStatus.STATUS_TRIGGERED
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('action dropdown has copy and view order actions', async () => {
|
||||
const onView = jest.fn();
|
||||
const user = userEvent.setup();
|
||||
await act(async () => {
|
||||
render(generateJsx({ rowData, onView }));
|
||||
});
|
||||
const dropdownMenuButtons = screen.getByTestId('dropdown-menu');
|
||||
dropdownMenuButtons.click();
|
||||
await user.click(dropdownMenuButtons as HTMLButtonElement);
|
||||
const menuItems = screen.getAllByRole('menuitem');
|
||||
expect(menuItems).toHaveLength(2);
|
||||
expect(menuItems[0]).toHaveTextContent('Copy order ID');
|
||||
expect(menuItems[1]).toHaveTextContent('View order details');
|
||||
menuItems[1].click();
|
||||
expect(onView).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,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,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
DropdownMenuItem,
|
||||
TradingDropdownCopyItem,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { ForwardedRef } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import {
|
||||
@@ -28,6 +35,7 @@ import type {
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { StopOrder } from '../order-data-provider/stop-orders-data-provider';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import type { Order } from '../order-data-provider';
|
||||
|
||||
const defaultColDef = {
|
||||
resizable: true,
|
||||
@@ -38,12 +46,13 @@ const defaultColDef = {
|
||||
export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
|
||||
onCancel: (order: StopOrder) => void;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
onView: (order: Order) => void;
|
||||
isReadOnly: boolean;
|
||||
};
|
||||
|
||||
export const StopOrdersTable = memo<
|
||||
StopOrdersTableProps & { ref?: ForwardedRef<AgGridReact> }
|
||||
>(({ onCancel, onMarketClick, ...props }: StopOrdersTableProps) => {
|
||||
>(({ onCancel, onView, onMarketClick, ...props }: StopOrdersTableProps) => {
|
||||
const showAllActions = !props.isReadOnly;
|
||||
const columnDefs: ColDef[] = useMemo(
|
||||
() => [
|
||||
@@ -236,12 +245,32 @@ export const StopOrdersTable = memo<
|
||||
{t('Cancel')}
|
||||
</ButtonLink>
|
||||
)}
|
||||
{data.status === Schema.StopOrderStatus.STATUS_TRIGGERED &&
|
||||
data.order && (
|
||||
<ActionsDropdown data-testid="stop-order-actions-content">
|
||||
<TradingDropdownCopyItem
|
||||
value={data.order.id}
|
||||
text={t('Copy order ID')}
|
||||
/>
|
||||
<DropdownMenuItem
|
||||
key={'view-order'}
|
||||
data-testid="view-order"
|
||||
onClick={() =>
|
||||
data.order &&
|
||||
onView({ ...data.order, market: data.market })
|
||||
}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.INFO} size={16} />
|
||||
{t('View order details')}
|
||||
</DropdownMenuItem>
|
||||
</ActionsDropdown>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[onCancel, onMarketClick, props.isReadOnly, showAllActions]
|
||||
[onCancel, onMarketClick, onView, props.isReadOnly, showAllActions]
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,12 @@ import * as Schema from '@vegaprotocol/types';
|
||||
import type { Account } from '@vegaprotocol/accounts';
|
||||
import type { MarketWithData } from '@vegaprotocol/markets';
|
||||
import type { PositionFieldsFragment } from './__generated__/Positions';
|
||||
import { getMetrics, rejoinPositionData } from './positions-data-providers';
|
||||
import type { Position } from './positions-data-providers';
|
||||
import {
|
||||
getMetrics,
|
||||
preparePositions,
|
||||
rejoinPositionData,
|
||||
} from './positions-data-providers';
|
||||
import { PositionStatus } from '@vegaprotocol/types';
|
||||
|
||||
const accounts = [
|
||||
@@ -223,4 +228,29 @@ describe('getMetrics && rejoinPositionData', () => {
|
||||
);
|
||||
expect(metrics[1].status).toEqual(positions[1].positionStatus);
|
||||
});
|
||||
|
||||
it('sorts and filters positions', () => {
|
||||
const createPosition = (override?: Partial<Position>) =>
|
||||
({
|
||||
marketState: Schema.MarketState.STATE_ACTIVE,
|
||||
marketCode: 'a',
|
||||
...override,
|
||||
} as Position);
|
||||
|
||||
const data = [
|
||||
createPosition(),
|
||||
createPosition({
|
||||
marketCode: 'c',
|
||||
marketState: Schema.MarketState.STATE_CANCELLED,
|
||||
}),
|
||||
createPosition({ marketCode: 'd' }),
|
||||
createPosition({ marketCode: 'b' }),
|
||||
];
|
||||
|
||||
const withoutClosed = preparePositions(data, false);
|
||||
expect(withoutClosed.map((p) => p.marketCode)).toEqual(['a', 'b', 'd']);
|
||||
|
||||
const withClosed = preparePositions(data, true);
|
||||
expect(withClosed.map((p) => p.marketCode)).toEqual(['a', 'b', 'c', 'd']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,6 +41,7 @@ export interface Position {
|
||||
marketId: string;
|
||||
marketCode: string;
|
||||
marketTradingMode: Schema.MarketTradingMode;
|
||||
marketState: Schema.MarketState;
|
||||
markPrice: string | undefined;
|
||||
notional: string | undefined;
|
||||
openVolume: string;
|
||||
@@ -119,6 +120,7 @@ export const getMetrics = (
|
||||
marketId: market.id,
|
||||
marketCode: market.tradableInstrument.instrument.code,
|
||||
marketTradingMode: market.tradingMode,
|
||||
marketState: market.state,
|
||||
markPrice: marketData ? marketData.markPrice : undefined,
|
||||
notional: notional
|
||||
? notional.multipliedBy(10 ** marketDecimalPlaces).toFixed(0)
|
||||
@@ -179,7 +181,7 @@ const getSubscriptionVariables = (
|
||||
): PositionsSubscriptionSubscriptionVariables[] =>
|
||||
([] as string[]).concat(variables.partyIds).map((partyId) => ({ partyId }));
|
||||
|
||||
const positionsDataProvider = makeDataProvider<
|
||||
export const positionsDataProvider = makeDataProvider<
|
||||
PositionsQuery,
|
||||
PositionFieldsFragment[],
|
||||
PositionsSubscriptionSubscription,
|
||||
@@ -248,6 +250,26 @@ export const rejoinPositionData = (
|
||||
return null;
|
||||
};
|
||||
|
||||
export const preparePositions = (metrics: Position[], showClosed: boolean) => {
|
||||
return sortBy(metrics, 'marketCode').filter((p) => {
|
||||
if (showClosed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
[
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketState.STATE_PENDING,
|
||||
Schema.MarketState.STATE_SUSPENDED,
|
||||
].includes(p.marketState)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
export const positionsMarketsProvider = makeDerivedDataProvider<
|
||||
string[],
|
||||
never,
|
||||
@@ -265,7 +287,7 @@ export const positionsMarketsProvider = makeDerivedDataProvider<
|
||||
export const positionsMetricsProvider = makeDerivedDataProvider<
|
||||
Position[],
|
||||
Position[],
|
||||
PositionsQueryVariables & { marketIds: string[] }
|
||||
PositionsQueryVariables & { marketIds: string[]; showClosed: boolean }
|
||||
>(
|
||||
[
|
||||
(callback, client, variables) =>
|
||||
@@ -281,10 +303,10 @@ export const positionsMetricsProvider = makeDerivedDataProvider<
|
||||
marketIds: variables.marketIds,
|
||||
}),
|
||||
],
|
||||
([positions, accounts, marketsData]) => {
|
||||
([positions, accounts, marketsData], variables) => {
|
||||
const positionsData = rejoinPositionData(positions, marketsData);
|
||||
const metrics = getMetrics(positionsData, accounts as Account[] | null);
|
||||
return sortBy(metrics, 'marketCode');
|
||||
return preparePositions(metrics, variables.showClosed);
|
||||
},
|
||||
(data, delta, previousData) =>
|
||||
data.filter((row) => {
|
||||
|
||||
@@ -16,6 +16,7 @@ interface PositionsManagerProps {
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
isReadOnly: boolean;
|
||||
gridProps?: ReturnType<typeof useDataGridEvents>;
|
||||
showClosed?: boolean;
|
||||
}
|
||||
|
||||
export const PositionsManager = ({
|
||||
@@ -23,6 +24,7 @@ export const PositionsManager = ({
|
||||
onMarketClick,
|
||||
isReadOnly,
|
||||
gridProps,
|
||||
showClosed = false,
|
||||
}: PositionsManagerProps) => {
|
||||
const { pubKeys, pubKey } = useVegaWallet();
|
||||
const create = useVegaTransactionStore((store) => store.create);
|
||||
@@ -60,7 +62,7 @@ export const PositionsManager = ({
|
||||
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: positionsMetricsProvider,
|
||||
variables: { partyIds, marketIds: marketIds || [] },
|
||||
variables: { partyIds, marketIds: marketIds || [], showClosed },
|
||||
skip: !marketIds,
|
||||
});
|
||||
|
||||
@@ -68,7 +70,7 @@ export const PositionsManager = ({
|
||||
<PositionsTable
|
||||
pubKey={pubKey}
|
||||
pubKeys={pubKeys}
|
||||
rowData={error ? [] : data}
|
||||
rowData={data}
|
||||
onMarketClick={onMarketClick}
|
||||
onClose={onClose}
|
||||
isReadOnly={isReadOnly}
|
||||
|
||||
@@ -27,6 +27,7 @@ const singleRow: Position = {
|
||||
marketId: 'string',
|
||||
marketCode: 'ETHBTC.QM21',
|
||||
marketTradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
||||
marketState: Schema.MarketState.STATE_ACTIVE,
|
||||
markPrice: '123',
|
||||
notional: '12300',
|
||||
openVolume: '100',
|
||||
|
||||
@@ -473,8 +473,8 @@ export const PositionsTable = ({
|
||||
</div>
|
||||
);
|
||||
},
|
||||
minWidth: 75,
|
||||
maxWidth: 75,
|
||||
minWidth: 55,
|
||||
maxWidth: 55,
|
||||
}
|
||||
: null,
|
||||
];
|
||||
|
||||
@@ -17,4 +17,17 @@ export const proposalsDataProvider = makeDataProvider<
|
||||
never,
|
||||
never,
|
||||
ProposalsListQueryVariables
|
||||
>({ query: ProposalsListDocument, getData });
|
||||
>({
|
||||
query: ProposalsListDocument,
|
||||
getData,
|
||||
/**
|
||||
* Ignores errors for not found settlement asset for NewMarket proposals.
|
||||
*
|
||||
* It can happen that a NewMarket proposal is incomplete and does not contain
|
||||
* `futureProduct` details. This guard protects against that.
|
||||
*
|
||||
* GQL Path: `terms.change.instrument.futureProduct.settlementAsset`
|
||||
*/
|
||||
errorPolicyGuard: (errors) =>
|
||||
errors.every((e) => e.message.match(/failed to get asset for ID/)),
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"name": "@vegaprotocol/react-helpers",
|
||||
"version": "0.2.5"
|
||||
"version": "0.2.5",
|
||||
"peerDependencies": {
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ module.exports = {
|
||||
900: '#F9FAFA',
|
||||
},
|
||||
},
|
||||
danger: '#FF077F',
|
||||
danger: '#EC003C',
|
||||
warning: '#FF8700',
|
||||
success: '#00F780',
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "@vegaprotocol/types",
|
||||
"version": "0.0.4"
|
||||
"version": "0.0.5"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
{
|
||||
"name": "@vegaprotocol/ui-toolkit",
|
||||
"version": "0.12.7"
|
||||
"version": "0.12.8",
|
||||
"peerDependencies": {
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ export * from './form-group';
|
||||
export * from './healthbar';
|
||||
export * from './icon';
|
||||
export * from './indicator';
|
||||
export * from './input-error';
|
||||
export * from './input';
|
||||
export * from './input-error';
|
||||
export * from './key-value-table';
|
||||
export * from './link';
|
||||
export * from './loader';
|
||||
@@ -48,10 +48,18 @@ export * from './tiny-scroll';
|
||||
export * from './toast';
|
||||
export * from './toggle';
|
||||
export * from './tooltip';
|
||||
export * from './trading-button';
|
||||
export * from './trading-dropdown';
|
||||
export * from './traffic-light';
|
||||
export * from './vega-icons';
|
||||
export * from './vega-logo';
|
||||
export * from './viewing-as-user';
|
||||
export * from './pill';
|
||||
|
||||
// Trading specific components
|
||||
export * from './trading-button';
|
||||
export * from './trading-checkbox';
|
||||
export * from './trading-dropdown';
|
||||
export * from './trading-form-group';
|
||||
export * from './trading-input-error';
|
||||
export * from './trading-input';
|
||||
export * from './trading-radio-group';
|
||||
export * from './trading-select';
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { TradingCheckbox } from './checkbox';
|
||||
|
||||
describe('Checkbox', () => {
|
||||
it('should render checkbox with label successfully', () => {
|
||||
render(<TradingCheckbox label="test" />);
|
||||
expect(screen.getByText('test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render a checked checkbox if specified in state', () => {
|
||||
render(<TradingCheckbox label="label" checked={true} />);
|
||||
expect(screen.getByTestId(/icon-/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render an unchecked checkbox if specified in state', () => {
|
||||
render(<TradingCheckbox label="unchecked" checked={false} />);
|
||||
expect(screen.queryByTestId(/icon-/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render an indeterminate checkbox if specified in state', () => {
|
||||
render(<TradingCheckbox label="indeterminate" checked="indeterminate" />);
|
||||
expect(screen.getByTestId('indeterminate-icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('fires callback on change if provided', () => {
|
||||
const callback = jest.fn();
|
||||
|
||||
render(
|
||||
<TradingCheckbox
|
||||
name="test"
|
||||
label="onchange"
|
||||
onCheckedChange={callback}
|
||||
/>
|
||||
);
|
||||
|
||||
const checkbox = screen.getByText('onchange');
|
||||
fireEvent.click(checkbox);
|
||||
expect(callback).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { Meta, StoryFn } from '@storybook/react';
|
||||
import type { TradingCheckboxProps } from './checkbox';
|
||||
import { TradingCheckbox } from './checkbox';
|
||||
|
||||
export default {
|
||||
component: TradingCheckbox,
|
||||
title: 'Checkbox',
|
||||
} as Meta<typeof TradingCheckbox>;
|
||||
|
||||
const Template: StoryFn<TradingCheckboxProps> = (args) => (
|
||||
<TradingCheckbox {...args} />
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
name: 'default',
|
||||
label: 'Regular checkbox',
|
||||
};
|
||||
|
||||
export const Overflow = Template.bind({});
|
||||
Overflow.args = {
|
||||
name: 'overflow',
|
||||
label:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
|
||||
};
|
||||
|
||||
export const Disabled = Template.bind({});
|
||||
Disabled.args = {
|
||||
disabled: true,
|
||||
label: 'Disabled',
|
||||
};
|
||||
|
||||
export const Indeterminate = Template.bind({});
|
||||
Indeterminate.args = {
|
||||
name: 'default',
|
||||
checked: 'indeterminate',
|
||||
label: 'Indeterminate checkbox',
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import { VegaIcon, VegaIconNames } from '../icon';
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type CheckedState = boolean | 'indeterminate';
|
||||
export interface TradingCheckboxProps {
|
||||
checked?: CheckedState;
|
||||
label?: ReactNode;
|
||||
name?: string;
|
||||
onCheckedChange?: (checked: CheckedState) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export const TradingCheckbox = ({
|
||||
checked,
|
||||
label,
|
||||
name,
|
||||
onCheckedChange,
|
||||
disabled = false,
|
||||
}: TradingCheckboxProps) => {
|
||||
const rootClasses = classNames(
|
||||
'relative flex justify-center items-center w-3 h-3',
|
||||
'border rounded-sm overflow-hidden',
|
||||
'border-vega-clight-500 dark:border-vega-cdark-500',
|
||||
'aria-checked:border-vega-clight-400 dark:aria-checked:border-vega-cdark-400',
|
||||
'disabled:border-vega-clight-600 dark:disabled:border-vega-cdark-600',
|
||||
'bg-vega-clight-700 dark:bg-vega-cdark-700'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex gap-1.5 items-center">
|
||||
<CheckboxPrimitive.Root
|
||||
name={name}
|
||||
id={name}
|
||||
className={rootClasses}
|
||||
checked={checked}
|
||||
onCheckedChange={onCheckedChange}
|
||||
disabled={disabled}
|
||||
data-testid={name}
|
||||
>
|
||||
<CheckboxPrimitive.CheckboxIndicator className="flex justify-center items-center w-3 h-3">
|
||||
{checked === 'indeterminate' ? (
|
||||
<span
|
||||
data-testid="indeterminate-icon"
|
||||
className="absolute w-[8px] h-[2px] bg-vega-clight-50 dark:bg-vega-cdark-50"
|
||||
/>
|
||||
) : (
|
||||
<VegaIcon name={VegaIconNames.TICK} size={10} />
|
||||
)}
|
||||
</CheckboxPrimitive.CheckboxIndicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
<label
|
||||
htmlFor={name}
|
||||
className={classNames('text-xs flex-1', {
|
||||
'text-vega-clight-200 dark:text-vega-cdark-200': disabled,
|
||||
})}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './checkbox';
|
||||
@@ -0,0 +1,23 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import { TradingFormGroup } from './form-group';
|
||||
|
||||
describe('FormGroup', () => {
|
||||
it('should render label if given a label', () => {
|
||||
render(
|
||||
<TradingFormGroup label="label" labelFor="test">
|
||||
<input id="test"></input>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
expect(screen.getByLabelText('label')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render children', () => {
|
||||
render(
|
||||
<TradingFormGroup label="label" labelFor="test">
|
||||
<input data-testid="foo" id="test"></input>
|
||||
</TradingFormGroup>
|
||||
);
|
||||
expect(screen.getByTestId('foo')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import classNames from 'classnames';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export interface TradingFormGroupProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
label: string | ReactNode; // For accessibility reasons this must always be set for screen readers. If you want it to not show, then use the hideLabel prop"
|
||||
labelFor: string; // Same as above
|
||||
hideLabel?: boolean;
|
||||
disabled?: boolean;
|
||||
labelDescription?: string;
|
||||
labelAlign?: 'left' | 'right';
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export const TradingFormGroup = ({
|
||||
children,
|
||||
className,
|
||||
label,
|
||||
labelFor,
|
||||
labelDescription,
|
||||
labelAlign = 'left',
|
||||
hideLabel = false,
|
||||
compact = false,
|
||||
disabled = false,
|
||||
}: TradingFormGroupProps) => {
|
||||
const wrapperClasses = classNames(
|
||||
'relative',
|
||||
{
|
||||
'mb-2': compact,
|
||||
'mb-4': !compact,
|
||||
},
|
||||
className
|
||||
);
|
||||
const labelClasses = classNames('block mb-2 text-xs', {
|
||||
'text-right': labelAlign === 'right',
|
||||
'sr-only': hideLabel,
|
||||
'text-muted': disabled,
|
||||
});
|
||||
return (
|
||||
<div data-testid="form-group" className={wrapperClasses}>
|
||||
{label && (
|
||||
<label htmlFor={labelFor} className={labelClasses}>
|
||||
{label}
|
||||
{labelDescription && (
|
||||
<div className="font-light mt-1">{labelDescription}</div>
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
import type { StoryFn, Meta } from '@storybook/react';
|
||||
import { TradingInput } from '../trading-input';
|
||||
import type { TradingFormGroupProps } from './form-group';
|
||||
import { TradingFormGroup } from './form-group';
|
||||
export default {
|
||||
component: TradingFormGroup,
|
||||
title: 'FormGroup',
|
||||
argTypes: {
|
||||
label: {
|
||||
type: 'string',
|
||||
},
|
||||
labelFor: {
|
||||
type: 'string',
|
||||
},
|
||||
labelDescription: {
|
||||
type: 'string',
|
||||
},
|
||||
className: {
|
||||
type: 'string',
|
||||
},
|
||||
hasError: {
|
||||
type: 'boolean',
|
||||
},
|
||||
disabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
const Template: StoryFn<TradingFormGroupProps> = (args) => (
|
||||
<TradingFormGroup {...args}>
|
||||
<TradingInput id="labelFor" />
|
||||
</TradingFormGroup>
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
label: 'Label',
|
||||
labelFor: 'labelFor',
|
||||
};
|
||||
|
||||
export const WithLabelDescription = Template.bind({});
|
||||
WithLabelDescription.args = {
|
||||
label: 'Label',
|
||||
labelFor: 'labelFor',
|
||||
labelDescription: 'Description text',
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './form-group';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './input-error';
|
||||
@@ -0,0 +1,10 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import { TradingInputError } from './input-error';
|
||||
|
||||
describe('InputError', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<TradingInputError />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { StoryFn, Meta } from '@storybook/react';
|
||||
import { TradingInputError } from './input-error';
|
||||
|
||||
export default {
|
||||
component: TradingInputError,
|
||||
title: 'InputError',
|
||||
} as Meta;
|
||||
|
||||
const Template: StoryFn = (args) => <TradingInputError {...args} />;
|
||||
|
||||
export const Danger = Template.bind({});
|
||||
Danger.args = {
|
||||
children: 'An error that might have happened',
|
||||
};
|
||||
|
||||
export const Warning = Template.bind({});
|
||||
Warning.args = {
|
||||
intent: 'warning',
|
||||
children: 'Something that might be an issue',
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user