Add SelectionList component to DRY up (#4639)
* Added SelectionList component for selections * Use SelectionList in DappPermisions * AddDapps uses SelectionList * Fix AccountCard to consistent height * Convert Signer defaults to SelectionList * Subtle selection border * Convert VaultAccounts to SelectionList * Add tests for SectionList component * Apply scroll fixes from lates commit in #4621 * Remove unneeded logs * Remove extra div, fixing ParityBar overflow
This commit is contained in:
parent
5817cfdf41
commit
a6ed3dc5dc
@ -19,10 +19,6 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.container {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-top: .5em !important;
|
||||
}
|
||||
@ -49,26 +45,3 @@
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
.selectIcon {
|
||||
position: absolute;
|
||||
right: 0.5em;
|
||||
top: 0.5em;
|
||||
}
|
||||
|
||||
.selected,
|
||||
.unselected {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.unselected {
|
||||
background: rgba(0, 0, 0, 0.4) !important;
|
||||
|
||||
.selectIcon {
|
||||
opacity: 0.15;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
background: rgba(255, 255, 255, 0.15) !important;
|
||||
}
|
||||
|
@ -18,8 +18,7 @@ import { observer } from 'mobx-react';
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { DappCard, Portal, SectionList } from '~/ui';
|
||||
import { CheckIcon } from '~/ui/Icons';
|
||||
import { DappCard, Portal, SelectionList } from '~/ui';
|
||||
|
||||
import styles from './addDapps.css';
|
||||
|
||||
@ -48,45 +47,43 @@ export default class AddDapps extends Component {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className={ styles.container }>
|
||||
<div className={ styles.warning } />
|
||||
{
|
||||
this.renderList(store.sortedLocal, store.displayApps,
|
||||
<FormattedMessage
|
||||
id='dapps.add.local.label'
|
||||
defaultMessage='Applications locally available'
|
||||
/>,
|
||||
<FormattedMessage
|
||||
id='dapps.add.local.desc'
|
||||
defaultMessage='All applications installed locally on the machine by the user for access by the Parity client.'
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.renderList(store.sortedBuiltin, store.displayApps,
|
||||
<FormattedMessage
|
||||
id='dapps.add.builtin.label'
|
||||
defaultMessage='Applications bundled with Parity'
|
||||
/>,
|
||||
<FormattedMessage
|
||||
id='dapps.add.builtin.desc'
|
||||
defaultMessage='Experimental applications developed by the Parity team to show off dapp capabilities, integration, experimental features and to control certain network-wide client behaviour.'
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.renderList(store.sortedNetwork, store.displayApps,
|
||||
<FormattedMessage
|
||||
id='dapps.add.network.label'
|
||||
defaultMessage='Applications on the global network'
|
||||
/>,
|
||||
<FormattedMessage
|
||||
id='dapps.add.network.desc'
|
||||
defaultMessage='These applications are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.'
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className={ styles.warning } />
|
||||
{
|
||||
this.renderList(store.sortedLocal, store.displayApps,
|
||||
<FormattedMessage
|
||||
id='dapps.add.local.label'
|
||||
defaultMessage='Applications locally available'
|
||||
/>,
|
||||
<FormattedMessage
|
||||
id='dapps.add.local.desc'
|
||||
defaultMessage='All applications installed locally on the machine by the user for access by the Parity client.'
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.renderList(store.sortedBuiltin, store.displayApps,
|
||||
<FormattedMessage
|
||||
id='dapps.add.builtin.label'
|
||||
defaultMessage='Applications bundled with Parity'
|
||||
/>,
|
||||
<FormattedMessage
|
||||
id='dapps.add.builtin.desc'
|
||||
defaultMessage='Experimental applications developed by the Parity team to show off dapp capabilities, integration, experimental features and to control certain network-wide client behaviour.'
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.renderList(store.sortedNetwork, store.displayApps,
|
||||
<FormattedMessage
|
||||
id='dapps.add.network.label'
|
||||
defaultMessage='Applications on the global network'
|
||||
/>,
|
||||
<FormattedMessage
|
||||
id='dapps.add.network.desc'
|
||||
defaultMessage='These applications are not affiliated with Parity nor are they published by Parity. Each remain under the control of their respective authors. Please ensure that you understand the goals for each application before interacting.'
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
@ -102,9 +99,11 @@ export default class AddDapps extends Component {
|
||||
<div className={ styles.header }>{ header }</div>
|
||||
<div className={ styles.byline }>{ byline }</div>
|
||||
</div>
|
||||
<SectionList
|
||||
<SelectionList
|
||||
isChecked={ this.isVisible }
|
||||
items={ items }
|
||||
noStretch
|
||||
onSelectClick={ this.onSelect }
|
||||
renderItem={ this.renderApp }
|
||||
/>
|
||||
</div>
|
||||
@ -112,30 +111,27 @@ export default class AddDapps extends Component {
|
||||
}
|
||||
|
||||
renderApp = (app) => {
|
||||
const { store } = this.props;
|
||||
const isVisible = store.displayApps[app.id].visible;
|
||||
|
||||
const onClick = () => {
|
||||
if (isVisible) {
|
||||
store.hideApp(app.id);
|
||||
} else {
|
||||
store.showApp(app.id);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DappCard
|
||||
app={ app }
|
||||
className={
|
||||
isVisible
|
||||
? styles.selected
|
||||
: styles.unselected
|
||||
}
|
||||
key={ app.id }
|
||||
onClick={ onClick }
|
||||
>
|
||||
<CheckIcon className={ styles.selectIcon } />
|
||||
</DappCard>
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
isVisible = (app) => {
|
||||
const { store } = this.props;
|
||||
|
||||
return store.displayApps[app.id].visible;
|
||||
}
|
||||
|
||||
onSelect = (app) => {
|
||||
const { store } = this.props;
|
||||
|
||||
if (this.isVisible(app)) {
|
||||
store.hideApp(app.id);
|
||||
} else {
|
||||
store.showApp(app.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,51 +15,6 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
right: 0.5em;
|
||||
top: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.selected,
|
||||
.unselected {
|
||||
margin-bottom: 0.25em;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.unselected {
|
||||
background: rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background: rgba(255, 255, 255, 0.15) !important;
|
||||
|
||||
&.default {
|
||||
background: rgba(255, 255, 255, 0.35) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.unselected {
|
||||
}
|
||||
|
||||
.iconDisabled {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.legend {
|
||||
opacity: 0.75;
|
||||
|
||||
|
@ -19,8 +19,8 @@ import React, { Component, PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { AccountCard, Portal, SectionList } from '~/ui';
|
||||
import { CheckIcon, StarIcon, StarOutlineIcon } from '~/ui/Icons';
|
||||
import { AccountCard, Portal, SelectionList } from '~/ui';
|
||||
import { CheckIcon, StarIcon } from '~/ui/Icons';
|
||||
|
||||
import styles from './dappPermissions.css';
|
||||
|
||||
@ -61,60 +61,34 @@ class DappPermissions extends Component {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className={ styles.container }>
|
||||
<SectionList
|
||||
items={ permissionStore.accounts }
|
||||
noStretch
|
||||
renderItem={ this.renderAccount }
|
||||
/>
|
||||
</div>
|
||||
<SelectionList
|
||||
items={ permissionStore.accounts }
|
||||
noStretch
|
||||
onDefaultClick={ this.onMakeDefault }
|
||||
onSelectClick={ this.onSelect }
|
||||
renderItem={ this.renderAccount }
|
||||
/>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
onMakeDefault = (account) => {
|
||||
this.props.permissionStore.setDefaultAccount(account.address);
|
||||
}
|
||||
|
||||
onSelect = (account) => {
|
||||
this.props.permissionStore.selectAccount(account.address);
|
||||
}
|
||||
|
||||
renderAccount = (account) => {
|
||||
const { balances, permissionStore } = this.props;
|
||||
const { balances } = this.props;
|
||||
const balance = balances[account.address];
|
||||
|
||||
const onMakeDefault = () => {
|
||||
permissionStore.setDefaultAccount(account.address);
|
||||
};
|
||||
|
||||
const onSelect = () => {
|
||||
permissionStore.selectAccount(account.address);
|
||||
};
|
||||
|
||||
let className;
|
||||
|
||||
if (account.checked) {
|
||||
className = account.default
|
||||
? `${styles.selected} ${styles.default}`
|
||||
: styles.selected;
|
||||
} else {
|
||||
className = styles.unselected;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.item }>
|
||||
<AccountCard
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
className={ className }
|
||||
onClick={ onSelect }
|
||||
/>
|
||||
<div className={ styles.overlay }>
|
||||
{
|
||||
account.checked && account.default
|
||||
? <StarIcon />
|
||||
: <StarOutlineIcon className={ styles.iconDisabled } onClick={ onMakeDefault } />
|
||||
}
|
||||
{
|
||||
account.checked
|
||||
? <CheckIcon onClick={ onSelect } />
|
||||
: <CheckIcon className={ styles.iconDisabled } onClick={ onSelect } />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<AccountCard
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,9 @@ import { bindActionCreators } from 'redux';
|
||||
|
||||
import { newError } from '~/redux/actions';
|
||||
import { personalAccountsInfo } from '~/redux/providers/personalActions';
|
||||
import { AccountCard, Button, Portal, SectionList } from '~/ui';
|
||||
import { AccountCard, Button, Portal, SelectionList } from '~/ui';
|
||||
import { CancelIcon, CheckIcon } from '~/ui/Icons';
|
||||
|
||||
import styles from './vaultAccounts.css';
|
||||
|
||||
@observer
|
||||
class VaultAccounts extends Component {
|
||||
static contextTypes = {
|
||||
@ -92,55 +90,47 @@ class VaultAccounts extends Component {
|
||||
/>
|
||||
}
|
||||
>
|
||||
<SectionList
|
||||
items={ vaultAccounts }
|
||||
noStretch
|
||||
renderItem={ this.renderAccount }
|
||||
selectedAccounts={ selectedAccounts }
|
||||
/>
|
||||
{ this.renderList(vaultAccounts, selectedAccounts) }
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: There are a lot of similarities between the dapp permissions selector
|
||||
// (although that has defaults) and this one. A genrerix multi-select component
|
||||
// would be applicable going forward. (Originals passed in, new selections back)
|
||||
renderList (vaultAccounts) {
|
||||
return (
|
||||
<SelectionList
|
||||
isChecked={ this.isSelected }
|
||||
items={ vaultAccounts }
|
||||
noStretch
|
||||
onSelectClick={ this.onSelect }
|
||||
renderItem={ this.renderAccount }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccount = (account) => {
|
||||
const { balances } = this.props;
|
||||
const { vaultName, selectedAccounts } = this.props.vaultStore;
|
||||
const balance = balances[account.address];
|
||||
const isInVault = account.meta.vault === vaultName;
|
||||
const isSelected = isInVault
|
||||
? !selectedAccounts[account.address]
|
||||
: selectedAccounts[account.address];
|
||||
|
||||
const onSelect = () => {
|
||||
this.props.vaultStore.toggleSelectedAccount(account.address);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={ styles.item }>
|
||||
<AccountCard
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
className={
|
||||
isSelected
|
||||
? styles.selected
|
||||
: styles.unselected
|
||||
}
|
||||
onClick={ onSelect }
|
||||
/>
|
||||
<div className={ styles.overlay }>
|
||||
{
|
||||
isSelected
|
||||
? <CheckIcon onClick={ onSelect } />
|
||||
: <CheckIcon className={ styles.iconDisabled } onClick={ onSelect } />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<AccountCard
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
isSelected = (account) => {
|
||||
const { vaultName, selectedAccounts } = this.props.vaultStore;
|
||||
|
||||
return account.meta.vault === vaultName
|
||||
? !selectedAccounts[account.address]
|
||||
: selectedAccounts[account.address];
|
||||
}
|
||||
|
||||
onSelect = (account) => {
|
||||
this.props.vaultStore.toggleSelectedAccount(account.address);
|
||||
}
|
||||
|
||||
onClose = () => {
|
||||
this.props.vaultStore.closeAccountsModal();
|
||||
}
|
||||
|
@ -130,11 +130,11 @@ describe('modals/VaultAccounts', () => {
|
||||
});
|
||||
|
||||
describe('components', () => {
|
||||
describe('SectionList', () => {
|
||||
describe('SelectionList', () => {
|
||||
let sectionList;
|
||||
|
||||
beforeEach(() => {
|
||||
sectionList = component.find('SectionList');
|
||||
sectionList = component.find('SelectionList');
|
||||
});
|
||||
|
||||
it('has the filtered accounts', () => {
|
||||
|
@ -20,7 +20,7 @@
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 0.5em 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
transition: transform ease-out 0.1s;
|
||||
transform: scale(1);
|
||||
|
@ -39,7 +39,7 @@ export default class AccountCard extends Component {
|
||||
};
|
||||
|
||||
render () {
|
||||
const { account, balance, className } = this.props;
|
||||
const { account, balance, className, onFocus } = this.props;
|
||||
const { copied } = this.state;
|
||||
const { address, description, meta = {}, name } = account;
|
||||
const { tags = [] } = meta;
|
||||
@ -49,14 +49,18 @@ export default class AccountCard extends Component {
|
||||
classes.push(styles.copied);
|
||||
}
|
||||
|
||||
const props = onFocus
|
||||
? { tabIndex: 0 }
|
||||
: {};
|
||||
|
||||
return (
|
||||
<div
|
||||
key={ address }
|
||||
tabIndex={ 0 }
|
||||
className={ classes.join(' ') }
|
||||
onClick={ this.onClick }
|
||||
onFocus={ this.onFocus }
|
||||
onKeyDown={ this.handleKeyDown }
|
||||
{ ...props }
|
||||
>
|
||||
<div className={ styles.mainContainer }>
|
||||
<div className={ styles.infoContainer }>
|
||||
|
@ -146,4 +146,8 @@
|
||||
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.account {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
}
|
||||
|
@ -346,6 +346,7 @@ class AddressSelect extends Component {
|
||||
<AccountCard
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
className={ styles.account }
|
||||
key={ `account_${index}` }
|
||||
onClick={ this.handleClick }
|
||||
onFocus={ this.focusItem }
|
||||
|
17
js/src/ui/SelectionList/index.js
Normal file
17
js/src/ui/SelectionList/index.js
Normal 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 './selectionList';
|
@ -15,16 +15,25 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* TODO: These overlap with DappPermissions now, make DRY */
|
||||
/* (selection component or just styles?) */
|
||||
.iconDisabled {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
box-shadow: inset 0 0 0 2px rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
@ -33,16 +42,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.selected,
|
||||
.unselected {
|
||||
margin-bottom: 0.25em;
|
||||
width: 100%;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.selected {
|
||||
background: rgba(255, 255, 255, 0.15) !important;
|
||||
box-shadow: inset 0 0 0 2px rgb(255, 255, 255);
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.unselected {
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.iconDisabled {
|
||||
opacity: 0.15;
|
||||
}
|
93
js/src/ui/SelectionList/selectionList.js
Normal file
93
js/src/ui/SelectionList/selectionList.js
Normal file
@ -0,0 +1,93 @@
|
||||
// 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, { Component, PropTypes } from 'react';
|
||||
|
||||
import { CheckIcon, StarIcon, StarOutlineIcon } from '~/ui/Icons';
|
||||
import SectionList from '~/ui/SectionList';
|
||||
import { arrayOrObjectProptype } from '~/util/proptypes';
|
||||
|
||||
import styles from './selectionList.css';
|
||||
|
||||
export default class SelectionList extends Component {
|
||||
static propTypes = {
|
||||
isChecked: PropTypes.func,
|
||||
items: arrayOrObjectProptype().isRequired,
|
||||
noStretch: PropTypes.bool,
|
||||
onDefaultClick: PropTypes.func,
|
||||
onSelectClick: PropTypes.func.isRequired,
|
||||
renderItem: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
render () {
|
||||
const { items, noStretch } = this.props;
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
items={ items }
|
||||
noStretch={ noStretch }
|
||||
renderItem={ this.renderItem }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderItem = (item, index) => {
|
||||
const { isChecked, onDefaultClick, onSelectClick, renderItem } = this.props;
|
||||
const isSelected = isChecked
|
||||
? isChecked(item)
|
||||
: item.checked;
|
||||
|
||||
const makeDefault = () => {
|
||||
onDefaultClick(item);
|
||||
return false;
|
||||
};
|
||||
const selectItem = () => {
|
||||
onSelectClick(item);
|
||||
return false;
|
||||
};
|
||||
|
||||
let defaultIcon = null;
|
||||
|
||||
if (onDefaultClick) {
|
||||
defaultIcon = isSelected && item.default
|
||||
? <StarIcon />
|
||||
: <StarOutlineIcon className={ styles.iconDisabled } onClick={ makeDefault } />;
|
||||
}
|
||||
|
||||
const classes = isSelected
|
||||
? [styles.item, styles.selected]
|
||||
: [styles.item, styles.unselected];
|
||||
|
||||
return (
|
||||
<div className={ classes.join(' ') }>
|
||||
<div
|
||||
className={ styles.content }
|
||||
onClick={ selectItem }
|
||||
>
|
||||
{ renderItem(item, index) }
|
||||
</div>
|
||||
<div className={ styles.overlay }>
|
||||
{ defaultIcon }
|
||||
{
|
||||
isSelected
|
||||
? <CheckIcon onClick={ selectItem } />
|
||||
: <CheckIcon className={ styles.iconDisabled } onClick={ selectItem } />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
100
js/src/ui/SelectionList/selectionList.spec.js
Normal file
100
js/src/ui/SelectionList/selectionList.spec.js
Normal file
@ -0,0 +1,100 @@
|
||||
// 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import SelectionList from './';
|
||||
|
||||
const ITEMS = ['A', 'B', 'C'];
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
let renderItem;
|
||||
let onDefaultClick;
|
||||
let onSelectClick;
|
||||
|
||||
function render (props = {}) {
|
||||
renderItem = sinon.stub();
|
||||
onDefaultClick = sinon.stub();
|
||||
onSelectClick = sinon.stub();
|
||||
|
||||
component = shallow(
|
||||
<SelectionList
|
||||
items={ ITEMS }
|
||||
noStretch='testNoStretch'
|
||||
onDefaultClick={ onDefaultClick }
|
||||
onSelectClick={ onSelectClick }
|
||||
renderItem={ renderItem }
|
||||
/>
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('ui/SelectionList', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('SectionList', () => {
|
||||
let section;
|
||||
|
||||
beforeEach(() => {
|
||||
section = component.find('SectionList');
|
||||
});
|
||||
|
||||
it('renders the SectionList', () => {
|
||||
expect(section.get(0)).to.be.ok;
|
||||
});
|
||||
|
||||
it('passes the items through', () => {
|
||||
expect(section.props().items).to.deep.equal(ITEMS);
|
||||
});
|
||||
|
||||
it('passes internal render method', () => {
|
||||
expect(section.props().renderItem).to.equal(instance.renderItem);
|
||||
});
|
||||
|
||||
it('passes noStretch prop through', () => {
|
||||
expect(section.props().noStretch).to.equal('testNoStretch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('instance methods', () => {
|
||||
describe('renderItem', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
result = instance.renderItem('testItem', 'testIndex');
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('calls into parent renderItem', () => {
|
||||
expect(renderItem).to.have.been.calledWith('testItem', 'testIndex');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -46,6 +46,7 @@ export ParityBackground from './ParityBackground';
|
||||
export Portal from './Portal';
|
||||
export QrCode from './QrCode';
|
||||
export SectionList from './SectionList';
|
||||
export SelectionList from './SelectionList';
|
||||
export ShortenedHash from './ShortenedHash';
|
||||
export SignerIcon from './SignerIcon';
|
||||
export Tags from './Tags';
|
||||
|
@ -37,7 +37,7 @@ export default class AccountStore {
|
||||
@action setDefaultAccount = (defaultAccount) => {
|
||||
transaction(() => {
|
||||
this.accounts = this.accounts.map((account) => {
|
||||
account.default = account.address === defaultAccount;
|
||||
account.checked = account.address === defaultAccount;
|
||||
|
||||
return account;
|
||||
});
|
||||
@ -90,7 +90,7 @@ export default class AccountStore {
|
||||
const account = accounts[address];
|
||||
|
||||
account.address = address;
|
||||
account.default = address === this.defaultAccount;
|
||||
account.checked = address === this.defaultAccount;
|
||||
|
||||
return account;
|
||||
})
|
||||
|
@ -24,7 +24,7 @@ import { connect } from 'react-redux';
|
||||
import store from 'store';
|
||||
|
||||
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
||||
import { AccountCard, Badge, Button, ContainerTitle, IdentityIcon, ParityBackground, SectionList } from '~/ui';
|
||||
import { AccountCard, Badge, Button, ContainerTitle, IdentityIcon, ParityBackground, SelectionList } from '~/ui';
|
||||
import { CancelIcon, FingerprintIcon } from '~/ui/Icons';
|
||||
import DappsStore from '~/views/Dapps/dappsStore';
|
||||
import { Embedded as Signer } from '~/views/Signer';
|
||||
@ -328,10 +328,11 @@ class ParityBar extends Component {
|
||||
{
|
||||
displayType === DISPLAY_ACCOUNTS
|
||||
? (
|
||||
<SectionList
|
||||
<SelectionList
|
||||
className={ styles.accountsSection }
|
||||
items={ this.accountStore.accounts }
|
||||
noStretch
|
||||
onSelectClick={ this.onMakeDefault }
|
||||
renderItem={ this.renderAccount }
|
||||
/>
|
||||
)
|
||||
@ -344,31 +345,23 @@ class ParityBar extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
onMakeDefault = (account) => {
|
||||
this.toggleAccountsDisplay();
|
||||
|
||||
return this.accountStore
|
||||
.makeDefaultAccount(account.address)
|
||||
.then(() => this.accountStore.loadAccounts());
|
||||
}
|
||||
|
||||
renderAccount = (account) => {
|
||||
const { balances } = this.props;
|
||||
const balance = balances[account.address];
|
||||
const makeDefaultAccount = () => {
|
||||
this.toggleAccountsDisplay();
|
||||
return this.accountStore
|
||||
.makeDefaultAccount(account.address)
|
||||
.then(() => this.accountStore.loadAccounts());
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={ styles.account }
|
||||
onClick={ makeDefaultAccount }
|
||||
>
|
||||
<AccountCard
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
className={
|
||||
account.default
|
||||
? styles.selected
|
||||
: styles.unselected
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<AccountCard
|
||||
account={ account }
|
||||
balance={ balance }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user