This reverts commit 466a1be22f.
This commit is contained in:
parent
ecaf7e4792
commit
6aba26bf4e
@ -27,7 +27,6 @@
|
|||||||
"You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.",
|
"You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.": "You are about to remove this account from your list of available accounts. Once completed, should you need to access it again, you will have to re-create the account either via seed or via a backup file.",
|
||||||
"You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.",
|
"You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.": "You are about to remove this address from your address book. Once completed, should you need to access it again, you will have to re-add the address.",
|
||||||
"You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.",
|
"You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.": "You are about to remove this contract from your list of available contracts. Once completed, should you need to access it again, you will have to manually add the contract's address in the Instantiate tab.",
|
||||||
"account balance": "account balance",
|
|
||||||
"address copied": "address copied",
|
"address copied": "address copied",
|
||||||
"available to be unlocked": "available to be unlocked",
|
"available to be unlocked": "available to be unlocked",
|
||||||
"beneficiary": "beneficiary",
|
"beneficiary": "beneficiary",
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're
|
|||||||
import styled, { ThemeContext } from 'styled-components';
|
import styled, { ThemeContext } from 'styled-components';
|
||||||
|
|
||||||
import { ApiPromise } from '@polkadot/api';
|
import { ApiPromise } from '@polkadot/api';
|
||||||
import { AddressInfo, AddressSmall, Badge, Button, ChainLock, CryptoType, Forget, Icon, IdentityIcon, LinkExternal, Menu, Popup, StatusContext, Tags } from '@polkadot/react-components';
|
import { AddressInfo, AddressMini, AddressSmall, Badge, Button, ChainLock, CryptoType, Forget, Icon, IdentityIcon, LinkExternal, Menu, Popup, StatusContext, Tags } from '@polkadot/react-components';
|
||||||
import { useAccountInfo, useApi, useBalancesAll, useBestNumber, useCall, useLedger, useStakingInfo, useToggle } from '@polkadot/react-hooks';
|
import { useAccountInfo, useApi, useBalancesAll, useBestNumber, useCall, useLedger, useStakingInfo, useToggle } from '@polkadot/react-hooks';
|
||||||
import { keyring } from '@polkadot/ui-keyring';
|
import { keyring } from '@polkadot/ui-keyring';
|
||||||
import { BN_ZERO, formatBalance, formatNumber, isFunction } from '@polkadot/util';
|
import { BN_ZERO, formatBalance, formatNumber, isFunction } from '@polkadot/util';
|
||||||
@ -47,7 +47,6 @@ interface Props {
|
|||||||
proxy?: [ProxyDefinition[], BN];
|
proxy?: [ProxyDefinition[], BN];
|
||||||
setBalance: (address: string, value: AccountBalance) => void;
|
setBalance: (address: string, value: AccountBalance) => void;
|
||||||
toggleFavorite: (address: string) => void;
|
toggleFavorite: (address: string) => void;
|
||||||
isEven: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DemocracyUnlockable {
|
interface DemocracyUnlockable {
|
||||||
@ -100,9 +99,8 @@ const transformRecovery = {
|
|||||||
transform: (opt: Option<RecoveryConfig>) => opt.unwrapOr(null)
|
transform: (opt: Option<RecoveryConfig>) => opt.unwrapOr(null)
|
||||||
};
|
};
|
||||||
|
|
||||||
function Account ({ account: { address, meta }, className = '', delegation, filter, isEven, isFavorite, proxy, setBalance, toggleFavorite }: Props): React.ReactElement<Props> | null {
|
function Account ({ account: { address, meta }, className = '', delegation, filter, isFavorite, proxy, setBalance, toggleFavorite }: Props): React.ReactElement<Props> | null {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [isExpanded, toggleIsExpanded] = useToggle(false);
|
|
||||||
const { theme } = useContext<ThemeDef>(ThemeContext);
|
const { theme } = useContext<ThemeDef>(ThemeContext);
|
||||||
const { queueExtrinsic } = useContext(StatusContext);
|
const { queueExtrinsic } = useContext(StatusContext);
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
@ -383,334 +381,278 @@ function Account ({ account: { address, meta }, className = '', delegation, filt
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<tr className={className}>
|
||||||
<tr className={`${className}${isExpanded ? ' noBorder' : ''} ${isEven ? 'isEven' : 'isOdd'}`}>
|
<td className='favorite'>
|
||||||
<td className='favorite'>
|
<Icon
|
||||||
<Icon
|
color={isFavorite ? 'orange' : 'gray'}
|
||||||
color={isFavorite ? 'orange' : 'gray'}
|
icon='star'
|
||||||
icon='star'
|
onClick={_onFavorite}
|
||||||
onClick={_onFavorite}
|
/>
|
||||||
/>
|
</td>
|
||||||
</td>
|
<td className='together'>
|
||||||
<td className='together'>
|
{meta.genesisHash
|
||||||
{meta.genesisHash
|
? <Badge color='transparent' />
|
||||||
? <Badge color='transparent' />
|
: isDevelopment
|
||||||
: isDevelopment
|
? (
|
||||||
? (
|
<Badge
|
||||||
<Badge
|
className='devBadge'
|
||||||
className='devBadge'
|
color='orange'
|
||||||
color='orange'
|
hover={t<string>('This is a development account derived from the known development seed. Do not use for any funds on a non-development network.')}
|
||||||
hover={t<string>('This is a development account derived from the known development seed. Do not use for any funds on a non-development network.')}
|
icon='wrench'
|
||||||
icon='wrench'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
: (
|
|
||||||
<Badge
|
|
||||||
color='orange'
|
|
||||||
hover={
|
|
||||||
<div>
|
|
||||||
<p>{t<string>('This account is available on all networks. It is recommended to link to a specific network via the account options ("only this network" option) to limit availability. For accounts from an extension, set the network on the extension.')}</p>
|
|
||||||
<p>{t<string>('This does not send any transaction, rather is only sets the genesis in the account JSON.')}</p>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
icon='exclamation-triangle'
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
{recoveryInfo && (
|
|
||||||
<Badge
|
|
||||||
color='green'
|
|
||||||
hover={
|
|
||||||
<div>
|
|
||||||
<p>{t<string>('This account is recoverable, with the following friends:')}</p>
|
|
||||||
<div>
|
|
||||||
{recoveryInfo.friends.map((friend, index): React.ReactNode => (
|
|
||||||
<IdentityIcon
|
|
||||||
key={index}
|
|
||||||
value={friend}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{t<string>('threshold')}</td>
|
|
||||||
<td>{formatNumber(recoveryInfo.threshold)}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{t<string>('delay')}</td>
|
|
||||||
<td>{formatNumber(recoveryInfo.delayPeriod)}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{t<string>('deposit')}</td>
|
|
||||||
<td>{formatBalance(recoveryInfo.deposit)}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
icon='shield'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{multiInfos && multiInfos.length !== 0 && (
|
|
||||||
<Badge
|
|
||||||
color='red'
|
|
||||||
hover={t<string>('Multisig approvals pending')}
|
|
||||||
info={multiInfos.length}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isProxied && !proxyInfo.hasOwned && (
|
|
||||||
<Badge
|
|
||||||
color='red'
|
|
||||||
hover={t<string>('Proxied account has no owned proxies')}
|
|
||||||
info='0'
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{delegation?.accountDelegated && (
|
|
||||||
<Badge
|
|
||||||
color='blue'
|
|
||||||
hover={t<string>('This account has a governance delegation')}
|
|
||||||
icon='calendar-check'
|
|
||||||
onClick={toggleDelegate}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!!proxy?.[0].length && api.api.tx.utility && (
|
|
||||||
<Badge
|
|
||||||
color='blue'
|
|
||||||
hover={t<string>('This account has {{proxyNumber}} proxy set.', {
|
|
||||||
replace: {
|
|
||||||
proxyNumber: proxy[0].length
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
icon='arrow-right'
|
|
||||||
onClick={toggleProxyOverview}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className='address'>
|
|
||||||
<AddressSmall
|
|
||||||
parentAddress={meta.parentAddress}
|
|
||||||
value={address}
|
|
||||||
withShortAddress
|
|
||||||
/>
|
|
||||||
{isBackupOpen && (
|
|
||||||
<Backup
|
|
||||||
address={address}
|
|
||||||
key='modal-backup-account'
|
|
||||||
onClose={toggleBackup}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isDelegateOpen && (
|
|
||||||
<DelegateModal
|
|
||||||
key='modal-delegate'
|
|
||||||
onClose={toggleDelegate}
|
|
||||||
previousAmount={delegation?.amount}
|
|
||||||
previousConviction={delegation?.conviction}
|
|
||||||
previousDelegatedAccount={delegation?.accountDelegated}
|
|
||||||
previousDelegatingAccount={address}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isDeriveOpen && (
|
|
||||||
<Derive
|
|
||||||
from={address}
|
|
||||||
key='modal-derive-account'
|
|
||||||
onClose={toggleDerive}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isForgetOpen && (
|
|
||||||
<Forget
|
|
||||||
address={address}
|
|
||||||
key='modal-forget-account'
|
|
||||||
onClose={toggleForget}
|
|
||||||
onForget={_onForget}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isIdentityMainOpen && (
|
|
||||||
<IdentityMain
|
|
||||||
address={address}
|
|
||||||
key='modal-identity-main'
|
|
||||||
onClose={toggleIdentityMain}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isIdentitySubOpen && (
|
|
||||||
<IdentitySub
|
|
||||||
address={address}
|
|
||||||
key='modal-identity-sub'
|
|
||||||
onClose={toggleIdentitySub}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isPasswordOpen && (
|
|
||||||
<ChangePass
|
|
||||||
address={address}
|
|
||||||
key='modal-change-pass'
|
|
||||||
onClose={togglePassword}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isTransferOpen && (
|
|
||||||
<Transfer
|
|
||||||
key='modal-transfer'
|
|
||||||
onClose={toggleTransfer}
|
|
||||||
senderId={address}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isProxyOverviewOpen && (
|
|
||||||
<ProxyOverview
|
|
||||||
key='modal-proxy-overview'
|
|
||||||
onClose={toggleProxyOverview}
|
|
||||||
previousProxy={proxy}
|
|
||||||
proxiedAccount={address}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isMultisigOpen && multiInfos && (
|
|
||||||
<MultisigApprove
|
|
||||||
address={address}
|
|
||||||
key='multisig-approve'
|
|
||||||
onClose={toggleMultisig}
|
|
||||||
ongoing={multiInfos}
|
|
||||||
threshold={meta.threshold as number}
|
|
||||||
who={meta.who as string[]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isRecoverAccountOpen && (
|
|
||||||
<RecoverAccount
|
|
||||||
address={address}
|
|
||||||
key='recover-account'
|
|
||||||
onClose={toggleRecoverAccount}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isRecoverSetupOpen && (
|
|
||||||
<RecoverSetup
|
|
||||||
address={address}
|
|
||||||
key='recover-setup'
|
|
||||||
onClose={toggleRecoverSetup}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isUndelegateOpen && (
|
|
||||||
<UndelegateModal
|
|
||||||
accountDelegating={address}
|
|
||||||
key='modal-delegate'
|
|
||||||
onClose={toggleUndelegate}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className='number'>
|
|
||||||
<CryptoType accountId={address} />
|
|
||||||
</td>
|
|
||||||
<td className='number media--1500'>
|
|
||||||
{balancesAll?.accountNonce.gt(BN_ZERO) && formatNumber(balancesAll.accountNonce)}
|
|
||||||
</td>
|
|
||||||
<td className='number'>
|
|
||||||
<AddressInfo
|
|
||||||
address={address}
|
|
||||||
balancesAll={balancesAll}
|
|
||||||
withBalance={{
|
|
||||||
available: false,
|
|
||||||
bonded: false,
|
|
||||||
locked: false,
|
|
||||||
redeemable: false,
|
|
||||||
reserved: false,
|
|
||||||
total: true,
|
|
||||||
unlocking: false,
|
|
||||||
vested: false
|
|
||||||
}}
|
|
||||||
withExtended={false}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td className='button'>
|
|
||||||
{isFunction(api.api.tx.balances?.transfer) && (
|
|
||||||
<Button
|
|
||||||
icon='paper-plane'
|
|
||||||
label={t<string>('send')}
|
|
||||||
onClick={toggleTransfer}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Popup
|
|
||||||
className={`theme--${theme}`}
|
|
||||||
isOpen={isSettingsOpen}
|
|
||||||
onClose={toggleSettings}
|
|
||||||
trigger={
|
|
||||||
<Button
|
|
||||||
icon='ellipsis-v'
|
|
||||||
isDisabled={!menuItems.length}
|
|
||||||
onClick={toggleSettings}
|
|
||||||
/>
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<Badge
|
||||||
|
color='orange'
|
||||||
|
hover={
|
||||||
|
<div>
|
||||||
|
<p>{t<string>('This account is available on all networks. It is recommended to link to a specific network via the account options ("only this network" option) to limit availability. For accounts from an extension, set the network on the extension.')}</p>
|
||||||
|
<p>{t<string>('This does not send any transaction, rather is only sets the genesis in the account JSON.')}</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
icon='exclamation-triangle'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{recoveryInfo && (
|
||||||
|
<Badge
|
||||||
|
color='green'
|
||||||
|
hover={
|
||||||
|
<div>
|
||||||
|
<p>{t<string>('This account is recoverable, with the following friends:')}</p>
|
||||||
|
<div>
|
||||||
|
{recoveryInfo.friends.map((friend, index): React.ReactNode => (
|
||||||
|
<IdentityIcon
|
||||||
|
key={index}
|
||||||
|
value={friend}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{t<string>('threshold')}</td>
|
||||||
|
<td>{formatNumber(recoveryInfo.threshold)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{t<string>('delay')}</td>
|
||||||
|
<td>{formatNumber(recoveryInfo.delayPeriod)}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{t<string>('deposit')}</td>
|
||||||
|
<td>{formatBalance(recoveryInfo.deposit)}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
>
|
icon='shield'
|
||||||
<Menu
|
|
||||||
onClick={toggleSettings}
|
|
||||||
text
|
|
||||||
vertical
|
|
||||||
>
|
|
||||||
{menuItems}
|
|
||||||
</Menu>
|
|
||||||
</Popup>
|
|
||||||
</td>
|
|
||||||
<td className='links media--1400'>
|
|
||||||
<LinkExternal
|
|
||||||
className='ui--AddressCard-exporer-link'
|
|
||||||
data={address}
|
|
||||||
isLogo
|
|
||||||
type='address'
|
|
||||||
/>
|
/>
|
||||||
</td>
|
)}
|
||||||
<td className='button'>
|
{multiInfos && multiInfos.length !== 0 && (
|
||||||
<div className='table-column-icon'
|
<Badge
|
||||||
data-testid='row-toggle'
|
color='red'
|
||||||
onClick={toggleIsExpanded}>
|
hover={t<string>('Multisig approvals pending')}
|
||||||
<Icon icon={
|
info={multiInfos.length}
|
||||||
isExpanded
|
/>
|
||||||
? 'caret-up'
|
)}
|
||||||
: 'caret-down'
|
{isProxied && !proxyInfo.hasOwned && (
|
||||||
}
|
<Badge
|
||||||
/>
|
color='red'
|
||||||
</div>
|
hover={t<string>('Proxied account has no owned proxies')}
|
||||||
</td>
|
info='0'
|
||||||
</tr>
|
/>
|
||||||
<tr className={`${className} ${isExpanded ? 'isExpanded' : 'isCollapsed'} ${isEven ? 'isEven' : 'isOdd'}`}>
|
)}
|
||||||
<td colSpan={2} />
|
{delegation?.accountDelegated && (
|
||||||
<td>
|
<Badge
|
||||||
<div className='tags'
|
color='blue'
|
||||||
data-testid='tags'>
|
hover={t<string>('This account has a governance delegation')}
|
||||||
<Tags
|
icon='calendar-check'
|
||||||
value={tags}
|
onClick={toggleDelegate}
|
||||||
withTitle
|
/>
|
||||||
/>
|
)}
|
||||||
</div>
|
{!!proxy?.[0].length && api.api.tx.utility && (
|
||||||
</td>
|
<Badge
|
||||||
<td className='media--1500'/>
|
color='blue'
|
||||||
<td/>
|
hover={t<string>('This account has {{proxyNumber}} proxy set.', {
|
||||||
<td>
|
replace: {
|
||||||
<AddressInfo
|
proxyNumber: proxy[0].length
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
icon='arrow-right'
|
||||||
|
onClick={toggleProxyOverview}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className='address'>
|
||||||
|
<AddressSmall value={address} />
|
||||||
|
{isBackupOpen && (
|
||||||
|
<Backup
|
||||||
address={address}
|
address={address}
|
||||||
balancesAll={balancesAll}
|
key='modal-backup-account'
|
||||||
withBalance={{
|
onClose={toggleBackup}
|
||||||
available: true,
|
|
||||||
bonded: true,
|
|
||||||
locked: true,
|
|
||||||
redeemable: true,
|
|
||||||
reserved: true,
|
|
||||||
total: false,
|
|
||||||
unlocking: true,
|
|
||||||
vested: true
|
|
||||||
}}
|
|
||||||
withExtended={false}
|
|
||||||
/>
|
/>
|
||||||
</td>
|
)}
|
||||||
<td colSpan={4} />
|
{isDelegateOpen && (
|
||||||
</tr>
|
<DelegateModal
|
||||||
</>
|
key='modal-delegate'
|
||||||
|
onClose={toggleDelegate}
|
||||||
|
previousAmount={delegation?.amount}
|
||||||
|
previousConviction={delegation?.conviction}
|
||||||
|
previousDelegatedAccount={delegation?.accountDelegated}
|
||||||
|
previousDelegatingAccount={address}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isDeriveOpen && (
|
||||||
|
<Derive
|
||||||
|
from={address}
|
||||||
|
key='modal-derive-account'
|
||||||
|
onClose={toggleDerive}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isForgetOpen && (
|
||||||
|
<Forget
|
||||||
|
address={address}
|
||||||
|
key='modal-forget-account'
|
||||||
|
onClose={toggleForget}
|
||||||
|
onForget={_onForget}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isIdentityMainOpen && (
|
||||||
|
<IdentityMain
|
||||||
|
address={address}
|
||||||
|
key='modal-identity-main'
|
||||||
|
onClose={toggleIdentityMain}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isIdentitySubOpen && (
|
||||||
|
<IdentitySub
|
||||||
|
address={address}
|
||||||
|
key='modal-identity-sub'
|
||||||
|
onClose={toggleIdentitySub}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isPasswordOpen && (
|
||||||
|
<ChangePass
|
||||||
|
address={address}
|
||||||
|
key='modal-change-pass'
|
||||||
|
onClose={togglePassword}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isTransferOpen && (
|
||||||
|
<Transfer
|
||||||
|
key='modal-transfer'
|
||||||
|
onClose={toggleTransfer}
|
||||||
|
senderId={address}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isProxyOverviewOpen && (
|
||||||
|
<ProxyOverview
|
||||||
|
key='modal-proxy-overview'
|
||||||
|
onClose={toggleProxyOverview}
|
||||||
|
previousProxy={proxy}
|
||||||
|
proxiedAccount={address}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isMultisigOpen && multiInfos && (
|
||||||
|
<MultisigApprove
|
||||||
|
address={address}
|
||||||
|
key='multisig-approve'
|
||||||
|
onClose={toggleMultisig}
|
||||||
|
ongoing={multiInfos}
|
||||||
|
threshold={meta.threshold as number}
|
||||||
|
who={meta.who as string[]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isRecoverAccountOpen && (
|
||||||
|
<RecoverAccount
|
||||||
|
address={address}
|
||||||
|
key='recover-account'
|
||||||
|
onClose={toggleRecoverAccount}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isRecoverSetupOpen && (
|
||||||
|
<RecoverSetup
|
||||||
|
address={address}
|
||||||
|
key='recover-setup'
|
||||||
|
onClose={toggleRecoverSetup}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isUndelegateOpen && (
|
||||||
|
<UndelegateModal
|
||||||
|
accountDelegating={address}
|
||||||
|
key='modal-delegate'
|
||||||
|
onClose={toggleUndelegate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className='address media--1400'>
|
||||||
|
{(meta.parentAddress as string) && (
|
||||||
|
<AddressMini value={meta.parentAddress} />
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td className='number'>
|
||||||
|
<CryptoType accountId={address} />
|
||||||
|
</td>
|
||||||
|
<td className='all'>
|
||||||
|
<div className='tags'>
|
||||||
|
<Tags value={tags} />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className='number media--1500'>
|
||||||
|
{balancesAll?.accountNonce.gt(BN_ZERO) && formatNumber(balancesAll.accountNonce)}
|
||||||
|
</td>
|
||||||
|
<td className='number'>
|
||||||
|
<AddressInfo
|
||||||
|
address={address}
|
||||||
|
balancesAll={balancesAll}
|
||||||
|
withBalance
|
||||||
|
withBalanceToggle
|
||||||
|
withExtended={false}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className='button'>
|
||||||
|
{isFunction(api.api.tx.balances?.transfer) && (
|
||||||
|
<Button
|
||||||
|
icon='paper-plane'
|
||||||
|
label={t<string>('send')}
|
||||||
|
onClick={toggleTransfer}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Popup
|
||||||
|
className={`theme--${theme}`}
|
||||||
|
isOpen={isSettingsOpen}
|
||||||
|
onClose={toggleSettings}
|
||||||
|
trigger={
|
||||||
|
<Button
|
||||||
|
icon='ellipsis-v'
|
||||||
|
isDisabled={!menuItems.length}
|
||||||
|
onClick={toggleSettings}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Menu
|
||||||
|
onClick={toggleSettings}
|
||||||
|
text
|
||||||
|
vertical
|
||||||
|
>
|
||||||
|
{menuItems}
|
||||||
|
</Menu>
|
||||||
|
</Popup>
|
||||||
|
</td>
|
||||||
|
<td className='links media--1400'>
|
||||||
|
<LinkExternal
|
||||||
|
className='ui--AddressCard-exporer-link'
|
||||||
|
data={address}
|
||||||
|
isLogo
|
||||||
|
type='address'
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default React.memo(styled(Account)`
|
export default React.memo(styled(Account)`
|
||||||
&.isCollapsed {
|
|
||||||
visibility: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.isExpanded {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags {
|
.tags {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 1.5rem;
|
min-height: 1.5rem;
|
||||||
@ -719,15 +661,4 @@ export default React.memo(styled(Account)`
|
|||||||
.devBadge {
|
.devBadge {
|
||||||
opacity: 0.65;
|
opacity: 0.65;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-column-icon {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 1.7rem;
|
|
||||||
height: 1.7rem;
|
|
||||||
border: 1px solid var(--border-table);
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`);
|
`);
|
||||||
|
|||||||
@ -2,37 +2,20 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import type { Balance } from '@polkadot/types/interfaces';
|
import type { Balance } from '@polkadot/types/interfaces';
|
||||||
|
import type { AccountOverrides } from '../../test/hooks/default';
|
||||||
|
|
||||||
import { fireEvent, screen, within } from '@testing-library/react';
|
import { screen, within } from '@testing-library/react';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
|
|
||||||
import { DeriveBalancesAll, DeriveStakingAccount } from '@polkadot/api-derive/types';
|
|
||||||
import i18next from '@polkadot/react-components/i18n';
|
import i18next from '@polkadot/react-components/i18n';
|
||||||
import toShortAddress from '@polkadot/react-components/util/toShortAddress';
|
|
||||||
import { UseAccountInfo } from '@polkadot/react-hooks/types';
|
|
||||||
import { balanceOf } from '@polkadot/test-support/creation/balance';
|
import { balanceOf } from '@polkadot/test-support/creation/balance';
|
||||||
import { makeStakingLedger as ledger } from '@polkadot/test-support/creation/stakingInfo/stakingLedger';
|
import { makeStakingLedger as ledger } from '@polkadot/test-support/creation/stakingInfo/stakingLedger';
|
||||||
import { MemoryStore } from '@polkadot/test-support/keyring';
|
import { MemoryStore } from '@polkadot/test-support/keyring';
|
||||||
import { keyring } from '@polkadot/ui-keyring';
|
import { keyring } from '@polkadot/ui-keyring';
|
||||||
import { KeyringJson$Meta } from '@polkadot/ui-keyring/types';
|
|
||||||
|
|
||||||
import { AccountOverrides } from '../../test/hooks/default';
|
import { AccountsPage } from '../../test/pages/accountsPage';
|
||||||
import { AccountsPage, format } from '../../test/pages/accountsPage';
|
|
||||||
|
|
||||||
describe('Accounts page', () => {
|
describe('Accounts page', () => {
|
||||||
const aliceAddress = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY';
|
|
||||||
const bobAddress = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
|
|
||||||
const charlieAddress = '5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy';
|
|
||||||
const aliceDerivedAddress = '5Dc96kiTPTfZHmq6yTFSqejJzfUNfQQjneNesRWf9MDppJsd';
|
|
||||||
|
|
||||||
const addAccountToKeyring = (address: string, meta: KeyringJson$Meta) => {
|
|
||||||
keyring.addExternal(address, meta);
|
|
||||||
};
|
|
||||||
|
|
||||||
const forgetAccountFromKeyring = (address: string) => {
|
|
||||||
keyring.forgetAccount(address);
|
|
||||||
};
|
|
||||||
|
|
||||||
let accountsPage: AccountsPage;
|
let accountsPage: AccountsPage;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -42,7 +25,6 @@ describe('Accounts page', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
accountsPage = new AccountsPage();
|
accountsPage = new AccountsPage();
|
||||||
forgetAccountFromKeyring(aliceDerivedAddress);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when no accounts', () => {
|
describe('when no accounts', () => {
|
||||||
@ -79,7 +61,17 @@ describe('Accounts page', () => {
|
|||||||
|
|
||||||
describe('when some accounts exist', () => {
|
describe('when some accounts exist', () => {
|
||||||
it('the accounts table contains some account rows', async () => {
|
it('the accounts table contains some account rows', async () => {
|
||||||
renderDefaultAccounts(2);
|
defaultRender(
|
||||||
|
{
|
||||||
|
balance: {
|
||||||
|
freeBalance: balance(10000)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
balance: {
|
||||||
|
freeBalance: balance(999)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const accountRows = await accountsPage.findAccountRows();
|
const accountRows = await accountsPage.findAccountRows();
|
||||||
|
|
||||||
@ -87,117 +79,44 @@ describe('Accounts page', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('account rows display the total balance info', async () => {
|
it('account rows display the total balance info', async () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender(
|
||||||
anAccountWithBalance({ freeBalance: balance(500) }),
|
{
|
||||||
anAccountWithBalance({ freeBalance: balance(200), reservedBalance: balance(150) })
|
balance: {
|
||||||
);
|
freeBalance: balance(500)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
balance: {
|
||||||
|
freeBalance: balance(200),
|
||||||
|
reservedBalance: balance(150)
|
||||||
|
}
|
||||||
|
});
|
||||||
const rows = await accountsPage.findAccountRows();
|
const rows = await accountsPage.findAccountRows();
|
||||||
|
|
||||||
await rows[0].assertBalancesTotal(balance(500));
|
const row1TotalActual = await within(rows[0]).findByTestId('balance-summary');
|
||||||
await rows[1].assertBalancesTotal(balance(350));
|
const row1TotalExpected = accountsPage.format(balance(500));
|
||||||
});
|
|
||||||
|
|
||||||
it('account rows display the details balance info', async () => {
|
expect(row1TotalActual).toHaveTextContent(row1TotalExpected);
|
||||||
renderAccountsWithDefaultAddresses(
|
|
||||||
anAccountWithBalance({ freeBalance: balance(500), lockedBalance: balance(30) }),
|
|
||||||
anAccountWithBalance({ availableBalance: balance(50), freeBalance: balance(200), reservedBalance: balance(150) })
|
|
||||||
);
|
|
||||||
|
|
||||||
const rows = await accountsPage.findAccountRows();
|
const row2TotalActual = await within(rows[1]).findByTestId('balance-summary');
|
||||||
|
const row2TotalExpected = accountsPage.format(balance(350));
|
||||||
|
|
||||||
await rows[0].assertBalancesDetails([
|
expect(row2TotalActual).toHaveTextContent(row2TotalExpected);
|
||||||
{ amount: balance(0), name: 'transferrable' },
|
|
||||||
{ amount: balance(30), name: 'locked' }]);
|
|
||||||
await rows[1].assertBalancesDetails([
|
|
||||||
{ amount: balance(50), name: 'transferrable' },
|
|
||||||
{ amount: balance(150), name: 'reserved' }]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('derived account displays parent account info', async () => {
|
|
||||||
addAccountToKeyring(aliceDerivedAddress, { parentAddress: aliceAddress });
|
|
||||||
|
|
||||||
renderAccountsForAddresses(
|
|
||||||
aliceAddress,
|
|
||||||
aliceDerivedAddress
|
|
||||||
);
|
|
||||||
|
|
||||||
const accountRows = await accountsPage.findAccountRows();
|
|
||||||
|
|
||||||
expect(accountRows).toHaveLength(2);
|
|
||||||
await accountRows[1].assertParentAccountName('ALICE');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('a separate column for parent account is not displayed', async () => {
|
|
||||||
renderDefaultAccounts(1);
|
|
||||||
const accountsTable = await accountsPage.findAccountsTable();
|
|
||||||
|
|
||||||
assertColumnNotExistInTable('parent', accountsTable);
|
|
||||||
assertColumnExistsInTable('type', accountsTable);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('account rows display the shorted address', async () => {
|
|
||||||
renderAccountsForAddresses(
|
|
||||||
aliceAddress
|
|
||||||
);
|
|
||||||
const accountRows = await accountsPage.findAccountRows();
|
|
||||||
|
|
||||||
expect(accountRows).toHaveLength(1);
|
|
||||||
const aliceShortAddress = toShortAddress(aliceAddress);
|
|
||||||
|
|
||||||
await accountRows[0].assertShortAddress(aliceShortAddress);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('when account is not tagged, account row details displays no tags info', async () => {
|
|
||||||
renderDefaultAccounts(1);
|
|
||||||
const rows = await accountsPage.findAccountRows();
|
|
||||||
|
|
||||||
await rows[0].assertTags('no tags');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('when account is tagged, account row details displays tags', async () => {
|
|
||||||
renderAccountsWithDefaultAddresses(
|
|
||||||
anAccountWithInfo({ tags: ['my tag', 'Super Tag'] })
|
|
||||||
);
|
|
||||||
|
|
||||||
const rows = await accountsPage.findAccountRows();
|
|
||||||
|
|
||||||
await rows[0].assertTags('my tagSuper Tag');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('account details rows keep colouring from their primary rows', async () => {
|
|
||||||
renderDefaultAccounts(3);
|
|
||||||
|
|
||||||
const rows = await accountsPage.findAccountRows();
|
|
||||||
|
|
||||||
expect(rows[0].primaryRow).toHaveClass('isOdd');
|
|
||||||
expect(rows[0].detailsRow).toHaveClass('isOdd');
|
|
||||||
|
|
||||||
expect(rows[1].primaryRow).toHaveClass('isEven');
|
|
||||||
expect(rows[1].detailsRow).toHaveClass('isEven');
|
|
||||||
|
|
||||||
expect(rows[2].primaryRow).toHaveClass('isOdd');
|
|
||||||
expect(rows[2].detailsRow).toHaveClass('isOdd');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('account details rows toggled on icon toggle click', async () => {
|
|
||||||
renderDefaultAccounts(1);
|
|
||||||
|
|
||||||
const row = (await accountsPage.findAccountRows())[0];
|
|
||||||
const toggle = await within(row.primaryRow).findByTestId('row-toggle');
|
|
||||||
|
|
||||||
expect(row.detailsRow).toHaveClass('isCollapsed');
|
|
||||||
|
|
||||||
fireEvent.click(toggle);
|
|
||||||
|
|
||||||
expect(row.detailsRow).toHaveClass('isExpanded');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays some summary', () => {
|
it('displays some summary', () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender(
|
||||||
anAccountWithBalance({ freeBalance: balance(500) }),
|
{
|
||||||
anAccountWithBalance({ freeBalance: balance(200), reservedBalance: balance(150) })
|
balance: {
|
||||||
);
|
freeBalance: balance(500)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
balance: {
|
||||||
|
freeBalance: balance(200),
|
||||||
|
reservedBalance: balance(150)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const summaries = screen.queryAllByTestId(/card-summary:total \w+/i);
|
const summaries = screen.queryAllByTestId(/card-summary:total \w+/i);
|
||||||
|
|
||||||
@ -205,21 +124,26 @@ describe('Accounts page', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('displays balance summary', async () => {
|
it('displays balance summary', async () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender(
|
||||||
anAccountWithBalance({ freeBalance: balance(500) }),
|
{
|
||||||
anAccountWithBalance({ freeBalance: balance(200), reservedBalance: balance(150) })
|
balance: {
|
||||||
);
|
freeBalance: balance(500)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
balance: {
|
||||||
|
freeBalance: balance(200),
|
||||||
|
reservedBalance: balance(150)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const summary = await screen.findByTestId(/card-summary:(total )?balance/i);
|
const summary = await screen.findByTestId(/card-summary:(total )?balance/i);
|
||||||
|
|
||||||
expect(summary).toHaveTextContent(showBalance(500 + 200 + 150));
|
expect(summary).toHaveTextContent(showBalance(500 + 200 + 150));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays transferable summary', async () => {
|
it('displays transferrable summary', async () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender({ balance: { availableBalance: balance(400) } }, { balance: { availableBalance: balance(600) } });
|
||||||
anAccountWithBalance({ availableBalance: balance(400) }),
|
|
||||||
anAccountWithBalance({ availableBalance: balance(600) })
|
|
||||||
);
|
|
||||||
|
|
||||||
const summary = await screen.findByTestId(/card-summary:(total )?transferrable/i);
|
const summary = await screen.findByTestId(/card-summary:(total )?transferrable/i);
|
||||||
|
|
||||||
@ -227,10 +151,7 @@ describe('Accounts page', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('displays locked summary', async () => {
|
it('displays locked summary', async () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender({ balance: { lockedBalance: balance(400) } }, { balance: { lockedBalance: balance(600) } });
|
||||||
anAccountWithBalance({ lockedBalance: balance(400) }),
|
|
||||||
anAccountWithBalance({ lockedBalance: balance(600) })
|
|
||||||
);
|
|
||||||
|
|
||||||
const summary = await screen.findByTestId(/card-summary:(total )?locked/i);
|
const summary = await screen.findByTestId(/card-summary:(total )?locked/i);
|
||||||
|
|
||||||
@ -238,10 +159,17 @@ describe('Accounts page', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('displays bonded summary', async () => {
|
it('displays bonded summary', async () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender(
|
||||||
anAccountWithStaking({ stakingLedger: ledger(balance(70)) }),
|
{
|
||||||
anAccountWithStaking({ stakingLedger: ledger(balance(20)) })
|
staking: {
|
||||||
);
|
stakingLedger: ledger(balance(70))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
staking: {
|
||||||
|
stakingLedger: ledger(balance(20))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const summary = await screen.findByTestId(/card-summary:(total )?bonded/i);
|
const summary = await screen.findByTestId(/card-summary:(total )?bonded/i);
|
||||||
|
|
||||||
@ -249,40 +177,43 @@ describe('Accounts page', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('displays unbonding summary', async () => {
|
it('displays unbonding summary', async () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender(
|
||||||
anAccountWithStaking({
|
{
|
||||||
unlocking: [
|
staking: {
|
||||||
{
|
unlocking: [
|
||||||
remainingEras: new BN('1000000000'),
|
{
|
||||||
value: balance(200)
|
remainingEras: new BN('1000000000'),
|
||||||
},
|
value: balance(200)
|
||||||
{
|
},
|
||||||
remainingEras: new BN('2000000000'),
|
{
|
||||||
value: balance(300)
|
remainingEras: new BN('2000000000'),
|
||||||
},
|
value: balance(300)
|
||||||
{
|
},
|
||||||
remainingEras: new BN('3000000000'),
|
{
|
||||||
value: balance(400)
|
remainingEras: new BN('3000000000'),
|
||||||
}
|
value: balance(400)
|
||||||
]
|
}
|
||||||
}),
|
]
|
||||||
anAccountWithStaking({
|
}
|
||||||
unlocking: [
|
},
|
||||||
{
|
{
|
||||||
remainingEras: new BN('1000000000'),
|
staking: {
|
||||||
value: balance(100)
|
unlocking: [
|
||||||
},
|
{
|
||||||
{
|
remainingEras: new BN('1000000000'),
|
||||||
remainingEras: new BN('2000000000'),
|
value: balance(100)
|
||||||
value: balance(200)
|
},
|
||||||
},
|
{
|
||||||
{
|
remainingEras: new BN('2000000000'),
|
||||||
remainingEras: new BN('3000000000'),
|
value: balance(200)
|
||||||
value: balance(300)
|
},
|
||||||
}
|
{
|
||||||
]
|
remainingEras: new BN('3000000000'),
|
||||||
})
|
value: balance(300)
|
||||||
);
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const summary = await screen.findByTestId(/card-summary:(total )?unbonding/i);
|
const summary = await screen.findByTestId(/card-summary:(total )?unbonding/i);
|
||||||
|
|
||||||
@ -290,10 +221,7 @@ describe('Accounts page', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('displays redeemable summary', async () => {
|
it('displays redeemable summary', async () => {
|
||||||
renderAccountsWithDefaultAddresses(
|
defaultRender({ staking: { redeemable: balance(4000) } }, { staking: { redeemable: balance(5000) } });
|
||||||
anAccountWithStaking({ redeemable: balance(4000) }),
|
|
||||||
anAccountWithStaking({ redeemable: balance(5000) })
|
|
||||||
);
|
|
||||||
|
|
||||||
const summary = await screen.findByTestId(/card-summary:(total )?redeemable/i);
|
const summary = await screen.findByTestId(/card-summary:(total )?redeemable/i);
|
||||||
|
|
||||||
@ -313,55 +241,22 @@ describe('Accounts page', () => {
|
|||||||
return balanceOf(amountInt.toString() + decimalsPadded);
|
return balanceOf(amountInt.toString() + decimalsPadded);
|
||||||
};
|
};
|
||||||
|
|
||||||
const anAccount = (): AccountOverrides => ({});
|
const showBalance = function (amount: number): string {
|
||||||
|
return accountsPage.format(balance(amount));
|
||||||
const anAccountWithBalance = (balance: { [P in keyof DeriveBalancesAll]?: DeriveBalancesAll[P] }) => ({
|
};
|
||||||
balance
|
|
||||||
});
|
|
||||||
|
|
||||||
const anAccountWithInfo = (info: { [P in keyof UseAccountInfo]?: UseAccountInfo[P] }) => ({
|
|
||||||
info
|
|
||||||
});
|
|
||||||
|
|
||||||
const anAccountWithStaking = (staking: { [P in keyof DeriveStakingAccount]?: DeriveStakingAccount[P]}) => ({
|
|
||||||
staking
|
|
||||||
});
|
|
||||||
|
|
||||||
function assertColumnNotExistInTable (columnName: string, table: HTMLElement) {
|
|
||||||
expect(within(table).queryByRole('columnheader', { name: columnName })).toBeFalsy();
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertColumnExistsInTable (columnName: string, table: HTMLElement) {
|
|
||||||
expect(within(table).getByRole('columnheader', { name: columnName })).toBeTruthy();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showBalance (amount: number): string {
|
|
||||||
return format(balance(amount));
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultAddresses = [
|
const defaultAddresses = [
|
||||||
aliceAddress,
|
'5DAAnrj7VHTznn2AWBemMuyBwZWs6FNFjdyVXUeYum3PTXFy',
|
||||||
bobAddress,
|
'5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw'
|
||||||
charlieAddress
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function renderAccountsWithDefaultAddresses (...overrides: AccountOverrides[]): void {
|
const defaultRender = function (...overrides: AccountOverrides[]): void {
|
||||||
const accounts = overrides.map((accountProperties, index) =>
|
const accounts: [string, AccountOverrides][] = [];
|
||||||
[defaultAddresses[index], accountProperties] as [string, AccountOverrides]);
|
|
||||||
|
for (let i = 0; i < overrides.length; i++) {
|
||||||
|
accounts.push([defaultAddresses[i], overrides[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
accountsPage.renderPage(accounts);
|
accountsPage.renderPage(accounts);
|
||||||
}
|
};
|
||||||
|
|
||||||
function renderAccountsForAddresses (...addresses: string[]): void {
|
|
||||||
const accounts = addresses.map((address) => [address, anAccount()] as [string, AccountOverrides]);
|
|
||||||
|
|
||||||
accountsPage.renderPage(accounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderDefaultAccounts (accountsNumber: number): void {
|
|
||||||
const accounts = Array.from({ length: accountsNumber },
|
|
||||||
(_, index) => [defaultAddresses[index], anAccount()] as [string, AccountOverrides]);
|
|
||||||
|
|
||||||
accountsPage.renderPage(accounts);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -73,12 +73,13 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen
|
|||||||
|
|
||||||
const headerRef = useRef([
|
const headerRef = useRef([
|
||||||
[t('accounts'), 'start', 3],
|
[t('accounts'), 'start', 3],
|
||||||
|
[t('parent'), 'address media--1400'],
|
||||||
[t('type')],
|
[t('type')],
|
||||||
|
[t('tags'), 'start'],
|
||||||
[t('transactions'), 'media--1500'],
|
[t('transactions'), 'media--1500'],
|
||||||
[t('balances'), 'balances'],
|
[t('balances'), 'expand'],
|
||||||
[],
|
[],
|
||||||
[undefined, 'media--1400'],
|
[undefined, 'media--1400']
|
||||||
[]
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect((): void => {
|
useEffect((): void => {
|
||||||
@ -234,7 +235,6 @@ function Overview ({ className = '', onStatusChange }: Props): React.ReactElemen
|
|||||||
account={account}
|
account={account}
|
||||||
delegation={delegation}
|
delegation={delegation}
|
||||||
filter={filterOn}
|
filter={filterOn}
|
||||||
isEven={!!(index % 2)}
|
|
||||||
isFavorite={isFavorite}
|
isFavorite={isFavorite}
|
||||||
key={`${index}:${account.address}`}
|
key={`${index}:${account.address}`}
|
||||||
proxy={proxies?.[index]}
|
proxy={proxies?.[index]}
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
import type { DeriveBalancesAll, DeriveStakingAccount } from '@polkadot/api-derive/types';
|
import type { DeriveBalancesAll, DeriveStakingAccount } from '@polkadot/api-derive/types';
|
||||||
import type { UseAccountInfo } from '@polkadot/react-hooks/types';
|
|
||||||
|
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
|
|
||||||
@ -12,7 +11,6 @@ import { makeStakingLedger } from '@polkadot/test-support/creation/stakingInfo/s
|
|||||||
|
|
||||||
export interface Account {
|
export interface Account {
|
||||||
balance: DeriveBalancesAll,
|
balance: DeriveBalancesAll,
|
||||||
info: UseAccountInfo,
|
|
||||||
staking: DeriveStakingAccount
|
staking: DeriveStakingAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,9 +26,6 @@ export interface AccountOverrides {
|
|||||||
staking?: {
|
staking?: {
|
||||||
[P in keyof DeriveStakingAccount]?: DeriveStakingAccount[P];
|
[P in keyof DeriveStakingAccount]?: DeriveStakingAccount[P];
|
||||||
};
|
};
|
||||||
info?: {
|
|
||||||
[P in keyof UseAccountInfo]?: UseAccountInfo[P];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const emptyAccounts: UseAccounts = {
|
export const emptyAccounts: UseAccounts = {
|
||||||
@ -79,12 +74,6 @@ export const defaultStakingAccount: DeriveStakingAccount = {
|
|||||||
]
|
]
|
||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
||||||
export const defaultAccountInfo: UseAccountInfo = {
|
|
||||||
flags: {},
|
|
||||||
tags: []
|
|
||||||
} as any;
|
|
||||||
|
|
||||||
class MockAccountHooks {
|
class MockAccountHooks {
|
||||||
public useAccounts: UseAccounts = emptyAccounts;
|
public useAccounts: UseAccounts = emptyAccounts;
|
||||||
public accountsMap: AccountsMap = {};
|
public accountsMap: AccountsMap = {};
|
||||||
@ -103,7 +92,6 @@ class MockAccountHooks {
|
|||||||
for (const [address, props] of accounts) {
|
for (const [address, props] of accounts) {
|
||||||
const staking = { ...defaultStakingAccount };
|
const staking = { ...defaultStakingAccount };
|
||||||
const balance = { ...defaultBalanceAccount };
|
const balance = { ...defaultBalanceAccount };
|
||||||
const info = { ...defaultAccountInfo };
|
|
||||||
|
|
||||||
// Typescript does not recognize that keys and values from Object.entries are safe,
|
// Typescript does not recognize that keys and values from Object.entries are safe,
|
||||||
// so we have to use "any" here.
|
// so we have to use "any" here.
|
||||||
@ -112,12 +100,9 @@ class MockAccountHooks {
|
|||||||
Object.entries(props.balance || balance).forEach(function ([key, value]) { (balance as any)[key] = value; });
|
Object.entries(props.balance || balance).forEach(function ([key, value]) { (balance as any)[key] = value; });
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||||
Object.entries(props.staking || staking).forEach(function ([key, value]) { (staking as any)[key] = value; });
|
Object.entries(props.staking || staking).forEach(function ([key, value]) { (staking as any)[key] = value; });
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
||||||
Object.entries(props.info || info).forEach(function ([key, value]) { (info as any)[key] = value; });
|
|
||||||
|
|
||||||
this.accountsMap[address] = {
|
this.accountsMap[address] = {
|
||||||
balance: balance,
|
balance: balance,
|
||||||
info: info,
|
|
||||||
staking: staking
|
staking: staking
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,60 +25,12 @@ let queueExtrinsic: (value: PartialQueueTxExtrinsic) => void;
|
|||||||
class NotYetRendered extends Error {
|
class NotYetRendered extends Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// utility wrapper over an account item in accounts table, serves basic assertions about an account row
|
export type AccountRow = HTMLElement;
|
||||||
export class AccountRow {
|
|
||||||
public primaryRow: HTMLElement;
|
|
||||||
public detailsRow: HTMLElement;
|
|
||||||
|
|
||||||
constructor (primaryRow: HTMLElement, detailsRow: HTMLElement) {
|
|
||||||
this.primaryRow = primaryRow;
|
|
||||||
this.detailsRow = detailsRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
async assertBalancesTotal (expected: Balance): Promise<void> {
|
|
||||||
const balanceActual = await within(this.primaryRow).findByTestId('balance-summary');
|
|
||||||
const balanceExpected = format(expected);
|
|
||||||
|
|
||||||
expect(balanceActual).toHaveTextContent(balanceExpected);
|
|
||||||
}
|
|
||||||
|
|
||||||
async assertBalancesDetails (expected: {name: string, amount: Balance}[]): Promise<void> {
|
|
||||||
for (const expectedBalanceDetailsItem of expected) {
|
|
||||||
const labelElement = await within(this.detailsRow).findByText(expectedBalanceDetailsItem.name);
|
|
||||||
const balanceElement = labelElement.nextSibling;
|
|
||||||
const amount = format(expectedBalanceDetailsItem.amount);
|
|
||||||
|
|
||||||
expect(balanceElement).toHaveTextContent(amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async assertParentAccountName (expectedParentAccount: string): Promise<void> {
|
|
||||||
const parentAccount = await within(this.primaryRow).findByTestId('parent');
|
|
||||||
|
|
||||||
expect(parentAccount).toHaveTextContent(expectedParentAccount);
|
|
||||||
}
|
|
||||||
|
|
||||||
async assertTags (tagsContent: string): Promise<void> {
|
|
||||||
const tagsActual = await within(this.detailsRow).findByTestId('tags');
|
|
||||||
|
|
||||||
expect(tagsActual).toHaveTextContent(tagsContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async assertShortAddress (expectedShortAddress: string): Promise<void> {
|
|
||||||
const actualShortAddress = await within(this.primaryRow).findByTestId('short-address');
|
|
||||||
|
|
||||||
expect(actualShortAddress).toHaveTextContent(expectedShortAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jest.mock('@polkadot/react-hooks/useAccounts', () => ({
|
jest.mock('@polkadot/react-hooks/useAccounts', () => ({
|
||||||
useAccounts: () => mockAccountHooks.useAccounts
|
useAccounts: () => mockAccountHooks.useAccounts
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('@polkadot/react-hooks/useAccountInfo', () => ({
|
|
||||||
useAccountInfo: (address: string) => mockAccountHooks.accountsMap[address].info
|
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('@polkadot/react-hooks/useLoadingDelay', () => ({
|
jest.mock('@polkadot/react-hooks/useLoadingDelay', () => ({
|
||||||
useLoadingDelay: () => false
|
useLoadingDelay: () => false
|
||||||
}));
|
}));
|
||||||
@ -161,20 +113,16 @@ export class AccountsPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async findAccountRows (): Promise<AccountRow[]> {
|
async findAccountRows (): Promise<AccountRow[]> {
|
||||||
|
this.assertRendered();
|
||||||
const table = await this.findAccountsTable();
|
const table = await this.findAccountsTable();
|
||||||
const tableBody = table.getElementsByTagName('tbody')[0];
|
const tableBody = table.getElementsByTagName('tbody')[0];
|
||||||
const htmlRows = (await within(tableBody).findAllByRole('row'))
|
const rows = await within(tableBody).findAllByRole('row');
|
||||||
.filter((r) => r.className.startsWith('Account-'));
|
|
||||||
const rows: AccountRow[] = [];
|
|
||||||
|
|
||||||
for (let rowIdx = 0; rowIdx < htmlRows.length; rowIdx = rowIdx + 2) {
|
return rows.filter((r) => r.className.startsWith('Account-'));
|
||||||
const primary = htmlRows[rowIdx];
|
}
|
||||||
const details = htmlRows[rowIdx + 1];
|
|
||||||
|
|
||||||
rows.push(new AccountRow(primary, details));
|
format (amount: Balance | BN): string {
|
||||||
}
|
return formatBalance(amount, { decimals: 12, forceUnit: '-', withUnit: true });
|
||||||
|
|
||||||
return rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private assertRendered () {
|
private assertRendered () {
|
||||||
@ -183,7 +131,3 @@ export class AccountsPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const format = (amount: Balance | BN): string => {
|
|
||||||
return formatBalance(amount, { decimals: 12, forceUnit: '-', withUnit: true });
|
|
||||||
};
|
|
||||||
|
|||||||
@ -81,11 +81,6 @@ const DEFAULT_PREFS = {
|
|||||||
validatorPayment: true
|
validatorPayment: true
|
||||||
};
|
};
|
||||||
|
|
||||||
// auxiliary component that helps aligning balances details, fills up the space when no icon for a balance is specified
|
|
||||||
function IconVoid (): React.ReactElement {
|
|
||||||
return <span className='icon-void'> </span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lookupLock (lookup: Record<string, string>, lockId: LockIdentifier): string {
|
function lookupLock (lookup: Record<string, string>, lockId: LockIdentifier): string {
|
||||||
const lockHex = lockId.toHex();
|
const lockHex = lockId.toHex();
|
||||||
|
|
||||||
@ -227,11 +222,10 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
|
|
||||||
!withBalanceToggle && balancesAll && balanceDisplay.total && allItems.push(
|
!withBalanceToggle && balancesAll && balanceDisplay.total && allItems.push(
|
||||||
<React.Fragment key={0}>
|
<React.Fragment key={0}>
|
||||||
<Label label={t<string>('account balance')} />
|
<Label label={t<string>('total')} />
|
||||||
<FormatBalance
|
<FormatBalance
|
||||||
className='result'
|
className='result'
|
||||||
formatIndex={formatIndex}
|
formatIndex={formatIndex}
|
||||||
labelPost={<IconVoid/>}
|
|
||||||
value={balancesAll.freeBalance.add(balancesAll.reservedBalance)}
|
value={balancesAll.freeBalance.add(balancesAll.reservedBalance)}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -242,7 +236,6 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
<FormatBalance
|
<FormatBalance
|
||||||
className='result'
|
className='result'
|
||||||
formatIndex={formatIndex}
|
formatIndex={formatIndex}
|
||||||
labelPost={<IconVoid/>}
|
|
||||||
value={(balancesAll as DeriveBalancesAll).availableBalance}
|
value={(balancesAll as DeriveBalancesAll).availableBalance}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -253,7 +246,7 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
<FormatBalance
|
<FormatBalance
|
||||||
className='result'
|
className='result'
|
||||||
formatIndex={formatIndex}
|
formatIndex={formatIndex}
|
||||||
labelPost={
|
label={
|
||||||
<Icon
|
<Icon
|
||||||
icon='info-circle'
|
icon='info-circle'
|
||||||
tooltip={`${address}-vested-trigger`}
|
tooltip={`${address}-vested-trigger`}
|
||||||
@ -293,7 +286,7 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
<FormatBalance
|
<FormatBalance
|
||||||
className='result'
|
className='result'
|
||||||
formatIndex={formatIndex}
|
formatIndex={formatIndex}
|
||||||
labelPost={
|
label={
|
||||||
<Icon
|
<Icon
|
||||||
icon='info-circle'
|
icon='info-circle'
|
||||||
tooltip={`${address}-locks-trigger`}
|
tooltip={`${address}-locks-trigger`}
|
||||||
@ -321,7 +314,6 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
<FormatBalance
|
<FormatBalance
|
||||||
className='result'
|
className='result'
|
||||||
formatIndex={formatIndex}
|
formatIndex={formatIndex}
|
||||||
labelPost={<IconVoid/>}
|
|
||||||
value={balancesAll.reservedBalance}
|
value={balancesAll.reservedBalance}
|
||||||
/>
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -332,7 +324,6 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
<FormatBalance
|
<FormatBalance
|
||||||
className='result'
|
className='result'
|
||||||
formatIndex={formatIndex}
|
formatIndex={formatIndex}
|
||||||
labelPost={<IconVoid/>}
|
|
||||||
value={ownBonded}
|
value={ownBonded}
|
||||||
>
|
>
|
||||||
{otherBonded.length !== 0 && (
|
{otherBonded.length !== 0 && (
|
||||||
@ -340,7 +331,6 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
<FormatBalance
|
<FormatBalance
|
||||||
formatIndex={formatIndex}
|
formatIndex={formatIndex}
|
||||||
key={index}
|
key={index}
|
||||||
labelPost={<IconVoid/>}
|
|
||||||
value={bonded}
|
value={bonded}
|
||||||
/>
|
/>
|
||||||
)})</>
|
)})</>
|
||||||
@ -363,10 +353,7 @@ function createBalanceItems (formatIndex: number, lookup: Record<string, string>
|
|||||||
<React.Fragment key={7}>
|
<React.Fragment key={7}>
|
||||||
<Label label={t<string>('unbonding')} />
|
<Label label={t<string>('unbonding')} />
|
||||||
<div className='result'>
|
<div className='result'>
|
||||||
<StakingUnbonding
|
<StakingUnbonding stakingInfo={stakingInfo} />
|
||||||
iconPosition='right'
|
|
||||||
stakingInfo={stakingInfo}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
@ -503,11 +490,12 @@ export default withMulti(
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
&:not(.ui--AddressInfo-expander) {
|
&:not(.ui--AddressInfo-expander) {
|
||||||
justify-content: flex-end;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column {
|
.column {
|
||||||
max-width: 260px;
|
justify-content: start;
|
||||||
|
|
||||||
&.column--expander {
|
&.column--expander {
|
||||||
width: 17.5rem;
|
width: 17.5rem;
|
||||||
|
|
||||||
@ -525,14 +513,12 @@ export default withMulti(
|
|||||||
&:not(.column--expander) {
|
&:not(.column--expander) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: grid;
|
display: grid;
|
||||||
column-gap: 1.5rem;
|
|
||||||
row-gap: 0.571rem;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
padding-right: 0.5rem;
|
padding-right: 0.5rem;
|
||||||
text-align: left;
|
text-align: right;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
.help.circle.icon {
|
.help.circle.icon {
|
||||||
@ -542,19 +528,12 @@ export default withMulti(
|
|||||||
|
|
||||||
.result {
|
.result {
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
.ui--Icon,
|
.icon {
|
||||||
.icon-void {
|
margin-left: 0;
|
||||||
margin-left: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
margin-right: 0;
|
|
||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-void {
|
|
||||||
float: right;
|
|
||||||
width: 1em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,6 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import AccountName from './AccountName';
|
import AccountName from './AccountName';
|
||||||
import IdentityIcon from './IdentityIcon';
|
import IdentityIcon from './IdentityIcon';
|
||||||
import ParentAddress from './ParentAddress';
|
|
||||||
import { toShortAddress } from './util';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@ -17,85 +15,38 @@ interface Props {
|
|||||||
defaultName?: string;
|
defaultName?: string;
|
||||||
onClickName?: () => void;
|
onClickName?: () => void;
|
||||||
overrideName?: React.ReactNode;
|
overrideName?: React.ReactNode;
|
||||||
parentAddress?: string;
|
|
||||||
withSidebar?: boolean;
|
withSidebar?: boolean;
|
||||||
withShortAddress?: boolean;
|
|
||||||
toggle?: unknown;
|
toggle?: unknown;
|
||||||
value?: string | Address | AccountId | null | Uint8Array;
|
value?: string | Address | AccountId | null | Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AddressSmall ({ children, className = '', defaultName, onClickName, overrideName, parentAddress, toggle, value, withShortAddress = false, withSidebar = true }: Props): React.ReactElement<Props> {
|
function AddressSmall ({ children, className = '', defaultName, onClickName, overrideName, toggle, value, withSidebar = true }: Props): React.ReactElement<Props> {
|
||||||
return (
|
return (
|
||||||
<div className={`ui--AddressSmall ${className}`}>
|
<div className={`ui--AddressSmall ${className}`}>
|
||||||
<div>
|
<IdentityIcon value={value as Uint8Array} />
|
||||||
<IdentityIcon value={value as Uint8Array} />
|
<AccountName
|
||||||
</div>
|
className={withSidebar ? 'withSidebar' : ''}
|
||||||
<div className='addressContainer'>
|
defaultName={defaultName}
|
||||||
<div className='additionalInfo parentAccountName'>
|
onClick={onClickName}
|
||||||
{parentAddress && <ParentAddress address={parentAddress}/>}
|
override={overrideName}
|
||||||
</div>
|
toggle={toggle}
|
||||||
<AccountName
|
value={value}
|
||||||
className={`accountName ${withSidebar ? 'withSidebar' : ''}`}
|
withSidebar={withSidebar}
|
||||||
defaultName={defaultName}
|
>
|
||||||
onClick={onClickName}
|
{children}
|
||||||
override={overrideName}
|
</AccountName>
|
||||||
toggle={toggle}
|
|
||||||
value={value}
|
|
||||||
withSidebar={withSidebar}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</AccountName>
|
|
||||||
<div className='additionalInfo shortAddress'
|
|
||||||
data-testid='short-address'>
|
|
||||||
{withShortAddress && toShortAddress(value)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default React.memo(styled(AddressSmall)`
|
export default React.memo(styled(AddressSmall)`
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.ui--IdentityIcon {
|
.ui--IdentityIcon {
|
||||||
margin-right: 0.75rem;
|
margin-right: 0.75rem;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.additionalInfo {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-self: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.parentAccountName {
|
|
||||||
grid-area: parentAccountName;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accountName {
|
|
||||||
grid-area: accountName;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shortAddress {
|
|
||||||
grid-area: shortAddress;
|
|
||||||
color: #8B8B8B;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.addressContainer {
|
|
||||||
border: 0.031rem;
|
|
||||||
height: 3.438rem;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 100%;
|
|
||||||
grid-template-rows: 33% 33% 33%;
|
|
||||||
grid-template-areas:
|
|
||||||
"parentAccountName"
|
|
||||||
"accountName"
|
|
||||||
"shortAddress";
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui--AccountName {
|
.ui--AccountName {
|
||||||
max-width: 26rem;
|
max-width: 26rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
// Copyright 2017-2021 @polkadot/page-accounts authors & contributors
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import AccountName from '@polkadot/react-components/AccountName';
|
|
||||||
import { Icon } from '@polkadot/react-components/index';
|
|
||||||
|
|
||||||
interface ParentAccountProps {
|
|
||||||
address: string,
|
|
||||||
className?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
function ParentAddress ({ address, className }: ParentAccountProps): React.ReactElement<ParentAccountProps> {
|
|
||||||
return (
|
|
||||||
<div className={className}
|
|
||||||
data-testid='parent'>
|
|
||||||
<Icon className='parent-icon'
|
|
||||||
icon='code-branch'/>
|
|
||||||
|
|
||||||
<AccountName
|
|
||||||
value={address}
|
|
||||||
withSidebar
|
|
||||||
>
|
|
||||||
</AccountName>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default React.memo(styled(ParentAddress)`
|
|
||||||
align-items: center;
|
|
||||||
color: #8B8B8B;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
& .parent-icon {
|
|
||||||
font-size: 0.625rem;
|
|
||||||
margin-right: 0.3rem;
|
|
||||||
margin-left: 0.15rem;
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
@ -16,7 +16,6 @@ import Tooltip from './Tooltip';
|
|||||||
import { useTranslation } from './translate';
|
import { useTranslation } from './translate';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
iconPosition: 'left' | 'right';
|
|
||||||
className?: string;
|
className?: string;
|
||||||
stakingInfo?: DeriveStakingAccount;
|
stakingInfo?: DeriveStakingAccount;
|
||||||
}
|
}
|
||||||
@ -42,7 +41,7 @@ function extractTotals (stakingInfo?: DeriveStakingAccount, progress?: DeriveSes
|
|||||||
return [mapped, total];
|
return [mapped, total];
|
||||||
}
|
}
|
||||||
|
|
||||||
function StakingUnbonding ({ className = '', iconPosition = 'left', stakingInfo }: Props): React.ReactElement<Props> | null {
|
function StakingUnbonding ({ className = '', stakingInfo }: Props): React.ReactElement<Props> | null {
|
||||||
const { api } = useApi();
|
const { api } = useApi();
|
||||||
const progress = useCall<DeriveSessionProgress>(api.derive.session.progress);
|
const progress = useCall<DeriveSessionProgress>(api.derive.session.progress);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -60,13 +59,10 @@ function StakingUnbonding ({ className = '', iconPosition = 'left', stakingInfo
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
{iconPosition === 'left' && (
|
<Icon
|
||||||
<Icon
|
icon='clock'
|
||||||
className='left'
|
tooltip={trigger}
|
||||||
icon='clock'
|
/>
|
||||||
tooltip={trigger}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<FormatBalance value={total} />
|
<FormatBalance value={total} />
|
||||||
<Tooltip
|
<Tooltip
|
||||||
text={mapped.map(([{ value }, eras, blocks], index): React.ReactNode => (
|
text={mapped.map(([{ value }, eras, blocks], index): React.ReactNode => (
|
||||||
@ -90,13 +86,6 @@ function StakingUnbonding ({ className = '', iconPosition = 'left', stakingInfo
|
|||||||
))}
|
))}
|
||||||
trigger={trigger}
|
trigger={trigger}
|
||||||
/>
|
/>
|
||||||
{iconPosition === 'right' && (
|
|
||||||
<Icon
|
|
||||||
className='right'
|
|
||||||
icon='clock'
|
|
||||||
tooltip={trigger}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -104,16 +93,11 @@ function StakingUnbonding ({ className = '', iconPosition = 'left', stakingInfo
|
|||||||
export default React.memo(styled(StakingUnbonding)`
|
export default React.memo(styled(StakingUnbonding)`
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
.ui--Icon.left {
|
.ui--Icon {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui--Icon.right {
|
|
||||||
margin-left: 0.25rem;
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui--FormatBalance {
|
.ui--FormatBalance {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,11 +124,6 @@ export default React.memo(styled(Head)`
|
|||||||
&.start {
|
&.start {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.balances {
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 15.571rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tr {
|
tr {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import React, { useMemo } from 'react';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import { useApi } from '@polkadot/react-hooks';
|
import { useApi } from '@polkadot/react-hooks';
|
||||||
import { formatBalance, isString } from '@polkadot/util';
|
import { formatBalance } from '@polkadot/util';
|
||||||
|
|
||||||
import { useTranslation } from './translate';
|
import { useTranslation } from './translate';
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ interface Props {
|
|||||||
formatIndex?: number;
|
formatIndex?: number;
|
||||||
isShort?: boolean;
|
isShort?: boolean;
|
||||||
label?: React.ReactNode;
|
label?: React.ReactNode;
|
||||||
labelPost?: LabelPost;
|
labelPost?: string;
|
||||||
value?: Compact<any> | BN | string | null | 'all';
|
value?: Compact<any> | BN | string | null | 'all';
|
||||||
valueFormatted?: string;
|
valueFormatted?: string;
|
||||||
withCurrency?: boolean;
|
withCurrency?: boolean;
|
||||||
@ -31,8 +31,6 @@ interface Props {
|
|||||||
const M_LENGTH = 6 + 1;
|
const M_LENGTH = 6 + 1;
|
||||||
const K_LENGTH = 3 + 1;
|
const K_LENGTH = 3 + 1;
|
||||||
|
|
||||||
type LabelPost = string | React.ReactNode
|
|
||||||
|
|
||||||
function getFormat (registry: Registry, formatIndex = 0): [number, string] {
|
function getFormat (registry: Registry, formatIndex = 0): [number, string] {
|
||||||
const decimals = registry.chainDecimals;
|
const decimals = registry.chainDecimals;
|
||||||
const tokens = registry.chainTokens;
|
const tokens = registry.chainTokens;
|
||||||
@ -47,18 +45,18 @@ function getFormat (registry: Registry, formatIndex = 0): [number, string] {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElement (prefix: string, postfix: string, unit: string, label: LabelPost = '', isShort = false): React.ReactNode {
|
function createElement (prefix: string, postfix: string, unit: string, label = '', isShort = false): React.ReactNode {
|
||||||
return <>{`${prefix}${isShort ? '' : '.'}`}{!isShort && <span className='ui--FormatBalance-postfix'>{`0000${postfix || ''}`.slice(-4)}</span>}<span className='ui--FormatBalance-unit'> {unit}</span>{label}</>;
|
return <>{`${prefix}${isShort ? '' : '.'}`}{!isShort && <span className='ui--FormatBalance-postfix'>{`0000${postfix || ''}`.slice(-4)}</span>}<span className='ui--FormatBalance-unit'> {unit}</span>{label}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function splitFormat (value: string, label?: LabelPost, isShort?: boolean): React.ReactNode {
|
function splitFormat (value: string, label?: string, isShort?: boolean): React.ReactNode {
|
||||||
const [prefix, postfixFull] = value.split('.');
|
const [prefix, postfixFull] = value.split('.');
|
||||||
const [postfix, unit] = postfixFull.split(' ');
|
const [postfix, unit] = postfixFull.split(' ');
|
||||||
|
|
||||||
return createElement(prefix, postfix, unit, label, isShort);
|
return createElement(prefix, postfix, unit, label, isShort);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyFormat (value: Compact<any> | BN | string, [decimals, token]: [number, string], withCurrency = true, withSi?: boolean, _isShort?: boolean, labelPost?: LabelPost): React.ReactNode {
|
function applyFormat (value: Compact<any> | BN | string, [decimals, token]: [number, string], withCurrency = true, withSi?: boolean, _isShort?: boolean, labelPost?: string): React.ReactNode {
|
||||||
const [prefix, postfix] = formatBalance(value, { decimals, forceUnit: '-', withSi: false }).split('.');
|
const [prefix, postfix] = formatBalance(value, { decimals, forceUnit: '-', withSi: false }).split('.');
|
||||||
const isShort = _isShort || (withSi && prefix.length >= K_LENGTH);
|
const isShort = _isShort || (withSi && prefix.length >= K_LENGTH);
|
||||||
const unitPost = withCurrency ? token : '';
|
const unitPost = withCurrency ? token : '';
|
||||||
@ -86,17 +84,17 @@ function FormatBalance ({ children, className = '', format, formatIndex, isShort
|
|||||||
// labelPost here looks messy, however we ensure we have one less text node
|
// labelPost here looks messy, however we ensure we have one less text node
|
||||||
return (
|
return (
|
||||||
<div className={`ui--FormatBalance ${className}`}>
|
<div className={`ui--FormatBalance ${className}`}>
|
||||||
{label ? <>{label} </> : ''}<span className='ui--FormatBalance-value'
|
{label ? <>{label} </> : ''}
|
||||||
data-testid='balance-summary' >{
|
<span
|
||||||
|
className='ui--FormatBalance-value'
|
||||||
|
data-testid='balance-summary'>{
|
||||||
valueFormatted
|
valueFormatted
|
||||||
? splitFormat(valueFormatted, labelPost, isShort)
|
? splitFormat(valueFormatted, labelPost, isShort)
|
||||||
: value
|
: value
|
||||||
? value === 'all'
|
? value === 'all'
|
||||||
? t<string>('everything{{labelPost}}', { replace: { labelPost } })
|
? t<string>('everything{{labelPost}}', { replace: { labelPost } })
|
||||||
: applyFormat(value, formatInfo, withCurrency, withSi, isShort, labelPost)
|
: applyFormat(value, formatInfo, withCurrency, withSi, isShort, labelPost)
|
||||||
: isString(labelPost)
|
: `-${labelPost || ''}`
|
||||||
? `-${labelPost}`
|
|
||||||
: labelPost
|
|
||||||
}</span>{children}
|
}</span>{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user