Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
512 additions
and
27 deletions.
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
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
181 changes: 159 additions & 22 deletions
181
source/js/buyers-guide/components/creep-vote/creep-vote.jsx
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 |
---|---|---|
@@ -1,42 +1,179 @@ | ||
import React from 'react'; | ||
import Creepometer from '../creepometer/creepometer.jsx'; | ||
import CreepChart from '../creepiness-chart/creepiness-chart.jsx'; | ||
import LikelyhoodChart from '../likelyhood-chart/likelyhood-chart.jsx'; | ||
|
||
export default class CreepVote extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = this.getInitialState(); | ||
} | ||
|
||
getInitialState() { | ||
let conf = this.props.votes.confidence; | ||
let totalVotes = conf[0] + conf[1]; | ||
|
||
this.state = {}; | ||
return { | ||
totalVotes, | ||
creepiness: 50, | ||
confidence: undefined, | ||
didVote: false | ||
}; | ||
} | ||
|
||
render() { | ||
return ( | ||
<div className="creep-vote py-5"> | ||
<div className="row mb-5"> | ||
<div className="col-12 col-md-6"> | ||
<div className="mb-4 text-center"> | ||
<h3 className="h5-heading mb-2">How creepy is this product?</h3> | ||
<p>Majority of voters think it is super creepy</p> | ||
</div> | ||
<Creepometer initialValue={50}></Creepometer> | ||
showVoteResult() { | ||
if (this.state.creepinessSubmitted && this.state.confidenceSubmitted) { | ||
this.setState({ didVote: true }); | ||
} | ||
} | ||
|
||
sendVoteFor(payload) { | ||
let attribute = payload.attribute; | ||
let url = `/privacynotincluded/vote`; | ||
let method = `POST`; | ||
let credentials = `same-origin`; | ||
let headers = { | ||
"X-CSRFToken": this.props.csrf, | ||
"Content-Type": `application/json` | ||
}; | ||
|
||
fetch(url, { | ||
method, | ||
credentials, | ||
headers, | ||
body: JSON.stringify(payload) | ||
}) | ||
.then(response => { | ||
let update = {}; | ||
|
||
update[`${attribute}Submitted`] = true; | ||
this.setState(update, () => { | ||
this.showVoteResult(); | ||
}); | ||
}) | ||
.catch(e => { | ||
console.warn(e); | ||
this.setState({ disableVoteButton: false }); | ||
}); | ||
} | ||
|
||
submitVote(evt) { | ||
evt.preventDefault(); | ||
|
||
let confidence = this.state.confidence; | ||
|
||
if (confidence === undefined) { | ||
return; | ||
} | ||
|
||
this.setState({ disableVoteButton: true }); | ||
|
||
let productID = this.props.productID; | ||
|
||
this.sendVoteFor({ | ||
attribute: `confidence`, | ||
productID, | ||
value: confidence, | ||
}); | ||
|
||
this.sendVoteFor({ | ||
attribute: `creepiness`, | ||
productID, | ||
value: this.state.creepiness | ||
}); | ||
} | ||
|
||
setCreepiness(creepiness) { | ||
this.setState({ creepiness }); | ||
} | ||
|
||
setConfidence(confidence) { | ||
this.setState({ confidence }); | ||
} | ||
|
||
/** | ||
* @returns {jsx} What users see when they haven't voted on this product yet. | ||
*/ | ||
renderVoteAsk() { | ||
return (<form method="post" className="creep-vote" id="creep-vote" onSubmit={evt => this.submitVote(evt)}> | ||
<div className="row mb-5"> | ||
<div className="col-12 col-md-6"> | ||
<div className="mb-4 text-center"> | ||
<h3 className="h5-heading mb-2">How creepy is this product?</h3> | ||
<p className="help-text">Majority of voters think it is super creepy</p> | ||
</div> | ||
<div className="col-12 col-md-6"> | ||
<div className="mb-4 text-center"> | ||
<h3 className="h5-heading mb-2">How likely are you to buy it?</h3> | ||
<p>Majority of voters are not likely to buy it</p> | ||
</div> | ||
<div className="text-center"> | ||
<button>Likely</button> | ||
<button>Not likely</button> | ||
<Creepometer initialValue={this.state.creepiness} onChange={value => this.setCreepiness(value)}></Creepometer> | ||
</div> | ||
<div className="col-12 col-md-6"> | ||
<div className="mb-4 text-center"> | ||
<h3 className="h5-heading mb-2">How likely are you to buy it?</h3> | ||
<p className="help-text">Majority of voters are not likely to buy it</p> | ||
</div> | ||
<div className="text-center"> | ||
<div class="btn-group btn-group-toggle mt-5" data-toggle="buttons"> | ||
<label for="likely"> | ||
<input type="radio" name="wouldbuy" id="likely" autocomplete="off" required/> | ||
<span class="likely btn" onClick={evt => this.setConfidence(true)}><img alt="thumb up" src="/_images/buyers-guide/icon-thumb-up-black.svg" /> Likely</span> | ||
</label> | ||
<label for="unlikely"> | ||
<input type="radio" name="wouldbuy" id="unlikely" autocomplete="off" required/> | ||
<span class="unlikely btn" onClick={evt => this.setConfidence(false)}><img alt="thumb down" src="/_images/buyers-guide/icon-thumb-down-black.svg" /> Not likely</span> | ||
</label> | ||
</div> | ||
</div> | ||
</div> | ||
<div className="row"> | ||
</div> | ||
<div className="row"> | ||
<div className="col-12 text-center"> | ||
<button type="submit" className="btn btn-ghost mb-2" disabled={this.state.confidence===undefined}>Vote & See Results</button> | ||
<p>{this.state.totalVotes} votes</p> | ||
</div> | ||
</div> | ||
</form>); | ||
} | ||
|
||
/** | ||
* @returns {jsx} What users see when they have voted on this product. | ||
*/ | ||
renderDidVote(){ | ||
return( | ||
<div> | ||
<div className="mb-5"> | ||
<div className="col-12 text-center"> | ||
<button className="btn btn-ghost mb-2">Vote & See Results</button> | ||
<p>367 votes</p> | ||
<h3 className="h5-heading mb-1">Thanks for voting! Here are the results so far:</h3> | ||
<div className="text-muted">{this.state.totalVotes + 1} Votes</div> | ||
</div> | ||
<div className="row mt-3"> | ||
<div className="col"> | ||
<CreepChart userVoteGroup={Math.floor(this.state.creepiness/20)} values={this.props.votes.creepiness.vote_breakdown} /> | ||
</div> | ||
<div className="col likelyhood-chart p-5"> | ||
<LikelyhoodChart values={this.props.votes.confidence} /> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
render() { | ||
let voteContent; | ||
|
||
if(this.state.didVote){ | ||
voteContent = this.renderDidVote(); | ||
} else { | ||
voteContent = this.renderVoteAsk(); | ||
} | ||
|
||
return ( | ||
<div className="creep-vote py-5"> | ||
{ voteContent } | ||
<div className="text-center"> | ||
<div>View comments or share your results</div> | ||
{/* TODO: Make these share links work */} | ||
<div className="share-links">fb, tw, email</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
} |
55 changes: 54 additions & 1 deletion
55
source/js/buyers-guide/components/creep-vote/creep-vote.scss
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 |
---|---|---|
@@ -1,4 +1,57 @@ | ||
$radio-button-radius: 50px; | ||
$btn-shadow-width: 3px; | ||
|
||
.creep-vote { | ||
border-top: 1px solid #c8c8ca; | ||
border-bottom: 1px solid #c8c8ca; | ||
border-top: 1px solid #c8c8ca; | ||
|
||
button[disabled] { | ||
color: #bbbbbb; | ||
|
||
&.btn-ghost:hover { | ||
cursor: auto; | ||
color: inherit; | ||
background: inherit; | ||
} | ||
} | ||
|
||
.btn-group { | ||
display: flex; | ||
justify-content: center; | ||
|
||
.btn { | ||
border: 1px solid black; | ||
border-bottom-width: $btn-shadow-width; | ||
color: black; | ||
padding: 0.5em 1.5em; | ||
text-transform: initial; | ||
transition: all 0.1s linear; | ||
} | ||
|
||
input { display: none; } | ||
|
||
input:checked + span { | ||
background-color: #5cccff; | ||
} | ||
|
||
input:-moz-ui-invalid + span { | ||
border-color: red; | ||
} | ||
|
||
label:first-of-type span { | ||
border-radius: $radio-button-radius 0 0 $radio-button-radius; | ||
} | ||
|
||
label:last-of-type span { | ||
border-radius: 0 $radio-button-radius $radio-button-radius 0; | ||
} | ||
|
||
img { | ||
height: 1em; | ||
} | ||
} | ||
|
||
.help-text { | ||
font-style: italic; | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
source/js/buyers-guide/components/creepiness-chart/creepiness-chart.jsx
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,57 @@ | ||
import React from 'react'; | ||
|
||
export default class CreepChart extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = this.getInitialState(); | ||
} | ||
|
||
getInitialState() { | ||
let values = this.props.values; | ||
let data = [ | ||
{c: `no-creep`, label: `Not creepy`, value: values[0], offset: 0}, | ||
{c: `little-creep`, label: `A little creepy`, value: values[1], offset: 225}, | ||
{c: `somewhat-creep`, label: `Somewhat creepy`, value: values[2], offset: 475}, | ||
{c: `very-creep`, label: `Very creepy`, value: values[3], offset: 725}, | ||
{c: `super-creep`, label: `Super creepy`, value: values[4], offset: 975} | ||
]; | ||
let sum = data.reduce((tally, v) => tally + v.value, 0); | ||
|
||
return { | ||
totalCreepiness: sum, | ||
creepinessData: data | ||
}; | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<table id="creepiness-score"> | ||
<tbody> | ||
{ | ||
this.state.creepinessData.map((data,index) => { | ||
let percent = Math.round(100 * data.value / this.state.totalCreepiness); | ||
let voteColumn = this.props.userVoteGroup === index ? `your-vote` : ``; | ||
|
||
return ( | ||
<tr key={data.c} className={`${voteColumn} ${data.c}`}> | ||
<th> | ||
<div className="bar" style={{height: `${percent}px`,}}></div> | ||
<span className="creep-label">{data.label}</span> | ||
<span className="creep-face"></span> | ||
</th> | ||
<td className="creepiness">{percent}%</td> | ||
</tr> | ||
); | ||
}) | ||
} | ||
</tbody> | ||
</table> | ||
<div className="row"> | ||
<div className="col text-left text-muted">Not creepy</div> | ||
<div className="col text-right text-muted">Super creepy</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
} |
Oops, something went wrong.