feat(2211): download proposal forms data as json (#2521)
* feat(2211): view proposal forms data as json * feat(2211): removed the vega wallet barrier to viewing proposal forms * feat(2211): tweaked the json viewing to download the proposal form json, with a useful title (proposal title and readable datetime) * feat(2211): removed unwanted NX_SENTRY_DSN config from dev environments * feat(2211): unit test for download-json.ts * feat(2211): unit test for proposal-form-download-json.spec.tsx * feat(2211): simplified unit test for proposal-form-download-json.spec.tsx * feat(2211): removed unneeded unit test * feat(2211): revoked change that was causing test failures
This commit is contained in:
parent
863c288e0d
commit
c5b72e5a64
@ -6,4 +6,3 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
|
||||
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
||||
|
@ -9,4 +9,3 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
|
||||
|
@ -159,8 +159,10 @@
|
||||
"Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked.": "Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked.",
|
||||
"proposals": "Proposals",
|
||||
"proposal": "Proposal",
|
||||
"submitting": "Submitting",
|
||||
"submittingProposal": "Submitting proposal",
|
||||
"submit": "Submit",
|
||||
"submitProposal": "Submit proposal",
|
||||
"connectWalletToSubmitProposal": "Connect your wallet to submit a proposal",
|
||||
"proposedEnactment": "Proposed enactment",
|
||||
"Enacted": "Enacted",
|
||||
"enactedOn": "Enacted on",
|
||||
@ -729,5 +731,6 @@
|
||||
"homeRewardsIntro": "Track rewards you've earned for trading, liquidity provision, market creation, and staking.",
|
||||
"homeRewardsButtonText": "See rewards",
|
||||
"homeVegaTokenIntro": "VEGA Token is a governance asset used to make and vote on proposals, and nominate validators.",
|
||||
"homeVegaTokenButtonText": "Manage tokens"
|
||||
"homeVegaTokenButtonText": "Manage tokens",
|
||||
"downloadProposalJson": "Download proposal as JSON"
|
||||
}
|
||||
|
26
apps/token/src/lib/download-json.ts
Normal file
26
apps/token/src/lib/download-json.ts
Normal file
@ -0,0 +1,26 @@
|
||||
export const downloadJson = (jsonString: string, proposalTitle: string) => {
|
||||
try {
|
||||
const now = new Date();
|
||||
const day = now.getDate().toString().padStart(2, '0');
|
||||
const month = now.toLocaleString('en-US', { month: 'short' });
|
||||
const year = now.getFullYear().toString();
|
||||
const hours = now.getHours().toString().padStart(2, '0');
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||||
const seconds = now.getSeconds().toString().padStart(2, '0');
|
||||
// e.g. "2023-Jan-03-23-59-59"
|
||||
const formattedDateTime = `${day}-${month}-${year}-${hours}-${minutes}-${seconds}`;
|
||||
|
||||
const blob = new Blob([jsonString], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = url;
|
||||
a.download = `${proposalTitle}-${formattedDateTime}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
@ -5,3 +5,4 @@ export * from './proposal-form-terms';
|
||||
export * from './proposal-form-submit';
|
||||
export * from './proposal-form-transaction-dialog';
|
||||
export * from './proposal-form-vote-and-enactment-deadline';
|
||||
export * from './proposal-form-download-json';
|
||||
|
@ -0,0 +1,12 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ProposalFormDownloadJson } from './proposal-form-download-json';
|
||||
|
||||
describe('ProposalFormDownloadJson', () => {
|
||||
it('calls the downloadJson method when the button is clicked', () => {
|
||||
const downloadJson = jest.fn();
|
||||
render(<ProposalFormDownloadJson downloadJson={downloadJson} />);
|
||||
const button = screen.getByTestId('proposal-download-json');
|
||||
fireEvent.click(button);
|
||||
expect(downloadJson).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
@ -0,0 +1,19 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface ProposalFormDownloadJsonProps {
|
||||
downloadJson: () => void;
|
||||
}
|
||||
|
||||
export const ProposalFormDownloadJson = ({
|
||||
downloadJson,
|
||||
}: ProposalFormDownloadJsonProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<Button data-testid="proposal-download-json" onClick={downloadJson}>
|
||||
{t('downloadProposalJson')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,59 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { ProposalFormSubmit } from './proposal-form-submit';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
|
||||
const renderComponent = (
|
||||
context: VegaWalletContextShape,
|
||||
isSubmitting: boolean
|
||||
) => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={context}>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('Proposal Form Submit', () => {
|
||||
it('should display connection message and button if wallet not connected', () => {
|
||||
renderComponent({ pubKey: null } as VegaWalletContextShape, false);
|
||||
|
||||
expect(
|
||||
screen.getByText('Connect your wallet to submit a proposal')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByTestId('connect-to-vega-wallet-btn')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display submit button if wallet is connected', () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
renderComponent(
|
||||
{
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
} as VegaWalletContextShape,
|
||||
false
|
||||
);
|
||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||
'Submit proposal'
|
||||
);
|
||||
});
|
||||
|
||||
it('should display submitting button text if wallet is connected and submitting', () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
renderComponent(
|
||||
{
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
} as VegaWalletContextShape,
|
||||
true
|
||||
);
|
||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||
'Submitting proposal'
|
||||
);
|
||||
});
|
||||
});
|
@ -1,5 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
|
||||
interface ProposalFormSubmitProps {
|
||||
isSubmitting: boolean;
|
||||
@ -8,17 +10,25 @@ interface ProposalFormSubmitProps {
|
||||
export const ProposalFormSubmit = ({
|
||||
isSubmitting,
|
||||
}: ProposalFormSubmitProps) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mt-10 my-20">
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
data-testid="proposal-submit"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? t('Submitting') : t('Submit')} {t('Proposal')}
|
||||
</Button>
|
||||
<div className="mb-6">
|
||||
<div className="mb-4 font-bold uppercase text-vega-orange">
|
||||
{!pubKey && t('connectWalletToSubmitProposal')}
|
||||
</div>
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
data-testid="proposal-submit"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? t('submittingProposal') : t('submitProposal')}
|
||||
</Button>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -437,7 +437,7 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<div className="mb-10">
|
||||
<ProposalFormSubheader>
|
||||
{enactmentRegister && enactmentMinClose && enactmentMaxClose
|
||||
? t('ProposalVoteAndEnactmentTitle')
|
||||
@ -535,6 +535,6 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
maxEnactmentHours={maxEnactmentHours}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ describe('Propose Freeform', () => {
|
||||
expect(screen.getByTestId('proposal-description')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -12,18 +12,19 @@ import {
|
||||
ProposalFormSubmit,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import {
|
||||
createDocsLinks,
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface FreeformProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@ -50,10 +51,11 @@ export const ProposeFreeform = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<FreeformProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: FreeformProposalFormFields) => {
|
||||
const assembleProposal = (fields: FreeformProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
fields.proposalVoteDeadline ===
|
||||
deadlineToRoundedHours(
|
||||
@ -66,7 +68,7 @@ export const ProposeFreeform = () => {
|
||||
params.governance_proposal_freeform_maxClose
|
||||
).toString();
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@ -79,86 +81,101 @@ export const ProposeFreeform = () => {
|
||||
isVoteDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: FreeformProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-freeform-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={params}>
|
||||
<Heading title={t('NewFreeformProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_freeform_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={params}
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewFreeformProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_freeform_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="freeform-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="freeform-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_freeform_minClose}
|
||||
voteMaxClose={params.governance_proposal_freeform_maxClose}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_freeform_minClose}
|
||||
voteMaxClose={params.governance_proposal_freeform_maxClose}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -112,6 +112,7 @@ describe('Propose Network Parameter', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -20,6 +20,7 @@ import {
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
ProposalFormDownloadJson,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import {
|
||||
@ -33,8 +34,8 @@ import {
|
||||
TextArea,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
interface SelectedNetworkParamCurrentValueProps {
|
||||
value: string;
|
||||
@ -92,6 +93,7 @@ export const ProposeNetworkParameter = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<NetworkParameterProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@ -99,8 +101,8 @@ export const ProposeNetworkParameter = () => {
|
||||
? Object.entries(params).find(([key]) => key === selectedNetworkParam)
|
||||
: null;
|
||||
|
||||
const onSubmit = async (fields: NetworkParameterProposalFormFields) => {
|
||||
const acutalNetworkParamKey = fields.proposalNetworkParameterKey
|
||||
const assembleProposal = (fields: NetworkParameterProposalFormFields) => {
|
||||
const actualNetworkParamKey = fields.proposalNetworkParameterKey
|
||||
.split('_')
|
||||
.join('.');
|
||||
|
||||
@ -121,7 +123,7 @@ export const ProposeNetworkParameter = () => {
|
||||
params.governance_proposal_updateNetParam_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@ -129,7 +131,7 @@ export const ProposeNetworkParameter = () => {
|
||||
terms: {
|
||||
updateNetworkParameter: {
|
||||
changes: {
|
||||
key: acutalNetworkParamKey,
|
||||
key: actualNetworkParamKey,
|
||||
value: fields.proposalNetworkParameterValue,
|
||||
},
|
||||
},
|
||||
@ -144,7 +146,19 @@ export const ProposeNetworkParameter = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: NetworkParameterProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-network-param-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -152,163 +166,161 @@ export const ProposeNetworkParameter = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NetworkParameterProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NetworkParameterProposal')} />
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreNetParamsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/network-parameters`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/network-parameters`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreNetParamsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/network-parameters`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/network-parameters`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="network-parameter-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="network-parameter-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAParameterToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAParameterToChange')}
|
||||
labelFor="proposal-parameter-key"
|
||||
hideLabel={true}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-parameter-select"
|
||||
id="proposal-parameter-key"
|
||||
{...register('proposalNetworkParameterKey', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAParameterToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAParameterToChange')}
|
||||
labelFor="proposal-parameter-key"
|
||||
hideLabel={true}
|
||||
onChange={(e) => setSelectedNetworkParam(e.target.value)}
|
||||
value={selectedNetworkParam}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-parameter-select"
|
||||
id="proposal-parameter-key"
|
||||
{...register('proposalNetworkParameterKey', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
onChange={(e) => setSelectedNetworkParam(e.target.value)}
|
||||
value={selectedNetworkParam}
|
||||
>
|
||||
<option value="">{t('SelectParameter')}</option>
|
||||
{Object.keys(params).map((key) => {
|
||||
const actualKey = key.split('_').join('.');
|
||||
return (
|
||||
<option key={key} value={key}>
|
||||
{actualKey}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
{errors?.proposalNetworkParameterKey?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterKey?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
{selectedNetworkParam && (
|
||||
<div className="mt-[-10px]">
|
||||
{selectedParamEntry && (
|
||||
<SelectedNetworkParamCurrentValue
|
||||
value={selectedParamEntry[1]}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormGroup
|
||||
label={t('NewProposedValue')}
|
||||
labelFor="proposal-parameter-new-value"
|
||||
>
|
||||
<TextArea
|
||||
data-testid="selected-proposal-param-new-value"
|
||||
id="proposal-parameter-new-value"
|
||||
{...register('proposalNetworkParameterValue', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
/>
|
||||
{errors?.proposalNetworkParameterValue?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterValue?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</div>
|
||||
<option value="">{t('SelectParameter')}</option>
|
||||
{Object.keys(params).map((key) => {
|
||||
const actualKey = key.split('_').join('.');
|
||||
return (
|
||||
<option key={key} value={key}>
|
||||
{actualKey}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
{errors?.proposalNetworkParameterKey?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterKey?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={
|
||||
params.governance_proposal_updateNetParam_minClose
|
||||
}
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateNetParam_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxEnact
|
||||
}
|
||||
/>
|
||||
{selectedNetworkParam && (
|
||||
<div className="mt-[-10px]">
|
||||
{selectedParamEntry && (
|
||||
<SelectedNetworkParamCurrentValue
|
||||
value={selectedParamEntry[1]}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<FormGroup
|
||||
label={t('NewProposedValue')}
|
||||
labelFor="proposal-parameter-new-value"
|
||||
>
|
||||
<TextArea
|
||||
data-testid="selected-proposal-param-new-value"
|
||||
id="proposal-parameter-new-value"
|
||||
{...register('proposalNetworkParameterValue', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
/>
|
||||
{errors?.proposalNetworkParameterValue?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalNetworkParameterValue?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={
|
||||
params.governance_proposal_updateNetParam_minClose
|
||||
}
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateNetParam_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateNetParam_maxEnact
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -110,6 +110,7 @@ describe('Propose New Asset', () => {
|
||||
expect(screen.getByTestId('proposal-validation-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -21,13 +21,14 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface NewAssetProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@ -62,10 +63,11 @@ export const ProposeNewAsset = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<NewAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewAssetProposalFormFields) => {
|
||||
const assembleProposal = (fields: NewAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_asset_minClose
|
||||
@ -87,7 +89,7 @@ export const ProposeNewAsset = () => {
|
||||
params.governance_proposal_asset_maxClose
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@ -111,7 +113,19 @@ export const ProposeNewAsset = () => {
|
||||
isValidationDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: NewAssetProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-new-asset-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -119,112 +133,111 @@ export const ProposeNewAsset = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NewAssetProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_asset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewAssetProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_asset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="new-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="new-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>{t('NewAsset')}</ProposalFormSubheader>
|
||||
<ProposalFormSubheader>{t('NewAsset')}</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_asset_minClose}
|
||||
voteMaxClose={params.governance_proposal_asset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_asset_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_asset_maxEnact}
|
||||
validationRequired={true}
|
||||
onValidationMinMax={setValue}
|
||||
validationRegister={register('proposalValidationDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
validationErrorMessage={
|
||||
errors?.proposalValidationDeadline?.message
|
||||
}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_asset_minClose}
|
||||
voteMaxClose={params.governance_proposal_asset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_asset_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_asset_maxEnact}
|
||||
validationRequired={true}
|
||||
onValidationMinMax={setValue}
|
||||
validationRegister={register('proposalValidationDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
validationErrorMessage={
|
||||
errors?.proposalValidationDeadline?.message
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -104,6 +104,7 @@ describe('Propose New Market', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -20,13 +20,14 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface NewMarketProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@ -60,10 +61,11 @@ export const ProposeNewMarket = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<NewMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: NewMarketProposalFormFields) => {
|
||||
const assembleProposal = (fields: NewMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_market_minClose
|
||||
@ -81,7 +83,7 @@ export const ProposeNewMarket = () => {
|
||||
params.governance_proposal_market_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@ -101,7 +103,19 @@ export const ProposeNewMarket = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: NewMarketProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-new-market-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -109,104 +123,103 @@ export const ProposeNewMarket = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NewMarketProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_market_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewMarketProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_market_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="new-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="new-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>{t('NewMarket')}</ProposalFormSubheader>
|
||||
<ProposalFormSubheader>{t('NewMarket')}</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newMarket (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.newMarket (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_market_minClose}
|
||||
voteMaxClose={params.governance_proposal_market_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_market_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_market_maxEnact}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_market_minClose}
|
||||
voteMaxClose={params.governance_proposal_market_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={params.governance_proposal_market_minEnact}
|
||||
enactmentMaxClose={params.governance_proposal_market_maxEnact}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -2,7 +2,6 @@ import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
ExternalLink,
|
||||
@ -20,8 +19,10 @@ import { useProposalSubmit } from '@vegaprotocol/governance';
|
||||
import {
|
||||
ProposalFormSubmit,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
} from '../../components/propose';
|
||||
import { ProposalRawMinRequirements } from './proposal-raw-min-requirements';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface RawProposalFormFields {
|
||||
rawProposalData: string;
|
||||
@ -48,6 +49,7 @@ export const ProposeRaw = () => {
|
||||
register,
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
watch,
|
||||
} = useForm<RawProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
@ -57,88 +59,90 @@ export const ProposeRaw = () => {
|
||||
await submit(JSON.parse(fields.rawProposalData));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(JSON.stringify(formData), 'vega-raw-proposal');
|
||||
};
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('NewRawProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalRawMinRequirements
|
||||
assetMin={params.governance_proposal_asset_minProposerBalance}
|
||||
updateAssetMin={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
marketMin={params.governance_proposal_market_minProposerBalance}
|
||||
updateMarketMin={
|
||||
params.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
updateNetParamMin={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
freeformMin={
|
||||
params.governance_proposal_freeform_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('NewRawProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
target="_blank"
|
||||
>
|
||||
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
<ProposalRawMinRequirements
|
||||
assetMin={params.governance_proposal_asset_minProposerBalance}
|
||||
updateAssetMin={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
marketMin={params.governance_proposal_market_minProposerBalance}
|
||||
updateMarketMin={
|
||||
params.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
updateNetParamMin={
|
||||
params.governance_proposal_updateNetParam_minProposerBalance
|
||||
}
|
||||
freeformMin={params.governance_proposal_freeform_minProposerBalance}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
/>
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
target="_blank"
|
||||
>
|
||||
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="raw-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormGroup
|
||||
label="Make a proposal by submitting JSON"
|
||||
labelFor="proposal-data"
|
||||
>
|
||||
<TextArea
|
||||
id="proposal-data"
|
||||
className="min-h-[200px]"
|
||||
hasError={hasError}
|
||||
data-testid="proposal-data"
|
||||
{...register('rawProposalData', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
/>
|
||||
{errors.rawProposalData?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors.rawProposalData?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreProposalsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/governance`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="raw-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormGroup
|
||||
label="Make a proposal by submitting JSON"
|
||||
labelFor="proposal-data"
|
||||
>
|
||||
<TextArea
|
||||
id="proposal-data"
|
||||
className="min-h-[200px]"
|
||||
hasError={hasError}
|
||||
data-testid="proposal-data"
|
||||
{...register('rawProposalData', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
{errors.rawProposalData?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors.rawProposalData?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -111,6 +111,7 @@ describe('Propose Update Asset', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -20,13 +20,14 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface UpdateAssetProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@ -60,10 +61,11 @@ export const ProposeUpdateAsset = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<UpdateAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateAssetProposalFormFields) => {
|
||||
const assembleProposal = (fields: UpdateAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateAsset_minClose
|
||||
@ -81,7 +83,7 @@ export const ProposeUpdateAsset = () => {
|
||||
params.governance_proposal_updateAsset_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@ -101,7 +103,19 @@ export const ProposeUpdateAsset = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: UpdateAssetProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-update-asset-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -109,110 +123,107 @@ export const ProposeUpdateAsset = () => {
|
||||
loading={networkParamsLoading}
|
||||
error={networkParamsError}
|
||||
data={params}
|
||||
>
|
||||
<Heading title={t('UpdateAssetProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
render={(params) => (
|
||||
<>
|
||||
<Heading title={t('UpdateAssetProposal')} />
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateAsset_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreAssetsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/assets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="update-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="update-asset-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('UpdateAsset')}
|
||||
</ProposalFormSubheader>
|
||||
<ProposalFormSubheader>{t('UpdateAsset')}</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.updateAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={'Terms.updateAsset (JSON format)'}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_updateAsset_minClose}
|
||||
voteMaxClose={params.governance_proposal_updateAsset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateAsset_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateAsset_maxEnact
|
||||
}
|
||||
/>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={params.governance_proposal_updateAsset_minClose}
|
||||
voteMaxClose={params.governance_proposal_updateAsset_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateAsset_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateAsset_maxEnact
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -187,6 +187,7 @@ describe('Propose Update Market', () => {
|
||||
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-submit')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
|
||||
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
@ -34,9 +35,9 @@ import {
|
||||
Select,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { useProposalMarketsQueryQuery } from './__generated___/UpdateMarket';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
|
||||
export interface UpdateMarketProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@ -101,10 +102,11 @@ export const ProposeUpdateMarket = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<UpdateMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
|
||||
const onSubmit = async (fields: UpdateMarketProposalFormFields) => {
|
||||
const assembleProposal = (fields: UpdateMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
fields.proposalVoteDeadline,
|
||||
params.governance_proposal_updateMarket_minClose
|
||||
@ -122,7 +124,7 @@ export const ProposeUpdateMarket = () => {
|
||||
params.governance_proposal_updateMarket_maxEnact
|
||||
);
|
||||
|
||||
await submit({
|
||||
return {
|
||||
rationale: {
|
||||
title: fields.proposalTitle,
|
||||
description: fields.proposalDescription,
|
||||
@ -145,176 +147,182 @@ export const ProposeUpdateMarket = () => {
|
||||
isEnactmentDeadlineAtMaximum
|
||||
),
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const onSubmit = async (fields: UpdateMarketProposalFormFields) => {
|
||||
await submit(assembleProposal(fields));
|
||||
};
|
||||
|
||||
const viewJson = () => {
|
||||
const formData = watch();
|
||||
downloadJson(
|
||||
JSON.stringify(assembleProposal(formData)),
|
||||
'vega-update-market-proposal'
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={networkParamsLoading && marketsLoading}
|
||||
error={networkParamsError && marketsError}
|
||||
data={params && marketsData}
|
||||
>
|
||||
<Heading title={t('UpdateMarketProposal')} />
|
||||
<VegaWalletContainer>
|
||||
{() => (
|
||||
<>
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
params.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={params.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
data={{ ...params, ...marketsData }}
|
||||
render={(data) => (
|
||||
<>
|
||||
<Heading title={t('UpdateMarketProposal')} />
|
||||
<ProposalMinRequirements
|
||||
minProposalBalance={
|
||||
data.governance_proposal_updateMarket_minProposerBalance
|
||||
}
|
||||
spamProtectionMin={data.spam_protection_proposal_min_tokens}
|
||||
userAction={ProposalUserAction.CREATE}
|
||||
/>
|
||||
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
{VEGA_DOCS_URL && (
|
||||
<p className="text-sm" data-testid="proposal-docs-link">
|
||||
<span className="mr-1">{t('ProposalTermsText')}</span>
|
||||
<ExternalLink
|
||||
href={`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
}${DOCS_LINK}`}
|
||||
target="_blank"
|
||||
>{`${
|
||||
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
|
||||
}${DOCS_LINK}`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
{VEGA_EXPLORER_URL && (
|
||||
<p className="text-sm">
|
||||
{t('MoreMarketsInfo')}{' '}
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/markets`}
|
||||
target="_blank"
|
||||
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div data-testid="update-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
<div data-testid="update-market-proposal-form">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<ProposalFormSubheader>
|
||||
{t('ProposalRationale')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
<ProposalFormTitle
|
||||
registerField={register('proposalTitle', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAMarketToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAMarketToChange')}
|
||||
labelFor="proposal-market"
|
||||
hideLabel={true}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-market-select"
|
||||
id="proposal-market"
|
||||
{...register('proposalMarketId', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalTitle?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormDescription
|
||||
registerField={register('proposalDescription', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
errorMessage={errors?.proposalDescription?.message}
|
||||
/>
|
||||
|
||||
<ProposalFormSubheader>
|
||||
{t('SelectAMarketToChange')}
|
||||
</ProposalFormSubheader>
|
||||
|
||||
<FormGroup
|
||||
label={t('SelectAMarketToChange')}
|
||||
labelFor="proposal-market"
|
||||
hideLabel={true}
|
||||
onChange={(e) => setSelectedMarket(e.target.value)}
|
||||
>
|
||||
<Select
|
||||
data-testid="proposal-market-select"
|
||||
id="proposal-market"
|
||||
{...register('proposalMarketId', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
onChange={(e) => setSelectedMarket(e.target.value)}
|
||||
>
|
||||
<option value="">{t('SelectMarket')}</option>
|
||||
{sortedMarkets.map((market) => (
|
||||
<option value={market.id} key={market.id}>
|
||||
{market.tradableInstrument.instrument.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{errors?.proposalMarketId?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalMarketId?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
{selectedMarket && (
|
||||
<div className="mt-[-20px] mb-6">
|
||||
<KeyValueTable data-testid="update-market-details">
|
||||
<KeyValueTableRow>
|
||||
{t('MarketName')}
|
||||
{
|
||||
marketsData?.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.name
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketCode')}
|
||||
{
|
||||
marketsData?.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.code
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketId')}
|
||||
{selectedMarket}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
<option value="">{t('SelectMarket')}</option>
|
||||
{sortedMarkets.map((market) => (
|
||||
<option value={market.id} key={market.id}>
|
||||
{market.tradableInstrument.instrument.name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
{errors?.proposalMarketId?.message && (
|
||||
<InputError intent="danger">
|
||||
{errors?.proposalMarketId?.message}
|
||||
</InputError>
|
||||
)}
|
||||
</FormGroup>
|
||||
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={t('ProposeUpdateMarketTerms')}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
{selectedMarket && (
|
||||
<div className="mt-[-20px] mb-6">
|
||||
<KeyValueTable data-testid="update-market-details">
|
||||
<KeyValueTableRow>
|
||||
{t('MarketName')}
|
||||
{
|
||||
data.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.name
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketCode')}
|
||||
{
|
||||
data.marketsConnection?.edges?.find(
|
||||
({ node: market }) => market.id === selectedMarket
|
||||
)?.node.tradableInstrument.instrument.code
|
||||
}
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('MarketId')}
|
||||
{selectedMarket}
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={
|
||||
params.governance_proposal_updateMarket_minClose
|
||||
}
|
||||
voteMaxClose={
|
||||
params.governance_proposal_updateMarket_maxClose
|
||||
}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
params.governance_proposal_updateMarket_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
params.governance_proposal_updateMarket_maxEnact
|
||||
}
|
||||
/>
|
||||
<ProposalFormTerms
|
||||
registerField={register('proposalTerms', {
|
||||
required: t('Required'),
|
||||
validate: (value) => validateJson(value),
|
||||
})}
|
||||
labelOverride={t('ProposeUpdateMarketTerms')}
|
||||
errorMessage={errors?.proposalTerms?.message}
|
||||
docsLink={DOCS_LINK}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</AsyncRenderer>
|
||||
<ProposalFormVoteAndEnactmentDeadline
|
||||
onVoteMinMax={setValue}
|
||||
voteRegister={register('proposalVoteDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
voteErrorMessage={errors?.proposalVoteDeadline?.message}
|
||||
voteMinClose={data.governance_proposal_updateMarket_minClose}
|
||||
voteMaxClose={data.governance_proposal_updateMarket_maxClose}
|
||||
onEnactMinMax={setValue}
|
||||
enactmentRegister={register('proposalEnactmentDeadline', {
|
||||
required: t('Required'),
|
||||
})}
|
||||
enactmentErrorMessage={
|
||||
errors?.proposalEnactmentDeadline?.message
|
||||
}
|
||||
enactmentMinClose={
|
||||
data.governance_proposal_updateMarket_minEnact
|
||||
}
|
||||
enactmentMaxClose={
|
||||
data.governance_proposal_updateMarket_maxEnact
|
||||
}
|
||||
/>
|
||||
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user