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';
export default function DappIcon ({ app, className, small }, context) {
const { dappsUrl } = context.api;
export default function DappIcon ({ app, className, small }, { api }) {
const { dappsUrl } = api;
return (
<img

View File

@ -26,7 +26,9 @@ import styles from './dropdown.css';
const NAME_ID = ' ';
// 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 (
<LabelComponent label={ label }>
<SemanticDropdown
@ -37,7 +39,7 @@ export default function Dropdown ({ className, disabled = false, error, fullWidt
id={ NAME_ID }
name={ NAME_ID }
onBlur={ onBlur }
onChange={ onChange }
onChange={ _onChange }
onKeyDown={ onKeyDown }
options={ options }
placeholder={ parseI18NString(context, hint) }

View File

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

View File

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

View File

@ -60,10 +60,5 @@ describe('LanguageSelector', () => {
it('has locale items', () => {
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/>.
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) {
return null;
}
return (
<div className={ styles.steps }>
<Stepper activeStep={ activeStep }>
<div className={ className }>
<SemanticStep.Group ordered>
{
steps.map((label, index) => {
return (
<Step key={ label.key || index }>
<StepLabel>
{ label }
</StepLabel>
</Step>
);
})
steps.map((label, index) => (
<Step
isActive={ activeStep === index }
isCompleted={ activeStep > index }
key={ label.key || index }
label={ label.label || label }
/>
))
}
</Stepper>
</SemanticStep.Group>
</div>
);
}
Steps.Step = Step;
Steps.propTypes = {
activeStep: PropTypes.number,
className: PropTypes.string,
steps: PropTypes.array
};

View File

@ -31,12 +31,12 @@ function render (props = {}) {
return component;
}
describe('ui/Title/Steps', () => {
describe('ui/Steps', () => {
beforeEach(() => {
render({ steps: ['stepA', 'stepB'] });
});
it('renders the Stepper', () => {
expect(component.find('Stepper').get(0)).to.be.ok;
it('renders the defaults', () => {
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 styles from '../title.css';
export default function Waiting ({ activeStep, busy, busySteps }) {
export default function Waiting ({ activeStep, busy, busySteps, className }) {
const isWaiting = busy || (busySteps || []).includes(activeStep);
if (!isWaiting) {
@ -28,7 +26,7 @@ export default function Waiting ({ activeStep, busy, busySteps }) {
}
return (
<div className={ styles.waiting }>
<div className={ className }>
<Progress />
</div>
);
@ -37,5 +35,6 @@ export default function Waiting ({ activeStep, busy, busySteps }) {
Waiting.propTypes = {
activeStep: PropTypes.number,
busy: PropTypes.bool,
busySteps: PropTypes.array
busySteps: PropTypes.array,
className: PropTypes.string
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -16,11 +16,8 @@
import { action, computed, observable, transaction } from 'mobx';
const CHANGE_ACTION = 'CHANGE_ACTION';
const TEST_ACTION = 'TEST_ACTION';
export default class Store {
@observable actionTab = TEST_ACTION;
@observable activeTab = 0;
@observable address = null;
@observable busy = false;
@observable infoMessage = null;
@ -44,9 +41,9 @@ export default class Store {
return this.newPasswordRepeat === this.newPassword;
}
@action setActionTab = (actionTab) => {
@action setActiveTab = (activeTab) => {
transaction(() => {
this.actionTab = actionTab;
this.activeTab = activeTab;
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);
}
onSelectCoin = (event, data) => {
const { value } = data;
onSelectCoin = (event, value) => {
this.props.store.setCoinSymbol(value);
}

View File

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

View File

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

View File

@ -178,7 +178,7 @@ export default class DetailsStep extends Component {
}
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}`;
return (
@ -200,7 +200,7 @@ export default class DetailsStep extends Component {
});
}
onFuncChange = (event, index, signature) => {
onFuncChange = (event, signature) => {
const { contract, onFuncChange } = this.props;
onFuncChange(event, contract.functions.find((fn) => fn.signature === signature));

View File

@ -58,23 +58,4 @@ describe('modals/ExecuteContract/DetailsStep', () => {
it('renders', () => {
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;
}
describe('modals/ExecuteContract', () => {
describe('views/Contract/ExecuteContract', () => {
it('renders', () => {
expect(render({ accounts: {} })).to.be.ok;
});

View File

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

View File

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

View File

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

View File

@ -16,15 +16,16 @@
import React, { Component, PropTypes } from 'react';
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 imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
import styles from './settings.css';
const TABS = ['views', 'background', 'proxy', 'parity'];
export default class Settings extends Component {
static contextTypes = {
router: PropTypes.object.isRequired
@ -36,22 +37,36 @@ export default class Settings extends Component {
render () {
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;
let proxy = null;
if (!isProxied) {
proxy = this.renderTab(hash, 'proxy', <EthernetIcon />);
}
return (
<div>
<Menu className={ styles.tabs } name={ hash }>
{ this.renderTab(hash, 'views', <VisibleIcon />) }
{ this.renderTab(hash, 'background', <BackgroundIcon />) }
{ proxy }
{ this.renderTab(hash, 'parity', <img src={ imagesEthcoreBlock } className={ styles.imageIcon } />) }
</Menu>
<Tabs
activeTab={ TABS.indexOf(hash) }
className={ styles.tabs }
onChange={ this.onActivate }
tabs={ [
{
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>
{ children }
</Page>
@ -59,32 +74,9 @@ export default class Settings extends Component {
);
}
renderTab (hash, name, icon) {
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) => {
onActivate = (event, tabIndex) => {
const { router } = this.context;
return (event) => {
router.push(`/${name}`);
};
router.push(`/${TABS[tabIndex]}`);
}
}

View File

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