Default Account selector in Signer overlay (#4375)
* Manage default accounts * Portal * Portal * Allow Portal to be used in as both top-level and popover * modal/popover variable naming * Move to Portal * export Portal in ~/ui * WIP * Tags handle empty values * Export AccountCard in ~/ui * Allow ETH-only & zero display * Use ui/Balance for balance display * Add tests for Balance & Tags component availability * WIP * Default overlay display to block (not flex) * Revert block * WIP * Add className, optional handlers only * WIP * Properly handle optional onKeyDown * Selection updated * Align margins * Remove old code * Remove debug logging * TransitionGroup for animations * No anim * Cleanups * Revert addons removal * Fix tests * defaultAccount * Selection actually selects * WIP tests * tests WIP * Expand tests * Container for scrollbars * Add parity_defaultAccount RPC (with subscription) * Add jsonrpc interface
This commit is contained in:
parent
a414729de9
commit
3bdd32f9ec
101
js/src/views/ParityBar/accountStore.js
Normal file
101
js/src/views/ParityBar/accountStore.js
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// 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 { action, observable, transaction } from 'mobx';
|
||||||
|
|
||||||
|
export default class AccountStore {
|
||||||
|
@observable accounts = [];
|
||||||
|
@observable defaultAccount = null;
|
||||||
|
@observable isLoading = false;
|
||||||
|
|
||||||
|
constructor (api) {
|
||||||
|
this._api = api;
|
||||||
|
|
||||||
|
this.loadAccounts();
|
||||||
|
this.subscribeDefaultAccount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setAccounts = (accounts) => {
|
||||||
|
this.accounts = accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setDefaultAccount = (defaultAccount) => {
|
||||||
|
this.defaultAccount = defaultAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action setLoading = (isLoading) => {
|
||||||
|
this.isLoading = isLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeDefaultAccount = (address) => {
|
||||||
|
const accounts = [address].concat(
|
||||||
|
this.accounts
|
||||||
|
.filter((account) => account.address !== address)
|
||||||
|
.map((account) => account.address)
|
||||||
|
);
|
||||||
|
|
||||||
|
return this._api.parity
|
||||||
|
.setNewDappsWhitelist(accounts)
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('makeDefaultAccount', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
loadAccounts () {
|
||||||
|
this.setLoading(true);
|
||||||
|
|
||||||
|
return Promise
|
||||||
|
.all([
|
||||||
|
this._api.parity.getNewDappsWhitelist(),
|
||||||
|
this._api.parity.allAccountsInfo()
|
||||||
|
])
|
||||||
|
.then(([whitelist, accounts]) => {
|
||||||
|
transaction(() => {
|
||||||
|
this.setLoading(false);
|
||||||
|
this.setAccounts(
|
||||||
|
Object
|
||||||
|
.keys(accounts)
|
||||||
|
.filter((address) => {
|
||||||
|
const isAccount = accounts[address].uuid;
|
||||||
|
const isWhitelisted = !whitelist || whitelist.includes(address);
|
||||||
|
|
||||||
|
return isAccount && isWhitelisted;
|
||||||
|
})
|
||||||
|
.map((address) => {
|
||||||
|
const account = accounts[address];
|
||||||
|
|
||||||
|
account.address = address;
|
||||||
|
account.default = address === this.defaultAccount;
|
||||||
|
|
||||||
|
return account;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
this.setLoading(false);
|
||||||
|
console.warn('loadAccounts', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeDefaultAccount () {
|
||||||
|
return this._api.subscribe('parity_defaultAccount', (error, defaultAccount) => {
|
||||||
|
if (!error) {
|
||||||
|
this.setDefaultAccount(defaultAccount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
104
js/src/views/ParityBar/accountStore.spec.js
Normal file
104
js/src/views/ParityBar/accountStore.spec.js
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// 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 sinon from 'sinon';
|
||||||
|
|
||||||
|
import AccountStore from './accountStore';
|
||||||
|
|
||||||
|
import { ACCOUNT_DEFAULT, ACCOUNT_FIRST, ACCOUNT_NEW, createApi } from './parityBar.test.js';
|
||||||
|
|
||||||
|
let api;
|
||||||
|
let store;
|
||||||
|
|
||||||
|
function create () {
|
||||||
|
api = createApi();
|
||||||
|
store = new AccountStore(api);
|
||||||
|
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('views/ParityBar/AccountStore', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
create();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('subscribes to defaultAccount', () => {
|
||||||
|
expect(api.subscribe).to.have.been.calledWith('parity_defaultAccount');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('@action', () => {
|
||||||
|
describe('setAccounts', () => {
|
||||||
|
it('sets the accounts', () => {
|
||||||
|
store.setAccounts('testing');
|
||||||
|
expect(store.accounts).to.equal('testing');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setDefaultAccount', () => {
|
||||||
|
it('sets the default account', () => {
|
||||||
|
store.setDefaultAccount('testing');
|
||||||
|
expect(store.defaultAccount).to.equal('testing');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setLoading', () => {
|
||||||
|
it('sets the loading status', () => {
|
||||||
|
store.setLoading('testing');
|
||||||
|
expect(store.isLoading).to.equal('testing');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('operations', () => {
|
||||||
|
describe('loadAccounts', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
sinon.spy(store, 'setAccounts');
|
||||||
|
|
||||||
|
return store.loadAccounts();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
store.setAccounts.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into parity_getNewDappsWhitelist', () => {
|
||||||
|
expect(api.parity.getNewDappsWhitelist).to.have.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into parity_allAccountsInfo', () => {
|
||||||
|
expect(api.parity.allAccountsInfo).to.have.been.called;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets the accounts', () => {
|
||||||
|
expect(store.setAccounts).to.have.been.called;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('makeDefaultAccount', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
return store.makeDefaultAccount(ACCOUNT_NEW);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls into parity_setNewDappsWhitelist (with ordering)', () => {
|
||||||
|
expect(api.parity.setNewDappsWhitelist).to.have.been.calledWith([
|
||||||
|
ACCOUNT_NEW, ACCOUNT_FIRST, ACCOUNT_DEFAULT
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -15,6 +15,44 @@
|
|||||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.account {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.accountOverlay {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.5em;
|
||||||
|
top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iconDisabled {
|
||||||
|
opacity: 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected,
|
||||||
|
.unselected {
|
||||||
|
margin: 0.125em 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unselected {
|
||||||
|
background: rgba(0, 0, 0, 0.4) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background: rgba(255, 255, 255, 0.35) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -26,7 +64,8 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar, .expanded {
|
.bar,
|
||||||
|
.expanded {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-family: 'Roboto', sans-serif;
|
font-family: 'Roboto', sans-serif;
|
||||||
@ -110,7 +149,9 @@
|
|||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button, .parityButton {
|
.button,
|
||||||
|
.iconButton,
|
||||||
|
.parityButton {
|
||||||
overflow: visible !important;
|
overflow: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +164,14 @@
|
|||||||
fill: white !important;
|
fill: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.iconButton {
|
||||||
|
min-width: 2em !important;
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin: 6px 0.5em 0 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -151,7 +200,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.header, .corner {
|
.header,
|
||||||
|
.corner {
|
||||||
button {
|
button {
|
||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
@ -180,7 +230,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.parityIcon, .signerIcon {
|
.parityIcon,
|
||||||
|
.signerIcon {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
@ -14,25 +14,30 @@
|
|||||||
// 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 { throttle } from 'lodash';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { throttle } from 'lodash';
|
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
|
|
||||||
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
import imagesEthcoreBlock from '~/../assets/images/parity-logo-white-no-text.svg';
|
||||||
|
import { AccountCard, Badge, Button, ContainerTitle, IdentityIcon, ParityBackground, SectionList } from '~/ui';
|
||||||
import { CancelIcon, FingerprintIcon } from '~/ui/Icons';
|
import { CancelIcon, FingerprintIcon } from '~/ui/Icons';
|
||||||
import { Badge, Button, ContainerTitle, ParityBackground } from '~/ui';
|
|
||||||
import { Embedded as Signer } from '../Signer';
|
|
||||||
import DappsStore from '~/views/Dapps/dappsStore';
|
import DappsStore from '~/views/Dapps/dappsStore';
|
||||||
|
import { Embedded as Signer } from '~/views/Signer';
|
||||||
|
|
||||||
|
import AccountStore from './accountStore';
|
||||||
import styles from './parityBar.css';
|
import styles from './parityBar.css';
|
||||||
|
|
||||||
const LS_STORE_KEY = '_parity::parityBar';
|
const LS_STORE_KEY = '_parity::parityBar';
|
||||||
const DEFAULT_POSITION = { right: '1em', bottom: 0 };
|
const DEFAULT_POSITION = { right: '1em', bottom: 0 };
|
||||||
|
const DISPLAY_ACCOUNTS = 'accounts';
|
||||||
|
const DISPLAY_SIGNER = 'signer';
|
||||||
|
|
||||||
|
@observer
|
||||||
class ParityBar extends Component {
|
class ParityBar extends Component {
|
||||||
app = null;
|
app = null;
|
||||||
measures = null;
|
measures = null;
|
||||||
@ -49,6 +54,7 @@ class ParityBar extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
displayType: DISPLAY_SIGNER,
|
||||||
moving: false,
|
moving: false,
|
||||||
opened: false,
|
opened: false,
|
||||||
position: DEFAULT_POSITION
|
position: DEFAULT_POSITION
|
||||||
@ -67,6 +73,8 @@ class ParityBar extends Component {
|
|||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
const { api } = this.context;
|
const { api } = this.context;
|
||||||
|
|
||||||
|
this.accountStore = new AccountStore(api);
|
||||||
|
|
||||||
// Hook to the dapp loaded event to position the
|
// Hook to the dapp loaded event to position the
|
||||||
// Parity Bar accordingly
|
// Parity Bar accordingly
|
||||||
DappsStore.get(api).on('loaded', (app) => {
|
DappsStore.get(api).on('loaded', (app) => {
|
||||||
@ -84,14 +92,14 @@ class ParityBar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (count < newCount) {
|
if (count < newCount) {
|
||||||
this.setOpened(true);
|
this.setOpened(true, DISPLAY_SIGNER);
|
||||||
} else if (newCount === 0 && count === 1) {
|
} else if (newCount === 0 && count === 1) {
|
||||||
this.setOpened(false);
|
this.setOpened(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpened (opened) {
|
setOpened (opened, displayType = DISPLAY_SIGNER) {
|
||||||
this.setState({ opened });
|
this.setState({ displayType, opened });
|
||||||
|
|
||||||
if (!this.bar) {
|
if (!this.bar) {
|
||||||
return;
|
return;
|
||||||
@ -186,9 +194,19 @@ class ParityBar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={ styles.cornercolor }>
|
||||||
className={ styles.cornercolor }
|
<Button
|
||||||
>
|
className={ styles.iconButton }
|
||||||
|
icon={
|
||||||
|
<IdentityIcon
|
||||||
|
address={ this.accountStore.defaultAccount }
|
||||||
|
button
|
||||||
|
center
|
||||||
|
inline
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onClick={ this.toggleAccountsDisplay }
|
||||||
|
/>
|
||||||
{
|
{
|
||||||
this.renderLink(
|
this.renderLink(
|
||||||
<Button
|
<Button
|
||||||
@ -214,7 +232,7 @@ class ParityBar extends Component {
|
|||||||
className={ styles.button }
|
className={ styles.button }
|
||||||
icon={ <FingerprintIcon /> }
|
icon={ <FingerprintIcon /> }
|
||||||
label={ this.renderSignerLabel() }
|
label={ this.renderSignerLabel() }
|
||||||
onClick={ this.toggleDisplay }
|
onClick={ this.toggleSignerDisplay }
|
||||||
/>
|
/>
|
||||||
{ this.renderDrag() }
|
{ this.renderDrag() }
|
||||||
</div>
|
</div>
|
||||||
@ -267,27 +285,85 @@ class ParityBar extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderExpanded () {
|
renderExpanded () {
|
||||||
|
const { displayType } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={ styles.container }>
|
||||||
<div className={ styles.header }>
|
<div className={ styles.header }>
|
||||||
<div className={ styles.title }>
|
<div className={ styles.title }>
|
||||||
<ContainerTitle title='Parity Signer: Pending' />
|
<ContainerTitle
|
||||||
|
title={
|
||||||
|
displayType === DISPLAY_ACCOUNTS
|
||||||
|
? (
|
||||||
|
<FormattedMessage
|
||||||
|
id='parityBar.title.accounts'
|
||||||
|
defaultMessage='Default Account'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<FormattedMessage
|
||||||
|
id='parityBar.title.signer'
|
||||||
|
defaultMessage='Parity Signer: Pending'
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className={ styles.actions }>
|
<div className={ styles.actions }>
|
||||||
<Button
|
<Button
|
||||||
icon={ <CancelIcon /> }
|
icon={ <CancelIcon /> }
|
||||||
label='Close'
|
label={
|
||||||
onClick={ this.toggleDisplay }
|
<FormattedMessage
|
||||||
|
id='parityBar.button.close'
|
||||||
|
defaultMessage='Close'
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onClick={ this.toggleSignerDisplay }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={ styles.content }>
|
<div className={ styles.content }>
|
||||||
|
{
|
||||||
|
displayType === DISPLAY_ACCOUNTS
|
||||||
|
? (
|
||||||
|
<SectionList
|
||||||
|
items={ this.accountStore.accounts }
|
||||||
|
noStretch
|
||||||
|
renderItem={ this.renderAccount }
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
: (
|
||||||
<Signer />
|
<Signer />
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderAccount = (account) => {
|
||||||
|
const onMakeDefault = () => {
|
||||||
|
this.toggleAccountsDisplay();
|
||||||
|
this.accountStore.makeDefaultAccount(account.address);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={ styles.account }
|
||||||
|
onClick={ onMakeDefault }
|
||||||
|
>
|
||||||
|
<AccountCard
|
||||||
|
account={ account }
|
||||||
|
className={
|
||||||
|
account.default
|
||||||
|
? styles.selected
|
||||||
|
: styles.unselected
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
renderLabel (name, bubble) {
|
renderLabel (name, bubble) {
|
||||||
return (
|
return (
|
||||||
<div className={ styles.label }>
|
<div className={ styles.label }>
|
||||||
@ -497,10 +573,20 @@ class ParityBar extends Component {
|
|||||||
this.savePosition(position);
|
this.savePosition(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDisplay = () => {
|
toggleAccountsDisplay = () => {
|
||||||
const { opened } = this.state;
|
const { opened } = this.state;
|
||||||
|
|
||||||
this.setOpened(!opened);
|
this.setOpened(!opened, DISPLAY_ACCOUNTS);
|
||||||
|
|
||||||
|
if (!opened) {
|
||||||
|
this.accountStore.loadAccounts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSignerDisplay = () => {
|
||||||
|
const { opened } = this.state;
|
||||||
|
|
||||||
|
this.setOpened(!opened, DISPLAY_SIGNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
get config () {
|
get config () {
|
||||||
@ -542,13 +628,22 @@ class ParityBar extends Component {
|
|||||||
stringToPosition (value) {
|
stringToPosition (value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case 'top-left':
|
case 'top-left':
|
||||||
return { top: 0, left: '1em' };
|
return {
|
||||||
|
left: '1em',
|
||||||
|
top: 0
|
||||||
|
};
|
||||||
|
|
||||||
case 'top-right':
|
case 'top-right':
|
||||||
return { top: 0, right: '1em' };
|
return {
|
||||||
|
right: '1em',
|
||||||
|
top: 0
|
||||||
|
};
|
||||||
|
|
||||||
case 'bottom-left':
|
case 'bottom-left':
|
||||||
return { bottom: 0, left: '1em' };
|
return {
|
||||||
|
bottom: 0,
|
||||||
|
left: '1em'
|
||||||
|
};
|
||||||
|
|
||||||
case 'bottom-right':
|
case 'bottom-right':
|
||||||
default:
|
default:
|
||||||
|
@ -20,6 +20,9 @@ import sinon from 'sinon';
|
|||||||
|
|
||||||
import ParityBar from './';
|
import ParityBar from './';
|
||||||
|
|
||||||
|
import { createApi } from './parityBar.test.js';
|
||||||
|
|
||||||
|
let api;
|
||||||
let component;
|
let component;
|
||||||
let instance;
|
let instance;
|
||||||
let store;
|
let store;
|
||||||
@ -35,6 +38,7 @@ function createRedux (state = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function render (props = {}, state = {}) {
|
function render (props = {}, state = {}) {
|
||||||
|
api = createApi();
|
||||||
component = shallow(
|
component = shallow(
|
||||||
<ParityBar { ...props } />,
|
<ParityBar { ...props } />,
|
||||||
{
|
{
|
||||||
@ -42,7 +46,7 @@ function render (props = {}, state = {}) {
|
|||||||
store: createRedux(state)
|
store: createRedux(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).find('ParityBar').shallow({ context: { api: {} } });
|
).find('ParityBar').shallow({ context: { api } });
|
||||||
instance = component.instance();
|
instance = component.instance();
|
||||||
|
|
||||||
return component;
|
return component;
|
||||||
@ -77,8 +81,14 @@ describe('views/ParityBar', () => {
|
|||||||
expect(bar.find('div')).not.to.have.length(0);
|
expect(bar.find('div')).not.to.have.length(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders the Account selector button', () => {
|
||||||
|
const icon = bar.find('Button').first().props().icon;
|
||||||
|
|
||||||
|
expect(icon.type.displayName).to.equal('Connect(IdentityIcon)');
|
||||||
|
});
|
||||||
|
|
||||||
it('renders the Parity button', () => {
|
it('renders the Parity button', () => {
|
||||||
const label = shallow(bar.find('Button').first().props().label);
|
const label = shallow(bar.find('Button').at(1).props().label);
|
||||||
|
|
||||||
expect(label.find('FormattedMessage').props().id).to.equal('parityBar.label.parity');
|
expect(label.find('FormattedMessage').props().id).to.equal('parityBar.label.parity');
|
||||||
});
|
});
|
||||||
|
55
js/src/views/ParityBar/parityBar.test.js
Normal file
55
js/src/views/ParityBar/parityBar.test.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// 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 sinon from 'sinon';
|
||||||
|
|
||||||
|
const ACCOUNT_DEFAULT = '0x2345678901';
|
||||||
|
const ACCOUNT_FIRST = '0x1234567890';
|
||||||
|
const ACCOUNT_NEW = '0x0987654321';
|
||||||
|
const ACCOUNTS = {
|
||||||
|
[ACCOUNT_FIRST]: { uuid: 123 },
|
||||||
|
[ACCOUNT_DEFAULT]: { uuid: 234 },
|
||||||
|
'0x3456789012': {},
|
||||||
|
[ACCOUNT_NEW]: { uuid: 456 }
|
||||||
|
};
|
||||||
|
|
||||||
|
function createApi () {
|
||||||
|
const api = {
|
||||||
|
subscribe: (params, callback) => {
|
||||||
|
callback(null, ACCOUNT_DEFAULT);
|
||||||
|
|
||||||
|
return Promise.resolve(1);
|
||||||
|
},
|
||||||
|
parity: {
|
||||||
|
defaultAccount: sinon.stub().resolves(ACCOUNT_DEFAULT),
|
||||||
|
allAccountsInfo: sinon.stub().resolves(ACCOUNTS),
|
||||||
|
getNewDappsWhitelist: sinon.stub().resolves(null),
|
||||||
|
setNewDappsWhitelist: sinon.stub().resolves(true)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sinon.spy(api, 'subscribe');
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
ACCOUNT_DEFAULT,
|
||||||
|
ACCOUNT_FIRST,
|
||||||
|
ACCOUNT_NEW,
|
||||||
|
ACCOUNTS,
|
||||||
|
createApi
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user