fix(explorer): fix bug where no votes displayed a yes icon (#3699)
This commit is contained in:
parent
f121836b4e
commit
8a080d3279
@ -1,3 +1,5 @@
|
|||||||
|
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
// https://github.com/vegaprotocol/vega/blob/develop/core/blockchain/response.go
|
// https://github.com/vegaprotocol/vega/blob/develop/core/blockchain/response.go
|
||||||
export const ErrorCodes = new Map([
|
export const ErrorCodes = new Map([
|
||||||
[51, 'Transaction failed validation'],
|
[51, 'Transaction failed validation'],
|
||||||
@ -28,7 +30,11 @@ export const ChainResponseCode = ({
|
|||||||
}: ChainResponseCodeProps) => {
|
}: ChainResponseCodeProps) => {
|
||||||
const isSuccess = successCodes.has(code);
|
const isSuccess = successCodes.has(code);
|
||||||
|
|
||||||
const icon = isSuccess ? '✅' : '❌';
|
const icon = isSuccess ? (
|
||||||
|
<Icon name="tick-circle" className="fill-vega-green-550" />
|
||||||
|
) : (
|
||||||
|
<Icon name="cross" className="fill-vega-pink-550" />
|
||||||
|
);
|
||||||
const label = ErrorCodes.get(code) || 'Unknown response code';
|
const label = ErrorCodes.get(code) || 'Unknown response code';
|
||||||
|
|
||||||
// Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245
|
// Hack for batches with many errors - see https://github.com/vegaprotocol/vega/issues/7245
|
||||||
@ -36,7 +42,7 @@ export const ChainResponseCode = ({
|
|||||||
error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error;
|
error && error.length > 100 ? error.replace(/,/g, ',\r\n') : error;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div title={`Response code: ${code} - ${label}`} className="inline-block">
|
<div title={`Response code: ${code} - ${label}`} className=" inline-block">
|
||||||
<span
|
<span
|
||||||
className="mr-2"
|
className="mr-2"
|
||||||
aria-label={isSuccess ? 'Success' : 'Warning'}
|
aria-label={isSuccess ? 'Success' : 'Warning'}
|
||||||
|
@ -4,6 +4,7 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
|
|||||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||||
import ProposalLink from '../../links/proposal-link/proposal-link';
|
import ProposalLink from '../../links/proposal-link/proposal-link';
|
||||||
|
import { VoteIcon } from '../../vote-icon/vote-icon';
|
||||||
|
|
||||||
interface TxProposalVoteProps {
|
interface TxProposalVoteProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
@ -30,27 +31,22 @@ export const TxProposalVote = ({
|
|||||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vote = txData.command.voteSubmission.value ? '👍' : '👎';
|
const vote = txData.command.voteSubmission.value === 'VALUE_YES';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||||
<TableRow modifier="bordered">
|
|
||||||
<TableCell>{t('Proposal ID')}</TableCell>
|
|
||||||
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell>{t('Proposal details')}</TableCell>
|
<TableCell>{t('Proposal details')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<ProposalLink id={txData.command.voteSubmission.proposalId} />
|
<ProposalLink id={txData.command.voteSubmission.proposalId} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow modifier="bordered">
|
|
||||||
<TableCell>{t('Proposal')}</TableCell>
|
|
||||||
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell>{t('Vote')}</TableCell>
|
<TableCell>{t('Vote')}</TableCell>
|
||||||
<TableCell>{vote}</TableCell>
|
<TableCell>
|
||||||
|
<VoteIcon vote={vote} />
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableWithTbody>
|
</TableWithTbody>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { components } from '../../../types/explorer';
|
import type { components } from '../../../types/explorer';
|
||||||
|
import { VoteIcon } from '../vote-icon/vote-icon';
|
||||||
|
|
||||||
interface TxOrderTypeProps {
|
interface TxOrderTypeProps {
|
||||||
orderType: string;
|
orderType: string;
|
||||||
@ -137,12 +138,15 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
|||||||
let type = displayString[orderType] || orderType;
|
let type = displayString[orderType] || orderType;
|
||||||
|
|
||||||
let colours =
|
let colours =
|
||||||
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-150';
|
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
|
||||||
|
|
||||||
// This will get unwieldy and should probably produce a different colour of tag
|
// This will get unwieldy and should probably produce a different colour of tag
|
||||||
if (type === 'Chain Event' && !!command?.chainEvent) {
|
if (type === 'Chain Event' && !!command?.chainEvent) {
|
||||||
type = getLabelForChainEvent(command.chainEvent);
|
type = getLabelForChainEvent(command.chainEvent);
|
||||||
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
||||||
|
} else if (type === 'Validator Heartbeat') {
|
||||||
|
colours =
|
||||||
|
'text-white dark-text-white bg-vega-light-200 dark:bg-vega-dark-100';
|
||||||
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
||||||
if (command && !!command.proposalSubmission) {
|
if (command && !!command.proposalSubmission) {
|
||||||
type = getLabelForProposal(command.proposalSubmission);
|
type = getLabelForProposal(command.proposalSubmission);
|
||||||
@ -150,6 +154,16 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
|||||||
colours = 'text-black bg-vega-yellow';
|
colours = 'text-black bg-vega-yellow';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'Vote on Proposal') {
|
||||||
|
return (
|
||||||
|
<VoteIcon
|
||||||
|
vote={command?.voteSubmission?.value === 'VALUE_YES'}
|
||||||
|
yesText="Proposal vote"
|
||||||
|
noText="Proposal vote"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (type === 'Vote on Proposal' || type === 'Vote Submission') {
|
if (type === 'Vote on Proposal' || type === 'Vote Submission') {
|
||||||
colours = 'text-black bg-vega-yellow';
|
colours = 'text-black bg-vega-yellow';
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,6 @@ describe('Txs infinite list item', () => {
|
|||||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||||
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||||
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||||
expect(screen.getByTestId('tx-success')).toHaveTextContent('Success: ✅');
|
expect(screen.getByTestId('tx-success')).toHaveTextContent('Success');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -31,7 +31,7 @@ export const TxsInfiniteListItem = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
data-testid="transaction-row"
|
data-testid="transaction-row"
|
||||||
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10 py-2"
|
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="text-sm col-span-10 md:col-span-3 leading-none"
|
className="text-sm col-span-10 md:col-span-3 leading-none"
|
||||||
@ -83,7 +83,7 @@ export const TxsInfiniteListItem = ({
|
|||||||
data-testid="tx-success"
|
data-testid="tx-success"
|
||||||
>
|
>
|
||||||
<span className="md:hidden uppercase text-vega-dark-300">
|
<span className="md:hidden uppercase text-vega-dark-300">
|
||||||
Success:
|
Success
|
||||||
</span>
|
</span>
|
||||||
{isNumber(code) ? (
|
{isNumber(code) ? (
|
||||||
<ChainResponseCode code={code} hideLabel={true} />
|
<ChainResponseCode code={code} hideLabel={true} />
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { VoteIcon } from './vote-icon';
|
||||||
|
|
||||||
|
describe('Vote TX icon', () => {
|
||||||
|
it('should use the text For by default for yes votes', () => {
|
||||||
|
const yes = render(<VoteIcon vote={true} />);
|
||||||
|
expect(yes.getByTestId('label')).toHaveTextContent('For');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the yesText for yes votes if specified', () => {
|
||||||
|
const yes = render(<VoteIcon vote={true} yesText="Test" />);
|
||||||
|
expect(yes.getByTestId('label')).toHaveTextContent('Test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the tick icon for yes votes', () => {
|
||||||
|
const no = render(<VoteIcon vote={true} />);
|
||||||
|
expect(no.getByRole('img')).toHaveAttribute(
|
||||||
|
'aria-label',
|
||||||
|
'tick-circle icon'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the text Against by default for no votes', () => {
|
||||||
|
const no = render(<VoteIcon vote={false} />);
|
||||||
|
expect(no.getByTestId('label')).toHaveTextContent('Against');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the noText for no votes if specified', () => {
|
||||||
|
const no = render(<VoteIcon vote={false} noText="Test" />);
|
||||||
|
expect(no.getByTestId('label')).toHaveTextContent('Test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the delete icon for no votes', () => {
|
||||||
|
const no = render(<VoteIcon vote={false} />);
|
||||||
|
expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon');
|
||||||
|
});
|
||||||
|
});
|
40
apps/explorer/src/app/components/vote-icon/vote-icon.tsx
Normal file
40
apps/explorer/src/app/components/vote-icon/vote-icon.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import type { IconName } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
export interface VoteIconProps {
|
||||||
|
// True is a yes vote, false is undefined or no vorte
|
||||||
|
vote: boolean;
|
||||||
|
// Defaults to 'For', but can be any text
|
||||||
|
yesText?: string;
|
||||||
|
// Defaults to 'Against', but can be any text
|
||||||
|
noText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays a lozenge with an icon representing the way a user voted for a proposal.
|
||||||
|
* The yes and no text can be overridden
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function VoteIcon({
|
||||||
|
vote,
|
||||||
|
yesText = 'For',
|
||||||
|
noText = 'Against',
|
||||||
|
}: VoteIconProps) {
|
||||||
|
const label = vote ? yesText : noText;
|
||||||
|
const bg = vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
|
||||||
|
const icon: IconName = vote ? 'tick-circle' : 'delete';
|
||||||
|
const fill = vote ? 'vega-green-300' : 'vega-pink-300';
|
||||||
|
const text = vote ? 'vega-green-200' : 'vega-pink-200';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`voteicon inline-block my-1 py-1 px-2 py rounded-md text-white leading-one sm align-top ${bg}`}
|
||||||
|
>
|
||||||
|
<Icon name={icon} size={3} className={`mr-2 p-0 fill-${fill}`} />
|
||||||
|
<span className={`text-base text-${text}`} data-testid="label">
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -60,3 +60,7 @@
|
|||||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||||
--ag-font-size: 12px;
|
--ag-font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.voteicon svg {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user