mirror of
https://github.com/status-im/MyCrypto.git
synced 2025-02-23 08:18:17 +00:00
Improve form validation (#1772)
* Change gas price validation to be string input based * Change sanitization to use Nunber * Have validators use Number over parseFloat * Fix css validation class * Add valid css to address field * Add data field validation * Remove unused import * Fix button being hidden on inputs * Dead code removal * Unify textarea and input class validation * Adjust validity styling to only apply after a value has been inputted * Do not pass custom props to DOM
This commit is contained in:
parent
32416469e4
commit
8d27d0ba4d
@ -21,7 +21,7 @@ export const AddressField: React.SFC<Props> = ({ isReadOnly, isSelfAddress, isCh
|
||||
{translate(isSelfAddress ? 'X_ADDRESS' : 'SEND_ADDR')}
|
||||
</div>
|
||||
<Input
|
||||
className={`input-group-input ${isValid ? '' : 'invalid'}`}
|
||||
isValid={isValid}
|
||||
type="text"
|
||||
value={isCheckSummed ? toChecksumAddress(currentTo.raw) : currentTo.raw}
|
||||
placeholder={donationAddressMap.ETH}
|
||||
|
@ -23,9 +23,7 @@ export const AmountField: React.SFC<Props> = ({
|
||||
<label className="AmountField-group input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('SEND_AMOUNT_SHORT')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
isAmountValid(raw, customValidator, isValid) ? '' : 'invalid'
|
||||
}`}
|
||||
isValid={isAmountValid(raw, customValidator, isValid)}
|
||||
type="number"
|
||||
placeholder="1"
|
||||
value={raw}
|
||||
|
@ -66,9 +66,8 @@ export default class AddCustomTokenForm extends React.PureComponent<Props, State
|
||||
<label className="AddCustom-field form-group" key={field.name}>
|
||||
<div className="input-group-header">{field.label}</div>
|
||||
<Input
|
||||
className={`${
|
||||
errors[field.name] ? 'invalid' : field.value ? 'valid' : ''
|
||||
} input-group-input-small`}
|
||||
isValid={!errors[field.name]}
|
||||
className="input-group-input-small"
|
||||
type="text"
|
||||
name={field.name}
|
||||
value={field.value}
|
||||
|
@ -20,7 +20,12 @@ class DetailsClass extends Component<StateProps> {
|
||||
<div className="tx-modal-details">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">Network</div>
|
||||
<Input readOnly={true} value={`${network} network - provided by ${service}`} />
|
||||
<Input
|
||||
isValid={true}
|
||||
showValidAsPlain={true}
|
||||
readOnly={true}
|
||||
value={`${network} network - provided by ${service}`}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<SerializedTransaction
|
||||
|
@ -117,7 +117,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||
<label className="col-sm-9 input-group flex-grow-1">
|
||||
<div className="input-group-header">{translate('CUSTOM_NODE_NAME')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${this.state.name && invalids.name ? 'invalid' : ''}`}
|
||||
isValid={!(this.state.name && invalids.name)}
|
||||
type="text"
|
||||
placeholder="My Node"
|
||||
value={this.state.name}
|
||||
@ -142,9 +142,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||
<label className="col-sm-6 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_NAME')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.customNetworkId && invalids.customNetworkId ? 'invalid' : ''
|
||||
}`}
|
||||
isValid={!(this.state.customNetworkId && invalids.customNetworkId)}
|
||||
type="text"
|
||||
placeholder="My Custom Network"
|
||||
value={this.state.customNetworkId}
|
||||
@ -154,9 +152,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||
<label className="col-sm-3 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_CURRENCY')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.customNetworkUnit && invalids.customNetworkUnit ? 'invalid' : ''
|
||||
}`}
|
||||
isValid={!(this.state.customNetworkUnit && invalids.customNetworkUnit)}
|
||||
type="text"
|
||||
placeholder="ETH"
|
||||
value={this.state.customNetworkUnit}
|
||||
@ -166,11 +162,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||
<label className="col-sm-3 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_CHAIN_ID')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.customNetworkChainId && invalids.customNetworkChainId
|
||||
? 'invalid'
|
||||
: ''
|
||||
}`}
|
||||
isValid={!(this.state.customNetworkChainId && invalids.customNetworkChainId)}
|
||||
type="text"
|
||||
placeholder="1"
|
||||
value={this.state.customNetworkChainId}
|
||||
@ -183,7 +175,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||
<label className="input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_URL')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${this.state.url && invalids.url ? 'invalid' : ''}`}
|
||||
isValid={!(this.state.url && invalids.url)}
|
||||
type="text"
|
||||
placeholder="https://127.0.0.1:8545/"
|
||||
value={this.state.url}
|
||||
@ -207,9 +199,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||
<label className="col-sm-6 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('INPUT_USERNAME_LABEL')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.username && invalids.username ? 'invalid' : ''
|
||||
}`}
|
||||
isValid={!(this.state.username && invalids.username)}
|
||||
type="text"
|
||||
value={this.state.username}
|
||||
onChange={e => this.setState({ username: e.currentTarget.value })}
|
||||
@ -218,9 +208,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
||||
<label className="col-sm-6 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('INPUT_PASSWORD_LABEL')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.password && invalids.password ? 'invalid' : ''
|
||||
}`}
|
||||
isValid={!(this.state.password && invalids.password)}
|
||||
type="password"
|
||||
value={this.state.password}
|
||||
onChange={e => this.setState({ password: e.currentTarget.value })}
|
||||
|
@ -6,12 +6,12 @@ import { Input } from 'components/ui';
|
||||
|
||||
export const DataField: React.SFC<{}> = () => (
|
||||
<DataFieldFactory
|
||||
withProps={({ data: { raw }, dataExists, onChange, readOnly }) => (
|
||||
withProps={({ data: { raw }, validData, onChange, readOnly }) => (
|
||||
<div className="input-group-wrapper">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('OFFLINE_STEP2_LABEL_6')}</div>
|
||||
<Input
|
||||
className={dataExists ? 'is-valid' : 'is-invalid'}
|
||||
isValid={validData}
|
||||
type="text"
|
||||
placeholder={donationAddressMap.ETH}
|
||||
value={raw}
|
||||
|
@ -7,7 +7,7 @@ import { isEtherTransaction } from 'selectors/transaction';
|
||||
import { AppState } from 'reducers';
|
||||
export interface CallBackProps {
|
||||
data: AppState['transaction']['fields']['data'];
|
||||
dataExists: boolean;
|
||||
validData: boolean;
|
||||
readOnly: boolean;
|
||||
onChange(ev: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>): void;
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Query } from 'components/renderCbs';
|
||||
import { getData, getDataExists } from 'selectors/transaction';
|
||||
import { getData } from 'selectors/transaction';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { CallBackProps } from 'components/DataFieldFactory';
|
||||
import { isHexString } from 'ethereumjs-util';
|
||||
|
||||
interface OwnProps {
|
||||
withProps(props: CallBackProps): React.ReactElement<any> | null;
|
||||
@ -11,19 +12,19 @@ interface OwnProps {
|
||||
}
|
||||
interface StateProps {
|
||||
data: AppState['transaction']['fields']['data'];
|
||||
dataExists: boolean;
|
||||
validData: boolean;
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateProps;
|
||||
|
||||
class DataInputClass extends Component<Props> {
|
||||
public render() {
|
||||
const { data, onChange, dataExists } = this.props;
|
||||
const { data, onChange, validData } = this.props;
|
||||
return (
|
||||
<Query
|
||||
params={['readOnly']}
|
||||
withQuery={({ readOnly }) =>
|
||||
this.props.withProps({ data, onChange, readOnly: !!readOnly, dataExists })
|
||||
this.props.withProps({ data, onChange, readOnly: !!readOnly, validData })
|
||||
}
|
||||
/>
|
||||
);
|
||||
@ -32,5 +33,5 @@ class DataInputClass extends Component<Props> {
|
||||
|
||||
export const DataInput = connect((state: AppState) => ({
|
||||
data: getData(state),
|
||||
dataExists: getDataExists(state)
|
||||
validData: getData(state).raw === '' || isHexString(getData(state).raw)
|
||||
}))(DataInputClass);
|
||||
|
@ -30,7 +30,7 @@ export const GasLimitField: React.SFC<Props> = ({
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
className={gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}
|
||||
isValid={gasLimitValidator(raw)}
|
||||
type="number"
|
||||
placeholder="21000"
|
||||
readOnly={!!readOnly}
|
||||
|
@ -34,7 +34,7 @@
|
||||
&-refresh {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 1rem;
|
||||
bottom: 0rem;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,8 @@ class NonceField extends React.Component<Props> {
|
||||
/>
|
||||
</div>
|
||||
<Input
|
||||
className={`Nonce-field-input ${!!value ? 'is-valid' : 'is-invalid'}`}
|
||||
isValid={!!value}
|
||||
className="Nonce-field-input"
|
||||
type="number"
|
||||
placeholder="7"
|
||||
value={raw}
|
||||
|
@ -9,7 +9,6 @@ import { NonceField, GasLimitField, DataField } from 'components';
|
||||
import { connect } from 'react-redux';
|
||||
import { getAutoGasLimitEnabled } from 'selectors/config';
|
||||
import { isValidGasPrice } from 'selectors/transaction';
|
||||
import { sanitizeNumericalInput } from 'libs/values';
|
||||
import { Input } from 'components/ui';
|
||||
import { EAC_SCHEDULING_CONFIG } from 'libs/scheduling';
|
||||
import { getScheduleGasPrice, getTimeBounty } from 'selectors/schedule';
|
||||
@ -83,9 +82,11 @@ class AdvancedGas extends React.Component<Props, State> {
|
||||
<div className="input-group-header">
|
||||
{translateRaw('OFFLINE_STEP2_LABEL_3')} (gwei)
|
||||
</div>
|
||||
{/*We leave type as string instead of number, because things such as multiple decimals
|
||||
or invalid exponent notation does not fire the onchange handler
|
||||
so the component will not display as invalid for such things */}
|
||||
<Input
|
||||
className={!!gasPrice.raw && !validGasPrice ? 'invalid' : ''}
|
||||
type="number"
|
||||
isValid={validGasPrice}
|
||||
placeholder="40"
|
||||
value={gasPrice.raw}
|
||||
onChange={this.handleGasPriceChange}
|
||||
@ -173,7 +174,7 @@ class AdvancedGas extends React.Component<Props, State> {
|
||||
|
||||
private handleGasPriceChange = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
const { value } = ev.currentTarget;
|
||||
this.props.inputGasPrice(sanitizeNumericalInput(value));
|
||||
this.props.inputGasPrice(value);
|
||||
};
|
||||
|
||||
private handleToggleAutoGasLimit = (_: React.FormEvent<HTMLInputElement>) => {
|
||||
|
@ -54,6 +54,9 @@ class FeeSummary extends React.Component<Props> {
|
||||
scheduleGasLimit
|
||||
} = this.props;
|
||||
|
||||
if (!gasPrice.value || gasPrice.value.eqn(0) || !gasLimit.value || gasLimit.value.eqn(0)) {
|
||||
return null;
|
||||
}
|
||||
if (isGasEstimating) {
|
||||
return (
|
||||
<div className="FeeSummary is-loading">
|
||||
|
@ -69,7 +69,8 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
|
||||
<div className={`TogglablePassword input-group input-group-inline`}>
|
||||
{isTextareaWhenVisible && isVisible ? (
|
||||
<TextArea
|
||||
className={`${className} ${!isValid ? 'invalid' : ''}`}
|
||||
isValid={!!isValid}
|
||||
className={className}
|
||||
value={value}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
@ -84,11 +85,12 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
isValid={!!isValid}
|
||||
value={value}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
type={isVisible ? 'text' : 'password'}
|
||||
className={`${className} ${!isValid ? 'invalid' : ''} border-rad-right-0`}
|
||||
className={`${className} border-rad-right-0`}
|
||||
placeholder={placeholder}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
|
@ -132,7 +132,7 @@ class DeterministicWalletsModalClass extends React.PureComponent<Props, State> {
|
||||
<React.Fragment>
|
||||
<div className="DWModal-path-custom">
|
||||
<Input
|
||||
className={customPath ? (isValidPath(customPath) ? 'valid' : 'invalid') : ''}
|
||||
isValid={customPath ? isValidPath(customPath) : true}
|
||||
value={customPath}
|
||||
placeholder="m/44'/60'/0'/0"
|
||||
onChange={this.handleChangeCustomPath}
|
||||
|
@ -65,9 +65,8 @@ export class KeystoreDecrypt extends PureComponent {
|
||||
|
||||
{isWalletPending ? <Spinner /> : ''}
|
||||
<Input
|
||||
className={`${password.length > 0 ? 'is-valid' : 'is-invalid'} ${
|
||||
file.length && isWalletPending ? 'hidden' : ''
|
||||
}`}
|
||||
isValid={password.length > 0}
|
||||
className={`${file.length && isWalletPending ? 'hidden' : ''}`}
|
||||
disabled={!file}
|
||||
value={password}
|
||||
onChange={this.onPasswordChange}
|
||||
|
@ -67,6 +67,8 @@ class MnemonicDecryptClass extends PureComponent<Props, State> {
|
||||
<div className="form-group">
|
||||
<p>{translate('ADD_LABEL_8')}</p>
|
||||
<Input
|
||||
isValid={true}
|
||||
showValidAsPlain={true}
|
||||
value={pass}
|
||||
onChange={this.onPasswordChange}
|
||||
placeholder={translateRaw('INPUT_PASSWORD_LABEL')}
|
||||
|
@ -74,7 +74,7 @@ export class PrivateKeyDecrypt extends PureComponent<Props> {
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('ADD_LABEL_3')}</div>
|
||||
<Input
|
||||
className={`form-control ${password.length > 0 ? 'is-valid' : 'is-invalid'}`}
|
||||
isValid={password.length > 0}
|
||||
value={password}
|
||||
onChange={this.onPasswordChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
|
@ -60,7 +60,8 @@ class ViewOnlyDecryptClass extends PureComponent<Props, State> {
|
||||
)}
|
||||
|
||||
<Input
|
||||
className={`ViewOnly-input ${isValid ? 'is-valid' : 'is-invalid'}`}
|
||||
isValid={isValid}
|
||||
className="ViewOnly-input"
|
||||
value={address}
|
||||
onChange={this.changeAddress}
|
||||
placeholder={translateRaw('VIEW_ONLY_ENTER')}
|
||||
|
@ -1,25 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
interface RequiredProps {
|
||||
condition: boolean;
|
||||
conditionalProps: {
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional
|
||||
*/
|
||||
export const withConditional = <WrappedComponentProps extends {}>(
|
||||
PassedComponent: React.ComponentType<WrappedComponentProps>
|
||||
) =>
|
||||
class extends Component<WrappedComponentProps & RequiredProps, {}> {
|
||||
public render() {
|
||||
const { condition, conditionalProps, ...passedProps } = this.props as any;
|
||||
return condition ? (
|
||||
<PassedComponent {...{ ...passedProps, ...(conditionalProps as object) }} />
|
||||
) : (
|
||||
<PassedComponent {...passedProps} />
|
||||
);
|
||||
}
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from './Conditional';
|
@ -1,6 +0,0 @@
|
||||
import React from 'react';
|
||||
import { withConditional } from 'components/hocs';
|
||||
import { Input } from 'components/ui';
|
||||
|
||||
const inpt: React.SFC<React.InputHTMLAttributes<any>> = props => <Input {...props} />;
|
||||
export const ConditionalInput = withConditional(inpt);
|
@ -1,33 +1,65 @@
|
||||
import React, { HTMLProps } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import './Input.scss';
|
||||
|
||||
interface State {
|
||||
hasBlurred: boolean;
|
||||
/**
|
||||
* @description when the input has not had any values inputted yet
|
||||
* e.g. "Pristine" condition
|
||||
*/
|
||||
isStateless: boolean;
|
||||
}
|
||||
|
||||
class Input extends React.Component<HTMLProps<HTMLInputElement>, State> {
|
||||
interface OwnProps extends HTMLProps<HTMLInputElement> {
|
||||
isValid: boolean;
|
||||
showValidAsPlain?: boolean;
|
||||
}
|
||||
|
||||
class Input extends React.Component<OwnProps, State> {
|
||||
public state: State = {
|
||||
hasBlurred: false
|
||||
hasBlurred: false,
|
||||
isStateless: true
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { showValidAsPlain, isValid, ...htmlProps } = this.props;
|
||||
const hasValue = !!this.props.value && this.props.value.toString().length > 0;
|
||||
const classname = classnames(
|
||||
this.props.className,
|
||||
'input-group-input',
|
||||
'form-control',
|
||||
this.state.isStateless
|
||||
? ''
|
||||
: isValid ? (showValidAsPlain ? '' : `is-valid valid`) : `is-invalid invalid`,
|
||||
this.state.hasBlurred && 'has-blurred',
|
||||
hasValue && 'has-value'
|
||||
);
|
||||
|
||||
return (
|
||||
<input
|
||||
{...this.props}
|
||||
{...htmlProps}
|
||||
onBlur={e => {
|
||||
this.setState({ hasBlurred: true });
|
||||
if (this.props && this.props.onBlur) {
|
||||
this.props.onBlur(e);
|
||||
}
|
||||
}}
|
||||
onChange={this.handleOnChange}
|
||||
onWheel={this.props.type === 'number' ? this.preventNumberScroll : undefined}
|
||||
className={`input-group-input ${this.props.className} ${
|
||||
this.state.hasBlurred ? 'has-blurred' : ''
|
||||
} ${!!this.props.value && this.props.value.toString().length > 0 ? 'has-value' : ''}`}
|
||||
className={classname}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private handleOnChange = (args: React.FormEvent<HTMLInputElement>) => {
|
||||
if (this.state.isStateless) {
|
||||
this.setState({ isStateless: false });
|
||||
}
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(args);
|
||||
}
|
||||
};
|
||||
// When number inputs are scrolled on while in focus, the number changes. So we blur
|
||||
// it if it's focused to prevent that behavior, without preventing the scroll.
|
||||
private preventNumberScroll(ev: React.WheelEvent<HTMLInputElement>) {
|
||||
|
@ -11,7 +11,7 @@ export default class SimpleSelect extends PureComponent<Props, {}> {
|
||||
return (
|
||||
<select
|
||||
value={this.props.value || this.props.options[0]}
|
||||
className={'form-control'}
|
||||
className="form-control"
|
||||
onChange={this.props.onChange}
|
||||
>
|
||||
{this.props.options.map((obj, i) => {
|
||||
|
@ -1,30 +1,62 @@
|
||||
import React, { HTMLProps } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import './Input.scss';
|
||||
|
||||
interface State {
|
||||
hasBlurred: boolean;
|
||||
/**
|
||||
* @description when the input has not had any values inputted yet
|
||||
* e.g. "Pristine" condition
|
||||
*/
|
||||
isStateless: boolean;
|
||||
}
|
||||
|
||||
class TextArea extends React.Component<HTMLProps<HTMLTextAreaElement>, State> {
|
||||
interface OwnProps extends HTMLProps<HTMLTextAreaElement> {
|
||||
isValid: boolean;
|
||||
showValidAsPlain?: boolean;
|
||||
}
|
||||
|
||||
class TextArea extends React.Component<OwnProps, State> {
|
||||
public state: State = {
|
||||
hasBlurred: false
|
||||
hasBlurred: false,
|
||||
isStateless: true
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { showValidAsPlain, isValid, ...htmlProps } = this.props;
|
||||
const classname = classnames(
|
||||
this.props.className,
|
||||
'input-group-input',
|
||||
'form-control',
|
||||
this.state.isStateless
|
||||
? ''
|
||||
: isValid ? (showValidAsPlain ? '' : `is-valid valid`) : `is-invalid invalid`,
|
||||
this.state.hasBlurred && 'has-blurred'
|
||||
);
|
||||
|
||||
return (
|
||||
<textarea
|
||||
{...this.props}
|
||||
{...htmlProps}
|
||||
onBlur={e => {
|
||||
this.setState({ hasBlurred: true });
|
||||
if (this.props && this.props.onBlur) {
|
||||
this.props.onBlur(e);
|
||||
}
|
||||
}}
|
||||
className={`input-group-input ${this.props.className} ${
|
||||
this.state.hasBlurred ? 'has-blurred' : ''
|
||||
}`}
|
||||
onChange={this.handleOnChange}
|
||||
className={classname}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private handleOnChange = (args: React.FormEvent<HTMLTextAreaElement>) => {
|
||||
if (this.state.isStateless) {
|
||||
this.setState({ isStateless: false });
|
||||
}
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default TextArea;
|
||||
|
@ -17,6 +17,5 @@ export { default as TextArea } from './TextArea';
|
||||
export { default as Address } from './Address';
|
||||
export { default as CodeBlock } from './CodeBlock';
|
||||
export { default as Toggle } from './Toggle';
|
||||
export * from './ConditionalInput';
|
||||
export * from './Expandable';
|
||||
export * from './InlineSpinner';
|
||||
|
@ -65,7 +65,7 @@ class BroadcastTx extends Component<Props> {
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="0xf86b0284ee6b2800825208944bbeeb066ed09b7aed07bf39eee0460dfa26152088016345785d8a00008029a03ba7a0cc6d1756cd771f2119cf688b6d4dc9d37096089f0331fe0de0d1cc1254a02f7bcd19854c8d46f8de09e457aec25b127ab4328e1c0d24bfbff8702ee1f474"
|
||||
className={stateTransaction ? '' : 'invalid'}
|
||||
isValid={!!stateTransaction}
|
||||
value={userInput}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
@ -42,7 +42,7 @@ class TxHashInput extends React.Component<Props, State> {
|
||||
public render() {
|
||||
const { recentTxs } = this.props;
|
||||
const { hash } = this.state;
|
||||
const validClass = hash ? (isValidTxHash(hash) ? 'is-valid' : 'is-invalid') : '';
|
||||
|
||||
let selectOptions: Option[] = [];
|
||||
|
||||
if (recentTxs && recentTxs.length) {
|
||||
@ -75,8 +75,9 @@ class TxHashInput extends React.Component<Props, State> {
|
||||
|
||||
<Input
|
||||
value={hash}
|
||||
isValid={hash ? isValidTxHash(hash) : true}
|
||||
placeholder="0x16e521..."
|
||||
className={`TxHashInput-field ${validClass}`}
|
||||
className="TxHashInput-field"
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import translate from 'translations';
|
||||
import classnames from 'classnames';
|
||||
import { DataFieldFactory } from 'components/DataFieldFactory';
|
||||
import { SendButtonFactory } from 'components/SendButtonFactory';
|
||||
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
||||
@ -31,16 +30,15 @@ class DeployClass extends Component<DispatchProps> {
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('CONTRACT_BYTECODE')}</div>
|
||||
<DataFieldFactory
|
||||
withProps={({ data: { raw, value }, onChange, readOnly }) => (
|
||||
withProps={({ data: { raw }, onChange, readOnly, validData }) => (
|
||||
<TextArea
|
||||
isValid={validData && !!raw}
|
||||
name="byteCode"
|
||||
placeholder="0x8f87a973e..."
|
||||
rows={6}
|
||||
onChange={onChange}
|
||||
disabled={readOnly}
|
||||
className={classnames('Deploy-field-input', {
|
||||
'is-valid': value && value.length > 0
|
||||
})}
|
||||
className="Deploy-field-input"
|
||||
value={raw}
|
||||
/>
|
||||
)}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AmountFieldFactory } from 'components/AmountFieldFactory';
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { Input } from 'components/ui';
|
||||
|
||||
export const AmountField: React.SFC = () => (
|
||||
@ -12,11 +12,10 @@ export const AmountField: React.SFC = () => (
|
||||
<Input
|
||||
name="value"
|
||||
value={raw}
|
||||
isValid={isValid || raw === ''}
|
||||
onChange={onChange}
|
||||
readOnly={readOnly}
|
||||
className={classnames('InteractExplorer-field-input', 'form-control', {
|
||||
'is-invalid': !(isValid || raw === '')
|
||||
})}
|
||||
className="InteractExplorer-field-input"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -116,6 +116,7 @@ class InteractExplorerClass extends Component<Props, State> {
|
||||
<div className="input-group-header">{name + ' ' + type}</div>
|
||||
<Input
|
||||
className="InteractExplorer-func-in-input"
|
||||
isValid={!!(inputs[name] && inputs[name].rawData)}
|
||||
name={name}
|
||||
value={(inputs[name] && inputs[name].rawData) || ''}
|
||||
onChange={this.handleInputChange}
|
||||
@ -138,7 +139,8 @@ class InteractExplorerClass extends Component<Props, State> {
|
||||
<label className="input-group">
|
||||
<div className="input-group-header"> ↳ {name + ' ' + type}</div>
|
||||
<Input
|
||||
className="InteractExplorer-func-out-input "
|
||||
className="InteractExplorer-func-out-input"
|
||||
isValid={!!decodedFieldValue}
|
||||
value={decodedFieldValue}
|
||||
disabled={true}
|
||||
/>
|
||||
|
@ -4,7 +4,6 @@ import { getNetworkContracts } from 'selectors/config';
|
||||
import { connect } from 'react-redux';
|
||||
import { AppState } from 'reducers';
|
||||
import { isValidETHAddress, isValidAbiJson } from 'libs/validators';
|
||||
import classnames from 'classnames';
|
||||
import { NetworkContract } from 'types/network';
|
||||
import { donationAddressMap } from 'config';
|
||||
import { Input, TextArea, CodeBlock, Dropdown } from 'components/ui';
|
||||
@ -126,9 +125,8 @@ class InteractForm extends Component<Props, State> {
|
||||
name="contract_address"
|
||||
autoComplete="off"
|
||||
value={currentTo.raw}
|
||||
className={classnames('InteractForm-address-field-input', {
|
||||
invalid: !isValid
|
||||
})}
|
||||
isValid={isValid}
|
||||
className="InteractForm-address-field-input"
|
||||
spellCheck={false}
|
||||
onChange={onChange}
|
||||
/>
|
||||
@ -144,7 +142,8 @@ class InteractForm extends Component<Props, State> {
|
||||
contract.name === 'Custom' ? (
|
||||
<TextArea
|
||||
placeholder={this.abiJsonPlaceholder}
|
||||
className={`InteractForm-interface-field-input ${validAbiJson ? '' : 'invalid'}`}
|
||||
isValid={!!validAbiJson}
|
||||
className="InteractForm-interface-field-input"
|
||||
onChange={this.handleInput('abiJson')}
|
||||
value={abiJson}
|
||||
rows={6}
|
||||
@ -155,7 +154,8 @@ class InteractForm extends Component<Props, State> {
|
||||
) : (
|
||||
<TextArea
|
||||
placeholder={this.abiJsonPlaceholder}
|
||||
className={`InteractForm-interface-field-input ${validAbiJson ? '' : 'invalid'}`}
|
||||
isValid={!!validAbiJson}
|
||||
className="InteractForm-interface-field-input"
|
||||
onChange={this.handleInput('abiJson')}
|
||||
value={abiJson}
|
||||
rows={6}
|
||||
|
@ -37,9 +37,8 @@ class NameInput extends Component<Props, State> {
|
||||
<label className="input-group input-group-inline ENSInput-name">
|
||||
<Input
|
||||
value={domainToCheck}
|
||||
className={`${
|
||||
!domainToCheck ? '' : isValidDomain ? '' : 'invalid'
|
||||
} border-rad-right-0`}
|
||||
isValid={!!domainToCheck && isValidDomain}
|
||||
className="border-rad-right-0"
|
||||
type="text"
|
||||
placeholder="mycrypto"
|
||||
onChange={this.onChange}
|
||||
|
@ -21,6 +21,8 @@ const PaperWallet: React.SFC<Props> = props => (
|
||||
<h1 className="GenPaper-title">{translate('GEN_LABEL_5')}</h1>
|
||||
<Input
|
||||
value={stripHexPrefix(props.privateKey)}
|
||||
showValidAsPlain={true}
|
||||
isValid={true}
|
||||
aria-label={translateRaw('X_PRIVKEY')}
|
||||
aria-describedby="x_PrivKeyDesc"
|
||||
type="text"
|
||||
|
@ -61,7 +61,13 @@ export default class MnemonicWord extends React.Component<Props, State> {
|
||||
{word}
|
||||
</button>
|
||||
) : (
|
||||
<Input className="MnemonicWord-word-input" value={word} readOnly={true} />
|
||||
<Input
|
||||
className="MnemonicWord-word-input"
|
||||
value={word}
|
||||
readOnly={true}
|
||||
showValidAsPlain={true}
|
||||
isValid={true}
|
||||
/>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
|
@ -38,7 +38,7 @@ class ScheduleDepositFieldClass extends Component<Props> {
|
||||
</span>
|
||||
</div>
|
||||
<Input
|
||||
className={!!scheduleDeposit.raw && !validScheduleDeposit ? 'invalid' : ''}
|
||||
isValid={scheduleDeposit.raw && validScheduleDeposit}
|
||||
type="number"
|
||||
placeholder="0.00001"
|
||||
value={scheduleDeposit.raw}
|
||||
|
@ -34,7 +34,7 @@ class ScheduleGasLimitFieldClass extends React.Component<Props> {
|
||||
<InlineSpinner active={gasEstimationPending} text="Calculating" />
|
||||
</div>
|
||||
<Input
|
||||
className={!!scheduleGasLimit.raw && !validScheduleGasLimit ? 'invalid' : ''}
|
||||
isValid={scheduleGasLimit.raw && validScheduleGasLimit}
|
||||
type="number"
|
||||
placeholder={EAC_SCHEDULING_CONFIG.SCHEDULE_GAS_LIMIT_FALLBACK.toString()}
|
||||
value={scheduleGasLimit.raw}
|
||||
|
@ -27,7 +27,7 @@ class ScheduleGasPriceFieldClass extends React.Component<Props> {
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translateRaw('SCHEDULE_GAS_PRICE')} (gwei)</div>
|
||||
<Input
|
||||
className={!!scheduleGasPrice.raw && !validScheduleGasPrice ? 'invalid' : ''}
|
||||
isValid={scheduleGasPrice.raw && validScheduleGasPrice}
|
||||
type="number"
|
||||
placeholder="40"
|
||||
value={scheduleGasPrice.raw}
|
||||
|
@ -25,7 +25,7 @@ export const TimeBountyField: React.SFC<Props> = ({ isReadOnly }) => (
|
||||
</span>
|
||||
</div>
|
||||
<Input
|
||||
className={`input-group-input ${isValid ? '' : 'invalid'}`}
|
||||
isValid={isValid}
|
||||
type="text"
|
||||
value={currentTimeBounty.raw}
|
||||
placeholder={translateRaw('SCHEDULE_TIMEBOUNTY_PLACEHOLDER')}
|
||||
|
@ -32,7 +32,7 @@ export const WindowSizeField: React.SFC<Props> = ({ isReadOnly }) => (
|
||||
</span>
|
||||
</div>
|
||||
<Input
|
||||
className={`input-group-input ${isValid ? '' : 'invalid'}`}
|
||||
isValid={isValid}
|
||||
type="text"
|
||||
value={currentWindowSize.raw}
|
||||
placeholder={
|
||||
|
@ -14,7 +14,7 @@ export const WindowStartField: React.SFC<Props> = ({ isReadOnly }) => (
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('SCHEDULE_BLOCK')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${isValid ? '' : 'invalid'}`}
|
||||
isValid={isValid}
|
||||
type="text"
|
||||
value={currentWindowStart.raw}
|
||||
placeholder={translateRaw('SCHEDULE_BLOCK_PLACEHOLDER')}
|
||||
|
@ -57,7 +57,8 @@ export class SignMessage extends Component<Props, State> {
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('MSG_MESSAGE')}</div>
|
||||
<TextArea
|
||||
className={`SignMessage-inputBox ${message ? 'is-valid' : 'is-invalid'}`}
|
||||
isValid={!!message}
|
||||
className="SignMessage-inputBox"
|
||||
placeholder={messagePlaceholder}
|
||||
value={message}
|
||||
onChange={this.handleMessageChange}
|
||||
|
@ -41,7 +41,8 @@ export class VerifyMessage extends Component<Props, State> {
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('MSG_SIGNATURE')}</div>
|
||||
<TextArea
|
||||
className={`VerifyMessage-inputBox ${signature ? 'is-valid' : 'is-invalid'}`}
|
||||
isValid={!!signature}
|
||||
className="VerifyMessage-inputBox"
|
||||
placeholder={signaturePlaceholder}
|
||||
value={signature}
|
||||
onChange={this.handleSignatureChange}
|
||||
|
@ -316,12 +316,7 @@ export default class CurrencySwap extends PureComponent<Props, State> {
|
||||
<div className="input-group input-group-inline">
|
||||
<Input
|
||||
id="origin-swap-input"
|
||||
className={`input-group-input ${
|
||||
!!origin.amount &&
|
||||
this.isMinMaxValid(origin.amount, origin.label, destination.label)
|
||||
? ''
|
||||
: 'invalid'
|
||||
}`}
|
||||
isValid={this.isMinMaxValid(origin.amount, origin.label, destination.label)}
|
||||
type="number"
|
||||
placeholder={translateRaw('SEND_AMOUNT_SHORT')}
|
||||
value={isNaN(origin.amount) ? '' : origin.amount}
|
||||
@ -341,12 +336,7 @@ export default class CurrencySwap extends PureComponent<Props, State> {
|
||||
<div className="input-group-header">{translate('SWAP_RECEIVE_INPUT_LABEL')}</div>
|
||||
<Input
|
||||
id="destination-swap-input"
|
||||
className={`${
|
||||
!!destination.amount &&
|
||||
this.isMinMaxValid(origin.amount, origin.label, destination.label)
|
||||
? ''
|
||||
: 'invalid'
|
||||
}`}
|
||||
isValid={this.isMinMaxValid(origin.amount, origin.label, destination.label)}
|
||||
type="number"
|
||||
placeholder={translateRaw('SEND_AMOUNT_SHORT')}
|
||||
value={isNaN(destination.amount) ? '' : destination.amount}
|
||||
|
@ -37,7 +37,12 @@ class FieldsClass extends Component<Props> {
|
||||
<div className="col-xs-12">
|
||||
<AddressFieldFactory
|
||||
withProps={({ currentTo }) => (
|
||||
<Input type="text" value={currentTo.raw} readOnly={true} />
|
||||
<Input
|
||||
type="text"
|
||||
value={currentTo.raw}
|
||||
readOnly={true}
|
||||
isValid={!!currentTo.raw}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
@ -60,6 +65,8 @@ class FieldsClass extends Component<Props> {
|
||||
)}
|
||||
{isValid && (
|
||||
<Input
|
||||
isValid={true}
|
||||
showValidAsPlain={true}
|
||||
type="text"
|
||||
value={`${currentValue.raw} ${this.props.unit}`}
|
||||
readOnly={true}
|
||||
|
@ -21,6 +21,7 @@ export default class PaymentInfo extends PureComponent<Props, {}> {
|
||||
})}
|
||||
<Input
|
||||
className="SwapPayment-address"
|
||||
isValid={!!this.props.paymentAddress}
|
||||
value={this.props.paymentAddress || undefined}
|
||||
disabled={true}
|
||||
/>
|
||||
|
@ -79,7 +79,8 @@ export default class ReceivingAddress extends PureComponent<StateProps & ActionP
|
||||
</h4>
|
||||
|
||||
<Input
|
||||
className={`SwapAddress-address-input ${!validAddress ? 'invalid' : ''}`}
|
||||
isValid={validAddress}
|
||||
className="SwapAddress-address-input"
|
||||
type="text"
|
||||
value={destinationAddress}
|
||||
onChange={this.onChangeDestinationAddress}
|
||||
|
@ -83,7 +83,13 @@ Rate: ${rates[pair].rate} ${origin.label}/${destination.label}`;
|
||||
<small>{translate('SWAP_SUPPORT_LINK_BROKEN')}</small>
|
||||
</p>
|
||||
{open ? (
|
||||
<TextArea defaultValue={fallbackBody} className="form-control input-sm" rows={9} />
|
||||
<TextArea
|
||||
isValid={true}
|
||||
showValidAsPlain={true}
|
||||
defaultValue={fallbackBody}
|
||||
className="form-control input-sm"
|
||||
rows={9}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</section>
|
||||
|
@ -140,10 +140,10 @@ export function isValidPath(dPath: string) {
|
||||
}
|
||||
|
||||
export const isValidValue = (value: string) =>
|
||||
!!(value && isFinite(parseFloat(value)) && parseFloat(value) >= 0);
|
||||
!!(value && isFinite(Number(value)) && Number(value) >= 0);
|
||||
|
||||
export const gasLimitValidator = (gasLimit: number | string) => {
|
||||
const gasLimitFloat = typeof gasLimit === 'string' ? parseFloat(gasLimit) : gasLimit;
|
||||
const gasLimitFloat = typeof gasLimit === 'string' ? Number(gasLimit) : gasLimit;
|
||||
return (
|
||||
validNumber(gasLimitFloat) &&
|
||||
gasLimitFloat >= GAS_LIMIT_LOWER_BOUND &&
|
||||
@ -152,7 +152,7 @@ export const gasLimitValidator = (gasLimit: number | string) => {
|
||||
};
|
||||
|
||||
export const gasPriceValidator = (gasPrice: number | string): boolean => {
|
||||
const gasPriceFloat = typeof gasPrice === 'string' ? parseFloat(gasPrice) : gasPrice;
|
||||
const gasPriceFloat = typeof gasPrice === 'string' ? Number(gasPrice) : gasPrice;
|
||||
return (
|
||||
validNumber(gasPriceFloat) &&
|
||||
gasPriceFloat >= GAS_PRICE_GWEI_LOWER_BOUND &&
|
||||
@ -172,7 +172,7 @@ export const timeBountyValidator = (timeBounty: BN | number | string | null): bo
|
||||
);
|
||||
}
|
||||
|
||||
const timeBountyFloat = typeof timeBounty === 'string' ? parseFloat(timeBounty) : timeBounty;
|
||||
const timeBountyFloat = typeof timeBounty === 'string' ? Number(timeBounty) : timeBounty;
|
||||
|
||||
return validNumber(timeBountyFloat);
|
||||
};
|
||||
|
@ -44,7 +44,7 @@ export const buildEIP681TokenRequest = (
|
||||
}`;
|
||||
|
||||
export const sanitizeNumericalInput = (input: string): string => {
|
||||
const inputFloat = parseFloat(input);
|
||||
const inputFloat = Number(input);
|
||||
|
||||
if (!input || isNaN(inputFloat)) {
|
||||
return input;
|
||||
|
@ -31,12 +31,12 @@ export function* handleGasLimitInput({ payload }: InputGasLimitAction): SagaIter
|
||||
}
|
||||
|
||||
export function* handleGasPriceInput({ payload }: InputGasPriceAction): SagaIterator {
|
||||
const priceFloat = parseFloat(payload);
|
||||
const validGasPrice: boolean = yield call(gasPriceValidator, priceFloat);
|
||||
const gasPrice = Number(payload);
|
||||
const validGasPrice: boolean = yield call(gasPriceValidator, gasPrice);
|
||||
yield put(
|
||||
setGasPriceField({
|
||||
raw: payload,
|
||||
value: validGasPrice ? gasPriceToBase(priceFloat) : Wei('0')
|
||||
value: validGasPrice ? gasPriceToBase(gasPrice) : Wei('0')
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
z-index: 999;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user