-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Poll): Add main site poll component (#159)
* Add files via upload * Add files via upload * Delete index.html * Delete style.css * Delete Graph.tsx * Delete index.tsx * Create poll component (#84) * Add files via upload * Delete Choice.tsx * Delete Graph.tsx * Delete Question.tsx * Delete index.mdx * Delete index.tsx * Delete index.html * Delete style.css * Add files via upload * Delete Choice.tsx * Delete Graph.tsx * Delete Question.tsx * Delete index.mdx * Delete index.tsx * Delete Graph.js * Delete Graph.tsx * Delete index.js * Delete index.tsx * Add files via upload * Update index.mdx * Create poll component #84 * Update index.mdx * fix d3 dependency
- Loading branch information
Showing
7 changed files
with
376 additions
and
2 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
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,64 @@ | ||
import { faCheckCircle } from '@fortawesome/free-solid-svg-icons' | ||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' | ||
import * as React from 'react' | ||
import { css } from 'react-emotion' | ||
import * as MainSiteStyles from '../../globals/mainsiteGlobalStyles' | ||
|
||
interface ChoiceProps { | ||
choice: string | ||
votes: number | ||
} | ||
|
||
class Choice extends React.Component<ChoiceProps> { | ||
public state = { | ||
votes: this.props.votes, | ||
} | ||
|
||
constructor(props) { | ||
super(props); | ||
} | ||
|
||
public handleClick = () => { | ||
this.setState({ votes: this.state.votes+1 }); | ||
this.props.handler(); | ||
} | ||
|
||
public render() { | ||
return ( | ||
<div | ||
className={css` | ||
padding: 5px 0px 5px; | ||
&:first-child { | ||
padding-top: 0px; | ||
} | ||
&:last-child { | ||
border-bottom: none; | ||
padding-bottom: 0px; | ||
} | ||
`} | ||
> | ||
<p | ||
className={css` | ||
font-family: ${MainSiteStyles.storyListFont}, serif; | ||
font-size: 0.775rem; | ||
font-weight: 700; | ||
line-height: 1rem; | ||
margin: 0px 0px 3px; | ||
`} | ||
> | ||
<a onClick = {this.handleClick}> | ||
<FontAwesomeIcon | ||
icon={faCheckCircle} | ||
className={css` | ||
margin-right: 10px; | ||
`} | ||
/> | ||
</a> | ||
{this.props.choice} | ||
</p> | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
export default Choice |
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,145 @@ | ||
import React from "react" | ||
import { css } from 'react-emotion' | ||
import * as MainSiteStyles from '../../globals/mainsiteGlobalStyles' | ||
import * as d3 from "d3" | ||
import Choice from './Choice' | ||
|
||
class Graph extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { h: 200 }; | ||
this.wrap = this.wrap.bind(this); | ||
} | ||
|
||
public wrap(text, width) { | ||
let maxLines = 1; | ||
text.each(function() { | ||
const t = d3.select(this); | ||
const words = t.text().split(/\s+/).reverse(); | ||
let word; | ||
let line = []; | ||
let lineNumber = 1; | ||
const y = t.attr("y"); | ||
const dy = 10; | ||
let tspan = t.text(null).append("tspan").attr("x", 0).attr("y", y); | ||
word = words.pop(); | ||
while (word) { | ||
line.push(word); | ||
tspan.text(line.join(" ")); | ||
if (tspan.node().getComputedTextLength() > width) { | ||
line.pop(); | ||
tspan.text(line.join(" ")); | ||
line = [word]; | ||
tspan = t.append("tspan").attr("x", 0).attr("dy", dy + "px").text(word); | ||
lineNumber++; | ||
} | ||
if(lineNumber > maxLines) { | ||
maxLines = lineNumber; | ||
} | ||
word = words.pop(); | ||
} | ||
}); | ||
const newHeightValue = (maxLines-2) * 35; | ||
if(this.state.h !== 200 + newHeightValue) { | ||
this.setState({h: this.state.h+newHeightValue}); | ||
} | ||
} | ||
|
||
public componentDidMount() { | ||
this.draw(); | ||
} | ||
|
||
public componentDidUpdate() { | ||
this.draw(); | ||
} | ||
|
||
public draw = () => { | ||
const data = [...this.props.data]; | ||
data.sort((a, b) => a.votes - b.votes); | ||
|
||
const svg = d3.select(this.svg); | ||
const margin = { top: 15, right: 120, bottom: 0, left: 20 }; | ||
const width = 225; | ||
const height = this.state.h; | ||
|
||
svg.selectAll("g").remove(); | ||
|
||
const g = svg | ||
.append("g") | ||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | ||
|
||
const x = d3 | ||
.scaleLinear() | ||
.domain([0, d3.max(data, d => d.votes)]) | ||
.rangeRound([0, width]); | ||
|
||
const y = d3 | ||
.scaleBand() | ||
.rangeRound([height, 0]) | ||
.padding(5) | ||
.domain(data.map(d => d.choice)); | ||
|
||
g | ||
.append("g") | ||
.call(d3.axisRight(y).tickSize(0)) | ||
.attr("font-family", "PT Serif") | ||
.attr("font-size", "10px") | ||
.attr("font-weight", "bold") | ||
.attr("transform", "translate(0, -15)") | ||
.selectAll("path") | ||
.attr("stroke", "transparent") | ||
|
||
g | ||
.append("g") | ||
.call(d3.axisTop(x).tickSize(0)) | ||
.attr("font-family", "PT Serif") | ||
.attr("font-size", "10px") | ||
.attr("font-weight", "bold") | ||
.attr("transform", "translate(0, "+((this.state.h-200)/10)+")") | ||
.selectAll("path") | ||
|
||
g | ||
.selectAll(".bar") | ||
.data(data) | ||
.enter() | ||
.append("rect") | ||
.attr("transform", "translate(0, -25)") | ||
.attr("class", "bar") | ||
.attr("y", d => y(d.choice)) | ||
.attr("height", "3px") | ||
.style("fill", "#0080C6") | ||
.transition() | ||
.delay(250) | ||
.attr("width", d => x(d.votes)) | ||
|
||
g | ||
.selectAll(".tick text") | ||
.call(this.wrap, width) | ||
} | ||
|
||
|
||
public render() { | ||
return ( | ||
<div> | ||
<p | ||
className={css` | ||
font-family: ${MainSiteStyles.storyListFont}, serif; | ||
font-size: 10px; | ||
margin: -20px 0px 10px 155px; | ||
`} | ||
> | ||
*{this.props.legend} | ||
</p> | ||
<svg | ||
className={css` | ||
display: inline-block; | ||
height: ${this.state.h}px; | ||
`} | ||
ref={e => (this.svg = e)} | ||
/> | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
export default Graph; |
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,30 @@ | ||
import * as React from 'react' | ||
import { css } from 'react-emotion' | ||
import * as MainSiteStyles from '../../globals/mainsiteGlobalStyles' | ||
|
||
interface QuestionProps { | ||
text: string | ||
} | ||
|
||
export default function Question(props: QuestionProps) { | ||
return ( | ||
<div | ||
className={css` | ||
padding: ${MainSiteStyles.cardInnerPadding}; | ||
padding-bottom: 0px; | ||
`} | ||
> | ||
<h3 | ||
className={css` | ||
font-family: ${MainSiteStyles.storyListFont}, serif; | ||
font-size: 0.875rem; | ||
font-weight: 700; | ||
line-height: 1.125rem; | ||
margin: 0px 0px 3px; | ||
`} | ||
> | ||
{props.text} | ||
</h3> | ||
</div> | ||
) | ||
} |
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,39 @@ | ||
--- | ||
name: Poll | ||
route: /poll | ||
--- | ||
|
||
import { Playground, PropsTable } from 'docz' | ||
import Poll from '.' | ||
|
||
# Poll | ||
|
||
A poll with a graph of the results. | ||
|
||
<PropsTable of={Poll} /> | ||
|
||
<Playground> | ||
<Poll | ||
poll={[ | ||
{ | ||
choice: 'De Neve', | ||
votes: 10, | ||
}, | ||
{ | ||
choice: 'Covel', | ||
votes: 90, | ||
}, | ||
{ | ||
choice: 'Feast', | ||
votes: 30, | ||
}, | ||
{ | ||
choice: 'Bruin Plate', | ||
votes: 40, | ||
} | ||
]} | ||
question={"There's a lot going on at UCLA. Tuition hikes, protests, and more fun things. That's why we're asking you this question. What's your favorite dining hall?"} | ||
hasVoted={false} | ||
legend={"Number of Students"} | ||
/> | ||
</Playground> |
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,95 @@ | ||
import * as React from 'react' | ||
import { css } from 'react-emotion' | ||
import * as MainSiteStyles from '../../globals/mainsiteGlobalStyles' | ||
import Choice from './Choice' | ||
import Question from './Question' | ||
import Graph from './Graph' | ||
|
||
/** | ||
* Poll Properties | ||
*/ | ||
|
||
interface PollProps { | ||
/** A list of the choices. */ | ||
poll: ChoiceProps[] | ||
/** Poll question. */ | ||
question: string | ||
/** Graph legend. */ | ||
legend: string | ||
} | ||
|
||
class Poll extends React.Component<PollProps> { | ||
public state = { | ||
hasVoted: false, | ||
} | ||
|
||
constructor(props) { | ||
super(props); | ||
this.handler = this.handler.bind(this); | ||
} | ||
|
||
public handler() { | ||
this.setState({ | ||
hasVoted: true | ||
}); | ||
} | ||
|
||
public render() { | ||
const renderedChoices = this.props.poll.map((poll, index) => ( | ||
<Choice | ||
choice={poll.choice} | ||
votes={poll.votes} | ||
key={index} | ||
handler={this.handler} | ||
/> | ||
)) | ||
|
||
return ( | ||
<div | ||
className={css` | ||
background-color: ${MainSiteStyles.white}; | ||
box-shadow: ${MainSiteStyles.cardShadow}; | ||
justify-content: center; | ||
margin: 16px auto; | ||
max-width: 292px; | ||
`} | ||
> | ||
<div | ||
className={css` | ||
background-color: ${MainSiteStyles.black}; | ||
padding: 2px 0px 4px 10px; | ||
`} | ||
> | ||
<h2 | ||
className={css` | ||
color: ${MainSiteStyles.white}; | ||
font-family: ${MainSiteStyles.topBarFont}, sans-serif; | ||
font-size: 1.125rem; | ||
font-weight: 900; | ||
line-height: 1.4375rem; | ||
margin: 0px; | ||
overflow-wrap: break-word; | ||
`} | ||
> | ||
{'POLL'} | ||
</h2> | ||
</div> | ||
<Question | ||
text={this.props.question} | ||
/> | ||
<div | ||
className={css` | ||
padding: ${MainSiteStyles.cardInnerPadding}; | ||
`} | ||
> | ||
{!this.state.hasVoted && renderedChoices} | ||
{this.state.hasVoted && | ||
<Graph data={this.props.poll} legend={this.props.legend} /> | ||
} | ||
</div> | ||
</div> | ||
) | ||
} | ||
} | ||
|
||
export default Poll |