fix(governance): proposal vote status header component (#4561)
This commit is contained in:
parent
6616aceebb
commit
86e122389c
@ -24,6 +24,7 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
|||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import { VoteState } from '../vote-details/use-user-vote';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/proposals', () => ({
|
jest.mock('@vegaprotocol/proposals', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/proposals'),
|
...jest.requireActual('@vegaprotocol/proposals'),
|
||||||
@ -36,7 +37,8 @@ jest.mock('@vegaprotocol/proposals', () => ({
|
|||||||
const renderComponent = (
|
const renderComponent = (
|
||||||
proposal: ProposalQuery['proposal'],
|
proposal: ProposalQuery['proposal'],
|
||||||
isListItem = true,
|
isListItem = true,
|
||||||
mocks: MockedResponse[] = []
|
mocks: MockedResponse[] = [],
|
||||||
|
voteState?: VoteState
|
||||||
) =>
|
) =>
|
||||||
render(
|
render(
|
||||||
<AppStateProvider>
|
<AppStateProvider>
|
||||||
@ -47,6 +49,7 @@ const renderComponent = (
|
|||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
isListItem={isListItem}
|
isListItem={isListItem}
|
||||||
networkParams={mockNetworkParams}
|
networkParams={mockNetworkParams}
|
||||||
|
voteState={voteState}
|
||||||
/>
|
/>
|
||||||
</VegaWalletContext.Provider>
|
</VegaWalletContext.Provider>
|
||||||
</MockedProvider>
|
</MockedProvider>
|
||||||
@ -386,10 +389,15 @@ describe('Proposal header', () => {
|
|||||||
closingDatetime: nextWeek.toString(),
|
closingDatetime: nextWeek.toString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
renderComponent(proposal, true, [
|
renderComponent(
|
||||||
// @ts-ignore generateProposal always creates an id
|
proposal,
|
||||||
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_NO),
|
true,
|
||||||
]);
|
[
|
||||||
|
// @ts-ignore generateProposal always creates an id
|
||||||
|
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_NO),
|
||||||
|
],
|
||||||
|
VoteState.No
|
||||||
|
);
|
||||||
expect(await screen.findByTestId('user-voted-no')).toBeInTheDocument();
|
expect(await screen.findByTestId('user-voted-no')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -400,10 +408,15 @@ describe('Proposal header', () => {
|
|||||||
closingDatetime: nextWeek.toString(),
|
closingDatetime: nextWeek.toString(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
renderComponent(proposal, true, [
|
renderComponent(
|
||||||
// @ts-ignore generateProposal always creates an id
|
proposal,
|
||||||
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_YES),
|
true,
|
||||||
]);
|
[
|
||||||
|
// @ts-ignore generateProposal always creates an id
|
||||||
|
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_YES),
|
||||||
|
],
|
||||||
|
VoteState.Yes
|
||||||
|
);
|
||||||
expect(await screen.findByTestId('user-voted-yes')).toBeInTheDocument();
|
expect(await screen.findByTestId('user-voted-yes')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,25 +8,26 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
|||||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||||
import { CurrentProposalState } from '../current-proposal-state';
|
import { CurrentProposalState } from '../current-proposal-state';
|
||||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||||
import { useUserVote } from '../vote-details/use-user-vote';
|
|
||||||
import { ProposalVotingStatus } from '../proposal-voting-status';
|
import { ProposalVotingStatus } from '../proposal-voting-status';
|
||||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||||
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import type { VoteState } from '../vote-details/use-user-vote';
|
||||||
|
|
||||||
export const ProposalHeader = ({
|
export const ProposalHeader = ({
|
||||||
proposal,
|
proposal,
|
||||||
networkParams,
|
networkParams,
|
||||||
isListItem = true,
|
isListItem = true,
|
||||||
|
voteState,
|
||||||
}: {
|
}: {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
networkParams: Partial<NetworkParamsResult>;
|
networkParams: Partial<NetworkParamsResult>;
|
||||||
isListItem?: boolean;
|
isListItem?: boolean;
|
||||||
|
voteState?: VoteState | null;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { voteState } = useUserVote(proposal?.id);
|
|
||||||
const change = proposal?.terms.change;
|
const change = proposal?.terms.change;
|
||||||
|
|
||||||
let details: ReactNode;
|
let details: ReactNode;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||||
import { Proposal } from './proposal';
|
import { Proposal } from './proposal';
|
||||||
@ -44,11 +46,15 @@ jest.mock('../list-asset', () => ({
|
|||||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||||
render(
|
render(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<Proposal
|
<MockedProvider>
|
||||||
restData={{}}
|
<VegaWalletProvider>
|
||||||
proposal={proposal as ProposalQuery['proposal']}
|
<Proposal
|
||||||
networkParams={mockNetworkParams}
|
restData={{}}
|
||||||
/>
|
proposal={proposal as ProposalQuery['proposal']}
|
||||||
|
networkParams={mockNetworkParams}
|
||||||
|
/>
|
||||||
|
</VegaWalletProvider>
|
||||||
|
</MockedProvider>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -19,6 +19,8 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
|
|||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { ProposalMarketChanges } from '../proposal-market-changes';
|
import { ProposalMarketChanges } from '../proposal-market-changes';
|
||||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||||
|
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
||||||
|
import { useUserVote } from '../vote-details/use-user-vote';
|
||||||
|
|
||||||
export enum ProposalType {
|
export enum ProposalType {
|
||||||
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
||||||
@ -53,6 +55,8 @@ export const Proposal = ({
|
|||||||
mostRecentlyEnactedAssociatedMarketProposal,
|
mostRecentlyEnactedAssociatedMarketProposal,
|
||||||
}: ProposalProps) => {
|
}: ProposalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { submit, Dialog, finalizedVote } = useVoteSubmit();
|
||||||
|
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||||
|
|
||||||
if (!proposal) {
|
if (!proposal) {
|
||||||
return null;
|
return null;
|
||||||
@ -132,10 +136,12 @@ export const Proposal = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ProposalHeader
|
<ProposalHeader
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
isListItem={false}
|
isListItem={false}
|
||||||
networkParams={networkParams}
|
networkParams={networkParams}
|
||||||
|
voteState={voteState}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div id="details">
|
<div id="details">
|
||||||
@ -207,6 +213,10 @@ export const Proposal = ({
|
|||||||
spamProtectionMinTokens={
|
spamProtectionMinTokens={
|
||||||
networkParams?.spam_protection_voting_min_tokens
|
networkParams?.spam_protection_voting_min_tokens
|
||||||
}
|
}
|
||||||
|
submit={submit}
|
||||||
|
dialog={Dialog}
|
||||||
|
voteState={voteState}
|
||||||
|
voteDatetime={voteDatetime}
|
||||||
/>
|
/>
|
||||||
</RoundedWrapper>
|
</RoundedWrapper>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||||
|
import { useUserVote } from '../vote-details/use-user-vote';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||||
@ -14,12 +15,17 @@ export const ProposalsListItem = ({
|
|||||||
proposal,
|
proposal,
|
||||||
networkParams,
|
networkParams,
|
||||||
}: ProposalsListItemProps) => {
|
}: ProposalsListItemProps) => {
|
||||||
|
const { voteState } = useUserVote(proposal?.id);
|
||||||
if (!proposal || !proposal.id || !networkParams) return null;
|
if (!proposal || !proposal.id || !networkParams) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li id={proposal.id} data-testid="proposals-list-item">
|
<li id={proposal.id} data-testid="proposals-list-item">
|
||||||
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
||||||
<ProposalHeader proposal={proposal} networkParams={networkParams} />
|
<ProposalHeader
|
||||||
|
proposal={proposal}
|
||||||
|
networkParams={networkParams}
|
||||||
|
voteState={voteState}
|
||||||
|
/>
|
||||||
<ProposalsListItemDetails proposal={proposal} />
|
<ProposalsListItemDetails proposal={proposal} />
|
||||||
</RoundedWrapper>
|
</RoundedWrapper>
|
||||||
</li>
|
</li>
|
||||||
|
@ -188,11 +188,7 @@ export const VoteButtons = ({
|
|||||||
(voteState === VoteState.Yes || voteState === VoteState.No) && (
|
(voteState === VoteState.Yes || voteState === VoteState.No) && (
|
||||||
<p data-testid="you-voted">
|
<p data-testid="you-voted">
|
||||||
<span>{t('youVoted')}:</span>{' '}
|
<span>{t('youVoted')}:</span>{' '}
|
||||||
<span
|
<span className="text-white font-bold">
|
||||||
className={
|
|
||||||
voteState === VoteState.Yes ? 'text-success' : 'text-danger'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{t(`voteState_${voteState}`)}
|
{t(`voteState_${voteState}`)}
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
{voteDatetime ? (
|
{voteDatetime ? (
|
||||||
|
@ -3,23 +3,29 @@ import { formatDistanceToNow } from 'date-fns';
|
|||||||
import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
|
import { VoteProgress } from '@vegaprotocol/proposals';
|
||||||
import { formatNumber } from '../../../../lib/format-number';
|
import { formatNumber } from '../../../../lib/format-number';
|
||||||
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
||||||
import { useVoteInformation } from '../../hooks';
|
import { useVoteInformation } from '../../hooks';
|
||||||
import { useUserVote } from './use-user-vote';
|
|
||||||
import { CurrentProposalStatus } from '../current-proposal-status';
|
import { CurrentProposalStatus } from '../current-proposal-status';
|
||||||
import { VoteButtonsContainer } from './vote-buttons';
|
import { VoteButtonsContainer } from './vote-buttons';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { ProposalType } from '../proposal/proposal';
|
import { ProposalType } from '../proposal/proposal';
|
||||||
|
import type { VoteValue } from '@vegaprotocol/types';
|
||||||
|
import type { DialogProps } from '@vegaprotocol/wallet';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import type { VoteState } from './use-user-vote';
|
||||||
|
|
||||||
interface VoteDetailsProps {
|
interface VoteDetailsProps {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
minVoterBalance: string | null | undefined;
|
minVoterBalance: string | null | undefined;
|
||||||
spamProtectionMinTokens: string | null | undefined;
|
spamProtectionMinTokens: string | null | undefined;
|
||||||
proposalType: ProposalType | null;
|
proposalType: ProposalType | null;
|
||||||
|
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||||
|
dialog: (props: DialogProps) => JSX.Element;
|
||||||
|
voteState: VoteState | null;
|
||||||
|
voteDatetime: Date | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VoteDetails = ({
|
export const VoteDetails = ({
|
||||||
@ -27,6 +33,10 @@ export const VoteDetails = ({
|
|||||||
minVoterBalance,
|
minVoterBalance,
|
||||||
spamProtectionMinTokens,
|
spamProtectionMinTokens,
|
||||||
proposalType,
|
proposalType,
|
||||||
|
submit,
|
||||||
|
dialog,
|
||||||
|
voteState,
|
||||||
|
voteDatetime,
|
||||||
}: VoteDetailsProps) => {
|
}: VoteDetailsProps) => {
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey } = useVegaWallet();
|
||||||
const {
|
const {
|
||||||
@ -48,8 +58,7 @@ export const VoteDetails = ({
|
|||||||
} = useVoteInformation({ proposal });
|
} = useVoteInformation({ proposal });
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { submit, Dialog, finalizedVote } = useVoteSubmit();
|
|
||||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
|
||||||
const defaultDecimals = 2;
|
const defaultDecimals = 2;
|
||||||
const daysLeft = t('daysLeft', {
|
const daysLeft = t('daysLeft', {
|
||||||
daysLeft: formatDistanceToNow(new Date(proposal?.terms.closingDatetime)),
|
daysLeft: formatDistanceToNow(new Date(proposal?.terms.closingDatetime)),
|
||||||
@ -219,7 +228,7 @@ export const VoteDetails = ({
|
|||||||
spamProtectionMinTokens={spamProtectionMinTokens}
|
spamProtectionMinTokens={spamProtectionMinTokens}
|
||||||
className="flex"
|
className="flex"
|
||||||
submit={submit}
|
submit={submit}
|
||||||
dialog={Dialog}
|
dialog={dialog}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
|
Loading…
Reference in New Issue
Block a user