Warning & Error with exclamation triangle (#4331)

* Warning & Error with exclamation triangle

* Fix lint

* Apply suggestions from code review
This commit is contained in:
Jaco Greeff 2021-01-10 08:37:51 +01:00 committed by GitHub
parent f525d51312
commit d8b2bab912
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 122 additions and 79 deletions

View File

@ -13,7 +13,7 @@ import styled from 'styled-components';
import { DEV_PHRASE } from '@polkadot/keyring/defaults';
import { getEnvironment } from '@polkadot/react-api/util';
import { AddressRow, Button, Checkbox, CopyButton, Dropdown, Expander, Input, InputAddress, Modal, TextArea } from '@polkadot/react-components';
import { AddressRow, Button, Checkbox, CopyButton, Dropdown, Expander, Input, InputAddress, MarkError, MarkWarning, Modal, TextArea } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { keyring } from '@polkadot/ui-keyring';
import { settings } from '@polkadot/ui-settings';
@ -374,10 +374,10 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
value={derivePath}
/>
{deriveValidation?.error && (
<article className='error'>{errorIndex[deriveValidation.error] || deriveValidation.error}</article>
<MarkError content={errorIndex[deriveValidation.error] || deriveValidation.error} />
)}
{deriveValidation?.warning && (
<article className='warning'>{errorIndex[deriveValidation.warning]}</article>
<MarkWarning content={errorIndex[deriveValidation.warning]} />
)}
</Modal.Column>
<Modal.Column>

View File

@ -7,7 +7,7 @@ import type { KeypairType } from '@polkadot/util-crypto/types';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import { AddressRow, Button, Input, InputAddress, Modal, Password, StatusContext } from '@polkadot/react-components';
import { AddressRow, Button, Input, InputAddress, MarkError, Modal, Password, StatusContext } from '@polkadot/react-components';
import { useApi, useDebounce, useToggle } from '@polkadot/react-hooks';
import { keyring } from '@polkadot/ui-keyring';
import { keyExtractPath } from '@polkadot/util-crypto';
@ -228,7 +228,7 @@ function Derive ({ className = '', from, onClose }: Props): React.ReactElement {
placeholder={t<string>('//hard/soft')}
/>
{deriveError && (
<article className='error'>{deriveError}</article>
<MarkError content={deriveError} />
)}
<Input
className='full'

View File

@ -3,6 +3,8 @@
import React from 'react';
import { MarkWarning } from '@polkadot/react-components';
import { useTranslation } from '../translate';
const isElectron = navigator.userAgent.toLowerCase().indexOf(' electron/') > -1;
@ -15,9 +17,7 @@ function ExternalWarning (): React.ReactElement | null {
}
return (
<article className='warning'>
<p>{t<string>('Consider storing your account in a signer such as a browser extension, hardware device, QR-capable phone wallet (non-connected) or desktop application for optimal account security.')}&nbsp;{t<string>('Future versions of the web-only interface will drop support for non-external accounts, much like the IPFS version.')}</p>
</article>
<MarkWarning content={<>{t<string>('Consider storing your account in a signer such as a browser extension, hardware device, QR-capable phone wallet (non-connected) or desktop application for optimal account security.')}&nbsp;{t<string>('Future versions of the web-only interface will drop support for non-external accounts, much like the IPFS version.')}</>} />
);
}

View File

@ -7,7 +7,7 @@ import type { ModalProps } from '../types';
import React, { useCallback, useMemo, useState } from 'react';
import { AddressRow, Button, InputAddress, InputFile, Modal, Password } from '@polkadot/react-components';
import { AddressRow, Button, InputAddress, InputFile, MarkWarning, Modal, Password } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { keyring } from '@polkadot/ui-keyring';
import { u8aToString } from '@polkadot/util';
@ -121,6 +121,9 @@ function Import ({ className = '', onClose, onStatusChange }: Props): React.Reac
onChange={_onChangeFile}
withLabel
/>
{differentGenesis && (
<MarkWarning content={t<string>('The network from which this account was originally generated is different than the network you are currently connected to. Once imported ensure you toggle the "allow on any network" option for the account to keep it visible on the current network.')} />
)}
</Modal.Column>
<Modal.Column>
<p>{t<string>('Supply a backed-up JSON file, encrypted with your account-specific password.')}</p>
@ -143,11 +146,6 @@ function Import ({ className = '', onClose, onStatusChange }: Props): React.Reac
<p>{t<string>('The password previously used to encrypt this account.')}</p>
</Modal.Column>
</Modal.Columns>
{ differentGenesis &&
<article className='warning'>
<p>{t<string>('The network from which this account was originally generated is different than the network you are currently connected to. Once imported ensure you toggle the "allow on any network" option for the account to keep it visible on the current network.')}</p>
</article>
}
<ExternalWarning />
</Modal.Content>
<Modal.Actions onCancel={onClose}>

View File

@ -7,6 +7,7 @@ import type { AmountValidateState } from '../Accounts/types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import { MarkError, MarkWarning } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { BN_ZERO } from '@polkadot/util';
@ -43,12 +44,10 @@ function ValidateAmount ({ amount, delegatingAccount, onError }: Props): React.R
}
}, [api, onError, amount, t, delegatingAccountBalance]);
if (error || warning) {
return (
<article className={error ? 'error' : 'warning'}>
<div>{error || warning}</div>
</article>
);
if (error) {
return <MarkError content={error} />;
} else if (warning) {
return <MarkWarning content={warning} />;
}
return null;

View File

@ -4,7 +4,7 @@
import React, { useCallback, useRef, useState } from 'react';
import { getLedger } from '@polkadot/react-api';
import { Button, Dropdown, Modal } from '@polkadot/react-components';
import { Button, Dropdown, MarkError, Modal } from '@polkadot/react-components';
import { keyring } from '@polkadot/ui-keyring';
import { useTranslation } from '../translate';
@ -98,7 +98,7 @@ function Ledger ({ className, onClose }: Props): React.ReactElement<Props> {
value={addIndex}
/>
{error && (
<article className='error'>{error.message}</article>
<MarkError content={error.message} />
)}
</Modal.Column>
<Modal.Column>

View File

@ -9,7 +9,7 @@ import BN from 'bn.js';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { BatchWarning, Button, Dropdown, InputAddress, Modal, TxButton } from '@polkadot/react-components';
import { BatchWarning, Button, Dropdown, InputAddress, MarkError, Modal, TxButton } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { BN_ZERO, isFunction } from '@polkadot/util';
@ -136,9 +136,7 @@ function NewProxy ({ index, onChangeAccount, onChangeType, onRemove, proxiedAcco
value={accountId}
/>
{accountId.eq(proxiedAccount) && (
<article className='error'>
{t<string>('You should not setup proxies to act as a self-proxy.')}
</article>
<MarkError content={t<string>('You should not setup proxies to act as a self-proxy.')} />
)}
<Dropdown
help={'Type of proxy'}

View File

@ -8,7 +8,7 @@ import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { InputAddress, InputBalance, Modal, Toggle, TxButton } from '@polkadot/react-components';
import { InputAddress, InputBalance, MarkWarning, Modal, Toggle, TxButton } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { Available } from '@polkadot/react-query';
import { BN_ZERO, isFunction } from '@polkadot/util';
@ -166,7 +166,7 @@ function Transfer ({ className = '', onClose, recipientId: propRecipientId, send
/>
)}
{!isProtected && accountInfo && accountInfo.refcount && !accountInfo.refcount.isZero() && (
<article className='warning'>{t<string>('There is an existing reference count on the sender account. As such the account cannot be reaped from the state.')}</article>
<MarkWarning content={t<string>('There is an existing reference count on the sender account. As such the account cannot be reaped from the state.')} />
)}
</Modal.Column>
<Modal.Column>

View File

@ -7,7 +7,7 @@ import BN from 'bn.js';
import React, { useCallback, useEffect, useState } from 'react';
import { DeriveBalancesAll } from '@polkadot/api-derive/types';
import { Button, Input, InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components';
import { Button, Input, InputAddress, InputBalance, MarkError, Modal, TxButton } from '@polkadot/react-components';
import { useApi, useCall, useToggle } from '@polkadot/react-hooks';
import { BN_ZERO } from '@polkadot/util';
@ -86,9 +86,7 @@ function BountyCreate () {
value={title}
/>
{!isTitleValid && (title !== TITLE_DEFAULT_VALUE) && (
<article className='error'>
{t<string>('Title too long')}
</article>
<MarkError content={t<string>('Title too long')} />
)}
</Modal.Column>
<Modal.Column>
@ -106,9 +104,7 @@ function BountyCreate () {
value={value}
/>
{!isValueValid && !value?.eq(BOUNTY_DEFAULT_VALUE) && (
<article className='error'>
{t<string>('Allocation value is smaller than the minimum bounty value.')}
</article>
<MarkError content={t<string>('Allocation value is smaller than the minimum bounty value.')} />
)}
</Modal.Column>
<Modal.Column>
@ -139,9 +135,7 @@ function BountyCreate () {
withLabel
/>
{!hasFunds && (
<article className='error'>
{t<string>('Account does not have enough funds.')}
</article>
<MarkError content={t<string>('Account does not have enough funds.')} />
)}
</Modal.Column>
<Modal.Column>

View File

@ -5,7 +5,7 @@ import type { AccountId, Hash, Proposal, ProposalIndex } from '@polkadot/types/i
import React, { useState } from 'react';
import { Button, Modal, ProposedAction, TxButton, VoteAccount } from '@polkadot/react-components';
import { Button, MarkWarning, Modal, ProposedAction, TxButton, VoteAccount } from '@polkadot/react-components';
import { useAccounts, useApi, useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
@ -57,16 +57,14 @@ function Voting ({ hash, idNumber, isDisabled, members, prime, proposal }: Props
filter={members}
onChange={setAccountId}
/>
{isPrime && (
<MarkWarning content={t<string>('You are voting with this collective\'s prime account. The vote will be the default outcome in case of any abstentions.')} />
)}
</Modal.Column>
<Modal.Column>
<p>{t<string>('The council account for this vote. The selection is filtered by the current members.')}</p>
</Modal.Column>
</Modal.Columns>
{isPrime && (
<article className='warning'>
<div>{t<string>('You are voting with this collective\'s prime account. The vote will be the default outcome in case of any abstentions.')}</div>
</article>
)}
</Modal.Content>
<Modal.Actions onCancel={toggleVoting}>
<TxButton

View File

@ -5,7 +5,7 @@ import type { SubmittableExtrinsic } from '@polkadot/api/types';
import React, { useCallback, useState } from 'react';
import { Button, Extrinsic, InputAddress, TxButton } from '@polkadot/react-components';
import { Button, Extrinsic, InputAddress, MarkError, TxButton } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { BalanceFree } from '@polkadot/react-query';
@ -48,7 +48,7 @@ function Selection (): React.ReactElement {
onError={_onExtrinsicError}
/>
{error && (
<article className='error'>{error}</article>
<MarkError content={error} />
)}
<Button.Group>
<TxButton

View File

@ -5,7 +5,7 @@ import type { KeypairType } from '@polkadot/util-crypto/types';
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { Button, Dropdown, Input, Modal, StatusContext } from '@polkadot/react-components';
import { Button, Dropdown, Input, MarkWarning, Modal, StatusContext } from '@polkadot/react-components';
import { keyring } from '@polkadot/ui-keyring';
import { assert, u8aToHex } from '@polkadot/util';
import { keyExtractSuri, mnemonicValidate } from '@polkadot/util-crypto';
@ -93,9 +93,7 @@ function InjectKeys ({ onClose }: Props): React.ReactElement<Props> | null {
onChange={setSuri}
value={suri}
/>
<article className='warning'>
<div>{t<string>('This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.')}</div>
</article>
<MarkWarning content={t<string>('This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.')} />
</Modal.Column>
<Modal.Column>
<p>{t<string>('The seed and derivation path will be submitted to the validator node. this is an advanced operation, only to be performed when you are sure of the security and connection risks.')}</p>

View File

@ -7,6 +7,7 @@ import type { AmountValidateState } from '../types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import { MarkError, MarkWarning } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { BN_TEN, BN_THOUSAND, BN_ZERO, formatBalance } from '@polkadot/util';
@ -69,12 +70,10 @@ function ValidateAmount ({ currentAmount, onError, stashId, value }: Props): Rea
}
}, [api, currentAmount, onError, stashBalance, t, value]);
if (error || warning) {
return (
<article className={error ? 'error' : 'warning'}>
<div>{error || warning}</div>
</article>
);
if (error) {
return <MarkError content={error} />;
} else if (warning) {
return <MarkWarning content={warning} />;
}
return null;

View File

@ -7,6 +7,7 @@ import type { AccountId, StakingLedger } from '@polkadot/types/interfaces';
import React, { useEffect, useState } from 'react';
import { MarkError, MarkWarning } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { useTranslation } from '../../translate';
@ -75,9 +76,9 @@ function ValidateController ({ accountId, controllerId, defaultController, onErr
}
return (
<article className={isFatal ? 'error' : 'warning'}>
<div>{error}</div>
</article>
isFatal
? <MarkError content={error} />
: <MarkWarning content={error} />
);
}

View File

@ -5,6 +5,8 @@ import type { I18nProps } from '@polkadot/react-components/types';
import React, { useEffect, useState } from 'react';
import { MarkWarning } from '@polkadot/react-components';
import { useTranslation } from '../../translate';
interface Props extends I18nProps {
@ -34,9 +36,7 @@ function ValidateSessionEd25519 ({ onError, sessionId, stashId }: Props): React.
}
return (
<article className='warning'>
<div>{error}</div>
</article>
<MarkWarning content={error} />
);
}

View File

@ -4,6 +4,8 @@
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import { MarkWarning } from '@polkadot/react-components';
import { useTranslation } from '../../translate';
interface Props {
@ -35,9 +37,7 @@ function InputValidationUnstakeThreshold ({ onError, unstakeThreshold }: Props):
}
return (
<article className='warning'>
<div>{error}</div>
</article>
<MarkWarning content={error} />
);
}

View File

@ -7,7 +7,7 @@ import type { NominateInfo } from './types';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { InputAddress, InputAddressMulti, Modal } from '@polkadot/react-components';
import { InputAddress, InputAddressMulti, MarkWarning, Modal } from '@polkadot/react-components';
import { useApi, useFavorites } from '@polkadot/react-hooks';
import { MAX_NOMINATIONS, STORE_FAVS_BASE } from '../../constants';
@ -80,7 +80,7 @@ function Nominate ({ className = '', controllerId, nominating, onChange, stashId
onChange={setSelected}
valueLabel={t<string>('nominated accounts')}
/>
<article className='warning'>{t<string>('You should trust your nominations to act competently and honest; basing your decision purely on their current profitability could lead to reduced profits or even loss of funds.')}</article>
<MarkWarning content={t<string>('You should trust your nominations to act competently and honest; basing your decision purely on their current profitability could lead to reduced profits or even loss of funds.')} />
</Modal.Column>
<Modal.Column>
<p>{t<string>('Nominators can be selected manually from the list of all currently available validators.')}</p>

View File

@ -6,7 +6,7 @@ import type { AccountId, Hash } from '@polkadot/types/interfaces';
import BN from 'bn.js';
import React, { useState } from 'react';
import { Button, Modal, TxButton, VoteAccount } from '@polkadot/react-components';
import { Button, MarkWarning, Modal, TxButton, VoteAccount } from '@polkadot/react-components';
import { useAccounts, useApi, useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
@ -42,9 +42,7 @@ function Voting ({ hash, members, prime, proposalId }: Props): React.ReactElemen
onChange={setAccountId}
/>
{(accountId === prime?.toString()) && (
<article className='warning'>
<div>{t<string>('You are voting with this collective\'s prime account. The vote will be the default outcome in case of any abstentions.')}</div>
</article>
<MarkWarning content={t<string>('You are voting with this collective\'s prime account. The vote will be the default outcome in case of any abstentions.')} />
)}
</Modal.Content>
<Modal.Actions onCancel={toggleVoting}>

View File

@ -4,7 +4,7 @@
import BN from 'bn.js';
import React, { useMemo, useState } from 'react';
import { Button, InputAddress, InputBalance, Modal, Static, TxButton } from '@polkadot/react-components';
import { Button, InputAddress, InputBalance, MarkWarning, Modal, Static, TxButton } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import { useTranslation } from '../translate';
@ -83,15 +83,13 @@ function Propose ({ className }: Props): React.ReactElement<Props> | null {
isDisabled
label={t<string>('minimum bond')}
/>
<MarkWarning content={t<string>('Be aware that once submitted the proposal will be put to a council vote. If the proposal is rejected due to a lack of info, invalid requirements or non-benefit to the network as a whole, the full bond posted (as describe above) will be lost.')} />
</Modal.Column>
<Modal.Column>
<p>{t<string>('The value is the amount that is being asked for and that will be allocated to the beneficiary if the proposal is approved.')}</p>
<p>{t<string>('Of the beneficiary amount, at least {{bondPercentage}} would need to be put up as collateral. The maximum of this and the minimum bond will be used to secure the proposal, refundable if it passes.', { replace: { bondPercentage } })}</p>
</Modal.Column>
</Modal.Columns>
<article className='warning'>
<p>{t<string>('Be aware that once submitted the proposal will be put to a council vote. If the proposal is rejected due to a lack of info, invalid requirements or non-benefit to the network as a whole, the full bond posted (as describe above) will be lost.')}</p>
</article>
</Modal.Content>
<Modal.Actions onCancel={toggleOpen}>
<TxButton

View File

@ -3,6 +3,7 @@
import React from 'react';
import { MarkWarning } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { isFunction } from '@polkadot/util';
@ -17,9 +18,7 @@ function BatchWarning (): React.ReactElement | null {
}
return (
<article className='warning'>
<p>{t<string>('This chain does not yet support atomic batch operations. This means that if the transaction gets executed and one of the operations do fail (due to invalid data or lack of available funds) some of the changes made may not be applied.')}</p>
</article>
<MarkWarning content={t<string>('This chain does not yet support atomic batch operations. This means that if the transaction gets executed and one of the operations do fail (due to invalid data or lack of available funds) some of the changes made may not be applied.')} />
);
}

View File

@ -14,7 +14,7 @@ interface Props {
function Card ({ children, className = '', isError, isSuccess, withBottomMargin }: Props): React.ReactElement<Props> {
return (
<article className={`ui--Card${className}${(isError && !isSuccess) ? ' error' : ''}${(!isError && isSuccess) ? ' success' : ''}${withBottomMargin ? ' withBottomMargin' : ''}`}>
<article className={`ui--Card ${className}${(isError && !isSuccess) ? ' error' : ''}${(!isError && isSuccess) ? ' success' : ''}${withBottomMargin ? ' withBottomMargin' : ''}`}>
{children}
</article>
);

View File

@ -0,0 +1,28 @@
// Copyright 2017-2021 @polkadot/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import styled from 'styled-components';
import Icon from './Icon';
interface Props {
children?: React.ReactNode;
className?: string;
content?: React.ReactNode;
}
function MarkError ({ children, className = '', content }: Props): React.ReactElement<Props> {
return (
<article className={`mark error ${className}`}>
<Icon icon='exclamation-triangle' />{content}{children}
</article>
);
}
export default React.memo(styled(MarkError)`
.ui--Icon {
color: rgba(255, 12, 12, 1);
margin-right: 0.5rem;
}
`);

View File

@ -0,0 +1,28 @@
// Copyright 2017-2021 @polkadot/react-components authors & contributors
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import styled from 'styled-components';
import Icon from './Icon';
interface Props {
children?: React.ReactNode;
className?: string;
content?: React.ReactNode;
}
function MarkWarning ({ children, className = '', content }: Props): React.ReactElement<Props> {
return (
<article className={`mark warning ${className}`}>
<Icon icon='exclamation-triangle' />{content}{children}
</article>
);
}
export default React.memo(styled(MarkWarning)`
.ui--Icon {
color: rgba(255, 196, 12, 1);
margin-right: 0.5rem;
}
`);

View File

@ -64,6 +64,8 @@ export { default as LabelHelp } from './LabelHelp';
export { default as Labelled } from './Labelled';
export { default as LinkExternal } from './LinkExternal';
export { default as LockedVote } from './LockedVote';
export { default as MarkError } from './MarkError';
export { default as MarkWarning } from './MarkWarning';
export { default as Menu } from './Menu';
export { default as Modal } from './Modal';
export { default as Nonce } from './Nonce';

View File

@ -259,6 +259,11 @@ export default createGlobalStyle<Props & ThemeProps>(({ theme, uiHighlight }: Pr
}
}
&.mark {
margin: 0.5rem 0 0.5rem 2.25rem;
padding: 0.5rem 1rem;
}
&.extraMargin {
margin: 2rem auto;
}