Compare commits

..
Author SHA1 Message Date
Matthew Russell a1e8fc878d chore: remove forwardRef as refs weren't being passed 2023-06-21 17:11:27 -07:00
140 changed files with 1439 additions and 2605 deletions
+19 -41
View File
@@ -6,9 +6,7 @@ on:
- release/* - release/*
- develop - develop
- main - main
# uncomment pull_request and comment pull_request_target to test CI changes against feature branch not target branch (develop) pull_request:
# pull_request:
pull_request_target:
types: types:
- opened - opened
- ready_for_review - ready_for_review
@@ -22,8 +20,6 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Cache node modules - name: Cache node modules
id: cache id: cache
@@ -64,7 +60,6 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
@@ -112,82 +107,65 @@ jobs:
echo "Branch slug: ${branch_slug}" echo "Branch slug: ${branch_slug}"
echo ">>>> eof debug" echo ">>>> eof debug"
projects_array=() projects_e2e=""
preview_governance="not deployed" preview_governance="not deployed"
preview_trading="not deployed" preview_trading="not deployed"
preview_explorer="not deployed" preview_explorer="not deployed"
preview_tools="not deployed" preview_tools="not deployed"
# parse if affected is any of three main applications, if none - use all of them
if echo "$affected" | grep -q governance; then if echo "$affected" | grep -q governance; then
echo "Governance is affected" echo "Governance is affected"
projects_array+=("governance") projects_e2e+='"governance-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug") preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi fi
if echo "$affected" | grep -q trading; then if echo "$affected" | grep -q trading; then
echo "Trading is affected" echo "Trading is affected"
projects_array+=("trading") projects_e2e+='"trading-e2e" '
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug") preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi fi
if echo "$affected" | grep -q explorer; then if echo "$affected" | grep -q explorer; then
echo "Explorer is affected" echo "Explorer is affected"
projects_array+=("explorer") projects_e2e+='"explorer-e2e" '
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug") preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi fi
if [[ ${#projects_array[@]} -eq 0 ]]; then if [[ -z "$projects_e2e" ]]; then
projects_array=("governance" "trading" "explorer") projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug") preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug") preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug") preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi fi
projects="$(echo $projects_e2e | sed 's|-e2e||g')"
# applications parsed before this loop are applicable for running e2e-tests
projects_e2e_array=()
for project in "${projects_array[@]}"; do
projects_e2e_array+=("${project}-e2e")
done
# all applications below this loop are not applicable for running e2e-test
# check if pull request event to deploy tools
if [[ "${{ github.event_name }}" = "pull_request" ]]; then if [[ "${{ github.event_name }}" = "pull_request" ]]; then
if echo "$affected" | grep -q multisig-signer; then if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected" echo "Tools are affected"
# tools are only applicable to check previews or deploy from develop to mainnet
echo "Deploying tools on preview" echo "Deploying tools on preview"
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug") preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
projects+=' "multisig-signer" '
projects_array+=("multisig-signer")
fi fi
# those apps deploy only from develop to mainnet
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
if echo "$affected" | grep -q multisig-signer; then if echo "$affected" | grep -q multisig-signer; then
echo "Tools are affected" echo "Tools are affected"
# tools are only applicable to check previews or deploy from develop to mainnet
echo "Deploying tools on s3" echo "Deploying tools on s3"
projects+=' "multisig-signer" '
projects_array+=("multisig-signer")
fi fi
if echo "$affected" | grep -q static; then if echo "$affected" | grep -q static; then
echo "static is affected" echo "static is affected"
echo "Deploying static on s3" echo "Deploying static on s3"
projects+=' "static" '
projects_array+=("static")
fi fi
if echo "$affected" | grep -q ui-toolkit; then if echo "$affected" | grep -q ui-toolkit; then
echo "ui-toolkit is affected" echo "ui-toolkit is affected"
echo "Deploying ui-toolkit on s3" echo "Deploying ui-toolkit on s3"
projects+=' "ui-toolkit" '
projects_array+=("ui-toolkit")
fi fi
fi fi
echo "Projects: ${projects_array[@]}" projects_e2e=${projects_e2e%?}
echo "Projects E2E: ${projects_e2e_array[@]}" projects_e2e=[${projects_e2e// /,}]
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}") projects=[${projects// /,}]
projects_e2e_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_e2e_array[@]}") echo PROJECTS_E2E=$projects_e2e >> $GITHUB_ENV
echo PROJECTS=$projects >> $GITHUB_ENV
echo PROJECTS_E2E=$projects_e2e_json >> $GITHUB_ENV
echo PROJECTS=$projects_json >> $GITHUB_ENV
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
-1
View File
@@ -33,7 +33,6 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
path: './frontend-monorepo' path: './frontend-monorepo'
ref: ${{ github.event.pull_request.head.sha || github.sha }}
# Restore node_modules from cache if possible # Restore node_modules from cache if possible
- name: Restore node_modules from cache - name: Restore node_modules from cache
-2
View File
@@ -11,8 +11,6 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Setup node - name: Setup node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
-2
View File
@@ -19,8 +19,6 @@ jobs:
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@v3 uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Set up QEMU - name: Set up QEMU
id: quemu id: quemu
+1 -1
View File
@@ -49,7 +49,7 @@ module.exports = defineConfig({
vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7', vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7',
vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de', vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de',
txTimeout: { timeout: 70000 }, txTimeout: { timeout: 70000 },
epochTimeout: { timeout: 12000 }, epochTimeout: { timeout: 10000 },
blockConfirmations: 3, blockConfirmations: 3,
grepTags: '@regression @smoke @slow', grepTags: '@regression @smoke @slow',
grepFilterSpecs: true, grepFilterSpecs: true,
@@ -1,63 +0,0 @@
export const previousEpochData = {
epoch: {
id: '7611',
validatorsConnection: {
edges: [
{
node: {
id: 'cd96782bc0ad5679869cf69fe7838a92212da7f53b4a214bed68067117494122',
stakedTotal: '3154229668720612941799',
rewardScore: {
rawValidatorScore: '0.2',
performanceScore: '1',
multisigScore: '0',
validatorScore: '0.2',
normalisedScore: '0.2007216887087119',
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
__typename: 'RewardScore',
},
rankingScore: {
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
rankingScore: '0.211713765544955625',
stakeScore: '0.2016321576618625',
performanceScore: '1',
votingPower: '2007',
__typename: 'RankingScore',
},
__typename: 'Node',
},
__typename: 'NodeEdge',
},
{
node: {
id: '887d936f797a47032eceb572a13b69b581d3b1fa595a7d021a3e3cf2a5d2acfd',
stakedTotal: '3151161904761904764551',
rewardScore: {
rawValidatorScore: '0.2',
performanceScore: '1',
multisigScore: '1',
validatorScore: '0.2',
normalisedScore: '0.2007216887087119',
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
__typename: 'RewardScore',
},
rankingScore: {
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
rankingScore: '0.211507855409133255',
stakeScore: '0.2014360527706031',
performanceScore: '1',
votingPower: '2007',
__typename: 'RankingScore',
},
__typename: 'Node',
},
__typename: 'NodeEdge',
},
],
__typename: 'NodesConnection',
},
__typename: 'Epoch',
},
};
@@ -89,14 +89,8 @@ context(
}); });
cy.get(proposalStatus).should('have.text', 'Open'); cy.get(proposalStatus).should('have.text', 'Open');
voteForProposal('for'); voteForProposal('for');
cy.get(proposalStatus, proposalTimeout) cy.get(proposalStatus, proposalTimeout).should('have.text', 'Passed');
.should('have.text', 'Passed') cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
.then(() => {
cy.get(proposalStatus, proposalTimeout).should(
'have.text',
'Enacted'
);
});
cy.get(votesTable).within(() => { cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible'); cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible'); cy.contains('Voting has ended.').should('be.visible');
@@ -130,7 +130,7 @@ context(
createRawProposal(); createRawProposal();
}); });
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () { it.skip('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM); goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle()); enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
cy.get('input:invalid') cy.get('input:invalid')
@@ -138,7 +138,7 @@ context(
.should('equal', 'Value must be greater than or equal to 1.'); .should('equal', 'Value must be greater than or equal to 1.');
}); });
it('Creating a proposal - proposal rejected - when closing time later than system default', function () { it.skip('Creating a proposal - proposal rejected - when closing time later than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM); goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody( enterUniqueFreeFormProposalBody(
'100000', '100000',
@@ -8,7 +8,6 @@ import {
} from '../../support/common.functions'; } from '../../support/common.functions';
import { import {
getDownloadedProposalJsonPath, getDownloadedProposalJsonPath,
getProposalFromTitle,
submitUniqueRawProposal, submitUniqueRawProposal,
} from '../../support/governance.functions'; } from '../../support/governance.functions';
import { import {
@@ -56,8 +55,7 @@ const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn'; const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status'; const liquidityVoteStatus = 'liquidity-votes-status';
const tokenVoteStatus = 'token-votes-status'; const tokenVoteStatus = 'token-votes-status';
const proposalJsonToggle = 'proposal-json-toggle'; const proposalTermsSection = 'proposal';
const proposalJsonSection = 'proposal-json';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey'); const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const fUSDCId = const fUSDCId =
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc'; '816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc';
@@ -188,7 +186,7 @@ context(
cy.get(maxVoteDeadline).click(); cy.get(maxVoteDeadline).click();
cy.get(enactmentDeadlineError).should( cy.get(enactmentDeadlineError).should(
'have.text', 'have.text',
'The proposal will fail if enactment is earlier than the voting deadline' 'Proposal will fail if enactment is earlier than the voting deadline'
); );
cy.get(proposalDownloadBtn) cy.get(proposalDownloadBtn)
.should('be.visible') .should('be.visible')
@@ -212,7 +210,6 @@ context(
'Able to submit valid new market proposal', 'Able to submit valid new market proposal',
{ tags: '@smoke' }, { tags: '@smoke' },
function () { function () {
const proposalTitle = 'Test new market proposal';
goToMakeNewProposal(governanceProposalType.NEW_MARKET); goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.get(newProposalTitle).type('Test new market proposal'); cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals'); cy.get(newProposalDescription).type('E2E test for proposals');
@@ -234,23 +231,6 @@ context(
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003 submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
}); });
}); });
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
cy.getByTestId('view-proposal-btn').click()
);
cy.getByTestId('proposal-market-data').within(() => {
cy.getByTestId('proposal-market-data-toggle').click();
cy.contains('Key details').click();
getMarketProposalDetailsFromTable('Name').should(
'have.text',
'Token test market'
);
cy.contains('Settlement asset').click();
// Settlement asset symbol
cy.getByTestId('3_value').should('have.text', 'fBTC');
cy.contains('Oracle').click();
cy.getByTestId('oracle-spec-links').should('have.attr', 'href');
});
} }
); );
@@ -474,8 +454,8 @@ context(
.within(() => { .within(() => {
cy.getByTestId(viewProposalBtn).click(); cy.getByTestId(viewProposalBtn).click();
}); });
cy.getByTestId(proposalJsonToggle).click(); cy.getByTestId('proposal-terms-toggle').click();
cy.getByTestId(proposalJsonSection).within(() => { cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('USDT Coin').should('be.visible'); cy.contains('USDT Coin').should('be.visible');
cy.contains('USDT').should('be.visible'); cy.contains('USDT').should('be.visible');
}); });
@@ -521,11 +501,13 @@ context(
.invoke('text') .invoke('text')
.should('not.be.empty'); .should('not.be.empty');
// 3001-VOTE-030 3001-VOTE-031 // 3001-VOTE-030 3001-VOTE-031
cy.getByTestId(proposalJsonToggle).click(); cy.getByTestId('proposal-terms-toggle').click();
cy.getByTestId(proposalJsonSection).within(() => { cy.getByTestId('proposal-terms').within(() => {
cy.contains(assetId).should('be.visible'); getProposalInformationFromTable('assetId').should('have.text', assetId);
cy.contains('lifetimeLimit').should('be.visible'); getProposalInformationFromTable('lifetimeLimit').should(
cy.contains('10').should('be.visible'); 'have.text',
'10'
);
}); });
}); });
@@ -623,13 +605,5 @@ context(
}); });
}); });
} }
function getMarketProposalDetailsFromTable(heading: string) {
return cy
.getByTestId('key-value-table-row')
.contains(heading)
.parent()
.siblings();
}
} }
); );
@@ -58,6 +58,8 @@ context(
before('visit staking tab and connect vega wallet', function () { before('visit staking tab and connect vega wallet', function () {
cy.visit('/'); cy.visit('/');
ethereumWalletConnect(); ethereumWalletConnect();
// this is a workaround for #2422 which can be removed once issue is resolved
cy.associateTokensToVegaWallet('4');
vegaWalletSetSpecifiedApprovalAmount('1000'); vegaWalletSetSpecifiedApprovalAmount('1000');
}); });
@@ -1,6 +1,5 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { import {
navigation, navigation,
verifyPageHeader, verifyPageHeader,
@@ -10,7 +9,6 @@ import {
clickOnValidatorFromList, clickOnValidatorFromList,
waitForBeginningOfEpoch, waitForBeginningOfEpoch,
} from '../../support/staking.functions'; } from '../../support/staking.functions';
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
const guideLink = '[data-testid="staking-guide-link"]'; const guideLink = '[data-testid="staking-guide-link"]';
const validatorTitle = '[data-testid="validator-node-title"]'; const validatorTitle = '[data-testid="validator-node-title"]';
@@ -32,7 +30,7 @@ const normalisedVotingPowerToolTip =
'[data-testid="normalised-voting-power-tooltip"]'; '[data-testid="normalised-voting-power-tooltip"]';
const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]'; const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]'; const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const multisigPenaltyToolTip = '[data-testid="multisig-error-tooltip"]'; const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]'; const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/; const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
@@ -145,6 +143,9 @@ context('Validators Page - verify elements on page', function () {
cy.get(overstakedPenaltyToolTip) cy.get(overstakedPenaltyToolTip)
.invoke('text') .invoke('text')
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886 .should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
.should('contain', 'Total penalties: 60.00%');
}); });
it('Should be able to see validator pending stake', function () { it('Should be able to see validator pending stake', function () {
@@ -154,22 +155,6 @@ context('Validators Page - verify elements on page', function () {
cy.wrap($pendingStake).should('contain.text', '0.00'); cy.wrap($pendingStake).should('contain.text', '0.00');
}); });
}); });
it('Should be able to see multisig error', function () {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'PreviousEpoch', previousEpochData);
});
waitForBeginningOfEpoch();
cy.getByTestId('total-penalty').first().realHover();
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
cy.getByTestId('total-penalty').eq(1).realHover();
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
});
} }
); );
@@ -287,28 +287,28 @@ context(
name: 'USDC (fake)', name: 'USDC (fake)',
symbol: 'fUSDC', symbol: 'fUSDC',
amount: '1000000', amount: '1000000',
expectedAmount: 10.0, expectedAmount: '10.00',
}, },
{ {
id: '8566db7257222b5b7ef2886394ad28b938b28680a54a169bbc795027b89d6665', id: '8566db7257222b5b7ef2886394ad28b938b28680a54a169bbc795027b89d6665',
name: 'DAI (fake)', name: 'DAI (fake)',
symbol: 'fDAI', symbol: 'fDAI',
amount: '200000', amount: '200000',
expectedAmount: 2.0, expectedAmount: '2.00',
}, },
{ {
id: '73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0', id: '73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
name: 'BTC (fake)', name: 'BTC (fake)',
symbol: 'fBTC', symbol: 'fBTC',
amount: '600000', amount: '600000',
expectedAmount: 6.0, expectedAmount: '6.00',
}, },
{ {
id: 'e02d4c15d790d1d2dffaf2dcd1cf06a1fe656656cf4ed18c8ce99f9e83643567', id: 'e02d4c15d790d1d2dffaf2dcd1cf06a1fe656656cf4ed18c8ce99f9e83643567',
name: 'EURO (fake)', name: 'EURO (fake)',
symbol: 'fEURO', symbol: 'fEURO',
amount: '800000', amount: '800000',
expectedAmount: 8.0, expectedAmount: '8.00',
}, },
]; ];
@@ -319,6 +319,12 @@ context(
cy.reload(); cy.reload();
waitForSpinner(); waitForSpinner();
cy.connectVegaWallet(); cy.connectVegaWallet();
cy.get(walletContainer).within(() => {
cy.getByTestId('currency-title', txTimeout).should(
'have.length.at.least',
5
);
});
}); });
for (const { name, symbol, expectedAmount } of assets) { for (const { name, symbol, expectedAmount } of assets) {
@@ -332,10 +338,10 @@ context(
.contains(name) .contains(name)
.parent() .parent()
.siblings() .siblings()
.then((elementAmount) => { .invoke('text')
const displayedAmount = parseFloat(elementAmount.text()); .should('have.length.at.least', 4)
expect(displayedAmount).be.gte(expectedAmount); .then(parseFloat)
}); .should('be.gte', parseFloat(expectedAmount));
cy.get(vegaWalletCurrencyTitle) cy.get(vegaWalletCurrencyTitle)
.contains(name) .contains(name)
@@ -37,7 +37,7 @@ export function navigateTo(page: navigation) {
}); });
} else { } else {
return cy.get(navigation.section, { timeout: 10000 }).within(() => { return cy.get(navigation.section, { timeout: 10000 }).within(() => {
cy.get(page).eq(0).click({ force: true }); cy.get(page).eq(0).click();
}); });
} }
} }
@@ -786,7 +786,6 @@
"performancePenalty": "Performance penalty", "performancePenalty": "Performance penalty",
"overstaked": "Overstaked", "overstaked": "Overstaked",
"overstakedPenalty": "Overstaked penalty", "overstakedPenalty": "Overstaked penalty",
"multisigPenalty": "Multisig penalty",
"homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject. Network upgrades are proposed and approved by validators.", "homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject. Network upgrades are proposed and approved by validators.",
"homeProposalsButtonText": "Browse, vote, and propose", "homeProposalsButtonText": "Browse, vote, and propose",
"homeValidatorsIntro": "Vega runs on a delegated proof of stake blockchain, where validators earn fees for validating block transactions. Tokenholders can nominate validators by staking tokens to them.", "homeValidatorsIntro": "Vega runs on a delegated proof of stake blockchain, where validators earn fees for validating block transactions. Tokenholders can nominate validators by staking tokens to them.",
@@ -36,7 +36,6 @@ import {
toBigNum, toBigNum,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { VALIDATOR_LOGO_MAP } from './logo-map'; import { VALIDATOR_LOGO_MAP } from './logo-map';
import { getMultisigStatusInfo } from '../../../../lib/get-multisig-status-info';
interface CanonisedConsensusNodeProps { interface CanonisedConsensusNodeProps {
id: string; id: string;
@@ -138,10 +137,6 @@ export const ConsensusValidatorsTable = ({
[totalStake] [totalStake]
); );
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
const allNodesInPreviousEpoch = removePaginationWrapper( const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges previousEpochData?.epoch.validatorsConnection?.edges
); );
@@ -228,8 +223,6 @@ export const ConsensusValidatorsTable = ({
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare [ValidatorFields.USER_STAKE_SHARE]: userStakeShare
? formatNumberPercentage(new BigNumber(userStakeShare), 2) ? formatNumberPercentage(new BigNumber(userStakeShare), 2)
: undefined, : undefined,
[ValidatorFields.MULTISIG_ERROR]:
multisigStatus?.showMultisigStatusError,
}; };
} }
); );
@@ -339,7 +332,6 @@ export const ConsensusValidatorsTable = ({
data, data,
decimals, decimals,
hideTopThird, hideTopThird,
multisigStatus?.showMultisigStatusError,
previousEpochData, previousEpochData,
thirdOfTotalStake, thirdOfTotalStake,
validatorsView, validatorsView,
@@ -39,7 +39,6 @@ export enum ValidatorFields {
STAKED_BY_USER = 'stakedByUser', STAKED_BY_USER = 'stakedByUser',
PENDING_USER_STAKE = 'pendingUserStake', PENDING_USER_STAKE = 'pendingUserStake',
USER_STAKE_SHARE = 'userStakeShare', USER_STAKE_SHARE = 'userStakeShare',
MULTISIG_ERROR = 'multisigError',
} }
export const addUserDataToValidator = ( export const addUserDataToValidator = (
@@ -327,7 +326,6 @@ interface TotalPenaltiesRendererProps {
overstakedAmount: string; overstakedAmount: string;
overstakingPenalty: string; overstakingPenalty: string;
totalPenalties: string; totalPenalties: string;
multisigError?: boolean;
}; };
} }
@@ -346,11 +344,10 @@ export const TotalPenaltiesRenderer = ({
<div data-testid="overstaked-penalty-tooltip"> <div data-testid="overstaked-penalty-tooltip">
{t('overstakedPenalty')}: {data.overstakingPenalty} {t('overstakedPenalty')}: {data.overstakingPenalty}
</div> </div>
{data.multisigError && ( <div data-testid="total-penalty-tooltip">
<div data-testid="multisig-error-tooltip"> {t('totalPenalties')}:{' '}
{t('multisigPenalty')}: 100% <span className="font-bold">{data.totalPenalties}</span>
</div> </div>
)}
</> </>
} }
> >
-1
View File
@@ -14,7 +14,6 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/ NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session" NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72 NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999
# Expose some env vars to cypress environment for market setup # Expose some env vars to cypress environment for market setup
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
@@ -1,15 +0,0 @@
describe('charts', { tags: '@smoke' }, () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.getByTestId('Depth').click();
});
it('can see market depth chart', () => {
// 6006-DEPC-001
cy.getByTestId('tab-depth').should('be.visible');
cy.get('.depth-chart-module_canvas__260De').should('be.visible');
});
});
@@ -182,7 +182,7 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
.contains('Liquidity monitoring parameters') .contains('Liquidity monitoring parameters')
.click(); .click();
validateMarketDataRow(0, 'Triggering Ratio', '0.7'); validateMarketDataRow(0, 'Triggering Ratio', '0');
validateMarketDataRow(1, 'Time Window', '3,600'); validateMarketDataRow(1, 'Time Window', '3,600');
validateMarketDataRow(2, 'Scaling Factor', '10'); validateMarketDataRow(2, 'Scaling Factor', '10');
}); });
@@ -1,4 +1,5 @@
import { checkSorting, aliasGQLQuery } from '@vegaprotocol/cypress'; import { checkSorting } from '@vegaprotocol/cypress';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { marketsDataQuery } from '@vegaprotocol/mock'; import { marketsDataQuery } from '@vegaprotocol/mock';
import { positionsQuery } from '@vegaprotocol/mock'; import { positionsQuery } from '@vegaprotocol/mock';
@@ -14,12 +15,13 @@ const toastContent = 'toast-content';
const tooltipContent = 'tooltip-content'; const tooltipContent = 'tooltip-content';
// #endregion // #endregion
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
describe('positions', { tags: '@smoke', testIsolation: true }, () => { describe('positions', { tags: '@smoke', testIsolation: true }, () => {
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
it('renders positions on trading page', () => { it('renders positions on trading page', () => {
visitAndClickPositions(); visitAndClickPositions();
// 7004-POSI-001 // 7004-POSI-001
@@ -62,14 +64,7 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
); );
}); });
}); });
describe('positions', { tags: '@regression', testIsolation: true }, () => { describe('positions', { tags: '@regression', testIsolation: true }, () => {
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
});
it('rows should be displayed despite errors', () => { it('rows should be displayed despite errors', () => {
const errors = [ const errors = [
{ {
@@ -169,9 +164,8 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
); );
}); });
// let elementWidth: number;
it('Resize column', () => { it('Resize column', () => {
let elementWidth: number;
visitAndClickPositions(); visitAndClickPositions();
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible'); cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
cy.get('.ag-header-container').within(() => { cy.get('.ag-header-container').within(() => {
@@ -186,33 +180,29 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
cy.get(`[col-id="marketName"]`) cy.get(`[col-id="marketName"]`)
.invoke('width') .invoke('width')
.should('be.greaterThan', 250); .should('be.greaterThan', 250);
}); cy.get(`[col-id="marketName"]`)
.invoke('width')
.then((width) => {
elementWidth = width as number;
})
.then(() => {
let localStorageCopy: Record<string, string>;
cy.window().then((win) => {
localStorageCopy = { ...win.localStorage };
});
// This test depends on the previous one cy.reload();
it('Has persisted column widths', () => { cy.window().then((win) => {
const width = 400; Object.keys(localStorageCopy).forEach((key) => {
win.localStorage.setItem(key, localStorageCopy[key]);
});
});
cy.window().then((win) => { // 7004-POSI-012
win.localStorage.setItem( cy.get('[col-id="marketName"]')
'vega_positions_store', .invoke('width')
JSON.stringify({ .should('equal', elementWidth);
state: { });
gridStore: {
columnState: [{ colId: 'marketName', width }],
},
},
})
);
});
visitAndClickPositions();
// 7004-POSI-012
cy.get('.ag-center-cols-container .ag-row')
.first()
.find('[col-id="marketName"]')
.invoke('outerWidth')
.should('equal', width);
}); });
it('Scroll horizontally', () => { it('Scroll horizontally', () => {
@@ -301,7 +291,6 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
cy.getByTestId(dialogContent).should('be.visible'); cy.getByTestId(dialogContent).should('be.visible');
}); });
}); });
function validatePositionsDisplayed(multiKey = false) { function validatePositionsDisplayed(multiKey = false) {
cy.getByTestId('tab-positions').should('be.visible'); cy.getByTestId('tab-positions').should('be.visible');
cy.getByTestId('tab-positions') cy.getByTestId('tab-positions')
@@ -337,7 +326,6 @@ function validatePositionsDisplayed(multiKey = false) {
cy.getByTestId('close-position').should('be.visible').and('have.length', 3); cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
} }
function assertPNLColor( function assertPNLColor(
pnlSelector: string, pnlSelector: string,
positiveClass: string, positiveClass: string,
@@ -359,7 +347,6 @@ function assertPNLColor(
} }
}); });
} }
function visitAndClickPositions() { function visitAndClickPositions() {
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.getByTestId(positions).click(); cy.getByTestId(positions).click();
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_ENV=DEVNET NX_VEGA_ENV=DEVNET
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
+1 -1
View File
@@ -1,7 +1,7 @@
NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.gateway.pokt.network/v1/lb/af6a2d529a11f8158bc8ca2a NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.gateway.pokt.network/v1/lb/af6a2d529a11f8158bc8ca2a
NX_ETHERSCAN_URL=https://etherscan.io NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_ENV=MAINNET NX_VEGA_ENV=MAINNET
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1 NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_ENV=TESTNET NX_VEGA_ENV=TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
+1 -1
View File
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613 NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.toml NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.toml
NX_VEGA_ENV=VALIDATOR_TESTNET NX_VEGA_ENV=VALIDATOR_TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks
@@ -1,5 +1,7 @@
import { import {
matchFilter, matchFilter,
liquidityProvisionsDataProvider,
LiquidityTable,
lpAggregatedDataProvider, lpAggregatedDataProvider,
useCheckLiquidityStatus, useCheckLiquidityStatus,
} from '@vegaprotocol/liquidity'; } from '@vegaprotocol/liquidity';
@@ -22,16 +24,18 @@ import {
ExternalLink, ExternalLink,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { memo, useEffect, useState } from 'react'; import { memo, useEffect, useRef, useState } from 'react';
import { Header, HeaderStat, HeaderTitle } from '../../components/header'; import { Header, HeaderStat, HeaderTitle } from '../../components/header';
import type { AgGridReact } from 'ag-grid-react';
import type { Filter } from '@vegaprotocol/liquidity';
import { Link, useParams } from 'react-router-dom'; import { Link, useParams } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router'; import { Links, Routes } from '../../pages/client-router';
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets'; import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
import { DocsLinks } from '@vegaprotocol/environment'; import { DocsLinks } from '@vegaprotocol/environment';
import { LiquidityContainer } from '../../components/liquidity-container';
const enum LiquidityTabs { const enum LiquidityTabs {
Active = 'active', Active = 'active',
@@ -45,6 +49,65 @@ export const Liquidity = () => {
return <LiquidityViewContainer marketId={marketId} />; return <LiquidityViewContainer marketId={marketId} />;
}; };
const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
variables: { marketId: marketId || '' },
update: () => true,
skip: !marketId,
});
useEffect(() => {
const interval = setInterval(reload, 30000);
return () => clearInterval(interval);
}, [reload]);
};
export const LiquidityContainer = ({
marketId,
filter,
}: {
marketId: string | undefined;
filter?: Filter;
}) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data: market } = useMarket(marketId);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const { data, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
variables: { marketId: marketId || '', filter },
skip: !marketId,
});
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const quantum =
market?.tradableInstrument.instrument.product.settlementAsset.quantum || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
return (
<div className="h-full relative">
<LiquidityTable
ref={gridRef}
rowData={data}
symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces}
quantum={quantum}
stakeToCcyVolume={stakeToCcyVolume}
overlayNoRowsTemplate={error ? error.message : t('No data')}
/>
</div>
);
};
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => { const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
const { data: market } = useMarket(marketId); const { data: market } = useMarket(marketId);
const { data: marketData } = useStaticMarketData(marketId); const { data: marketData } = useStaticMarketData(marketId);
@@ -8,7 +8,7 @@ import {
VegaIconNames, VegaIconNames,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { useCallback, useState, useMemo } from 'react'; import { useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { FixedSizeList } from 'react-window'; import { FixedSizeList } from 'react-window';
import AutoSizer from 'react-virtualized-auto-sizer'; import AutoSizer from 'react-virtualized-auto-sizer';
@@ -184,6 +184,7 @@ const MarketList = ({
if (error) { if (error) {
return <div>{error.message}</div>; return <div>{error.message}</div>;
} }
return ( return (
<AutoSizer> <AutoSizer>
{({ width, height }) => ( {({ width, height }) => (
@@ -203,29 +204,6 @@ const MarketList = ({
); );
}; };
interface ListItemData {
data: MarketMaybeWithDataAndCandles[];
onSelect?: (marketId: string) => void;
currentMarketId?: string;
}
const ListItem = ({
index,
style,
data,
}: {
index: number;
style: CSSProperties;
data: ListItemData;
}) => (
<MarketSelectorItem
market={data.data[index]}
currentMarketId={data.currentMarketId}
style={style}
onSelect={data.onSelect}
/>
);
const List = ({ const List = ({
data, data,
loading, loading,
@@ -234,20 +212,28 @@ const List = ({
onSelect, onSelect,
noItems, noItems,
currentMarketId, currentMarketId,
}: ListItemData & { }: {
data: MarketMaybeWithDataAndCandles[];
loading: boolean; loading: boolean;
width: number; width: number;
height: number; height: number;
noItems: string; noItems: string;
onSelect?: (marketId: string) => void;
currentMarketId?: string;
}) => { }) => {
const itemKey = useCallback( const row = ({ index, style }: { index: number; style: CSSProperties }) => {
(index: number, data: ListItemData) => data.data[index].id, const market = data[index];
[]
); return (
const itemData = useMemo( <MarketSelectorItem
() => ({ data, onSelect, currentMarketId }), market={market}
[data, onSelect, currentMarketId] currentMarketId={currentMarketId}
); style={style}
onSelect={onSelect}
/>
);
};
if (!data || loading) { if (!data || loading) {
return ( return (
<div style={{ width, height }}> <div style={{ width, height }}>
@@ -273,13 +259,11 @@ const List = ({
<FixedSizeList <FixedSizeList
className="virtualized-list" className="virtualized-list"
itemCount={data.length} itemCount={data.length}
itemData={itemData}
itemSize={130} itemSize={130}
itemKey={itemKey}
width={width} width={width}
height={height} height={height}
> >
{ListItem} {row}
</FixedSizeList> </FixedSizeList>
); );
}; };
@@ -19,7 +19,10 @@ import {
VegaIcon, VegaIcon,
VegaIconNames, VegaIconNames,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
import { VegaWalletContainer } from '../../components/vega-wallet-container'; import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { HeaderTitle } from '../../components/header'; import { HeaderTitle } from '../../components/header';
import { import {
@@ -46,6 +49,7 @@ const MarketBottomPanel = memo(
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' }); const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
const { screenSize } = useScreenDimensions(); const { screenSize } = useScreenDimensions();
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler();
return 'xxxl' === screenSize ? ( return 'xxxl' === screenSize ? (
<ResizableGrid <ResizableGrid
@@ -65,6 +69,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component <TradingViews.orders.component
marketId={marketId} marketId={marketId}
filter={Filter.Open} filter={Filter.Open}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketOpenOrders"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -73,6 +81,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component <TradingViews.orders.component
marketId={marketId} marketId={marketId}
filter={Filter.Closed} filter={Filter.Closed}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketClosedOrders"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -81,12 +93,22 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component <TradingViews.orders.component
marketId={marketId} marketId={marketId}
filter={Filter.Rejected} filter={Filter.Rejected}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketRejectOrders"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="orders" name={t('All')}> <Tab id="orders" name={t('All')}>
<VegaWalletContainer> <VegaWalletContainer>
<TradingViews.orders.component marketId={marketId} /> <TradingViews.orders.component
marketId={marketId}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketAllOrders"
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
@@ -94,6 +116,7 @@ const MarketBottomPanel = memo(
<TradingViews.fills.component <TradingViews.fills.component
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
storeKey="marketFills"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -111,6 +134,8 @@ const MarketBottomPanel = memo(
<VegaWalletContainer> <VegaWalletContainer>
<TradingViews.positions.component <TradingViews.positions.component
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
noBottomPlaceholder
storeKey="marketPositions"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -120,6 +145,7 @@ const MarketBottomPanel = memo(
pinnedAsset={pinnedAsset} pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
hideButtons hideButtons
storeKey="marketCollateral"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -132,7 +158,10 @@ const MarketBottomPanel = memo(
<Tabs storageKey="console-trade-grid-bottom"> <Tabs storageKey="console-trade-grid-bottom">
<Tab id="positions" name={t('Positions')}> <Tab id="positions" name={t('Positions')}>
<VegaWalletContainer> <VegaWalletContainer>
<TradingViews.positions.component onMarketClick={onMarketClick} /> <TradingViews.positions.component
onMarketClick={onMarketClick}
storeKey="marketPositions"
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="open-orders" name={t('Open')}> <Tab id="open-orders" name={t('Open')}>
@@ -140,6 +169,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component <TradingViews.orders.component
marketId={marketId} marketId={marketId}
filter={Filter.Open} filter={Filter.Open}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketOpenOrders"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -148,6 +181,10 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component <TradingViews.orders.component
marketId={marketId} marketId={marketId}
filter={Filter.Closed} filter={Filter.Closed}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketClosedOrders"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -156,12 +193,22 @@ const MarketBottomPanel = memo(
<TradingViews.orders.component <TradingViews.orders.component
marketId={marketId} marketId={marketId}
filter={Filter.Rejected} filter={Filter.Rejected}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketRejectedOrders"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="orders" name={t('All')}> <Tab id="orders" name={t('All')}>
<VegaWalletContainer> <VegaWalletContainer>
<TradingViews.orders.component marketId={marketId} /> <TradingViews.orders.component
marketId={marketId}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketAllOrders"
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
@@ -169,6 +216,7 @@ const MarketBottomPanel = memo(
<TradingViews.fills.component <TradingViews.fills.component
marketId={marketId} marketId={marketId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
storeKey="marketFills"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -178,6 +226,7 @@ const MarketBottomPanel = memo(
pinnedAsset={pinnedAsset} pinnedAsset={pinnedAsset}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
hideButtons hideButtons
storeKey="marketCollateral"
/> />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
@@ -1,19 +1,18 @@
import type { ComponentProps } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { DealTicketContainer } from '@vegaprotocol/deal-ticket'; import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
import { MarketInfoAccordionContainer } from '@vegaprotocol/markets'; import { MarketInfoAccordionContainer } from '@vegaprotocol/markets';
import { OrderbookContainer } from '@vegaprotocol/market-depth';
import { OrderListContainer, Filter } from '@vegaprotocol/orders';
import type { OrderListContainerProps } from '@vegaprotocol/orders';
import { FillsContainer } from '@vegaprotocol/fills';
import { PositionsContainer } from '@vegaprotocol/positions';
import { TradesContainer } from '@vegaprotocol/trades'; import { TradesContainer } from '@vegaprotocol/trades';
import type { ComponentProps } from 'react';
import { DepthChartContainer } from '@vegaprotocol/market-depth'; import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart'; import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { OrderbookContainer } from '@vegaprotocol/market-depth'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { Filter } from '@vegaprotocol/orders';
import { NO_MARKET } from './constants';
import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container';
import { AccountsContainer } from '../../components/accounts-container'; import { AccountsContainer } from '../../components/accounts-container';
import { LiquidityContainer } from '../../components/liquidity-container'; import { NO_MARKET } from './constants';
import type { OrderContainerProps } from '../../components/orders-container'; import { LiquidityContainer } from '../liquidity/liquidity';
import { OrdersContainer } from '../../components/orders-container';
type MarketDependantView = type MarketDependantView =
| typeof CandlesChartContainer | typeof CandlesChartContainer
@@ -66,25 +65,25 @@ export const TradingViews = {
positions: { label: 'Positions', component: PositionsContainer }, positions: { label: 'Positions', component: PositionsContainer },
activeOrders: { activeOrders: {
label: 'Active', label: 'Active',
component: (props: OrderContainerProps) => ( component: (props: OrderListContainerProps) => (
<OrdersContainer {...props} filter={Filter.Open} /> <OrderListContainer {...props} filter={Filter.Open} />
), ),
}, },
closedOrders: { closedOrders: {
label: 'Closed', label: 'Closed',
component: (props: OrderContainerProps) => ( component: (props: OrderListContainerProps) => (
<OrdersContainer {...props} filter={Filter.Closed} /> <OrderListContainer {...props} filter={Filter.Closed} />
), ),
}, },
rejectedOrders: { rejectedOrders: {
label: 'Rejected', label: 'Rejected',
component: (props: OrderContainerProps) => ( component: (props: OrderListContainerProps) => (
<OrdersContainer {...props} filter={Filter.Rejected} /> <OrderListContainer {...props} filter={Filter.Rejected} />
), ),
}, },
orders: { orders: {
label: 'All', label: 'All',
component: OrdersContainer, component: OrderListContainer,
}, },
collateral: { label: 'Collateral', component: AccountsContainer }, collateral: { label: 'Collateral', component: AccountsContainer },
fills: { label: 'Fills', component: FillsContainer }, fills: { label: 'Fills', component: FillsContainer },
@@ -313,6 +313,7 @@ const ClosedMarketsDataGrid = ({
minWidth: 100, minWidth: 100,
}} }}
overlayNoRowsTemplate={error ? error.message : t('No markets')} overlayNoRowsTemplate={error ? error.message : t('No markets')}
storeKey="closedMarkets"
/> />
); );
}; };
@@ -2,6 +2,7 @@ import { Button } from '@vegaprotocol/ui-toolkit';
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits'; import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
import { depositsProvider } from '@vegaprotocol/deposits'; import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react'; import { useRef } from 'react';
@@ -16,13 +17,17 @@ export const DepositsContainer = () => {
skip: !pubKey, skip: !pubKey,
}); });
const openDepositDialog = useDepositDialog((state) => state.open); const openDepositDialog = useDepositDialog((state) => state.open);
const bottomPlaceholderProps = useBottomPlaceholder({ gridRef });
return ( return (
<div className="h-full"> <div className="h-full">
<DepositsTable <div className="h-full relative">
rowData={data} <DepositsTable
ref={gridRef} rowData={data}
overlayNoRowsTemplate={error ? error.message : t('No deposits')} ref={gridRef}
/> {...bottomPlaceholderProps}
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
/>
</div>
{!isReadOnly && ( {!isReadOnly && (
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded"> <div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
<Button <Button
@@ -1,39 +1,29 @@
import { useEffect } from 'react';
import type { ReactNode } from 'react';
import { LayoutPriority } from 'allotment';
import { titlefy } from '@vegaprotocol/utils'; import { titlefy } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws'; import { PositionsContainer } from '@vegaprotocol/positions';
import { usePaneLayout } from '@vegaprotocol/react-helpers'; import { OrderListContainer } from '@vegaprotocol/orders';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit'; import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import { usePageTitleStore } from '../../stores';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { AccountsContainer } from '../../components/accounts-container';
import { DepositsContainer } from './deposits-container';
import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container';
import { WithdrawalsContainer } from './withdrawals-container'; import { WithdrawalsContainer } from './withdrawals-container';
import { OrdersContainer } from '../../components/orders-container'; import { FillsContainer } from '@vegaprotocol/fills';
import type { ReactNode } from 'react';
import { useEffect } from 'react';
import { usePaneLayout } from '@vegaprotocol/react-helpers';
import { VegaWalletContainer } from '../../components/vega-wallet-container'; import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { LedgerContainer } from '../../components/ledger-container'; import { DepositsContainer } from './deposits-container';
import { LayoutPriority } from 'allotment';
import { usePageTitleStore } from '../../stores';
import { LedgerContainer } from '@vegaprotocol/ledger';
import { AccountsContainer } from '../../components/accounts-container';
import { AccountHistoryContainer } from './account-history-container'; import { AccountHistoryContainer } from './account-history-container';
import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
import { import {
ResizableGrid, ResizableGrid,
ResizableGridPanel, ResizableGridPanel,
} from '../../components/resizable-grid'; } from '../../components/resizable-grid';
const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals();
if (!ready || ready.length === 0) {
return null;
}
return (
<span className="bg-vega-blue-450 text-white text-[10px] rounded p-[3px] pb-[2px] leading-none">
{ready.length}
</span>
);
};
export const Portfolio = () => { export const Portfolio = () => {
const { updateTitle } = usePageTitleStore((store) => ({ const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle, updateTitle: store.updateTitle,
@@ -44,6 +34,7 @@ export const Portfolio = () => {
}, [updateTitle]); }, [updateTitle]);
const onMarketClick = useMarketClickHandler(true); const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler();
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' }); const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'h-full max-h-full flex flex-col'; const wrapperClasses = 'h-full max-h-full flex flex-col';
return ( return (
@@ -59,17 +50,29 @@ export const Portfolio = () => {
</Tab> </Tab>
<Tab id="positions" name={t('Positions')}> <Tab id="positions" name={t('Positions')}>
<VegaWalletContainer> <VegaWalletContainer>
<PositionsContainer onMarketClick={onMarketClick} allKeys /> <PositionsContainer
onMarketClick={onMarketClick}
noBottomPlaceholder
storeKey="portfolioPositions"
allKeys
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="orders" name={t('Orders')}> <Tab id="orders" name={t('Orders')}>
<VegaWalletContainer> <VegaWalletContainer>
<OrdersContainer /> <OrderListContainer
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
storeKey="portfolioOrders"
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
<VegaWalletContainer> <VegaWalletContainer>
<FillsContainer onMarketClick={onMarketClick} /> <FillsContainer
onMarketClick={onMarketClick}
storeKey="portfolioFills"
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}> <Tab id="ledger-entries" name={t('Ledger entries')}>
@@ -89,7 +92,10 @@ export const Portfolio = () => {
<Tabs storageKey="console-portfolio-bottom"> <Tabs storageKey="console-portfolio-bottom">
<Tab id="collateral" name={t('Collateral')}> <Tab id="collateral" name={t('Collateral')}>
<VegaWalletContainer> <VegaWalletContainer>
<AccountsContainer /> <AccountsContainer
storeKey="portfolioCollateral"
onMarketClick={onMarketClick}
/>
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab id="deposits" name={t('Deposits')}> <Tab id="deposits" name={t('Deposits')}>
@@ -97,11 +103,7 @@ export const Portfolio = () => {
<DepositsContainer /> <DepositsContainer />
</VegaWalletContainer> </VegaWalletContainer>
</Tab> </Tab>
<Tab <Tab id="withdrawals" name={t('Withdrawals')}>
id="withdrawals"
name={t('Withdrawals')}
indicator={<WithdrawalsIndicator />}
>
<WithdrawalsContainer /> <WithdrawalsContainer />
</Tab> </Tab>
</Tabs> </Tabs>
@@ -3,7 +3,6 @@ import {
withdrawalProvider, withdrawalProvider,
useWithdrawalDialog, useWithdrawalDialog,
WithdrawalsTable, WithdrawalsTable,
useIncompleteWithdrawals,
} from '@vegaprotocol/withdraws'; } from '@vegaprotocol/withdraws';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
@@ -18,7 +17,6 @@ export const WithdrawalsContainer = () => {
skip: !pubKey, skip: !pubKey,
}); });
const openWithdrawDialog = useWithdrawalDialog((state) => state.open); const openWithdrawDialog = useWithdrawalDialog((state) => state.open);
const { ready, delayed } = useIncompleteWithdrawals();
return ( return (
<VegaWalletContainer> <VegaWalletContainer>
@@ -27,8 +25,6 @@ export const WithdrawalsContainer = () => {
data-testid="withdrawals-history" data-testid="withdrawals-history"
rowData={data} rowData={data}
overlayNoRowsTemplate={error ? error.message : t('No withdrawals')} overlayNoRowsTemplate={error ? error.message : t('No withdrawals')}
ready={ready}
delayed={delayed}
/> />
</div> </div>
{!isReadOnly && ( {!isReadOnly && (
@@ -8,19 +8,16 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
import type { PinnedAsset } from '@vegaprotocol/accounts'; import type { PinnedAsset } from '@vegaprotocol/accounts';
import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts'; import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts';
import { useDepositDialog } from '@vegaprotocol/deposits'; import { useDepositDialog } from '@vegaprotocol/deposits';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
export const AccountsContainer = ({ export const AccountsContainer = ({
pinnedAsset, pinnedAsset,
hideButtons, hideButtons,
storeKey,
onMarketClick, onMarketClick,
}: { }: {
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
hideButtons?: boolean; hideButtons?: boolean;
storeKey?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}) => { }) => {
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
@@ -29,12 +26,6 @@ export const AccountsContainer = ({
const openDepositDialog = useDepositDialog((store) => store.open); const openDepositDialog = useDepositDialog((store) => store.open);
const openTransferDialog = useTransferDialog((store) => store.open); const openTransferDialog = useTransferDialog((store) => store.open);
const gridStore = useAccountStore((store) => store.gridStore);
const updateGridStore = useAccountStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
const onClickAsset = useCallback( const onClickAsset = useCallback(
(assetId?: string) => { (assetId?: string) => {
assetId && openAssetDetailsDialog(assetId); assetId && openAssetDetailsDialog(assetId);
@@ -60,7 +51,7 @@ export const AccountsContainer = ({
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
pinnedAsset={pinnedAsset} pinnedAsset={pinnedAsset}
gridProps={gridStoreCallbacks} storeKey={storeKey}
/> />
{!isReadOnly && !hideButtons && ( {!isReadOnly && !hideButtons && (
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded"> <div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
@@ -84,9 +75,3 @@ export const AccountsContainer = ({
</div> </div>
); );
}; };
const useAccountStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_accounts_store',
})
);
@@ -1,16 +1,13 @@
import type { DefaultWeb3ProviderContextShape } from '@vegaprotocol/web3';
import { import {
useEthereumConfig, useEthereumConfig,
createConnectors, createConnectors,
Web3Provider as Web3ProviderInternal, Web3Provider as Web3ProviderInternal,
useWeb3ConnectStore, useWeb3ConnectStore,
createDefaultProvider,
} from '@vegaprotocol/web3'; } from '@vegaprotocol/web3';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useState } from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
export const Web3Provider = ({ children }: { children: ReactNode }) => { export const Web3Provider = ({ children }: { children: ReactNode }) => {
@@ -20,13 +17,10 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
const connectors = useWeb3ConnectStore((store) => store.connectors); const connectors = useWeb3ConnectStore((store) => store.connectors);
const initializeConnectors = useWeb3ConnectStore((store) => store.initialize); const initializeConnectors = useWeb3ConnectStore((store) => store.initialize);
const [defaultProvider, setDefaultProvider] = useState<
DefaultWeb3ProviderContextShape['provider'] | undefined
>(undefined);
useEffect(() => { useEffect(() => {
if (config?.chain_id) { if (config?.chain_id) {
initializeConnectors( return initializeConnectors(
createConnectors( createConnectors(
ETHEREUM_PROVIDER_URL, ETHEREUM_PROVIDER_URL,
Number(config?.chain_id), Number(config?.chain_id),
@@ -35,11 +29,6 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
), ),
Number(config.chain_id) Number(config.chain_id)
); );
const defaultProvider = createDefaultProvider(
ETHEREUM_PROVIDER_URL,
Number(config?.chain_id)
);
setDefaultProvider(defaultProvider);
} }
}, [ }, [
config?.chain_id, config?.chain_id,
@@ -60,10 +49,7 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
}} }}
noDataMessage={t('Could not fetch Ethereum configuration')} noDataMessage={t('Could not fetch Ethereum configuration')}
> >
<Web3ProviderInternal <Web3ProviderInternal connectors={connectors}>
connectors={connectors}
defaultProvider={defaultProvider}
>
<>{children}</> <>{children}</>
</Web3ProviderInternal> </Web3ProviderInternal>
</AsyncRenderer> </AsyncRenderer>
@@ -1,49 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { FillsManager } from '@vegaprotocol/fills';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
export const FillsContainer = ({
marketId,
onMarketClick,
}: {
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}) => {
const { pubKey } = useVegaWallet();
const gridStore = useFillsStore((store) => store.gridStore);
const updateGridStore = useFillsStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
if (!pubKey) {
return (
<Splash>
<p>{t('Please connect Vega wallet')}</p>
</Splash>
);
}
return (
<FillsManager
partyId={pubKey}
marketId={marketId}
onMarketClick={onMarketClick}
gridProps={gridStoreCallbacks}
/>
);
};
const useFillsStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_fills_store',
})
);
@@ -1 +0,0 @@
export * from './fills-container';
@@ -1 +0,0 @@
export * from './ledger-container';
@@ -1,36 +0,0 @@
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { LedgerManager } from '@vegaprotocol/ledger';
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 { create } from 'zustand';
import { persist } from 'zustand/middleware';
export const LedgerContainer = () => {
const { pubKey } = useVegaWallet();
const gridStore = useLedgerStore((store) => store.gridStore);
const updateGridStore = useLedgerStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
if (!pubKey) {
return (
<Splash>
<p>{t('Please connect Vega wallet')}</p>
</Splash>
);
}
return <LedgerManager partyId={pubKey} gridProps={gridStoreCallbacks} />;
};
const useLedgerStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_ledger_store',
})
);
@@ -1 +0,0 @@
export * from './liquidity-container';
@@ -1,93 +0,0 @@
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import {
lpAggregatedDataProvider,
type Filter,
LiquidityTable,
liquidityProvisionsDataProvider,
} from '@vegaprotocol/liquidity';
import { useMarket } from '@vegaprotocol/markets';
import {
NetworkParams,
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import type { AgGridReact } from 'ag-grid-react';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { useEffect, useRef } from 'react';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
export const LiquidityContainer = ({
marketId,
filter,
}: {
marketId: string | undefined;
filter?: Filter;
}) => {
const gridRef = useRef<AgGridReact | null>(null);
const gridStore = useLiquidityStore((store) => store.gridStore);
const updateGridStore = useLiquidityStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
const { data: market } = useMarket(marketId);
// To be removed when liquidityProvision subscriptions are working
useReloadLiquidityData(marketId);
const { data, error } = useDataProvider({
dataProvider: lpAggregatedDataProvider,
variables: { marketId: marketId || '', filter },
skip: !marketId,
});
const assetDecimalPlaces =
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
const quantum =
market?.tradableInstrument.instrument.product.settlementAsset.quantum || 0;
const symbol =
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
const { params } = useNetworkParams([
NetworkParams.market_liquidity_stakeToCcyVolume,
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
return (
<div className="h-full relative">
<LiquidityTable
ref={gridRef}
rowData={data}
symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces}
quantum={quantum}
stakeToCcyVolume={stakeToCcyVolume}
overlayNoRowsTemplate={error ? error.message : t('No data')}
{...gridStoreCallbacks}
/>
</div>
);
};
const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
variables: { marketId: marketId || '' },
update: () => true,
skip: !marketId,
});
useEffect(() => {
const interval = setInterval(reload, 30000);
return () => clearInterval(interval);
}, [reload]);
};
const useLiquidityStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_ledger_store',
})
);
@@ -1 +0,0 @@
export * from './orders-container';
@@ -1,106 +0,0 @@
import { act, renderHook } from '@testing-library/react';
import {
FilterStatusValue,
STORAGE_KEY,
useOrderListGridState,
} from './orders-container';
import { Filter } from '@vegaprotocol/orders';
import { OrderType } from '@vegaprotocol/types';
describe('useOrderListGridState', () => {
afterAll(() => {
localStorage.clear();
});
const setup = (filter: Filter | undefined) => {
return renderHook(() => useOrderListGridState(filter));
};
it.each(Object.values(Filter))(
'providers correct AgGrid filter for %s',
(filter) => {
const { result } = setup(filter);
expect(typeof result.current.updateGridState).toBe('function');
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: {
status: {
value: FilterStatusValue[filter],
},
},
});
}
);
it('provides correct AgGrid filter for all', () => {
const { result } = setup(undefined);
expect(typeof result.current.updateGridState).toBe('function');
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: undefined,
});
});
it.each(Object.values(Filter))(
'sets and stores column state and filters for %s',
(filter) => {
const filterModel = {
type: {
value: [OrderType.TYPE_LIMIT],
},
};
const { result } = setup(filter);
act(() => {
result.current.updateGridState(filter, {
filterModel,
});
});
expect(result.current.gridState).toEqual({
columnState: undefined,
filterModel: {
...filterModel,
status: {
value: FilterStatusValue[filter],
},
},
});
const columnState = [{ colId: 'status', width: 200 }];
act(() => {
result.current.updateGridState(filter, {
columnState,
});
});
expect(result.current.gridState).toEqual({
columnState,
filterModel: {
...filterModel,
status: {
value: FilterStatusValue[filter],
},
},
});
const storeKeyMap = {
[Filter.Open]: 'open',
[Filter.Rejected]: 'rejected',
[Filter.Closed]: 'closed',
};
expect(JSON.parse(localStorage.getItem(STORAGE_KEY) || '')).toMatchObject(
{
state: {
[storeKeyMap[filter]]: {
columnState,
filterModel, // no need to check that status is set, hook will return status
},
},
}
);
}
);
});
@@ -1,166 +0,0 @@
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { Filter } from '@vegaprotocol/orders';
import { OrderListManager } from '@vegaprotocol/orders';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
useMarketClickHandler,
useMarketLiquidityClickHandler,
} from '../../lib/hooks/use-market-click-handler';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { DataGridStore } from '../../stores/datagrid-store-slice';
import { OrderStatus } from '@vegaprotocol/types';
export const FilterStatusValue = {
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
[Filter.Closed]: [
OrderStatus.STATUS_CANCELLED,
OrderStatus.STATUS_EXPIRED,
OrderStatus.STATUS_FILLED,
OrderStatus.STATUS_PARTIALLY_FILLED,
OrderStatus.STATUS_STOPPED,
],
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
};
export interface OrderContainerProps {
marketId?: string;
filter?: Filter;
}
export const OrdersContainer = ({ marketId, filter }: OrderContainerProps) => {
const { pubKey, isReadOnly } = useVegaWallet();
const onMarketClick = useMarketClickHandler(true);
const onOrderTypeClick = useMarketLiquidityClickHandler();
const { gridState, updateGridState } = useOrderListGridState(filter);
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => {
updateGridState(filter, newState);
});
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
}
return (
<OrderListManager
partyId={pubKey}
marketId={marketId}
filter={filter}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
isReadOnly={isReadOnly}
gridProps={gridStoreCallbacks}
/>
);
};
export const STORAGE_KEY = 'vega_order_list_store';
const useOrderListStore = create<{
open: DataGridStore;
closed: DataGridStore;
rejected: DataGridStore;
all: DataGridStore;
update: (filter: Filter | undefined, gridStore: DataGridStore) => void;
}>()(
persist(
(set) => ({
open: {},
closed: {},
rejected: {},
all: {},
update: (filter, newStore) => {
switch (filter) {
case Filter.Open: {
set((curr) => ({
open: {
...curr.open,
...newStore,
},
}));
return;
}
case Filter.Closed: {
set((curr) => ({
closed: {
...curr.closed,
...newStore,
},
}));
return;
}
case Filter.Rejected: {
set((curr) => ({
rejected: {
...curr.rejected,
...newStore,
},
}));
return;
}
case undefined: {
set((curr) => ({
all: {
...curr.all,
...newStore,
},
}));
return;
}
}
},
}),
{
name: STORAGE_KEY,
}
)
);
export const useOrderListGridState = (filter: Filter | undefined) => {
const updateGridState = useOrderListStore((store) => store.update);
const gridState = useOrderListStore((store) => {
// Return the column/filter state for the given filter but ensuring that
// each filter controlled by the tab is always applied
switch (filter) {
case Filter.Open: {
return {
columnState: store.open.columnState,
filterModel: {
...store.open.filterModel,
status: {
value: FilterStatusValue[Filter.Open],
},
},
};
}
case Filter.Closed: {
return {
columnState: store.closed.columnState,
filterModel: {
...store.closed.filterModel,
status: {
value: FilterStatusValue[Filter.Closed],
},
},
};
}
case Filter.Rejected: {
return {
columnState: store.rejected.columnState,
filterModel: {
...store.rejected.filterModel,
status: {
value: FilterStatusValue[Filter.Rejected],
},
},
};
}
default: {
return store.all;
}
}
});
return { gridState, updateGridState };
};
@@ -1 +0,0 @@
export * from './positions-container';
@@ -1,25 +1,17 @@
import { render, screen } from '@testing-library/react'; import { render, screen, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TelemetryApproval } from './telemetry-approval'; import { TelemetryApproval } from './telemetry-approval';
jest.mock('@vegaprotocol/logger', () => ({
SentryInit: () => undefined,
SentryClose: () => undefined,
}));
jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ VEGA_ENV: 'test', SENTRY_DSN: 'sentry-dsn' }),
}));
describe('TelemetryApproval', () => { describe('TelemetryApproval', () => {
it('click on checkbox should be properly handled', async () => { it('click on checkbox should be properly handled', () => {
const helpText = 'My help text'; const helpText = 'My help text';
render(<TelemetryApproval helpText={helpText} />); render(<TelemetryApproval helpText={helpText} />);
expect(screen.getByRole('checkbox')).toHaveAttribute( expect(screen.getByRole('checkbox')).toHaveAttribute(
'data-state', 'data-state',
'unchecked' 'unchecked'
); );
await userEvent.click(screen.getByRole('checkbox')); act(() => {
screen.getByRole('checkbox').click();
});
expect(screen.getByRole('checkbox')).toHaveAttribute( expect(screen.getByRole('checkbox')).toHaveAttribute(
'data-state', 'data-state',
'checked' 'checked'
@@ -11,7 +11,6 @@ import { WelcomeNoticeDialog } from './welcome-notice-dialog';
import { useGlobalStore } from '../../stores'; import { useGlobalStore } from '../../stores';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import { Networks } from '@vegaprotocol/environment'; import { Networks } from '@vegaprotocol/environment';
import { isTestEnv } from '@vegaprotocol/utils';
export const WelcomeDialog = () => { export const WelcomeDialog = () => {
const { VEGA_ENV } = useEnvironment(); const { VEGA_ENV } = useEnvironment();
@@ -32,7 +31,9 @@ export const WelcomeDialog = () => {
); );
const isRiskDialogNeeded = const isRiskDialogNeeded =
riskAccepted !== 'true' && VEGA_ENV !== Networks.MAINNET && !isTestEnv(); riskAccepted !== 'true' &&
VEGA_ENV !== Networks.MAINNET &&
!('Cypress' in window);
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog; const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
+20
View File
@@ -0,0 +1,20 @@
const windowOrDefault = (key: string, defaultValue?: string) => {
if (typeof window !== 'undefined') {
if (window._env_ && window._env_[key]) {
return window._env_[key];
}
}
return defaultValue || '';
};
/**
* Need to have default value as next in-lines environment variables. Cannot figure out dynamic keys.
* So must provide the default with the key so that next can figure it out.
*/
export const ENV = {
envName: windowOrDefault('NX_VEGA_ENV', process.env['NX_VEGA_ENV']),
dsn: windowOrDefault(
'NX_TRADING_SENTRY_DSN',
process.env['NX_TRADING_SENTRY_DSN']
),
};
+1
View File
@@ -0,0 +1 @@
export * from './env';
@@ -12,26 +12,22 @@ jest.mock('@vegaprotocol/react-helpers', () => ({
.fn() .fn()
.mockImplementation(() => [false, mockSetValue, mockRemoveValue]), .mockImplementation(() => [false, mockSetValue, mockRemoveValue]),
})); }));
jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: () => ({ VEGA_ENV: 'test', SENTRY_DSN: 'sentry-dsn' }),
}));
describe('useTelemetryApproval', () => { describe('useTelemetryApproval', () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
}); });
it('hook should return proper array', () => { it('hook should return proper array', () => {
const { result } = renderHook(() => useTelemetryApproval()); const res = renderHook(() => useTelemetryApproval());
expect(result.current[0]).toEqual(false); expect(res.result.current[0]).toEqual(false);
expect(result.current[1]).toEqual(expect.any(Function)); expect(res.result.current[1]).toEqual(expect.any(Function));
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY); expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY);
}); });
it('hook should init stuff properly', async () => { it('hook should init stuff properly', async () => {
const { result } = renderHook(() => useTelemetryApproval()); const res = renderHook(() => useTelemetryApproval());
await act(() => { await act(() => {
result.current[1](true); res.result.current[1](true);
}); });
await waitFor(() => { await waitFor(() => {
expect(SentryInit).toHaveBeenCalled(); expect(SentryInit).toHaveBeenCalled();
@@ -40,9 +36,9 @@ describe('useTelemetryApproval', () => {
}); });
it('hook should close stuff properly', async () => { it('hook should close stuff properly', async () => {
const { result } = renderHook(() => useTelemetryApproval()); const res = renderHook(() => useTelemetryApproval());
await act(() => { await act(() => {
result.current[1](false); res.result.current[1](false);
}); });
await waitFor(() => { await waitFor(() => {
expect(SentryClose).toHaveBeenCalled(); expect(SentryClose).toHaveBeenCalled();
@@ -1,25 +1,24 @@
import { useLocalStorage } from '@vegaprotocol/react-helpers'; import { useLocalStorage } from '@vegaprotocol/react-helpers';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { SentryInit, SentryClose } from '@vegaprotocol/logger'; import { SentryInit, SentryClose } from '@vegaprotocol/logger';
import { useEnvironment } from '@vegaprotocol/environment'; import { ENV } from '../config';
export const STORAGE_KEY = 'vega_telemetry_approval'; export const STORAGE_KEY = 'vega_telemetry_approval';
export const useTelemetryApproval = (): [ export const useTelemetryApproval = (): [
value: boolean, value: boolean,
setValue: (value: boolean) => void setValue: (value: boolean) => void
] => { ] => {
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
const [value, setValue, removeValue] = useLocalStorage(STORAGE_KEY); const [value, setValue, removeValue] = useLocalStorage(STORAGE_KEY);
const setApprove = useCallback( const setApprove = useCallback(
(value: boolean) => { (value: boolean) => {
if (value && SENTRY_DSN) { if (value) {
SentryInit(SENTRY_DSN, VEGA_ENV); SentryInit(ENV.dsn, ENV.envName);
return setValue('1'); return setValue('1');
} }
SentryClose(); SentryClose();
removeValue(); removeValue();
}, },
[setValue, removeValue, SENTRY_DSN, VEGA_ENV] [setValue, removeValue]
); );
return [Boolean(value), setApprove]; return [Boolean(value), setApprove];
}; };
+2 -2
View File
@@ -34,6 +34,7 @@ import { ViewingBanner } from '../components/viewing-banner';
import { AnnouncementBanner, UpgradeBanner } from '../components/banner'; import { AnnouncementBanner, UpgradeBanner } from '../components/banner';
import { AppLoader, DynamicLoader } from '../components/app-loader'; import { AppLoader, DynamicLoader } from '../components/app-loader';
import { Navbar } from '../components/navbar'; import { Navbar } from '../components/navbar';
import { ENV } from '../lib/config';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { activeOrdersProvider } from '@vegaprotocol/orders'; import { activeOrdersProvider } from '@vegaprotocol/orders';
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval'; import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
@@ -154,11 +155,10 @@ const PartyData = () => {
}; };
const MaybeConnectEagerly = () => { const MaybeConnectEagerly = () => {
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
useVegaEagerConnect(Connectors); useVegaEagerConnect(Connectors);
const [isTelemetryApproved] = useTelemetryApproval(); const [isTelemetryApproved] = useTelemetryApproval();
useEthereumEagerConnect( useEthereumEagerConnect(
isTelemetryApproved ? { dsn: SENTRY_DSN, env: VEGA_ENV } : {} isTelemetryApproved ? { dsn: ENV.dsn, env: ENV.envName } : {}
); );
const { pubKey, connect } = useVegaWallet(); const { pubKey, connect } = useVegaWallet();
+4
View File
@@ -20,6 +20,10 @@ export default function Document() {
href="https://static.vega.xyz/favicon.ico" href="https://static.vega.xyz/favicon.ico"
/> />
<script src="/theme-setter.js" type="text/javascript" async /> <script src="/theme-setter.js" type="text/javascript" async />
{['1', 'true'].includes(process.env['NX_USE_ENV_OVERRIDES'] || '') ? (
/* eslint-disable-next-line @next/next/no-sync-scripts */
<script src="/assets/env-config.js" type="text/javascript" />
) : null}
</Head> </Head>
<body className="font-alpha dark:bg-black dark:text-white"> <body className="font-alpha dark:bg-black dark:text-white">
<Main /> <Main />
-5
View File
@@ -3,17 +3,12 @@ import { useUpdateNetworkParametersToasts } from '@vegaprotocol/proposals';
import { useVegaTransactionToasts } from '@vegaprotocol/web3'; import { useVegaTransactionToasts } from '@vegaprotocol/web3';
import { useEthereumTransactionToasts } from '@vegaprotocol/web3'; import { useEthereumTransactionToasts } from '@vegaprotocol/web3';
import { useEthereumWithdrawApprovalsToasts } from '@vegaprotocol/web3'; import { useEthereumWithdrawApprovalsToasts } from '@vegaprotocol/web3';
import { Routes } from './client-router';
import { useReadyToWithdrawalToasts } from '@vegaprotocol/withdraws';
export const ToastsManager = () => { export const ToastsManager = () => {
useUpdateNetworkParametersToasts(); useUpdateNetworkParametersToasts();
useVegaTransactionToasts(); useVegaTransactionToasts();
useEthereumTransactionToasts(); useEthereumTransactionToasts();
useEthereumWithdrawApprovalsToasts(); useEthereumWithdrawApprovalsToasts();
useReadyToWithdrawalToasts({
withdrawalsLink: `${Routes.PORTFOLIO}`,
});
const toasts = useToasts((store) => store.toasts); const toasts = useToasts((store) => store.toasts);
return <ToastsContainer order="desc" toasts={toasts} />; return <ToastsContainer order="desc" toasts={toasts} />;
@@ -1,25 +0,0 @@
import type { ColumnState } from 'ag-grid-community';
import type { StateCreator } from 'zustand';
export type DataGridStore = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
filterModel?: { [key: string]: any };
columnState?: ColumnState[];
};
export type DataGridSlice = {
gridStore: DataGridStore;
updateGridStore: (gridStore: DataGridStore) => void;
};
export const createDataGridSlice: StateCreator<DataGridSlice> = (set) => ({
gridStore: {},
updateGridStore: (newStore) => {
set((curr) => ({
gridStore: {
...curr.gridStore,
...newStore,
},
}));
},
});
@@ -7,7 +7,6 @@ import {
} from '@testing-library/react'; } from '@testing-library/react';
import * as helpers from '@vegaprotocol/data-provider'; import * as helpers from '@vegaprotocol/data-provider';
import { AccountManager } from './accounts-manager'; import { AccountManager } from './accounts-manager';
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
const mockedUseDataProvider = jest.fn(); const mockedUseDataProvider = jest.fn();
jest.mock('@vegaprotocol/data-provider', () => ({ jest.mock('@vegaprotocol/data-provider', () => ({
@@ -15,13 +14,6 @@ jest.mock('@vegaprotocol/data-provider', () => ({
useDataProvider: jest.fn(() => mockedUseDataProvider()), useDataProvider: jest.fn(() => mockedUseDataProvider()),
})); }));
const gridProps = {
onGridReady: jest.fn(),
onColumnResized: jest.fn(),
onFilterChanged: jest.fn(),
onSortChanged: jest.fn(),
} as unknown as ReturnType<typeof useDataGridEvents>;
describe('AccountManager', () => { describe('AccountManager', () => {
describe('when rerender', () => { describe('when rerender', () => {
beforeEach(() => { beforeEach(() => {
@@ -51,7 +43,6 @@ describe('AccountManager', () => {
partyId="partyOne" partyId="partyOne"
onClickAsset={jest.fn} onClickAsset={jest.fn}
isReadOnly={false} isReadOnly={false}
gridProps={gridProps}
/> />
); );
expect( expect(
@@ -64,7 +55,6 @@ describe('AccountManager', () => {
partyId="partyTwo" partyId="partyTwo"
onClickAsset={jest.fn} onClickAsset={jest.fn}
isReadOnly={false} isReadOnly={false}
gridProps={gridProps}
/> />
); );
}); });
@@ -82,7 +72,6 @@ describe('AccountManager', () => {
partyId="partyOne" partyId="partyOne"
onClickAsset={jest.fn} onClickAsset={jest.fn}
isReadOnly={false} isReadOnly={false}
gridProps={gridProps}
/> />
); );
rerenderer = rerender; rerenderer = rerender;
@@ -96,7 +85,6 @@ describe('AccountManager', () => {
partyId="partyOne" partyId="partyOne"
onClickAsset={jest.fn} onClickAsset={jest.fn}
isReadOnly={false} isReadOnly={false}
gridProps={gridProps}
/> />
); );
}); });
@@ -122,7 +110,6 @@ describe('AccountManager', () => {
partyId="partyOne" partyId="partyOne"
onClickAsset={jest.fn} onClickAsset={jest.fn}
isReadOnly={false} isReadOnly={false}
gridProps={gridProps}
/> />
); );
}); });
+4 -4
View File
@@ -11,7 +11,6 @@ import type { PinnedAsset } from './accounts-table';
import { AccountTable } from './accounts-table'; import { AccountTable } from './accounts-table';
import { Dialog } from '@vegaprotocol/ui-toolkit'; import { Dialog } from '@vegaprotocol/ui-toolkit';
import BreakdownTable from './breakdown-table'; import BreakdownTable from './breakdown-table';
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
const AccountBreakdown = ({ const AccountBreakdown = ({
assetId, assetId,
@@ -103,7 +102,7 @@ interface AccountManagerProps {
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean; isReadOnly: boolean;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
gridProps?: ReturnType<typeof useDataGridEvents>; storeKey?: string;
} }
export const AccountManager = ({ export const AccountManager = ({
@@ -113,11 +112,12 @@ export const AccountManager = ({
partyId, partyId,
isReadOnly, isReadOnly,
pinnedAsset, pinnedAsset,
storeKey,
onMarketClick, onMarketClick,
gridProps,
}: AccountManagerProps) => { }: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
const [breakdownAssetId, setBreakdownAssetId] = useState<string>(); const [breakdownAssetId, setBreakdownAssetId] = useState<string>();
const { data, error } = useDataProvider({ const { data, error } = useDataProvider({
dataProvider: aggregatedAccountsDataProvider, dataProvider: aggregatedAccountsDataProvider,
variables: { partyId }, variables: { partyId },
@@ -144,8 +144,8 @@ export const AccountManager = ({
onClickBreakdown={setBreakdownAssetId} onClickBreakdown={setBreakdownAssetId}
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
pinnedAsset={pinnedAsset} pinnedAsset={pinnedAsset}
storeKey={storeKey}
overlayNoRowsTemplate={error ? error.message : t('No accounts')} overlayNoRowsTemplate={error ? error.message : t('No accounts')}
{...gridProps}
/> />
<AccountBreakdownDialog <AccountBreakdownDialog
assetId={breakdownAssetId} assetId={breakdownAssetId}
+2
View File
@@ -73,6 +73,7 @@ export interface AccountTableProps extends AgGridReactProps {
onClickBreakdown?: (assetId: string) => void; onClickBreakdown?: (assetId: string) => void;
isReadOnly: boolean; isReadOnly: boolean;
pinnedAsset?: PinnedAsset; pinnedAsset?: PinnedAsset;
storeKey?: string;
} }
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>( export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
@@ -306,6 +307,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
<AgGrid <AgGrid
{...props} {...props}
style={{ width: '100%', height: '100%' }} style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No accounts')}
getRowId={({ data }: { data: AccountFields }) => data.asset.id} getRowId={({ data }: { data: AccountFields }) => data.asset.id}
ref={ref} ref={ref}
tooltipShowDelay={500} tooltipShowDelay={500}
+1 -9
View File
@@ -1,12 +1,4 @@
{ {
"presets": [ "presets": ["@nrwl/next/babel"],
[
"@nrwl/next/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": [] "plugins": []
} }
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__"], "ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [ "overrides": [
{ {
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+1 -1
View File
@@ -8,7 +8,7 @@
"executor": "@nrwl/web:rollup", "executor": "@nrwl/web:rollup",
"outputs": ["{options.outputPath}"], "outputs": ["{options.outputPath}"],
"options": { "options": {
"outputPath": "dist/libs/announcements", "outputPath": "dist/libs/accounts",
"tsConfig": "libs/announcements/tsconfig.lib.json", "tsConfig": "libs/announcements/tsconfig.lib.json",
"project": "libs/announcements/package.json", "project": "libs/announcements/package.json",
"entryFile": "libs/announcements/src/index.ts", "entryFile": "libs/announcements/src/index.ts",
+1 -1
View File
@@ -131,7 +131,7 @@ export const CandlesChartContainer = ({
return ( return (
<div className="h-full flex flex-col"> <div className="h-full flex flex-col">
<div className="px-4 py-2 flex flex-row flex-wrap gap-2"> <div className="px-4 py-2 flex flex-row flex-wrap gap-4">
<DropdownMenu <DropdownMenu
trigger={ trigger={
<DropdownMenuTrigger> <DropdownMenuTrigger>
@@ -1,11 +0,0 @@
import type { ProposalSubmissionBody } from '@vegaprotocol/wallet';
import { sendVegaTx } from './wallet-client';
import { waitForProposal } from './propose-market';
import { determineId } from '../utils';
const vegaPubKey = Cypress.env('VEGA_PUBLIC_KEY');
export async function submitProposal(proposalTx: ProposalSubmissionBody) {
cy.highlight('Submitting proposal');
const result = await sendVegaTx(vegaPubKey, proposalTx);
await waitForProposal(determineId(result.result.transaction.signature.value));
}
@@ -1,6 +1,5 @@
import { createWalletClient } from '../capsule/wallet-client'; import { createWalletClient, sendVegaTx } from '../capsule/wallet-client';
import type { ProposalSubmissionBody } from '@vegaprotocol/wallet'; import type { ProposalSubmissionBody } from '@vegaprotocol/wallet';
import { submitProposal } from '../capsule/submit-proposal';
declare global { declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
@@ -16,8 +15,11 @@ export const addVegaWalletSubmitProposal = () => {
Cypress.Commands.add('VegaWalletSubmitProposal', (proposalTx) => { Cypress.Commands.add('VegaWalletSubmitProposal', (proposalTx) => {
const vegaWalletUrl = Cypress.env('VEGA_WALLET_URL'); const vegaWalletUrl = Cypress.env('VEGA_WALLET_URL');
const token = Cypress.env('VEGA_WALLET_API_TOKEN'); const token = Cypress.env('VEGA_WALLET_API_TOKEN');
const vegaPubKey = Cypress.env('VEGA_PUBLIC_KEY');
createWalletClient(vegaWalletUrl, token); createWalletClient(vegaWalletUrl, token);
cy.wrap(submitProposal(proposalTx));
cy.highlight('Submitting proposal');
sendVegaTx(vegaPubKey, proposalTx);
}); });
}; };
+2 -1
View File
@@ -1,4 +1,5 @@
export * from './lib/ag-grid/ag-grid-lazy'; export * from './lib/ag-grid/ag-grid-lazy';
export * from './lib/ag-grid/use-column-sizes';
export * from './lib/column-definitions'; export * from './lib/column-definitions';
@@ -23,4 +24,4 @@ export * from './lib/type-helpers';
export * from './lib/cells/grid-progress-bar'; export * from './lib/cells/grid-progress-bar';
export * from './lib/ag-grid-update'; export * from './lib/ag-grid-update';
export * from './lib/use-datagrid-events'; export * from './lib/use-bottom-placeholder';
@@ -2,16 +2,23 @@ import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
import { AgGridReact } from 'ag-grid-react'; import { AgGridReact } from 'ag-grid-react';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers'; import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useColumnSizes } from './use-column-sizes';
import classNames from 'classnames'; import classNames from 'classnames';
export const AgGridThemed = ({ export const AgGridThemed = ({
style, style,
gridRef, gridRef,
storeKey,
...props ...props
}: (AgGridReactProps | AgReactUiProps) & { }: (AgGridReactProps | AgReactUiProps) & {
style?: React.CSSProperties; style?: React.CSSProperties;
gridRef?: React.ForwardedRef<AgGridReact>; gridRef?: React.ForwardedRef<AgGridReact>;
storeKey?: string;
}) => { }) => {
const commonColumnCallbacks = useColumnSizes({
storeKey,
props,
});
const { theme } = useThemeSwitcher(); const { theme } = useThemeSwitcher();
const defaultProps = { const defaultProps = {
rowHeight: 22, rowHeight: 22,
@@ -29,7 +36,12 @@ export const AgGridThemed = ({
return ( return (
<div className={wrapperClasses} style={style}> <div className={wrapperClasses} style={style}>
<AgGridReact {...defaultProps} {...props} ref={gridRef} /> <AgGridReact
{...defaultProps}
{...props}
{...commonColumnCallbacks}
ref={gridRef}
/>
</div> </div>
); );
}; };
@@ -4,6 +4,7 @@ import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
type Props = AgGridReactProps & { type Props = AgGridReactProps & {
style?: React.CSSProperties; style?: React.CSSProperties;
gridRef?: React.Ref<AgGridReact>; gridRef?: React.Ref<AgGridReact>;
storeKey?: string;
}; };
export const AgGridLazyInternal = lazy(() => export const AgGridLazyInternal = lazy(() =>
@@ -0,0 +1,122 @@
import type {
Column,
ColumnResizedEvent,
GridSizeChangedEvent,
GridReadyEvent,
} from 'ag-grid-community';
import { renderHook, act, waitFor } from '@testing-library/react';
import { useColumnSizes } from './use-column-sizes';
import * as reactHelpers from '@vegaprotocol/react-helpers';
const mockApis = {
api: {
sizeColumnsToFit: jest.fn(),
},
columnApi: {
setColumnWidths: jest.fn(),
},
};
const mockValueSetter = jest.fn();
const mockStore = {
sizes: { testid: { col1: 100 } },
valueSetter: mockValueSetter,
};
jest.mock('zustand', () => ({
...jest.requireActual('zustand'),
create: () =>
jest.fn(() =>
jest.fn().mockImplementation((creator) => {
return creator(mockStore);
})
),
}));
describe('UseColumnSizes hook', () => {
const storeKey = 'testid';
beforeEach(() => {
jest.clearAllMocks();
});
it('should return proper methods', () => {
const { result } = renderHook(() =>
useColumnSizes({ storeKey, props: {} })
);
expect(Object.keys(result.current)).toHaveLength(3);
expect(result.current).toStrictEqual({
onColumnResized: expect.any(Function),
onGridReady: expect.any(Function),
onGridSizeChanged: expect.any(Function),
});
});
it('onGridSizeChanged should call setSize', async () => {
jest
.spyOn(reactHelpers, 'useScreenDimensions')
.mockReturnValue({ screenSize: 'xxl' });
const { result } = renderHook(() =>
useColumnSizes({ storeKey, props: {} })
);
await act(() => {
result.current.onGridSizeChanged?.({
clientWidth: 1000,
...mockApis,
} as GridSizeChangedEvent);
});
await waitFor(() => {
expect(mockApis.columnApi.setColumnWidths).toHaveBeenCalledWith([
{ key: 'col1', newWidth: 100 },
]);
});
});
it('onColumnResized should fill up store', async () => {
const columns: Column[] = [
{ getColId: () => 'col1', getActualWidth: () => 100 },
{ getColId: () => 'col2', getActualWidth: () => 200 },
] as Column[];
const sizeObj = { col1: 100, col2: 200, clientWidth: 1000 };
const { result } = renderHook(() =>
useColumnSizes({ storeKey, props: {} })
);
await act(() => {
result.current.onGridSizeChanged?.({
clientWidth: 1000,
...mockApis,
} as GridSizeChangedEvent);
});
await act(() => {
result.current.onColumnResized?.({
columns,
finished: true,
source: 'uiColumnDragged',
...mockApis,
} as ColumnResizedEvent);
});
await waitFor(() => {
expect(mockValueSetter).toHaveBeenCalledWith(storeKey, sizeObj);
});
});
it('onGridReady should call setSizes', async () => {
const props = { onGridReady: jest.fn() };
const { result } = renderHook(() => useColumnSizes({ storeKey, props }));
const obTest = { cool: 1, ...mockApis };
await act(() => {
result.current.onGridReady?.(obTest as GridReadyEvent);
});
expect(props.onGridReady).toHaveBeenCalledWith(obTest);
expect(mockApis.api.sizeColumnsToFit).toHaveBeenCalledWith();
});
it('if no storeKey should be transparent', () => {
const { result } = renderHook(() =>
useColumnSizes({ storeKey: '', props: {} })
);
expect(result.current).toStrictEqual({
onColumnResized: undefined,
onGridReady: undefined,
onGridSizeChanged: undefined,
});
});
});
@@ -0,0 +1,145 @@
import { useCallback, useRef } from 'react';
import type {
GridSizeChangedEvent,
GridReadyEvent,
ColumnResizedEvent,
} from 'ag-grid-community';
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
const STORAGE_KEY = 'vega_columns_sizes_store';
export const useColumnSizesStore = create<{
sizes: Record<string, Record<string, number>>;
valueSetter: (storeKey: string, value: Record<string, number>) => void;
}>()(
persist(
immer((set) => ({
sizes: {},
valueSetter: (storeKey, value) =>
set((state) => {
state.sizes[storeKey] = {
...(state.sizes[storeKey] || {}),
...value,
};
return state;
}),
})),
{ name: STORAGE_KEY }
)
);
interface UseColumnSizesProps {
props: AgGridReactProps | AgReactUiProps;
storeKey?: string;
}
export const useColumnSizes = ({
storeKey = '',
props,
}: UseColumnSizesProps): {
onColumnResized?: (event: ColumnResizedEvent) => void;
onGridReady?: (event: GridReadyEvent) => void;
onGridSizeChanged?: (event: GridSizeChangedEvent) => void;
} => {
const sizes = useColumnSizesStore((store) => store.sizes[storeKey] || {});
const valueSetter = useColumnSizesStore((store) => store.valueSetter);
const widthRef = useRef(sizes['clientWidth'] || 0);
const {
onColumnResized: parentOnColumnResized,
onGridReady: parentOnGridReady,
onGridSizeChanged: parentOnGridSizeChanged,
} = props;
const recalculateSizes = useCallback((sizes: Record<string, number>) => {
if (
widthRef.current &&
sizes['clientWidth'] &&
widthRef.current !== sizes['clientWidth']
) {
const oldWidth = sizes['clientWidth'];
const ratio = widthRef.current / oldWidth;
return {
...Object.entries(sizes).reduce((agg, [key, value]) => {
agg[key] = value * ratio;
return agg;
}, {} as Record<string, number>),
width: widthRef.current,
} as Record<string, number>;
}
return sizes;
}, []);
const onColumnResized = useCallback(
(event: ColumnResizedEvent) => {
parentOnColumnResized?.(event);
if (
storeKey &&
event.source === 'uiColumnDragged' &&
event.finished &&
widthRef.current
) {
const { columns } = event;
if (columns?.length) {
const sizesObj = columns.reduce((aggr, column) => {
aggr[column.getColId()] = column.getActualWidth();
return aggr;
}, {} as Record<string, number>);
sizesObj['clientWidth'] = widthRef.current;
valueSetter(storeKey, sizesObj);
}
}
},
[valueSetter, storeKey, parentOnColumnResized]
);
const { screenSize } = useScreenDimensions();
const largeScreen = ['xl', 'xxl', 'xxxl'].includes(screenSize);
const setSizes = useCallback(
(apiEvent: GridReadyEvent | GridSizeChangedEvent) => {
if (!storeKey || !Object.keys(sizes).length || !widthRef.current) {
largeScreen && apiEvent?.api.sizeColumnsToFit();
} else {
const recalculatedSizes = recalculateSizes(sizes);
const newSizes = Object.entries(recalculatedSizes).map(
([key, size]) => ({
key,
newWidth: size,
})
);
apiEvent.columnApi.setColumnWidths(newSizes);
}
},
[storeKey, recalculateSizes, sizes, largeScreen]
);
const onGridReady = useCallback(
(event: GridReadyEvent) => {
parentOnGridReady?.(event);
setSizes(event);
},
[setSizes, parentOnGridReady]
);
const onGridSizeChanged = useCallback(
(event: GridSizeChangedEvent) => {
parentOnGridSizeChanged?.(event);
widthRef.current = event.clientWidth;
setSizes(event);
},
[parentOnGridSizeChanged, setSizes]
);
if (storeKey) {
return {
onGridReady,
onGridSizeChanged,
onColumnResized,
};
}
return {
onGridReady: parentOnGridReady,
onGridSizeChanged: parentOnGridSizeChanged,
onColumnResized: parentOnColumnResized,
};
};
@@ -0,0 +1,68 @@
import type { RefObject } from 'react';
import { useCallback, useMemo } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import type { IsFullWidthRowParams, RowHeightParams } from 'ag-grid-community';
const NO_HOVER_CSS_RULE = { 'no-hover': 'data?.isLastPlaceholder' };
const ROW_ID = 'bottom-placeholder';
const fullWidthCellRenderer = () => null;
const isFullWidthRow = (params: IsFullWidthRowParams) =>
params.rowNode.data?.isLastPlaceholder;
interface Props {
gridRef: RefObject<AgGridReact>;
disabled?: boolean;
}
// eslint-disable-next-line @typescript-eslint/ban-types
export const useBottomPlaceholder = ({ gridRef, disabled }: Props) => {
const onBodyScrollEnd = useCallback(() => {
const rowCont = gridRef.current?.api.getDisplayedRowCount() ?? 0;
if (rowCont) {
const lastRow = gridRef.current?.api.getDisplayedRowAtIndex(rowCont - 1);
if (lastRow && lastRow.data) {
const placeholderRow = {
...lastRow.data,
isLastPlaceholder: true,
id: ROW_ID,
};
const transaction = gridRef.current?.api.getRowNode(ROW_ID)
? { update: [placeholderRow] }
: { add: [placeholderRow] };
gridRef.current?.api.applyTransaction(transaction);
}
}
}, [gridRef]);
const onRowsChanged = useCallback(() => {
const placeholderNode = gridRef.current?.api.getRowNode(ROW_ID);
if (placeholderNode) {
const transaction = {
remove: [placeholderNode.data],
};
gridRef.current?.api.applyTransaction(transaction);
}
onBodyScrollEnd();
}, [gridRef, onBodyScrollEnd]);
const getRowHeight = useCallback(
(params: RowHeightParams) =>
params.data?.isLastPlaceholder ? 50 : undefined,
[]
);
return useMemo(
() =>
!disabled
? {
onBodyScrollEnd,
rowClassRules: NO_HOVER_CSS_RULE,
isFullWidthRow,
fullWidthCellRenderer,
onSortChanged: onRowsChanged,
onFilterChanged: onRowsChanged,
getRowHeight,
}
: {},
[onBodyScrollEnd, onRowsChanged, disabled, getRowHeight]
);
};
@@ -1,185 +0,0 @@
import { act, render, waitFor } from '@testing-library/react';
import {
useDataGridEvents,
GRID_EVENT_DEBOUNCE_TIME,
} from './use-datagrid-events';
import { AgGridThemed } from './ag-grid/ag-grid-lazy-themed';
import type { MutableRefObject } from 'react';
import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
const gridProps = {
rowData: [{ id: 1 }],
columnDefs: [
{
field: 'id',
width: 100,
resizable: true,
filter: 'agNumberColumnFilter',
},
],
style: { width: 500, height: 300 },
};
// Not using render hook so I can pass event callbacks
// to a rendered grid
function setup(...args: Parameters<typeof useDataGridEvents>) {
let gridRef;
function TestComponent() {
const hookCallbacks = useDataGridEvents(...args);
gridRef = useRef<AgGridReact | null>(null);
return <AgGridThemed gridRef={gridRef} {...gridProps} {...hookCallbacks} />;
}
render(<TestComponent />);
return gridRef as unknown as MutableRefObject<AgGridReact>;
}
describe('useDataGridEvents', () => {
const originalWarn = console.warn;
beforeAll(() => {
jest.useFakeTimers();
// disabling some ag grid warnings that are caused by test setup only
console.warn = () => undefined;
});
afterAll(() => {
jest.useRealTimers();
console.warn = originalWarn;
});
it('default state is set and callback is called on column or filter event', async () => {
const callback = jest.fn();
const initialState = {
filterModel: undefined,
columnState: undefined,
};
const result = setup(initialState, callback);
// column state was not updated, so the default width provided by the
// col def should be set
expect(result.current.columnApi.getColumnState()[0].width).toEqual(
gridProps.columnDefs[0].width
);
// no filters set
expect(result.current.api.getFilterModel()).toEqual({});
const newWidth = 400;
// Set col width
await act(async () => {
result.current.columnApi.setColumnWidth('id', newWidth);
});
act(() => {
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
});
expect(callback).toHaveBeenCalledWith({
columnState: [expect.objectContaining({ colId: 'id', width: newWidth })],
filterModel: {},
});
callback.mockClear();
expect(result.current.columnApi.getColumnState()[0].width).toEqual(
newWidth
);
// Set filter
await act(async () => {
result.current.columnApi.applyColumnState({
state: [{ colId: 'id', sort: 'asc' }],
applyOrder: true,
});
});
act(() => {
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
});
expect(callback).toHaveBeenCalledWith({
columnState: [expect.objectContaining({ colId: 'id', sort: 'asc' })],
filterModel: {},
});
callback.mockClear();
expect(result.current.columnApi.getColumnState()[0].sort).toEqual('asc');
// Set filter
const idFilter = {
filter: 1,
filterType: 'number',
type: 'equals',
};
await act(async () => {
result.current.api.setFilterModel({
id: idFilter,
});
});
act(() => {
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
});
expect(callback).toHaveBeenCalledWith({
columnState: expect.any(Object),
filterModel: {
id: idFilter,
},
});
callback.mockClear();
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
});
it('applies grid state on ready', async () => {
const idFilter = {
filter: 1,
filterType: 'number',
type: 'equals',
};
const colState = { colId: 'id', width: 300, sort: 'desc' as const };
const initialState = {
filterModel: {
id: idFilter,
},
columnState: [colState],
};
const result = setup(initialState, jest.fn());
await waitFor(() => {
expect(result.current.api.getFilterModel()['id']).toEqual(idFilter);
expect(result.current.columnApi.getColumnState()[0]).toEqual(
expect.objectContaining(colState)
);
});
});
it('debounces events', async () => {
const callback = jest.fn();
const initialState = {
filterModel: undefined,
columnState: undefined,
};
const result = setup(initialState, callback);
const newWidth = 400;
// Set col width multiple times
await act(async () => {
result.current.columnApi.setColumnWidth('id', newWidth);
result.current.columnApi.setColumnWidth('id', newWidth);
result.current.columnApi.setColumnWidth('id', newWidth);
});
expect(callback).not.toHaveBeenCalled();
act(() => {
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
});
expect(callback).toHaveBeenCalledTimes(1);
});
});
@@ -1,66 +0,0 @@
import debounce from 'lodash/debounce';
import type {
ColumnResizedEvent,
ColumnState,
FilterChangedEvent,
GridReadyEvent,
SortChangedEvent,
} from 'ag-grid-community';
import { useCallback, useMemo } from 'react';
type State = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
filterModel?: { [key: string]: any };
columnState?: ColumnState[];
};
type Event = ColumnResizedEvent | FilterChangedEvent | SortChangedEvent;
export const GRID_EVENT_DEBOUNCE_TIME = 300;
export const useDataGridEvents = (
state: State,
callback: (data: State) => void
) => {
// This function can be called very frequently by the onColumnResized
// grid callback, so its memoized to only update after resizing is finished
const onGridChange = useMemo(
() =>
debounce(({ api, columnApi }: Event) => {
if (!api || !columnApi) return;
const columnState = columnApi.getColumnState();
const filterModel = api.getFilterModel();
callback({ columnState, filterModel });
}, GRID_EVENT_DEBOUNCE_TIME),
[callback]
);
// check if we have stored column states or filter models and apply if we do
const onGridReady = useCallback(
({ api, columnApi }: GridReadyEvent) => {
if (!api || !columnApi) return;
if (state.columnState) {
columnApi.applyColumnState({
state: state.columnState,
applyOrder: true,
});
} else {
// ensure columns fit available space if no widths are set
api.sizeColumnsToFit();
}
if (state.filterModel) {
api.setFilterModel(state.filterModel);
}
},
[state]
);
return {
onGridReady,
onColumnResized: onGridChange,
onFilterChanged: onGridChange,
onSortChanged: onGridChange,
};
};
@@ -124,7 +124,7 @@ export const DealTicket = ({
normalizedOrder.size, normalizedOrder.size,
market.positionDecimalPlaces market.positionDecimalPlaces
).multipliedBy(toBigNum(price, market.decimalPlaces)), ).multipliedBy(toBigNum(price, market.decimalPlaces)),
market.decimalPlaces asset.decimals
); );
} }
return null; return null;
@@ -133,6 +133,7 @@ export const DealTicket = ({
normalizedOrder?.size, normalizedOrder?.size,
market.decimalPlaces, market.decimalPlaces,
market.positionDecimalPlaces, market.positionDecimalPlaces,
asset.decimals,
]); ]);
const feeEstimate = useEstimateFees( const feeEstimate = useEstimateFees(
+3 -1
View File
@@ -24,8 +24,10 @@ export const DepositsTable = forwardRef<
return ( return (
<AgGrid <AgGrid
ref={ref} ref={ref}
defaultColDef={{ flex: 1 }} defaultColDef={{ resizable: true }}
style={{ width: '100%', height: '100%' }} style={{ width: '100%', height: '100%' }}
suppressCellFocus={true}
storeKey="depositTable"
{...props} {...props}
> >
<AgGridColumn headerName="Asset" field="asset.symbol" /> <AgGridColumn headerName="Asset" field="asset.symbol" />
@@ -301,25 +301,6 @@ describe('useEnvironment', () => {
expect(fetch).toHaveBeenCalledWith(configUrl); expect(fetch).toHaveBeenCalledWith(configUrl);
}); });
it('uses env vars from window._env_ if set', async () => {
const url = 'http://foo.bar.com';
// @ts-ignore _env_ is declared in app
window._env_ = {
VEGA_URL: url,
};
const { result } = setup();
await act(async () => {
result.current.initialize();
});
expect(result.current.VEGA_URL).toBe(url);
// @ts-ignore delete _env_
delete window['_env_'];
});
it('sets error if environment is invalid', async () => { it('sets error if environment is invalid', async () => {
const error = console.error; const error = console.error;
console.error = noop; console.error = noop;
+23 -96
View File
@@ -269,96 +269,34 @@ const testSubscription = (client: Client) => {
* here to appease the environment store interface * here to appease the environment store interface
*/ */
function compileEnvVars() { function compileEnvVars() {
const VEGA_ENV = windowOrDefault( const VEGA_ENV = process.env['NX_VEGA_ENV'] as Networks;
'VEGA_ENV',
process.env['NX_VEGA_ENV']
) as Networks;
const env: Environment = { const env: Environment = {
VEGA_URL: windowOrDefault('VEGA_URL', process.env['NX_VEGA_URL']), VEGA_URL: process.env['NX_VEGA_URL'],
VEGA_ENV, VEGA_ENV,
VEGA_CONFIG_URL: windowOrDefault( VEGA_CONFIG_URL: process.env['NX_VEGA_CONFIG_URL'] as string,
'VEGA_CONFIG_URL', VEGA_NETWORKS: parseNetworks(process.env['NX_VEGA_NETWORKS']),
process.env['NX_VEGA_CONFIG_URL'] as string VEGA_WALLET_URL: process.env['NX_VEGA_WALLET_URL'] as string,
), HOSTED_WALLET_URL: process.env['NX_HOSTED_WALLET_URL'],
VEGA_NETWORKS: parseNetworks( ETHERSCAN_URL: getEtherscanUrl(VEGA_ENV, process.env['NX_ETHERSCAN_URL']),
windowOrDefault('VEGA_NETWORKS', process.env['NX_VEGA_NETWORKS'])
),
VEGA_WALLET_URL: windowOrDefault(
'VEGA_WALLET_URL',
process.env['NX_VEGA_WALLET_URL'] as string
),
HOSTED_WALLET_URL: windowOrDefault(
'HOSTED_WALLET_URL',
process.env['NX_HOSTED_WALLET_URL']
),
ETHERSCAN_URL: getEtherscanUrl(
VEGA_ENV,
windowOrDefault('ETHERSCAN_URL', process.env['NX_ETHERSCAN_URL'])
),
ETHEREUM_PROVIDER_URL: getEthereumProviderUrl( ETHEREUM_PROVIDER_URL: getEthereumProviderUrl(
VEGA_ENV, VEGA_ENV,
windowOrDefault( process.env['NX_ETHEREUM_PROVIDER_URL']
'ETHEREUM_PROVIDER_URL',
process.env['NX_ETHEREUM_PROVIDER_URL']
)
), ),
ETH_LOCAL_PROVIDER_URL: windowOrDefault( ETH_LOCAL_PROVIDER_URL: process.env['NX_ETH_LOCAL_PROVIDER_URL'],
'ETH_LOCAL_PROVIDER_URL', ETH_WALLET_MNEMONIC: process.env['NX_ETH_WALLET_MNEMONIC'],
process.env['NX_ETH_LOCAL_PROVIDER_URL'] ORACLE_PROOFS_URL: process.env['NX_ORACLE_PROOFS_URL'],
), VEGA_DOCS_URL: process.env['NX_VEGA_DOCS_URL'],
ETH_WALLET_MNEMONIC: windowOrDefault( VEGA_CONSOLE_URL: process.env['NX_VEGA_CONSOLE_URL'],
'ETH_WALLET_MNEMONIC', VEGA_EXPLORER_URL: process.env['NX_VEGA_EXPLORER_URL'],
process.env['NX_ETH_WALLET_MNEMONIC'] VEGA_TOKEN_URL: process.env['NX_VEGA_TOKEN_URL'],
), GITHUB_FEEDBACK_URL: process.env['NX_GITHUB_FEEDBACK_URL'],
ORACLE_PROOFS_URL: windowOrDefault( MAINTENANCE_PAGE: parseBoolean(process.env['NX_MAINTENANCE_PAGE']),
'ORACLE_PROOFS_URL', GIT_BRANCH: process.env['GIT_COMMIT_BRANCH'],
process.env['NX_ORACLE_PROOFS_URL'] GIT_COMMIT_HASH: process.env['GIT_COMMIT_HASH'],
), GIT_ORIGIN_URL: process.env['GIT_ORIGIN_URL'],
VEGA_DOCS_URL: windowOrDefault( ANNOUNCEMENTS_CONFIG_URL: process.env['NX_ANNOUNCEMENTS_CONFIG_URL'],
'VEGA_DOCS_URL', VEGA_INCIDENT_URL: process.env['NX_VEGA_INCIDENT_URL'],
process.env['NX_VEGA_DOCS_URL'] APP_VERSION: process.env['NX_APP_VERSION'],
),
VEGA_CONSOLE_URL: windowOrDefault(
'VEGA_CONSOLE_URL',
process.env['NX_VEGA_CONSOLE_URL']
),
VEGA_EXPLORER_URL: windowOrDefault(
'VEGA_EXPLORER_URL',
process.env['NX_VEGA_EXPLORER_URL']
),
VEGA_TOKEN_URL: windowOrDefault(
'VEGA_TOKEN_URL',
process.env['NX_VEGA_TOKEN_URL']
),
GITHUB_FEEDBACK_URL: windowOrDefault(
'GITHUB_FEEDBACK_URL',
process.env['NX_GITHUB_FEEDBACK_URL']
),
MAINTENANCE_PAGE: parseBoolean(
windowOrDefault('MAINTENANCE_PAGE', process.env['NX_MAINTENANCE_PAGE'])
),
GIT_BRANCH: windowOrDefault(
'GIT_COMMIT_BRANCH',
process.env['GIT_COMMIT_BRANCH']
),
GIT_COMMIT_HASH: windowOrDefault(
'GIT_COMMIT_HASH',
process.env['GIT_COMMIT_HASH']
),
GIT_ORIGIN_URL: windowOrDefault(
'GIT_ORIGIN_URL',
process.env['GIT_ORIGIN_URL']
),
ANNOUNCEMENTS_CONFIG_URL: windowOrDefault(
'ANNOUNCEMENTS_CONFIG_URL',
process.env['NX_ANNOUNCEMENTS_CONFIG_URL']
),
VEGA_INCIDENT_URL: windowOrDefault(
'VEGA_INCIDENT_URL',
process.env['NX_VEGA_INCIDENT_URL']
),
APP_VERSION: windowOrDefault('APP_VERSION', process.env['NX_APP_VERSION']),
SENTRY_DSN: windowOrDefault('SENTRY_DSN', process.env['NX_SENTRY_DSN']),
}; };
return env; return env;
@@ -403,14 +341,3 @@ function getEtherscanUrl(
? 'https://etherscan.io' ? 'https://etherscan.io'
: 'https://sepolia.etherscan.io'; : 'https://sepolia.etherscan.io';
} }
export function windowOrDefault(key: string, defaultValue?: string) {
if (typeof window !== 'undefined') {
// @ts-ignore avoid conflic in env
if (window._env_ && window._env_[key]) {
// @ts-ignore presence has been check above
return window._env_[key];
}
}
return defaultValue || undefined;
}
@@ -5,7 +5,6 @@ import { useEnvironment } from './use-environment';
import { useNavigatorOnline } from '@vegaprotocol/react-helpers'; import { useNavigatorOnline } from '@vegaprotocol/react-helpers';
import { Intent } from '@vegaprotocol/ui-toolkit'; import { Intent } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { isTestEnv } from '@vegaprotocol/utils';
const POLL_INTERVAL = 1000; const POLL_INTERVAL = 1000;
const BLOCK_THRESHOLD = 3; const BLOCK_THRESHOLD = 3;
@@ -39,7 +38,7 @@ export const useNodeHealth = () => {
return; return;
} }
if (!isTestEnv() && window.location.hostname !== 'localhost') { if (!('Cypress' in window) && window.location.hostname !== 'localhost') {
startPolling(POLL_INTERVAL); startPolling(POLL_INTERVAL);
} }
}, [error, startPolling, stopPolling]); }, [error, startPolling, stopPolling]);
@@ -52,7 +52,6 @@ const schemaObject = {
ANNOUNCEMENTS_CONFIG_URL: z.optional(z.string()), ANNOUNCEMENTS_CONFIG_URL: z.optional(z.string()),
VEGA_INCIDENT_URL: z.optional(z.string()), VEGA_INCIDENT_URL: z.optional(z.string()),
APP_VERSION: z.optional(z.string()), APP_VERSION: z.optional(z.string()),
SENTRY_DSN: z.optional(z.string()),
}; };
// combine schema above with custom rule to ensure either // combine schema above with custom rule to ensure either
+1 -1
View File
@@ -1,3 +1,3 @@
export * from './lib/fills-manager'; export * from './lib/fills-container';
export * from './lib/fills-data-provider'; export * from './lib/fills-data-provider';
export * from './lib/__generated__/Fills'; export * from './lib/__generated__/Fills';
+33
View File
@@ -0,0 +1,33 @@
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { FillsManager } from './fills-manager';
export const FillsContainer = ({
marketId,
onMarketClick,
storeKey,
}: {
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
storeKey?: string;
}) => {
const { pubKey } = useVegaWallet();
if (!pubKey) {
return (
<Splash>
<p>{t('Please connect Vega wallet')}</p>
</Splash>
);
}
return (
<FillsManager
partyId={pubKey}
marketId={marketId}
onMarketClick={onMarketClick}
storeKey={storeKey}
/>
);
};
+8 -4
View File
@@ -2,7 +2,7 @@ import type { AgGridReact } from 'ag-grid-react';
import { useRef } from 'react'; import { useRef } from 'react';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { FillsTable } from './fills-table'; import { FillsTable } from './fills-table';
import type { useDataGridEvents } from '@vegaprotocol/datagrid'; import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import type * as Schema from '@vegaprotocol/types'; import type * as Schema from '@vegaprotocol/types';
import { fillsWithMarketProvider } from './fills-data-provider'; import { fillsWithMarketProvider } from './fills-data-provider';
@@ -11,14 +11,14 @@ interface FillsManagerProps {
partyId: string; partyId: string;
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
gridProps: ReturnType<typeof useDataGridEvents>; storeKey?: string;
} }
export const FillsManager = ({ export const FillsManager = ({
partyId, partyId,
marketId, marketId,
onMarketClick, onMarketClick,
gridProps, storeKey,
}: FillsManagerProps) => { }: FillsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = { const filter: Schema.TradesFilter | Schema.TradesSubscriptionFilter = {
@@ -38,6 +38,9 @@ export const FillsManager = ({
}, },
variables: { filter }, variables: { filter },
}); });
const bottomPlaceholderProps = useBottomPlaceholder({
gridRef,
});
return ( return (
<FillsTable <FillsTable
@@ -45,8 +48,9 @@ export const FillsManager = ({
rowData={data} rowData={data}
partyId={partyId} partyId={partyId}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
storeKey={storeKey}
{...bottomPlaceholderProps}
overlayNoRowsTemplate={error ? error.message : t('No fills')} overlayNoRowsTemplate={error ? error.message : t('No fills')}
{...gridProps}
/> />
); );
}; };
+1
View File
@@ -39,6 +39,7 @@ export type Role = typeof TAKER | typeof MAKER | '-';
export type Props = (AgGridReactProps | AgReactUiProps) & { export type Props = (AgGridReactProps | AgReactUiProps) & {
partyId: string; partyId: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
storeKey?: string;
}; };
export const FillsTable = forwardRef<AgGridReact, Props>( export const FillsTable = forwardRef<AgGridReact, Props>(
+1
View File
@@ -1,2 +1,3 @@
export * from './lib/ledger-container';
export * from './lib/ledger-manager'; export * from './lib/ledger-manager';
export * from './lib/__generated__/LedgerEntries'; export * from './lib/__generated__/LedgerEntries';
+17
View File
@@ -0,0 +1,17 @@
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { LedgerManager } from './ledger-manager';
export const LedgerContainer = () => {
const { pubKey } = useVegaWallet();
if (!pubKey) {
return (
<Splash>
<p>{t('Please connect Vega wallet')}</p>
</Splash>
);
}
return <LedgerManager partyId={pubKey} />;
};
+7 -19
View File
@@ -10,7 +10,6 @@ import { LedgerTable } from './ledger-table';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import type * as Types from '@vegaprotocol/types'; import type * as Types from '@vegaprotocol/types';
import { LedgerExportLink } from './ledger-export-link'; import { LedgerExportLink } from './ledger-export-link';
import type { useDataGridEvents } from '@vegaprotocol/datagrid';
export interface Filter { export interface Filter {
vegaTime?: { vegaTime?: {
@@ -25,13 +24,7 @@ const defaultFilter = {
}, },
}; };
export const LedgerManager = ({ export const LedgerManager = ({ partyId }: { partyId: string }) => {
partyId,
gridProps,
}: {
partyId: string;
gridProps: ReturnType<typeof useDataGridEvents>;
}) => {
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
const [filter, setFilter] = useState<Filter>(defaultFilter); const [filter, setFilter] = useState<Filter>(defaultFilter);
@@ -40,7 +33,7 @@ export const LedgerManager = ({
partyId, partyId,
dateRange: filter?.vegaTime?.value, dateRange: filter?.vegaTime?.value,
pagination: { pagination: {
first: 10, first: 5000,
}, },
}), }),
[partyId, filter?.vegaTime?.value] [partyId, filter?.vegaTime?.value]
@@ -52,23 +45,18 @@ export const LedgerManager = ({
skip: !variables.partyId, skip: !variables.partyId,
}); });
const onFilterChanged = useCallback( const onFilterChanged = useCallback((event: FilterChangedEvent) => {
(event: FilterChangedEvent) => { const updatedFilter = { ...defaultFilter, ...event.api.getFilterModel() };
const updatedFilter = { ...defaultFilter, ...event.api.getFilterModel() }; setFilter(updatedFilter);
setFilter(updatedFilter); }, []);
gridProps.onFilterChanged(event);
},
[gridProps]
);
return ( return (
<div className="h-full relative"> <div className="h-full relative">
<LedgerTable <LedgerTable
ref={gridRef} ref={gridRef}
rowData={data} rowData={data}
overlayNoRowsTemplate={error ? error.message : t('No entries')}
{...gridProps}
onFilterChanged={onFilterChanged} onFilterChanged={onFilterChanged}
overlayNoRowsTemplate={error ? error.message : t('No entries')}
/> />
{data && <LedgerExportLink entries={data} partyId={partyId} />} {data && <LedgerExportLink entries={data} partyId={partyId} />}
</div> </div>
+4 -1
View File
@@ -49,7 +49,7 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
(props, ref) => { (props, ref) => {
return ( return (
<AgGrid <AgGrid
style={{ width: '100%', height: '100%' }} style={{ width: '100%', height: 'calc(100% - 50px)' }}
ref={ref} ref={ref}
tooltipShowDelay={500} tooltipShowDelay={500}
defaultColDef={{ defaultColDef={{
@@ -61,6 +61,9 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
buttons: ['reset'], buttons: ['reset'],
}, },
}} }}
storeKey="ledgerTable"
suppressLoadingOverlay
suppressNoRowsOverlay
{...props} {...props}
> >
<AgGridColumn <AgGridColumn
@@ -1,7 +1,6 @@
# Liquidity Provisions # Liquidity Provisions
fragment LiquidityProvisionFields on LiquidityProvision { fragment LiquidityProvisionFields on LiquidityProvision {
id
party { party {
id id
accountsConnection(marketId: $marketId, type: ACCOUNT_TYPE_BOND) { accountsConnection(marketId: $marketId, type: ACCOUNT_TYPE_BOND) {
+2 -3
View File
@@ -3,14 +3,14 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client'; import * as Apollo from '@apollo/client';
const defaultOptions = {} as const; const defaultOptions = {} as const;
export type LiquidityProvisionFieldsFragment = { __typename?: 'LiquidityProvision', id?: string | null, createdAt: any, updatedAt?: any | null, commitmentAmount: string, fee: string, status: Types.LiquidityProvisionStatus, party: { __typename?: 'Party', id: string, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string } } | null> | null } | null } }; export type LiquidityProvisionFieldsFragment = { __typename?: 'LiquidityProvision', createdAt: any, updatedAt?: any | null, commitmentAmount: string, fee: string, status: Types.LiquidityProvisionStatus, party: { __typename?: 'Party', id: string, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string } } | null> | null } | null } };
export type LiquidityProvisionsQueryVariables = Types.Exact<{ export type LiquidityProvisionsQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID']; marketId: Types.Scalars['ID'];
}>; }>;
export type LiquidityProvisionsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', liquidityProvisionsConnection?: { __typename?: 'LiquidityProvisionsConnection', edges?: Array<{ __typename?: 'LiquidityProvisionsEdge', node: { __typename?: 'LiquidityProvision', id?: string | null, createdAt: any, updatedAt?: any | null, commitmentAmount: string, fee: string, status: Types.LiquidityProvisionStatus, party: { __typename?: 'Party', id: string, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string } } | null> | null } | null } } } | null> | null } | null } | null }; export type LiquidityProvisionsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', liquidityProvisionsConnection?: { __typename?: 'LiquidityProvisionsConnection', edges?: Array<{ __typename?: 'LiquidityProvisionsEdge', node: { __typename?: 'LiquidityProvision', createdAt: any, updatedAt?: any | null, commitmentAmount: string, fee: string, status: Types.LiquidityProvisionStatus, party: { __typename?: 'Party', id: string, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string } } | null> | null } | null } } } | null> | null } | null } | null };
export type LiquidityProvisionsUpdateSubscriptionVariables = Types.Exact<{ export type LiquidityProvisionsUpdateSubscriptionVariables = Types.Exact<{
partyId?: Types.InputMaybe<Types.Scalars['ID']>; partyId?: Types.InputMaybe<Types.Scalars['ID']>;
@@ -31,7 +31,6 @@ export type LiquidityProviderFeeShareQuery = { __typename?: 'Query', market?: {
export const LiquidityProvisionFieldsFragmentDoc = gql` export const LiquidityProvisionFieldsFragmentDoc = gql`
fragment LiquidityProvisionFields on LiquidityProvision { fragment LiquidityProvisionFields on LiquidityProvision {
id
party { party {
id id
accountsConnection(marketId: $marketId, type: ACCOUNT_TYPE_BOND) { accountsConnection(marketId: $marketId, type: ACCOUNT_TYPE_BOND) {
@@ -38,7 +38,8 @@ export const liquidityProvisionsDataProvider = makeDataProvider<
) => { ) => {
return produce(data || [], (draft) => { return produce(data || [], (draft) => {
deltas?.forEach((delta) => { deltas?.forEach((delta) => {
const index = draft.findIndex((a) => delta.id === a.id); const id = delta.id;
const index = draft.findIndex((a) => delta.id === id);
if (index !== -1) { if (index !== -1) {
draft[index].commitmentAmount = delta.commitmentAmount; draft[index].commitmentAmount = delta.commitmentAmount;
draft[index].fee = delta.fee; draft[index].fee = delta.fee;
@@ -46,7 +47,6 @@ export const liquidityProvisionsDataProvider = makeDataProvider<
draft[index].status = delta.status; draft[index].status = delta.status;
} else { } else {
draft.unshift({ draft.unshift({
id: delta.id,
commitmentAmount: delta.commitmentAmount, commitmentAmount: delta.commitmentAmount,
fee: delta.fee, fee: delta.fee,
status: delta.status, status: delta.status,
+3 -2
View File
@@ -188,7 +188,7 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
<AgGrid <AgGrid
style={{ width: '100%', height: '100%' }} style={{ width: '100%', height: '100%' }}
overlayNoRowsTemplate={t('No liquidity provisions')} overlayNoRowsTemplate={t('No liquidity provisions')}
getRowId={({ data }) => data.id} getRowId={({ data }) => `${data.party.id}-${data.status}`}
ref={ref} ref={ref}
tooltipShowDelay={500} tooltipShowDelay={500}
defaultColDef={{ defaultColDef={{
@@ -197,9 +197,10 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
tooltipComponent: TooltipCellComponent, tooltipComponent: TooltipCellComponent,
sortable: true, sortable: true,
}} }}
storeKey="liquidityProvisionTable"
{...props} {...props}
columnDefs={colDefs} columnDefs={colDefs}
/> ></AgGrid>
); );
} }
); );
@@ -1,5 +1,6 @@
import { import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
formatNumber,
formatNumberPercentage, formatNumberPercentage,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
@@ -46,10 +47,7 @@ const Row = ({
if (asPercentage) { if (asPercentage) {
return formatNumberPercentage(new BigNumber(value).times(100)); return formatNumberPercentage(new BigNumber(value).times(100));
} }
if (assetSymbol) { return `${formatNumber(Number(value))} ${assetSymbol}`;
return `${Number(value).toLocaleString()} ${assetSymbol}`;
}
return Number(value).toLocaleString();
}; };
const formattedValue = getFormattedValue(value); const formattedValue = getFormattedValue(value);
@@ -94,7 +94,7 @@ export const marketInfoQuery = (
}, },
lpPriceRange: '0.02', lpPriceRange: '0.02',
liquidityMonitoringParameters: { liquidityMonitoringParameters: {
triggeringRatio: '0.7', triggeringRatio: '0',
targetStakeParameters: { targetStakeParameters: {
timeWindow: 3600, timeWindow: 3600,
scalingFactor: 10, scalingFactor: 10,
@@ -61,6 +61,7 @@ export const MarketListTable = forwardRef<
columnDefs={columnDefs} columnDefs={columnDefs}
suppressCellFocus suppressCellFocus
components={{ PriceFlashCell, MarketName }} components={{ PriceFlashCell, MarketName }}
storeKey="allMarkets"
{...props} {...props}
/> />
); );
+3 -62
View File
@@ -1,50 +1,13 @@
import { marketDataErrorPolicyGuard } from '@vegaprotocol/data-provider'; import { marketDataErrorPolicyGuard } from '@vegaprotocol/data-provider';
import { makeDataProvider } from '@vegaprotocol/data-provider'; import { makeDataProvider } from '@vegaprotocol/data-provider';
import type { import type { MarketsDataQuery } from './__generated__/markets-data';
MarketsDataQuery,
MarketsDataQueryVariables,
} from './__generated__/markets-data';
import type {
MarketDataUpdateSubscription,
MarketDataUpdateFieldsFragment,
MarketDataUpdateSubscriptionVariables,
} from './__generated__/market-data';
import { MarketDataUpdateDocument } from './__generated__/market-data';
import { MarketsDataDocument } from './__generated__/markets-data'; import { MarketsDataDocument } from './__generated__/markets-data';
import type { MarketData } from './market-data-provider'; import type { MarketData } from './market-data-provider';
const getData = (responseData: MarketsDataQuery | null): MarketData[] => const getData = (responseData: MarketsDataQuery | null): MarketData[] | null =>
responseData?.marketsConnection?.edges responseData?.marketsConnection?.edges
.filter((edge) => edge.node.data) .filter((edge) => edge.node.data)
.map((edge) => edge.node.data as MarketData) || []; .map((edge) => edge.node.data as MarketData) || null;
export const mapMarketDataUpdateToMarketData = (
delta: MarketDataUpdateFieldsFragment
): MarketData => {
const { marketId, __typename, ...marketData } = delta;
return { ...marketData, market: { id: marketId } };
};
const update = (
data: MarketData[] | null,
delta: MarketDataUpdateFieldsFragment
) => {
const updatedData = data ? [...data] : [];
const item = mapMarketDataUpdateToMarketData(delta);
const index = updatedData.findIndex(
(data) => data.market.id === item.market.id
);
if (index !== -1) {
updatedData[index] = { ...updatedData[index], ...item };
} else {
updatedData.push(item);
}
return updatedData;
};
const getDelta = (
subscriptionData: MarketDataUpdateSubscription
): MarketDataUpdateFieldsFragment => subscriptionData.marketsData[0];
export const marketsDataProvider = makeDataProvider< export const marketsDataProvider = makeDataProvider<
MarketsDataQuery, MarketsDataQuery,
@@ -56,25 +19,3 @@ export const marketsDataProvider = makeDataProvider<
getData, getData,
errorPolicyGuard: marketDataErrorPolicyGuard, errorPolicyGuard: marketDataErrorPolicyGuard,
}); });
type Variables = { marketIds: string[] };
export const marketsLiveDataProvider = makeDataProvider<
MarketsDataQuery,
MarketData[],
MarketDataUpdateSubscription,
MarketDataUpdateFieldsFragment,
Variables,
MarketDataUpdateSubscriptionVariables,
MarketsDataQueryVariables
>({
query: MarketsDataDocument,
subscriptionQuery: MarketDataUpdateDocument,
getData,
getDelta,
update,
errorPolicyGuard: marketDataErrorPolicyGuard,
getQueryVariables: () => ({}),
getSubscriptionVariables: ({ marketIds }: Variables) =>
marketIds.map((marketId) => ({ marketId })),
});
+1 -44
View File
@@ -10,15 +10,10 @@ import type {
} from './__generated__/markets'; } from './__generated__/markets';
import type { MarketsCandlesQueryVariables } from './__generated__/markets-candles'; import type { MarketsCandlesQueryVariables } from './__generated__/markets-candles';
import { import { marketsDataProvider } from './markets-data-provider';
marketsDataProvider,
marketsLiveDataProvider,
mapMarketDataUpdateToMarketData,
} from './markets-data-provider';
import { marketDataProvider } from './market-data-provider'; import { marketDataProvider } from './market-data-provider';
import { marketsCandlesProvider } from './markets-candles-provider'; import { marketsCandlesProvider } from './markets-candles-provider';
import type { MarketData } from './market-data-provider'; import type { MarketData } from './market-data-provider';
import type { MarketDataUpdateFieldsFragment } from './__generated__';
import type { MarketCandles } from './markets-candles-provider'; import type { MarketCandles } from './markets-candles-provider';
import { useMemo } from 'react'; import { useMemo } from 'react';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
@@ -165,44 +160,6 @@ export const allMarketsWithDataProvider = makeDerivedDataProvider<
addData(parts[0] as Market[], parts[1] as MarketData[]) addData(parts[0] as Market[], parts[1] as MarketData[])
); );
export const allMarketsWithLiveDataProvider = makeDerivedDataProvider<
MarketMaybeWithData[],
MarketMaybeWithData,
{ marketIds: string[] }
>(
[
(callback, client, variables) =>
marketsProvider(callback, client, undefined),
marketsLiveDataProvider,
],
(partsData, variables, prevData, parts) => {
if (prevData && parts[1].isUpdate) {
const data = mapMarketDataUpdateToMarketData(parts[1].delta);
const index = prevData.findIndex(
(market) => market.id === data.market.id
);
if (index !== -1) {
const updatedData = [...prevData];
updatedData[index] = { ...updatedData[index], data };
return updatedData;
} else {
return prevData;
}
}
return addData(partsData[0] as Market[], partsData[1] as MarketData[]);
},
(data, parts) => {
if (!parts[1].isUpdate && parts[1].delta) {
return;
}
return data.find(
(market) =>
market.id ===
(parts[1].delta as MarketDataUpdateFieldsFragment).marketId
);
}
);
export type MarketMaybeWithDataAndCandles = MarketMaybeWithData & export type MarketMaybeWithDataAndCandles = MarketMaybeWithData &
MarketMaybeWithCandles; MarketMaybeWithCandles;
+1
View File
@@ -1,4 +1,5 @@
export * from './order-data-provider'; export * from './order-data-provider';
export * from './order-list'; export * from './order-list';
export * from './order-list-manager'; export * from './order-list-manager';
export * from './order-list-container';
export * from './mocks/generate-orders'; export * from './mocks/generate-orders';
@@ -0,0 +1,42 @@
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import type { Filter } from './order-list-manager';
import { OrderListManager } from './order-list-manager';
export interface OrderListContainerProps {
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
enforceBottomPlaceholder?: boolean;
filter?: Filter;
storeKey?: string;
}
export const OrderListContainer = ({
marketId,
onMarketClick,
onOrderTypeClick,
enforceBottomPlaceholder,
filter,
storeKey,
}: OrderListContainerProps) => {
const { pubKey, isReadOnly } = useVegaWallet();
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
}
return (
<OrderListManager
partyId={pubKey}
marketId={marketId}
filter={filter}
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
isReadOnly={isReadOnly}
enforceBottomPlaceholder={enforceBottomPlaceholder}
storeKey={storeKey}
/>
);
};
@@ -2,9 +2,11 @@ import { t } from '@vegaprotocol/i18n';
import { useCallback, useRef, useState } from 'react'; import { useCallback, useRef, useState } from 'react';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import type { GridReadyEvent, FilterChangedEvent } from 'ag-grid-community';
import { OrderListTable } from '../order-list/order-list'; import { OrderListTable } from '../order-list/order-list';
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order'; import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
import type { useDataGridEvents } from '@vegaprotocol/datagrid'; import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider'; import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
import { import {
@@ -14,31 +16,59 @@ import {
import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet'; import type { OrderTxUpdateFieldsFragment } from '@vegaprotocol/wallet';
import { OrderEditDialog } from '../order-list/order-edit-dialog'; import { OrderEditDialog } from '../order-list/order-edit-dialog';
import type { Order } from '../order-data-provider'; import type { Order } from '../order-data-provider';
import { OrderStatus } from '@vegaprotocol/types';
export enum Filter { export enum Filter {
'Open' = 'Open', 'Open',
'Closed' = 'Closed', 'Closed',
'Rejected' = 'Rejected', 'Rejected',
} }
const FilterStatusValue = {
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
[Filter.Closed]: [
OrderStatus.STATUS_CANCELLED,
OrderStatus.STATUS_EXPIRED,
OrderStatus.STATUS_FILLED,
OrderStatus.STATUS_PARTIALLY_FILLED,
OrderStatus.STATUS_STOPPED,
],
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
};
export interface OrderListManagerProps { export interface OrderListManagerProps {
partyId: string; partyId: string;
marketId?: string; marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void; onMarketClick?: (marketId: string, metaKey?: boolean) => void;
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void; onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
isReadOnly: boolean; isReadOnly: boolean;
enforceBottomPlaceholder?: boolean;
filter?: Filter; filter?: Filter;
gridProps?: ReturnType<typeof useDataGridEvents>; storeKey?: string;
} }
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
<div className="dark:bg-black/75 bg-white/75 h-auto flex justify-end px-[11px] py-2 absolute bottom-0 right-3 rounded">
<Button
variant="primary"
size="sm"
onClick={onClick}
data-testid="cancelAll"
>
{t('Cancel all')}
</Button>
</div>
);
export const OrderListManager = ({ export const OrderListManager = ({
partyId, partyId,
marketId, marketId,
onMarketClick, onMarketClick,
onOrderTypeClick, onOrderTypeClick,
isReadOnly, isReadOnly,
enforceBottomPlaceholder,
filter, filter,
gridProps, storeKey,
}: OrderListManagerProps) => { }: OrderListManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null); const gridRef = useRef<AgGridReact | null>(null);
const [editOrder, setEditOrder] = useState<Order | null>(null); const [editOrder, setEditOrder] = useState<Order | null>(null);
@@ -61,6 +91,14 @@ export const OrderListManager = ({
}, },
}); });
const {
onFilterChanged: bottomPlaceholderOnFilterChanged,
...bottomPlaceholderProps
} = useBottomPlaceholder({
gridRef,
disabled: !enforceBottomPlaceholder && !isReadOnly && !hasAmendableOrder,
});
const cancel = useCallback( const cancel = useCallback(
(order: Order) => { (order: Order) => {
if (!order.market) return; if (!order.market) return;
@@ -74,6 +112,26 @@ export const OrderListManager = ({
[create] [create]
); );
const onGridReady = useCallback(
({ api }: GridReadyEvent) => {
if (filter !== undefined) {
api.setFilterModel({
status: {
value: FilterStatusValue[filter],
},
});
}
},
[filter]
);
const onFilterChanged = useCallback(
(event: FilterChangedEvent) => {
bottomPlaceholderOnFilterChanged?.();
},
[bottomPlaceholderOnFilterChanged]
);
const cancelAll = useCallback(() => { const cancelAll = useCallback(() => {
create({ create({
orderCancellation: {}, orderCancellation: {},
@@ -84,17 +142,20 @@ export const OrderListManager = ({
<> <>
<div className="h-full relative"> <div className="h-full relative">
<OrderListTable <OrderListTable
rowData={data} rowData={data as Order[]}
ref={gridRef} ref={gridRef}
filter={filter} filter={filter}
onGridReady={onGridReady}
onCancel={cancel} onCancel={cancel}
onEdit={setEditOrder} onEdit={setEditOrder}
onMarketClick={onMarketClick} onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick} onOrderTypeClick={onOrderTypeClick}
onFilterChanged={onFilterChanged}
isReadOnly={isReadOnly} isReadOnly={isReadOnly}
storeKey={storeKey}
suppressAutoSize suppressAutoSize
overlayNoRowsTemplate={error ? error.message : t('No orders')} overlayNoRowsTemplate={error ? error.message : t('No orders')}
{...gridProps} {...bottomPlaceholderProps}
/> />
</div> </div>
{!isReadOnly && hasAmendableOrder && ( {!isReadOnly && hasAmendableOrder && (
@@ -137,16 +198,3 @@ export const OrderListManager = ({
</> </>
); );
}; };
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
<div className="dark:bg-black/75 bg-white/75 h-auto flex justify-end px-[11px] py-2 absolute bottom-0 right-3 rounded">
<Button
variant="primary"
size="sm"
onClick={onClick}
data-testid="cancelAll"
>
{t('Cancel all')}
</Button>
</div>
);
@@ -2,13 +2,14 @@ import {
addDecimalsFormatNumber, addDecimalsFormatNumber,
getDateTimeFormat, getDateTimeFormat,
isNumeric, isNumeric,
toBigNum,
} from '@vegaprotocol/utils'; } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import { ButtonLink } from '@vegaprotocol/ui-toolkit'; import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react';
import BigNumber from 'bignumber.js';
import type { ForwardedRef } from 'react'; import type { ForwardedRef } from 'react';
import { memo, forwardRef, useMemo } from 'react'; import { memo, forwardRef } from 'react';
import { import {
AgGridLazy as AgGrid, AgGridLazy as AgGrid,
SetFilter, SetFilter,
@@ -23,13 +24,11 @@ import type {
TypedDataAgGrid, TypedDataAgGrid,
VegaICellRendererParams, VegaICellRendererParams,
VegaValueFormatterParams, VegaValueFormatterParams,
VegaValueGetterParams,
} from '@vegaprotocol/datagrid'; } from '@vegaprotocol/datagrid';
import type { AgGridReact } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react';
import type { Order } from '../order-data-provider'; import type { Order } from '../order-data-provider';
import { OrderActionsDropdown } from '../order-actions-dropdown'; import { OrderActionsDropdown } from '../order-actions-dropdown';
import { Filter } from '../order-list-manager'; import { Filter } from '../order-list-manager';
import type { ColDef } from 'ag-grid-community';
export type OrderListTableProps = TypedDataAgGrid<Order> & { export type OrderListTableProps = TypedDataAgGrid<Order> & {
marketId?: string; marketId?: string;
@@ -39,6 +38,7 @@ export type OrderListTableProps = TypedDataAgGrid<Order> & {
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void; onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
filter?: Filter; filter?: Filter;
isReadOnly: boolean; isReadOnly: boolean;
storeKey?: string;
}; };
export const OrderListTable = memo< export const OrderListTable = memo<
@@ -54,40 +54,49 @@ export const OrderListTable = memo<
: filter === undefined || filter === Filter.Open : filter === undefined || filter === Filter.Open
? true ? true
: false; : false;
const columnDefs: ColDef[] = useMemo(
() => [ return (
{ <AgGrid
headerName: t('Market'), ref={ref}
field: 'market.tradableInstrument.instrument.code', defaultColDef={{
cellRenderer: 'MarketNameCell', resizable: true,
cellRendererParams: { idPath: 'market.id', onMarketClick }, sortable: true,
minWidth: 150, filterParams: { buttons: ['reset'] },
}, }}
{ style={{
headerName: t('Size'), width: '100%',
field: 'size', height: '100%',
cellClass: 'font-mono text-right', }}
type: 'rightAligned', getRowId={({ data }) => data.id}
cellClassRules: { components={{ MarketNameCell, OrderTypeCell }}
{...props}
>
<AgGridColumn
headerName={t('Market')}
field="market.tradableInstrument.instrument.code"
cellRenderer="MarketNameCell"
cellRendererParams={{ idPath: 'market.id', onMarketClick }}
minWidth={150}
/>
<AgGridColumn
headerName={t('Size')}
field="size"
cellClass="font-mono text-right"
type="rightAligned"
cellClassRules={{
[positiveClassNames]: ({ data }: { data: Order }) => [positiveClassNames]: ({ data }: { data: Order }) =>
data?.side === Schema.Side.SIDE_BUY, data?.side === Schema.Side.SIDE_BUY,
[negativeClassNames]: ({ data }: { data: Order }) => [negativeClassNames]: ({ data }: { data: Order }) =>
data?.side === Schema.Side.SIDE_SELL, data?.side === Schema.Side.SIDE_SELL,
}, }}
valueGetter: ({ data }: VegaValueGetterParams<Order>) => { valueFormatter={({
return data?.size && data.market value,
? toBigNum(data.size, data.market.positionDecimalPlaces ?? 0)
.multipliedBy(data.side === Schema.Side.SIDE_SELL ? -1 : 1)
.toNumber()
: undefined;
},
valueFormatter: ({
data, data,
}: VegaValueFormatterParams<Order, 'size'>) => { }: VegaValueFormatterParams<Order, 'size'>) => {
if (!data) { if (!data) {
return ''; return undefined;
} }
if (!data?.market || !isNumeric(data.size)) { if (!data?.market || !isNumeric(value)) {
return '-'; return '-';
} }
const prefix = data const prefix = data
@@ -98,33 +107,33 @@ export const OrderListTable = memo<
return ( return (
prefix + prefix +
addDecimalsFormatNumber( addDecimalsFormatNumber(
data.size, value,
data.market.positionDecimalPlaces data.market.positionDecimalPlaces
) )
); );
}, }}
minWidth: 80, minWidth={80}
}, />
{ <AgGridColumn
field: 'type', field="type"
filter: SetFilter, filter={SetFilter}
filterParams: { filterParams={{
set: Schema.OrderTypeMapping, set: Schema.OrderTypeMapping,
}, }}
cellRenderer: 'OrderTypeCell', cellRenderer="OrderTypeCell"
cellRendererParams: { cellRendererParams={{
onClick: onOrderTypeClick, onClick: onOrderTypeClick,
}, }}
minWidth: 80, minWidth={80}
}, />
{ <AgGridColumn
field: 'status', field="status"
filter: SetFilter, filter={SetFilter}
filterParams: { filterParams={{
set: Schema.OrderStatusMapping, set: Schema.OrderStatusMapping,
readonly: filter !== undefined, readonly: filter !== undefined,
}, }}
valueFormatter: ({ valueFormatter={({
value, value,
data, data,
}: VegaValueFormatterParams<Order, 'status'>) => { }: VegaValueFormatterParams<Order, 'status'>) => {
@@ -136,8 +145,8 @@ export const OrderListTable = memo<
}`; }`;
} }
return value ? Schema.OrderStatusMapping[value] : ''; return value ? Schema.OrderStatusMapping[value] : '';
}, }}
cellRenderer: ({ cellRenderer={({
valueFormatted, valueFormatted,
data, data,
}: { }: {
@@ -147,51 +156,45 @@ export const OrderListTable = memo<
<span data-testid={`order-status-${data?.id}`}> <span data-testid={`order-status-${data?.id}`}>
{valueFormatted} {valueFormatted}
</span> </span>
), )}
minWidth: 100, minWidth={100}
}, />
{ <AgGridColumn
headerName: t('Filled'), headerName={t('Filled')}
field: 'remaining', field="remaining"
cellClass: 'font-mono text-right', cellClass="font-mono text-right"
type: 'rightAligned', type="rightAligned"
valueGetter: ({ data }: VegaValueGetterParams<Order>) => { valueFormatter={({
return data?.size && data.market
? toBigNum(
(BigInt(data.size) - BigInt(data.remaining)).toString(),
data.market.positionDecimalPlaces ?? 0
).toNumber()
: undefined;
},
valueFormatter: ({
data, data,
value, value,
}: VegaValueFormatterParams<Order, 'remaining'>): string => { }: VegaValueFormatterParams<Order, 'remaining'>) => {
if (!data) { if (!data) {
return ''; return undefined;
} }
if (!data?.market || !isNumeric(value) || !isNumeric(data.size)) { if (!data?.market || !isNumeric(value) || !isNumeric(data.size)) {
return '-'; return '-';
} }
const { positionDecimalPlaces } = data.market; const dps = data.market.positionDecimalPlaces;
const filled = BigInt(data.size) - BigInt(data.remaining); const size = new BigNumber(data.size);
const remaining = new BigNumber(value);
const fills = size.minus(remaining);
return `${addDecimalsFormatNumber( return `${addDecimalsFormatNumber(
filled.toString(), fills.toString(),
positionDecimalPlaces dps
)}/${addDecimalsFormatNumber(data.size, positionDecimalPlaces)}`; )}/${addDecimalsFormatNumber(size.toString(), dps)}`;
}, }}
minWidth: 100, minWidth={100}
}, />
{ <AgGridColumn
field: 'price', field="price"
type: 'rightAligned', type="rightAligned"
cellClass: 'font-mono text-right', cellClass="font-mono text-right"
valueFormatter: ({ valueFormatter={({
value, value,
data, data,
}: VegaValueFormatterParams<Order, 'price'>) => { }: VegaValueFormatterParams<Order, 'price'>) => {
if (!data) { if (!data) {
return ''; return undefined;
} }
if ( if (
!data?.market || !data?.market ||
@@ -201,16 +204,16 @@ export const OrderListTable = memo<
return '-'; return '-';
} }
return addDecimalsFormatNumber(value, data.market.decimalPlaces); return addDecimalsFormatNumber(value, data.market.decimalPlaces);
}, }}
minWidth: 100, minWidth={100}
}, />
{ <AgGridColumn
field: 'timeInForce', field="timeInForce"
filter: SetFilter, filter={SetFilter}
filterParams: { filterParams={{
set: Schema.OrderTimeInForceMapping, set: Schema.OrderTimeInForceMapping,
}, }}
valueFormatter: ({ valueFormatter={({
value, value,
data, data,
}: VegaValueFormatterParams<Order, 'timeInForce'>) => { }: VegaValueFormatterParams<Order, 'timeInForce'>) => {
@@ -232,13 +235,13 @@ export const OrderListTable = memo<
}${data?.reduceOnly ? t('. Reduce only') : ''}`; }${data?.reduceOnly ? t('. Reduce only') : ''}`;
return label; return label;
}, }}
minWidth: 150, minWidth={150}
}, />
{ <AgGridColumn
field: 'createdAt', field="createdAt"
filter: DateRangeFilter, filter={DateRangeFilter}
cellRenderer: ({ cellRenderer={({
value, value,
}: VegaICellRendererParams<Order, 'createdAt'>) => { }: VegaICellRendererParams<Order, 'createdAt'>) => {
return ( return (
@@ -246,12 +249,12 @@ export const OrderListTable = memo<
{value ? getDateTimeFormat().format(new Date(value)) : value} {value ? getDateTimeFormat().format(new Date(value)) : value}
</span> </span>
); );
}, }}
minWidth: 150, minWidth={150}
}, />
{ <AgGridColumn
field: 'updatedAt', field="updatedAt"
cellRenderer: ({ cellRenderer={({
data, data,
value, value,
}: VegaICellRendererParams<Order, 'updatedAt'>) => { }: VegaICellRendererParams<Order, 'updatedAt'>) => {
@@ -263,15 +266,15 @@ export const OrderListTable = memo<
{value ? getDateTimeFormat().format(new Date(value)) : '-'} {value ? getDateTimeFormat().format(new Date(value)) : '-'}
</span> </span>
); );
}, }}
minWidth: 150, minWidth={150}
}, />
{ <AgGridColumn
colId: 'amend', colId="amend"
...COL_DEFS.actions, {...COL_DEFS.actions}
minWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth, minWidth={showAllActions ? 120 : COL_DEFS.actions.minWidth}
maxWidth: showAllActions ? 120 : COL_DEFS.actions.minWidth, maxWidth={showAllActions ? 120 : COL_DEFS.actions.minWidth}
cellRenderer: ({ data }: { data?: Order }) => { cellRenderer={({ data }: { data?: Order }) => {
if (!data) return null; if (!data) return null;
return ( return (
@@ -295,37 +298,9 @@ export const OrderListTable = memo<
<OrderActionsDropdown id={data?.id} /> <OrderActionsDropdown id={data?.id} />
</div> </div>
); );
}, }}
}, />
], </AgGrid>
[
filter,
onCancel,
onEdit,
onMarketClick,
onOrderTypeClick,
props.isReadOnly,
showAllActions,
]
);
return (
<AgGrid
ref={ref}
defaultColDef={{
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
}}
columnDefs={columnDefs}
style={{
width: '100%',
height: '100%',
}}
getRowId={({ data }) => data.id}
components={{ MarketNameCell, OrderTypeCell }}
{...props}
/>
); );
} }
) )
+2 -1
View File
@@ -1,6 +1,7 @@
export * from './lib/__generated__/Positions'; export * from './lib/__generated__/Positions';
export * from './lib/positions-container';
export * from './lib/positions-data-providers'; export * from './lib/positions-data-providers';
export * from './lib/positions-table'; export * from './lib/positions-table';
export * from './lib/positions-manager';
export * from './lib/use-market-margin'; export * from './lib/use-market-margin';
export * from './lib/use-open-volume'; export * from './lib/use-open-volume';
export * from './lib/use-positions-data';

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