Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Fix some bugs in votings
Browse files Browse the repository at this point in the history
  • Loading branch information
yasharAyari committed Aug 14, 2017
1 parent dee1efc commit be92b01
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 64 deletions.
68 changes: 45 additions & 23 deletions src/components/voting/confirmVotes.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,68 @@
import React from 'react';
import { connect } from 'react-redux';
import grid from 'flexboxgrid/dist/flexboxgrid.css';
import { Button } from 'react-toolbox/lib/button';
import Input from 'react-toolbox/lib/input';
import { vote } from '../../utils/api/delegate';
import { alertDialogDisplayed } from '../../actions/dialog';
import { clearVoteLists, pendingVotesAdded } from '../../actions/voting';
import InfoParagraph from '../infoParagraph';
import ActionBar from '../actionBar';
import { SYNC_ACTIVE_INTERVAL } from '../../constants/api';
import Fees from '../../constants/fees';

const delay = 10000;
export class ConfirmVotes extends React.Component {
constructor() {
super();
this.state = {
secondSecret: '',
};
}

confirm() {
const secondSecret = this.state.secondSecret.length === 0 ? null : this.state.secondSecret;
const text = 'Your votes were successfully submitted. It can take several seconds before they are processed.';

vote(
this.props.activePeer,
this.props.account.passphrase,
this.props.account.publicKey,
this.props.votedList,
this.props.unvotedList,
secondSecret,
).then(() => {
).then((data) => {
this.props.pendingVotesAdded();

// add to pending transaction
this.props.addTransaction({
id: data.transactionId,
senderPublicKey: this.props.account.publicKey,
senderId: this.props.account.address,
amount: 0,
fee: Fees.vote,
type: 3,
});

// remove pending votes
setTimeout(() => {
this.props.clearVoteLists();
}, delay);
}, SYNC_ACTIVE_INTERVAL);
this.props.showSuccessAlert({
title: 'Success',
type: 'success',
text,
});
});
}

setSecondPass(name, value) {
this.setState({ ...this.state, [name]: value });
}

render() {
const secondPass = this.props.account.secondSignature === 0 ? null :
<Input type='text' label='Second Passphrase' name='secondSecret' className='secondSecret'
value={this.state.secondSecret} onChange={this.setSecondPass.bind(this, 'secondSecret')}/>;
const secondPassphrase = this.props.account.secondSignature === 1 ?
<Input type='text' label='Second Passphrase' name='secondSecret'
className='secondSecret' value={this.state.secondSecret}
onChange={this.setSecondPass.bind(this, 'secondSecret')}/> : null;

return (
<article>
<h3>Add vote to</h3>
Expand All @@ -55,23 +73,27 @@ export class ConfirmVotes extends React.Component {
<ul>
{this.props.unvotedList.map(item => <li key={item.username}>{item.username}</li>)}
</ul>
{secondPass}

{secondPassphrase}

<InfoParagraph>
<p>You can select up to 33 delegates in one voting turn.</p>
<p>You can vote for up to 101 delegates in total.</p>
You can select up to 33 delegates in one voting turn.
<br />
You can vote for up to 101 delegates in total.
</InfoParagraph>
<footer className={`${grid.row} ${grid['between-xs']}`}>
<Button key={0} label='Cancel'
className='cancel-button'
onClick={this.props.closeDialog}
/>
<Button key={1} label='Confirm' id="confirm"
className='send-button'
primary={true} raised={true}
disabled={this.props.votedList.length === 0 && this.props.unvotedList.length === 0}
onClick={this.confirm.bind(this)}
/>
</footer>

<ActionBar
secondaryButton={{
onClick: this.props.closeDialog,
}}
primaryButton={{
label: 'Vote',
fee: Fees.vote,
disabled: (
this.props.votedList.length === 0 &&
this.props.unvotedList.length === 0),
onClick: this.confirm.bind(this),
}} />
</article>
);
}
Expand Down
21 changes: 12 additions & 9 deletions src/components/voting/confirmVotes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { mount } from 'enzyme';
import chaiEnzyme from 'chai-enzyme';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import PropTypes from 'prop-types';
import sinonStubPromise from 'sinon-stub-promise';
import store from '../../store';
import ConfrimVotesContainer, { ConfirmVotes } from './confirmVotes';
Expand Down Expand Up @@ -40,37 +41,39 @@ const props = {
showSuccessAlert: sinon.spy(),
clearVoteLists: sinon.spy(),
pendingVotesAdded: sinon.spy(),
addTransaction: sinon.spy(),
};
describe('ConfrimVotesContainer', () => {
it('should render ConfrimVotes', () => {
const wrapper = mount(<ConfrimVotesContainer {...props} store={store} />);
const wrapper = mount(<ConfrimVotesContainer {...props} store={store} />, {
context: { store },
childContextTypes: { store: PropTypes.object.isRequired },
});
expect(wrapper.find('ConfirmVotes').exists()).to.be.equal(true);
});
});
describe('ConfrimVotes', () => {
let wrapper;
const delegateApiMock = sinon.stub(delegateApi, 'vote');
beforeEach(() => {
wrapper = mount(<ConfirmVotes {...props} />);
wrapper = mount(<ConfirmVotes {...props} />, {
context: { store },
childContextTypes: { store: PropTypes.object.isRequired },
});
});

it('should call vote api when confirm button is pressed', () => {
const clock = sinon.useFakeTimers();
delegateApiMock.returnsPromise().resolves({ success: true });
wrapper.find('#confirm').simulate('click');
wrapper.instance().confirm();
expect(props.pendingVotesAdded).to.have.been.calledWith();
expect(props.addTransaction).to.have.been.calledWith();
expect(props.showSuccessAlert).to.have.been.calledWith();
// it should triger 'props.clearVoteLists' after 10000 ms
clock.tick(10000);
expect(props.clearVoteLists).to.have.been.calledWith();
});

it('confirm button should be disable when votedList and unvotedList is empty', () => {
wrapper.setProps({ unvotedList: [], votedList: [] });
const disabled = wrapper.find('#confirm').props().disabled;
expect(disabled).to.be.equal(true);
});

it('should update state when "setSecondPass" is called', () => {
wrapper.setProps({
account: Object.assign(props.account, { secondSignature: 1 }),
Expand Down
17 changes: 12 additions & 5 deletions src/components/voting/votingHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Button } from 'react-toolbox/lib/button';
import { IconMenu, MenuItem } from 'react-toolbox/lib/menu';
import Input from 'react-toolbox/lib/input';
import styles from './voting.css';
import ConfirmVotes from './confirmVotes';
import Confirm from './confirmVotes';

class VotingHeader extends React.Component {
constructor() {
Expand All @@ -14,6 +14,7 @@ class VotingHeader extends React.Component {
searchIcon: 'search',
};
}

search(name, value) {
const icon = value.length > 0 ? 'close' : 'search';
this.setState({
Expand All @@ -22,23 +23,26 @@ class VotingHeader extends React.Component {
});
this.props.search(value);
}

clearSearch() {
if (this.state.searchIcon === 'close') {
this.search('query', '');
}
}

confirmVoteText() {
let text = 'VOTE';
let info = 'VOTE';
const voted = this.props.votedList.filter(item => !item.pending).length;
const unvoted = this.props.unvotedList.filter(item => !item.pending).length;
if (voted > 0 || unvoted > 0) {
const seprator = (voted > 0 && unvoted > 0) ? ' / ' : ''; // eslint-disable-line
const votedHtml = voted > 0 ? <span className={styles.voted}>+{voted}</span> : '';
const unvotedHtml = unvoted > 0 ? <span className={styles.unvoted}>-{unvoted}</span> : '';
text = <span>VOTE ({votedHtml}{seprator}{unvotedHtml})</span>;
info = <span>VOTE ({votedHtml}{seprator}{unvotedHtml})</span>;
}
return text;
return info;
}

render() {
const button = <div className={styles.votesMenuButton}>
<i className="material-icons">visibility</i>
Expand Down Expand Up @@ -69,7 +73,10 @@ class VotingHeader extends React.Component {
<Button icon='done' flat
onClick={() => this.props.setActiveDialog({
title: 'Verify Vote for delegates',
childComponent: ConfirmVotes,
childComponent: Confirm,
childComponentProps: {
addTransaction: this.props.addTransaction,
},
})}
label={this.confirmVoteText()} />
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/components/voting/votingHeaderWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { connect } from 'react-redux';
import VotingHeader from './votingHeader';
import { dialogDisplayed } from '../../actions/dialog';
import { removedFromVoteList } from '../../actions/voting';
import { transactionAdded } from '../../actions/transactions';


const mapDispatchToProps = dispatch => ({
setActiveDialog: data => dispatch(dialogDisplayed(data)),
addToUnvoted: data => dispatch(removedFromVoteList(data)),
addTransaction: data => dispatch(transactionAdded(data)),
});
const mapStateToProps = state => ({
votedList: state.voting.votedList,
Expand Down
53 changes: 26 additions & 27 deletions src/components/voting/votingRow.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
import React from 'react';
import { TableRow, TableCell } from 'react-toolbox/lib/table';
import styles from './voting.css';
import SelectableRow from './selectableRow';
import Checkbox from './voteCheckbox';

const setRowClass = (item) => {
let className = '';
if (item.pending) {
className = styles.pendingRow;
} else if (item.selected && item.voted) {
className = styles.votedRow;
} else if (!item.selected && item.voted) {
className = styles.downVoteRow;
} else if (item.selected && !item.voted) {
className = styles.upVoteRow;
const setRowClass = ({ pending, selected, voted }) => {
if (pending) {
return styles.pendingRow;
} else if (selected) {
return voted ? styles.votedRow : styles.upVoteRow;
}
return className;
return voted ? styles.downVoteRow : '';
};

const VotingRow = (props) => {
const { data } = props;
return (<TableRow {...props} className={`${styles.row} ${setRowClass(data)}`}>
<TableCell>
<Checkbox styles={styles}
value={data.selected}
pending={data.pending}
data={data}
/>
</TableCell>
<TableCell>{data.rank}</TableCell>
<TableCell>{data.username}</TableCell>
<TableCell>{data.address}</TableCell>
<TableCell>{data.productivity} %</TableCell>
<TableCell>{data.approval} %</TableCell>
</TableRow>
);
};
const VotingRow = props => (<TableRow {...props} className={`${styles.row} ${setRowClass(props.data)}`}>
<TableCell>
<SelectableRow styles={styles}
value={props.data.selected}
pending={props.data.pending}
data={props.data}
/>
</TableCell>
<TableCell>{props.data.rank}</TableCell>
<TableCell>{props.data.username}</TableCell>
<TableCell>{props.data.address}</TableCell>
<TableCell>{props.data.productivity} %</TableCell>
<TableCell>{props.data.approval} %</TableCell>
</TableRow>
);

export default VotingRow;

0 comments on commit be92b01

Please sign in to comment.