feat(governance): improve asset proposal details view (#4216)
This commit is contained in:
parent
5e93e98f07
commit
a31008ea26
@ -836,5 +836,6 @@
|
|||||||
"AllProposals": "All proposals",
|
"AllProposals": "All proposals",
|
||||||
"RejectedProposals": "Rejected proposals",
|
"RejectedProposals": "Rejected proposals",
|
||||||
"networkGovernance": "Network governance",
|
"networkGovernance": "Network governance",
|
||||||
"networkUpgrades": "Network upgrades"
|
"networkUpgrades": "Network upgrades",
|
||||||
|
"assetSpecification": "Asset specification"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export * from './proposal-asset-details';
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
@ -10,6 +10,7 @@ import { ProposalDescription } from '../proposal-description';
|
|||||||
import { ProposalChangeTable } from '../proposal-change-table';
|
import { ProposalChangeTable } from '../proposal-change-table';
|
||||||
import { ProposalJson } from '../proposal-json';
|
import { ProposalJson } from '../proposal-json';
|
||||||
import { ProposalVotesTable } from '../proposal-votes-table';
|
import { ProposalVotesTable } from '../proposal-votes-table';
|
||||||
|
import { ProposalAssetDetails } from '../proposal-asset-details';
|
||||||
import { VoteDetails } from '../vote-details';
|
import { VoteDetails } from '../vote-details';
|
||||||
import { ListAsset } from '../list-asset';
|
import { ListAsset } from '../list-asset';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
@ -17,6 +18,8 @@ import { ProposalMarketData } from '../proposal-market-data';
|
|||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||||
|
import type { AssetQuery } from '@vegaprotocol/assets';
|
||||||
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export enum ProposalType {
|
export enum ProposalType {
|
||||||
@ -30,6 +33,7 @@ export enum ProposalType {
|
|||||||
export interface ProposalProps {
|
export interface ProposalProps {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
newMarketData?: MarketInfoWithData | null;
|
newMarketData?: MarketInfoWithData | null;
|
||||||
|
assetData?: AssetQuery | null;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
restData: any;
|
restData: any;
|
||||||
}
|
}
|
||||||
@ -38,6 +42,7 @@ export const Proposal = ({
|
|||||||
proposal,
|
proposal,
|
||||||
restData,
|
restData,
|
||||||
newMarketData,
|
newMarketData,
|
||||||
|
assetData,
|
||||||
}: ProposalProps) => {
|
}: ProposalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { params, loading, error } = useNetworkParams([
|
const { params, loading, error } = useNetworkParams([
|
||||||
@ -54,6 +59,23 @@ export const Proposal = ({
|
|||||||
return null;
|
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 minVoterBalance = null;
|
||||||
let proposalType = null;
|
let proposalType = null;
|
||||||
|
|
||||||
@ -138,6 +160,14 @@ export const Proposal = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{(proposal.terms.change.__typename === 'NewAsset' ||
|
||||||
|
proposal.terms.change.__typename === 'UpdateAsset') &&
|
||||||
|
asset && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<ProposalAssetDetails asset={asset} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<ProposalJson proposal={restData?.data?.proposal} />
|
<ProposalJson proposal={restData?.data?.proposal} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -191,12 +191,13 @@ export const ProposalsList = ({
|
|||||||
{sortedProposals.open.length > 0 ||
|
{sortedProposals.open.length > 0 ||
|
||||||
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
||||||
<ul data-testid="open-proposals">
|
<ul data-testid="open-proposals">
|
||||||
{sortedProtocolUpgradeProposals.open.map((proposal) => (
|
{filterString.length < 1 &&
|
||||||
<ProtocolUpgradeProposalsListItem
|
sortedProtocolUpgradeProposals.open.map((proposal) => (
|
||||||
key={proposal.upgradeBlockHeight}
|
<ProtocolUpgradeProposalsListItem
|
||||||
proposal={proposal}
|
key={proposal.upgradeBlockHeight}
|
||||||
/>
|
proposal={proposal}
|
||||||
))}
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
||||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
||||||
|
@ -9,6 +9,7 @@ import { useFetch } from '@vegaprotocol/react-helpers';
|
|||||||
import { ENV } from '../../../config';
|
import { ENV } from '../../../config';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
||||||
|
import { useAssetQuery } from '@vegaprotocol/assets';
|
||||||
|
|
||||||
export const ProposalContainer = () => {
|
export const ProposalContainer = () => {
|
||||||
const params = useParams<{ proposalId: string }>();
|
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(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(refetch, 2000);
|
const interval = setInterval(refetch, 2000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
@ -42,15 +62,20 @@ export const ProposalContainer = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
loading={loading || newMarketLoading}
|
loading={loading || newMarketLoading || assetLoading}
|
||||||
error={error || newMarketError}
|
error={error || newMarketError || assetError}
|
||||||
data={newMarketData ? { newMarketData, data } : data}
|
data={{
|
||||||
|
...data,
|
||||||
|
...(newMarketData ? { newMarketData } : {}),
|
||||||
|
...(assetData ? { assetData } : {}),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{data?.proposal ? (
|
{data?.proposal ? (
|
||||||
<Proposal
|
<Proposal
|
||||||
proposal={data.proposal}
|
proposal={data.proposal}
|
||||||
restData={restData}
|
restData={restData}
|
||||||
newMarketData={newMarketData}
|
newMarketData={newMarketData}
|
||||||
|
assetData={assetData}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ProposalNotFound />
|
<ProposalNotFound />
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -224,9 +224,11 @@ export const testId = (detail: AssetDetail, field: 'label' | 'value') =>
|
|||||||
|
|
||||||
export type AssetDetailsTableProps = {
|
export type AssetDetailsTableProps = {
|
||||||
asset: Asset;
|
asset: Asset;
|
||||||
|
omitRows?: AssetDetail[];
|
||||||
} & Omit<KeyValueTableRowProps, 'children'>;
|
} & Omit<KeyValueTableRowProps, 'children'>;
|
||||||
export const AssetDetailsTable = ({
|
export const AssetDetailsTable = ({
|
||||||
asset,
|
asset,
|
||||||
|
omitRows = [],
|
||||||
...props
|
...props
|
||||||
}: AssetDetailsTableProps) => {
|
}: AssetDetailsTableProps) => {
|
||||||
const longStringModifiers = (key: AssetDetail, value: string) =>
|
const longStringModifiers = (key: AssetDetail, value: string) =>
|
||||||
@ -243,7 +245,7 @@ export const AssetDetailsTable = ({
|
|||||||
return (
|
return (
|
||||||
<KeyValueTable>
|
<KeyValueTable>
|
||||||
{details
|
{details
|
||||||
.filter(({ value }) => Boolean(value))
|
.filter(({ key, value }) => Boolean(value) && !omitRows.includes(key))
|
||||||
.map(({ key, label, value, tooltip, valueTooltip }) => (
|
.map(({ key, label, value, tooltip, valueTooltip }) => (
|
||||||
<KeyValueTableRow key={key} {...props}>
|
<KeyValueTableRow key={key} {...props}>
|
||||||
<div
|
<div
|
||||||
|
Loading…
Reference in New Issue
Block a user