Ui 2 additional MUI replacements (#5623)

* Loader spinner

* Progressbar conversion

* Update Progress

* Transfer operational again

* Remove List/ListItem from features

* Remove extra animation

* Remove iconButton

* Remove underline(input to be replaced)

* Convert Sort to Popup

* Remove unused AutoComplete component

* Simplify ModalBox

* Allow empty Actionbar

* Adjust shapeshift icon

* Simplify MUI theme (before removal)

* Update tests
This commit is contained in:
Jaco Greeff 2017-05-15 14:49:47 +02:00 committed by GitHub
parent c5fa9844f2
commit c27d96a4f1
45 changed files with 208 additions and 727 deletions

View File

@ -101,8 +101,12 @@ class Requests extends Component {
: ( : (
<Progress <Progress
max={ 6 } max={ 6 }
mode={ state.type === WAITING_STATE ? 'indeterminate' : 'determinate' } isDeterminate={ state.type !== WAITING_STATE }
value={ state.type === DONE_STATE ? +request.blockHeight : 6 } value={
state.type === DONE_STATE
? +request.blockHeight
: 6
}
/> />
) )
} }

View File

@ -1,20 +0,0 @@
/* 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/>.
*/
.sortButton {
min-width: 50px !important;
}

View File

@ -18,14 +18,12 @@ import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import IconMenu from 'material-ui/IconMenu';
import MenuItem from 'material-ui/MenuItem';
import Button from '~/ui/Button'; import Button from '~/ui/Button';
import { SortIcon } from '~/ui/Icons'; import { SortIcon } from '~/ui/Icons';
import List from '~/ui/List';
import Popup from '~/ui/Popup';
import SortStore from './sortStore'; import SortStore from './sortStore';
import styles from './sort.css';
@observer @observer
export default class ActionbarSort extends Component { export default class ActionbarSort extends Component {
@ -53,67 +51,52 @@ export default class ActionbarSort extends Component {
const { showDefault } = this.props; const { showDefault } = this.props;
return ( return (
<IconMenu <Popup
iconButtonElement={ isOpen={ this.store.menuOpen }
trigger={
<Button <Button
className={ styles.sortButton }
label=''
icon={ <SortIcon /> } icon={ <SortIcon /> }
onClick={ this.store.handleMenuOpen } onClick={ this.store.handleMenuOpen }
/> />
} }
open={ this.store.menuOpen }
onRequestChange={ this.store.handleMenuChange }
onItemTouchTap={ this.store.handleSortChange }
targetOrigin={ { horizontal: 'right', vertical: 'top' } }
anchorOrigin={ { horizontal: 'right', vertical: 'top' } }
touchTapCloseDelay={ 0 }
> >
{ <List
showDefault items={ [
? this.renderMenuItem('', ( showDefault && this.renderMenuItem('', (
<FormattedMessage <FormattedMessage
id='ui.actionbar.sort.typeDefault' id='ui.actionbar.sort.typeDefault'
defaultMessage='Default' defaultMessage='Default'
/> />
)) )),
: null
}
{
this.renderMenuItem('tags', ( this.renderMenuItem('tags', (
<FormattedMessage <FormattedMessage
id='ui.actionbar.sort.typeTags' id='ui.actionbar.sort.typeTags'
defaultMessage='Sort by tags' defaultMessage='Sort by tags'
/> />
)) )),
}
{
this.renderMenuItem('name', ( this.renderMenuItem('name', (
<FormattedMessage <FormattedMessage
id='ui.actionbar.sort.typeName' id='ui.actionbar.sort.typeName'
defaultMessage='Sort by name' defaultMessage='Sort by name'
/> />
)) )),
}
{
this.renderMenuItem('eth', ( this.renderMenuItem('eth', (
<FormattedMessage <FormattedMessage
id='ui.actionbar.sort.typeEth' id='ui.actionbar.sort.typeEth'
defaultMessage='Sort by ETH' defaultMessage='Sort by ETH'
/> />
)) ))
} ].concat(this.renderSortByMetas()) }
onClick={ this.store.handleSortChange }
{ this.renderSortByMetas() } />
</IconMenu> </Popup>
); );
} }
renderSortByMetas () { renderSortByMetas () {
const { metas } = this.props; const { metas } = this.props;
return metas return metas.map((meta) => {
.map((meta, index) => {
const label = ( const label = (
<FormattedMessage <FormattedMessage
id='ui.actionbar.sort.sortBy' id='ui.actionbar.sort.sortBy'
@ -124,31 +107,17 @@ export default class ActionbarSort extends Component {
/> />
); );
return this.renderMenuItem(meta.key, label, index); return this.renderMenuItem(meta.key, label);
}); });
} }
renderMenuItem (value, label, key = null) { renderMenuItem (key, label) {
const { order } = this.props; const { order } = this.props;
const props = {}; return {
isActive: order === key,
if (key !== null) { key,
props.key = key; label
} };
const checked = order === value;
return (
<MenuItem
checked={ checked }
value={ value }
primaryText={ label }
innerDivStyle={ {
paddingLeft: checked ? 50 : 16
} }
{ ...props }
/>
);
} }
} }

View File

@ -37,9 +37,8 @@ export default class SortStore {
this.menuOpen = open; this.menuOpen = open;
} }
@action handleSortChange = (event, child) => { @action handleSortChange = (event, order) => {
const order = child.props.value; this.handleMenuChange(false);
this.onChange(order); this.onChange(order);
this.saveOrder(order); this.saveOrder(order);
} }

View File

@ -21,6 +21,10 @@ import { nodeOrStringProptype } from '@parity/shared/util/proptypes';
import styles from './actionbar.css'; import styles from './actionbar.css';
export default function Actionbar ({ buttons, children, className, title }) { export default function Actionbar ({ buttons, children, className, title }) {
if (!buttons && !title) {
return null;
}
return ( return (
<div className={ `${styles.actionbar} ${className}` }> <div className={ `${styles.actionbar} ${className}` }>
<h3 className={ styles.title }> <h3 className={ styles.title }>

View File

@ -21,7 +21,10 @@ import Actionbar from './actionbar';
function renderShallow (props) { function renderShallow (props) {
return shallow( return shallow(
<Actionbar { ...props } /> <Actionbar
title='title'
{ ...props }
/>
); );
} }

View File

@ -15,10 +15,10 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { List, ListItem } from 'material-ui/List';
import React, { Component } from 'react'; import React, { Component } from 'react';
import Checkbox from '~/ui/Form/Checkbox'; import Checkbox from '~/ui/Form/Checkbox';
import List from '~/ui/List';
import defaults, { MODES } from './defaults'; import defaults, { MODES } from './defaults';
import Store from './store'; import Store from './store';
@ -34,14 +34,14 @@ export default class Features extends Component {
} }
return ( return (
<List> <List
{ items={
Object Object
.keys(defaults) .keys(defaults)
.filter((key) => defaults[key].mode !== MODES.PRODUCTION) .filter((key) => defaults[key].mode !== MODES.PRODUCTION)
.map(this.renderItem) .map(this.renderItem)
} }
</List> />
); );
} }
@ -49,22 +49,22 @@ export default class Features extends Component {
const feature = defaults[key]; const feature = defaults[key];
const onCheck = () => this.store.toggleActive(key); const onCheck = () => this.store.toggleActive(key);
return ( return {
<ListItem description: (
key={ `feature_${key}` } <div className={ styles.description }>
leftCheckbox={ { feature.description }
</div>
),
key,
label: (
<div>
<Checkbox <Checkbox
checked={ this.store.active[key] } checked={ this.store.active[key] }
onClick={ onCheck } onClick={ onCheck }
/> />
} <span>{ feature.name }</span>
primaryText={ feature.name }
secondaryText={
<div className={ styles.description }>
{ feature.description }
</div> </div>
} )
/> };
);
} }
} }

View File

@ -33,7 +33,7 @@ function render (props = { visible: true }) {
return component; return component;
} }
describe('views/Settings/Features', () => { describe('ui/Features', () => {
beforeEach(() => { beforeEach(() => {
render(); render();
}); });
@ -80,10 +80,6 @@ describe('views/Settings/Features', () => {
it('renders an item', () => { it('renders an item', () => {
expect(item).not.to.be.null; expect(item).not.to.be.null;
}); });
it('displays the correct name', () => {
expect(item.props.primaryText).to.equal(defaults[key].name);
});
}); });
}); });
}); });

View File

@ -20,7 +20,6 @@ import { connect } from 'react-redux';
import keycode, { codes } from 'keycode'; import keycode, { codes } from 'keycode';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import TextFieldUnderline from 'material-ui/TextField/TextFieldUnderline';
import apiutil from '@parity/api/util'; import apiutil from '@parity/api/util';
import { nodeOrStringProptype } from '@parity/shared/util/proptypes'; import { nodeOrStringProptype } from '@parity/shared/util/proptypes';
@ -36,8 +35,6 @@ import Portal from '~/ui/Portal';
import AddressSelectStore from './addressSelectStore'; import AddressSelectStore from './addressSelectStore';
import styles from './addressSelect.css'; import styles from './addressSelect.css';
const BOTTOM_BORDER_STYLE = { borderBottom: 'solid 3px' };
// Current Form ID // Current Form ID
let currentId = 1; let currentId = 1;
@ -178,9 +175,8 @@ class AddressSelect extends Component {
} }
renderContent () { renderContent () {
const { muiTheme } = this.context;
const { hint, disabled, label, readOnly } = this.props; const { hint, disabled, label, readOnly } = this.props;
const { expanded, inputFocused } = this.state; const { expanded } = this.state;
if (disabled || readOnly) { if (disabled || readOnly) {
return null; return null;
@ -213,16 +209,14 @@ class AddressSelect extends Component {
onChange={ this.handleChange } onChange={ this.handleChange }
ref={ this.setInputRef } ref={ this.setInputRef }
/> />
{ this.renderLoader() } {
</div> this.store.loading && (
<Loading
<div className={ styles.underline }> className={ styles.loader }
<TextFieldUnderline size='small'
focus={ inputFocused }
focusStyle={ BOTTOM_BORDER_STYLE }
muiTheme={ muiTheme }
style={ BOTTOM_BORDER_STYLE }
/> />
)
}
</div> </div>
</div> </div>
} }
@ -234,19 +228,6 @@ class AddressSelect extends Component {
); );
} }
renderLoader () {
if (!this.store.loading) {
return null;
}
return (
<Loading
className={ styles.loader }
size={ 0.5 }
/>
);
}
renderCurrentInput () { renderCurrentInput () {
const { inputValue } = this.state; const { inputValue } = this.state;

View File

@ -1,24 +0,0 @@
/* 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 {
&:last-child {
&.divider {
display: none;
}
}
}

View File

@ -1,268 +0,0 @@
// 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 keycode from 'keycode';
import { isEqual } from 'lodash';
import { MenuItem, AutoComplete as MUIAutoComplete, Divider as MUIDivider } from 'material-ui';
import { PopoverAnimationVertical } from 'material-ui/Popover';
import React, { Component, PropTypes } from 'react';
import { nodeOrStringProptype } from '@parity/shared/util/proptypes';
import styles from './autocomplete.css';
// Hack to prevent "Unknown prop `disableFocusRipple` on <hr> tag" error
class Divider extends Component {
static muiName = MUIDivider.muiName;
render () {
return (
<div
style={ { margin: '0.25em 0' } }
className={ [styles.item, styles.divider].join(' ') }
>
<MUIDivider style={ { height: 2 } } />
</div>
);
}
}
export default class AutoComplete extends Component {
static propTypes = {
className: PropTypes.string,
disabled: PropTypes.bool,
entry: PropTypes.object,
entries: PropTypes.oneOfType([
PropTypes.array,
PropTypes.object
]),
error: nodeOrStringProptype(),
filter: PropTypes.func,
hint: nodeOrStringProptype(),
label: nodeOrStringProptype(),
onChange: PropTypes.func.isRequired,
onUpdateInput: PropTypes.func,
renderItem: PropTypes.func,
value: PropTypes.string
};
state = {
lastChangedValue: undefined,
entry: null,
open: false,
dataSource: [],
dividerBreaks: []
};
dividersVisibility = {};
componentWillMount () {
const dataSource = this.getDataSource();
this.setState({ dataSource });
}
componentWillReceiveProps (nextProps) {
const prevEntries = Object.keys(this.props.entries || {}).sort();
const nextEntries = Object.keys(nextProps.entries || {}).sort();
if (!isEqual(prevEntries, nextEntries)) {
const dataSource = this.getDataSource(nextProps);
this.setState({ dataSource });
}
}
render () {
const { disabled, error, hint, label, value, className, onUpdateInput } = this.props;
const { open, dataSource } = this.state;
return (
<MUIAutoComplete
className={ className }
disabled={ disabled }
floatingLabelText={ label }
hintText={ hint }
errorText={ error }
onNewRequest={ this.onChange }
onUpdateInput={ onUpdateInput }
searchText={ value }
onFocus={ this.onFocus }
onClose={ this.onClose }
animation={ PopoverAnimationVertical }
filter={ this.handleFilter }
popoverProps={ { open } }
openOnFocus
menuCloseDelay={ 0 }
fullWidth
floatingLabelFixed
dataSource={ dataSource }
menuProps={ { maxHeight: 400 } }
ref='muiAutocomplete'
onKeyDown={ this.onKeyDown }
/>
);
}
getDataSource (props = this.props) {
const { renderItem, entries } = props;
const entriesArray = (entries instanceof Array)
? entries
: Object.values(entries);
let currentDivider = 0;
let firstSet = false;
const dataSource = entriesArray.map((entry, index) => {
// Render divider
if (typeof entry === 'string' && entry.toLowerCase() === 'divider') {
// Don't add divider if nothing before
if (!firstSet) {
return undefined;
}
const item = {
text: '',
divider: currentDivider,
isDivider: true,
value: (
<Divider />
)
};
currentDivider++;
return item;
}
let item;
if (renderItem && typeof renderItem === 'function') {
item = renderItem(entry);
// Add the item class to the entry
const classNames = [ styles.item ].concat(item.value.props.className);
item.value = React.cloneElement(item.value, { className: classNames.join(' ') });
} else {
item = {
text: entry,
value: (
<MenuItem
className={ styles.item }
primaryText={ entry }
/>
)
};
}
if (!firstSet) {
item.first = true;
firstSet = true;
}
item.divider = currentDivider;
item.entry = entry;
return item;
}).filter((item) => item !== undefined);
return dataSource;
}
handleFilter = (searchText, name, item) => {
if (item.isDivider) {
return this.dividersVisibility[item.divider];
}
if (item.first) {
this.dividersVisibility = {};
}
const { filter } = this.props;
const show = filter(searchText, name, item);
// Show the related divider
if (show) {
this.dividersVisibility[item.divider] = true;
}
return show;
}
onKeyDown = (event) => {
const { muiAutocomplete } = this.refs;
switch (keycode(event)) {
case 'down':
const { menu } = muiAutocomplete.refs;
menu && menu.handleKeyDown(event);
break;
case 'enter':
case 'tab':
event.preventDefault();
event.stopPropagation();
event.which = 'useless';
const e = new CustomEvent('down');
e.which = 40;
muiAutocomplete && muiAutocomplete.handleKeyDown(e);
break;
}
}
onChange = (item, idx) => {
if (idx === -1) {
return;
}
const { dataSource } = this.state;
const { entry } = dataSource[idx];
this.handleOnChange(entry);
this.setState({ entry, open: false });
}
onClose = (event) => {
const { onUpdateInput } = this.props;
if (!onUpdateInput) {
const { entry } = this.state;
this.handleOnChange(entry);
}
}
onFocus = () => {
const { entry } = this.props;
this.setState({ entry, open: true }, () => {
this.handleOnChange(null, true);
});
}
handleOnChange = (value, empty) => {
const { lastChangedValue } = this.state;
if (value !== lastChangedValue) {
this.setState({ lastChangedValue: value });
this.props.onChange(value, empty);
}
}
}

View File

@ -26,7 +26,7 @@ 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 }, { intl }) { export default function Dropdown ({ className, disabled = false, error, fullWidth = true, hint, label, onBlur, onChange, onKeyDown, options, text, value }, context) {
const _onChange = (event, { value }) => onChange(event, value); const _onChange = (event, { value }) => onChange(event, value);
return ( return (

View File

@ -53,8 +53,6 @@ export default class InputChip extends Component {
const { clearOnBlur, className, hint, label, tokens } = this.props; const { clearOnBlur, className, hint, label, tokens } = this.props;
const { focused } = this.state; const { focused } = this.state;
const classes = `${className}`;
const textFieldStyle = { const textFieldStyle = {
height: 55 height: 55
}; };
@ -65,7 +63,7 @@ export default class InputChip extends Component {
return ( return (
<ChipInput <ChipInput
className={ classes } className={ className }
ref='chipInput' ref='chipInput'
value={ tokens } value={ tokens }

View File

@ -16,11 +16,11 @@
*/ */
.strength { .strength {
margin-top: 1.25em;
} }
.feedback { .feedback {
font-size: 0.75em; font-size: 0.75em;
margin: -2em 0 1.5em 0;
} }
.label { .label {

View File

@ -23,12 +23,6 @@ import Progress from '~/ui/Progress';
import styles from './passwordStrength.css'; import styles from './passwordStrength.css';
const BAR_STYLE = {
borderRadius: 1,
height: 7,
marginTop: '0.5em'
};
export default class PasswordStrength extends Component { export default class PasswordStrength extends Component {
static propTypes = { static propTypes = {
input: PropTypes.string.isRequired input: PropTypes.string.isRequired
@ -69,10 +63,6 @@ export default class PasswordStrength extends Component {
const { score, feedback } = strength; const { score, feedback } = strength;
// Score is between 0 and 4
const value = score * 100 / 5 + 20;
const color = this.getStrengthBarColor(score);
return ( return (
<div className={ styles.strength }> <div className={ styles.strength }>
<label className={ styles.label }> <label className={ styles.label }>
@ -82,10 +72,10 @@ export default class PasswordStrength extends Component {
/> />
</label> </label>
<Progress <Progress
color={ color } color={ this.getStrengthBarColor(score) }
determinate isDeterminate
style={ BAR_STYLE } max={ 100 }
value={ value } value={ score * 100 / 5 + 20 }
/> />
<div className={ styles.feedback }> <div className={ styles.feedback }>
{ this.renderFeedback(feedback) } { this.renderFeedback(feedback) }
@ -111,8 +101,10 @@ export default class PasswordStrength extends Component {
getStrengthBarColor (score) { getStrengthBarColor (score) {
switch (score) { switch (score) {
case 4: case 4:
return 'green';
case 3: case 3:
return 'lightgreen'; return 'blue';
case 2: case 2:
return 'yellow'; return 'yellow';

View File

@ -18,13 +18,12 @@ import React, { Component, PropTypes } from 'react';
import { range } from 'lodash'; import { range } from 'lodash';
import BigNumber from 'bignumber.js'; import BigNumber from 'bignumber.js';
import IconButton from 'material-ui/IconButton';
import { fromWei, toWei } from '@parity/api/util/wei'; import { fromWei, toWei } from '@parity/api/util/wei';
import { bytesToHex } from '@parity/api/util/format'; import { bytesToHex } from '@parity/api/util/format';
import { ABI_TYPES, parseAbiType } from '@parity/shared/util/abi'; import { ABI_TYPES, parseAbiType } from '@parity/shared/util/abi';
import { nodeOrStringProptype } from '@parity/shared/util/proptypes'; import { nodeOrStringProptype } from '@parity/shared/util/proptypes';
import Button from '~/ui/Button';
import Dropdown from '~/ui/Form/Dropdown'; import Dropdown from '~/ui/Form/Dropdown';
import Input from '~/ui/Form/Input'; import Input from '~/ui/Form/Input';
import InputAddressSelect from '~/ui/Form/InputAddressSelect'; import InputAddressSelect from '~/ui/Form/InputAddressSelect';
@ -142,40 +141,16 @@ export default class TypedInput extends Component {
} }
renderLength () { renderLength () {
const iconStyle = {
width: 16,
height: 16
};
const style = {
width: 24,
height: 24,
padding: 0
};
const plusStyle = {
...style,
backgroundColor: 'rgba(255, 255, 255, 0.25)',
borderRadius: '50%'
};
return ( return (
<div style={ { marginTop: '0.75em' } }> <div style={ { marginTop: '0.75em' } }>
<IconButton <Button
iconStyle={ iconStyle } icon={ <AddIcon /> }
style={ plusStyle } onClick={ this.onAddField }
onTouchTap={ this.onAddField } />
> <Button
<AddIcon /> icon={ <RemoveIcon /> }
</IconButton> onClick={ this.onRemoveField }
/>
<IconButton
iconStyle={ iconStyle }
style={ style }
onTouchTap={ this.onRemoveField }
>
<RemoveIcon />
</IconButton>
</div> </div>
); );
} }

View File

@ -16,6 +16,13 @@
*/ */
.item { .item {
&.active {
background: rgba(0, 0, 255, 0.25);
}
&.inactive {
}
.description { .description {
} }

View File

@ -19,10 +19,10 @@ import { List as SemanticList } from 'semantic-ui-react';
import styles from './item.css'; import styles from './item.css';
export default function Item ({ buttons, className, description, icon, label, onClick, style }) { export default function Item ({ buttons, className, description, icon, isActive, label, onClick, style }) {
return ( return (
<SemanticList.Item <SemanticList.Item
className={ styles.item } className={ `${styles.item} ${isActive ? styles.active : styles.inactive} ${className}` }
onClick={ onClick } onClick={ onClick }
style={ style } style={ style }
> >
@ -52,8 +52,8 @@ Item.propTypes = {
buttons: PropTypes.any, buttons: PropTypes.any,
className: PropTypes.string, className: PropTypes.string,
description: PropTypes.node, description: PropTypes.node,
key: PropTypes.any,
icon: PropTypes.node, icon: PropTypes.node,
isActive: PropTypes.bool,
label: PropTypes.node, label: PropTypes.node,
onClick: PropTypes.func, onClick: PropTypes.func,
style: PropTypes.object style: PropTypes.object

View File

@ -31,11 +31,12 @@ export default function List ({ className, items, label, onClick, style }) {
<LabelComponent label={ label }> <LabelComponent label={ label }>
<SemanticList className={ `${styles.list} ${className}` }> <SemanticList className={ `${styles.list} ${className}` }>
{ {
items.map(({ buttons, description, icon, key, label }, index) => ( items.filter((item) => item).map(({ buttons, description, icon, isActive, key, label }, index) => (
<Item <Item
buttons={ buttons } buttons={ buttons }
description={ description } description={ description }
icon={ icon } icon={ icon }
isActive={ isActive }
key={ key || index } key={ key || index }
label={ label } label={ label }
onClick={ wrapOnClick(key || index) } onClick={ wrapOnClick(key || index) }

View File

@ -15,29 +15,19 @@
// 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 CircularProgress from 'material-ui/CircularProgress'; import { Loader as SemanticLoader } from 'semantic-ui-react';
import styles from './loading.css'; import styles from './loading.css';
export default function Loading ({ className, size, thickness }) { export default function Loading ({ className, size = 'medium' }) {
return ( return (
<div className={ [ styles.loading, className ].join(' ') }> <div className={ `${styles.loading} ${className}` }>
<CircularProgress <SemanticLoader />
size={ size * 60 }
thickness={ thickness }
/>
</div> </div>
); );
} }
Loading.propTypes = { Loading.propTypes = {
className: PropTypes.string, className: PropTypes.string,
size: PropTypes.number, size: PropTypes.string
thickness: PropTypes.number
};
Loading.defaultProps = {
className: '',
size: 2,
thickness: 3.5
}; };

View File

@ -100,7 +100,7 @@ class MethodDecoding extends Component {
if (isLoading) { if (isLoading) {
return ( return (
<div className={ styles.loading }> <div className={ styles.loading }>
<Loading size={ 1 } thickness={ 2 } /> <Loading />
</div> </div>
); );
} }

View File

@ -23,8 +23,12 @@ import styles from './busy.css';
export default function Busy ({ children, state, title }) { export default function Busy ({ children, state, title }) {
return ( return (
<div className={ styles.center }> <div className={ styles.center }>
<div className={ styles.title }>{ title }</div> <div className={ styles.title }>
<div className={ styles.state }>{ state }</div> { title }
</div>
<div className={ styles.state }>
{ state }
</div>
{ children } { children }
</div> </div>
); );

View File

@ -18,8 +18,6 @@ import React, { PropTypes } from 'react';
import { nodeOrStringProptype } from '@parity/shared/util/proptypes'; import { nodeOrStringProptype } from '@parity/shared/util/proptypes';
import Body from './body';
import Summary from './summary';
import styles from './modalBox.css'; import styles from './modalBox.css';
export default function ModalBox ({ children, icon, summary }) { export default function ModalBox ({ children, icon, summary }) {
@ -29,8 +27,20 @@ export default function ModalBox ({ children, icon, summary }) {
{ icon } { icon }
</div> </div>
<div className={ styles.content }> <div className={ styles.content }>
<Summary summary={ summary } /> {
<Body children={ children } /> summary && (
<div className={ styles.summary }>
{ summary }
</div>
)
}
{
children && (
<div className={ styles.body }>
{ children }
</div>
)
}
</div> </div>
</div> </div>
); );

View File

@ -52,11 +52,11 @@ describe('ui/ModalBox', () => {
describe('components', () => { describe('components', () => {
it('adds the Body as supplied', () => { it('adds the Body as supplied', () => {
expect(component.find('Body').props().children).to.deep.equal(CHILDREN); expect(component.find('#testChild').text()).to.equal('testChild');
}); });
it('adds the Summary as supplied', () => { it('adds the Summary as supplied', () => {
expect(component.find('Summary').props().summary).to.deep.equal(SUMMARY); expect(component.find('#testSummary').text()).to.equal('testSummary');
}); });
}); });
}); });

View File

@ -1,37 +0,0 @@
// 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 from 'react';
import { nodeOrStringProptype } from '@parity/shared/util/proptypes';
import styles from './modalBox.css';
export default function Summary ({ summary }) {
if (!summary) {
return null;
}
return (
<div className={ styles.summary }>
{ summary }
</div>
);
}
Summary.propTypes = {
summary: nodeOrStringProptype()
};

View File

@ -24,16 +24,10 @@ import styles from './page.css';
export default function Page ({ buttons, className, children, padded, title }) { export default function Page ({ buttons, className, children, padded, title }) {
return ( return (
<div> <div>
{
title || buttons
? (
<Actionbar <Actionbar
buttons={ buttons } buttons={ buttons }
title={ title } title={ title }
/> />
)
: null
}
<div <div
className={ className={
[ [

View File

@ -14,4 +14,4 @@
// 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/>.
export default from './autocomplete'; export default from './popup';

View File

@ -15,21 +15,25 @@
// 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 { Popup as SemanticPopup } from 'semantic-ui-react';
import styles from './modalBox.css'; export default function Popup ({ children, className, isOpen, trigger, triggerOn }) {
export default function Body ({ children }) {
if (!children) {
return null;
}
return ( return (
<div className={ styles.body }> <SemanticPopup
className={ className }
on={ triggerOn }
open={ isOpen }
trigger={ trigger }
>
{ children } { children }
</div> </SemanticPopup>
); );
} }
Body.propTypes = { Popup.propTypes = {
children: PropTypes.node children: PropTypes.node.isRequired,
className: PropTypes.string,
isOpen: PropTypes.bool,
trigger: PropTypes.node,
triggerOn: PropTypes.string
}; };

View File

@ -14,23 +14,22 @@
// 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 { LinearProgress } from 'material-ui';
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Progress as SemanticProgress } from 'semantic-ui-react';
export default function Progress ({ className, color, determinate, max, min, style, value }) { export default function Progress ({ className, color, isDeterminate, max, style, value }) {
return ( return (
<LinearProgress <SemanticProgress
className={ className } className={ className }
color={ color } color={ color }
max={ max } indicating={ !isDeterminate }
min={ min } percent={
mode={ isDeterminate
determinate ? 100 * value / max
? 'determinate' : 100
: 'indeterminate'
} }
size='small'
style={ style } style={ style }
value={ value }
/> />
); );
} }
@ -38,13 +37,8 @@ export default function Progress ({ className, color, determinate, max, min, sty
Progress.propTypes = { Progress.propTypes = {
className: PropTypes.string, className: PropTypes.string,
color: PropTypes.string, color: PropTypes.string,
determinate: PropTypes.bool, isDeterminate: PropTypes.bool,
max: PropTypes.number, max: PropTypes.number,
min: PropTypes.number,
style: PropTypes.object, style: PropTypes.object,
value: PropTypes.number value: PropTypes.number
}; };
Progress.defaultProps = {
determinate: false
};

View File

@ -16,26 +16,9 @@
import GeoPattern from 'geopattern'; import GeoPattern from 'geopattern';
import getMuiTheme from 'material-ui/styles/getMuiTheme'; import getMuiTheme from 'material-ui/styles/getMuiTheme';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
const muiTheme = getMuiTheme(lightBaseTheme); const muiTheme = getMuiTheme();
// muiTheme.inkBar.backgroundColor = 'transparent';
// muiTheme.paper.backgroundColor = 'rgb(18, 18, 18)';
// muiTheme.raisedButton.primaryTextColor = 'white';
// muiTheme.snackbar.backgroundColor = 'rgba(255, 30, 30, 0.9)';
// muiTheme.snackbar.textColor = 'rgba(255, 255, 255, 0.75)';
// muiTheme.stepper.textColor = '#eee';
// muiTheme.stepper.disabledTextColor = '#777';
// muiTheme.tabs = lightTheme.tabs;
// muiTheme.tabs.backgroundColor = 'transparent';
// muiTheme.tabs.selectedTextColor = 'white';
// muiTheme.tabs.textColor = 'rgba(255, 255, 255, 0.5)';
// muiTheme.textField.floatingLabelColor = 'rgba(255, 255, 255, 0.5)';
// muiTheme.textField.hintColor = 'rgba(255, 255, 255, 0.5)';
// muiTheme.textField.disabledTextColor = muiTheme.textField.textColor;
// muiTheme.toolbar = lightTheme.toolbar;
// muiTheme.toolbar.backgroundColor = 'transparent';
muiTheme.zIndex.layer = 4000; muiTheme.zIndex.layer = 4000;
muiTheme.zIndex.popover = 4100; muiTheme.zIndex.popover = 4100;

View File

@ -1,55 +0,0 @@
// 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 getMuiTheme from 'material-ui/styles/getMuiTheme';
import lightBaseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
const muiTheme = getMuiTheme(lightBaseTheme);
import theme from './theme';
describe('ui/Theme', () => {
it('is MUI-based', () => {
expect(Object.keys(theme)).to.deep.equal(Object.keys(muiTheme).concat('parity'));
});
it('allows setting of Parity backgrounds', () => {
expect(typeof theme.parity.setBackgroundSeed === 'function').to.be.true;
expect(typeof theme.parity.getBackgroundStyle === 'function').to.be.true;
});
describe('parity', () => {
describe('setBackgroundSeed', () => {
const SEED = 'testseed';
beforeEach(() => {
theme.parity.setBackgroundSeed(SEED);
});
it('sets the correct theme values', () => {
expect(theme.parity.backgroundSeed).to.equal(SEED);
});
});
describe('getBackgroundStyle', () => {
it('generates a style containing background', () => {
const style = theme.parity.getBackgroundStyle();
expect(style).to.have.property('background');
});
});
});
});

View File

@ -181,10 +181,7 @@ class TxHash extends Component {
if (!(transactionReceipt && transactionReceipt.blockNumber && transactionReceipt.blockNumber.gt(0))) { if (!(transactionReceipt && transactionReceipt.blockNumber && transactionReceipt.blockNumber.gt(0))) {
return ( return (
<div className={ styles.confirm }> <div className={ styles.confirm }>
<Progress <Progress className={ styles.progressbar } />
className={ styles.progressbar }
color='white'
/>
<div className={ styles.progressinfo }> <div className={ styles.progressinfo }>
<FormattedMessage <FormattedMessage
id='ui.txHash.waiting' id='ui.txHash.waiting'
@ -208,11 +205,9 @@ class TxHash extends Component {
<div className={ styles.confirm }> <div className={ styles.confirm }>
<Progress <Progress
className={ styles.progressbar } className={ styles.progressbar }
min={ 0 }
max={ maxConfirmations } max={ maxConfirmations }
value={ value } value={ value }
color='white' isDeterminate
determinate
/> />
<div className={ styles.progressinfo }> <div className={ styles.progressinfo }>
<abbr title={ `block #${blockNumber.toFormat(0)}` }> <abbr title={ `block #${blockNumber.toFormat(0)}` }>

View File

@ -132,7 +132,7 @@ describe('ui/TxHash', () => {
}); });
it('renders determinate progressbar', () => { it('renders determinate progressbar', () => {
expect(child.find('Progress[determinate]')).to.have.length(1); expect(child.find('Progress[isDeterminate]')).to.have.length(1);
}); });
it('renders confirmation text', () => { it('renders confirmation text', () => {

View File

@ -48,6 +48,7 @@ export { Busy as BusyStep, Completed as CompletedStep } from './Modal';
export ModalBox from './ModalBox'; export ModalBox from './ModalBox';
export muiTheme from './Theme'; export muiTheme from './Theme';
export Page from './Page'; export Page from './Page';
export Popup from './Popup';
export Portal from './Portal'; export Portal from './Portal';
export Progress from './Progress'; export Progress from './Progress';
export QrCode from './QrCode'; export QrCode from './QrCode';
@ -61,6 +62,7 @@ export Snackbar from './Snackbar';
export Tabs from './Tabs'; export Tabs from './Tabs';
export Tags from './Tags'; export Tags from './Tags';
export Title from './Title'; export Title from './Title';
export TokenImage from './TokenImage';
export TxHash from './TxHash'; export TxHash from './TxHash';
export TxList from './TxList'; export TxList from './TxList';
export VaultCard from './VaultCard'; export VaultCard from './VaultCard';

View File

@ -18,8 +18,7 @@ import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { nullableProptype } from '@parity/shared/util/proptypes'; import { nullableProptype } from '@parity/shared/util/proptypes';
import Form, { AddressSelect, Checkbox, Input, InputAddressSelect } from '@parity/ui/Form';
import Form, { Input, InputAddressSelect, AddressSelect, Checkbox } from '@parity/ui/Form';
import TokenSelect from './tokenSelect'; import TokenSelect from './tokenSelect';
import styles from '../transfer.css'; import styles from '../transfer.css';

View File

@ -54,7 +54,8 @@ class TokenSelect extends Component {
const { api } = this.context; const { api } = this.context;
const { balance, tokens } = this.props; const { balance, tokens } = this.props;
const items = Object.keys(balance) const items = Object
.keys(balance)
.map((tokenId) => { .map((tokenId) => {
const token = tokens[tokenId]; const token = tokens[tokenId];
const tokenValue = balance[tokenId]; const tokenValue = balance[tokenId];
@ -122,4 +123,7 @@ function mapStateToProps (state) {
return { tokens }; return { tokens };
} }
export default connect(mapStateToProps)(TokenSelect); export default connect(
mapStateToProps,
null
)(TokenSelect);

View File

@ -15,11 +15,6 @@
/* along with Parity. If not, see <http://www.gnu.org/licenses/>. /* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/ */
.btnicon {
width: 24px;
height: 24px;
}
.textbox { .textbox {
line-height: 1.5em; line-height: 1.5em;
margin-bottom: 1.5em; margin-bottom: 1.5em;

View File

@ -191,7 +191,7 @@ class Account extends Component {
<Button <Button
icon={ icon={
<img <img
className={ styles.btnicon } className='icon'
src={ shapeshiftBtn } src={ shapeshiftBtn }
/> />
} }

View File

@ -163,7 +163,7 @@ export default class CreateAccount extends Component {
if (!accounts) { if (!accounts) {
return ( return (
<Loading className={ styles.selector } size={ 1 } /> <Loading className={ styles.selector } />
); );
} }

View File

@ -58,9 +58,7 @@ export default class Events extends Component {
if (isLoading) { if (isLoading) {
return ( return (
<Container title={ TITLE }> <Container title={ TITLE }>
<div> <Loading />
<Loading size={ 2 } />
</div>
</Container> </Container>
); );
} }

View File

@ -275,10 +275,7 @@ class ContractDevelop extends Component {
if (selectedBuild < 0) { if (selectedBuild < 0) {
return ( return (
<div className={ `${styles.panel} ${styles.centeredMessage}` }> <div className={ `${styles.panel} ${styles.centeredMessage}` }>
<Loading <Loading />
size={ 1.2 }
thickness={ 5 }
/>
<p> <p>
<FormattedMessage <FormattedMessage
id='writeContract.title.loading' id='writeContract.title.loading'
@ -313,10 +310,7 @@ class ContractDevelop extends Component {
content = ( content = (
<div className={ styles.panel }> <div className={ styles.panel }>
<div className={ styles.centeredMessage }> <div className={ styles.centeredMessage }>
<Loading <Loading />
size={ 1.2 }
thickness={ 5 }
/>
<p> <p>
<FormattedMessage <FormattedMessage
id='writeContract.title.solidity' id='writeContract.title.solidity'
@ -496,10 +490,7 @@ class ContractDevelop extends Component {
if (compiling) { if (compiling) {
return ( return (
<div className={ styles.centeredMessage }> <div className={ styles.centeredMessage }>
<Loading <Loading />
size={ 1.2 }
thickness={ 5 }
/>
<p> <p>
<FormattedMessage <FormattedMessage
id='writeContract.compiling.busy' id='writeContract.compiling.busy'

View File

@ -116,7 +116,7 @@ export default class Node extends Component {
<Dropdown <Dropdown
onChange={ onChange } onChange={ onChange }
value={ level } value={ level }
values={ LOGLEVEL_OPTIONS } options={ LOGLEVEL_OPTIONS }
/> />
</div> </div>
); );

View File

@ -55,9 +55,7 @@ export default class Settings extends Component {
icon: <BackgroundIcon />, icon: <BackgroundIcon />,
label: <FormattedMessage id='settings.background.label' /> label: <FormattedMessage id='settings.background.label' />
}, },
isProxied !isProxied && {
? null
: {
icon: <EthernetIcon />, icon: <EthernetIcon />,
label: <FormattedMessage id='settings.proxy.label' /> label: <FormattedMessage id='settings.proxy.label' />
}, },

View File

@ -266,16 +266,11 @@ class WalletConfirmation extends Component {
{ {
pending pending
? ( ? (
<Progress <Progress style={ style } />
key={ `pending_${operation}` }
style={ style }
/>
) )
: ( : (
<Progress <Progress
key={ `unpending_${operation}` } isDeterminate
determinate
min={ 0 }
max={ require.toNumber() } max={ require.toNumber() }
value={ confirmedBy.length } value={ confirmedBy.length }
style={ style } style={ style }

View File

@ -49,7 +49,7 @@ class WalletContainer extends Component {
if (netVersion === '0') { if (netVersion === '0') {
return ( return (
<Loading size={ 4 } /> <Loading size='large' />
); );
} }
@ -193,7 +193,7 @@ class Wallet extends Component {
if (!owners || !require) { if (!owners || !require) {
return ( return (
<div style={ { marginTop: '4em' } }> <div style={ { marginTop: '4em' } }>
<Loading size={ 4 } /> <Loading size='large' />
</div> </div>
); );
} }