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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
/>
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,6 +26,7 @@ $smallFontSize: 0.75rem;
|
||||
color: $bylineColor;
|
||||
display: -webkit-box;
|
||||
line-height: $bylineLineHeight;
|
||||
min-height: $bylineMaxHeight;
|
||||
max-height: $bylineMaxHeight;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
@@ -45,5 +46,8 @@ $smallFontSize: 0.75rem;
|
||||
.title {
|
||||
line-height: $titleLineHeight;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -16,33 +16,39 @@
|
||||
*/
|
||||
|
||||
$background: rgba(18, 18, 18, 0.85);
|
||||
$backgroundOverlay: rgba(18, 18, 18, 1);
|
||||
$backgroundHover: rgba(18, 18, 18, 1);
|
||||
$transitionAll: all 0.75s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
|
||||
.container {
|
||||
background: $background;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
padding: 0em;
|
||||
transition: all 0.75s cubic-bezier(0.23, 1, 0.32, 1);
|
||||
position: relative;
|
||||
transition: $transitionAll;
|
||||
width: 100%;
|
||||
|
||||
.hoverOverlay {
|
||||
background: $backgroundOverlay;
|
||||
display: none;
|
||||
background: $background;
|
||||
left: 0;
|
||||
margin-top: -1.5em;
|
||||
opacity: inherit;
|
||||
padding: 0 1.5em 1.5em 1.5em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
transition: $transitionAll;
|
||||
transform: scale(0.5, 0);
|
||||
transform-origin: top center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $backgroundOverlay;
|
||||
background: $backgroundHover;
|
||||
|
||||
.hoverOverlay {
|
||||
display: block;
|
||||
background: $backgroundHover;
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/>.
|
||||
|
||||
export AccountsIcon from 'material-ui/svg-icons/action/account-balance-wallet';
|
||||
export AddIcon from 'material-ui/svg-icons/content/add';
|
||||
export AttachFileIcon from 'material-ui/svg-icons/editor/attach-file';
|
||||
export CancelIcon from 'material-ui/svg-icons/content/clear';
|
||||
@@ -28,6 +29,7 @@ export DeleteIcon from 'material-ui/svg-icons/action/delete';
|
||||
export DoneIcon from 'material-ui/svg-icons/action/done-all';
|
||||
export EditIcon from 'material-ui/svg-icons/content/create';
|
||||
export FingerprintIcon from 'material-ui/svg-icons/action/fingerprint';
|
||||
export KeyIcon from 'material-ui/svg-icons/communication/vpn-key';
|
||||
export LinkIcon from 'material-ui/svg-icons/content/link';
|
||||
export LockedIcon from 'material-ui/svg-icons/action/lock';
|
||||
export MoveIcon from 'material-ui/svg-icons/action/open-with';
|
||||
@@ -41,6 +43,7 @@ export SnoozeIcon from 'material-ui/svg-icons/av/snooze';
|
||||
export StarCircleIcon from 'material-ui/svg-icons/action/stars';
|
||||
export StarIcon from 'material-ui/svg-icons/toggle/star';
|
||||
export StarOutlineIcon from 'material-ui/svg-icons/toggle/star-border';
|
||||
export UnlockedIcon from 'material-ui/svg-icons/action/lock-open';
|
||||
export VerifyIcon from 'material-ui/svg-icons/action/verified-user';
|
||||
export VisibleIcon from 'material-ui/svg-icons/image/remove-red-eye';
|
||||
export VpnIcon from 'material-ui/svg-icons/notification/vpn-lock';
|
||||
|
||||
28
js/src/ui/Icons/index.spec.js
Normal file
28
js/src/ui/Icons/index.spec.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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 { createElement } from 'react';
|
||||
|
||||
import * as Icons from './';
|
||||
|
||||
describe('ui/Icons', () => {
|
||||
Object.keys(Icons).forEach((icon) => {
|
||||
it(`contains & renders ${icon}`, () => {
|
||||
expect(shallow(createElement(Icons[icon]))).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -31,22 +31,20 @@ export default class Page extends Component {
|
||||
|
||||
render () {
|
||||
const { buttons, className, children, title } = this.props;
|
||||
const classes = `${styles.layout} ${className}`;
|
||||
let actionbar = null;
|
||||
|
||||
if (title || buttons) {
|
||||
actionbar = (
|
||||
<Actionbar
|
||||
buttons={ buttons }
|
||||
title={ title }
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ actionbar }
|
||||
<div className={ classes }>
|
||||
{
|
||||
title || buttons
|
||||
? (
|
||||
<Actionbar
|
||||
buttons={ buttons }
|
||||
title={ title }
|
||||
/>
|
||||
)
|
||||
: null
|
||||
}
|
||||
<div className={ [styles.layout, className].join(' ') }>
|
||||
{ children }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
79
js/src/ui/Page/page.spec.js
Normal file
79
js/src/ui/Page/page.spec.js
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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 Page from './';
|
||||
|
||||
const BUTTONS = ['buttonA', 'buttonB'];
|
||||
const CLASSNAME = 'testClass';
|
||||
const TESTTEXT = 'testing children';
|
||||
const TITLE = 'test title';
|
||||
|
||||
let component;
|
||||
|
||||
function render () {
|
||||
component = shallow(
|
||||
<Page
|
||||
buttons={ BUTTONS }
|
||||
className={ CLASSNAME }
|
||||
title={ TITLE }
|
||||
>
|
||||
<div id='testContent'>
|
||||
{ TESTTEXT }
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('ui/Page', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
it('renders the children', () => {
|
||||
expect(component.find('div[id="testContent"]').text()).to.equal(TESTTEXT);
|
||||
});
|
||||
|
||||
describe('components', () => {
|
||||
describe('ActionBar', () => {
|
||||
let actions;
|
||||
|
||||
beforeEach(() => {
|
||||
actions = component.find('Actionbar');
|
||||
});
|
||||
|
||||
it('renders the actionbar', () => {
|
||||
expect(actions.get(0)).to.be.ok;
|
||||
});
|
||||
|
||||
it('passes the provided title', () => {
|
||||
expect(actions.props().title).to.equal(TITLE);
|
||||
});
|
||||
|
||||
it('passed the provided buttons', () => {
|
||||
expect(actions.props().buttons).to.equal(BUTTONS);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
$modalMargin: 1.5em;
|
||||
$modalPadding: 1.5em;
|
||||
$modalPaddingChild: 3em;
|
||||
$modalBackZ: 2500;
|
||||
|
||||
/* This should be the default case, the Portal used as a stand-alone modal */
|
||||
@@ -50,7 +51,7 @@ $popoverZ: 3600;
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 0.25;
|
||||
z-index: -1;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
@@ -67,11 +68,24 @@ $popoverZ: 3600;
|
||||
}
|
||||
|
||||
&.modal {
|
||||
bottom: $modalBottom;
|
||||
left: $modalLeft;
|
||||
right: $modalRight;
|
||||
top: $modalTop;
|
||||
z-index: $modalZ;
|
||||
&:not(.small) {
|
||||
bottom: $modalBottom;
|
||||
left: $modalLeft;
|
||||
right: $modalRight;
|
||||
top: $modalTop;
|
||||
z-index: $modalZ;
|
||||
}
|
||||
|
||||
/* TODO: Small Portals don't adjust their overall height like we have with the
|
||||
/* rest, so really tiny screens and large small Portals (it shouldn't be be done,
|
||||
/* but may well be) will scretch to non-visible areas.
|
||||
*/
|
||||
&.small {
|
||||
margin: 1.5em auto;
|
||||
max-width: 768px;
|
||||
position: relative;
|
||||
width: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
&.popover {
|
||||
@@ -100,8 +114,11 @@ $popoverZ: 3600;
|
||||
|
||||
.childContainer {
|
||||
flex: 1;
|
||||
margin: 0 -$modalPadding;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
padding: 0 $modalPaddingChild;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.closeIcon {
|
||||
|
||||
@@ -43,6 +43,7 @@ export default class Portal extends Component {
|
||||
className: PropTypes.string,
|
||||
hideClose: PropTypes.bool,
|
||||
isChildModal: PropTypes.bool,
|
||||
isSmallModal: PropTypes.bool,
|
||||
onKeyDown: PropTypes.func,
|
||||
steps: PropTypes.array,
|
||||
title: nodeOrStringProptype()
|
||||
@@ -63,7 +64,7 @@ export default class Portal extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { activeStep, busy, busySteps, children, className, isChildModal, open, steps, title } = this.props;
|
||||
const { activeStep, busy, busySteps, children, className, isChildModal, isSmallModal, open, steps, title } = this.props;
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
@@ -85,6 +86,9 @@ export default class Portal extends Component {
|
||||
isChildModal
|
||||
? styles.popover
|
||||
: styles.modal,
|
||||
isSmallModal
|
||||
? styles.small
|
||||
: null,
|
||||
className
|
||||
].join(' ')
|
||||
}
|
||||
|
||||
@@ -15,12 +15,17 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.title {
|
||||
.title,
|
||||
.subtitle {
|
||||
.steps {
|
||||
margin: -0.5em 0 -1em 0;
|
||||
}
|
||||
|
||||
.waiting {
|
||||
margin: 1em -1em -1em -1em;
|
||||
margin: 1em -1.5em 0 -1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
@@ -30,15 +30,18 @@ import styles from './title.css';
|
||||
export default class Title extends Component {
|
||||
static propTypes = {
|
||||
activeStep: PropTypes.number,
|
||||
description: nodeOrStringProptype(),
|
||||
busy: PropTypes.bool,
|
||||
busySteps: PropTypes.array,
|
||||
byline: nodeOrStringProptype(),
|
||||
className: PropTypes.string,
|
||||
isSubTitle: PropTypes.bool,
|
||||
steps: PropTypes.array,
|
||||
title: nodeOrStringProptype()
|
||||
}
|
||||
|
||||
render () {
|
||||
const { activeStep, className, steps, title } = this.props;
|
||||
const { activeStep, byline, className, description, isSubTitle, steps, title } = this.props;
|
||||
|
||||
if (!title && !steps) {
|
||||
return null;
|
||||
@@ -47,10 +50,17 @@ export default class Title extends Component {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
[styles.title, className].join(' ')
|
||||
[
|
||||
isSubTitle
|
||||
? styles.subtitle
|
||||
: styles.title,
|
||||
className
|
||||
].join(' ')
|
||||
}
|
||||
>
|
||||
<ContainerTitle
|
||||
byline={ byline }
|
||||
description={ description }
|
||||
title={
|
||||
steps
|
||||
? steps[activeStep || 0]
|
||||
|
||||
90
js/src/ui/Title/title.spec.js
Normal file
90
js/src/ui/Title/title.spec.js
Normal file
@@ -0,0 +1,90 @@
|
||||
// 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 Title from './';
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
|
||||
function render (props = {}) {
|
||||
component = shallow(
|
||||
<Title
|
||||
activeStep={ 0 }
|
||||
byline='testByline'
|
||||
className='testClass'
|
||||
description='testDescription'
|
||||
title='testTitle'
|
||||
{ ...props }
|
||||
/>
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('ui/Title', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('instance methods', () => {
|
||||
describe('renderSteps', () => {
|
||||
let stepper;
|
||||
|
||||
beforeEach(() => {
|
||||
render({ steps: ['stepA', 'stepB'] });
|
||||
stepper = shallow(instance.renderSteps());
|
||||
});
|
||||
|
||||
it('renders the Stepper', () => {
|
||||
expect(stepper.find('Stepper').get(0)).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderTimeline', () => {
|
||||
let steps;
|
||||
|
||||
beforeEach(() => {
|
||||
render({ steps: ['stepA', 'StepB'] });
|
||||
steps = instance.renderTimeline();
|
||||
});
|
||||
|
||||
it('renders the Step', () => {
|
||||
expect(steps.length).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderWaiting', () => {
|
||||
let waiting;
|
||||
|
||||
beforeEach(() => {
|
||||
render({ busy: true });
|
||||
waiting = shallow(instance.renderWaiting());
|
||||
});
|
||||
|
||||
it('renders the LinearProgress', () => {
|
||||
expect(waiting.find('LinearProgress').get(0)).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/ui/VaultCard/Layout/index.js
Normal file
17
js/src/ui/VaultCard/Layout/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 './layout';
|
||||
45
js/src/ui/VaultCard/Layout/layout.css
Normal file
45
js/src/ui/VaultCard/Layout/layout.css
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
$imageHeight: 56px;
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
min-height: $imageHeight;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
|
||||
&.border {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
.identityIcon {
|
||||
margin-right: 1em;
|
||||
vertical-align: top;
|
||||
|
||||
&.locked {
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.33;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
66
js/src/ui/VaultCard/Layout/layout.js
Normal file
66
js/src/ui/VaultCard/Layout/layout.js
Normal file
@@ -0,0 +1,66 @@
|
||||
// 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 Title from '~/ui/Title';
|
||||
import IdentityIcon from '~/ui/IdentityIcon';
|
||||
|
||||
import styles from './layout.css';
|
||||
|
||||
export default class Layout extends Component {
|
||||
static propTypes = {
|
||||
vault: PropTypes.object.isRequired,
|
||||
withBorder: PropTypes.bool
|
||||
};
|
||||
|
||||
render () {
|
||||
const { vault, withBorder } = this.props;
|
||||
const { isOpen, meta, name } = vault;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
[
|
||||
styles.layout,
|
||||
withBorder
|
||||
? styles.border
|
||||
: null
|
||||
].join(' ')
|
||||
}
|
||||
>
|
||||
<IdentityIcon
|
||||
address={ name }
|
||||
center
|
||||
className={
|
||||
[
|
||||
styles.identityIcon,
|
||||
isOpen || withBorder
|
||||
? styles.unlocked
|
||||
: styles.locked
|
||||
].join(' ')
|
||||
}
|
||||
/>
|
||||
<div className={ styles.info }>
|
||||
<Title
|
||||
byline={ meta.description }
|
||||
title={ name }
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
90
js/src/ui/VaultCard/Layout/layout.spec.js
Normal file
90
js/src/ui/VaultCard/Layout/layout.spec.js
Normal file
@@ -0,0 +1,90 @@
|
||||
// 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 Layout from './';
|
||||
|
||||
const DESCRIPTION = 'some description';
|
||||
const NAME = 'testName';
|
||||
|
||||
let component;
|
||||
|
||||
function render () {
|
||||
component = shallow(
|
||||
<Layout
|
||||
vault={ {
|
||||
isOpen: true,
|
||||
meta: {
|
||||
description: DESCRIPTION,
|
||||
passwordHint: 'some hint'
|
||||
},
|
||||
name: NAME
|
||||
} }
|
||||
/>
|
||||
);
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('ui/VaultCard/Layout', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('components', () => {
|
||||
describe('IdentityIcon', () => {
|
||||
let icon;
|
||||
|
||||
beforeEach(() => {
|
||||
icon = component.find('Connect(IdentityIcon)');
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(icon.get(0)).to.be.ok;
|
||||
});
|
||||
|
||||
it('passes the name as address key', () => {
|
||||
expect(icon.props().address).to.equal(NAME);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Title', () => {
|
||||
let title;
|
||||
|
||||
beforeEach(() => {
|
||||
title = component.find('Title');
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(title.get(0)).to.be.ok;
|
||||
});
|
||||
|
||||
it('passes the name as title', () => {
|
||||
expect(title.props().title).to.equal(NAME);
|
||||
});
|
||||
|
||||
it('passes the description as byline', () => {
|
||||
expect(title.props().byline).to.equal(DESCRIPTION);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/ui/VaultCard/index.js
Normal file
17
js/src/ui/VaultCard/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 './vaultCard';
|
||||
62
js/src/ui/VaultCard/vaultCard.css
Normal file
62
js/src/ui/VaultCard/vaultCard.css
Normal file
@@ -0,0 +1,62 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
.container {
|
||||
text-align: left;
|
||||
|
||||
.accounts {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 1.5em;
|
||||
|
||||
.account {
|
||||
margin: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin-top: 1.5em;
|
||||
opacity: 0.5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin: -1em -1em 0.75em -1em;
|
||||
text-align: right;
|
||||
|
||||
button.status {
|
||||
min-width: 2em !important;
|
||||
}
|
||||
|
||||
button:not(.status) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.buttons {
|
||||
button.status {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
button:not(.status) {
|
||||
display: inline-block !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
js/src/ui/VaultCard/vaultCard.js
Normal file
102
js/src/ui/VaultCard/vaultCard.js
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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 { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import Button from '~/ui/Button';
|
||||
import Container from '~/ui/Container';
|
||||
import IdentityIcon from '~/ui/IdentityIcon';
|
||||
import { LockedIcon, UnlockedIcon } from '~/ui/Icons';
|
||||
|
||||
import Layout from './Layout';
|
||||
import styles from './vaultCard.css';
|
||||
|
||||
export default class VaultCard extends Component {
|
||||
static propTypes = {
|
||||
accounts: PropTypes.array,
|
||||
buttons: PropTypes.array,
|
||||
vault: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
static Layout = Layout;
|
||||
|
||||
render () {
|
||||
const { buttons, vault } = this.props;
|
||||
const { isOpen } = vault;
|
||||
|
||||
return (
|
||||
<Container
|
||||
className={ styles.container }
|
||||
hover={
|
||||
isOpen
|
||||
? this.renderAccounts()
|
||||
: null
|
||||
}
|
||||
>
|
||||
<div className={ styles.buttons }>
|
||||
<Button
|
||||
className={ styles.status }
|
||||
disabled
|
||||
icon={
|
||||
isOpen
|
||||
? <UnlockedIcon />
|
||||
: <LockedIcon />
|
||||
}
|
||||
key='status'
|
||||
/>
|
||||
{ buttons }
|
||||
</div>
|
||||
<Layout vault={ vault } />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccounts () {
|
||||
const { accounts } = this.props;
|
||||
|
||||
if (!accounts || !accounts.length) {
|
||||
return (
|
||||
<div className={ styles.empty }>
|
||||
<FormattedMessage
|
||||
id='vaults.accounts.empty'
|
||||
defaultMessage='There are no accounts in this vault'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={ styles.accounts }>
|
||||
{
|
||||
accounts.map((address) => {
|
||||
return (
|
||||
<Link to={ `/accounts/${address}` }>
|
||||
<IdentityIcon
|
||||
address={ address }
|
||||
center
|
||||
className={ styles.account }
|
||||
key={ address }
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
94
js/src/ui/VaultCard/vaultCard.spec.js
Normal file
94
js/src/ui/VaultCard/vaultCard.spec.js
Normal file
@@ -0,0 +1,94 @@
|
||||
// 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 VaultCard from './';
|
||||
|
||||
const VAULT = { name: 'testing', isOpen: true };
|
||||
|
||||
let component;
|
||||
let instance;
|
||||
|
||||
function render (props = {}) {
|
||||
component = shallow(
|
||||
<VaultCard
|
||||
vault={ VAULT }
|
||||
{ ...props }
|
||||
/>
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('ui/VaultCard', () => {
|
||||
beforeEach(() => {
|
||||
render();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('components', () => {
|
||||
describe('Layout', () => {
|
||||
let layout;
|
||||
|
||||
beforeEach(() => {
|
||||
layout = component.find('Layout');
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
expect(layout.get(0)).to.be.ok;
|
||||
});
|
||||
|
||||
it('passes the vault', () => {
|
||||
expect(layout.props().vault).to.deep.equal(VAULT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('instance methods', () => {
|
||||
describe('renderAccounts', () => {
|
||||
it('renders empty when no accounts supplied', () => {
|
||||
expect(
|
||||
shallow(instance.renderAccounts()).find('FormattedMessage').props().id
|
||||
).to.equal('vaults.accounts.empty');
|
||||
});
|
||||
|
||||
describe('with accounts', () => {
|
||||
const ACCOUNTS = ['0x123', '0x456'];
|
||||
let identities;
|
||||
|
||||
beforeEach(() => {
|
||||
render({ accounts: ACCOUNTS });
|
||||
identities = shallow(instance.renderAccounts()).find('Connect(IdentityIcon)');
|
||||
});
|
||||
|
||||
it('renders the accounts when supplied', () => {
|
||||
expect(identities).to.have.length(2);
|
||||
});
|
||||
|
||||
it('renders accounts with correct address', () => {
|
||||
console.log(identities.get(0));
|
||||
expect(identities.get(0).props.address).to.equal(ACCOUNTS[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -53,4 +53,5 @@ export Title from './Title';
|
||||
export Tooltips, { Tooltip } from './Tooltips';
|
||||
export TxHash from './TxHash';
|
||||
export TxList from './TxList';
|
||||
export VaultCard from './VaultCard';
|
||||
export Warning from './Warning';
|
||||
|
||||
Reference in New Issue
Block a user