parent
							
								
									002e8b00d4
								
							
						
					
					
						commit
						2bbefcd438
					
				| @ -14,137 +14,130 @@ | ||||
| // You should have received a copy of the GNU General Public License
 | ||||
| // along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import { observer } from 'mobx-react'; | ||||
| import React, { Component, PropTypes } from 'react'; | ||||
| import ContentClear from 'material-ui/svg-icons/content/clear'; | ||||
| import ContentSave from 'material-ui/svg-icons/content/save'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import { Button, Form, Input, InputChip, Modal } from '~/ui'; | ||||
| import { validateName } from '~/util/validation'; | ||||
| import { CancelIcon, SaveIcon } from '~/ui/Icons'; | ||||
| 
 | ||||
| import Store from './store'; | ||||
| 
 | ||||
| @observer | ||||
| export default class EditMeta extends Component { | ||||
|   static contextTypes = { | ||||
|     api: PropTypes.object.isRequired, | ||||
|     store: PropTypes.object.isRequired | ||||
|     api: PropTypes.object.isRequired | ||||
|   } | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     keys: PropTypes.array.isRequired, | ||||
|     account: PropTypes.object.isRequired, | ||||
|     onClose: PropTypes.func.isRequired | ||||
|   } | ||||
| 
 | ||||
|   state = { | ||||
|     meta: Object.assign({}, this.props.account.meta), | ||||
|     metaErrors: {}, | ||||
|     name: this.props.account.name, | ||||
|     nameError: null | ||||
|   } | ||||
|   store = new Store(this.context.api, this.props.account); | ||||
| 
 | ||||
|   render () { | ||||
|     const { name, nameError } = this.state; | ||||
|     const { description, name, nameError, tags } = this.store; | ||||
| 
 | ||||
|     return ( | ||||
|       <Modal | ||||
|         visible | ||||
|         actions={ this.renderActions() } | ||||
|         title='edit metadata'> | ||||
|         title={ | ||||
|           <FormattedMessage | ||||
|             id='editMeta.title' | ||||
|             defaultMessage='edit metadata' /> | ||||
|         } | ||||
|         visible> | ||||
|         <Form> | ||||
|           <Input | ||||
|             label='name' | ||||
|             value={ name } | ||||
|             error={ nameError } | ||||
|             onSubmit={ this.onNameChange } /> | ||||
|           { this.renderMetaFields() } | ||||
|           { this.renderTags() } | ||||
|             label={ | ||||
|               <FormattedMessage | ||||
|                 id='editMeta.name.label' | ||||
|                 defaultMessage='name' /> | ||||
|             } | ||||
|             onSubmit={ this.store.setName } | ||||
|             value={ name } /> | ||||
|           <Input | ||||
|             hint={ | ||||
|               <FormattedMessage | ||||
|                 id='editMeta.description.hint' | ||||
|                 defaultMessage='description for this address' /> | ||||
|             } | ||||
|             label={ | ||||
|               <FormattedMessage | ||||
|                 id='editMeta.description.label' | ||||
|                 defaultMessage='address description' /> | ||||
|             } | ||||
|             value={ description } | ||||
|             onSubmit={ this.store.setDescription } /> | ||||
|           { this.renderAccountFields() } | ||||
|           <InputChip | ||||
|             addOnBlur | ||||
|             hint={ | ||||
|               <FormattedMessage | ||||
|                 id='editMeta.tags.hint' | ||||
|                 defaultMessage='press <Enter> to add a tag' /> | ||||
|             } | ||||
|             label={ | ||||
|               <FormattedMessage | ||||
|                 id='editMeta.tags.label' | ||||
|                 defaultMessage='(optional) tags' /> | ||||
|             } | ||||
|             onTokensChange={ this.store.setTags } | ||||
|             tokens={ tags } /> | ||||
|         </Form> | ||||
|       </Modal> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderActions () { | ||||
|     const { nameError } = this.state; | ||||
|     const { hasError } = this.store; | ||||
| 
 | ||||
|     return [ | ||||
|       <Button | ||||
|         label='Cancel' | ||||
|         icon={ <ContentClear /> } | ||||
|         icon={ <CancelIcon /> } | ||||
|         onClick={ this.props.onClose } />, | ||||
|       <Button | ||||
|         disabled={ !!nameError } | ||||
|         disabled={ hasError } | ||||
|         label='Save' | ||||
|         icon={ <ContentSave /> } | ||||
|         icon={ <SaveIcon /> } | ||||
|         onClick={ this.onSave } /> | ||||
|     ]; | ||||
|   } | ||||
| 
 | ||||
|   renderMetaFields () { | ||||
|     const { keys } = this.props; | ||||
|     const { meta } = this.state; | ||||
|   renderAccountFields () { | ||||
|     const { isAccount, passwordHint } = this.store; | ||||
| 
 | ||||
|     return keys.map((key) => { | ||||
|       const onSubmit = (value) => this.onMetaChange(key, value); | ||||
|       const label = `(optional) ${key}`; | ||||
|       const hint = `the optional ${key} metadata`; | ||||
| 
 | ||||
|       return ( | ||||
|         <Input | ||||
|           key={ key } | ||||
|           label={ label } | ||||
|           hint={ hint } | ||||
|           value={ meta[key] || '' } | ||||
|           onSubmit={ onSubmit } /> | ||||
|       ); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   renderTags () { | ||||
|     const { meta } = this.state; | ||||
|     if (!isAccount) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|       <InputChip | ||||
|         tokens={ meta.tags || [] } | ||||
|         onTokensChange={ this.onTagsChange } | ||||
|         label='(optional) tags' | ||||
|         hint='press <Enter> to add a tag' | ||||
|         addOnBlur | ||||
|       /> | ||||
|       <Input | ||||
|         hint={ | ||||
|           <FormattedMessage | ||||
|             id='editMeta.passwordHint.hint' | ||||
|             defaultMessage='a hint to allow password recovery' /> | ||||
|         } | ||||
|         label={ | ||||
|           <FormattedMessage | ||||
|             id='editMeta.passwordHint.label' | ||||
|             defaultMessage='(optional) password hint' /> | ||||
|         } | ||||
|         value={ passwordHint } | ||||
|         onSubmit={ this.store.setPasswordHint } /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   onTagsChange = (newTags) => { | ||||
|     this.onMetaChange('tags', newTags); | ||||
|   } | ||||
| 
 | ||||
|   onNameChange = (name) => { | ||||
|     this.setState(validateName(name)); | ||||
|   } | ||||
| 
 | ||||
|   onMetaChange = (key, value) => { | ||||
|     const { meta } = this.state; | ||||
| 
 | ||||
|     this.setState({ | ||||
|       meta: Object.assign(meta, { [key]: value }) | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   onSave = () => { | ||||
|     const { api, store } = this.context; | ||||
|     const { account } = this.props; | ||||
|     const { name, nameError, meta } = this.state; | ||||
| 
 | ||||
|     if (nameError) { | ||||
|     if (this.store.hasError) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     Promise | ||||
|       .all([ | ||||
|         api.parity.setAccountName(account.address, name), | ||||
|         api.parity.setAccountMeta(account.address, Object.assign({}, account.meta, meta)) | ||||
|       ]) | ||||
|       .then(() => this.props.onClose()) | ||||
|       .catch((error) => { | ||||
|         console.error('onSave', error); | ||||
|         store.dispatch({ type: 'newError', error }); | ||||
|       }); | ||||
|     return this.store | ||||
|       .save() | ||||
|       .then(() => this.props.onClose()); | ||||
|   } | ||||
| } | ||||
|  | ||||
							
								
								
									
										75
									
								
								js/src/modals/EditMeta/editMeta.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								js/src/modals/EditMeta/editMeta.spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| // Copyright 2015, 2016 Parity Technologies (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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import { shallow } from 'enzyme'; | ||||
| import React from 'react'; | ||||
| import sinon from 'sinon'; | ||||
| 
 | ||||
| import EditMeta from './'; | ||||
| 
 | ||||
| import { ACCOUNT } from './editMeta.test.js'; | ||||
| 
 | ||||
| let component; | ||||
| let onClose; | ||||
| 
 | ||||
| function render (props) { | ||||
|   onClose = sinon.stub(); | ||||
| 
 | ||||
|   component = shallow( | ||||
|     <EditMeta | ||||
|       { ...props } | ||||
|       account={ ACCOUNT } | ||||
|       onClose={ onClose } />, | ||||
|     { | ||||
|       context: { | ||||
|         api: { | ||||
|           parity: { | ||||
|             setAccountName: sinon.stub().resolves(), | ||||
|             setAccountMeta: sinon.stub().resolves() | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ); | ||||
| 
 | ||||
|   return component; | ||||
| } | ||||
| 
 | ||||
| describe('modals/EditMeta', () => { | ||||
|   describe('rendering', () => { | ||||
|     it('renders defaults', () => { | ||||
|       expect(render()).to.be.ok; | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('actions', () => { | ||||
|     beforeEach(() => { | ||||
|       render(); | ||||
|     }); | ||||
| 
 | ||||
|     describe('onSave', () => { | ||||
|       it('calls store.save() & props.onClose', () => { | ||||
|         const instance = component.instance(); | ||||
|         sinon.spy(instance.store, 'save'); | ||||
| 
 | ||||
|         instance.onSave().then(() => { | ||||
|           expect(instance.store.save).to.have.been.called; | ||||
|           expect(onClose).to.have.been.called; | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
							
								
								
									
										45
									
								
								js/src/modals/EditMeta/editMeta.test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								js/src/modals/EditMeta/editMeta.test.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | ||||
| // Copyright 2015, 2016 Parity Technologies (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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| const ACCOUNT = { | ||||
|   address: '0x123456789a123456789a123456789a123456789a', | ||||
|   meta: { | ||||
|     description: 'Call me bob', | ||||
|     passwordHint: 'some hint', | ||||
|     tags: ['testing'] | ||||
|   }, | ||||
|   name: 'Bobby', | ||||
|   uuid: '123-456' | ||||
| }; | ||||
| 
 | ||||
| const ADDRESS = { | ||||
|   address: '0x0123456789012345678901234567890123456789', | ||||
|   meta: { | ||||
|     description: 'Some address', | ||||
|     extraMeta: { | ||||
|       some: 'random', | ||||
|       extra: { | ||||
|         meta: 'data' | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   name: 'Random address' | ||||
| }; | ||||
| 
 | ||||
| export { | ||||
|   ACCOUNT, | ||||
|   ADDRESS | ||||
| }; | ||||
							
								
								
									
										97
									
								
								js/src/modals/EditMeta/store.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								js/src/modals/EditMeta/store.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| // Copyright 2015, 2016 Parity Technologies (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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import { action, computed, observable, toJS, transaction } from 'mobx'; | ||||
| 
 | ||||
| import { newError } from '~/redux/actions'; | ||||
| import { validateName } from '~/util/validation'; | ||||
| 
 | ||||
| export default class Store { | ||||
|   @observable address = null; | ||||
|   @observable isAccount = false; | ||||
|   @observable description = null; | ||||
|   @observable meta = {}; | ||||
|   @observable name = null; | ||||
|   @observable nameError = null; | ||||
|   @observable passwordHint = null; | ||||
|   @observable tags = []; | ||||
| 
 | ||||
|   constructor (api, account) { | ||||
|     const { address, name, meta, uuid } = account; | ||||
| 
 | ||||
|     this._api = api; | ||||
| 
 | ||||
|     this.isAccount = !!uuid; | ||||
|     this.address = address; | ||||
|     this.meta = Object.assign({}, meta || {}); | ||||
|     this.name = name || ''; | ||||
| 
 | ||||
|     this.description = this.meta.description || ''; | ||||
|     this.passwordHint = this.meta.passwordHint || ''; | ||||
|     this.tags = [].concat((meta || {}).tags || []); | ||||
|   } | ||||
| 
 | ||||
|   @computed get hasError () { | ||||
|     return !!(this.nameError); | ||||
|   } | ||||
| 
 | ||||
|   @action setDescription = (description) => { | ||||
|     this.description = description; | ||||
|   } | ||||
| 
 | ||||
|   @action setName = (_name) => { | ||||
|     const { name, nameError } = validateName(_name); | ||||
| 
 | ||||
|     transaction(() => { | ||||
|       this.name = name; | ||||
|       this.setNameError(nameError); | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   @action setNameError = (nameError) => { | ||||
|     this.nameError = nameError; | ||||
|   } | ||||
| 
 | ||||
|   @action setPasswordHint = (passwordHint) => { | ||||
|     this.passwordHint = passwordHint; | ||||
|   } | ||||
| 
 | ||||
|   @action setTags = (tags) => { | ||||
|     this.tags = [].concat(tags); | ||||
|   } | ||||
| 
 | ||||
|   save () { | ||||
|     const meta = { | ||||
|       description: this.description, | ||||
|       tags: this.tags.peek() | ||||
|     }; | ||||
| 
 | ||||
|     if (this.isAccount) { | ||||
|       meta.passwordHint = this.passwordHint; | ||||
|     } | ||||
| 
 | ||||
|     return Promise | ||||
|       .all([ | ||||
|         this._api.parity.setAccountName(this.address, this.name), | ||||
|         this._api.parity.setAccountMeta(this.address, Object.assign({}, toJS(this.meta), meta)) | ||||
|       ]) | ||||
|       .catch((error) => { | ||||
|         console.error('onSave', error); | ||||
| 
 | ||||
|         newError(error); | ||||
|       }); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										180
									
								
								js/src/modals/EditMeta/store.spec.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								js/src/modals/EditMeta/store.spec.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,180 @@ | ||||
| // Copyright 2015, 2016 Parity Technologies (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 <http://www.gnu.org/licenses/>.
 | ||||
| 
 | ||||
| import { toJS } from 'mobx'; | ||||
| import sinon from 'sinon'; | ||||
| 
 | ||||
| import Store from './store'; | ||||
| import { ACCOUNT, ADDRESS } from './editMeta.test.js'; | ||||
| 
 | ||||
| let api; | ||||
| let store; | ||||
| 
 | ||||
| function createStore (account) { | ||||
|   api = { | ||||
|     parity: { | ||||
|       setAccountName: sinon.stub().resolves(), | ||||
|       setAccountMeta: sinon.stub().resolves() | ||||
|     } | ||||
|   }; | ||||
| 
 | ||||
|   store = new Store(api, account); | ||||
| 
 | ||||
|   return store; | ||||
| } | ||||
| 
 | ||||
| describe('modals/EditMeta/Store', () => { | ||||
|   describe('constructor', () => { | ||||
|     describe('accounts', () => { | ||||
|       beforeEach(() => { | ||||
|         createStore(ACCOUNT); | ||||
|       }); | ||||
| 
 | ||||
|       it('flags it as an account', () => { | ||||
|         expect(store.isAccount).to.be.true; | ||||
|       }); | ||||
| 
 | ||||
|       it('extracts the address', () => { | ||||
|         expect(store.address).to.equal(ACCOUNT.address); | ||||
|       }); | ||||
| 
 | ||||
|       it('extracts the name', () => { | ||||
|         expect(store.name).to.equal(ACCOUNT.name); | ||||
|       }); | ||||
| 
 | ||||
|       it('extracts the tags', () => { | ||||
|         expect(store.tags.peek()).to.deep.equal(ACCOUNT.meta.tags); | ||||
|       }); | ||||
| 
 | ||||
|       describe('meta', () => { | ||||
|         it('extracts the full meta', () => { | ||||
|           expect(toJS(store.meta)).to.deep.equal(ACCOUNT.meta); | ||||
|         }); | ||||
| 
 | ||||
|         it('extracts the description', () => { | ||||
|           expect(store.description).to.equal(ACCOUNT.meta.description); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('addresses', () => { | ||||
|       beforeEach(() => { | ||||
|         createStore(ADDRESS); | ||||
|       }); | ||||
| 
 | ||||
|       it('flags it as not an account', () => { | ||||
|         expect(store.isAccount).to.be.false; | ||||
|       }); | ||||
| 
 | ||||
|       it('extracts the address', () => { | ||||
|         expect(store.address).to.equal(ADDRESS.address); | ||||
|       }); | ||||
| 
 | ||||
|       it('extracts the name', () => { | ||||
|         expect(store.name).to.equal(ADDRESS.name); | ||||
|       }); | ||||
| 
 | ||||
|       it('extracts the tags (empty)', () => { | ||||
|         expect(store.tags.peek()).to.deep.equal([]); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('@computed', () => { | ||||
|     beforeEach(() => { | ||||
|       createStore(ADDRESS); | ||||
|     }); | ||||
| 
 | ||||
|     describe('hasError', () => { | ||||
|       it('is false when no nameError', () => { | ||||
|         store.setNameError(null); | ||||
|         expect(store.hasError).to.be.false; | ||||
|       }); | ||||
| 
 | ||||
|       it('is false with a nameError', () => { | ||||
|         store.setNameError('some error'); | ||||
|         expect(store.hasError).to.be.true; | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('@actions', () => { | ||||
|     beforeEach(() => { | ||||
|       createStore(ADDRESS); | ||||
|     }); | ||||
| 
 | ||||
|     describe('setDescription', () => { | ||||
|       it('sets the description', () => { | ||||
|         store.setDescription('description'); | ||||
|         expect(store.description).to.equal('description'); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('setName', () => { | ||||
|       it('sets the name', () => { | ||||
|         store.setName('valid name'); | ||||
|         expect(store.name).to.equal('valid name'); | ||||
|         expect(store.nameError).to.be.null; | ||||
|       }); | ||||
| 
 | ||||
|       it('sets name and error on invalid', () => { | ||||
|         store.setName(''); | ||||
|         expect(store.name).to.equal(''); | ||||
|         expect(store.nameError).not.to.be.null; | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('setPasswordHint', () => { | ||||
|       it('sets the description', () => { | ||||
|         store.setPasswordHint('passwordHint'); | ||||
|         expect(store.passwordHint).to.equal('passwordHint'); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('setTags', () => { | ||||
|       it('sets the tags', () => { | ||||
|         store.setTags(['taga', 'tagb']); | ||||
|         expect(store.tags.peek()).to.deep.equal(['taga', 'tagb']); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('save', () => { | ||||
|     beforeEach(() => { | ||||
|       createStore(ACCOUNT); | ||||
|     }); | ||||
| 
 | ||||
|     it('calls parity.setAccountName with the set value', () => { | ||||
|       store.setName('test name'); | ||||
|       store.save(); | ||||
| 
 | ||||
|       expect(api.parity.setAccountName).to.be.calledWith(ACCOUNT.address, 'test name'); | ||||
|     }); | ||||
| 
 | ||||
|     it('calls parity.setAccountMeta with the adjusted values', () => { | ||||
|       store.setDescription('some new description'); | ||||
|       store.setPasswordHint('some new passwordhint'); | ||||
|       store.setTags(['taga']); | ||||
|       store.save(); | ||||
| 
 | ||||
|       expect(api.parity.setAccountMeta).to.have.been.calledWith(ACCOUNT.address, Object.assign({}, ACCOUNT.meta, { | ||||
|         description: 'some new description', | ||||
|         passwordHint: 'some new passwordhint', | ||||
|         tags: ['taga'] | ||||
|       })); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @ -20,19 +20,24 @@ import ChipInput from 'material-ui-chip-input'; | ||||
| import { blue300 } from 'material-ui/styles/colors'; | ||||
| import { uniq } from 'lodash'; | ||||
| 
 | ||||
| import { nodeOrStringProptype } from '~/util/proptypes'; | ||||
| 
 | ||||
| import styles from './inputChip.css'; | ||||
| 
 | ||||
| export default class InputChip extends Component { | ||||
|   static propTypes = { | ||||
|     tokens: PropTypes.array.isRequired, | ||||
|     addOnBlur: PropTypes.bool, | ||||
|     clearOnBlur: PropTypes.bool, | ||||
|     className: PropTypes.string, | ||||
|     hint: PropTypes.string, | ||||
|     label: PropTypes.string, | ||||
|     hint: nodeOrStringProptype(), | ||||
|     label: nodeOrStringProptype(), | ||||
|     onTokensChange: PropTypes.func, | ||||
|     onInputChange: PropTypes.func, | ||||
|     onBlur: PropTypes.func, | ||||
|     addOnBlur: PropTypes.bool, | ||||
|     clearOnBlur: PropTypes.bool | ||||
|     tokens: PropTypes.oneOfType([ | ||||
|       PropTypes.array, | ||||
|       PropTypes.object | ||||
|     ]).isRequired | ||||
|   } | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|  | ||||
| @ -21,6 +21,7 @@ import ContractIcon from 'material-ui/svg-icons/action/code'; | ||||
| import DoneIcon from 'material-ui/svg-icons/action/done-all'; | ||||
| import PrevIcon from 'material-ui/svg-icons/navigation/arrow-back'; | ||||
| import NextIcon from 'material-ui/svg-icons/navigation/arrow-forward'; | ||||
| import SaveIcon from 'material-ui/svg-icons/content/save'; | ||||
| import SnoozeIcon from 'material-ui/svg-icons/av/snooze'; | ||||
| 
 | ||||
| export { | ||||
| @ -31,5 +32,6 @@ export { | ||||
|   DoneIcon, | ||||
|   PrevIcon, | ||||
|   NextIcon, | ||||
|   SaveIcon, | ||||
|   SnoozeIcon | ||||
| }; | ||||
|  | ||||
| @ -186,7 +186,6 @@ class Account extends Component { | ||||
|     return ( | ||||
|       <EditMeta | ||||
|         account={ account } | ||||
|         keys={ ['description', 'passwordHint'] } | ||||
|         onClose={ this.onEditClick } /> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -182,7 +182,6 @@ class Address extends Component { | ||||
|     return ( | ||||
|       <EditMeta | ||||
|         account={ contact } | ||||
|         keys={ ['description'] } | ||||
|         onClose={ this.onEditClick } /> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -220,7 +220,7 @@ class Contract extends Component { | ||||
|         key='editmeta' | ||||
|         icon={ <ContentCreate /> } | ||||
|         label='edit' | ||||
|         onClick={ this.onEditClick } />, | ||||
|         onClick={ this.showEditDialog } />, | ||||
|       <Button | ||||
|         key='delete' | ||||
|         icon={ <ActionDelete /> } | ||||
| @ -262,8 +262,7 @@ class Contract extends Component { | ||||
|     return ( | ||||
|       <EditMeta | ||||
|         account={ account } | ||||
|         keys={ ['description'] } | ||||
|         onClose={ this.onEditClick } /> | ||||
|         onClose={ this.closeEditDialog } /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| @ -312,10 +311,12 @@ class Contract extends Component { | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   onEditClick = () => { | ||||
|     this.setState({ | ||||
|       showEditDialog: !this.state.showEditDialog | ||||
|     }); | ||||
|   closeEditDialog = () => { | ||||
|     this.setState({ showEditDialog: false }); | ||||
|   } | ||||
| 
 | ||||
|   showEditDialog = () => { | ||||
|     this.setState({ showEditDialog: true }); | ||||
|   } | ||||
| 
 | ||||
|   closeDeleteDialog = () => { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user