Ui 2 remove mui from views & shell (#5613)

* Stepper from semantic-ui

* Tabs component

* ui/Tabs in loadContract

* Adapt Dropdown events

* Remove direct use of semantic-ui

* Remove IconButton & Subheader

* Remove IconMenu

* ui/List & List.Item

* Be explicit in context props
This commit is contained in:
Jaco Greeff 2017-05-12 17:06:53 +02:00 committed by GitHub
parent 1243728725
commit 7cdddd344b
39 changed files with 740 additions and 482 deletions

View File

@ -18,8 +18,8 @@ import React, { PropTypes } from 'react';
import styles from './dappIcon.css'; import styles from './dappIcon.css';
export default function DappIcon ({ app, className, small }, context) { export default function DappIcon ({ app, className, small }, { api }) {
const { dappsUrl } = context.api; const { dappsUrl } = api;
return ( return (
<img <img

View File

@ -26,7 +26,9 @@ import styles from './dropdown.css';
const NAME_ID = ' '; const NAME_ID = ' ';
// FIXME: Currently does not display the selected icon alongside // FIXME: Currently does not display the selected icon alongside
export default function Dropdown ({ className, disabled = false, error, fullWidth = true, hint, label, onBlur, onChange, onKeyDown, options, text, value }, context) { export default function Dropdown ({ className, disabled = false, error, fullWidth = true, hint, label, onBlur, onChange, onKeyDown, options, text, value }, { intl }) {
const _onChange = (event, { value }) => onChange(event, value);
return ( return (
<LabelComponent label={ label }> <LabelComponent label={ label }>
<SemanticDropdown <SemanticDropdown
@ -37,7 +39,7 @@ export default function Dropdown ({ className, disabled = false, error, fullWidt
id={ NAME_ID } id={ NAME_ID }
name={ NAME_ID } name={ NAME_ID }
onBlur={ onBlur } onBlur={ onBlur }
onChange={ onChange } onChange={ _onChange }
onKeyDown={ onKeyDown } onKeyDown={ onKeyDown }
options={ options } options={ options }
placeholder={ parseI18NString(context, hint) } placeholder={ parseI18NString(context, hint) }

View File

@ -392,9 +392,8 @@ export default class TypedInput extends Component {
); );
} }
onChangeBool = (event) => { onChangeBool = (event, value) => {
console.log('onChangeBool', event.target); this.props.onChange(value === 'true');
this.props.onChange(event.target.value === 'true');
} }
onEthTypeChange = () => { onEthTypeChange = () => {

View File

@ -63,7 +63,7 @@ export default class LanguageSelector extends Component {
); );
} }
onChange = (event, index, locale) => { onChange = (event, locale) => {
this.store.setLocale(locale || event.target.value); this.store.setLocale(locale);
} }
} }

View File

@ -60,10 +60,5 @@ describe('LanguageSelector', () => {
it('has locale items', () => { it('has locale items', () => {
expect(select.props().options.length > 0).to.be.true; expect(select.props().options.length > 0).to.be.true;
}); });
it('calls localeStore.setLocale when changed', () => {
select.simulate('change', { target: { value: 'de' } });
expect(localeStore.setLocale).to.have.been.calledWith('de');
});
}); });
}); });

View File

@ -0,0 +1,17 @@
// Copyright 2015-2017 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/>.
export default from './item';

View File

@ -0,0 +1,24 @@
/* Copyright 2015-2017 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/>.
*/
.item {
.description {
}
.label {
}
}

View File

@ -0,0 +1,60 @@
// Copyright 2015-2017 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 React, { PropTypes } from 'react';
import { List as SemanticList } from 'semantic-ui-react';
import styles from './item.css';
export default function Item ({ buttons, className, description, icon, label, onClick, style }) {
return (
<SemanticList.Item
className={ styles.item }
onClick={ onClick }
style={ style }
>
{
icon && (
<SemanticList.Icon>
{ icon }
</SemanticList.Icon>
)
}
<SemanticList.Content>
<div className={ styles.label }>
{ label }
</div>
<div className={ styles.description }>
{ description }
</div>
<div className={ styles.buttons }>
{ buttons }
</div>
</SemanticList.Content>
</SemanticList.Item>
);
}
Item.propTypes = {
buttons: PropTypes.any,
className: PropTypes.string,
description: PropTypes.node,
key: PropTypes.any,
icon: PropTypes.node,
label: PropTypes.node,
onClick: PropTypes.func,
style: PropTypes.object
};

17
js/src/ui/List/index.js Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2015-2017 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/>.
export default from './list';

19
js/src/ui/List/list.css Normal file
View File

@ -0,0 +1,19 @@
/* Copyright 2015-2017 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/>.
*/
.list {
}

58
js/src/ui/List/list.js Normal file
View File

@ -0,0 +1,58 @@
// Copyright 2015-2017 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 React, { PropTypes } from 'react';
import { List as SemanticList } from 'semantic-ui-react';
import LabelComponent from '../Form/LabelComponent';
import Item from './Item';
import styles from './list.css';
export default function List ({ className, items, label, onClick, style }) {
const wrapOnClick = (key) => {
return (event) => onClick && onClick(event, key);
};
return (
<LabelComponent label={ label }>
<SemanticList className={ `${styles.list} ${className}` }>
{
items.map(({ buttons, description, icon, key, label }, index) => (
<Item
buttons={ buttons }
description={ description }
icon={ icon }
key={ key || index }
label={ label }
onClick={ wrapOnClick(key || index) }
/>
))
}
</SemanticList>
</LabelComponent>
);
}
List.Item = Item;
List.propTypes = {
className: PropTypes.string,
items: PropTypes.array,
label: PropTypes.node,
onClick: PropTypes.func,
style: PropTypes.object
};

View File

@ -0,0 +1,17 @@
// Copyright 2015-2017 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/>.
export default from './step';

View File

@ -0,0 +1,41 @@
// Copyright 2015-2017 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 React, { PropTypes } from 'react';
import { Step as SemanticStep } from 'semantic-ui-react';
export default function Step ({ className, isActive, isCompleted, label }) {
return (
<SemanticStep
className={ className }
completed={ isCompleted }
active={ isActive }
>
<SemanticStep.Content>
<SemanticStep.Title>
{ label }
</SemanticStep.Title>
</SemanticStep.Content>
</SemanticStep>
);
}
Step.propTypes = {
className: PropTypes.string,
isActive: PropTypes.bool,
isCompleted: PropTypes.bool,
label: PropTypes.node
};

View File

@ -15,35 +15,37 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Step, Stepper, StepLabel } from 'material-ui/Stepper'; import { Step as SemanticStep } from 'semantic-ui-react';
import styles from '../title.css'; import Step from './Step';
export default function Steps ({ activeStep, steps }) { export default function Steps ({ activeStep, className, steps }) {
if (!steps || steps.length < 2) { if (!steps || steps.length < 2) {
return null; return null;
} }
return ( return (
<div className={ styles.steps }> <div className={ className }>
<Stepper activeStep={ activeStep }> <SemanticStep.Group ordered>
{ {
steps.map((label, index) => { steps.map((label, index) => (
return ( <Step
<Step key={ label.key || index }> isActive={ activeStep === index }
<StepLabel> isCompleted={ activeStep > index }
{ label } key={ label.key || index }
</StepLabel> label={ label.label || label }
</Step> />
); ))
})
} }
</Stepper> </SemanticStep.Group>
</div> </div>
); );
} }
Steps.Step = Step;
Steps.propTypes = { Steps.propTypes = {
activeStep: PropTypes.number, activeStep: PropTypes.number,
className: PropTypes.string,
steps: PropTypes.array steps: PropTypes.array
}; };

View File

@ -31,12 +31,12 @@ function render (props = {}) {
return component; return component;
} }
describe('ui/Title/Steps', () => { describe('ui/Steps', () => {
beforeEach(() => { beforeEach(() => {
render({ steps: ['stepA', 'stepB'] }); render({ steps: ['stepA', 'stepB'] });
}); });
it('renders the Stepper', () => { it('renders the defaults', () => {
expect(component.find('Stepper').get(0)).to.be.ok; expect(component).to.be.ok;
}); });
}); });

View File

@ -0,0 +1,17 @@
// Copyright 2015-2017 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/>.
export default from './tab';

41
js/src/ui/Tabs/Tab/tab.js Normal file
View File

@ -0,0 +1,41 @@
// Copyright 2015-2017 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 React, { PropTypes } from 'react';
import { Menu } from 'semantic-ui-react';
export default function Tab ({ isActive, className, index, label, name, onClick, style }) {
return (
<Menu.Item
active={ isActive }
index={ index }
name={ name }
onClick={ onClick }
>
{ label }
</Menu.Item>
);
}
Tab.propTypes = {
className: PropTypes.string,
index: PropTypes.number,
isActive: PropTypes.bool,
label: PropTypes.any,
name: PropTypes.string,
onClick: PropTypes.func,
style: PropTypes.object
};

17
js/src/ui/Tabs/index.js Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2015-2017 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/>.
export default from './tabs';

60
js/src/ui/Tabs/tabs.js Normal file
View File

@ -0,0 +1,60 @@
// Copyright 2015-2017 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 React, { PropTypes } from 'react';
import { Menu } from 'semantic-ui-react';
import Tab from './Tab';
export default function Tabs ({ activeTab, className, tabs, onChange }) {
const onTabClick = (event, { index }) => onChange && onChange(event, index);
return (
<Menu
className={ className }
pointing
>
{
tabs.map((tab, index) => {
if (!tab) {
return null;
}
const key = `tab_${index}`;
return (
<Tab
isActive={ activeTab === index }
index={ index }
key={ key }
label={ tab.label || tab }
onClick={ onTabClick }
/>
);
})
}
</Menu>
);
}
Tabs.Tab = Tab;
Tabs.propTypes = {
activeTab: PropTypes.number,
className: PropTypes.string,
onChange: PropTypes.func,
tabs: PropTypes.array
};

View File

@ -18,9 +18,7 @@ import React, { PropTypes } from 'react';
import Progress from '~/ui/Progress'; import Progress from '~/ui/Progress';
import styles from '../title.css'; export default function Waiting ({ activeStep, busy, busySteps, className }) {
export default function Waiting ({ activeStep, busy, busySteps }) {
const isWaiting = busy || (busySteps || []).includes(activeStep); const isWaiting = busy || (busySteps || []).includes(activeStep);
if (!isWaiting) { if (!isWaiting) {
@ -28,7 +26,7 @@ export default function Waiting ({ activeStep, busy, busySteps }) {
} }
return ( return (
<div className={ styles.waiting }> <div className={ className }>
<Progress /> <Progress />
</div> </div>
); );
@ -37,5 +35,6 @@ export default function Waiting ({ activeStep, busy, busySteps }) {
Waiting.propTypes = { Waiting.propTypes = {
activeStep: PropTypes.number, activeStep: PropTypes.number,
busy: PropTypes.bool, busy: PropTypes.bool,
busySteps: PropTypes.array busySteps: PropTypes.array,
className: PropTypes.string
}; };

View File

@ -18,7 +18,7 @@
.title, .title,
.subtitle { .subtitle {
.steps { .steps {
margin: -0.5em 0 -1em 0; margin: 1em 0 0 0;
} }
.waiting { .waiting {

View File

@ -19,8 +19,8 @@ import React, { PropTypes } from 'react';
import { nodeOrStringProptype } from '@parity/shared/util/proptypes'; import { nodeOrStringProptype } from '@parity/shared/util/proptypes';
import { Title as ContainerTitle } from '~/ui/Container'; import { Title as ContainerTitle } from '~/ui/Container';
import Steps from '~/ui/Steps';
import Steps from './Steps';
import Waiting from './Waiting'; import Waiting from './Waiting';
import styles from './title.css'; import styles from './title.css';
@ -52,12 +52,14 @@ export default function Title ({ activeStep, busy, busySteps, byline, className,
/> />
<Steps <Steps
activeStep={ activeStep } activeStep={ activeStep }
className={ styles.steps }
steps={ steps } steps={ steps }
/> />
<Waiting <Waiting
activeStep={ activeStep } activeStep={ activeStep }
busy={ busy } busy={ busy }
busySteps={ busySteps } busySteps={ busySteps }
className={ styles.waiting }
/> />
</div> </div>
); );

View File

@ -21,8 +21,7 @@ import IconCache from '~/ui/IconCache';
const iconCache = IconCache.get(); const iconCache = IconCache.get();
export default function TokenImage ({ token }, context) { export default function TokenImage ({ token }, { api }) {
const { api } = context;
const imageurl = token.image || iconCache.images[token.address]; const imageurl = token.image || iconCache.images[token.address];
let imagesrc = unknownImage; let imagesrc = unknownImage;

View File

@ -41,6 +41,7 @@ export Icons from './Icons';
export IdentityIcon from './IdentityIcon'; export IdentityIcon from './IdentityIcon';
export IdentityName from './IdentityName'; export IdentityName from './IdentityName';
export LanguageSelector from './LanguageSelector'; export LanguageSelector from './LanguageSelector';
export List from './List';
export Loading from './Loading'; export Loading from './Loading';
export MethodDecoding from './MethodDecoding'; export MethodDecoding from './MethodDecoding';
export { Busy as BusyStep, Completed as CompletedStep } from './Modal'; export { Busy as BusyStep, Completed as CompletedStep } from './Modal';
@ -57,6 +58,7 @@ export SelectionList from './SelectionList';
export ShortenedHash from './ShortenedHash'; export ShortenedHash from './ShortenedHash';
export SignerIcon from './SignerIcon'; export SignerIcon from './SignerIcon';
export Snackbar from './Snackbar'; export Snackbar from './Snackbar';
export Tabs from './Tabs';
export Tags from './Tags'; export Tags from './Tags';
export Title from './Title'; export Title from './Title';
export TxHash from './TxHash'; export TxHash from './TxHash';

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Menu, Segment } from 'semantic-ui-react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@ -22,12 +21,12 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { newError, openSnackbar } from '@parity/shared/redux/actions'; import { newError, openSnackbar } from '@parity/shared/redux/actions';
import { Button, IdentityName, IdentityIcon, Portal } from '@parity/ui'; import { Button, IdentityName, IdentityIcon, Portal, Tabs } from '@parity/ui';
import PasswordStrength from '@parity/ui/Form/PasswordStrength'; import PasswordStrength from '@parity/ui/Form/PasswordStrength';
import Form, { Input } from '@parity/ui/Form'; import Form, { Input } from '@parity/ui/Form';
import { CancelIcon, CheckIcon, SendIcon } from '@parity/ui/Icons'; import { CancelIcon, CheckIcon, SendIcon } from '@parity/ui/Icons';
import Store, { CHANGE_ACTION, TEST_ACTION } from './store'; import Store from './store';
import styles from './passwordManager.css'; import styles from './passwordManager.css';
const MSG_SUCCESS_STYLE = { const MSG_SUCCESS_STYLE = {
@ -37,12 +36,8 @@ const MSG_FAILURE_STYLE = {
backgroundColor: 'rgba(229, 115, 115, 0.75)' backgroundColor: 'rgba(229, 115, 115, 0.75)'
}; };
let MENU_CONTENT = 'TEST';
@observer @observer
class PasswordManager extends Component { class PasswordManager extends Component {
state = { activeItem: 'bio' };
static contextTypes = { static contextTypes = {
api: PropTypes.object.isRequired api: PropTypes.object.isRequired
} }
@ -132,40 +127,39 @@ class PasswordManager extends Component {
} }
renderPage () { renderPage () {
const { activeItem } = this.state; const { activeTab } = this.store;
return ( return (
<div> <div>
<Menu attached='top' tabular> <Tabs
<Menu.Item name='Test Password' active={ activeItem === 'Test Password' } onClick={ this.itemTestPassword } /> activeTab={ activeTab }
<Menu.Item name='Change Password' active={ activeItem === 'Change Password' } onClick={ this.itemChangePassword } /> tabs={ [
</Menu> <FormattedMessage
id='passwordChange.tabChange'
<Segment attached='bottom'> defaultMessage='Change Password'
{ MENU_CONTENT === 'TEST' ? this.renderTabTest() : this.renderTabChange() } />,
</Segment> <FormattedMessage
id='passwordChange.tabTest'
defaultMessage='TestPassword'
/>
] }
onChange={ this.onChangeTab }
/>
{
activeTab === 1
? this.renderTabTest()
: this.renderTabChange()
}
</div> </div>
); );
} }
itemTestPassword = (e, name) => { onChangeTab = (event, activeTab) => {
MENU_CONTENT = 'TEST'; this.store.setActiveTab(activeTab);
this.onActivateTestTab();
this.setState({ activeItem: name });
}
itemChangePassword = (e, name) => {
MENU_CONTENT = 'CHANGE';
this.onActivateChangeTab();
this.setState({ activeItem: name });
} }
renderTabTest () { renderTabTest () {
const { actionTab, busy } = this.store; const { busy } = this.store;
if (actionTab !== TEST_ACTION) {
return null;
}
return ( return (
<Form className={ styles.form }> <Form className={ styles.form }>
@ -196,11 +190,7 @@ class PasswordManager extends Component {
} }
renderTabChange () { renderTabChange () {
const { actionTab, busy, isRepeatValid, newPassword, passwordHint } = this.store; const { busy, isRepeatValid, newPassword, passwordHint } = this.store;
if (actionTab !== CHANGE_ACTION) {
return null;
}
return ( return (
<Form className={ styles.form }> <Form className={ styles.form }>
@ -300,7 +290,7 @@ class PasswordManager extends Component {
} }
renderDialogActions () { renderDialogActions () {
const { actionTab, busy, isRepeatValid } = this.store; const { activeTab, busy, isRepeatValid } = this.store;
const cancelBtn = ( const cancelBtn = (
<Button <Button
@ -332,7 +322,7 @@ class PasswordManager extends Component {
]; ];
} }
if (actionTab === TEST_ACTION) { if (activeTab === 1) {
return [ return [
cancelBtn, cancelBtn,
<Button <Button
@ -366,14 +356,6 @@ class PasswordManager extends Component {
]; ];
} }
onActivateChangeTab = () => {
this.store.setActionTab(CHANGE_ACTION);
}
onActivateTestTab = () => {
this.store.setActionTab(TEST_ACTION);
}
onEditCurrentPassword = (event, password) => { onEditCurrentPassword = (event, password) => {
this.store.setPassword(password); this.store.setPassword(password);
} }

View File

@ -16,11 +16,8 @@
import { action, computed, observable, transaction } from 'mobx'; import { action, computed, observable, transaction } from 'mobx';
const CHANGE_ACTION = 'CHANGE_ACTION';
const TEST_ACTION = 'TEST_ACTION';
export default class Store { export default class Store {
@observable actionTab = TEST_ACTION; @observable activeTab = 0;
@observable address = null; @observable address = null;
@observable busy = false; @observable busy = false;
@observable infoMessage = null; @observable infoMessage = null;
@ -44,9 +41,9 @@ export default class Store {
return this.newPasswordRepeat === this.newPassword; return this.newPasswordRepeat === this.newPassword;
} }
@action setActionTab = (actionTab) => { @action setActiveTab = (activeTab) => {
transaction(() => { transaction(() => {
this.actionTab = actionTab; this.activeTab = activeTab;
this.setInfoMessage(); this.setInfoMessage();
}); });
} }
@ -154,8 +151,3 @@ export default class Store {
}); });
} }
} }
export {
CHANGE_ACTION,
TEST_ACTION
};

View File

@ -129,9 +129,7 @@ export default class OptionsStep extends Component {
this.props.store.setRefundAddress(refundAddress); this.props.store.setRefundAddress(refundAddress);
} }
onSelectCoin = (event, data) => { onSelectCoin = (event, value) => {
const { value } = data;
this.props.store.setCoinSymbol(value); this.props.store.setCoinSymbol(value);
} }

View File

@ -103,7 +103,7 @@ describe('views/Account/Shapeshift/OptionsStep', () => {
}); });
it('sets the coinSymbol on the store', () => { it('sets the coinSymbol on the store', () => {
instance.onSelectCoin(null, { value: 'XMR' }); instance.onSelectCoin(null, 'XMR');
expect(store.setCoinSymbol).to.have.been.calledWith('XMR'); expect(store.setCoinSymbol).to.have.been.calledWith('XMR');
}); });
}); });

View File

@ -207,8 +207,8 @@ export default class Details extends Component {
); );
} }
onChangeToken = (event, data) => { onChangeToken = (event, token) => {
this.props.onChange('token', data.value); this.props.onChange('token', token);
} }
onEditSender = (event, sender) => { onEditSender = (event, sender) => {

View File

@ -178,7 +178,7 @@ export default class DetailsStep extends Component {
} }
return (func.abi.inputs || []).map((input, index) => { return (func.abi.inputs || []).map((input, index) => {
const onChange = (value) => onValueChange(null, index, value); const onChange = (event, value) => onValueChange(null, index, value);
const label = `${input.name}: ${input.type}`; const label = `${input.name}: ${input.type}`;
return ( return (
@ -200,7 +200,7 @@ export default class DetailsStep extends Component {
}); });
} }
onFuncChange = (event, index, signature) => { onFuncChange = (event, signature) => {
const { contract, onFuncChange } = this.props; const { contract, onFuncChange } = this.props;
onFuncChange(event, contract.functions.find((fn) => fn.signature === signature)); onFuncChange(event, contract.functions.find((fn) => fn.signature === signature));

View File

@ -58,23 +58,4 @@ describe('modals/ExecuteContract/DetailsStep', () => {
it('renders', () => { it('renders', () => {
expect(render({ accounts: {}, values: [ true ], valuesError: [ null ] })).to.be.ok; expect(render({ accounts: {}, values: [ true ], valuesError: [ null ] })).to.be.ok;
}); });
describe('parameter values', () => {
beforeEach(() => {
render({
accounts: {},
func: CONTRACT.functions[0],
values: [ false ],
valuesError: [ null ]
});
});
describe('bool parameters', () => {
it('toggles from false to true', () => {
component.find('TypedInput').last().shallow().simulate('change', { target: { value: 'true' } });
expect(onValueChange).to.have.been.calledWith(null, 0, true);
});
});
});
}); });

View File

@ -43,7 +43,7 @@ function render (props) {
return component; return component;
} }
describe('modals/ExecuteContract', () => { describe('views/Contract/ExecuteContract', () => {
it('renders', () => { it('renders', () => {
expect(render({ accounts: {} })).to.be.ok; expect(render({ accounts: {} })).to.be.ok;
}); });

View File

@ -14,27 +14,24 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Subheader, IconButton, Tabs, Tab } from 'material-ui';
import { List, ListItem, makeSelectable } from 'material-ui/List';
import moment from 'moment'; import moment from 'moment';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Button, Portal } from '@parity/ui'; import { Button, List, Portal, Tabs } from '@parity/ui';
import Editor from '@parity/ui/Editor'; import Editor from '@parity/ui/Editor';
import { CancelIcon, CheckIcon, DeleteIcon } from '@parity/ui/Icons'; import { CancelIcon, CheckIcon, DeleteIcon } from '@parity/ui/Icons';
import styles from './loadContract.css'; import styles from './loadContract.css';
const SelectableList = makeSelectable(List);
const REMOVAL_STYLE = { const REMOVAL_STYLE = {
backgroundColor: 'none', backgroundColor: 'none',
cursor: 'default' cursor: 'default'
}; };
const SELECTED_STYLE = {
backgroundColor: 'rgba(255, 255, 255, 0.1)' // const SELECTED_STYLE = {
}; // backgroundColor: 'rgba(255, 255, 255, 0.1)'
// };
export default class LoadContract extends Component { export default class LoadContract extends Component {
static propTypes = { static propTypes = {
@ -46,6 +43,7 @@ export default class LoadContract extends Component {
}; };
state = { state = {
activeTab: -1,
selected: -1, selected: -1,
deleteRequest: false, deleteRequest: false,
deleteId: -1 deleteId: -1
@ -81,7 +79,9 @@ export default class LoadContract extends Component {
} }
renderBody () { renderBody () {
if (this.state.deleteRequest) { const { activeTab, deleteRequest } = this.state;
if (deleteRequest) {
return this.renderConfirmRemoval(); return this.renderConfirmRemoval();
} }
@ -89,51 +89,57 @@ export default class LoadContract extends Component {
const contractsTab = Object.keys(contracts).length === 0 const contractsTab = Object.keys(contracts).length === 0
? null ? null
: ( : (
<Tab <FormattedMessage
label={ id='loadContract.tab.local'
<FormattedMessage defaultMessage='Local'
id='loadContract.tab.local' />
defaultMessage='Local'
/>
}
>
{ this.renderEditor() }
<SelectableList onChange={ this.onClickContract }>
<Subheader>
<FormattedMessage
id='loadContract.header.saved'
defaultMessage='Saved Contracts'
/>
</Subheader>
{ this.renderContracts(contracts) }
</SelectableList>
</Tab>
); );
return ( return (
<div className={ styles.loadContainer }> <div className={ styles.loadContainer }>
<Tabs onChange={ this.handleChangeTab }> <Tabs
{ contractsTab } activeTab={ activeTab }
<Tab onChange={ this.handleChangeTab }
label={ tabs={ [
<FormattedMessage contractsTab,
id='loadContract.tab.snippets' <FormattedMessage
defaultMessage='Snippets' id='loadContract.tab.snippets'
defaultMessage='Snippets'
/>
] }
/>
{ this.renderEditor() }
{
this.state.activeTab === 0
? (
<List
label={
<h4>
<FormattedMessage
id='loadContract.header.saved'
defaultMessage='Saved Contracts'
/>
</h4>
}
onChange={ this.onClickContract }
items={ this.renderContracts(contracts) }
/> />
} )
> : (
{ this.renderEditor() } <List
<SelectableList onChange={ this.onClickContract }> label={
<Subheader> <h4>
<FormattedMessage <FormattedMessage
id='loadContract.header.snippets' id='loadContract.header.snippets'
defaultMessage='Contract Snippets' defaultMessage='Contract Snippets'
/> />
</Subheader> </h4>
{ this.renderContracts(snippets, false) } }
</SelectableList> onClick={ this.onClickContract }
</Tab> items={ this.renderContracts(snippets, false) }
</Tabs> />
)
}
</div> </div>
); );
} }
@ -150,9 +156,9 @@ export default class LoadContract extends Component {
defaultMessage='Are you sure you want to remove the following contract from your saved contracts?' defaultMessage='Are you sure you want to remove the following contract from your saved contracts?'
/> />
</p> </p>
<ListItem <List.Item
primaryText={ name } label={ name }
secondaryText={ description={
<FormattedMessage <FormattedMessage
id='loadContract.removal.savedAt' id='loadContract.removal.savedAt'
defaultMessage='Saved {when}' defaultMessage='Saved {when}'
@ -200,46 +206,31 @@ export default class LoadContract extends Component {
} }
renderContracts (contracts, removable = true) { renderContracts (contracts, removable = true) {
const { selected } = this.state;
return Object return Object
.values(contracts) .values(contracts)
.map((contract) => { .map((contract) => {
const { id, name, timestamp, description } = contract; const { id, name, timestamp, description } = contract;
const onDelete = () => this.onDeleteRequest(id); const onDelete = () => this.onDeleteRequest(id);
return ( return {
<ListItem key: id,
key={ id } label: name,
primaryText={ name } buttons: removable && (
rightIconButton={ <Button
removable icon={ <DeleteIcon /> }
? ( onClick={ onDelete }
<IconButton onTouchTap={ onDelete }> />
<DeleteIcon /> ),
</IconButton> description: description || (
) <FormattedMessage
: null id='loadContract.contract.savedAt'
} defaultMessage='Saved {when}'
secondaryText={ values={ {
description || ( when: moment(timestamp).fromNow()
<FormattedMessage } }
id='loadContract.contract.savedAt' />
defaultMessage='Saved {when}' )
values={ { };
when: moment(timestamp).fromNow()
} }
/>
)
}
style={
selected === id
? SELECTED_STYLE
: null
}
value={ id }
/>
);
}); });
} }
@ -300,8 +291,8 @@ export default class LoadContract extends Component {
]; ];
} }
handleChangeTab = () => { handleChangeTab = (event, activeTab) => {
this.setState({ selected: -1 }); this.setState({ activeTab, selected: -1 });
} }
onClickContract = (event, selected) => { onClickContract = (event, selected) => {

View File

@ -179,7 +179,7 @@ export default class ContractDevelopStore {
this.reloadContracts(-1, sourcecode); this.reloadContracts(-1, sourcecode);
} }
@action handleSelectBuild = (_, index, value) => { @action handleSelectBuild = (event, value) => {
this.selectedBuild = value; this.selectedBuild = value;
return this return this
.loadSolidityVersion(this.builds[value]) .loadSolidityVersion(this.builds[value])
@ -282,7 +282,7 @@ export default class ContractDevelopStore {
this.showSaveModal = false; this.showSaveModal = false;
} }
@action handleSelectContract = (_, index, value) => { @action handleSelectContract = (event, value) => {
this.contractIndex = value; this.contractIndex = value;
this.contract = this.contracts[Object.keys(this.contracts)[value]]; this.contract = this.contracts[Object.keys(this.contracts)[value]];
} }

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Menu } from 'semantic-ui-react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
@ -67,7 +66,7 @@ export default class Node extends Component {
); );
} }
renderItem (name, func, label) { renderItem (name, label) {
return { return {
key: name, key: name,
text: name, text: name,
@ -129,46 +128,43 @@ export default class Node extends Component {
return ( return (
<div className={ layout.option }> <div className={ layout.option }>
<FormattedMessage <Dropdown
id='settings.parity.modes.hint'
defaultMessage='Choose the syncing mode for the Parity node'
/>
<Menu
vertical
id='parityModeSelect' id='parityModeSelect'
label={
<FormattedMessage
id='settings.parity.modes.hint'
defaultMessage='Choose the syncing mode for the Parity node'
/>
}
onChange={ this.onChangeMode }
value={ mode } value={ mode }
> options={ [
<Dropdown this.renderItem('active', (
item <FormattedMessage
text={ mode } id='settings.parity.modes.mode_active'
options={ [ defaultMessage='Continuously sync'
this.renderItem('active', this.onChangeMode, ( />
<FormattedMessage )),
id='settings.parity.modes.mode_active' this.renderItem('passive', (
defaultMessage='Continuously sync' <FormattedMessage
/> id='settings.parity.modes.mode_passive'
)), defaultMessage='Sync on intervals'
this.renderItem('passive', this.onChangeMode, ( />
<FormattedMessage )),
id='settings.parity.modes.mode_passive' this.renderItem('dark', (
defaultMessage='Sync on intervals' <FormattedMessage
/> id='settings.parity.modes.mode_dark'
)), defaultMessage='Sync when RPC is active'
this.renderItem('dark', this.onChangeMode, ( />
<FormattedMessage )),
id='settings.parity.modes.mode_dark' this.renderItem('offline', (
defaultMessage='Sync when RPC is active' <FormattedMessage
/> id='settings.parity.modes.mode_offline'
)), defaultMessage='No Syncing'
this.renderItem('offline', this.onChangeMode, ( />
<FormattedMessage ))
id='settings.parity.modes.mode_offline' ] }
defaultMessage='No Syncing' />
/>
))
] }
/>
</Menu>
</div> </div>
); );
} }
@ -178,78 +174,76 @@ export default class Node extends Component {
return ( return (
<div className={ layout.option }> <div className={ layout.option }>
<FormattedMessage <Dropdown
id='settings.parity.chains.hint'
defaultMessage='Choose the chain for the Parity node to sync to'
/>
<Menu
vertical
id='parityChainSelect' id='parityChainSelect'
label={
<FormattedMessage
id='settings.parity.chains.hint'
defaultMessage='Choose the chain for the Parity node to sync to'
/>
}
onChange={ this.onChangeChain }
value={ chain } value={ chain }
> options={ [
<Dropdown this.renderItem('foundation', (
text={ chain } <FormattedMessage
options={ [ id='settings.parity.chains.chain_foundation'
this.renderItem('foundation', this.onChangeChain, ( defaultMessage='Ethereum Foundation'
<FormattedMessage />
id='settings.parity.chains.chain_foundation' )),
defaultMessage='Ethereum Foundation' this.renderItem('kovan', (
/> <FormattedMessage
)), id='settings.parity.chains.chain_kovan'
this.renderItem('kovan', this.onChangeChain, ( defaultMessage='Kovan test network'
<FormattedMessage />
id='settings.parity.chains.chain_kovan' )),
defaultMessage='Kovan test network' this.renderItem('olympic', (
/> <FormattedMessage
)), id='settings.parity.chains.chain_olympic'
this.renderItem('olympic', this.onChangeChain, ( defaultMessage='Olympic test network'
<FormattedMessage />
id='settings.parity.chains.chain_olympic' )),
defaultMessage='Olympic test network' this.renderItem('morden', (
/> <FormattedMessage
)), id='settings.parity.chains.cmorden_kovan'
this.renderItem('morden', this.onChangeChain, ( defaultMessage='Morden (Classic) test network'
<FormattedMessage />
id='settings.parity.chains.cmorden_kovan' )),
defaultMessage='Morden (Classic) test network' this.renderItem('ropsten', (
/> <FormattedMessage
)), id='settings.parity.chains.chain_ropsten'
this.renderItem('ropsten', this.onChangeChain, ( defaultMessage='Ropsten test network'
<FormattedMessage />
id='settings.parity.chains.chain_ropsten' )),
defaultMessage='Ropsten test network' this.renderItem('classic', (
/> <FormattedMessage
)), id='settings.parity.chains.chain_classic'
this.renderItem('classic', this.onChangeChain, ( defaultMessage='Ethereum Classic network'
<FormattedMessage />
id='settings.parity.chains.chain_classic' )),
defaultMessage='Ethereum Classic network' this.renderItem('expanse', (
/> <FormattedMessage
)), id='settings.parity.chains.chain_expanse'
this.renderItem('expanse', this.onChangeChain, ( defaultMessage='Expanse network'
<FormattedMessage />
id='settings.parity.chains.chain_expanse' )),
defaultMessage='Expanse network' this.renderItem('dev', (
/> <FormattedMessage
)), id='settings.parity.chains.chain_dev'
this.renderItem('dev', this.onChangeChain, ( defaultMessage='Local development chain'
<FormattedMessage />
id='settings.parity.chains.chain_dev' ))
defaultMessage='Local development chain' ] }
/> />
))
] }
/>
</Menu>
</div> </div>
); );
} }
onChangeMode = (e, mode) => { onChangeMode = (event, mode) => {
this.store.changeMode(mode.name); this.store.changeMode(mode.name);
} }
onChangeChain = (e, chain) => { onChangeChain = (event, chain) => {
this.store.changeChain(chain.name); this.store.changeChain(chain.name);
} }
} }

View File

@ -84,7 +84,7 @@ describe('views/Settings/Node', () => {
let select; let select;
beforeEach(() => { beforeEach(() => {
select = component.find('Menu[id="parityModeSelect"]'); select = component.find('Dropdown[id="parityModeSelect"]');
sinon.spy(instance.store, 'changeMode'); sinon.spy(instance.store, 'changeMode');
}); });
@ -101,7 +101,7 @@ describe('views/Settings/Node', () => {
let select; let select;
beforeEach(() => { beforeEach(() => {
select = component.find('Menu[id="parityChainSelect"]'); select = component.find('Dropdown[id="parityChainSelect"]');
sinon.spy(instance.store, 'changeChain'); sinon.spy(instance.store, 'changeChain');
}); });

View File

@ -16,15 +16,16 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { Menu } from 'semantic-ui-react';
import { Page } from '@parity/ui'; import { Page, Tabs } from '@parity/ui';
import { BackgroundIcon, EthernetIcon, VisibleIcon } from '@parity/ui/Icons'; import { BackgroundIcon, EthernetIcon, VisibleIcon } from '@parity/ui/Icons';
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg'; import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
import styles from './settings.css'; import styles from './settings.css';
const TABS = ['views', 'background', 'proxy', 'parity'];
export default class Settings extends Component { export default class Settings extends Component {
static contextTypes = { static contextTypes = {
router: PropTypes.object.isRequired router: PropTypes.object.isRequired
@ -36,22 +37,36 @@ export default class Settings extends Component {
render () { render () {
const { children } = this.props; const { children } = this.props;
const hash = (window.location.hash || '').split('?')[0].split('/')[2]; const hash = (window.location.hash || '').split('?')[0].split('/')[1];
const isProxied = window.location.hostname.indexOf('.parity') !== -1; const isProxied = window.location.hostname.indexOf('.parity') !== -1;
let proxy = null;
if (!isProxied) {
proxy = this.renderTab(hash, 'proxy', <EthernetIcon />);
}
return ( return (
<div> <div>
<Menu className={ styles.tabs } name={ hash }> <Tabs
{ this.renderTab(hash, 'views', <VisibleIcon />) } activeTab={ TABS.indexOf(hash) }
{ this.renderTab(hash, 'background', <BackgroundIcon />) } className={ styles.tabs }
{ proxy } onChange={ this.onActivate }
{ this.renderTab(hash, 'parity', <img src={ imagesEthcoreBlock } className={ styles.imageIcon } />) } tabs={ [
</Menu> {
icon: <VisibleIcon />,
label: <FormattedMessage id='settings.views.label' />
},
{
icon: <BackgroundIcon />,
label: <FormattedMessage id='settings.background.label' />
},
isProxied
? null
: {
icon: <EthernetIcon />,
label: <FormattedMessage id='settings.proxy.label' />
},
{
icon: <img src={ imagesEthcoreBlock } className={ styles.imageIcon } />,
label: <FormattedMessage id='settings.parity.label' />
}
] }
/>
<Page> <Page>
{ children } { children }
</Page> </Page>
@ -59,32 +74,9 @@ export default class Settings extends Component {
); );
} }
renderTab (hash, name, icon) { onActivate = (event, tabIndex) => {
return (
<Menu.Item
className={
hash === name
? styles.tabactive
: styles.tab
}
icon={ icon }
key={ name }
content={
<div className={ styles.menu }>
<FormattedMessage id={ `settings.${name}.label` } />
</div>
}
onClick={ this.onActivate(name) }
name={ name }
/>
);
}
onActivate = (name) => {
const { router } = this.context; const { router } = this.context;
return (event) => { router.push(`/${TABS[tabIndex]}`);
router.push(`/${name}`);
};
} }
} }

View File

@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { MenuItem, IconMenu } from 'material-ui';
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import ReactTooltip from 'react-tooltip'; import ReactTooltip from 'react-tooltip';
@ -23,7 +22,7 @@ import { bindActionCreators } from 'redux';
import { bytesToHex } from '@parity/api/util/format'; import { bytesToHex } from '@parity/api/util/format';
import { confirmOperation, revokeOperation } from '@parity/shared/redux/providers/walletActions'; import { confirmOperation, revokeOperation } from '@parity/shared/redux/providers/walletActions';
import { Button, Container, InputAddress, IdentityIcon, Progress } from '@parity/ui'; import { Container, Dropdown, InputAddress, IdentityIcon, Progress } from '@parity/ui';
import TxRow from '@parity/ui/TxList/TxRow'; import TxRow from '@parity/ui/TxList/TxRow';
import styles from '../wallet.css'; import styles from '../wallet.css';
@ -89,14 +88,13 @@ class WalletConfirmations extends Component {
); );
} }
return realConfirmations return realConfirmations.map((confirmation) => (
.map((confirmation) => ( <WalletConfirmation
<WalletConfirmation key={ confirmation.operation }
key={ confirmation.operation } confirmation={ confirmation }
confirmation={ confirmation } { ...others }
{ ...others } />
/> ));
));
} }
} }
@ -130,13 +128,9 @@ class WalletConfirmation extends Component {
revokeOperation: PropTypes.func.isRequired revokeOperation: PropTypes.func.isRequired
}; };
state = {
openConfirm: false,
openRevoke: false
};
render () { render () {
const { confirmation } = this.props; const { confirmation } = this.props;
const { pending } = confirmation;
const confirmationsRows = []; const confirmationsRows = [];
const className = styles.light; const className = styles.light;
@ -158,65 +152,30 @@ class WalletConfirmation extends Component {
{ confirmationsRows } { confirmationsRows }
</tbody> </tbody>
</table> </table>
{ this.renderPending() } {
pending && (
<div className={ styles.pendingOverlay } />
)
}
</div> </div>
); );
} }
renderPending () { handleConfirm = (event, owner) => {
const { pending } = this.props.confirmation;
if (!pending) {
return null;
}
return (
<div className={ styles.pendingOverlay } />
);
}
handleOpenConfirm = () => {
this.setState({
openConfirm: true
});
}
handleCloseConfirm = () => {
this.setState({
openConfirm: false
});
}
handleOpenRevoke = () => {
this.setState({
openRevoke: true
});
}
handleCloseRevoke = () => {
this.setState({
openRevoke: false
});
}
handleConfirm = (e, item) => {
const { confirmOperation, confirmation, address } = this.props; const { confirmOperation, confirmation, address } = this.props;
const owner = item.props.value;
confirmOperation(address, owner, confirmation.operation); confirmOperation(address, owner, confirmation.operation);
} }
handleRevoke = (e, item) => { handleRevoke = (event, owner) => {
const { revokeOperation, confirmation, address } = this.props; const { revokeOperation, confirmation, address } = this.props;
const owner = item.props.value;
revokeOperation(address, owner, confirmation.operation); revokeOperation(address, owner, confirmation.operation);
} }
renderActions (confirmation, className) { renderActions (confirmation, className) {
const { owners, accounts } = this.props; const { owners, accounts } = this.props;
const { operation, confirmedBy, pending } = confirmation; const { operation, confirmedBy } = confirmation;
const { openConfirm, openRevoke } = this.state;
const addresses = Object.keys(accounts); const addresses = Object.keys(accounts);
@ -228,32 +187,6 @@ class WalletConfirmation extends Component {
.filter((owner) => addresses.includes(owner)) .filter((owner) => addresses.includes(owner))
.filter((owner) => confirmedBy.includes(owner)); .filter((owner) => confirmedBy.includes(owner));
const confirmButton = (
<Button
onClick={ this.handleOpenConfirm }
label={
<FormattedMessage
id='wallet.confirmations.buttons.confirmAs'
defaultMessage='Confirm As...'
/>
}
disabled={ pending || possibleConfirm.length === 0 }
/>
);
const revokeButton = (
<Button
onClick={ this.handleOpenRevoke }
label={
<FormattedMessage
id='wallet.confirmations.buttons.revokeAs'
defaultMessage='Revoke As...'
/>
}
disabled={ pending || possibleRevoke.length === 0 }
/>
);
return ( return (
<tr <tr
className={ className } className={ className }
@ -262,23 +195,30 @@ class WalletConfirmation extends Component {
<td /> <td />
<td colSpan={ 3 }> <td colSpan={ 3 }>
<div className={ styles.actions }> <div className={ styles.actions }>
<IconMenu <Dropdown
iconButtonElement={ confirmButton } label={
open={ openConfirm } <FormattedMessage
onRequestChange={ this.handleCloseConfirm } id='wallet.confirmations.buttons.confirmAs'
onItemTouchTap={ this.handleConfirm } defaultMessage='Confirm As...'
> />
{ possibleConfirm.map((address) => this.renderAccountItem(address)) } }
</IconMenu> onChange={ this.handleConfirm }
options={
<IconMenu possibleConfirm.map((address) => this.renderAccountItem(address))
iconButtonElement={ revokeButton } }
open={ openRevoke } />
onRequestChange={ this.handleCloseRevoke } <Dropdown
onItemTouchTap={ this.handleRevoke } label={
> <FormattedMessage
{ possibleRevoke.map((address) => this.renderAccountItem(address)) } id='wallet.confirmations.buttons.revokeAs'
</IconMenu> defaultMessage='Revoke As...'
/>
}
onChange={ this.handleRevoke }
oprions={
possibleRevoke.map((address) => this.renderAccountItem(address))
}
/>
</div> </div>
</td> </td>
<td /> <td />
@ -288,22 +228,22 @@ class WalletConfirmation extends Component {
renderAccountItem (address) { renderAccountItem (address) {
const account = this.props.accounts[address]; const account = this.props.accounts[address];
const name = account.name.toUpperCase() || account.address;
return ( return {
<MenuItem key: address,
key={ address } label: (
value={ address }
>
<div className={ styles.accountItem }> <div className={ styles.accountItem }>
<IdentityIcon <IdentityIcon
address={ address } address={ address }
center center
inline inline
/> />
<span>{ account.name.toUpperCase() || account.address }</span> <span>{ name }</span>
</div> </div>
</MenuItem> ),
); value: name
};
} }
renderProgress (confirmation) { renderProgress (confirmation) {
@ -398,18 +338,6 @@ class WalletConfirmation extends Component {
renderConfirmedBy (confirmation, className) { renderConfirmedBy (confirmation, className) {
const { operation, confirmedBy } = confirmation; const { operation, confirmedBy } = confirmation;
const confirmed = confirmedBy.map((owner) => (
<InputAddress
key={ owner }
value={ owner }
allowCopy={ false }
hideUnderline
disabled
small
text
/>
));
return ( return (
<tr key={ `details_${operation}` } className={ className }> <tr key={ `details_${operation}` } className={ className }>
<td colSpan={ 5 } style={ { padding: 0 } }> <td colSpan={ 5 } style={ { padding: 0 } }>
@ -419,7 +347,19 @@ class WalletConfirmation extends Component {
data-effect='solid' data-effect='solid'
className={ styles.confirmed } className={ styles.confirmed }
> >
{ confirmed } {
confirmedBy.map((owner) => (
<InputAddress
key={ owner }
value={ owner }
allowCopy={ false }
hideUnderline
disabled
small
text
/>
))
}
</div> </div>
</td> </td>
</tr> </tr>