Add collapsible rows to democracy (#8656)
* Add collapsible rows to democracy * Adjust padding * shortHash * Fix test text * Move bounty links * Fix contacts tests
This commit is contained in:
parent
fd864a0368
commit
f288a265a1
@ -722,22 +722,19 @@ function Account ({ account: { address, meta }, className = '', delegation, filt
|
||||
</tr>
|
||||
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
|
||||
<td />
|
||||
<td className='top tagInfo'>
|
||||
<td className='columar'>
|
||||
<Columar isFull>
|
||||
<Columar.Column>
|
||||
<LinkExternal
|
||||
data={address}
|
||||
isMain
|
||||
type='address'
|
||||
withTitle
|
||||
/>
|
||||
</Columar.Column>
|
||||
</Columar>
|
||||
<Columar>
|
||||
<Columar.Column>
|
||||
<div
|
||||
className='tags'
|
||||
data-testid='tags'
|
||||
>
|
||||
<div data-testid='tags'>
|
||||
<Tags
|
||||
value={tags}
|
||||
withTitle
|
||||
@ -766,23 +763,6 @@ function Account ({ account: { address, meta }, className = '', delegation, filt
|
||||
}
|
||||
|
||||
export default React.memo(styled(Account)`
|
||||
&.isCollapsed {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
&.isExpanded {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.ui--Columar {
|
||||
margin: 0.5rem 0 1.5rem 0;
|
||||
}
|
||||
|
||||
.tags {
|
||||
width: 100%;
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
|
||||
.devBadge {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
@ -143,11 +143,11 @@ describe('Accounts page', () => {
|
||||
await accountRows[0].assertShortAddress(aliceShortAddress);
|
||||
});
|
||||
|
||||
it('when account is not tagged, account row details displays no tags info', async () => {
|
||||
it('when account is not tagged, account row details displays none info', async () => {
|
||||
accountsPage.renderDefaultAccounts(1);
|
||||
const rows = await accountsPage.getAccountRows();
|
||||
|
||||
await rows[0].assertTags('no tags');
|
||||
await rows[0].assertTags('none');
|
||||
});
|
||||
|
||||
it('when account is tagged, account row details displays tags', async () => {
|
||||
|
||||
@ -72,7 +72,7 @@ describe.skip('Sidebar', () => {
|
||||
it('cannot be edited if edit button has not been pressed', async () => {
|
||||
accountsPage.renderDefaultAccounts(1);
|
||||
sideBar = await accountsPage.openSidebarForRow(0);
|
||||
await sideBar.clickByText('no tags');
|
||||
await sideBar.clickByText('none');
|
||||
expect(sideBar.queryByRole('combobox')).toBeFalsy();
|
||||
|
||||
await expect(sideBar.typeAccountName(newName)).rejects.toThrowError(nameInputNotFoundError);
|
||||
@ -95,7 +95,7 @@ describe.skip('Sidebar', () => {
|
||||
);
|
||||
sideBar = await accountsPage.openSidebarForRow(0);
|
||||
|
||||
await sideBar.assertTags('no tags');
|
||||
await sideBar.assertTags('none');
|
||||
sideBar.edit();
|
||||
});
|
||||
|
||||
@ -104,7 +104,7 @@ describe.skip('Sidebar', () => {
|
||||
await sideBar.selectTag(defaultTag);
|
||||
|
||||
sideBar.cancel();
|
||||
await sideBar.assertTags('no tags');
|
||||
await sideBar.assertTags('none');
|
||||
await sideBar.assertAccountName(initialName);
|
||||
});
|
||||
|
||||
@ -130,7 +130,7 @@ describe.skip('Sidebar', () => {
|
||||
|
||||
fireEvent.click(await screen.findByText('accounts'));
|
||||
|
||||
await sideBar.assertTags('no tags');
|
||||
await sideBar.assertTags('none');
|
||||
await sideBar.assertAccountName('ALICE');
|
||||
|
||||
expect(sideBar.queryByRole('button', { name: 'Cancel' })).toBeFalsy();
|
||||
|
||||
@ -251,22 +251,19 @@ function Address ({ address, className = '', filter, isFavorite, toggleFavorite
|
||||
</tr>
|
||||
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
|
||||
<td />
|
||||
<td className='top'>
|
||||
<td className='columar'>
|
||||
<Columar isFull>
|
||||
<Columar.Column>
|
||||
<LinkExternal
|
||||
data={address}
|
||||
isMain
|
||||
type='address'
|
||||
withTitle
|
||||
/>
|
||||
</Columar.Column>
|
||||
</Columar>
|
||||
<Columar>
|
||||
<Columar.Column>
|
||||
<div
|
||||
className='tags'
|
||||
data-testid='tags'
|
||||
>
|
||||
<div data-testid='tags'>
|
||||
<Tags
|
||||
value={tags}
|
||||
withTitle
|
||||
@ -289,21 +286,5 @@ function Address ({ address, className = '', filter, isFavorite, toggleFavorite
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(styled(Address)`
|
||||
&.isCollapsed {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
&.isExpanded {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.tags {
|
||||
width: 100%;
|
||||
min-height: 1.5rem;
|
||||
}
|
||||
|
||||
.ui--Columar {
|
||||
margin: 0.5rem 0 1.5rem 0;
|
||||
}
|
||||
`);
|
||||
// FIXME: This is weird, if we remove the styled wrapper we have test failures...
|
||||
export default React.memo(styled(Address)``);
|
||||
|
||||
@ -60,6 +60,7 @@ describe('Addresses page', () => {
|
||||
describe('when some contacts exist', () => {
|
||||
it('the contacts table contains some contact rows', async () => {
|
||||
addressesPage.renderDefaultContacts(2);
|
||||
|
||||
const rows = await addressesPage.getAddressesRows();
|
||||
|
||||
expect(rows).toHaveLength(2);
|
||||
@ -97,7 +98,7 @@ describe('Addresses page', () => {
|
||||
addressesPage.renderDefaultContacts(1);
|
||||
const rows = await addressesPage.getAddressesRows();
|
||||
|
||||
await rows[0].assertTags('no tags');
|
||||
await rows[0].assertTags('none');
|
||||
});
|
||||
|
||||
it('when a contact is tagged, the details row displays tags', async () => {
|
||||
|
||||
@ -62,15 +62,15 @@ function Bounties ({ className }: Props): React.ReactElement {
|
||||
export default React.memo(styled(Bounties)`
|
||||
.bounties-table-wrapper table {
|
||||
tr {
|
||||
td,
|
||||
&:not(.filter) th {
|
||||
td, &:not(.filter) th {
|
||||
&:last-child {
|
||||
padding-right: 1.14rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ui--IdentityIcon {
|
||||
|
||||
.ui--IdentityIcon {
|
||||
margin-right: 0.42rem;
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import type { BlockNumber, Bounty as BountyType, BountyIndex } from '@polkadot/t
|
||||
import React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { AddressSmall, ExpandButton, LinkExternal } from '@polkadot/react-components';
|
||||
import { AddressSmall, Columar, ExpandButton, LinkExternal } from '@polkadot/react-components';
|
||||
import { useToggle } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
@ -118,10 +118,6 @@ function Bounty ({ bestNumber, bounty, className = '', description, index, propo
|
||||
</td>
|
||||
<td className='actions'>
|
||||
<div>
|
||||
<LinkExternal
|
||||
data={index}
|
||||
type='bounty'
|
||||
/>
|
||||
<BountyExtraActions
|
||||
bestNumber={bestNumber}
|
||||
description={description}
|
||||
@ -137,33 +133,46 @@ function Bounty ({ bestNumber, bounty, className = '', description, index, propo
|
||||
</td>
|
||||
</tr>
|
||||
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
|
||||
<td colSpan={2}>
|
||||
<div className='label-column-left'>
|
||||
<div className='label'>{t('Proposer')}</div>
|
||||
<AddressSmall value={proposer} />
|
||||
</div>
|
||||
</td>
|
||||
<td colSpan={2}>
|
||||
<div className='label-column-right'>
|
||||
<div className='label'>{t('Bond')}</div>
|
||||
<div className='inline-balance'><FormatBalance value={bond} /></div>
|
||||
</div>
|
||||
{curator && (
|
||||
<div className='label-column-right'>
|
||||
<div className='label'>{t("Curator's fee")}</div>
|
||||
<div className='inline-balance'>{<FormatBalance value={fee} />}</div>
|
||||
</div>
|
||||
)}
|
||||
<div className='label-column-right'>
|
||||
{curator && !curatorDeposit.isZero() && (
|
||||
<>
|
||||
<div className='label'>{t("Curator's deposit")}</div>
|
||||
<div className='inline-balance'>
|
||||
<FormatBalance value={curatorDeposit} />
|
||||
<td />
|
||||
<td
|
||||
className='columar'
|
||||
colSpan={3}
|
||||
>
|
||||
<Columar>
|
||||
<Columar.Column>
|
||||
<LinkExternal
|
||||
data={index}
|
||||
type='bounty'
|
||||
withTitle
|
||||
/>
|
||||
</Columar.Column>
|
||||
<Columar.Column>
|
||||
<div className='column'>
|
||||
<h5>{t('Proposer')}</h5>
|
||||
<AddressSmall value={proposer} />
|
||||
</div>
|
||||
<div className='column'>
|
||||
<h5>{t('Bond')}</h5>
|
||||
<div className='inline-balance'><FormatBalance value={bond} /></div>
|
||||
</div>
|
||||
{curator && (
|
||||
<div className='column'>
|
||||
<h5>{t("Curator's fee")}</h5>
|
||||
<div className='inline-balance'>{<FormatBalance value={fee} />}</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className='column'>
|
||||
{curator && !curatorDeposit.isZero() && (
|
||||
<>
|
||||
<h5>{t("Curator's deposit")}</h5>
|
||||
<div className='inline-balance'>
|
||||
<FormatBalance value={curatorDeposit} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Columar.Column>
|
||||
</Columar>
|
||||
</td>
|
||||
<td />
|
||||
<td />
|
||||
@ -210,31 +219,18 @@ export default React.memo(styled(Bounty)`
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.label {
|
||||
text-align: right;
|
||||
padding: 0 1.7rem 0 0;
|
||||
line-height: normal;
|
||||
color: var(--color-label);
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.label-column-right, .label-column-left{
|
||||
display: flex;
|
||||
.column {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
padding: 0 0 0.5rem;
|
||||
|
||||
.label {
|
||||
h5 {
|
||||
text-align: right;
|
||||
padding: 0 1.7rem 0 0;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.label-column-right {
|
||||
padding: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.label-column-left {
|
||||
padding: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
& .td-info-action-row {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
@ -5,8 +5,8 @@ import type { DeriveProposalExternal } from '@polkadot/api-derive/types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { AddressMini, Button, LinkExternal } from '@polkadot/react-components';
|
||||
import { useCollectiveMembers } from '@polkadot/react-hooks';
|
||||
import { AddressMini, Button, Columar, ExpandButton, LinkExternal } from '@polkadot/react-components';
|
||||
import { useCollectiveMembers, useToggle } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
|
||||
import Fasttrack from './Fasttrack';
|
||||
@ -20,42 +20,59 @@ interface Props {
|
||||
|
||||
function External ({ className = '', value: { image, imageHash, threshold } }: Props): React.ReactElement<Props> | null {
|
||||
const { isMember, members } = useCollectiveMembers('technicalCommittee');
|
||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<ProposalCell
|
||||
imageHash={imageHash}
|
||||
proposal={image?.proposal}
|
||||
/>
|
||||
<td className='address'>
|
||||
{image && (
|
||||
<AddressMini value={image.proposer} />
|
||||
)}
|
||||
</td>
|
||||
<td className='number'>
|
||||
{image && <FormatBalance value={image.balance} />}
|
||||
</td>
|
||||
<td className='button'>
|
||||
<Button.Group>
|
||||
{!image?.proposal && (
|
||||
<PreImageButton imageHash={imageHash} />
|
||||
)}
|
||||
{threshold && isMember && (
|
||||
<Fasttrack
|
||||
imageHash={imageHash}
|
||||
members={members}
|
||||
threshold={threshold}
|
||||
/>
|
||||
)}
|
||||
</Button.Group>
|
||||
</td>
|
||||
<td className='links media--1000'>
|
||||
<LinkExternal
|
||||
data={imageHash}
|
||||
type='external'
|
||||
<>
|
||||
<tr className={`${className}${isExpanded ? ' noBorder' : ''}`}>
|
||||
<ProposalCell
|
||||
imageHash={imageHash}
|
||||
proposal={image?.proposal}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<td className='address'>
|
||||
{image && (
|
||||
<AddressMini value={image.proposer} />
|
||||
)}
|
||||
</td>
|
||||
<td className='number'>
|
||||
{image && <FormatBalance value={image.balance} />}
|
||||
</td>
|
||||
<td className='actions'>
|
||||
<Button.Group>
|
||||
{!image?.proposal && (
|
||||
<PreImageButton imageHash={imageHash} />
|
||||
)}
|
||||
{threshold && isMember && (
|
||||
<Fasttrack
|
||||
imageHash={imageHash}
|
||||
members={members}
|
||||
threshold={threshold}
|
||||
/>
|
||||
)}
|
||||
<ExpandButton
|
||||
expanded={isExpanded}
|
||||
onClick={toggleIsExpanded}
|
||||
/>
|
||||
</Button.Group>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
|
||||
<td
|
||||
className='columar'
|
||||
colSpan={100}
|
||||
>
|
||||
<Columar isFull>
|
||||
<Columar.Column>
|
||||
<LinkExternal
|
||||
data={imageHash}
|
||||
type='external'
|
||||
withTitle
|
||||
/>
|
||||
</Columar.Column>
|
||||
</Columar>
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -24,8 +24,7 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
|
||||
[t('external'), 'start'],
|
||||
[t('proposer'), 'address'],
|
||||
[t('locked')],
|
||||
[],
|
||||
[undefined, 'media--1000']
|
||||
[]
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -33,6 +32,7 @@ function Externals ({ className }: Props): React.ReactElement<Props> | null {
|
||||
className={className}
|
||||
empty={external === null && t<string>('No external proposal')}
|
||||
header={headerRef.current}
|
||||
withCollapsibleRows
|
||||
>
|
||||
{external && <External value={external} />}
|
||||
</Table>
|
||||
|
||||
@ -5,7 +5,8 @@ import type { DeriveProposal } from '@polkadot/api-derive/types';
|
||||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { AddressMini, Button, ExpanderScroll, LinkExternal } from '@polkadot/react-components';
|
||||
import { AddressMini, Button, Columar, ExpandButton, ExpanderScroll, LinkExternal } from '@polkadot/react-components';
|
||||
import { useToggle } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
@ -21,6 +22,7 @@ interface Props {
|
||||
|
||||
function Proposal ({ className = '', value: { balance, image, imageHash, index, proposer, seconds } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||
|
||||
const seconding = useMemo(
|
||||
() => seconds.filter((_address, index) => index !== 0),
|
||||
@ -40,47 +42,64 @@ function Proposal ({ className = '', value: { balance, image, imageHash, index,
|
||||
);
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td className='number'><h1>{formatNumber(index)}</h1></td>
|
||||
<ProposalCell
|
||||
imageHash={imageHash}
|
||||
proposal={image?.proposal}
|
||||
/>
|
||||
<td className='address'>
|
||||
<AddressMini value={proposer} />
|
||||
</td>
|
||||
<td className='number together media--1200'>
|
||||
<FormatBalance value={balance} />
|
||||
</td>
|
||||
<td className='expand'>
|
||||
{seconding.length !== 0 && (
|
||||
<ExpanderScroll
|
||||
empty={seconding && t<string>('No endorsements')}
|
||||
renderChildren={renderSeconds}
|
||||
summary={t<string>('Endorsed ({{count}})', { replace: { count: seconding.length } })}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='button'>
|
||||
<Button.Group>
|
||||
{!image?.proposal && (
|
||||
<PreImageButton imageHash={imageHash} />
|
||||
)}
|
||||
<Seconding
|
||||
deposit={balance}
|
||||
depositors={seconds || []}
|
||||
image={image}
|
||||
proposalId={index}
|
||||
/>
|
||||
</Button.Group>
|
||||
</td>
|
||||
<td className='links media--1000'>
|
||||
<LinkExternal
|
||||
data={index}
|
||||
type='proposal'
|
||||
<>
|
||||
<tr className={`${className}${isExpanded ? ' noBorder' : ''}`}>
|
||||
<td className='number'><h1>{formatNumber(index)}</h1></td>
|
||||
<ProposalCell
|
||||
imageHash={imageHash}
|
||||
proposal={image?.proposal}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<td className='address'>
|
||||
<AddressMini value={proposer} />
|
||||
</td>
|
||||
<td className='number together media--1200'>
|
||||
<FormatBalance value={balance} />
|
||||
</td>
|
||||
<td className='expand'>
|
||||
{seconding.length !== 0 && (
|
||||
<ExpanderScroll
|
||||
empty={seconding && t<string>('No endorsements')}
|
||||
renderChildren={renderSeconds}
|
||||
summary={t<string>('Endorsed ({{count}})', { replace: { count: seconding.length } })}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='actions'>
|
||||
<Button.Group>
|
||||
{!image?.proposal && (
|
||||
<PreImageButton imageHash={imageHash} />
|
||||
)}
|
||||
<Seconding
|
||||
deposit={balance}
|
||||
depositors={seconds || []}
|
||||
image={image}
|
||||
proposalId={index}
|
||||
/>
|
||||
<ExpandButton
|
||||
expanded={isExpanded}
|
||||
onClick={toggleIsExpanded}
|
||||
/>
|
||||
</Button.Group>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
|
||||
<td />
|
||||
<td
|
||||
className='columar'
|
||||
colSpan={100}
|
||||
>
|
||||
<Columar isFull>
|
||||
<Columar.Column>
|
||||
<LinkExternal
|
||||
data={index}
|
||||
type='proposal'
|
||||
withTitle
|
||||
/>
|
||||
</Columar.Column>
|
||||
</Columar>
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import type { Hash, Proposal, ProposalIndex } from '@polkadot/types/interfaces';
|
||||
import type { HexString } from '@polkadot/util/types';
|
||||
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import usePreimage from '@polkadot/app-preimages/usePreimage';
|
||||
import { CallExpander } from '@polkadot/react-components';
|
||||
@ -38,7 +39,7 @@ function ProposalCell ({ className = '', imageHash, proposal }: Props): React.Re
|
||||
|
||||
return (
|
||||
<td className={`${className} all`}>
|
||||
{t('preimage {{hash}}', { replace: { hash: `${textHash.slice(0, 8)}…${textHash.slice(-8)}` } })}
|
||||
<div className='shortHash'>{textHash.slice(0, 8)}…{textHash.slice(-8)}</div>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
@ -65,4 +66,12 @@ function ProposalCell ({ className = '', imageHash, proposal }: Props): React.Re
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ProposalCell);
|
||||
export default React.memo(styled(ProposalCell)`
|
||||
.shortHash {
|
||||
font: var(--font-mono);
|
||||
|
||||
+ div {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
@ -24,8 +24,7 @@ function Proposals ({ className }: Props): React.ReactElement<Props> {
|
||||
[t('proposals'), 'start', 2],
|
||||
[t('proposer'), 'address'],
|
||||
[t('locked'), 'media--1200'],
|
||||
[undefined, undefined, 2],
|
||||
[undefined, 'media--1000']
|
||||
[undefined, undefined, 2]
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -33,6 +32,7 @@ function Proposals ({ className }: Props): React.ReactElement<Props> {
|
||||
className={className}
|
||||
empty={proposals && t<string>('No active proposals')}
|
||||
header={headerRef.current}
|
||||
withCollapsibleRows
|
||||
>
|
||||
{proposals?.map((proposal): React.ReactNode => (
|
||||
<ProposalDisplay
|
||||
|
||||
@ -7,8 +7,8 @@ import type { Balance } from '@polkadot/types/interfaces';
|
||||
import React, { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Badge, Button, Icon, LinkExternal, Progress } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useBestNumber, useCall } from '@polkadot/react-hooks';
|
||||
import { Badge, Button, Columar, ExpandButton, Icon, LinkExternal, Progress } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useBestNumber, useCall, useToggle } from '@polkadot/react-hooks';
|
||||
import { BlockToTime } from '@polkadot/react-query';
|
||||
import { BN, BN_ONE, formatNumber, isBoolean } from '@polkadot/util';
|
||||
|
||||
@ -44,6 +44,7 @@ function Referendum ({ className = '', value: { allAye, allNay, image, imageHash
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const bestNumber = useBestNumber();
|
||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
||||
const totalIssuance = useCall<Balance>(api.query.balances?.totalIssuance);
|
||||
const { changeAye, changeNay } = useChangeCalc(status.threshold, votedAye, votedNay, votedTotal);
|
||||
const threshold = useMemo(
|
||||
@ -89,88 +90,105 @@ function Referendum ({ className = '', value: { allAye, allNay, image, imageHash
|
||||
const remainBlock = status.end.sub(bestNumber).isub(BN_ONE);
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td className='number'><h1>{formatNumber(index)}</h1></td>
|
||||
<ProposalCell
|
||||
imageHash={imageHash}
|
||||
proposal={image?.proposal}
|
||||
/>
|
||||
<td className='number together media--1200'>
|
||||
<BlockToTime value={remainBlock} />
|
||||
{t<string>('{{blocks}} blocks', { replace: { blocks: formatNumber(remainBlock) } })}
|
||||
</td>
|
||||
<td className='number together media--1400'>
|
||||
<BlockToTime value={enactBlock.sub(bestNumber)} />
|
||||
#{formatNumber(enactBlock)}
|
||||
</td>
|
||||
<td className='number together media--1400'>
|
||||
{percentages && (
|
||||
<>
|
||||
<div>{percentages.turnout}</div>
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
<td className='badge'>
|
||||
{isBoolean(isPassing) && (
|
||||
<Badge
|
||||
color={isPassing ? 'green' : 'red'}
|
||||
hover={
|
||||
isPassing
|
||||
? t<string>('{{threshold}}, passing', { replace: { threshold } })
|
||||
: t<string>('{{threshold}}, not passing', { replace: { threshold } })
|
||||
}
|
||||
icon={isPassing ? 'check' : 'times'}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='expand'>
|
||||
<ReferendumVotes
|
||||
change={changeAye}
|
||||
count={voteCountAye}
|
||||
isAye
|
||||
isWinning={isPassing}
|
||||
total={votedAye}
|
||||
votes={allAye}
|
||||
<>
|
||||
<tr className={`${className}${isExpanded ? ' noBorder' : ''}`}>
|
||||
<td className='number'><h1>{formatNumber(index)}</h1></td>
|
||||
<ProposalCell
|
||||
imageHash={imageHash}
|
||||
proposal={image?.proposal}
|
||||
/>
|
||||
<ReferendumVotes
|
||||
change={changeNay}
|
||||
count={voteCountNay}
|
||||
isAye={false}
|
||||
isWinning={!isPassing}
|
||||
total={votedNay}
|
||||
votes={allNay}
|
||||
/>
|
||||
</td>
|
||||
<td className='media--1000 middle chart'>
|
||||
<Progress
|
||||
total={totalCalculated}
|
||||
value={votedAye}
|
||||
/>
|
||||
</td>
|
||||
<td className='button'>
|
||||
<Button.Group>
|
||||
{!image?.proposal && (
|
||||
<PreImageButton imageHash={imageHash} />
|
||||
<td className='number together media--1200'>
|
||||
<BlockToTime value={remainBlock} />
|
||||
{t<string>('{{blocks}} blocks', { replace: { blocks: formatNumber(remainBlock) } })}
|
||||
</td>
|
||||
<td className='number together media--1400'>
|
||||
<BlockToTime value={enactBlock.sub(bestNumber)} />
|
||||
#{formatNumber(enactBlock)}
|
||||
</td>
|
||||
<td className='number together media--1400'>
|
||||
{percentages && (
|
||||
<>
|
||||
<div>{percentages.turnout}</div>
|
||||
</>
|
||||
)}
|
||||
<Voting
|
||||
proposal={image?.proposal}
|
||||
referendumId={index}
|
||||
</td>
|
||||
<td className='badge'>
|
||||
{isBoolean(isPassing) && (
|
||||
<Badge
|
||||
color={isPassing ? 'green' : 'red'}
|
||||
hover={
|
||||
isPassing
|
||||
? t<string>('{{threshold}}, passing', { replace: { threshold } })
|
||||
: t<string>('{{threshold}}, not passing', { replace: { threshold } })
|
||||
}
|
||||
icon={isPassing ? 'check' : 'times'}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
<td className='expand'>
|
||||
<ReferendumVotes
|
||||
change={changeAye}
|
||||
count={voteCountAye}
|
||||
isAye
|
||||
isWinning={isPassing}
|
||||
total={votedAye}
|
||||
votes={allAye}
|
||||
/>
|
||||
</Button.Group>
|
||||
</td>
|
||||
<td className='badge'>
|
||||
<Icon
|
||||
color={hasVoted ? (hasVotedAye ? 'green' : 'red') : 'gray'}
|
||||
icon='asterisk'
|
||||
/>
|
||||
</td>
|
||||
<td className='links media--1000'>
|
||||
<LinkExternal
|
||||
data={index}
|
||||
type='referendum'
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<ReferendumVotes
|
||||
change={changeNay}
|
||||
count={voteCountNay}
|
||||
isAye={false}
|
||||
isWinning={!isPassing}
|
||||
total={votedNay}
|
||||
votes={allNay}
|
||||
/>
|
||||
</td>
|
||||
<td className='media--1000 middle chart'>
|
||||
<Progress
|
||||
total={totalCalculated}
|
||||
value={votedAye}
|
||||
/>
|
||||
</td>
|
||||
<td className='badge'>
|
||||
<Icon
|
||||
color={hasVoted ? (hasVotedAye ? 'green' : 'red') : 'gray'}
|
||||
icon='asterisk'
|
||||
/>
|
||||
</td>
|
||||
<td className='actions'>
|
||||
<Button.Group>
|
||||
{!image?.proposal && (
|
||||
<PreImageButton imageHash={imageHash} />
|
||||
)}
|
||||
<Voting
|
||||
proposal={image?.proposal}
|
||||
referendumId={index}
|
||||
/>
|
||||
<ExpandButton
|
||||
expanded={isExpanded}
|
||||
onClick={toggleIsExpanded}
|
||||
/>
|
||||
</Button.Group>
|
||||
</td>
|
||||
</tr>
|
||||
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'}`}>
|
||||
<td />
|
||||
<td
|
||||
className='columar'
|
||||
colSpan={100}
|
||||
>
|
||||
<Columar isFull>
|
||||
<Columar.Column>
|
||||
<LinkExternal
|
||||
data={index}
|
||||
type='referendum'
|
||||
withTitle
|
||||
/>
|
||||
</Columar.Column>
|
||||
</Columar>
|
||||
</td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -26,8 +26,7 @@ function Referendums ({ className = '', referendums }: Props): React.ReactElemen
|
||||
[undefined, 'badge'],
|
||||
[t('votes'), 'expand'],
|
||||
[t<string>('support'), 'media--1000'],
|
||||
[undefined, undefined, 2],
|
||||
[undefined, 'media--1000']
|
||||
[undefined, undefined, 2]
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -35,6 +34,7 @@ function Referendums ({ className = '', referendums }: Props): React.ReactElemen
|
||||
className={className}
|
||||
empty={referendums && t<string>('No active referendums')}
|
||||
header={headerRef.current}
|
||||
withCollapsibleRows
|
||||
>
|
||||
{referendums?.map((referendum): React.ReactNode => (
|
||||
<Referendum
|
||||
|
||||
@ -16,11 +16,11 @@ interface Props {
|
||||
className?: string;
|
||||
data: BN | number | string;
|
||||
hash?: string;
|
||||
isMain?: boolean;
|
||||
isText?: boolean;
|
||||
isSidebar?: boolean;
|
||||
isSmall?: boolean;
|
||||
type: LinkTypes;
|
||||
withTitle?: boolean;
|
||||
}
|
||||
|
||||
// function shortName (name: string): string {
|
||||
@ -61,7 +61,7 @@ function genLinks (systemChain: string, { data, hash, isSidebar, isText, type }:
|
||||
.filter((node): node is React.ReactNode => !!node);
|
||||
}
|
||||
|
||||
function LinkExternal ({ className = '', data, hash, isMain, isSidebar, isSmall, isText, type }: Props): React.ReactElement<Props> | null {
|
||||
function LinkExternal ({ className = '', data, hash, isSidebar, isSmall, isText, type, withTitle }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { systemChain } = useApi();
|
||||
const links = useMemo(
|
||||
@ -69,17 +69,22 @@ function LinkExternal ({ className = '', data, hash, isMain, isSidebar, isSmall,
|
||||
[systemChain, data, hash, isSidebar, isText, type]
|
||||
);
|
||||
|
||||
if (!links.length) {
|
||||
if (!links.length && !withTitle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`${className} ui--LinkExternal ${isText ? 'isText' : 'isLogo'}${isMain ? ' isMain' : ''}${isSmall ? ' isSmall' : ''}${isSidebar ? ' isSidebar' : ''}`}>
|
||||
<div className={`${className} ui--LinkExternal ${isText ? 'isText' : 'isLogo'}${withTitle ? ' isMain' : ''}${isSmall ? ' isSmall' : ''}${isSidebar ? ' isSidebar' : ''}`}>
|
||||
{(isText && !isSmall) && <div>{t<string>('View this externally')}</div>}
|
||||
{isMain && (
|
||||
{withTitle && (
|
||||
<h5>{t('external links')}</h5>
|
||||
)}
|
||||
<div className='links'>{links.map((link, index) => <span key={index}>{link}</span>)}</div>
|
||||
<div className='links'>
|
||||
{links.length
|
||||
? links.map((link, index) => <span key={index}>{link}</span>)
|
||||
: <label>{t('none')}</label>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -315,6 +315,15 @@ export default React.memo(styled(Table)`
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
&.columar {
|
||||
vertical-align: top;
|
||||
|
||||
.ui--Columar .ui--Column {
|
||||
margin: 1rem 0 0.75rem 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.middle {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ function Tags ({ children, className = '', isEditable, isEditing, onChange, onSa
|
||||
label={tag}
|
||||
/>
|
||||
))
|
||||
: <label>{t<string>('no tags')}</label>,
|
||||
: <label>{t<string>('none')}</label>,
|
||||
[t, value]
|
||||
);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user