feat(governance): improve asset proposal details view (#4216)

This commit is contained in:
Sam Keen 2023-06-29 14:41:25 +01:00 committed by GitHub
parent 5e93e98f07
commit a31008ea26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 11 deletions

View File

@ -836,5 +836,6 @@
"AllProposals": "All proposals",
"RejectedProposals": "Rejected proposals",
"networkGovernance": "Network governance",
"networkUpgrades": "Network upgrades"
"networkUpgrades": "Network upgrades",
"assetSpecification": "Asset specification"
}

View File

@ -0,0 +1 @@
export * from './proposal-asset-details';

View File

@ -0,0 +1,48 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { AssetDetail, AssetDetailsTable } from '@vegaprotocol/assets';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
export const ProposalAssetDetails = ({
asset,
}: {
asset: AssetFieldsFragment;
}) => {
const { t } = useTranslation();
const [showAssetDetails, setShowAssetDetails] = useState(false);
return (
<section data-testid="proposal-asset-details">
<CollapsibleToggle
toggleState={showAssetDetails}
setToggleState={setShowAssetDetails}
dataTestId={'proposal-asset-details-toggle'}
>
<SubHeading title={t('assetSpecification')} />
</CollapsibleToggle>
{showAssetDetails && (
<div className="mb-10 pb-4">
<AssetDetailsTable
asset={asset}
omitRows={[
AssetDetail.STATUS,
AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
AssetDetail.GLOBAL_REWARD_POOL_ACCOUNT_BALANCE,
AssetDetail.MAKER_PAID_FEES_ACCOUNT_BALANCE,
AssetDetail.MAKER_RECEIVED_FEES_ACCOUNT_BALANCE,
AssetDetail.LP_FEE_REWARD_ACCOUNT_BALANCE,
AssetDetail.MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE,
]}
inline={true}
noBorder={true}
dtClassName="text-black dark:text-white text-ui !px-0 !font-normal"
ddClassName="text-black dark:text-white text-ui !px-0 !font-normal max-w-full"
/>
</div>
)}
</section>
);
};

View File

@ -10,6 +10,7 @@ import { ProposalDescription } from '../proposal-description';
import { ProposalChangeTable } from '../proposal-change-table';
import { ProposalJson } from '../proposal-json';
import { ProposalVotesTable } from '../proposal-votes-table';
import { ProposalAssetDetails } from '../proposal-asset-details';
import { VoteDetails } from '../vote-details';
import { ListAsset } from '../list-asset';
import Routes from '../../../routes';
@ -17,6 +18,8 @@ import { ProposalMarketData } from '../proposal-market-data';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { AssetQuery } from '@vegaprotocol/assets';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { ProposalState } from '@vegaprotocol/types';
export enum ProposalType {
@ -30,6 +33,7 @@ export enum ProposalType {
export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
newMarketData?: MarketInfoWithData | null;
assetData?: AssetQuery | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
}
@ -38,6 +42,7 @@ export const Proposal = ({
proposal,
restData,
newMarketData,
assetData,
}: ProposalProps) => {
const { t } = useTranslation();
const { params, loading, error } = useNetworkParams([
@ -54,6 +59,23 @@ export const Proposal = ({
return null;
}
let asset = assetData
? removePaginationWrapper(assetData.assetsConnection?.edges)[0]
: undefined;
if (proposal.terms.change.__typename === 'UpdateAsset' && asset) {
asset = {
...asset,
quantum: proposal.terms.change.quantum,
};
if (asset.source.__typename === 'ERC20') {
asset.source.lifetimeLimit = proposal.terms.change.source.lifetimeLimit;
asset.source.withdrawThreshold =
proposal.terms.change.source.withdrawThreshold;
}
}
let minVoterBalance = null;
let proposalType = null;
@ -138,6 +160,14 @@ export const Proposal = ({
</div>
)}
{(proposal.terms.change.__typename === 'NewAsset' ||
proposal.terms.change.__typename === 'UpdateAsset') &&
asset && (
<div className="mb-4">
<ProposalAssetDetails asset={asset} />
</div>
)}
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
</div>

View File

@ -191,12 +191,13 @@ export const ProposalsList = ({
{sortedProposals.open.length > 0 ||
sortedProtocolUpgradeProposals.open.length > 0 ? (
<ul data-testid="open-proposals">
{sortedProtocolUpgradeProposals.open.map((proposal) => (
<ProtocolUpgradeProposalsListItem
key={proposal.upgradeBlockHeight}
proposal={proposal}
/>
))}
{filterString.length < 1 &&
sortedProtocolUpgradeProposals.open.map((proposal) => (
<ProtocolUpgradeProposalsListItem
key={proposal.upgradeBlockHeight}
proposal={proposal}
/>
))}
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
<ProposalsListItem key={proposal?.id} proposal={proposal} />

View File

@ -9,6 +9,7 @@ import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
import { useAssetQuery } from '@vegaprotocol/assets';
export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>();
@ -35,6 +36,25 @@ export const ProposalContainer = () => {
},
});
const {
data: assetData,
loading: assetLoading,
error: assetError,
} = useAssetQuery({
fetchPolicy: 'network-only',
variables: {
assetId:
(data?.proposal?.terms.change.__typename === 'NewAsset' &&
data?.proposal?.id) ||
(data?.proposal?.terms.change.__typename === 'UpdateAsset' &&
data.proposal.terms.change.assetId) ||
'',
},
skip: !['NewAsset', 'UpdateAsset'].includes(
data?.proposal?.terms?.change?.__typename || ''
),
});
useEffect(() => {
const interval = setInterval(refetch, 2000);
return () => clearInterval(interval);
@ -42,15 +62,20 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer
loading={loading || newMarketLoading}
error={error || newMarketError}
data={newMarketData ? { newMarketData, data } : data}
loading={loading || newMarketLoading || assetLoading}
error={error || newMarketError || assetError}
data={{
...data,
...(newMarketData ? { newMarketData } : {}),
...(assetData ? { assetData } : {}),
}}
>
{data?.proposal ? (
<Proposal
proposal={data.proposal}
restData={restData}
newMarketData={newMarketData}
assetData={assetData}
/>
) : (
<ProposalNotFound />

View File

@ -80,4 +80,14 @@ describe('AssetDetailsTable', () => {
}
}
);
it('omits specified rows when omitRows prop is provided', async () => {
const asset = generateERC20Asset(1, Schema.AssetStatus.STATUS_ENABLED);
const omittedKeys = [AssetDetail.TYPE, AssetDetail.DECIMALS];
render(<AssetDetailsTable asset={asset} omitRows={omittedKeys} />);
for (const key of omittedKeys) {
expect(screen.queryByTestId(testId(key, 'label'))).toBeNull();
expect(screen.queryByTestId(testId(key, 'value'))).toBeNull();
}
});
});

View File

@ -224,9 +224,11 @@ export const testId = (detail: AssetDetail, field: 'label' | 'value') =>
export type AssetDetailsTableProps = {
asset: Asset;
omitRows?: AssetDetail[];
} & Omit<KeyValueTableRowProps, 'children'>;
export const AssetDetailsTable = ({
asset,
omitRows = [],
...props
}: AssetDetailsTableProps) => {
const longStringModifiers = (key: AssetDetail, value: string) =>
@ -243,7 +245,7 @@ export const AssetDetailsTable = ({
return (
<KeyValueTable>
{details
.filter(({ value }) => Boolean(value))
.filter(({ key, value }) => Boolean(value) && !omitRows.includes(key))
.map(({ key, label, value, tooltip, valueTooltip }) => (
<KeyValueTableRow key={key} {...props}>
<div