AddressInput component & Donation address (#157)

* Add donation address constant. Add AddressInput component. Replace using those 2 where needed.

* Fix invalid defaultValue logic
This commit is contained in:
William O'Beirne 2018-10-22 03:51:29 -04:00 committed by Daniel Ternyak
parent abc824b17e
commit 2895d1045a
9 changed files with 219 additions and 13 deletions

View File

@ -0,0 +1,35 @@
.AddressInput {
&-input {
&-identicon {
position: relative;
border-radius: 100%;
left: -2px;
width: 22px;
height: 22px;
.ant-input-affix-wrapper-lg & {
height: 28px;
width: 28px;
}
.ant-input-affix-wrapper-sm & {
left: -4px;
width: 16px;
height: 16px;
}
}
// Ant overrides
&.ant-input-affix-wrapper .ant-input:not(:first-child) {
padding-left: 38px;
}
&.ant-input-affix-wrapper-lg .ant-input:not(:first-child) {
padding-left: 44px;
}
&.ant-input-affix-wrapper-sm .ant-input:not(:first-child) {
padding-left: 28px;
}
}
}

View File

@ -0,0 +1,58 @@
import React from 'react';
import classnames from 'classnames';
import { Form, Input } from 'antd';
import { InputProps } from 'antd/lib/input';
import { FormItemProps } from 'antd/lib/form';
import { isValidEthAddress } from 'utils/validators';
import Identicon from 'components/Identicon';
import { DONATION } from 'utils/constants';
import './AddressInput.less';
export interface Props {
value: string | undefined;
className?: string;
showIdenticon?: boolean;
inputProps?: InputProps;
formItemProps?: FormItemProps;
onChange(ev: React.ChangeEvent<HTMLInputElement>): void;
}
export default class AddressInput extends React.Component<Props> {
render() {
const { value, onChange, className, showIdenticon } = this.props;
const passedFormItemProps = this.props.formItemProps || {};
const passedInputProps = this.props.inputProps || {};
const isInvalid = value && !isValidEthAddress(value);
const formItemProps = {
validateStatus: (isInvalid
? 'error'
: undefined) as FormItemProps['validateStatus'],
help: isInvalid ? 'Address is invalid' : undefined,
...passedFormItemProps,
className: classnames('AddressInput', className, passedFormItemProps.className),
};
const inputProps = {
placeholder: DONATION.ETH,
prefix: value &&
showIdenticon && (
<Identicon className="AddressInput-input-identicon" address={value} />
),
...passedInputProps,
value,
onChange,
className: classnames(
'AddressInput-input',
className && `${className}-input`,
passedInputProps.className,
),
};
return (
<Form.Item {...formItemProps}>
<Input {...inputProps} />
</Form.Item>
);
}
}

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Form, Input, Button } from 'antd';
import { Form, Button } from 'antd';
import { isValidEthAddress } from 'utils/validators';
import AddressInput from 'components/AddressInput';
import './Address.less';
interface Props {
@ -20,14 +21,12 @@ export default class AddressProvider extends React.Component<Props, State> {
const { address } = this.state;
return (
<Form className="AddressProvider" onSubmit={this.handleSubmit}>
<Form.Item className="AddressProvider-address">
<Input
size="large"
value={address}
onChange={this.handleChange}
placeholder="0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520"
/>
</Form.Item>
<AddressInput
className="AddressProvider-address"
value={address}
onChange={this.handleChange}
inputProps={{ size: 'large' }}
/>
<Button
type="primary"

View File

@ -4,6 +4,7 @@ import { RadioChangeEvent } from 'antd/lib/radio';
import { CreateFormState } from 'types';
import { getCreateErrors } from 'modules/create/utils';
import { ONE_DAY } from 'utils/time';
import { DONATION } from 'utils/constants';
interface State {
payOutAddress: string;
@ -43,7 +44,7 @@ export default class CreateFlowTeam extends React.Component<Props, State> {
<Input
size="large"
name="payOutAddress"
placeholder="0xe12a34230e5e7fc73d094e52025135e4fbf24653"
placeholder={DONATION.ETH}
type="text"
value={payOutAddress}
onChange={this.handleInputChange}
@ -192,7 +193,7 @@ const TrusteeFields = ({
<div style={{ display: 'flex' }}>
<Input
size="large"
placeholder="0xe12a34230e5e7fc73d094e52025135e4fbf24653"
placeholder={DONATION.ETH}
type="text"
value={value}
onChange={ev => onChange(index, ev.currentTarget.value)}

View File

@ -1,6 +1,24 @@
import { PROPOSAL_CATEGORY } from 'api/constants';
import { SOCIAL_TYPE, CreateFormState } from 'types';
function generateRandomAddress() {
return (
'0x' +
Math.random()
.toString(16)
.substring(2, 12) +
Math.random()
.toString(16)
.substring(2, 12) +
Math.random()
.toString(16)
.substring(2, 12) +
Math.random()
.toString(16)
.substring(2, 12)
);
}
const createExampleProposal = (
payOutAddress: string,
trustees: string[],
@ -29,7 +47,7 @@ const createExampleProposal = (
avatarUrl: `https://randomuser.me/api/portraits/women/${Math.floor(
Math.random() * 80,
)}.jpg`,
ethAddress: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
ethAddress: generateRandomAddress(),
emailAddress: 'designer@tshirt.com',
socialAccounts: {
[SOCIAL_TYPE.KEYBASE]: 'willo',

View File

@ -0,0 +1,3 @@
export const DONATION = {
ETH: '0x8B0B72F8bDE212991135668922fD5acE557DE6aB',
};

View File

@ -0,0 +1,91 @@
import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { DONATION } from 'utils/constants';
import 'components/AddressInput.less';
import AddressInput, { Props as AddressInputProps } from 'components/AddressInput';
const cases: ExampleProps[] = [
{
disp: 'Default Input',
props: {},
defaultValue: '',
},
{
disp: 'Input w/ Identicon',
props: {
showIdenticon: true,
},
},
{
disp: 'Input w/ Identicon (large)',
props: {
showIdenticon: true,
inputProps: {
size: 'large',
},
},
},
{
disp: 'Input w/ Identicon (small)',
props: {
showIdenticon: true,
inputProps: {
size: 'small',
},
},
},
{
disp: 'Invalid Input',
props: {},
defaultValue: '0x0what',
},
];
storiesOf('AddressInput', module).add('all', () => (
<div style={{ padding: '2em' }}>
{cases.map(c => (
<Example key={c.disp} {...c} />
))}
</div>
));
interface ExampleProps {
disp: string;
defaultValue?: string;
props: Partial<AddressInputProps>;
}
interface ExampleState {
value: string;
}
class Example extends React.Component<ExampleProps, ExampleState> {
constructor(props: ExampleProps) {
super(props);
this.state = {
value: props.defaultValue !== undefined ? props.defaultValue : DONATION.ETH,
};
}
render() {
const props = {
...this.props.props,
value: this.state.value,
onChange: this.handleChange,
};
return (
<div style={{ marginBottom: '2rem', width: '280px' }}>
<div style={{ fontSize: '0.9em', paddingBottom: '0.5rem' }}>
{this.props.disp}
</div>
<AddressInput {...props} />
</div>
);
}
private handleChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ value: ev.currentTarget.value });
};
}

View File

@ -1,6 +1,7 @@
import * as React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { storiesOf } from '@storybook/react';
import { DONATION } from 'utils/constants';
import 'components/UserRow/style.less';
import UserRow from 'components/UserRow';
@ -9,7 +10,7 @@ const user = {
name: 'Dana Hayes',
title: 'QA Engineer',
avatarUrl: 'https://randomuser.me/api/portraits/women/19.jpg',
ethAddress: '0x4bbeEB066eD09B7AEd07bF39EEe0460DFa261520',
ethAddress: DONATION.ETH,
emailAddress: 'test@test.test',
socialAccounts: {},
};