Vault Management UI (first round) (#4446)

* Add RPCs for parity_vault (create, open, list, etc.)

* WIP

* WIP

* WIP

* WIP (create should create)

* Create & close working

* WIP

* WIP

* WIP

* Open & Close now working

* WIP

* WIP

* Merge relevant changes from js-home

* Hover actions

* WIP (start of account assignment)

* Open, Close & Account assignment works

* Fix margins

* UI updates

* Update tests

* Add the parity_{get|set}VaultMeta calls

* Handle metadata

* Adjust padding in Open/Close modals

* moveAccounts take both in and out

* Adjust padding

* Fix stretch

* Optimize hover stretch

* pre-merge

* Cleanup variable naming (duplication)

* Rename Vault{Close,Open} -> Vault{Lock,Unlock}

* clearVaultFields uses setters

* TODO for small Portal sizes

* Vaults rendering tests

* .only

* libusb compile

* VaultCard rendering tests

* Update message keys (rename gone rouge)

* Display passwordHint op vault unlock

* Update failing tests

* Manually dispatch allAccountsInfo when move completed

* Open/Close always shows vault image in colour

* Password submit submits modal (PR comment)

* Add link to account
This commit is contained in:
Jaco Greeff
2017-02-20 16:40:01 +01:00
committed by Gav Wood
parent ac6180a6fe
commit 9e210e5eda
52 changed files with 3722 additions and 192 deletions

View File

@@ -14,6 +14,7 @@
/* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/
.body {
text-align: center;
}

View File

@@ -20,7 +20,7 @@ import { FormattedMessage } from 'react-intl';
import { nodeOrStringProptype } from '~/util/proptypes';
import Button from '../Button';
import Modal from '../Modal';
import Portal from '../Portal';
import { CancelIcon, CheckIcon } from '../Icons';
import styles from './confirmDialog.css';
@@ -42,47 +42,58 @@ export default class ConfirmDialog extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
className: PropTypes.string,
disabledConfirm: PropTypes.bool,
disabledDeny: PropTypes.bool,
busy: PropTypes.bool,
iconConfirm: PropTypes.node,
iconDeny: PropTypes.node,
labelConfirm: PropTypes.string,
labelDeny: PropTypes.string,
onConfirm: PropTypes.func.isRequired,
onDeny: PropTypes.func.isRequired,
open: PropTypes.bool,
title: nodeOrStringProptype().isRequired,
visible: PropTypes.bool.isRequired
visible: PropTypes.bool
}
render () {
const { children, className, title, visible } = this.props;
const { busy, children, className, disabledConfirm, disabledDeny, iconConfirm, iconDeny, labelConfirm, labelDeny, onConfirm, onDeny, open, title, visible } = this.props;
// TODO: visible is for compatibility with existing, open aligns with Portal.
// (Cleanup once all uses of ConfirmDialog has been migrated)
if (!visible && !open) {
return null;
}
return (
<Modal
<Portal
buttons={ [
<Button
disabled={ disabledDeny }
icon={ iconDeny || <CancelIcon /> }
key='deny'
label={ labelDeny || DEFAULT_NO }
onClick={ onDeny }
/>,
<Button
disabled={ disabledConfirm }
icon={ iconConfirm || <CheckIcon /> }
key='confirm'
label={ labelConfirm || DEFAULT_YES }
onClick={ onConfirm }
/>
] }
busy={ busy }
className={ className }
actions={ this.renderActions() }
isSmallModal
onClose={ onDeny }
title={ title }
visible={ visible }
open
>
<div className={ styles.body }>
{ children }
</div>
</Modal>
</Portal>
);
}
renderActions () {
const { iconConfirm, iconDeny, labelConfirm, labelDeny, onConfirm, onDeny } = this.props;
return [
<Button
icon={ iconDeny || <CancelIcon /> }
label={ labelDeny || DEFAULT_NO }
onClick={ onDeny }
/>,
<Button
icon={ iconConfirm || <CheckIcon /> }
label={ labelConfirm || DEFAULT_YES }
onClick={ onConfirm }
/>
];
}
}

View File

@@ -15,41 +15,24 @@
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { shallow } from 'enzyme';
import React, { PropTypes } from 'react';
import React from 'react';
import sinon from 'sinon';
import muiTheme from '../Theme';
import ConfirmDialog from './';
let component;
let instance;
let onConfirm;
let onDeny;
function createRedux () {
return {
dispatch: sinon.stub(),
subscribe: sinon.stub(),
getState: () => {
return {
settings: {
backgroundSeed: 'xyz'
}
};
}
};
}
function render (props = {}) {
onConfirm = sinon.stub();
onDeny = sinon.stub();
if (props.visible === undefined) {
props.visible = true;
if (props.open === undefined) {
props.open = true;
}
const baseComponent = shallow(
component = shallow(
<ConfirmDialog
{ ...props }
title='test title'
@@ -62,57 +45,54 @@ function render (props = {}) {
</ConfirmDialog>
);
instance = baseComponent.instance();
component = baseComponent.find('Connect(Modal)').shallow({
childContextTypes: {
muiTheme: PropTypes.object,
store: PropTypes.object
},
context: {
muiTheme,
store: createRedux()
}
});
return component;
}
describe('ui/ConfirmDialog', () => {
beforeEach(() => {
render();
});
it('renders defaults', () => {
expect(render()).to.be.ok;
expect(component).to.be.ok;
});
it('renders the body as provided', () => {
expect(render().find('div[id="testContent"]').text()).to.equal('some test content');
expect(component.find('div[id="testContent"]').text()).to.equal('some test content');
});
describe('properties', () => {
describe('Portal properties', () => {
let props;
beforeEach(() => {
props = render().props();
});
it('passes the actions', () => {
expect(props.actions).to.deep.equal(instance.renderActions());
props = component.find('Portal').props();
});
it('passes title', () => {
expect(props.title).to.equal('test title');
});
it('passes visiblity flag', () => {
expect(props.visible).to.be.true;
it('passes open flag', () => {
expect(props.open).to.be.true;
});
});
describe('renderActions', () => {
describe('defaults', () => {
it('passes the small flag', () => {
expect(props.isSmallModal).to.be.true;
});
it('maps onClose to onDeny', () => {
expect(props.onClose).to.equal(onDeny);
});
describe('buttons', () => {
let buttons;
beforeEach(() => {
render();
buttons = instance.renderActions();
buttons = component.props().buttons;
});
it('passes the buttons', () => {
expect(buttons.length).to.equal(2);
});
it('renders with supplied onConfim/onDeny callbacks', () => {
@@ -129,29 +109,27 @@ describe('ui/ConfirmDialog', () => {
expect(buttons[0].props.icon.type.displayName).to.equal('ContentClear');
expect(buttons[1].props.icon.type.displayName).to.equal('NavigationCheck');
});
});
describe('overrides', () => {
let buttons;
beforeEach(() => {
render({
labelConfirm: 'labelConfirm',
labelDeny: 'labelDeny',
iconConfirm: 'iconConfirm',
iconDeny: 'iconDeny'
describe('overrides', () => {
beforeEach(() => {
render({
labelConfirm: 'labelConfirm',
labelDeny: 'labelDeny',
iconConfirm: 'iconConfirm',
iconDeny: 'iconDeny'
});
buttons = component.props().buttons;
});
buttons = instance.renderActions();
});
it('renders supplied labels', () => {
expect(buttons[0].props.label).to.equal('labelDeny');
expect(buttons[1].props.label).to.equal('labelConfirm');
});
it('renders supplied labels', () => {
expect(buttons[0].props.label).to.equal('labelDeny');
expect(buttons[1].props.label).to.equal('labelConfirm');
});
it('renders supplied icons', () => {
expect(buttons[0].props.icon).to.equal('iconDeny');
expect(buttons[1].props.icon).to.equal('iconConfirm');
it('renders supplied icons', () => {
expect(buttons[0].props.icon).to.equal('iconDeny');
expect(buttons[1].props.icon).to.equal('iconConfirm');
});
});
});
});