feat(577): view new market proposals (#2078)
* feat: market proposal selector * feat: market proposal selector * feat: market proposal selector * feat: market proposal selector * feat: market proposal selector - fix linters * feat: market proposal selector - add some int tests * feat: market proposal selector - add some unit tests * feat: market proposal selector - improve union type extracting * feat: market proposal selector - fix failing on develop e2e tests * feat: market proposal selector - fix failing on develop e2e tests * feat: market proposal selector - fix failing on develop e2e tests * feat: market proposal selector - fix failing on develop e2e tests * feat: market proposal selector - fix failing on develop e2e tests
This commit is contained in:
parent
d1b45a65a0
commit
52e1757d33
@ -8,7 +8,7 @@ export const generateChainId = (
|
||||
const defaultResult = {
|
||||
statistics: {
|
||||
__typename: 'Statistics',
|
||||
chainId: 'test-chain-id',
|
||||
chainId: Cypress.env('VEGA_ENV').toLowerCase() || 'test-chain-id',
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@ export const generateStatistics = (
|
||||
const defaultResult = {
|
||||
statistics: {
|
||||
__typename: 'Statistics',
|
||||
chainId: 'test-chain-id',
|
||||
chainId: Cypress.env('VEGA_ENV').toLowerCase() || 'test-chain-id',
|
||||
blockHeight: '11',
|
||||
},
|
||||
};
|
||||
|
@ -2,13 +2,13 @@ import { useTranslation } from 'react-i18next';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { VoteProgress } from '@vegaprotocol/governance';
|
||||
import { formatNumber } from '../../../../lib/format-number';
|
||||
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { useUserVote } from './use-user-vote';
|
||||
import { CurrentProposalStatus } from '../current-proposal-status';
|
||||
import { VoteButtonsContainer } from './vote-buttons';
|
||||
import { VoteProgress } from './vote-progress';
|
||||
import { ProposalType } from '../proposal/proposal';
|
||||
import type { Proposal_proposal } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
|
@ -60,6 +60,37 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
.should('have.text', ExpectedSortedMarkets[i]);
|
||||
}
|
||||
});
|
||||
|
||||
it('proposed markets tab should be rendered properly', () => {
|
||||
cy.getByTestId('view-market-list-link')
|
||||
.should('have.attr', 'href', '#/markets')
|
||||
.click();
|
||||
cy.get('[data-testid="Active markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'active'
|
||||
);
|
||||
cy.get('[data-testid="Proposed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'inactive'
|
||||
);
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
cy.get('[data-testid="Proposed markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'active'
|
||||
);
|
||||
cy.getByTestId('tab-proposed-markets').should('be.visible');
|
||||
cy.get('.ag-body-viewport .ag-center-cols-container .ag-row').should(
|
||||
'have.length',
|
||||
10
|
||||
);
|
||||
cy.getByTestId('external-link').should('have.length', 11);
|
||||
cy.getByTestId('external-link')
|
||||
.eq(10)
|
||||
.should('have.text', 'Propose a new market');
|
||||
});
|
||||
});
|
||||
|
||||
function openMarketDropDown() {
|
||||
|
@ -34,6 +34,14 @@ export const generateNetworkParameters = (
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
key: 'governance.proposal.market.requiredMajority',
|
||||
value: '0.66',
|
||||
__typename: 'NetworkParameter',
|
||||
},
|
||||
__typename: 'NetworkParameterEdge',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
625
apps/trading-e2e/src/support/mocks/generate-proposals.ts
Normal file
625
apps/trading-e2e/src/support/mocks/generate-proposals.ts
Normal file
@ -0,0 +1,625 @@
|
||||
import type { ProposalsListQuery } from '@vegaprotocol/governance';
|
||||
import { Schema } from '@vegaprotocol/types';
|
||||
|
||||
const marketProposals: ProposalsListQuery = {
|
||||
proposalsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: 'e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829',
|
||||
reference: 'injected_at_runtime',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
datetime: '2022-11-15T12:38:55.901696Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '20000000000000000000000000',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-15T12:44:34Z',
|
||||
enactmentDatetime: '2022-11-15T12:44:54Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'ETHUSD',
|
||||
name: 'ETHUSD',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '1cd1deb532b97fbeb9262fe94499ecec5835e60ae564b7c5af530c90a13c29cb',
|
||||
reference: 'injected_at_runtime',
|
||||
state: Schema.ProposalState.STATE_REJECTED,
|
||||
datetime: '2022-11-15T12:38:08.810603Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-15T12:39:41Z',
|
||||
enactmentDatetime: '2022-11-15T12:39:51Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'ETHUSD',
|
||||
name: 'ETHUSD',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'e503cadb437861037cddfd7263d25b69102098a97573db23f8e5fc320cea1ce9',
|
||||
reference: 'injected_at_runtime',
|
||||
state: Schema.ProposalState.STATE_PASSED,
|
||||
datetime: '2022-11-14T16:18:57.437675Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '10000000000000000000000000',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-14T16:24:24Z',
|
||||
enactmentDatetime: '2022-11-14T16:24:34Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'LINKUSD',
|
||||
name: 'LINKUSD',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
|
||||
name: 'mUSDT-II',
|
||||
symbol: 'mUSDT-II',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'e38415b453d862c246743fa979b877d97e2c9cbe160b6174e02c5589864817a0',
|
||||
reference: 'injected_at_runtime',
|
||||
state: Schema.ProposalState.STATE_REJECTED,
|
||||
datetime: '2022-11-14T16:17:09.605382Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:32:22Z',
|
||||
enactmentDatetime: '2022-11-11T16:32:32Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'LINKUSD',
|
||||
name: 'LINKUSD',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
|
||||
name: 'mUSDT-II',
|
||||
symbol: 'mUSDT-II',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'e3119d341022a401cc68ba3a7ead5c431028d0060b3a49fc115025d7784c646f',
|
||||
reference: 'injected_at_runtime',
|
||||
state: Schema.ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
datetime: '2022-11-14T09:35:54.040219Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '40000000000000000000000000',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-14T09:40:57Z',
|
||||
enactmentDatetime: '2022-11-14T09:41:17Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'ETHUSD',
|
||||
name: 'ETHUSD',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '07549b2cf93abbf6fb8ad976af3f24876e5946df85a3b6f0a6338672c7453bfa',
|
||||
reference: 'injected_at_runtime',
|
||||
state: Schema.ProposalState.STATE_ENACTED,
|
||||
datetime: '2022-11-11T16:29:23.46877Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:32:22Z',
|
||||
enactmentDatetime: '2022-11-11T16:32:32Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'LINKUSD',
|
||||
name: 'LINKUSD',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
|
||||
name: 'mUSDT-II',
|
||||
symbol: 'mUSDT-II',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'de48ad0adc668a80a074e790d118b6a64fa4909fc60ebbc2c335be3b675fec93',
|
||||
reference: 'zMSg0drbaFnoM1kF8YdsbH28poqdsJr33rYwyETv',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
datetime: '2022-11-11T16:27:05.914266Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:28:25Z',
|
||||
enactmentDatetime: '2022-11-11T16:30:35Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'ETHDAI.MF21',
|
||||
name: 'ETHDAI Monthly (Dec 2022)',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'c80c1cc89dcc5a302e443b7fcd215cdadbb259f6b180db6253a5572ad6406253',
|
||||
reference: 'NlotxzM5Jg3LfEN1vYCxxQ5wcmUwfWAqQQtad3Se',
|
||||
state: Schema.ProposalState.STATE_PASSED,
|
||||
datetime: '2022-11-11T16:27:05.914266Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '20000000000000000000000000',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:28:25Z',
|
||||
enactmentDatetime: '2022-11-11T16:30:35Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'AAPL.MF21',
|
||||
name: 'Apple Monthly (Dec 2022)',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d',
|
||||
name: 'tUSDC TEST',
|
||||
symbol: 'tUSDC',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'ad2e531441c2e8a43e85423db399a4acc8f9a8a2376304a4c377d0da8eb31e80',
|
||||
reference: 'rVcSDJYfk3Ni6yi9ZwVcYXFaSA6miUZucHdzdGCv',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
datetime: '2022-11-11T16:27:05.220442Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '35000000000000000000000000',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:28:25Z',
|
||||
enactmentDatetime: '2022-11-11T16:30:35Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'BTCUSD.MF21',
|
||||
name: 'BTCUSD Monthly (Dec 2022)',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: 'acff1b95980c3355e65c7162c35917388702525c0475ba753f03d0efc9c6d6a3',
|
||||
reference: 'EMRU5vhlhKUFBx9D2sr4KWSS4PZhNhWEbRr8SMf7',
|
||||
state: Schema.ProposalState.STATE_PASSED,
|
||||
datetime: '2022-11-11T16:27:06.008336Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '0',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:28:25Z',
|
||||
enactmentDatetime: '2022-11-11T16:30:35Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'TSLA.QM21',
|
||||
name: 'Tesla Quarterly (Feb 2023)',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: '177e8f6c25a955bd18475084b99b2b1d37f28f3dec393fab7755a7e69c3d8c3b',
|
||||
name: 'tEURO TEST',
|
||||
symbol: 'tEURO',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '90e71c52b2f40db78efc24abe4217382993868cd24e45b3dd17147be4afaf884',
|
||||
reference: '1HpOzeaQhW3OgLlMS4uFIzrOJcK0zaGMN2ccWpF7',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
datetime: '2022-11-11T16:27:05.861888Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '22000000000000000000000000',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:28:25Z',
|
||||
enactmentDatetime: '2022-11-11T16:30:35Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'AAVEDAI.MF21',
|
||||
name: 'AAVEDAI Monthly (Dec 2022)',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '8b4aaea9cf7cbbeee90aa6ccea21bd5b9ec15c1872d6b0b3a58e31b741dd948a',
|
||||
reference: '9xlda7c2xrBTDVMNvsDdTSeVJqgCgvN3Uea7WPEN',
|
||||
state: Schema.ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
datetime: '2022-11-11T16:27:06.076146Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '12345600000000000000000000',
|
||||
totalNumber: '0',
|
||||
totalWeight: '0',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:28:25Z',
|
||||
enactmentDatetime: '2022-11-11T16:30:35Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'ETHBTC.QM21',
|
||||
name: 'ETHBTC Quarterly (Feb 2023)',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'cee709223217281d7893b650850ae8ee8a18b7539b5658f9b4cc24de95dd18ad',
|
||||
name: 'tBTC TEST',
|
||||
symbol: 'tBTC',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '325dfa07e1be5192376616241d23b4d71740fe712e298130bfd35d27738f1ce4',
|
||||
reference: 'WrmmmaQE4j70M0Wauh85oNpKOA1Lp8omIcn07DLS',
|
||||
state: Schema.ProposalState.STATE_OPEN,
|
||||
datetime: '2022-11-11T16:27:05.914266Z',
|
||||
votes: {
|
||||
yes: {
|
||||
totalTokens: '10000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '50000000000000000000000000',
|
||||
totalNumber: '1',
|
||||
totalWeight: '1',
|
||||
__typename: 'ProposalVoteSide',
|
||||
},
|
||||
__typename: 'ProposalVotes',
|
||||
},
|
||||
terms: {
|
||||
closingDatetime: '2022-11-11T16:28:25Z',
|
||||
enactmentDatetime: '2022-11-11T16:30:35Z',
|
||||
change: {
|
||||
instrument: {
|
||||
code: 'UNIDAI.MF21',
|
||||
name: 'UNIDAI Monthly (Dec 2022)',
|
||||
futureProduct: {
|
||||
settlementAsset: {
|
||||
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
__typename: 'Asset',
|
||||
},
|
||||
__typename: 'FutureProduct',
|
||||
},
|
||||
__typename: 'InstrumentConfiguration',
|
||||
},
|
||||
__typename: 'NewMarket',
|
||||
},
|
||||
__typename: 'ProposalTerms',
|
||||
},
|
||||
__typename: 'Proposal',
|
||||
},
|
||||
__typename: 'ProposalEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'ProposalsConnection',
|
||||
},
|
||||
};
|
||||
|
||||
export const generateMarketProposals = () => {
|
||||
return { ...marketProposals };
|
||||
};
|
@ -27,6 +27,7 @@ import {
|
||||
generatePartyBalance,
|
||||
generatePartyMarketData,
|
||||
} from './mocks/generate-fees';
|
||||
import { generateMarketProposals } from './mocks/generate-proposals';
|
||||
|
||||
const mockTradingPage = (
|
||||
req: CyHttpMessages.IncomingHttpRequest,
|
||||
@ -106,6 +107,7 @@ const mockTradingPage = (
|
||||
aliasQuery(req, 'PartyBalance', generatePartyBalance());
|
||||
aliasQuery(req, 'MarketPositions', generatePositions());
|
||||
aliasQuery(req, 'PartyMarketData', generatePartyMarketData());
|
||||
aliasQuery(req, 'ProposalsList', generateMarketProposals());
|
||||
};
|
||||
|
||||
declare global {
|
||||
|
@ -1,3 +1,3 @@
|
||||
import { Markets } from './markets';
|
||||
import { MarketsPage } from './markets-page';
|
||||
|
||||
export default Markets;
|
||||
export default MarketsPage;
|
||||
|
25
apps/trading/client-pages/markets/markets-page.tsx
Normal file
25
apps/trading/client-pages/markets/markets-page.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { t, titlefy } from '@vegaprotocol/react-helpers';
|
||||
import { Tabs, Tab } from '@vegaprotocol/ui-toolkit';
|
||||
import { Markets } from './markets';
|
||||
import { Proposed } from './proposed';
|
||||
import { usePageTitleStore } from '../../stores';
|
||||
|
||||
export const MarketsPage = () => {
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
useEffect(() => {
|
||||
updateTitle(titlefy(['Markets']));
|
||||
}, [updateTitle]);
|
||||
return (
|
||||
<Tabs>
|
||||
<Tab id="active-markets" name={t('Active markets')}>
|
||||
<Markets />
|
||||
</Tab>
|
||||
<Tab id="proposed-markets" name={t('Proposed markets')}>
|
||||
<Proposed />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
@ -1,25 +1,18 @@
|
||||
import { useCallback } from 'react';
|
||||
import { MarketsContainer } from '@vegaprotocol/market-list';
|
||||
import { useGlobalStore, usePageTitleStore } from '../../stores';
|
||||
import { useEffect } from 'react';
|
||||
import { titlefy } from '@vegaprotocol/react-helpers';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
export const Markets = () => {
|
||||
const navigate = useNavigate();
|
||||
const { update } = useGlobalStore((store) => ({ update: store.update }));
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
useEffect(() => {
|
||||
updateTitle(titlefy(['Markets']));
|
||||
}, [updateTitle]);
|
||||
|
||||
return (
|
||||
<MarketsContainer
|
||||
onSelect={(marketId) => {
|
||||
update({ marketId });
|
||||
navigate(`/markets/${marketId}`);
|
||||
}}
|
||||
/>
|
||||
const handleOnSelect = useCallback(
|
||||
(marketId: string) => {
|
||||
update({ marketId });
|
||||
navigate(`/markets/${marketId}`);
|
||||
},
|
||||
[update, navigate]
|
||||
);
|
||||
|
||||
return <MarketsContainer onSelect={handleOnSelect} />;
|
||||
};
|
||||
|
23
apps/trading/client-pages/markets/proposed.tsx
Normal file
23
apps/trading/client-pages/markets/proposed.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ProposalsList } from '@vegaprotocol/governance';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
NEW_PROPOSAL_LINK,
|
||||
TOKEN_DEFAULT_DOMAIN,
|
||||
} from '../../components/constants';
|
||||
|
||||
export const Proposed = () => {
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
const externalLink = `${
|
||||
VEGA_TOKEN_URL || TOKEN_DEFAULT_DOMAIN
|
||||
}${NEW_PROPOSAL_LINK}`;
|
||||
return (
|
||||
<>
|
||||
<ProposalsList />
|
||||
<ExternalLink className="py-4 px-[11px]" href={externalLink}>
|
||||
{t('Propose a new market')}
|
||||
</ExternalLink>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1 +1,3 @@
|
||||
export const DEBOUNCE_UPDATE_TIME = 500;
|
||||
export const TOKEN_DEFAULT_DOMAIN = 'https://token.fairground.wtf';
|
||||
export const NEW_PROPOSAL_LINK = '/governance/propose';
|
||||
|
@ -1,3 +1,6 @@
|
||||
export * from './proposals-hooks';
|
||||
export * from './proposals-queries';
|
||||
export * from './voting-hooks';
|
||||
export * from './proposals-data-provider';
|
||||
export * from './proposals-list';
|
||||
export * from './voting-progress';
|
||||
|
@ -0,0 +1,51 @@
|
||||
fragment NewMarketFields on NewMarket {
|
||||
instrument {
|
||||
code
|
||||
name
|
||||
futureProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment ProposalListFields on Proposal {
|
||||
id
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
votes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalWeight
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalWeight
|
||||
}
|
||||
}
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
...NewMarketFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ProposalsList($proposalType: ProposalType, $inState: ProposalState) {
|
||||
proposalsConnection(proposalType: $proposalType, inState: $inState) {
|
||||
edges {
|
||||
node {
|
||||
...ProposalListFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
import { Schema as Types } from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type NewMarketFieldsFragment = { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } };
|
||||
|
||||
export type ProposalListFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: string, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: string, enactmentDatetime?: string | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter' } } };
|
||||
|
||||
export type ProposalsListQueryVariables = Types.Exact<{
|
||||
proposalType?: Types.InputMaybe<Types.ProposalType>;
|
||||
inState?: Types.InputMaybe<Types.ProposalState>;
|
||||
}>;
|
||||
|
||||
|
||||
export type ProposalsListQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: string, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalWeight: string } }, terms: { __typename?: 'ProposalTerms', closingDatetime: string, enactmentDatetime?: string | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', code: string, name: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', id: string, name: string, symbol: string } } | null } } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter' } } } } | null> | null } | null };
|
||||
|
||||
export const NewMarketFieldsFragmentDoc = gql`
|
||||
fragment NewMarketFields on NewMarket {
|
||||
instrument {
|
||||
code
|
||||
name
|
||||
futureProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ProposalListFieldsFragmentDoc = gql`
|
||||
fragment ProposalListFields on Proposal {
|
||||
id
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
votes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalWeight
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalWeight
|
||||
}
|
||||
}
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
...NewMarketFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${NewMarketFieldsFragmentDoc}`;
|
||||
export const ProposalsListDocument = gql`
|
||||
query ProposalsList($proposalType: ProposalType, $inState: ProposalState) {
|
||||
proposalsConnection(proposalType: $proposalType, inState: $inState) {
|
||||
edges {
|
||||
node {
|
||||
...ProposalListFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${ProposalListFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useProposalsListQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useProposalsListQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useProposalsListQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useProposalsListQuery({
|
||||
* variables: {
|
||||
* proposalType: // value for 'proposalType'
|
||||
* inState: // value for 'inState'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useProposalsListQuery(baseOptions?: Apollo.QueryHookOptions<ProposalsListQuery, ProposalsListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ProposalsListQuery, ProposalsListQueryVariables>(ProposalsListDocument, options);
|
||||
}
|
||||
export function useProposalsListLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ProposalsListQuery, ProposalsListQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ProposalsListQuery, ProposalsListQueryVariables>(ProposalsListDocument, options);
|
||||
}
|
||||
export type ProposalsListQueryHookResult = ReturnType<typeof useProposalsListQuery>;
|
||||
export type ProposalsListLazyQueryHookResult = ReturnType<typeof useProposalsListLazyQuery>;
|
||||
export type ProposalsListQueryResult = Apollo.QueryResult<ProposalsListQuery, ProposalsListQueryVariables>;
|
2
libs/governance/src/lib/proposals-data-provider/index.ts
Normal file
2
libs/governance/src/lib/proposals-data-provider/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './proposals-data-provider';
|
||||
export * from './__generated___/Proposals';
|
@ -0,0 +1,18 @@
|
||||
import { makeDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
ProposalsListQuery,
|
||||
ProposalListFieldsFragment,
|
||||
} from './__generated___/Proposals';
|
||||
import { ProposalsListDocument } from './__generated___/Proposals';
|
||||
|
||||
const getData = (responseData: ProposalsListQuery) =>
|
||||
responseData.proposalsConnection?.edges
|
||||
?.filter((edge) => Boolean(edge?.node))
|
||||
.map((edge) => edge?.node as ProposalListFieldsFragment) || null;
|
||||
|
||||
export const proposalsListDataProvider = makeDataProvider<
|
||||
ProposalsListQuery,
|
||||
ProposalListFieldsFragment[],
|
||||
never,
|
||||
never
|
||||
>({ query: ProposalsListDocument, getData });
|
1
libs/governance/src/lib/proposals-list/index.ts
Normal file
1
libs/governance/src/lib/proposals-list/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './proposals-list';
|
@ -0,0 +1,96 @@
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
act,
|
||||
waitFor,
|
||||
getAllByRole,
|
||||
} from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { ProposalsList } from './proposals-list';
|
||||
import type { ProposalListFieldsFragment } from '../proposals-data-provider';
|
||||
import { Schema as Types } from '@vegaprotocol/types';
|
||||
|
||||
const votesMock = {
|
||||
yes: {
|
||||
totalTokens: '5000',
|
||||
},
|
||||
no: {
|
||||
totalTokens: '2000',
|
||||
},
|
||||
};
|
||||
let marketsProposalMock: ProposalListFieldsFragment[] | null = [
|
||||
{
|
||||
id: 'id-1',
|
||||
state: Types.ProposalState.STATE_OPEN,
|
||||
votes: { ...votesMock },
|
||||
},
|
||||
{
|
||||
id: 'id-2',
|
||||
state: Types.ProposalState.STATE_PASSED,
|
||||
votes: { ...votesMock },
|
||||
},
|
||||
{
|
||||
id: 'id-3',
|
||||
state: Types.ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
votes: { ...votesMock },
|
||||
},
|
||||
] as ProposalListFieldsFragment[];
|
||||
|
||||
const useDataProvider = () => {
|
||||
return {
|
||||
data: marketsProposalMock,
|
||||
loading: false,
|
||||
error: false,
|
||||
};
|
||||
};
|
||||
jest.mock('@vegaprotocol/react-helpers', () => ({
|
||||
...jest.requireActual('@vegaprotocol/react-helpers'),
|
||||
useDataProvider: jest.fn(() => useDataProvider()),
|
||||
}));
|
||||
|
||||
describe('ProposalsList', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should be properly rendered', async () => {
|
||||
await act(() => {
|
||||
render(<ProposalsList />, { wrapper: MockedProvider });
|
||||
});
|
||||
const container = document.querySelector('.ag-center-cols-container');
|
||||
await waitFor(() => {
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('some of states should be filtered out', async () => {
|
||||
marketsProposalMock = [
|
||||
{
|
||||
...(marketsProposalMock as ProposalListFieldsFragment[])[0],
|
||||
state: Types.ProposalState.STATE_ENACTED,
|
||||
},
|
||||
...(marketsProposalMock as ProposalListFieldsFragment[]).slice(1),
|
||||
];
|
||||
await act(() => {
|
||||
render(<ProposalsList />, { wrapper: MockedProvider });
|
||||
});
|
||||
const container = document.querySelector('.ag-center-cols-container');
|
||||
await waitFor(() => {
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('empty response should causes no data message display', async () => {
|
||||
marketsProposalMock = null;
|
||||
await act(() => {
|
||||
render(<ProposalsList />, { wrapper: MockedProvider });
|
||||
});
|
||||
const container = document.querySelector('.ag-center-cols-container');
|
||||
await waitFor(() => {
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('No Rows To Show')).toBeInTheDocument();
|
||||
});
|
||||
});
|
51
libs/governance/src/lib/proposals-list/proposals-list.tsx
Normal file
51
libs/governance/src/lib/proposals-list/proposals-list.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import {
|
||||
AgGridDynamic as AgGrid,
|
||||
AsyncRenderer,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { Schema as Types } from '@vegaprotocol/types';
|
||||
import { proposalsListDataProvider } from '../proposals-data-provider';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { useColumnDefs } from './use-column-defs';
|
||||
import type { ProposalListFieldsFragment } from '../proposals-data-provider/__generated___/Proposals';
|
||||
|
||||
export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) =>
|
||||
data.filter((proposal) =>
|
||||
[
|
||||
Types.ProposalState.STATE_OPEN,
|
||||
Types.ProposalState.STATE_PASSED,
|
||||
Types.ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
].includes(proposal.state)
|
||||
);
|
||||
|
||||
export const ProposalsList = () => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const handleOnGridReady = useCallback(() => {
|
||||
gridRef.current?.api?.sizeColumnsToFit();
|
||||
}, [gridRef]);
|
||||
const variables = useMemo(() => {
|
||||
return {
|
||||
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
|
||||
};
|
||||
}, []);
|
||||
const { data, loading, error } = useDataProvider({
|
||||
dataProvider: proposalsListDataProvider,
|
||||
variables,
|
||||
});
|
||||
const filteredData = getNewMarketProposals(data || []);
|
||||
const { columnDefs, defaultColDef } = useColumnDefs();
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={filteredData}>
|
||||
<AgGrid
|
||||
ref={gridRef}
|
||||
domLayout="autoHeight"
|
||||
className="min-w-full"
|
||||
columnDefs={columnDefs}
|
||||
rowData={filteredData}
|
||||
defaultColDef={defaultColDef}
|
||||
onGridReady={handleOnGridReady}
|
||||
/>
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
152
libs/governance/src/lib/proposals-list/use-column-defs.tsx
Normal file
152
libs/governance/src/lib/proposals-list/use-column-defs.tsx
Normal file
@ -0,0 +1,152 @@
|
||||
import { useMemo } from 'react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import {
|
||||
t,
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
getDateTimeFormat,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalStateMapping } from '@vegaprotocol/types';
|
||||
import type {
|
||||
ProposalListFieldsFragment,
|
||||
NewMarketFieldsFragment,
|
||||
} from '../proposals-data-provider/__generated___/Proposals';
|
||||
import { VoteProgress } from '../voting-progress';
|
||||
|
||||
const instrumentGuard = (
|
||||
change?: ProposalListFieldsFragment['terms']['change']
|
||||
): change is NewMarketFieldsFragment => {
|
||||
return change?.__typename === 'NewMarket';
|
||||
};
|
||||
|
||||
export const useColumnDefs = () => {
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_market_requiredMajority,
|
||||
]);
|
||||
const requiredMajorityPercentage = useMemo(() => {
|
||||
const requiredMajority =
|
||||
params?.governance_proposal_market_requiredMajority ?? 1;
|
||||
return new BigNumber(requiredMajority).times(100);
|
||||
}, [params?.governance_proposal_market_requiredMajority]);
|
||||
|
||||
const cellCss = 'grid h-full items-center';
|
||||
const columnDefs: ColDef[] = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
colId: 'market',
|
||||
headerName: t('Market'),
|
||||
field: 'terms.change.instrument.code',
|
||||
width: 150,
|
||||
cellStyle: { lineHeight: '14px' },
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<
|
||||
ProposalListFieldsFragment,
|
||||
'terms.change.instrument.code'
|
||||
>) => {
|
||||
const { change } = data?.terms || {};
|
||||
if (instrumentGuard(change) && VEGA_TOKEN_URL) {
|
||||
if (data?.id) {
|
||||
const link = `${VEGA_TOKEN_URL}/governance/${data.id}`;
|
||||
return (
|
||||
<ExternalLink href={link}>
|
||||
{change.instrument.code}
|
||||
</ExternalLink>
|
||||
);
|
||||
}
|
||||
return change.instrument.code;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'description',
|
||||
headerName: t('Description'),
|
||||
field: 'terms.change.instrument.name',
|
||||
},
|
||||
{
|
||||
colId: 'asset',
|
||||
headerName: t('Settlement asset'),
|
||||
field: 'terms.change.instrument.futureProduct.settlementAsset.name',
|
||||
},
|
||||
{
|
||||
colId: 'state',
|
||||
headerName: t('State'),
|
||||
field: 'state',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
}: VegaValueFormatterParams<ProposalListFieldsFragment, 'state'>) =>
|
||||
value ? ProposalStateMapping[value] : '-',
|
||||
},
|
||||
{
|
||||
colId: 'voting',
|
||||
headerName: t('Voting'),
|
||||
cellClass: 'flex justify-between leading-tight font-mono',
|
||||
cellRenderer: ({
|
||||
data,
|
||||
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
|
||||
if (data) {
|
||||
const yesTokens = new BigNumber(data.votes.yes.totalTokens);
|
||||
const noTokens = new BigNumber(data.votes.no.totalTokens);
|
||||
const totalTokensVoted = yesTokens.plus(noTokens);
|
||||
const yesPercentage = totalTokensVoted.isZero()
|
||||
? new BigNumber(0)
|
||||
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
|
||||
return (
|
||||
<div className="uppercase flex h-full items-center justify-center">
|
||||
<VoteProgress
|
||||
threshold={requiredMajorityPercentage}
|
||||
progress={yesPercentage}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
colId: 'closing-date',
|
||||
headerName: t('Closing date'),
|
||||
field: 'terms.closingDatetime',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
}: VegaValueFormatterParams<
|
||||
ProposalListFieldsFragment,
|
||||
'terms.closingDatetime'
|
||||
>) => (value ? getDateTimeFormat().format(new Date(value)) : '-'),
|
||||
},
|
||||
{
|
||||
colId: 'enactment-date',
|
||||
headerName: t('Enactment date'),
|
||||
field: 'terms.enactmentDatetime',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
}: VegaValueFormatterParams<
|
||||
ProposalListFieldsFragment,
|
||||
'terms.enactmentDatetime'
|
||||
>) => (value ? getDateTimeFormat().format(new Date(value)) : '-'),
|
||||
},
|
||||
];
|
||||
}, [VEGA_TOKEN_URL, requiredMajorityPercentage]);
|
||||
const defaultColDef: ColDef = useMemo(() => {
|
||||
return {
|
||||
sortable: false,
|
||||
cellClass: cellCss,
|
||||
};
|
||||
}, []);
|
||||
return useMemo(
|
||||
() => ({
|
||||
columnDefs,
|
||||
defaultColDef,
|
||||
}),
|
||||
[columnDefs, defaultColDef]
|
||||
);
|
||||
};
|
1
libs/governance/src/lib/voting-progress/index.ts
Normal file
1
libs/governance/src/lib/voting-progress/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './voting-progress';
|
@ -1,6 +1,6 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { VoteProgress } from './vote-progress';
|
||||
import { VoteProgress } from './voting-progress';
|
||||
|
||||
it('Renders with data-testid', () => {
|
||||
render(
|
@ -1,4 +1,4 @@
|
||||
import type { BigNumber } from '../../../../lib/bignumber';
|
||||
import type BigNumber from 'bignumber.js';
|
||||
|
||||
export const VoteProgress = ({
|
||||
progress,
|
||||
@ -14,7 +14,7 @@ export const VoteProgress = ({
|
||||
>
|
||||
<div
|
||||
data-testid="vote-progress-indicator"
|
||||
className="absolute -top-1 w-[1px] h-3 bg-white z-1"
|
||||
className="absolute -top-1 w-[1px] h-3 bg-neutral-300 dark:bg-white z-1"
|
||||
style={{ left: `${threshold}%` }}
|
||||
/>
|
||||
<div className="w-full h-2">
|
||||
@ -26,7 +26,7 @@ export const VoteProgress = ({
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="absolute left-0 bg-vega-red h-1"
|
||||
className="absolute right-0 bg-vega-red h-1"
|
||||
data-testid="vote-progress-bar-against"
|
||||
style={{
|
||||
width: `${100 - progress.toNumber()}%`,
|
Loading…
Reference in New Issue
Block a user