diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js
index 72e80f432..ac7f2da51 100644
--- a/js/src/ui/Form/AddressSelect/addressSelect.js
+++ b/js/src/ui/Form/AddressSelect/addressSelect.js
@@ -16,6 +16,7 @@
import React, { Component, PropTypes } from 'react';
import { MenuItem } from 'material-ui';
+import { isEqual } from 'lodash';
import AutoComplete from '../AutoComplete';
import IdentityIcon from '../../IdentityIcon';
@@ -24,57 +25,82 @@ import IdentityName from '../../IdentityName';
import styles from './addressSelect.css';
export default class AddressSelect extends Component {
+ static contextTypes = {
+ api: PropTypes.object.isRequired
+ }
+
static propTypes = {
disabled: PropTypes.bool,
accounts: PropTypes.object,
contacts: PropTypes.object,
+ contracts: PropTypes.object,
label: PropTypes.string,
hint: PropTypes.string,
error: PropTypes.string,
value: PropTypes.string,
tokens: PropTypes.object,
- onChange: PropTypes.func.isRequired
+ onChange: PropTypes.func.isRequired,
+ allowInput: PropTypes.bool
}
state = {
entries: {},
+ addresses: [],
value: ''
}
+ entriesFromProps (props = this.props) {
+ const { accounts, contacts, contracts } = props;
+ const entries = Object.assign({}, accounts || {}, contacts || {}, contracts || {});
+ return entries;
+ }
+
componentWillMount () {
- const { accounts, contacts, value } = this.props;
- const entries = Object.assign({}, accounts || {}, contacts || {});
- this.setState({ entries, value });
+ const { value } = this.props;
+ const entries = this.entriesFromProps();
+ const addresses = Object.keys(entries).sort();
+
+ this.setState({ entries, addresses, value });
}
componentWillReceiveProps (newProps) {
- const { accounts, contacts } = newProps;
- const entries = Object.assign({}, accounts || {}, contacts || {});
- this.setState({ entries });
+ const entries = this.entriesFromProps();
+ const addresses = Object.keys(entries).sort();
+
+ if (!isEqual(addresses, this.state.addresses)) {
+ this.setState({ entries, addresses });
+ }
+
+ if (newProps.value !== this.props.value) {
+ this.setState({ value: newProps.value });
+ }
}
render () {
- const { disabled, error, hint, label } = this.props;
- const { entries } = this.state;
- const value = this.getSearchText();
+ const { allowInput, disabled, error, hint, label } = this.props;
+ const { entries, value } = this.state;
+
+ const searchText = this.getSearchText();
+ const icon = this.renderIdentityIcon(value);
return (
-
- { this.renderIdentityIcon(value) }
+ { icon }
);
}
@@ -82,7 +108,7 @@ export default class AddressSelect extends Component {
renderIdentityIcon (inputValue) {
const { error, value } = this.props;
- if (error || !inputValue) {
+ if (error || !inputValue || value.length !== 42) {
return null;
}
@@ -96,8 +122,9 @@ export default class AddressSelect extends Component {
renderItem = (entry) => {
return {
- text: entry.address,
- value: this.renderSelectEntry(entry)
+ text: entry.name && entry.name.toUpperCase() || entry.address,
+ value: this.renderSelectEntry(entry),
+ address: entry.address
};
}
@@ -127,32 +154,48 @@ export default class AddressSelect extends Component {
getSearchText () {
const entry = this.getEntry();
- if (!entry) return '';
+ const { value } = this.state;
- return entry.name ? entry.name.toUpperCase() : '';
+ return entry && entry.name
+ ? entry.name.toUpperCase()
+ : value;
}
getEntry () {
- const { value } = this.props;
- if (!value) return '';
-
- const { entries } = this.state;
- return entries[value];
+ const { entries, value } = this.state;
+ return value ? entries[value] : null;
}
- handleFilter = (searchText, address) => {
+ handleFilter = (searchText, name, item) => {
+ const { address } = item;
const entry = this.state.entries[address];
const lowCaseSearch = searchText.toLowerCase();
- return [ entry.name, entry.address ]
+ return [entry.name, entry.address]
.some(text => text.toLowerCase().indexOf(lowCaseSearch) !== -1);
}
onChange = (entry, empty) => {
+ const { allowInput } = this.props;
+ const { value } = this.state;
+
const address = entry && entry.address
? entry.address
- : (empty ? '' : this.state.value);
+ : ((empty && !allowInput) ? '' : value);
this.props.onChange(null, address);
}
+
+ onUpdateInput = (query, choices) => {
+ const { api } = this.context;
+
+ const address = query.trim();
+
+ if (!/^0x/.test(address) && api.util.isAddressValid(`0x${address}`)) {
+ const checksumed = api.util.toChecksumAddress(`0x${address}`);
+ return this.props.onChange(null, checksumed);
+ }
+
+ this.props.onChange(null, address);
+ };
}
diff --git a/js/src/ui/Form/AutoComplete/autocomplete.js b/js/src/ui/Form/AutoComplete/autocomplete.js
index 353b4d73c..b0958da31 100644
--- a/js/src/ui/Form/AutoComplete/autocomplete.js
+++ b/js/src/ui/Form/AutoComplete/autocomplete.js
@@ -21,6 +21,7 @@ import { PopoverAnimationVertical } from 'material-ui/Popover';
export default class AutoComplete extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
+ onUpdateInput: PropTypes.func,
disabled: PropTypes.bool,
label: PropTypes.string,
hint: PropTypes.string,
@@ -43,7 +44,7 @@ export default class AutoComplete extends Component {
}
render () {
- const { disabled, error, hint, label, value, className, filter } = this.props;
+ const { disabled, error, hint, label, value, className, filter, onUpdateInput } = this.props;
const { open } = this.state;
return (
@@ -54,11 +55,11 @@ export default class AutoComplete extends Component {
hintText={ hint }
errorText={ error }
onNewRequest={ this.onChange }
+ onUpdateInput={ onUpdateInput }
searchText={ value }
onFocus={ this.onFocus }
onBlur={ this.onBlur }
animation={ PopoverAnimationVertical }
-
filter={ filter }
popoverProps={ { open } }
openOnFocus
@@ -108,11 +109,17 @@ export default class AutoComplete extends Component {
}
onBlur = () => {
- window.setTimeout(() => {
- const { entry } = this.state;
+ const { onUpdateInput } = this.props;
- this.handleOnChange(entry);
- }, 100);
+ // TODO: Handle blur gracefully where we use onUpdateInput (currently replaces input
+ // input where text is allowed with the last selected value from the dropdown)
+ if (!onUpdateInput) {
+ window.setTimeout(() => {
+ const { entry } = this.state;
+
+ this.handleOnChange(entry);
+ }, 100);
+ }
}
onFocus = () => {
@@ -131,5 +138,4 @@ export default class AutoComplete extends Component {
this.props.onChange(value, empty);
}
}
-
}
diff --git a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.css b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.css
deleted file mode 100644
index 0211ef568..000000000
--- a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.css
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright 2015, 2016 Ethcore (UK) Ltd.
-/* This file is part of Parity.
-/*
-/* Parity is free software: you can redistribute it and/or modify
-/* it under the terms of the GNU General Public License as published by
-/* the Free Software Foundation, either version 3 of the License, or
-/* (at your option) any later version.
-/*
-/* Parity is distributed in the hope that it will be useful,
-/* but WITHOUT ANY WARRANTY; without even the implied warranty of
-/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-/* GNU General Public License for more details.
-/*
-/* You should have received a copy of the GNU General Public License
-/* along with Parity. If not, see .
-*/
-.inputselect {
- position: relative;
-}
-
-.inputselect svg {
- padding-right: 84px;
-}
-
-.toggle {
- position: absolute !important;
- top: 38px;
- right: 0;
- display: inline-block !important;
- width: auto !important;
-}
diff --git a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js
index e2c5e8a1d..e77043aed 100644
--- a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js
+++ b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js
@@ -17,103 +17,46 @@
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
-import { Toggle } from 'material-ui';
import AddressSelect from '../AddressSelect';
-import InputAddress from '../InputAddress';
-
-import styles from './inputAddressSelect.css';
class InputAddressSelect extends Component {
static propTypes = {
- accounts: PropTypes.object,
- contacts: PropTypes.object,
- disabled: PropTypes.bool,
- editing: PropTypes.bool,
+ accounts: PropTypes.object.isRequired,
+ contacts: PropTypes.object.isRequired,
+ contracts: PropTypes.object.isRequired,
error: PropTypes.string,
label: PropTypes.string,
hint: PropTypes.string,
value: PropTypes.string,
- tokens: PropTypes.object,
onChange: PropTypes.func
};
- state = {
- editing: this.props.editing || false,
- entries: []
- }
-
render () {
- const { editing } = this.state;
-
- return (
-
- { editing ? this.renderInput() : this.renderSelect() }
-
-
- );
- }
-
- renderInput () {
- const { disabled, error, hint, label, value, tokens } = this.props;
-
- return (
-
- );
- }
-
- renderSelect () {
- const { accounts, contacts, disabled, error, hint, label, value, tokens } = this.props;
+ const { accounts, contacts, contracts, label, hint, error, value, onChange } = this.props;
return (
+ onChange={ onChange } />
);
}
-
- onToggle = () => {
- const { editing } = this.state;
-
- this.setState({
- editing: !editing
- });
- }
-
- onChangeInput = (event, value) => {
- this.props.onChange(event, value);
- }
-
- onChangeSelect = (event, value) => {
- this.props.onChange(event, value);
- }
}
function mapStateToProps (state) {
- const { accounts, contacts } = state.personal;
+ const { accounts, contacts, contracts } = state.personal;
return {
accounts,
- contacts
+ contacts,
+ contracts
};
}
diff --git a/js/src/views/Contract/Queries/inputQuery.js b/js/src/views/Contract/Queries/inputQuery.js
index f21379a23..94c569412 100644
--- a/js/src/views/Contract/Queries/inputQuery.js
+++ b/js/src/views/Contract/Queries/inputQuery.js
@@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react';
import LinearProgress from 'material-ui/LinearProgress';
import { Card, CardActions, CardTitle, CardText } from 'material-ui/Card';
-import { Button, Input } from '../../../ui';
+import { Button, Input, InputAddressSelect } from '../../../ui';
import styles from './queries.css';
@@ -124,8 +124,8 @@ export default class InputQuery extends Component {
const { name, type } = input;
const label = `${name ? `${name}: ` : ''}${type}`;
- const onChange = (event) => {
- const value = event.target.value;
+ const onChange = (event, input) => {
+ const value = event && event.target.value || input;
const { values } = this.state;
this.setState({
@@ -136,6 +136,19 @@ export default class InputQuery extends Component {
});
};
+ if (type === 'address') {
+ return (
+
+
+
+ );
+ }
+
return (