This repository has been archived by the owner on Apr 15, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 60
Add selected votes counts bar - Closes #445 #754
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
7d3d9ae
Add voting bar e2e test specification
slaweet 79dc67c
Add voting bar with summary numbers of votes
slaweet 4760423
Add unit tests for votingBar
slaweet 5b6dc01
Enable voting bar e2e test
slaweet dfada0d
Add more votingBar unit tests
slaweet 50a91cf
Add voting middleware
slaweet 9348eb9
Use votingConst in votingBar and voteDialog
slaweet 0cd6351
Give an offset to votingBar on md screen size
slaweet 3125bd0
Merge branch 'development' into 445-voting-bar
slaweet baf06de
Remove forgotten console.log
slaweet a9c40b4
Add a toast when maximum of votes exceeded
slaweet File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
.red { | ||
color: #c62828; | ||
} | ||
|
||
.fixedAtBottom { | ||
position: fixed; | ||
bottom: 0; | ||
left: 8px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import React from 'react'; | ||
import grid from 'flexboxgrid/dist/flexboxgrid.css'; | ||
|
||
import votingConst from '../../constants/voting'; | ||
import style from './votingBar.css'; | ||
|
||
const VotingBar = ({ votes }) => { | ||
const { maxCountOfVotes, maxCountOfVotesInOneTurn } = votingConst; | ||
const votedList = Object.keys(votes).filter(key => votes[key].confirmed); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use a single There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I set up a jsperf to see the difference: ForEach is 3 or 4 times faster, but the absolute values of how long it takes on a list of 100 still make the difference negligible. I like using |
||
const voteList = Object.keys(votes).filter( | ||
key => votes[key].unconfirmed && !votes[key].confirmed); | ||
const unvoteList = Object.keys(votes).filter( | ||
key => !votes[key].unconfirmed && votes[key].confirmed); | ||
const totalVotesCount = (votedList.length - unvoteList.length) + voteList.length; | ||
const totalNewVotesCount = voteList.length + unvoteList.length; | ||
|
||
return (voteList.length + unvoteList.length ? | ||
<div className={`${grid.row} ${style.fixedAtBottom} box voting-bar`}> | ||
<div className={ | ||
`${grid['col-sm-12']} ${grid['col-md-10']} ${grid['col-md-offset-1']} | ||
${grid.row} ${grid['center-xs']} ${grid['middle-xs']}`}> | ||
<span className={`${grid['col-sm-3']} ${grid['col-xs-12']} upvotes`}> | ||
<span>Upvotes: </span> | ||
<strong>{voteList.length}</strong> | ||
</span> | ||
<span className={`${grid['col-sm-3']} ${grid['col-xs-12']} downvotes`}> | ||
<span>Downvotes: </span> | ||
<strong>{unvoteList.length}</strong> | ||
</span> | ||
<span className={`${grid['col-sm-3']} ${grid['col-xs-12']} total-new-votes`}> | ||
<span>Total new votes: </span> | ||
<strong className={totalNewVotesCount > maxCountOfVotesInOneTurn && style.red}> | ||
{totalNewVotesCount} | ||
</strong> | ||
<span> / {maxCountOfVotesInOneTurn}</span> | ||
</span> | ||
<span className={`${grid['col-sm-3']} ${grid['col-xs-12']} total-votes`}> | ||
<span>Total votes: </span> | ||
<strong className={totalVotesCount > 101 && style.red}> | ||
{totalVotesCount} | ||
</strong> | ||
<span> / {maxCountOfVotes}</span> | ||
</span> | ||
</div> | ||
</div> : | ||
null | ||
); | ||
}; | ||
|
||
export default VotingBar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import React from 'react'; | ||
import { expect } from 'chai'; | ||
import { mount } from 'enzyme'; | ||
|
||
import VotingBar from './votingBar'; | ||
|
||
import styles from './votingBar.css'; | ||
|
||
describe('VotingBar', () => { | ||
let wrapper; | ||
const props = { | ||
votes: { | ||
voted: { | ||
confirmed: true, | ||
unconfirmed: false, | ||
}, | ||
downvote: { | ||
confirmed: true, | ||
unconfirmed: true, | ||
}, | ||
upvote: { | ||
confirmed: false, | ||
unconfirmed: true, | ||
}, | ||
upvote2: { | ||
confirmed: false, | ||
unconfirmed: true, | ||
}, | ||
notVoted: { | ||
confirmed: false, | ||
unconfirmed: false, | ||
}, | ||
}, | ||
}; | ||
|
||
const generateNVotes = n => ( | ||
[...Array(n)].map((item, i) => i).reduce( | ||
(dict, value) => { | ||
dict[`genesis_${value}`] = { unconfirmed: true }; | ||
return dict; | ||
}, {}) | ||
); | ||
|
||
beforeEach(() => { | ||
wrapper = mount(<VotingBar {...props} />); | ||
}); | ||
|
||
it('should render number of upvotes', () => { | ||
expect(wrapper.find('.upvotes')).to.have.text('Upvotes: 2'); | ||
}); | ||
|
||
it('should render number of downvotes', () => { | ||
expect(wrapper.find('.downvotes')).to.have.text('Downvotes: 1'); | ||
}); | ||
|
||
it('should render number of downvotes', () => { | ||
expect(wrapper.find('.total-new-votes')).to.have.text('Total new votes: 3 / 33'); | ||
}); | ||
|
||
it('should render number of total votes', () => { | ||
expect(wrapper.find('.total-votes')).to.have.text('Total votes: 3 / 101'); | ||
}); | ||
|
||
it('should not render if no upvotes or downvotes', () => { | ||
wrapper.setProps({ votes: {} }); | ||
expect(wrapper.html()).to.equal(null); | ||
}); | ||
|
||
it('should render number of total votes in red if 101 exceeded', () => { | ||
const votes = generateNVotes(102); | ||
|
||
expect(wrapper.find(`.total-votes .${styles.red}`)).to.be.not.present(); | ||
wrapper.setProps({ votes }); | ||
expect(wrapper.find('.total-votes')).to.have.text('Total votes: 102 / 101'); | ||
expect(wrapper.find(`.total-votes .${styles.red}`)).to.have.text('102'); | ||
}); | ||
|
||
it('should render number of total new votes in red if 33 exceeded', () => { | ||
const votes = generateNVotes(34); | ||
expect(wrapper.find(`.total-new-votes .${styles.red}`)).to.be.not.present(); | ||
wrapper.setProps({ votes }); | ||
expect(wrapper.find('.total-new-votes')).to.have.text('Total new votes: 34 / 33'); | ||
expect(wrapper.find(`.total-new-votes .${styles.red}`)).to.have.text('34'); | ||
}); | ||
}); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
const votingConst = { | ||
maxCountOfVotesInOneTurn: 33, | ||
maxCountOfVotes: 101, | ||
}; | ||
|
||
export default votingConst; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import actionTypes from '../../constants/actions'; | ||
import votingConst from '../../constants/voting'; | ||
import { errorToastDisplayed } from '../../actions/toaster'; | ||
|
||
const votingMiddleware = store => next => (action) => { | ||
next(action); | ||
if (action.type === actionTypes.voteToggled) { | ||
const { votes } = store.getState().voting; | ||
const currentVote = votes[action.data.username] || { unconfirmed: true, confirmed: false }; | ||
|
||
const newVoteCount = Object.keys(votes).filter( | ||
key => votes[key].confirmed !== votes[key].unconfirmed).length; | ||
if (newVoteCount === votingConst.maxCountOfVotesInOneTurn + 1 && | ||
currentVote.unconfirmed !== currentVote.confirmed) { | ||
const label = `Maximum of ${votingConst.maxCountOfVotesInOneTurn} votes in one transaction exceeded.`; | ||
const newAction = errorToastDisplayed({ label }); | ||
store.dispatch(newAction); | ||
} | ||
|
||
const voteCount = Object.keys(votes).filter( | ||
key => (votes[key].confirmed && !votes[key].unconfirmed) || votes[key].unconfirmed).length; | ||
if (voteCount === votingConst.maxCountOfVotes + 1 && | ||
currentVote.unconfirmed !== currentVote.confirmed) { | ||
const label = `Maximum of ${votingConst.maxCountOfVotes} votes exceeded.`; | ||
const newAction = errorToastDisplayed({ label }); | ||
store.dispatch(newAction); | ||
} | ||
} | ||
}; | ||
|
||
export default votingMiddleware; | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To prevent verbosity please perform this shorthand assignment while importing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to use it like that, but it doesn't work.
ends up in both
maxCountOfVotes
andmaxCountOfVotesInOneTurn
beingundefined