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:
Sam Keen 2023-01-09 09:48:47 +00:00 committed by GitHub
parent 863c288e0d
commit c5b72e5a64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 978 additions and 766 deletions

View File

@ -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_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996

View File

@ -9,4 +9,3 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996

View File

@ -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.", "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", "proposals": "Proposals",
"proposal": "Proposal", "proposal": "Proposal",
"submitting": "Submitting", "submittingProposal": "Submitting proposal",
"submit": "Submit", "submit": "Submit",
"submitProposal": "Submit proposal",
"connectWalletToSubmitProposal": "Connect your wallet to submit a proposal",
"proposedEnactment": "Proposed enactment", "proposedEnactment": "Proposed enactment",
"Enacted": "Enacted", "Enacted": "Enacted",
"enactedOn": "Enacted on", "enactedOn": "Enacted on",
@ -729,5 +731,6 @@
"homeRewardsIntro": "Track rewards you've earned for trading, liquidity provision, market creation, and staking.", "homeRewardsIntro": "Track rewards you've earned for trading, liquidity provision, market creation, and staking.",
"homeRewardsButtonText": "See rewards", "homeRewardsButtonText": "See rewards",
"homeVegaTokenIntro": "VEGA Token is a governance asset used to make and vote on proposals, and nominate validators.", "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"
} }

View 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);
}
};

View File

@ -5,3 +5,4 @@ export * from './proposal-form-terms';
export * from './proposal-form-submit'; export * from './proposal-form-submit';
export * from './proposal-form-transaction-dialog'; export * from './proposal-form-transaction-dialog';
export * from './proposal-form-vote-and-enactment-deadline'; export * from './proposal-form-vote-and-enactment-deadline';
export * from './proposal-form-download-json';

View File

@ -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);
});
});

View File

@ -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>
);
};

View File

@ -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'
);
});
});

View File

@ -1,5 +1,7 @@
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
interface ProposalFormSubmitProps { interface ProposalFormSubmitProps {
isSubmitting: boolean; isSubmitting: boolean;
@ -8,17 +10,25 @@ interface ProposalFormSubmitProps {
export const ProposalFormSubmit = ({ export const ProposalFormSubmit = ({
isSubmitting, isSubmitting,
}: ProposalFormSubmitProps) => { }: ProposalFormSubmitProps) => {
const { pubKey } = useVegaWallet();
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div className="mt-10 my-20"> <div className="mb-6">
<Button <div className="mb-4 font-bold uppercase text-vega-orange">
variant="primary" {!pubKey && t('connectWalletToSubmitProposal')}
type="submit" </div>
data-testid="proposal-submit" <VegaWalletContainer>
disabled={isSubmitting} {() => (
> <Button
{isSubmitting ? t('Submitting') : t('Submit')} {t('Proposal')} variant="primary"
</Button> type="submit"
data-testid="proposal-submit"
disabled={isSubmitting}
>
{isSubmitting ? t('submittingProposal') : t('submitProposal')}
</Button>
)}
</VegaWalletContainer>
</div> </div>
); );
}; };

View File

@ -437,7 +437,7 @@ export function ProposalFormVoteAndEnactmentDeadline({
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<> <div className="mb-10">
<ProposalFormSubheader> <ProposalFormSubheader>
{enactmentRegister && enactmentMinClose && enactmentMaxClose {enactmentRegister && enactmentMinClose && enactmentMaxClose
? t('ProposalVoteAndEnactmentTitle') ? t('ProposalVoteAndEnactmentTitle')
@ -535,6 +535,6 @@ export function ProposalFormVoteAndEnactmentDeadline({
maxEnactmentHours={maxEnactmentHours} maxEnactmentHours={maxEnactmentHours}
/> />
)} )}
</> </div>
); );
} }

View File

@ -109,6 +109,7 @@ describe('Propose Freeform', () => {
expect(screen.getByTestId('proposal-description')).toBeTruthy(); expect(screen.getByTestId('proposal-description')).toBeTruthy();
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-submit')).toBeTruthy(); expect(screen.getByTestId('proposal-submit')).toBeTruthy();
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy(); expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
}); });
}); });

View File

@ -12,18 +12,19 @@ import {
ProposalFormSubmit, ProposalFormSubmit,
ProposalFormTitle, ProposalFormTitle,
ProposalFormTransactionDialog, ProposalFormTransactionDialog,
ProposalFormDownloadJson,
ProposalFormVoteAndEnactmentDeadline, ProposalFormVoteAndEnactmentDeadline,
} from '../../components/propose'; } from '../../components/propose';
import { ProposalMinRequirements } from '../../components/shared'; import { ProposalMinRequirements } from '../../components/shared';
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
import { import {
createDocsLinks, createDocsLinks,
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
} from '@vegaprotocol/react-helpers'; } from '@vegaprotocol/react-helpers';
import { ProposalUserAction } from '../../components/shared'; import { ProposalUserAction } from '../../components/shared';
import { downloadJson } from '../../../../lib/download-json';
export interface FreeformProposalFormFields { export interface FreeformProposalFormFields {
proposalVoteDeadline: string; proposalVoteDeadline: string;
@ -50,10 +51,11 @@ export const ProposeFreeform = () => {
handleSubmit, handleSubmit,
formState: { isSubmitting, errors }, formState: { isSubmitting, errors },
setValue, setValue,
watch,
} = useForm<FreeformProposalFormFields>(); } = useForm<FreeformProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit(); const { finalizedProposal, submit, Dialog } = useProposalSubmit();
const onSubmit = async (fields: FreeformProposalFormFields) => { const assembleProposal = (fields: FreeformProposalFormFields) => {
const isVoteDeadlineAtMinimum = const isVoteDeadlineAtMinimum =
fields.proposalVoteDeadline === fields.proposalVoteDeadline ===
deadlineToRoundedHours( deadlineToRoundedHours(
@ -66,7 +68,7 @@ export const ProposeFreeform = () => {
params.governance_proposal_freeform_maxClose params.governance_proposal_freeform_maxClose
).toString(); ).toString();
await submit({ return {
rationale: { rationale: {
title: fields.proposalTitle, title: fields.proposalTitle,
description: fields.proposalDescription, description: fields.proposalDescription,
@ -79,86 +81,101 @@ export const ProposeFreeform = () => {
isVoteDeadlineAtMaximum isVoteDeadlineAtMaximum
), ),
}, },
}); };
};
const onSubmit = async (fields: FreeformProposalFormFields) => {
await submit(assembleProposal(fields));
};
const viewJson = () => {
const formData = watch();
downloadJson(
JSON.stringify(assembleProposal(formData)),
'vega-freeform-proposal'
);
}; };
return ( return (
<AsyncRenderer loading={loading} error={error} data={params}> <AsyncRenderer
<Heading title={t('NewFreeformProposal')} /> loading={loading}
<VegaWalletContainer> error={error}
{() => ( data={params}
<> render={(params) => (
<ProposalMinRequirements <>
minProposalBalance={ <Heading title={t('NewFreeformProposal')} />
params.governance_proposal_freeform_minProposerBalance
}
spamProtectionMin={params.spam_protection_proposal_min_tokens}
userAction={ProposalUserAction.CREATE}
/>
{VEGA_DOCS_URL && ( <ProposalMinRequirements
<p className="text-sm" data-testid="proposal-docs-link"> minProposalBalance={
<span className="mr-1">{t('ProposalTermsText')}</span> params.governance_proposal_freeform_minProposerBalance
<ExternalLink }
href={`${ spamProtectionMin={params.spam_protection_proposal_min_tokens}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE userAction={ProposalUserAction.CREATE}
}${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 createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink> }${DOCS_LINK}`}
</p> target="_blank"
)} >{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p>
)}
{VEGA_EXPLORER_URL && ( {VEGA_EXPLORER_URL && (
<p className="text-sm"> <p className="text-sm">
{t('MoreProposalsInfo')}{' '} {t('MoreProposalsInfo')}{' '}
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/governance`} href={`${VEGA_EXPLORER_URL}/governance`}
target="_blank" target="_blank"
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink> >{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
</p> </p>
)} )}
<div data-testid="freeform-proposal-form"> <div data-testid="freeform-proposal-form">
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<ProposalFormSubheader> <ProposalFormSubheader>
{t('ProposalRationale')} {t('ProposalRationale')}
</ProposalFormSubheader> </ProposalFormSubheader>
<ProposalFormTitle <ProposalFormTitle
registerField={register('proposalTitle', { registerField={register('proposalTitle', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalTitle?.message} errorMessage={errors?.proposalTitle?.message}
/> />
<ProposalFormDescription <ProposalFormDescription
registerField={register('proposalDescription', { registerField={register('proposalDescription', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalDescription?.message} errorMessage={errors?.proposalDescription?.message}
/> />
<ProposalFormVoteAndEnactmentDeadline <ProposalFormVoteAndEnactmentDeadline
onVoteMinMax={setValue} onVoteMinMax={setValue}
voteRegister={register('proposalVoteDeadline', { voteRegister={register('proposalVoteDeadline', {
required: t('Required'), required: t('Required'),
})} })}
voteErrorMessage={errors?.proposalVoteDeadline?.message} voteErrorMessage={errors?.proposalVoteDeadline?.message}
voteMinClose={params.governance_proposal_freeform_minClose} voteMinClose={params.governance_proposal_freeform_minClose}
voteMaxClose={params.governance_proposal_freeform_maxClose} voteMaxClose={params.governance_proposal_freeform_maxClose}
/> />
<ProposalFormSubmit isSubmitting={isSubmitting} /> <ProposalFormSubmit isSubmitting={isSubmitting} />
<ProposalFormTransactionDialog <ProposalFormDownloadJson downloadJson={viewJson} />
finalizedProposal={finalizedProposal} <ProposalFormTransactionDialog
TransactionDialog={Dialog} finalizedProposal={finalizedProposal}
/> TransactionDialog={Dialog}
</form> />
</div> </form>
</> </div>
)} </>
</VegaWalletContainer> )}
</AsyncRenderer> />
); );
}; };

View File

@ -112,6 +112,7 @@ describe('Propose Network Parameter', () => {
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-submit')).toBeTruthy(); expect(screen.getByTestId('proposal-submit')).toBeTruthy();
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy(); expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
}); });

View File

@ -20,6 +20,7 @@ import {
ProposalFormTitle, ProposalFormTitle,
ProposalFormTransactionDialog, ProposalFormTransactionDialog,
ProposalFormVoteAndEnactmentDeadline, ProposalFormVoteAndEnactmentDeadline,
ProposalFormDownloadJson,
} from '../../components/propose'; } from '../../components/propose';
import { ProposalMinRequirements } from '../../components/shared'; import { ProposalMinRequirements } from '../../components/shared';
import { import {
@ -33,8 +34,8 @@ import {
TextArea, TextArea,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
import { ProposalUserAction } from '../../components/shared'; import { ProposalUserAction } from '../../components/shared';
import { downloadJson } from '../../../../lib/download-json';
interface SelectedNetworkParamCurrentValueProps { interface SelectedNetworkParamCurrentValueProps {
value: string; value: string;
@ -92,6 +93,7 @@ export const ProposeNetworkParameter = () => {
handleSubmit, handleSubmit,
formState: { isSubmitting, errors }, formState: { isSubmitting, errors },
setValue, setValue,
watch,
} = useForm<NetworkParameterProposalFormFields>(); } = useForm<NetworkParameterProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit(); const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@ -99,8 +101,8 @@ export const ProposeNetworkParameter = () => {
? Object.entries(params).find(([key]) => key === selectedNetworkParam) ? Object.entries(params).find(([key]) => key === selectedNetworkParam)
: null; : null;
const onSubmit = async (fields: NetworkParameterProposalFormFields) => { const assembleProposal = (fields: NetworkParameterProposalFormFields) => {
const acutalNetworkParamKey = fields.proposalNetworkParameterKey const actualNetworkParamKey = fields.proposalNetworkParameterKey
.split('_') .split('_')
.join('.'); .join('.');
@ -121,7 +123,7 @@ export const ProposeNetworkParameter = () => {
params.governance_proposal_updateNetParam_maxEnact params.governance_proposal_updateNetParam_maxEnact
); );
await submit({ return {
rationale: { rationale: {
title: fields.proposalTitle, title: fields.proposalTitle,
description: fields.proposalDescription, description: fields.proposalDescription,
@ -129,7 +131,7 @@ export const ProposeNetworkParameter = () => {
terms: { terms: {
updateNetworkParameter: { updateNetworkParameter: {
changes: { changes: {
key: acutalNetworkParamKey, key: actualNetworkParamKey,
value: fields.proposalNetworkParameterValue, value: fields.proposalNetworkParameterValue,
}, },
}, },
@ -144,7 +146,19 @@ export const ProposeNetworkParameter = () => {
isEnactmentDeadlineAtMaximum 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 ( return (
@ -152,163 +166,161 @@ export const ProposeNetworkParameter = () => {
loading={networkParamsLoading} loading={networkParamsLoading}
error={networkParamsError} error={networkParamsError}
data={params} data={params}
> render={(params) => (
<Heading title={t('NetworkParameterProposal')} /> <>
<VegaWalletContainer> <Heading title={t('NetworkParameterProposal')} />
{() => ( <ProposalMinRequirements
<> minProposalBalance={
<ProposalMinRequirements params.governance_proposal_updateNetParam_minProposerBalance
minProposalBalance={ }
params.governance_proposal_updateNetParam_minProposerBalance spamProtectionMin={params.spam_protection_proposal_min_tokens}
} userAction={ProposalUserAction.CREATE}
spamProtectionMin={params.spam_protection_proposal_min_tokens} />
userAction={ProposalUserAction.CREATE}
/>
{VEGA_DOCS_URL && ( {VEGA_DOCS_URL && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank"
>{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink> }${DOCS_LINK}`}
</p> target="_blank"
)} >{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p>
)}
{VEGA_EXPLORER_URL && ( {VEGA_EXPLORER_URL && (
<p className="text-sm"> <p className="text-sm">
{t('MoreNetParamsInfo')}{' '} {t('MoreNetParamsInfo')}{' '}
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/network-parameters`} href={`${VEGA_EXPLORER_URL}/network-parameters`}
target="_blank" target="_blank"
>{`${VEGA_EXPLORER_URL}/network-parameters`}</ExternalLink> >{`${VEGA_EXPLORER_URL}/network-parameters`}</ExternalLink>
</p> </p>
)} )}
<div data-testid="network-parameter-proposal-form"> <div data-testid="network-parameter-proposal-form">
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<ProposalFormSubheader> <ProposalFormSubheader>
{t('ProposalRationale')} {t('ProposalRationale')}
</ProposalFormSubheader> </ProposalFormSubheader>
<ProposalFormTitle <ProposalFormTitle
registerField={register('proposalTitle', { 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'), required: t('Required'),
})} })}
errorMessage={errors?.proposalTitle?.message} onChange={(e) => setSelectedNetworkParam(e.target.value)}
/> value={selectedNetworkParam}
<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 <option value="">{t('SelectParameter')}</option>
data-testid="proposal-parameter-select" {Object.keys(params).map((key) => {
id="proposal-parameter-key" const actualKey = key.split('_').join('.');
{...register('proposalNetworkParameterKey', { return (
required: t('Required'), <option key={key} value={key}>
})} {actualKey}
onChange={(e) => setSelectedNetworkParam(e.target.value)} </option>
value={selectedNetworkParam} );
> })}
<option value="">{t('SelectParameter')}</option> </Select>
{Object.keys(params).map((key) => { {errors?.proposalNetworkParameterKey?.message && (
const actualKey = key.split('_').join('.'); <InputError intent="danger">
return ( {errors?.proposalNetworkParameterKey?.message}
<option key={key} value={key}> </InputError>
{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>
)} )}
</FormGroup>
<ProposalFormVoteAndEnactmentDeadline {selectedNetworkParam && (
onVoteMinMax={setValue} <div className="mt-[-10px]">
voteRegister={register('proposalVoteDeadline', { {selectedParamEntry && (
required: t('Required'), <SelectedNetworkParamCurrentValue
})} value={selectedParamEntry[1]}
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} /> <FormGroup
<ProposalFormTransactionDialog label={t('NewProposedValue')}
finalizedProposal={finalizedProposal} labelFor="proposal-parameter-new-value"
TransactionDialog={Dialog} >
/> <TextArea
</form> data-testid="selected-proposal-param-new-value"
</div> id="proposal-parameter-new-value"
</> {...register('proposalNetworkParameterValue', {
)} required: t('Required'),
</VegaWalletContainer> })}
</AsyncRenderer> />
{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>
</>
)}
/>
); );
}; };

View File

@ -110,6 +110,7 @@ describe('Propose New Asset', () => {
expect(screen.getByTestId('proposal-validation-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-validation-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-submit')).toBeTruthy(); expect(screen.getByTestId('proposal-submit')).toBeTruthy();
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy(); expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
}); });
}); });

View File

@ -21,13 +21,14 @@ import {
ProposalFormTerms, ProposalFormTerms,
ProposalFormTitle, ProposalFormTitle,
ProposalFormTransactionDialog, ProposalFormTransactionDialog,
ProposalFormDownloadJson,
ProposalFormVoteAndEnactmentDeadline, ProposalFormVoteAndEnactmentDeadline,
} from '../../components/propose'; } from '../../components/propose';
import { ProposalMinRequirements } from '../../components/shared'; import { ProposalMinRequirements } from '../../components/shared';
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
import { ProposalUserAction } from '../../components/shared'; import { ProposalUserAction } from '../../components/shared';
import { downloadJson } from '../../../../lib/download-json';
export interface NewAssetProposalFormFields { export interface NewAssetProposalFormFields {
proposalVoteDeadline: string; proposalVoteDeadline: string;
@ -62,10 +63,11 @@ export const ProposeNewAsset = () => {
handleSubmit, handleSubmit,
formState: { isSubmitting, errors }, formState: { isSubmitting, errors },
setValue, setValue,
watch,
} = useForm<NewAssetProposalFormFields>(); } = useForm<NewAssetProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit(); const { finalizedProposal, submit, Dialog } = useProposalSubmit();
const onSubmit = async (fields: NewAssetProposalFormFields) => { const assembleProposal = (fields: NewAssetProposalFormFields) => {
const isVoteDeadlineAtMinimum = doesValueEquateToParam( const isVoteDeadlineAtMinimum = doesValueEquateToParam(
fields.proposalVoteDeadline, fields.proposalVoteDeadline,
params.governance_proposal_asset_minClose params.governance_proposal_asset_minClose
@ -87,7 +89,7 @@ export const ProposeNewAsset = () => {
params.governance_proposal_asset_maxClose params.governance_proposal_asset_maxClose
); );
await submit({ return {
rationale: { rationale: {
title: fields.proposalTitle, title: fields.proposalTitle,
description: fields.proposalDescription, description: fields.proposalDescription,
@ -111,7 +113,19 @@ export const ProposeNewAsset = () => {
isValidationDeadlineAtMaximum 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 ( return (
@ -119,112 +133,111 @@ export const ProposeNewAsset = () => {
loading={networkParamsLoading} loading={networkParamsLoading}
error={networkParamsError} error={networkParamsError}
data={params} data={params}
> render={(params) => (
<Heading title={t('NewAssetProposal')} /> <>
<VegaWalletContainer> <Heading title={t('NewAssetProposal')} />
{() => (
<>
<ProposalMinRequirements
minProposalBalance={
params.governance_proposal_asset_minProposerBalance
}
spamProtectionMin={params.spam_protection_proposal_min_tokens}
userAction={ProposalUserAction.CREATE}
/>
{VEGA_DOCS_URL && ( <ProposalMinRequirements
<p className="text-sm" data-testid="proposal-docs-link"> minProposalBalance={
<span className="mr-1">{t('ProposalTermsText')}</span> params.governance_proposal_asset_minProposerBalance
<ExternalLink }
href={`${ spamProtectionMin={params.spam_protection_proposal_min_tokens}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE userAction={ProposalUserAction.CREATE}
}${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 createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink> }${DOCS_LINK}`}
</p> target="_blank"
)} >{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p>
)}
{VEGA_EXPLORER_URL && ( {VEGA_EXPLORER_URL && (
<p className="text-sm"> <p className="text-sm">
{t('MoreAssetsInfo')}{' '} {t('MoreAssetsInfo')}{' '}
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/assets`} href={`${VEGA_EXPLORER_URL}/assets`}
target="_blank" target="_blank"
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink> >{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
</p> </p>
)} )}
<div data-testid="new-asset-proposal-form"> <div data-testid="new-asset-proposal-form">
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<ProposalFormSubheader> <ProposalFormSubheader>
{t('ProposalRationale')} {t('ProposalRationale')}
</ProposalFormSubheader> </ProposalFormSubheader>
<ProposalFormTitle <ProposalFormTitle
registerField={register('proposalTitle', { registerField={register('proposalTitle', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalTitle?.message} errorMessage={errors?.proposalTitle?.message}
/> />
<ProposalFormDescription <ProposalFormDescription
registerField={register('proposalDescription', { registerField={register('proposalDescription', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalDescription?.message} errorMessage={errors?.proposalDescription?.message}
/> />
<ProposalFormSubheader>{t('NewAsset')}</ProposalFormSubheader> <ProposalFormSubheader>{t('NewAsset')}</ProposalFormSubheader>
<ProposalFormTerms <ProposalFormTerms
registerField={register('proposalTerms', { registerField={register('proposalTerms', {
required: t('Required'), required: t('Required'),
validate: (value) => validateJson(value), validate: (value) => validateJson(value),
})} })}
labelOverride={'Terms.newAsset (JSON format)'} labelOverride={'Terms.newAsset (JSON format)'}
errorMessage={errors?.proposalTerms?.message} errorMessage={errors?.proposalTerms?.message}
docsLink={DOCS_LINK} docsLink={DOCS_LINK}
/> />
<ProposalFormVoteAndEnactmentDeadline <ProposalFormVoteAndEnactmentDeadline
onVoteMinMax={setValue} onVoteMinMax={setValue}
voteRegister={register('proposalVoteDeadline', { voteRegister={register('proposalVoteDeadline', {
required: t('Required'), required: t('Required'),
})} })}
voteErrorMessage={errors?.proposalVoteDeadline?.message} voteErrorMessage={errors?.proposalVoteDeadline?.message}
voteMinClose={params.governance_proposal_asset_minClose} voteMinClose={params.governance_proposal_asset_minClose}
voteMaxClose={params.governance_proposal_asset_maxClose} voteMaxClose={params.governance_proposal_asset_maxClose}
onEnactMinMax={setValue} onEnactMinMax={setValue}
enactmentRegister={register('proposalEnactmentDeadline', { enactmentRegister={register('proposalEnactmentDeadline', {
required: t('Required'), required: t('Required'),
})} })}
enactmentErrorMessage={ enactmentErrorMessage={
errors?.proposalEnactmentDeadline?.message errors?.proposalEnactmentDeadline?.message
} }
enactmentMinClose={params.governance_proposal_asset_minEnact} enactmentMinClose={params.governance_proposal_asset_minEnact}
enactmentMaxClose={params.governance_proposal_asset_maxEnact} enactmentMaxClose={params.governance_proposal_asset_maxEnact}
validationRequired={true} validationRequired={true}
onValidationMinMax={setValue} onValidationMinMax={setValue}
validationRegister={register('proposalValidationDeadline', { validationRegister={register('proposalValidationDeadline', {
required: t('Required'), required: t('Required'),
})} })}
validationErrorMessage={ validationErrorMessage={
errors?.proposalValidationDeadline?.message errors?.proposalValidationDeadline?.message
} }
/> />
<ProposalFormSubmit isSubmitting={isSubmitting} /> <ProposalFormSubmit isSubmitting={isSubmitting} />
<ProposalFormTransactionDialog <ProposalFormDownloadJson downloadJson={viewJson} />
finalizedProposal={finalizedProposal} <ProposalFormTransactionDialog
TransactionDialog={Dialog} finalizedProposal={finalizedProposal}
/> TransactionDialog={Dialog}
</form> />
</div> </form>
</> </div>
)} </>
</VegaWalletContainer> )}
</AsyncRenderer> />
); );
}; };

View File

@ -104,6 +104,7 @@ describe('Propose New Market', () => {
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-submit')).toBeTruthy(); expect(screen.getByTestId('proposal-submit')).toBeTruthy();
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy(); expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
}); });
}); });

View File

@ -20,13 +20,14 @@ import {
ProposalFormTerms, ProposalFormTerms,
ProposalFormTitle, ProposalFormTitle,
ProposalFormTransactionDialog, ProposalFormTransactionDialog,
ProposalFormDownloadJson,
ProposalFormVoteAndEnactmentDeadline, ProposalFormVoteAndEnactmentDeadline,
} from '../../components/propose'; } from '../../components/propose';
import { ProposalMinRequirements } from '../../components/shared'; import { ProposalMinRequirements } from '../../components/shared';
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
import { ProposalUserAction } from '../../components/shared'; import { ProposalUserAction } from '../../components/shared';
import { downloadJson } from '../../../../lib/download-json';
export interface NewMarketProposalFormFields { export interface NewMarketProposalFormFields {
proposalVoteDeadline: string; proposalVoteDeadline: string;
@ -60,10 +61,11 @@ export const ProposeNewMarket = () => {
handleSubmit, handleSubmit,
formState: { isSubmitting, errors }, formState: { isSubmitting, errors },
setValue, setValue,
watch,
} = useForm<NewMarketProposalFormFields>(); } = useForm<NewMarketProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit(); const { finalizedProposal, submit, Dialog } = useProposalSubmit();
const onSubmit = async (fields: NewMarketProposalFormFields) => { const assembleProposal = (fields: NewMarketProposalFormFields) => {
const isVoteDeadlineAtMinimum = doesValueEquateToParam( const isVoteDeadlineAtMinimum = doesValueEquateToParam(
fields.proposalVoteDeadline, fields.proposalVoteDeadline,
params.governance_proposal_market_minClose params.governance_proposal_market_minClose
@ -81,7 +83,7 @@ export const ProposeNewMarket = () => {
params.governance_proposal_market_maxEnact params.governance_proposal_market_maxEnact
); );
await submit({ return {
rationale: { rationale: {
title: fields.proposalTitle, title: fields.proposalTitle,
description: fields.proposalDescription, description: fields.proposalDescription,
@ -101,7 +103,19 @@ export const ProposeNewMarket = () => {
isEnactmentDeadlineAtMaximum 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 ( return (
@ -109,104 +123,103 @@ export const ProposeNewMarket = () => {
loading={networkParamsLoading} loading={networkParamsLoading}
error={networkParamsError} error={networkParamsError}
data={params} data={params}
> render={(params) => (
<Heading title={t('NewMarketProposal')} /> <>
<VegaWalletContainer> <Heading title={t('NewMarketProposal')} />
{() => (
<>
<ProposalMinRequirements
minProposalBalance={
params.governance_proposal_market_minProposerBalance
}
spamProtectionMin={params.spam_protection_proposal_min_tokens}
userAction={ProposalUserAction.CREATE}
/>
{VEGA_DOCS_URL && ( <ProposalMinRequirements
<p className="text-sm" data-testid="proposal-docs-link"> minProposalBalance={
<span className="mr-1">{t('ProposalTermsText')}</span> params.governance_proposal_market_minProposerBalance
<ExternalLink }
href={`${ spamProtectionMin={params.spam_protection_proposal_min_tokens}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE userAction={ProposalUserAction.CREATE}
}${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 createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink> }${DOCS_LINK}`}
</p> target="_blank"
)} >{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p>
)}
{VEGA_EXPLORER_URL && ( {VEGA_EXPLORER_URL && (
<p className="text-sm"> <p className="text-sm">
{t('MoreMarketsInfo')}{' '} {t('MoreMarketsInfo')}{' '}
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/markets`} href={`${VEGA_EXPLORER_URL}/markets`}
target="_blank" target="_blank"
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink> >{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
</p> </p>
)} )}
<div data-testid="new-market-proposal-form"> <div data-testid="new-market-proposal-form">
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<ProposalFormSubheader> <ProposalFormSubheader>
{t('ProposalRationale')} {t('ProposalRationale')}
</ProposalFormSubheader> </ProposalFormSubheader>
<ProposalFormTitle <ProposalFormTitle
registerField={register('proposalTitle', { registerField={register('proposalTitle', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalTitle?.message} errorMessage={errors?.proposalTitle?.message}
/> />
<ProposalFormDescription <ProposalFormDescription
registerField={register('proposalDescription', { registerField={register('proposalDescription', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalDescription?.message} errorMessage={errors?.proposalDescription?.message}
/> />
<ProposalFormSubheader>{t('NewMarket')}</ProposalFormSubheader> <ProposalFormSubheader>{t('NewMarket')}</ProposalFormSubheader>
<ProposalFormTerms <ProposalFormTerms
registerField={register('proposalTerms', { registerField={register('proposalTerms', {
required: t('Required'), required: t('Required'),
validate: (value) => validateJson(value), validate: (value) => validateJson(value),
})} })}
labelOverride={'Terms.newMarket (JSON format)'} labelOverride={'Terms.newMarket (JSON format)'}
errorMessage={errors?.proposalTerms?.message} errorMessage={errors?.proposalTerms?.message}
docsLink={DOCS_LINK} docsLink={DOCS_LINK}
/> />
<ProposalFormVoteAndEnactmentDeadline <ProposalFormVoteAndEnactmentDeadline
onVoteMinMax={setValue} onVoteMinMax={setValue}
voteRegister={register('proposalVoteDeadline', { voteRegister={register('proposalVoteDeadline', {
required: t('Required'), required: t('Required'),
})} })}
voteErrorMessage={errors?.proposalVoteDeadline?.message} voteErrorMessage={errors?.proposalVoteDeadline?.message}
voteMinClose={params.governance_proposal_market_minClose} voteMinClose={params.governance_proposal_market_minClose}
voteMaxClose={params.governance_proposal_market_maxClose} voteMaxClose={params.governance_proposal_market_maxClose}
onEnactMinMax={setValue} onEnactMinMax={setValue}
enactmentRegister={register('proposalEnactmentDeadline', { enactmentRegister={register('proposalEnactmentDeadline', {
required: t('Required'), required: t('Required'),
})} })}
enactmentErrorMessage={ enactmentErrorMessage={
errors?.proposalEnactmentDeadline?.message errors?.proposalEnactmentDeadline?.message
} }
enactmentMinClose={params.governance_proposal_market_minEnact} enactmentMinClose={params.governance_proposal_market_minEnact}
enactmentMaxClose={params.governance_proposal_market_maxEnact} enactmentMaxClose={params.governance_proposal_market_maxEnact}
/> />
<ProposalFormSubmit isSubmitting={isSubmitting} /> <ProposalFormSubmit isSubmitting={isSubmitting} />
<ProposalFormTransactionDialog <ProposalFormDownloadJson downloadJson={viewJson} />
finalizedProposal={finalizedProposal} <ProposalFormTransactionDialog
TransactionDialog={Dialog} finalizedProposal={finalizedProposal}
/> TransactionDialog={Dialog}
</form> />
</div> </form>
</> </div>
)} </>
</VegaWalletContainer> )}
</AsyncRenderer> />
); );
}; };

View File

@ -2,7 +2,6 @@ import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
import { import {
AsyncRenderer, AsyncRenderer,
ExternalLink, ExternalLink,
@ -20,8 +19,10 @@ import { useProposalSubmit } from '@vegaprotocol/governance';
import { import {
ProposalFormSubmit, ProposalFormSubmit,
ProposalFormTransactionDialog, ProposalFormTransactionDialog,
ProposalFormDownloadJson,
} from '../../components/propose'; } from '../../components/propose';
import { ProposalRawMinRequirements } from './proposal-raw-min-requirements'; import { ProposalRawMinRequirements } from './proposal-raw-min-requirements';
import { downloadJson } from '../../../../lib/download-json';
export interface RawProposalFormFields { export interface RawProposalFormFields {
rawProposalData: string; rawProposalData: string;
@ -48,6 +49,7 @@ export const ProposeRaw = () => {
register, register,
handleSubmit, handleSubmit,
formState: { isSubmitting, errors }, formState: { isSubmitting, errors },
watch,
} = useForm<RawProposalFormFields>(); } = useForm<RawProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit(); const { finalizedProposal, submit, Dialog } = useProposalSubmit();
@ -57,88 +59,90 @@ export const ProposeRaw = () => {
await submit(JSON.parse(fields.rawProposalData)); await submit(JSON.parse(fields.rawProposalData));
}; };
const viewJson = () => {
const formData = watch();
downloadJson(JSON.stringify(formData), 'vega-raw-proposal');
};
return ( return (
<AsyncRenderer <AsyncRenderer
loading={networkParamsLoading} loading={networkParamsLoading}
error={networkParamsError} error={networkParamsError}
data={params} data={params}
> render={(params) => (
<Heading title={t('NewRawProposal')} /> <>
<VegaWalletContainer> <Heading title={t('NewRawProposal')} />
{() => (
<>
<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_DOCS_URL && ( <ProposalRawMinRequirements
<p className="text-sm" data-testid="proposal-docs-link"> assetMin={params.governance_proposal_asset_minProposerBalance}
<span className="mr-1">{t('ProposalTermsText')}</span> updateAssetMin={
<ExternalLink params.governance_proposal_updateAsset_minProposerBalance
href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE} }
target="_blank" marketMin={params.governance_proposal_market_minProposerBalance}
> updateMarketMin={
{createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE} params.governance_proposal_updateMarket_minProposerBalance
</ExternalLink> }
</p> updateNetParamMin={
)} params.governance_proposal_updateNetParam_minProposerBalance
}
freeformMin={params.governance_proposal_freeform_minProposerBalance}
spamProtectionMin={params.spam_protection_proposal_min_tokens}
/>
{VEGA_EXPLORER_URL && ( {VEGA_DOCS_URL && (
<p className="text-sm"> <p className="text-sm" data-testid="proposal-docs-link">
{t('MoreProposalsInfo')}{' '} <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/governance`} href={createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
target="_blank" target="_blank"
>{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink> >
</p> {createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE}
)} </ExternalLink>
</p>
)}
<div data-testid="raw-proposal-form"> {VEGA_EXPLORER_URL && (
<form onSubmit={handleSubmit(onSubmit)}> <p className="text-sm">
<FormGroup {t('MoreProposalsInfo')}{' '}
label="Make a proposal by submitting JSON" <ExternalLink
labelFor="proposal-data" href={`${VEGA_EXPLORER_URL}/governance`}
> target="_blank"
<TextArea >{`${VEGA_EXPLORER_URL}/governance`}</ExternalLink>
id="proposal-data" </p>
className="min-h-[200px]" )}
hasError={hasError}
data-testid="proposal-data" <div data-testid="raw-proposal-form">
{...register('rawProposalData', { <form onSubmit={handleSubmit(onSubmit)}>
required: t('Required'), <FormGroup
validate: (value) => validateJson(value), label="Make a proposal by submitting JSON"
})} labelFor="proposal-data"
/> >
{errors.rawProposalData?.message && ( <TextArea
<InputError intent="danger"> id="proposal-data"
{errors.rawProposalData?.message} className="min-h-[200px]"
</InputError> hasError={hasError}
)} data-testid="proposal-data"
</FormGroup> {...register('rawProposalData', {
<ProposalFormSubmit isSubmitting={isSubmitting} /> required: t('Required'),
<ProposalFormTransactionDialog validate: (value) => validateJson(value),
finalizedProposal={finalizedProposal} })}
TransactionDialog={Dialog}
/> />
</form> {errors.rawProposalData?.message && (
</div> <InputError intent="danger">
</> {errors.rawProposalData?.message}
)} </InputError>
</VegaWalletContainer> )}
</AsyncRenderer> </FormGroup>
<ProposalFormSubmit isSubmitting={isSubmitting} />
<ProposalFormDownloadJson downloadJson={viewJson} />
<ProposalFormTransactionDialog
finalizedProposal={finalizedProposal}
TransactionDialog={Dialog}
/>
</form>
</div>
</>
)}
/>
); );
}; };

View File

@ -111,6 +111,7 @@ describe('Propose Update Asset', () => {
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-submit')).toBeTruthy(); expect(screen.getByTestId('proposal-submit')).toBeTruthy();
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy(); expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
}); });
}); });

View File

@ -20,13 +20,14 @@ import {
ProposalFormTerms, ProposalFormTerms,
ProposalFormTitle, ProposalFormTitle,
ProposalFormTransactionDialog, ProposalFormTransactionDialog,
ProposalFormDownloadJson,
ProposalFormVoteAndEnactmentDeadline, ProposalFormVoteAndEnactmentDeadline,
} from '../../components/propose'; } from '../../components/propose';
import { ProposalMinRequirements } from '../../components/shared'; import { ProposalMinRequirements } from '../../components/shared';
import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit'; import { AsyncRenderer, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
import { ProposalUserAction } from '../../components/shared'; import { ProposalUserAction } from '../../components/shared';
import { downloadJson } from '../../../../lib/download-json';
export interface UpdateAssetProposalFormFields { export interface UpdateAssetProposalFormFields {
proposalVoteDeadline: string; proposalVoteDeadline: string;
@ -60,10 +61,11 @@ export const ProposeUpdateAsset = () => {
handleSubmit, handleSubmit,
formState: { isSubmitting, errors }, formState: { isSubmitting, errors },
setValue, setValue,
watch,
} = useForm<UpdateAssetProposalFormFields>(); } = useForm<UpdateAssetProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit(); const { finalizedProposal, submit, Dialog } = useProposalSubmit();
const onSubmit = async (fields: UpdateAssetProposalFormFields) => { const assembleProposal = (fields: UpdateAssetProposalFormFields) => {
const isVoteDeadlineAtMinimum = doesValueEquateToParam( const isVoteDeadlineAtMinimum = doesValueEquateToParam(
fields.proposalVoteDeadline, fields.proposalVoteDeadline,
params.governance_proposal_updateAsset_minClose params.governance_proposal_updateAsset_minClose
@ -81,7 +83,7 @@ export const ProposeUpdateAsset = () => {
params.governance_proposal_updateAsset_maxEnact params.governance_proposal_updateAsset_maxEnact
); );
await submit({ return {
rationale: { rationale: {
title: fields.proposalTitle, title: fields.proposalTitle,
description: fields.proposalDescription, description: fields.proposalDescription,
@ -101,7 +103,19 @@ export const ProposeUpdateAsset = () => {
isEnactmentDeadlineAtMaximum 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 ( return (
@ -109,110 +123,107 @@ export const ProposeUpdateAsset = () => {
loading={networkParamsLoading} loading={networkParamsLoading}
error={networkParamsError} error={networkParamsError}
data={params} data={params}
> render={(params) => (
<Heading title={t('UpdateAssetProposal')} /> <>
<VegaWalletContainer> <Heading title={t('UpdateAssetProposal')} />
{() => (
<>
<ProposalMinRequirements
minProposalBalance={
params.governance_proposal_updateAsset_minProposerBalance
}
spamProtectionMin={params.spam_protection_proposal_min_tokens}
userAction={ProposalUserAction.CREATE}
/>
{VEGA_DOCS_URL && ( <ProposalMinRequirements
<p className="text-sm" data-testid="proposal-docs-link"> minProposalBalance={
<span className="mr-1">{t('ProposalTermsText')}</span> params.governance_proposal_updateAsset_minProposerBalance
<ExternalLink }
href={`${ spamProtectionMin={params.spam_protection_proposal_min_tokens}
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE userAction={ProposalUserAction.CREATE}
}${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 createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink> }${DOCS_LINK}`}
</p> target="_blank"
)} >{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p>
)}
{VEGA_EXPLORER_URL && ( {VEGA_EXPLORER_URL && (
<p className="text-sm"> <p className="text-sm">
{t('MoreAssetsInfo')}{' '} {t('MoreAssetsInfo')}{' '}
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/assets`} href={`${VEGA_EXPLORER_URL}/assets`}
target="_blank" target="_blank"
>{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink> >{`${VEGA_EXPLORER_URL}/assets`}</ExternalLink>
</p> </p>
)} )}
<div data-testid="update-asset-proposal-form"> <div data-testid="update-asset-proposal-form">
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<ProposalFormSubheader> <ProposalFormSubheader>
{t('ProposalRationale')} {t('ProposalRationale')}
</ProposalFormSubheader> </ProposalFormSubheader>
<ProposalFormTitle <ProposalFormTitle
registerField={register('proposalTitle', { registerField={register('proposalTitle', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalTitle?.message} errorMessage={errors?.proposalTitle?.message}
/> />
<ProposalFormDescription <ProposalFormDescription
registerField={register('proposalDescription', { registerField={register('proposalDescription', {
required: t('Required'), required: t('Required'),
})} })}
errorMessage={errors?.proposalDescription?.message} errorMessage={errors?.proposalDescription?.message}
/> />
<ProposalFormSubheader> <ProposalFormSubheader>{t('UpdateAsset')}</ProposalFormSubheader>
{t('UpdateAsset')}
</ProposalFormSubheader>
<ProposalFormTerms <ProposalFormTerms
registerField={register('proposalTerms', { registerField={register('proposalTerms', {
required: t('Required'), required: t('Required'),
validate: (value) => validateJson(value), validate: (value) => validateJson(value),
})} })}
labelOverride={'Terms.updateAsset (JSON format)'} labelOverride={'Terms.updateAsset (JSON format)'}
errorMessage={errors?.proposalTerms?.message} errorMessage={errors?.proposalTerms?.message}
docsLink={DOCS_LINK} docsLink={DOCS_LINK}
/> />
<ProposalFormVoteAndEnactmentDeadline <ProposalFormVoteAndEnactmentDeadline
onVoteMinMax={setValue} onVoteMinMax={setValue}
voteRegister={register('proposalVoteDeadline', { voteRegister={register('proposalVoteDeadline', {
required: t('Required'), required: t('Required'),
})} })}
voteErrorMessage={errors?.proposalVoteDeadline?.message} voteErrorMessage={errors?.proposalVoteDeadline?.message}
voteMinClose={params.governance_proposal_updateAsset_minClose} voteMinClose={params.governance_proposal_updateAsset_minClose}
voteMaxClose={params.governance_proposal_updateAsset_maxClose} voteMaxClose={params.governance_proposal_updateAsset_maxClose}
onEnactMinMax={setValue} onEnactMinMax={setValue}
enactmentRegister={register('proposalEnactmentDeadline', { enactmentRegister={register('proposalEnactmentDeadline', {
required: t('Required'), required: t('Required'),
})} })}
enactmentErrorMessage={ enactmentErrorMessage={
errors?.proposalEnactmentDeadline?.message errors?.proposalEnactmentDeadline?.message
} }
enactmentMinClose={ enactmentMinClose={
params.governance_proposal_updateAsset_minEnact params.governance_proposal_updateAsset_minEnact
} }
enactmentMaxClose={ enactmentMaxClose={
params.governance_proposal_updateAsset_maxEnact params.governance_proposal_updateAsset_maxEnact
} }
/> />
<ProposalFormSubmit isSubmitting={isSubmitting} /> <ProposalFormSubmit isSubmitting={isSubmitting} />
<ProposalFormTransactionDialog <ProposalFormDownloadJson downloadJson={viewJson} />
finalizedProposal={finalizedProposal} <ProposalFormTransactionDialog
TransactionDialog={Dialog} finalizedProposal={finalizedProposal}
/> TransactionDialog={Dialog}
</form> />
</div> </form>
</> </div>
)} </>
</VegaWalletContainer> )}
</AsyncRenderer> />
); );
}; };

View File

@ -187,6 +187,7 @@ describe('Propose Update Market', () => {
expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-vote-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy(); expect(screen.getByTestId('proposal-enactment-deadline')).toBeTruthy();
expect(screen.getByTestId('proposal-submit')).toBeTruthy(); expect(screen.getByTestId('proposal-submit')).toBeTruthy();
expect(screen.getByTestId('proposal-download-json')).toBeTruthy();
expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy(); expect(screen.getByTestId('proposal-transaction-dialog')).toBeTruthy();
}); });

View File

@ -21,6 +21,7 @@ import {
ProposalFormTerms, ProposalFormTerms,
ProposalFormTitle, ProposalFormTitle,
ProposalFormTransactionDialog, ProposalFormTransactionDialog,
ProposalFormDownloadJson,
ProposalFormVoteAndEnactmentDeadline, ProposalFormVoteAndEnactmentDeadline,
} from '../../components/propose'; } from '../../components/propose';
import { ProposalMinRequirements } from '../../components/shared'; import { ProposalMinRequirements } from '../../components/shared';
@ -34,9 +35,9 @@ import {
Select, Select,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { Heading } from '../../../../components/heading'; import { Heading } from '../../../../components/heading';
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
import { ProposalUserAction } from '../../components/shared'; import { ProposalUserAction } from '../../components/shared';
import { useProposalMarketsQueryQuery } from './__generated___/UpdateMarket'; import { useProposalMarketsQueryQuery } from './__generated___/UpdateMarket';
import { downloadJson } from '../../../../lib/download-json';
export interface UpdateMarketProposalFormFields { export interface UpdateMarketProposalFormFields {
proposalVoteDeadline: string; proposalVoteDeadline: string;
@ -101,10 +102,11 @@ export const ProposeUpdateMarket = () => {
handleSubmit, handleSubmit,
formState: { isSubmitting, errors }, formState: { isSubmitting, errors },
setValue, setValue,
watch,
} = useForm<UpdateMarketProposalFormFields>(); } = useForm<UpdateMarketProposalFormFields>();
const { finalizedProposal, submit, Dialog } = useProposalSubmit(); const { finalizedProposal, submit, Dialog } = useProposalSubmit();
const onSubmit = async (fields: UpdateMarketProposalFormFields) => { const assembleProposal = (fields: UpdateMarketProposalFormFields) => {
const isVoteDeadlineAtMinimum = doesValueEquateToParam( const isVoteDeadlineAtMinimum = doesValueEquateToParam(
fields.proposalVoteDeadline, fields.proposalVoteDeadline,
params.governance_proposal_updateMarket_minClose params.governance_proposal_updateMarket_minClose
@ -122,7 +124,7 @@ export const ProposeUpdateMarket = () => {
params.governance_proposal_updateMarket_maxEnact params.governance_proposal_updateMarket_maxEnact
); );
await submit({ return {
rationale: { rationale: {
title: fields.proposalTitle, title: fields.proposalTitle,
description: fields.proposalDescription, description: fields.proposalDescription,
@ -145,176 +147,182 @@ export const ProposeUpdateMarket = () => {
isEnactmentDeadlineAtMaximum 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 ( return (
<AsyncRenderer <AsyncRenderer
loading={networkParamsLoading && marketsLoading} loading={networkParamsLoading && marketsLoading}
error={networkParamsError && marketsError} error={networkParamsError && marketsError}
data={params && marketsData} data={{ ...params, ...marketsData }}
> render={(data) => (
<Heading title={t('UpdateMarketProposal')} /> <>
<VegaWalletContainer> <Heading title={t('UpdateMarketProposal')} />
{() => ( <ProposalMinRequirements
<> minProposalBalance={
<ProposalMinRequirements data.governance_proposal_updateMarket_minProposerBalance
minProposalBalance={ }
params.governance_proposal_updateMarket_minProposerBalance spamProtectionMin={data.spam_protection_proposal_min_tokens}
} userAction={ProposalUserAction.CREATE}
spamProtectionMin={params.spam_protection_proposal_min_tokens} />
userAction={ProposalUserAction.CREATE}
/>
{VEGA_DOCS_URL && ( {VEGA_DOCS_URL && (
<p className="text-sm" data-testid="proposal-docs-link"> <p className="text-sm" data-testid="proposal-docs-link">
<span className="mr-1">{t('ProposalTermsText')}</span> <span className="mr-1">{t('ProposalTermsText')}</span>
<ExternalLink <ExternalLink
href={`${ href={`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}
target="_blank"
>{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink> }${DOCS_LINK}`}
</p> target="_blank"
)} >{`${
createDocsLinks(VEGA_DOCS_URL).PROPOSALS_GUIDE
}${DOCS_LINK}`}</ExternalLink>
</p>
)}
{VEGA_EXPLORER_URL && ( {VEGA_EXPLORER_URL && (
<p className="text-sm"> <p className="text-sm">
{t('MoreMarketsInfo')}{' '} {t('MoreMarketsInfo')}{' '}
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/markets`} href={`${VEGA_EXPLORER_URL}/markets`}
target="_blank" target="_blank"
>{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink> >{`${VEGA_EXPLORER_URL}/markets`}</ExternalLink>
</p> </p>
)} )}
<div data-testid="update-market-proposal-form"> <div data-testid="update-market-proposal-form">
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<ProposalFormSubheader> <ProposalFormSubheader>
{t('ProposalRationale')} {t('ProposalRationale')}
</ProposalFormSubheader> </ProposalFormSubheader>
<ProposalFormTitle <ProposalFormTitle
registerField={register('proposalTitle', { 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'), required: t('Required'),
})} })}
errorMessage={errors?.proposalTitle?.message} onChange={(e) => setSelectedMarket(e.target.value)}
/>
<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 <option value="">{t('SelectMarket')}</option>
data-testid="proposal-market-select" {sortedMarkets.map((market) => (
id="proposal-market" <option value={market.id} key={market.id}>
{...register('proposalMarketId', { {market.tradableInstrument.instrument.name}
required: t('Required'), </option>
})} ))}
onChange={(e) => setSelectedMarket(e.target.value)} </Select>
> {errors?.proposalMarketId?.message && (
<option value="">{t('SelectMarket')}</option> <InputError intent="danger">
{sortedMarkets.map((market) => ( {errors?.proposalMarketId?.message}
<option value={market.id} key={market.id}> </InputError>
{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>
)} )}
</FormGroup>
<ProposalFormTerms {selectedMarket && (
registerField={register('proposalTerms', { <div className="mt-[-20px] mb-6">
required: t('Required'), <KeyValueTable data-testid="update-market-details">
validate: (value) => validateJson(value), <KeyValueTableRow>
})} {t('MarketName')}
labelOverride={t('ProposeUpdateMarketTerms')} {
errorMessage={errors?.proposalTerms?.message} data.marketsConnection?.edges?.find(
docsLink={DOCS_LINK} ({ 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 <ProposalFormTerms
onVoteMinMax={setValue} registerField={register('proposalTerms', {
voteRegister={register('proposalVoteDeadline', { required: t('Required'),
required: t('Required'), validate: (value) => validateJson(value),
})} })}
voteErrorMessage={errors?.proposalVoteDeadline?.message} labelOverride={t('ProposeUpdateMarketTerms')}
voteMinClose={ errorMessage={errors?.proposalTerms?.message}
params.governance_proposal_updateMarket_minClose docsLink={DOCS_LINK}
} />
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
}
/>
<ProposalFormSubmit isSubmitting={isSubmitting} /> <ProposalFormVoteAndEnactmentDeadline
<ProposalFormTransactionDialog onVoteMinMax={setValue}
finalizedProposal={finalizedProposal} voteRegister={register('proposalVoteDeadline', {
TransactionDialog={Dialog} required: t('Required'),
/> })}
</form> voteErrorMessage={errors?.proposalVoteDeadline?.message}
</div> voteMinClose={data.governance_proposal_updateMarket_minClose}
</> voteMaxClose={data.governance_proposal_updateMarket_maxClose}
)} onEnactMinMax={setValue}
</VegaWalletContainer> enactmentRegister={register('proposalEnactmentDeadline', {
</AsyncRenderer> 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>
</>
)}
/>
); );
}; };