├── 07-d3-demo ├── img │ └── complete.png ├── README.md ├── data │ ├── prep_data.R │ └── .Rhistory ├── css │ └── styles.css ├── index.html └── js │ └── App.js ├── 05-state-demo ├── img │ └── complete.png ├── README.md ├── css │ └── styles.css ├── index.html └── js │ └── main.js ├── 03-component-demo ├── img │ └── complete.png ├── README.md ├── css │ └── styles.css ├── js │ └── main.js └── index.html ├── 06-state-exercise ├── img │ └── complete.png ├── data │ ├── prep_data.R │ └── .Rhistory ├── css │ └── styles.css ├── index.html ├── js │ ├── App.js │ └── solution.js └── README.md ├── 08-scatter-exercise ├── img │ └── complete.png ├── data │ ├── prep_data.R │ └── .Rhistory ├── css │ └── styles.css ├── js │ ├── App.js │ ├── App_solution.js │ ├── ScatterPlot.js │ └── ScatterPlot_solution.js ├── index.html └── README.md ├── 04-react-intro-exercise ├── img │ └── complete.png ├── css │ └── styles.css ├── js │ ├── main.js │ └── solution.js ├── index.html └── README.md ├── 09-small-multiples-exercise ├── img │ └── complete.png ├── data │ ├── prep_data.R │ └── .Rhistory ├── css │ └── styles.css ├── index.html ├── README.md └── js │ ├── App.js │ ├── ScatterPlot.js │ └── App_solution.js ├── 10-lifting-up-state-exercise ├── img │ └── complete.png ├── data │ ├── prep_data.R │ └── .Rhistory ├── css │ └── styles.css ├── index.html ├── README.md └── js │ ├── BarChart.js │ ├── BarChart_solution.js │ ├── ScatterPlot.js │ ├── ScatterPlot_solution.js │ ├── App.js │ └── App_solution.js ├── 01-basic-d3-demo ├── README.md ├── css │ └── styles.css ├── data │ ├── prep_data.R │ └── .Rhistory ├── index.html └── js │ └── ScatterPlot.js ├── 02-class-demo ├── README.md ├── css │ └── styles.css ├── js │ └── main.js └── index.html ├── css └── style.css ├── main.js ├── LICENSE ├── data └── demos.csv ├── lib ├── example-styles.css └── d3-tip.js ├── README.md └── index.html /07-d3-demo/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/07-d3-demo/img/complete.png -------------------------------------------------------------------------------- /05-state-demo/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/05-state-demo/img/complete.png -------------------------------------------------------------------------------- /03-component-demo/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/03-component-demo/img/complete.png -------------------------------------------------------------------------------- /06-state-exercise/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/06-state-exercise/img/complete.png -------------------------------------------------------------------------------- /08-scatter-exercise/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/08-scatter-exercise/img/complete.png -------------------------------------------------------------------------------- /04-react-intro-exercise/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/04-react-intro-exercise/img/complete.png -------------------------------------------------------------------------------- /09-small-multiples-exercise/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/09-small-multiples-exercise/img/complete.png -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/img/complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mkfreeman/react-d3/HEAD/10-lifting-up-state-exercise/img/complete.png -------------------------------------------------------------------------------- /01-basic-d3-demo/README.md: -------------------------------------------------------------------------------- 1 | # 01-basic-d3-demo 2 | This demo contains a simple D3 scatterplot -- participants should be comfortable with this use of D3 _before_ the workshop. -------------------------------------------------------------------------------- /05-state-demo/README.md: -------------------------------------------------------------------------------- 1 | # 05-state-demo 2 | This project builds a searchable todo list to demonstrate a React component that keeps track of state: 3 | 4 | ![searchable table](img/complete.png) -------------------------------------------------------------------------------- /03-component-demo/README.md: -------------------------------------------------------------------------------- 1 | # 03-component-demo 2 | This project demonstrates the basic using of creating and rendering a React component. It uses properties (_props_) to pass data into a very simple component: 3 | 4 | ![a paragraph rendered with props](img/complete.png) -------------------------------------------------------------------------------- /07-d3-demo/README.md: -------------------------------------------------------------------------------- 1 | # 05-state-demo 2 | This project uses D3 to draw data on a DOM node that is rendered by a React component. It uses a React component's state to track the number of circles to render, and then draws them in a `` element using D3 whenever the state changes: 3 | 4 | ![circles rendered with d3](img/complete.png) -------------------------------------------------------------------------------- /02-class-demo/README.md: -------------------------------------------------------------------------------- 1 | # 02-class-demo 2 | This project demonstrates a simple use of JavaScript _classes_. Because using React depends on leveraging a class structure in JavaScript, this is an important prerequisite to using React. 3 | 4 | When you open the page, make sure to **inspect the page** to see the of of classes on the console. -------------------------------------------------------------------------------- /02-class-demo/css/styles.css: -------------------------------------------------------------------------------- 1 | /* Bootstrap CSS */ 2 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 3 | @import url("../../lib/bootstrap.min.css"); 4 | 5 | .lead { 6 | margin-bottom:0px; 7 | } 8 | 9 | .jumbotron { 10 | padding:1rem 1rem; 11 | margin-bottom:20px; 12 | } -------------------------------------------------------------------------------- /03-component-demo/css/styles.css: -------------------------------------------------------------------------------- 1 | /* Bootstrap CSS */ 2 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 3 | @import url("../../lib/bootstrap.min.css"); 4 | 5 | .lead { 6 | margin-bottom:0px; 7 | } 8 | 9 | 10 | .jumbotron { 11 | padding:1rem 1rem; 12 | margin-bottom:20px; 13 | } -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | .header { 2 | font-weight: 300; 3 | font-size: 50px; 4 | border-bottom: 1px solid #d3d3d3; 5 | } 6 | 7 | h3 { 8 | font-weight: 300; 9 | font-size:24px; 10 | padding-bottom:0px; 11 | margin-bottom:0px; 12 | } 13 | 14 | p { 15 | opacity:.7; 16 | margin-top:0px; 17 | } 18 | 19 | strong { 20 | font-weight: 700; 21 | } -------------------------------------------------------------------------------- /01-basic-d3-demo/css/styles.css: -------------------------------------------------------------------------------- 1 | /* Bootstrap CSS */ 2 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 3 | @import url("../../lib/bootstrap.min.css"); 4 | 5 | .lead { 6 | margin-bottom:0px; 7 | } 8 | 9 | 10 | .jumbotron { 11 | padding:1rem 1rem; 12 | margin-bottom:20px; 13 | } 14 | 15 | /* Scatter plot */ 16 | circle { 17 | cursor:pointer; 18 | } 19 | .axis-label { 20 | text-anchor:middle; 21 | font-size:10px; 22 | } 23 | 24 | #root { 25 | padding-top:30px; 26 | text-align: center; 27 | } -------------------------------------------------------------------------------- /04-react-intro-exercise/css/styles.css: -------------------------------------------------------------------------------- 1 | /* Bootstrap CSS */ 2 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 3 | @import url("../../lib/bootstrap.min.css"); 4 | 5 | 6 | .jumbotron { 7 | padding:1rem 1rem; 8 | margin-bottom:20px; 9 | } 10 | 11 | .complete { 12 | text-decoration: line-through; 13 | } 14 | 15 | p { 16 | margin-bottom:0px; 17 | } 18 | 19 | .todo { 20 | padding:10px; 21 | margin:2px; 22 | border-radius: 5px; 23 | } 24 | .high { 25 | background:#c125084f; 26 | } 27 | 28 | .low { 29 | background:#0080004f; 30 | } 31 | 32 | .medium { 33 | background:#e8e6074f; 34 | } -------------------------------------------------------------------------------- /05-state-demo/css/styles.css: -------------------------------------------------------------------------------- 1 | /* Bootstrap CSS */ 2 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 3 | @import url("../../lib/bootstrap.min.css"); 4 | 5 | .complete { 6 | text-decoration: line-through; 7 | } 8 | 9 | .lead { 10 | margin-bottom:0px; 11 | } 12 | 13 | p { 14 | margin-bottom:0px; 15 | } 16 | 17 | 18 | .jumbotron { 19 | padding:1rem 1rem; 20 | margin-bottom:20px; 21 | } 22 | 23 | .todo { 24 | padding:10px; 25 | margin:2px; 26 | border-radius: 5px; 27 | } 28 | .high { 29 | background:#c125084f; 30 | } 31 | 32 | .low { 33 | background:#0080004f; 34 | } 35 | 36 | .medium { 37 | background:#e8e6074f; 38 | } -------------------------------------------------------------------------------- /07-d3-demo/data/prep_data.R: -------------------------------------------------------------------------------- 1 | # Data prep 2 | 3 | # Use built in `midwest` data from the `ggplot2` package 4 | library(ggplot2) 5 | library(dplyr) 6 | library(stringr) 7 | 8 | # Replace values with full state names 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin"), 16 | county = str_to_title(county) 17 | ) %>% 18 | select(county, state, inmetro, contains("per"), -perchsd) 19 | 20 | # Write data 21 | write.csv(prepped, "midwest.csv", row.names = F) -------------------------------------------------------------------------------- /01-basic-d3-demo/data/prep_data.R: -------------------------------------------------------------------------------- 1 | # Data prep 2 | 3 | # Use built in `midwest` data from the `ggplot2` package 4 | library(ggplot2) 5 | library(dplyr) 6 | library(stringr) 7 | 8 | # Replace values with full state names 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin"), 16 | county = str_to_title(county) 17 | ) %>% 18 | select(county, state, inmetro, contains("per"), -perchsd) 19 | 20 | # Write data 21 | write.csv(prepped, "midwest.csv", row.names = F) -------------------------------------------------------------------------------- /06-state-exercise/data/prep_data.R: -------------------------------------------------------------------------------- 1 | # Data prep 2 | 3 | # Use built in `midwest` data from the `ggplot2` package 4 | library(ggplot2) 5 | library(dplyr) 6 | library(stringr) 7 | 8 | # Replace values with full state names 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin"), 16 | county = str_to_title(county) 17 | ) %>% 18 | select(county, state, inmetro, contains("per"), -perchsd) 19 | 20 | # Write data 21 | write.csv(prepped, "midwest.csv", row.names = F) -------------------------------------------------------------------------------- /08-scatter-exercise/data/prep_data.R: -------------------------------------------------------------------------------- 1 | # Data prep 2 | 3 | # Use built in `midwest` data from the `ggplot2` package 4 | library(ggplot2) 5 | library(dplyr) 6 | library(stringr) 7 | 8 | # Replace values with full state names 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin"), 16 | county = str_to_title(county) 17 | ) %>% 18 | select(county, state, inmetro, contains("per"), -perchsd) 19 | 20 | # Write data 21 | write.csv(prepped, "midwest.csv", row.names = F) -------------------------------------------------------------------------------- /09-small-multiples-exercise/data/prep_data.R: -------------------------------------------------------------------------------- 1 | # Data prep 2 | 3 | # Use built in `midwest` data from the `ggplot2` package 4 | library(ggplot2) 5 | library(dplyr) 6 | library(stringr) 7 | 8 | # Replace values with full state names 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin"), 16 | county = str_to_title(county) 17 | ) %>% 18 | select(county, state, inmetro, contains("per"), -perchsd) 19 | 20 | # Write data 21 | write.csv(prepped, "midwest.csv", row.names = F) -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/data/prep_data.R: -------------------------------------------------------------------------------- 1 | # Data prep 2 | 3 | # Use built in `midwest` data from the `ggplot2` package 4 | library(ggplot2) 5 | library(dplyr) 6 | library(stringr) 7 | 8 | # Replace values with full state names 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin"), 16 | county = str_to_title(county) 17 | ) %>% 18 | select(county, state, inmetro, contains("per"), -perchsd) 19 | 20 | # Write data 21 | write.csv(prepped, "midwest.csv", row.names = F) -------------------------------------------------------------------------------- /04-react-intro-exercise/js/main.js: -------------------------------------------------------------------------------- 1 | // Making a todo list 2 | "use strict"; 3 | 4 | // A set of items to render in your todolist 5 | const todos = [ 6 | { 7 | description: "Learn how to use React and D3", 8 | priority: "high", 9 | status: "incomplete" 10 | 11 | }, 12 | { 13 | description: "Explore Paris", 14 | priority: "medium", 15 | status: "complete" 16 | }, 17 | { 18 | description: "Catch up on work", 19 | priority: "low", 20 | status: "incomplete" 21 | } 22 | ]; 23 | 24 | 25 | // Create a component Todo that represents an item in a to do list (see README.md for instructions) 26 | 27 | 28 | // Create a class TodoList (see README.md for instructions) 29 | 30 | 31 | // Render the TodoList component in the `root` element -------------------------------------------------------------------------------- /02-class-demo/js/main.js: -------------------------------------------------------------------------------- 1 | // A simple d3 scatter plot 2 | "use strict"; 3 | 4 | // Create a new class `Pet` 5 | class Pet { 6 | // Constructor function is called when the class is instantiated 7 | constructor(name) { 8 | this.name = name; 9 | } 10 | } 11 | 12 | // Create an instance of your pet 13 | let myPet = new Pet("Magnet"); 14 | console.log(myPet.name); 15 | 16 | // Create a new class `Dog` by extending the class `Pet` 17 | class Dog extends Pet { 18 | constructor(name, sound) { 19 | super(name); // call parent class constructor function 20 | this.sound = sound; 21 | } 22 | 23 | // Add a new method `bark` to the class 24 | bark() { 25 | console.log(`${this.sound}! My name is ${this.name}`); 26 | } 27 | } 28 | 29 | // Create an instance of a dog 30 | let myDog = new Dog("Mocha", "Woof Woof!"); 31 | 32 | // Have the dog bark 33 | myDog.bark(); -------------------------------------------------------------------------------- /06-state-exercise/css/styles.css: -------------------------------------------------------------------------------- 1 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 2 | @import url("../../lib/bootstrap.min.css"); 3 | 4 | /* Header */ 5 | .container { 6 | text-align: center; 7 | } 8 | 9 | .jumbotron { 10 | padding:1rem 1rem; 11 | margin-bottom:20px; 12 | } 13 | 14 | .lead { 15 | margin-bottom:0px; 16 | } 17 | 18 | /* Controls */ 19 | .custom-select { 20 | width:inherit; 21 | } 22 | 23 | .control-wrapper { 24 | display: inline-block; 25 | margin-left:10px; 26 | } 27 | 28 | .control-wrapper input[type="range"] { 29 | vertical-align: text-bottom;; 30 | } 31 | 32 | .control-container { 33 | border-bottom:1px solid #d3d3d3; 34 | margin-bottom:10px; 35 | padding-bottom:10px; 36 | } 37 | 38 | /* Buttons */ 39 | .btn:focus { 40 | box-shadow: none !important; 41 | } 42 | 43 | .btn-group { 44 | margin-left:10px; 45 | } -------------------------------------------------------------------------------- /03-component-demo/js/main.js: -------------------------------------------------------------------------------- 1 | // A simple react component 2 | "use strict"; 3 | 4 | // Create a component that represents someone's biography 5 | class Bio extends React.Component { 6 | // React components have a render method to describe how to draw them on the DOM 7 | render() { 8 | // Figure out the proper article 9 | let article = "a"; 10 | ["a", "e", "i", "o", "u"].forEach((d) => { 11 | if(this.props.description.startsWith(d)) { 12 | article = "an"; 13 | } 14 | }); 15 | return ( 16 |
17 |

{this.props.name}

18 |

Hello, my name is {this.props.name} and I am {article} {this.props.description}.

19 |
20 | ) 21 | } 22 | } 23 | 24 | // Render the Bio component in the `root` element 25 | ReactDOM.render( 26 | , 27 | document.getElementById('root') 28 | ); -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Data to pass to our List elements 4 | class Demos extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | this.state = { 8 | demos: [] 9 | }; 10 | } 11 | 12 | componentDidMount() { 13 | d3.csv('data/demos.csv', (data) => { 14 | this.setState({ demos: data }); 15 | }); 16 | } 17 | 18 | render() { 19 | return ( 20 |
21 | {this.state.demos.map(function (d, i) { 22 | return ( 23 |
24 |

{d.title}

25 |

{d.description} 26 |

27 |
28 | ) 29 | })} 30 |
31 | ); 32 | } 33 | } 34 | 35 | // Render your component in the `main` section 36 | ReactDOM.render(, 37 | document.querySelector('#examples') 38 | ); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Michael Freeman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /data/demos.csv: -------------------------------------------------------------------------------- 1 | title,description 2 | 01-basic-d3-demo,A demonstration of building a scatterplot using D3. This is the level of background participants are expected to have before moving through these materials. 3 | 02-class-demo,This page demonstrates how to leverage Classes in JavaScript. 4 | 03-component-demo,"In this demo, you can see how to create React components that accept information via props." 5 | 04-react-intro-exercise,"In this exercise, participants will walk through creating and rendering React components" 6 | 05-state-demo,This page demonstrates how to keep track of the state of an application. 7 | 06-state-exercise,This exercise challenges you to leverage the state of an application to keep track of information that changes through user interactions. 8 | 07-d3-demo,"Here, you can see how to leverage D3 to interact with components that are created using React." 9 | 08-scatter-exercise,"In this exercise, you will create a fully functional scatterplot using D3 and React in combination with one another" 10 | 09-small-multiples-exercise,"In this final exercise, you will see how simple it is to use React to render small multiples fo D3 visualizations." -------------------------------------------------------------------------------- /lib/example-styles.css: -------------------------------------------------------------------------------- 1 | .d3-tip { 2 | line-height: 1; 3 | font-weight: bold; 4 | padding: 12px; 5 | background: rgba(0, 0, 0, 0.8); 6 | color: #fff; 7 | border-radius: 2px; 8 | pointer-events: none; 9 | } 10 | 11 | /* Creates a small triangle extender for the tooltip */ 12 | .d3-tip:after { 13 | box-sizing: border-box; 14 | display: inline; 15 | font-size: 10px; 16 | width: 100%; 17 | line-height: 1; 18 | color: rgba(0, 0, 0, 0.8); 19 | position: absolute; 20 | pointer-events: none; 21 | } 22 | 23 | /* Northward tooltips */ 24 | .d3-tip.n:after { 25 | content: "\25BC"; 26 | margin: -1px 0 0 0; 27 | top: 100%; 28 | left: 0; 29 | text-align: center; 30 | } 31 | 32 | /* Eastward tooltips */ 33 | .d3-tip.e:after { 34 | content: "\25C0"; 35 | margin: -4px 0 0 0; 36 | top: 50%; 37 | left: -8px; 38 | } 39 | 40 | /* Southward tooltips */ 41 | .d3-tip.s:after { 42 | content: "\25B2"; 43 | margin: 0 0 1px 0; 44 | top: -8px; 45 | left: 0; 46 | text-align: center; 47 | } 48 | 49 | /* Westward tooltips */ 50 | .d3-tip.w:after { 51 | content: "\25B6"; 52 | margin: -4px 0 0 -1px; 53 | top: 50%; 54 | left: 100%; 55 | } 56 | -------------------------------------------------------------------------------- /07-d3-demo/css/styles.css: -------------------------------------------------------------------------------- 1 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 2 | @import url("../../lib/bootstrap.min.css"); 3 | 4 | /* Header */ 5 | .container { 6 | text-align: center; 7 | } 8 | 9 | .jumbotron { 10 | padding:1rem 1rem; 11 | margin-bottom:20px; 12 | } 13 | 14 | .lead { 15 | margin-bottom:0px; 16 | } 17 | /* Controls */ 18 | .custom-select { 19 | width:inherit; 20 | } 21 | 22 | .control-wrapper { 23 | display: inline-block; 24 | margin-left:10px; 25 | } 26 | 27 | .control-wrapper input[type="range"] { 28 | vertical-align: text-bottom;; 29 | } 30 | 31 | .control-container { 32 | border-bottom:1px solid #d3d3d3; 33 | margin-bottom:10px; 34 | padding-bottom:10px; 35 | } 36 | 37 | /* Scatter plot */ 38 | circle { 39 | cursor:pointer; 40 | } 41 | .axis-label { 42 | text-anchor:middle; 43 | font-size:10px; 44 | } 45 | 46 | .chart-wrapper { 47 | display: inline-block; 48 | } 49 | 50 | 51 | /* Tooltips */ 52 | .d3-tip { 53 | line-height: 1; 54 | font-weight: bold; 55 | padding: 12px; 56 | background: rgba(0, 0, 0, 0.8); 57 | color: #fff; 58 | border-radius: 2px; 59 | pointer-events: none; 60 | } 61 | 62 | -------------------------------------------------------------------------------- /08-scatter-exercise/css/styles.css: -------------------------------------------------------------------------------- 1 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 2 | @import url("../../lib/bootstrap.min.css"); 3 | 4 | /* Header */ 5 | .container { 6 | text-align: center; 7 | } 8 | 9 | .jumbotron { 10 | padding:1rem 1rem; 11 | margin-bottom:20px; 12 | } 13 | 14 | .lead { 15 | margin-bottom:0px; 16 | } 17 | 18 | /* Controls */ 19 | .custom-select { 20 | width:inherit; 21 | } 22 | 23 | .control-wrapper { 24 | display: inline-block; 25 | margin-left:10px; 26 | } 27 | 28 | .control-wrapper input[type="range"] { 29 | vertical-align: text-bottom;; 30 | } 31 | 32 | .control-container { 33 | border-bottom:1px solid #d3d3d3; 34 | margin-bottom:10px; 35 | padding-bottom:10px; 36 | } 37 | 38 | /* Scatter plot */ 39 | circle { 40 | cursor:pointer; 41 | } 42 | .axis-label { 43 | text-anchor:middle; 44 | font-size:10px; 45 | } 46 | 47 | .chart-wrapper { 48 | display: inline-block; 49 | } 50 | 51 | 52 | /* Tooltips */ 53 | .d3-tip { 54 | line-height: 1; 55 | font-weight: bold; 56 | padding: 12px; 57 | background: rgba(0, 0, 0, 0.8); 58 | color: #fff; 59 | border-radius: 2px; 60 | pointer-events: none; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /09-small-multiples-exercise/css/styles.css: -------------------------------------------------------------------------------- 1 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 2 | @import url("../../lib/bootstrap.min.css"); 3 | 4 | /* Header */ 5 | .container { 6 | text-align: center; 7 | } 8 | 9 | .lead { 10 | margin-bottom:0px; 11 | } 12 | 13 | .jumbotron { 14 | padding:1rem 1rem; 15 | margin-bottom:20px; 16 | } 17 | 18 | /* Controls */ 19 | .custom-select { 20 | width:inherit; 21 | } 22 | 23 | .control-wrapper { 24 | display: inline-block; 25 | margin-left:10px; 26 | } 27 | 28 | .control-wrapper input[type="range"] { 29 | vertical-align: text-bottom;; 30 | } 31 | 32 | .control-container { 33 | border-bottom:1px solid #d3d3d3; 34 | margin-bottom:10px; 35 | padding-bottom:10px; 36 | } 37 | 38 | /* Scatter plot */ 39 | circle { 40 | cursor:pointer; 41 | } 42 | .axis-label { 43 | text-anchor:middle; 44 | font-size:10px; 45 | } 46 | 47 | .chart-wrapper { 48 | display: inline-block; 49 | } 50 | 51 | 52 | /* Tooltips */ 53 | .d3-tip { 54 | line-height: 1; 55 | font-weight: bold; 56 | padding: 12px; 57 | background: rgba(0, 0, 0, 0.8); 58 | color: #fff; 59 | border-radius: 2px; 60 | pointer-events: none; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/css/styles.css: -------------------------------------------------------------------------------- 1 | /* @import url("https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"); */ 2 | @import url("../../lib/bootstrap.min.css"); 3 | 4 | /* Header */ 5 | .container { 6 | text-align: center; 7 | } 8 | 9 | .lead { 10 | margin-bottom:0px; 11 | } 12 | 13 | .jumbotron { 14 | padding:1rem 1rem; 15 | margin-bottom:20px; 16 | } 17 | 18 | /* Controls */ 19 | .custom-select { 20 | width:inherit; 21 | } 22 | 23 | .control-wrapper { 24 | display: inline-block; 25 | margin-left:10px; 26 | } 27 | 28 | .control-wrapper input[type="range"] { 29 | vertical-align: text-bottom;; 30 | } 31 | 32 | .control-container { 33 | border-bottom:1px solid #d3d3d3; 34 | margin-bottom:10px; 35 | padding-bottom:10px; 36 | } 37 | 38 | /* Scatter plot */ 39 | circle { 40 | cursor:pointer; 41 | } 42 | .axis-label { 43 | text-anchor:middle; 44 | font-size:10px; 45 | } 46 | 47 | .chart-wrapper { 48 | display: inline-block; 49 | } 50 | 51 | 52 | /* Tooltips */ 53 | .d3-tip { 54 | line-height: 1; 55 | font-weight: bold; 56 | padding: 12px; 57 | background: rgba(0, 0, 0, 0.8); 58 | color: #fff; 59 | border-radius: 2px; 60 | pointer-events: none; 61 | } 62 | 63 | -------------------------------------------------------------------------------- /01-basic-d3-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 01-basic-d3-demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |

01-basic-d3-demo

29 |

A mutual starting point for using D3 and React (i.e., what you should already be able to do)

30 | (all exercises) 31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /05-state-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 05-state-demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |

05-state-demo

30 |

A demo of how to use state to re-render a component.

31 | (all exercises) 32 |
33 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /03-component-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 03-component-demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |

03-component-demo

30 |

A quick demonstration of rendering a React component

31 | (all exercises) 32 |
33 |
34 |
35 | 36 | 37 | -------------------------------------------------------------------------------- /02-class-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 02-class-demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |

02-class-demo

30 |

A quick demonstration of using classes (inspect the element and see the console)

31 | (all exercises) 32 |
33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /04-react-intro-exercise/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 04-react-intro-exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |

04-react-intro-exercise

30 |

An exercise on creating components and passing them properties. See the 31 | README.md file for instructions.

32 | (all exercises) 33 |
34 |
35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /04-react-intro-exercise/js/solution.js: -------------------------------------------------------------------------------- 1 | // Solution: making a todo list 2 | "use strict"; 3 | 4 | // A set of items to render in your todolist 5 | const todos = [ 6 | { 7 | description: "Learn how to use React and D3", 8 | priority: "high", 9 | status: "incomplete" 10 | 11 | }, 12 | { 13 | description: "Explore Paris", 14 | priority: "medium", 15 | status: "complete" 16 | }, 17 | { 18 | description: "Catch up on work", 19 | priority: "low", 20 | status: "incomplete" 21 | } 22 | ]; 23 | 24 | // Create a component Todo that represents an item in a to do list (see README.md for instructions) 25 | class Todo extends React.Component { 26 | // Render the Todo item 27 | render() { 28 | return ( 29 |
30 |

{this.props.description}

31 |
32 | ) 33 | } 34 | } 35 | 36 | // Create a class TodoList (see README.md for instructions) 37 | class TodoList extends React.Component { 38 | // Render a Todo component for each element in the `list` props 39 | render() { 40 | return ( 41 |
42 | {this.props.list.map((d, i) => { 43 | return 44 | }) 45 | } 46 |
47 | ) 48 | } 49 | } 50 | 51 | // Render the TodoList component in the `root` element 52 | ReactDOM.render( 53 | , 54 | document.getElementById('root') 55 | ); -------------------------------------------------------------------------------- /08-scatter-exercise/js/App.js: -------------------------------------------------------------------------------- 1 | // Application file 2 | class App extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | 6 | // Set initial state 7 | this.state = { 8 | data: [], 9 | xVar: "percollege", 10 | yVar: "percbelowpoverty" 11 | }; 12 | } 13 | // Load data and set statewhen the component mounts 14 | componentDidMount() { 15 | 16 | } 17 | render() { 18 | // Get list of possible x and y variables 19 | let options = this.state.data.length === 0 ? [] : Object.keys(this.state.data[0]); 20 | options = options.filter((d) => d != "county" && d != "state"); 21 | 22 | // Compute `allData`: an array of objects for your scatterplot 23 | 24 | return ( 25 |
26 |
27 | 28 | {/* X Variable Select Menu */} 29 |
30 | 31 | 36 |
37 | 38 | {/* Create a Y Variable Select Menu */} 39 | 40 |
41 | 42 | {/* Render a `` plot */} 43 | 44 |
45 | ) 46 | } 47 | } 48 | 49 | // Render application 50 | ReactDOM.render( 51 | , 52 | document.getElementById('root') 53 | ); -------------------------------------------------------------------------------- /06-state-exercise/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 06-state-exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 |

06-state-exercise

40 |

An exercise exploring state in React components. See 41 | README.md for instructions.

42 | (all exercises) 43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /07-d3-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 07-d3-demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 |

07-d3-demo

40 |

A basic demonstration of React + D3

41 | (all exercises) 42 |
43 |
44 | 45 | 46 | -------------------------------------------------------------------------------- /08-scatter-exercise/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 08-scatter-exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |

08-scatter-exercise

41 |

Making a D3 scatterplot in React

42 | (all exercises) 43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /09-small-multiples-exercise/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 09-small-multiples-exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |

09-small-multiples-exercise

41 |

Building small multiples with React + D3

42 | (all exercises) 43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10-lifting-up-state-exercise 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 |

10-lifting-up-state-exercise

42 |

Adding interactivity across charts

43 | (all exercises) 44 |
45 |
46 | 47 | 48 | -------------------------------------------------------------------------------- /05-state-demo/js/main.js: -------------------------------------------------------------------------------- 1 | // Solution: making a todo list 2 | "use strict"; 3 | 4 | // A set of items to render in your todolist 5 | const todos = [ 6 | { 7 | description: "Learn how to use React and D3", 8 | priority: "high", 9 | status: "incomplete" 10 | 11 | }, 12 | { 13 | description: "Explore Paris", 14 | priority: "medium", 15 | status: "complete" 16 | }, 17 | { 18 | description: "Catch up on work", 19 | priority: "low", 20 | status: "incomplete" 21 | } 22 | ]; 23 | 24 | // Create a component ToDo that represents an item in a to do list (see README.md for instructions) 25 | class Todo extends React.Component { 26 | // Render the Todo item 27 | render() { 28 | return ( 29 |
30 |

{this.props.description}

31 |
32 | ) 33 | } 34 | } 35 | 36 | // Create a class TodoList (see README.md for instructions) 37 | class TodoList extends React.Component { 38 | // Add a constructor method to set the state 39 | constructor(props) { 40 | super(props); 41 | this.state = { 42 | search: "" 43 | } 44 | } 45 | 46 | // Render a Todo component for each element in the `list` props 47 | render() { 48 | // Filter down the list of todos to the state 49 | let currentList = this.props.list.filter((d) => { 50 | return d.description.match(this.state.search); 51 | }) 52 | return ( 53 |
54 | {/* An input element to search through the todo items*/} 55 | this.setState({ search: event.target.value })} /> 56 | {currentList.map((d, i) => { 57 | return 58 | }) 59 | } 60 |
61 | ) 62 | } 63 | } 64 | 65 | // Render the TodoList component in the `root` element 66 | ReactDOM.render( 67 | , 68 | document.getElementById('root') 69 | ); -------------------------------------------------------------------------------- /06-state-exercise/js/App.js: -------------------------------------------------------------------------------- 1 | // Application file 2 | class App extends React.Component { 3 | 4 | constructor(props) { 5 | super(props); 6 | 7 | // Set initial state 8 | this.state = { 9 | data: [], 10 | variable: "percollege", 11 | nShow: 10, 12 | sort:"ascending" 13 | }; 14 | } 15 | 16 | componentDidMount() { 17 | // Load data when the component mounts 18 | 19 | } 20 | render() { 21 | // Create a variable `options` storing the list of possible variables to show. 22 | // These should be the **object keys** from the first element in your data, excluding "county" and "state". 23 | 24 | 25 | // Create a variable `allData` in which you store the current value of interest 26 | // (based on `this.state.variable`), as well as the state and county 27 | 28 | 29 | // Store the mean of the current value in a variable `mean` 30 | 31 | 32 | // Store the top N values (based on `this.state.nShow`) from the data in a variable. 33 | // Observatiosn should be sorted by the current sorting variable (`this.state.variable`) 34 | // in either ascending or descending order (`this.state.sort`). 35 | 36 | 37 | // Return an HTML node to render 38 | return ( 39 |
40 |
41 |
42 | 43 | {/* Create a Select Menu to determine which variable is shown in the table */} 44 | 45 | 46 | {/*Create a Slider to control how many rows are shown in the table */} 47 | 48 | 49 | {/*Create a Button Group to control if rows in the table are shown in ascending or descending order */} 50 | 51 |
52 | 53 | {/* Display the average value (`mean`) in a paragraph element */} 54 | 55 | 56 | {/* Show a table of the top N counties */} 57 |
58 |
59 | ) 60 | } 61 | } 62 | 63 | // Render application 64 | ReactDOM.render( 65 | , 66 | document.getElementById('root') 67 | ); -------------------------------------------------------------------------------- /07-d3-demo/js/App.js: -------------------------------------------------------------------------------- 1 | // Application file 2 | class App extends React.Component { 3 | // Set initial state in the `constructor()` function 4 | constructor(props) { 5 | super(props); 6 | 7 | // Set initial state 8 | this.state = { 9 | numPoints:10 10 | }; 11 | } 12 | 13 | // When the component mounts, run the `updatePoints()` method 14 | componentDidMount() { 15 | this.updatePoints() 16 | } 17 | 18 | // When the component updates, run the `updatePoints()` method 19 | componentDidUpdate() { 20 | this.updatePoints() 21 | } 22 | 23 | // Method to update the cirlces using D3 24 | updatePoints() { 25 | // Randomly generate points based on the `width` and `height` props 26 | let data = d3.range(this.state.numPoints).map((d) => { 27 | return {x: Math.random() * this.props.width, y: Math.random() * this.props.height} 28 | }); 29 | 30 | // Select all the circles within thje element 31 | let circles = d3.select(this.chartArea).selectAll('circle').data(data); 32 | 33 | // Use the .enter() method to get your entering elements, and assign their positions 34 | circles.enter().append('circle') 35 | .attr('r', (d) => 3) 36 | .attr('fill', (d) => "blue") 37 | .attr('cx', (d) => d.x) 38 | .attr('cy', (d) => d.y); 39 | } 40 | 41 | // Render method 42 | render() { 43 | 44 | // Return HTML elements to hold the chart 45 | return ( 46 |
47 |
48 | 49 |
50 |
51 | 52 | { this.chartArea = node; }} 53 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`} /> 54 | 55 |
56 |
57 | ) 58 | } 59 | } 60 | 61 | // Set default properties 62 | App.defaultProps = { 63 | margin: { 64 | left: 0, 65 | right: 10, 66 | top: 20, 67 | bottom: 50 68 | } 69 | }; 70 | 71 | // Render application 72 | ReactDOM.render( 73 | , 74 | document.getElementById('root') 75 | ); 76 | 77 | -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/README.md: -------------------------------------------------------------------------------- 1 | # 10-lifting-up-state-exercise 2 | 3 | In this exercise, you'll follow the instructions below to **lift up state** in a React application, adding interactivity to the following charts: 4 | 5 | ![Complete small multiples scatterplots](img/complete.png) 6 | 7 | In doing so, you'll use **pass functions as props** to your React components. See the `*_solution.js` files for solutions. 8 | 9 | ## Instructions 10 | The majority of the code has been written for this exercise. The core challenge of this exercise is to figure out how to _pass functions as props_ so that you can _lift up state_. More specifically, you'll be able to **hover** on your scatterplot to change the selected county, or **click** on a row of your table to change the `y` axis. See below for the changes to make in each file. 11 | 12 | ### `` Component 13 | All changes to your application will be tracked in the **state** of your `` component. This component will need methods to update its state (i.e., `this.updateXvar()`) so that they can be easily _passed to its child components_. Importantly, you'll need to **bind the scope** to each of these methods in the `constructor()` method. To achieve this, follow these steps: 14 | 15 | - Create a new method `updateXvar()` for your component. It should take in a parameter (a _string_ of the new X variable to display), then update the state of `xVar` given that target's value. 16 | - Use the `updateXvar()` method in your ` this.setState({ xVar: d.target.value })}> 41 | {options.map((d) => { 42 | return 43 | })} 44 | 45 |
46 | 47 | {/* Y Variable Select Menu */} 48 |
49 | 50 | 55 |
56 |
57 | 58 | {/* Render scatter plot */} 59 | 64 |
65 | ) 66 | } 67 | } 68 | 69 | // Render application 70 | ReactDOM.render( 71 | , 72 | document.getElementById('root') 73 | ); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | React + D3 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 |

React + D3

50 |

51 | These resources are designed to teach D3 users how to scaffold their projects using React. Demos are completed demonstrations of certain techniques, while exercises contain instructions for building a particular page (a *_solution.js file is also included). These were originally developed for a workshop 52 | at 53 | OpenvisConf 2018. All code is available 54 | on GitHub. Built by 55 | Michael Freeman. See accompanying slides here 56 |

57 |
58 | 59 | 60 | -------------------------------------------------------------------------------- /08-scatter-exercise/js/ScatterPlot.js: -------------------------------------------------------------------------------- 1 | // Scatterplot 2 | class ScatterPlot extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | // Graph width and height - accounting for margins 6 | this.drawWidth = this.props.width - this.props.margin.left - this.props.margin.right; 7 | this.drawHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; 8 | 9 | } 10 | 11 | // When the component mounts, call the `update()` method 12 | componentDidMount() { 13 | this.update(); 14 | } 15 | 16 | // Whenever the component updates, call the `update()` method 17 | componentDidUpdate() { 18 | this.update(); 19 | } 20 | 21 | // Define the functions for `this.xScale()` and `this.yScale()` based on current data 22 | updateScales() { 23 | // Calculate limits: minimum/maximum x and y values in the data 24 | 25 | 26 | // Define scales `this.xScale()` and `this.yScale()` using `d3.scaleLinear()` 27 | 28 | } 29 | 30 | // Update the position of the circles 31 | updatePoints() { 32 | // Define hovers 33 | // Add tip 34 | let tip = d3.tip().attr('class', 'd3-tip').html(function (d) { 35 | return d.label; 36 | }); 37 | 38 | // Bind data: select all circles and bind data 39 | 40 | 41 | // Append and position elements: using `enter()` and `merge()` 42 | 43 | 44 | // Exit and remove elements 45 | 46 | 47 | // Add hovers using the d3-tip library 48 | 49 | } 50 | 51 | // Update axes 52 | updateAxes() { 53 | // Define axis functions 54 | 55 | 56 | // Draw axes: select your axes and call the axis functions defined above 57 | 58 | } 59 | 60 | // Update function: call `updateScales()`, `updateAxes()`, and `updatePoints()` 61 | update() { 62 | 63 | } 64 | 65 | // Render method 66 | render() { 67 | return ( 68 |
69 | 70 | {this.props.title} 71 | 72 | 73 | {/* Axes */} 74 | 75 | 76 | 77 | {/* Axis labels */} 78 | {this.props.xTitle} 80 | 81 | {this.props.yTitle} 83 | 84 |
85 | 86 | ) 87 | } 88 | } 89 | 90 | ScatterPlot.defaultProps = { 91 | data: [{ x: 10, y: 20 }, { x: 15, y: 35 }], 92 | width: 300, 93 | height: 300, 94 | radius: 5, 95 | color: "blue", 96 | margin: { 97 | left: 50, 98 | right: 10, 99 | top: 20, 100 | bottom: 50 101 | }, 102 | xTitle: "X Title", 103 | yTitle: "Y Title", 104 | title:"Chart Title" 105 | }; -------------------------------------------------------------------------------- /07-d3-demo/data/.Rhistory: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(ggplot2) 3 | View(midwest) 4 | dim(midwest) 5 | ?midwest 6 | levels(midwest$state) 7 | unique(midwest$state) 8 | styler:::style_active_file() 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin") 16 | ) 17 | View(prepped) 18 | library(tools) 19 | prepped <- midwest %>% 20 | mutate( 21 | state = replace(state, state == "IL", "Illinois"), 22 | state = replace(state, state == "IN", "Indiana"), 23 | state = replace(state, state == "MI", "Michigan"), 24 | state = replace(state, state == "OH", "Ohio"), 25 | state = replace(state, state == "WI", "Wisconsin"), 26 | county = toTitleCase(county) 27 | ) %>% 28 | select(county, state, %in% "pop") 29 | prepped <- midwest %>% 30 | mutate( 31 | state = replace(state, state == "IL", "Illinois"), 32 | state = replace(state, state == "IN", "Indiana"), 33 | state = replace(state, state == "MI", "Michigan"), 34 | state = replace(state, state == "OH", "Ohio"), 35 | state = replace(state, state == "WI", "Wisconsin"), 36 | county = toTitleCase(county) 37 | ) %>% 38 | select(county, state, inmetro, contains("per")) 39 | View(prepped) 40 | ?perchsd 41 | ?midwest 42 | prepped <- midwest %>% 43 | mutate( 44 | state = replace(state, state == "IL", "Illinois"), 45 | state = replace(state, state == "IN", "Indiana"), 46 | state = replace(state, state == "MI", "Michigan"), 47 | state = replace(state, state == "OH", "Ohio"), 48 | state = replace(state, state == "WI", "Wisconsin"), 49 | county = toTitleCase(county) 50 | ) %>% 51 | select(county, state, inmetro, contains("per"), -perchsd) 52 | midwest %>% 53 | mutate( 54 | state = replace(state, state == "IL", "Illinois"), 55 | state = replace(state, state == "IN", "Indiana"), 56 | state = replace(state, state == "MI", "Michigan"), 57 | state = replace(state, state == "OH", "Ohio"), 58 | state = replace(state, state == "WI", "Wisconsin"), 59 | county = toTitleCase(county) 60 | ) 61 | toTitleCase(midwest$county) 62 | library(stringr) 63 | prepped <- midwest %>% 64 | mutate( 65 | state = replace(state, state == "IL", "Illinois"), 66 | state = replace(state, state == "IN", "Indiana"), 67 | state = replace(state, state == "MI", "Michigan"), 68 | state = replace(state, state == "OH", "Ohio"), 69 | state = replace(state, state == "WI", "Wisconsin"), 70 | county = str_to_title(county) 71 | ) %>% 72 | select(county, state, inmetro, contains("per"), -perchsd) 73 | setwd("~/Documents/react-d3/demo/data") 74 | # Data prep 75 | # Use built in `midwest` data from the `ggplot2` package 76 | library(ggplot2) 77 | library(dplyr) 78 | library(stringr) 79 | # Replace values with full state names 80 | prepped <- midwest %>% 81 | mutate( 82 | state = replace(state, state == "IL", "Illinois"), 83 | state = replace(state, state == "IN", "Indiana"), 84 | state = replace(state, state == "MI", "Michigan"), 85 | state = replace(state, state == "OH", "Ohio"), 86 | state = replace(state, state == "WI", "Wisconsin"), 87 | county = str_to_title(county) 88 | ) %>% 89 | select(county, state, inmetro, contains("per"), -perchsd) 90 | # Write data 91 | write.csv("midwest.csv") 92 | # Data prep 93 | # Use built in `midwest` data from the `ggplot2` package 94 | library(ggplot2) 95 | library(dplyr) 96 | library(stringr) 97 | # Replace values with full state names 98 | prepped <- midwest %>% 99 | mutate( 100 | state = replace(state, state == "IL", "Illinois"), 101 | state = replace(state, state == "IN", "Indiana"), 102 | state = replace(state, state == "MI", "Michigan"), 103 | state = replace(state, state == "OH", "Ohio"), 104 | state = replace(state, state == "WI", "Wisconsin"), 105 | county = str_to_title(county) 106 | ) %>% 107 | select(county, state, inmetro, contains("per"), -perchsd) 108 | # Write data 109 | write.csv(prepped, "midwest.csv", row.names = F) 110 | -------------------------------------------------------------------------------- /01-basic-d3-demo/data/.Rhistory: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(ggplot2) 3 | View(midwest) 4 | dim(midwest) 5 | ?midwest 6 | levels(midwest$state) 7 | unique(midwest$state) 8 | styler:::style_active_file() 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin") 16 | ) 17 | View(prepped) 18 | library(tools) 19 | prepped <- midwest %>% 20 | mutate( 21 | state = replace(state, state == "IL", "Illinois"), 22 | state = replace(state, state == "IN", "Indiana"), 23 | state = replace(state, state == "MI", "Michigan"), 24 | state = replace(state, state == "OH", "Ohio"), 25 | state = replace(state, state == "WI", "Wisconsin"), 26 | county = toTitleCase(county) 27 | ) %>% 28 | select(county, state, %in% "pop") 29 | prepped <- midwest %>% 30 | mutate( 31 | state = replace(state, state == "IL", "Illinois"), 32 | state = replace(state, state == "IN", "Indiana"), 33 | state = replace(state, state == "MI", "Michigan"), 34 | state = replace(state, state == "OH", "Ohio"), 35 | state = replace(state, state == "WI", "Wisconsin"), 36 | county = toTitleCase(county) 37 | ) %>% 38 | select(county, state, inmetro, contains("per")) 39 | View(prepped) 40 | ?perchsd 41 | ?midwest 42 | prepped <- midwest %>% 43 | mutate( 44 | state = replace(state, state == "IL", "Illinois"), 45 | state = replace(state, state == "IN", "Indiana"), 46 | state = replace(state, state == "MI", "Michigan"), 47 | state = replace(state, state == "OH", "Ohio"), 48 | state = replace(state, state == "WI", "Wisconsin"), 49 | county = toTitleCase(county) 50 | ) %>% 51 | select(county, state, inmetro, contains("per"), -perchsd) 52 | midwest %>% 53 | mutate( 54 | state = replace(state, state == "IL", "Illinois"), 55 | state = replace(state, state == "IN", "Indiana"), 56 | state = replace(state, state == "MI", "Michigan"), 57 | state = replace(state, state == "OH", "Ohio"), 58 | state = replace(state, state == "WI", "Wisconsin"), 59 | county = toTitleCase(county) 60 | ) 61 | toTitleCase(midwest$county) 62 | library(stringr) 63 | prepped <- midwest %>% 64 | mutate( 65 | state = replace(state, state == "IL", "Illinois"), 66 | state = replace(state, state == "IN", "Indiana"), 67 | state = replace(state, state == "MI", "Michigan"), 68 | state = replace(state, state == "OH", "Ohio"), 69 | state = replace(state, state == "WI", "Wisconsin"), 70 | county = str_to_title(county) 71 | ) %>% 72 | select(county, state, inmetro, contains("per"), -perchsd) 73 | setwd("~/Documents/react-d3/demo/data") 74 | # Data prep 75 | # Use built in `midwest` data from the `ggplot2` package 76 | library(ggplot2) 77 | library(dplyr) 78 | library(stringr) 79 | # Replace values with full state names 80 | prepped <- midwest %>% 81 | mutate( 82 | state = replace(state, state == "IL", "Illinois"), 83 | state = replace(state, state == "IN", "Indiana"), 84 | state = replace(state, state == "MI", "Michigan"), 85 | state = replace(state, state == "OH", "Ohio"), 86 | state = replace(state, state == "WI", "Wisconsin"), 87 | county = str_to_title(county) 88 | ) %>% 89 | select(county, state, inmetro, contains("per"), -perchsd) 90 | # Write data 91 | write.csv("midwest.csv") 92 | # Data prep 93 | # Use built in `midwest` data from the `ggplot2` package 94 | library(ggplot2) 95 | library(dplyr) 96 | library(stringr) 97 | # Replace values with full state names 98 | prepped <- midwest %>% 99 | mutate( 100 | state = replace(state, state == "IL", "Illinois"), 101 | state = replace(state, state == "IN", "Indiana"), 102 | state = replace(state, state == "MI", "Michigan"), 103 | state = replace(state, state == "OH", "Ohio"), 104 | state = replace(state, state == "WI", "Wisconsin"), 105 | county = str_to_title(county) 106 | ) %>% 107 | select(county, state, inmetro, contains("per"), -perchsd) 108 | # Write data 109 | write.csv(prepped, "midwest.csv", row.names = F) 110 | -------------------------------------------------------------------------------- /06-state-exercise/data/.Rhistory: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(ggplot2) 3 | View(midwest) 4 | dim(midwest) 5 | ?midwest 6 | levels(midwest$state) 7 | unique(midwest$state) 8 | styler:::style_active_file() 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin") 16 | ) 17 | View(prepped) 18 | library(tools) 19 | prepped <- midwest %>% 20 | mutate( 21 | state = replace(state, state == "IL", "Illinois"), 22 | state = replace(state, state == "IN", "Indiana"), 23 | state = replace(state, state == "MI", "Michigan"), 24 | state = replace(state, state == "OH", "Ohio"), 25 | state = replace(state, state == "WI", "Wisconsin"), 26 | county = toTitleCase(county) 27 | ) %>% 28 | select(county, state, %in% "pop") 29 | prepped <- midwest %>% 30 | mutate( 31 | state = replace(state, state == "IL", "Illinois"), 32 | state = replace(state, state == "IN", "Indiana"), 33 | state = replace(state, state == "MI", "Michigan"), 34 | state = replace(state, state == "OH", "Ohio"), 35 | state = replace(state, state == "WI", "Wisconsin"), 36 | county = toTitleCase(county) 37 | ) %>% 38 | select(county, state, inmetro, contains("per")) 39 | View(prepped) 40 | ?perchsd 41 | ?midwest 42 | prepped <- midwest %>% 43 | mutate( 44 | state = replace(state, state == "IL", "Illinois"), 45 | state = replace(state, state == "IN", "Indiana"), 46 | state = replace(state, state == "MI", "Michigan"), 47 | state = replace(state, state == "OH", "Ohio"), 48 | state = replace(state, state == "WI", "Wisconsin"), 49 | county = toTitleCase(county) 50 | ) %>% 51 | select(county, state, inmetro, contains("per"), -perchsd) 52 | midwest %>% 53 | mutate( 54 | state = replace(state, state == "IL", "Illinois"), 55 | state = replace(state, state == "IN", "Indiana"), 56 | state = replace(state, state == "MI", "Michigan"), 57 | state = replace(state, state == "OH", "Ohio"), 58 | state = replace(state, state == "WI", "Wisconsin"), 59 | county = toTitleCase(county) 60 | ) 61 | toTitleCase(midwest$county) 62 | library(stringr) 63 | prepped <- midwest %>% 64 | mutate( 65 | state = replace(state, state == "IL", "Illinois"), 66 | state = replace(state, state == "IN", "Indiana"), 67 | state = replace(state, state == "MI", "Michigan"), 68 | state = replace(state, state == "OH", "Ohio"), 69 | state = replace(state, state == "WI", "Wisconsin"), 70 | county = str_to_title(county) 71 | ) %>% 72 | select(county, state, inmetro, contains("per"), -perchsd) 73 | setwd("~/Documents/react-d3/demo/data") 74 | # Data prep 75 | # Use built in `midwest` data from the `ggplot2` package 76 | library(ggplot2) 77 | library(dplyr) 78 | library(stringr) 79 | # Replace values with full state names 80 | prepped <- midwest %>% 81 | mutate( 82 | state = replace(state, state == "IL", "Illinois"), 83 | state = replace(state, state == "IN", "Indiana"), 84 | state = replace(state, state == "MI", "Michigan"), 85 | state = replace(state, state == "OH", "Ohio"), 86 | state = replace(state, state == "WI", "Wisconsin"), 87 | county = str_to_title(county) 88 | ) %>% 89 | select(county, state, inmetro, contains("per"), -perchsd) 90 | # Write data 91 | write.csv("midwest.csv") 92 | # Data prep 93 | # Use built in `midwest` data from the `ggplot2` package 94 | library(ggplot2) 95 | library(dplyr) 96 | library(stringr) 97 | # Replace values with full state names 98 | prepped <- midwest %>% 99 | mutate( 100 | state = replace(state, state == "IL", "Illinois"), 101 | state = replace(state, state == "IN", "Indiana"), 102 | state = replace(state, state == "MI", "Michigan"), 103 | state = replace(state, state == "OH", "Ohio"), 104 | state = replace(state, state == "WI", "Wisconsin"), 105 | county = str_to_title(county) 106 | ) %>% 107 | select(county, state, inmetro, contains("per"), -perchsd) 108 | # Write data 109 | write.csv(prepped, "midwest.csv", row.names = F) 110 | -------------------------------------------------------------------------------- /08-scatter-exercise/data/.Rhistory: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(ggplot2) 3 | View(midwest) 4 | dim(midwest) 5 | ?midwest 6 | levels(midwest$state) 7 | unique(midwest$state) 8 | styler:::style_active_file() 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin") 16 | ) 17 | View(prepped) 18 | library(tools) 19 | prepped <- midwest %>% 20 | mutate( 21 | state = replace(state, state == "IL", "Illinois"), 22 | state = replace(state, state == "IN", "Indiana"), 23 | state = replace(state, state == "MI", "Michigan"), 24 | state = replace(state, state == "OH", "Ohio"), 25 | state = replace(state, state == "WI", "Wisconsin"), 26 | county = toTitleCase(county) 27 | ) %>% 28 | select(county, state, %in% "pop") 29 | prepped <- midwest %>% 30 | mutate( 31 | state = replace(state, state == "IL", "Illinois"), 32 | state = replace(state, state == "IN", "Indiana"), 33 | state = replace(state, state == "MI", "Michigan"), 34 | state = replace(state, state == "OH", "Ohio"), 35 | state = replace(state, state == "WI", "Wisconsin"), 36 | county = toTitleCase(county) 37 | ) %>% 38 | select(county, state, inmetro, contains("per")) 39 | View(prepped) 40 | ?perchsd 41 | ?midwest 42 | prepped <- midwest %>% 43 | mutate( 44 | state = replace(state, state == "IL", "Illinois"), 45 | state = replace(state, state == "IN", "Indiana"), 46 | state = replace(state, state == "MI", "Michigan"), 47 | state = replace(state, state == "OH", "Ohio"), 48 | state = replace(state, state == "WI", "Wisconsin"), 49 | county = toTitleCase(county) 50 | ) %>% 51 | select(county, state, inmetro, contains("per"), -perchsd) 52 | midwest %>% 53 | mutate( 54 | state = replace(state, state == "IL", "Illinois"), 55 | state = replace(state, state == "IN", "Indiana"), 56 | state = replace(state, state == "MI", "Michigan"), 57 | state = replace(state, state == "OH", "Ohio"), 58 | state = replace(state, state == "WI", "Wisconsin"), 59 | county = toTitleCase(county) 60 | ) 61 | toTitleCase(midwest$county) 62 | library(stringr) 63 | prepped <- midwest %>% 64 | mutate( 65 | state = replace(state, state == "IL", "Illinois"), 66 | state = replace(state, state == "IN", "Indiana"), 67 | state = replace(state, state == "MI", "Michigan"), 68 | state = replace(state, state == "OH", "Ohio"), 69 | state = replace(state, state == "WI", "Wisconsin"), 70 | county = str_to_title(county) 71 | ) %>% 72 | select(county, state, inmetro, contains("per"), -perchsd) 73 | setwd("~/Documents/react-d3/demo/data") 74 | # Data prep 75 | # Use built in `midwest` data from the `ggplot2` package 76 | library(ggplot2) 77 | library(dplyr) 78 | library(stringr) 79 | # Replace values with full state names 80 | prepped <- midwest %>% 81 | mutate( 82 | state = replace(state, state == "IL", "Illinois"), 83 | state = replace(state, state == "IN", "Indiana"), 84 | state = replace(state, state == "MI", "Michigan"), 85 | state = replace(state, state == "OH", "Ohio"), 86 | state = replace(state, state == "WI", "Wisconsin"), 87 | county = str_to_title(county) 88 | ) %>% 89 | select(county, state, inmetro, contains("per"), -perchsd) 90 | # Write data 91 | write.csv("midwest.csv") 92 | # Data prep 93 | # Use built in `midwest` data from the `ggplot2` package 94 | library(ggplot2) 95 | library(dplyr) 96 | library(stringr) 97 | # Replace values with full state names 98 | prepped <- midwest %>% 99 | mutate( 100 | state = replace(state, state == "IL", "Illinois"), 101 | state = replace(state, state == "IN", "Indiana"), 102 | state = replace(state, state == "MI", "Michigan"), 103 | state = replace(state, state == "OH", "Ohio"), 104 | state = replace(state, state == "WI", "Wisconsin"), 105 | county = str_to_title(county) 106 | ) %>% 107 | select(county, state, inmetro, contains("per"), -perchsd) 108 | # Write data 109 | write.csv(prepped, "midwest.csv", row.names = F) 110 | -------------------------------------------------------------------------------- /09-small-multiples-exercise/data/.Rhistory: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(ggplot2) 3 | View(midwest) 4 | dim(midwest) 5 | ?midwest 6 | levels(midwest$state) 7 | unique(midwest$state) 8 | styler:::style_active_file() 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin") 16 | ) 17 | View(prepped) 18 | library(tools) 19 | prepped <- midwest %>% 20 | mutate( 21 | state = replace(state, state == "IL", "Illinois"), 22 | state = replace(state, state == "IN", "Indiana"), 23 | state = replace(state, state == "MI", "Michigan"), 24 | state = replace(state, state == "OH", "Ohio"), 25 | state = replace(state, state == "WI", "Wisconsin"), 26 | county = toTitleCase(county) 27 | ) %>% 28 | select(county, state, %in% "pop") 29 | prepped <- midwest %>% 30 | mutate( 31 | state = replace(state, state == "IL", "Illinois"), 32 | state = replace(state, state == "IN", "Indiana"), 33 | state = replace(state, state == "MI", "Michigan"), 34 | state = replace(state, state == "OH", "Ohio"), 35 | state = replace(state, state == "WI", "Wisconsin"), 36 | county = toTitleCase(county) 37 | ) %>% 38 | select(county, state, inmetro, contains("per")) 39 | View(prepped) 40 | ?perchsd 41 | ?midwest 42 | prepped <- midwest %>% 43 | mutate( 44 | state = replace(state, state == "IL", "Illinois"), 45 | state = replace(state, state == "IN", "Indiana"), 46 | state = replace(state, state == "MI", "Michigan"), 47 | state = replace(state, state == "OH", "Ohio"), 48 | state = replace(state, state == "WI", "Wisconsin"), 49 | county = toTitleCase(county) 50 | ) %>% 51 | select(county, state, inmetro, contains("per"), -perchsd) 52 | midwest %>% 53 | mutate( 54 | state = replace(state, state == "IL", "Illinois"), 55 | state = replace(state, state == "IN", "Indiana"), 56 | state = replace(state, state == "MI", "Michigan"), 57 | state = replace(state, state == "OH", "Ohio"), 58 | state = replace(state, state == "WI", "Wisconsin"), 59 | county = toTitleCase(county) 60 | ) 61 | toTitleCase(midwest$county) 62 | library(stringr) 63 | prepped <- midwest %>% 64 | mutate( 65 | state = replace(state, state == "IL", "Illinois"), 66 | state = replace(state, state == "IN", "Indiana"), 67 | state = replace(state, state == "MI", "Michigan"), 68 | state = replace(state, state == "OH", "Ohio"), 69 | state = replace(state, state == "WI", "Wisconsin"), 70 | county = str_to_title(county) 71 | ) %>% 72 | select(county, state, inmetro, contains("per"), -perchsd) 73 | setwd("~/Documents/react-d3/demo/data") 74 | # Data prep 75 | # Use built in `midwest` data from the `ggplot2` package 76 | library(ggplot2) 77 | library(dplyr) 78 | library(stringr) 79 | # Replace values with full state names 80 | prepped <- midwest %>% 81 | mutate( 82 | state = replace(state, state == "IL", "Illinois"), 83 | state = replace(state, state == "IN", "Indiana"), 84 | state = replace(state, state == "MI", "Michigan"), 85 | state = replace(state, state == "OH", "Ohio"), 86 | state = replace(state, state == "WI", "Wisconsin"), 87 | county = str_to_title(county) 88 | ) %>% 89 | select(county, state, inmetro, contains("per"), -perchsd) 90 | # Write data 91 | write.csv("midwest.csv") 92 | # Data prep 93 | # Use built in `midwest` data from the `ggplot2` package 94 | library(ggplot2) 95 | library(dplyr) 96 | library(stringr) 97 | # Replace values with full state names 98 | prepped <- midwest %>% 99 | mutate( 100 | state = replace(state, state == "IL", "Illinois"), 101 | state = replace(state, state == "IN", "Indiana"), 102 | state = replace(state, state == "MI", "Michigan"), 103 | state = replace(state, state == "OH", "Ohio"), 104 | state = replace(state, state == "WI", "Wisconsin"), 105 | county = str_to_title(county) 106 | ) %>% 107 | select(county, state, inmetro, contains("per"), -perchsd) 108 | # Write data 109 | write.csv(prepped, "midwest.csv", row.names = F) 110 | -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/data/.Rhistory: -------------------------------------------------------------------------------- 1 | library(dplyr) 2 | library(ggplot2) 3 | View(midwest) 4 | dim(midwest) 5 | ?midwest 6 | levels(midwest$state) 7 | unique(midwest$state) 8 | styler:::style_active_file() 9 | prepped <- midwest %>% 10 | mutate( 11 | state = replace(state, state == "IL", "Illinois"), 12 | state = replace(state, state == "IN", "Indiana"), 13 | state = replace(state, state == "MI", "Michigan"), 14 | state = replace(state, state == "OH", "Ohio"), 15 | state = replace(state, state == "WI", "Wisconsin") 16 | ) 17 | View(prepped) 18 | library(tools) 19 | prepped <- midwest %>% 20 | mutate( 21 | state = replace(state, state == "IL", "Illinois"), 22 | state = replace(state, state == "IN", "Indiana"), 23 | state = replace(state, state == "MI", "Michigan"), 24 | state = replace(state, state == "OH", "Ohio"), 25 | state = replace(state, state == "WI", "Wisconsin"), 26 | county = toTitleCase(county) 27 | ) %>% 28 | select(county, state, %in% "pop") 29 | prepped <- midwest %>% 30 | mutate( 31 | state = replace(state, state == "IL", "Illinois"), 32 | state = replace(state, state == "IN", "Indiana"), 33 | state = replace(state, state == "MI", "Michigan"), 34 | state = replace(state, state == "OH", "Ohio"), 35 | state = replace(state, state == "WI", "Wisconsin"), 36 | county = toTitleCase(county) 37 | ) %>% 38 | select(county, state, inmetro, contains("per")) 39 | View(prepped) 40 | ?perchsd 41 | ?midwest 42 | prepped <- midwest %>% 43 | mutate( 44 | state = replace(state, state == "IL", "Illinois"), 45 | state = replace(state, state == "IN", "Indiana"), 46 | state = replace(state, state == "MI", "Michigan"), 47 | state = replace(state, state == "OH", "Ohio"), 48 | state = replace(state, state == "WI", "Wisconsin"), 49 | county = toTitleCase(county) 50 | ) %>% 51 | select(county, state, inmetro, contains("per"), -perchsd) 52 | midwest %>% 53 | mutate( 54 | state = replace(state, state == "IL", "Illinois"), 55 | state = replace(state, state == "IN", "Indiana"), 56 | state = replace(state, state == "MI", "Michigan"), 57 | state = replace(state, state == "OH", "Ohio"), 58 | state = replace(state, state == "WI", "Wisconsin"), 59 | county = toTitleCase(county) 60 | ) 61 | toTitleCase(midwest$county) 62 | library(stringr) 63 | prepped <- midwest %>% 64 | mutate( 65 | state = replace(state, state == "IL", "Illinois"), 66 | state = replace(state, state == "IN", "Indiana"), 67 | state = replace(state, state == "MI", "Michigan"), 68 | state = replace(state, state == "OH", "Ohio"), 69 | state = replace(state, state == "WI", "Wisconsin"), 70 | county = str_to_title(county) 71 | ) %>% 72 | select(county, state, inmetro, contains("per"), -perchsd) 73 | setwd("~/Documents/react-d3/demo/data") 74 | # Data prep 75 | # Use built in `midwest` data from the `ggplot2` package 76 | library(ggplot2) 77 | library(dplyr) 78 | library(stringr) 79 | # Replace values with full state names 80 | prepped <- midwest %>% 81 | mutate( 82 | state = replace(state, state == "IL", "Illinois"), 83 | state = replace(state, state == "IN", "Indiana"), 84 | state = replace(state, state == "MI", "Michigan"), 85 | state = replace(state, state == "OH", "Ohio"), 86 | state = replace(state, state == "WI", "Wisconsin"), 87 | county = str_to_title(county) 88 | ) %>% 89 | select(county, state, inmetro, contains("per"), -perchsd) 90 | # Write data 91 | write.csv("midwest.csv") 92 | # Data prep 93 | # Use built in `midwest` data from the `ggplot2` package 94 | library(ggplot2) 95 | library(dplyr) 96 | library(stringr) 97 | # Replace values with full state names 98 | prepped <- midwest %>% 99 | mutate( 100 | state = replace(state, state == "IL", "Illinois"), 101 | state = replace(state, state == "IN", "Indiana"), 102 | state = replace(state, state == "MI", "Michigan"), 103 | state = replace(state, state == "OH", "Ohio"), 104 | state = replace(state, state == "WI", "Wisconsin"), 105 | county = str_to_title(county) 106 | ) %>% 107 | select(county, state, inmetro, contains("per"), -perchsd) 108 | # Write data 109 | write.csv(prepped, "midwest.csv", row.names = F) 110 | -------------------------------------------------------------------------------- /09-small-multiples-exercise/js/App.js: -------------------------------------------------------------------------------- 1 | // Application file 2 | class App extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | 6 | // Set initial state 7 | this.state = { 8 | data: [], 9 | xVar: "percollege", 10 | yVar: "percbelowpoverty", 11 | search: '', 12 | radius: 3, 13 | color: '#081AFF' 14 | }; 15 | } 16 | componentDidMount() { 17 | // Load data when the component mounts 18 | d3.csv("data/midwest.csv", (err, data) => { 19 | this.setState({ data: data }); 20 | }); 21 | } 22 | render() { 23 | // Get list of possible x and y variables 24 | let options = this.state.data.length === 0 ? [] : Object.keys(this.state.data[0]); 25 | options = options.filter((d) => d != "county" && d != "state"); 26 | 27 | // Store all of the data to be plotted 28 | let allData = this.state.data.map((d) => { 29 | return { 30 | x: d[this.state.xVar], 31 | y: d[this.state.yVar], 32 | label: d.county + ", " + d.state, 33 | group: d.state, 34 | selected: d.county.toLowerCase().match(this.state.search.toLowerCase()) != null && this.state.search !== '' 35 | }; 36 | }); 37 | 38 | // Nest the data to create different charts by state 39 | 40 | 41 | return ( 42 |
43 |
44 | 45 | {/* X Variable Select Menu */} 46 |
47 | 48 | 53 |
54 | 55 | {/* Y Variable Select Menu */} 56 |
57 | 58 | 63 |
64 | 65 | {/* Radius Slider */} 66 |
67 | 68 | this.setState({ radius: d.target.value })} /> 69 |
70 | 71 | {/* Color Picker */} 72 |
73 | 74 | this.setState({ color: d.target.value })} /> 75 |
76 | 77 | {/* Search Input */} 78 |
79 | this.setState({ search: d.target.value })} /> 80 |
81 |
82 | 83 | {/* Render scatter plots */} 84 | {/* iterate through `nestedData` here */ 85 | 86 | } 87 |
88 | ) 89 | } 90 | } 91 | 92 | // Render application 93 | ReactDOM.render( 94 | , 95 | document.getElementById('root') 96 | ); -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/js/BarChart.js: -------------------------------------------------------------------------------- 1 | // Scatterplot 2 | class BarChart extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | // Graph width and height - accounting for margins 6 | this.drawWidth = this.props.width - this.props.margin.left - this.props.margin.right; 7 | this.drawHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; 8 | 9 | } 10 | componentDidMount() { 11 | this.update(); 12 | } 13 | // Whenever the component updates, select the from the DOM, and use D3 to manipulte circles 14 | componentDidUpdate() { 15 | this.update(); 16 | } 17 | updateScales() { 18 | // Calculate limits 19 | let xMin = d3.min(this.props.data, (d) => +d.value * .9); 20 | let xMax = d3.max(this.props.data, (d) => +d.value * 1.1); 21 | 22 | // Define scales 23 | this.xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.drawWidth]); 24 | this.yScale = d3.scaleBand().rangeRound([0, this.drawHeight]).padding(0.1).domain(this.props.data.map((d) => d.label)); 25 | } 26 | updatePoints() { 27 | // Define hovers 28 | // Add tip 29 | let tip = d3.tip().attr('class', 'd3-tip').html(function (d) { 30 | return d.label; 31 | }); 32 | 33 | // Select all rects and bind data 34 | let rects = d3.select(this.chartArea).selectAll('rect').data(this.props.data); 35 | 36 | // Use the .enter() method to get your entering elements, and assign their positions 37 | rects.enter().append('rect') 38 | .merge(rects) 39 | .attr('fill', (d) => this.props.color) 40 | .attr('label', (d) => d.label) 41 | .style('fill-opacity', 0.3) 42 | .attr('width', (d) => this.xScale(d.value)) 43 | .attr('height', this.yScale.bandwidth()) 44 | .attr('y', (d) => this.yScale(d.label)); 45 | 46 | // Use the .exit() and .remove() methods to remove elements that are no longer in the data 47 | rects.exit().remove(); 48 | 49 | // Add hovers using the d3-tip library 50 | d3.select(this.chartArea).call(tip); 51 | } 52 | updateAxes() { 53 | let xAxisFunction = d3.axisBottom() 54 | .scale(this.xScale) 55 | .ticks(5, 's'); 56 | 57 | let yAxisFunction = d3.axisLeft() 58 | .scale(this.yScale) 59 | .ticks(5, 's'); 60 | 61 | d3.select(this.xAxis) 62 | .call(xAxisFunction); 63 | 64 | d3.select(this.yAxis) 65 | .call(yAxisFunction); 66 | } 67 | update() { 68 | this.updateScales(); 69 | this.updateAxes(); 70 | this.updatePoints(); 71 | } 72 | render() { 73 | return ( 74 |
75 | 76 | {this.props.title} 77 | { this.chartArea = node; }} 78 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`} /> 79 | 80 | {/* Axes */} 81 | { this.xAxis = node; }} 82 | transform={`translate(${this.props.margin.left}, ${this.props.height - this.props.margin.bottom})`}> 83 | { this.yAxis = node; }} 84 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`}> 85 | 86 | {/* Axis labels */} 87 | {this.props.xTitle} 89 | 90 |
91 | 92 | ) 93 | } 94 | } 95 | 96 | BarChart.defaultProps = { 97 | data: [{ x: 10, y: 20 }, { x: 15, y: 35 }], 98 | width: 300, 99 | height: 300, 100 | radius: 5, 101 | color: "blue", 102 | margin: { 103 | left: 120, 104 | right: 10, 105 | top: 20, 106 | bottom: 50 107 | }, 108 | xTitle: "X Title", 109 | yTitle: "Y Title", 110 | }; -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/js/BarChart_solution.js: -------------------------------------------------------------------------------- 1 | // Scatterplot 2 | class BarChart extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | // Graph width and height - accounting for margins 6 | this.drawWidth = this.props.width - this.props.margin.left - this.props.margin.right; 7 | this.drawHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; 8 | 9 | } 10 | componentDidMount() { 11 | this.update(); 12 | } 13 | // Whenever the component updates, select the from the DOM, and use D3 to manipulte circles 14 | componentDidUpdate() { 15 | this.update(); 16 | } 17 | updateScales() { 18 | // Calculate limits 19 | let xMin = d3.min(this.props.data, (d) => +d.value * .9); 20 | let xMax = d3.max(this.props.data, (d) => +d.value * 1.1); 21 | 22 | // Define scales 23 | this.xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.drawWidth]); 24 | this.yScale = d3.scaleBand().rangeRound([0, this.drawHeight]).padding(0.1).domain(this.props.data.map((d) => d.label)); 25 | } 26 | updatePoints() { 27 | // Define hovers 28 | // Add tip 29 | let tip = d3.tip().attr('class', 'd3-tip').html(function (d) { 30 | return d.label; 31 | }); 32 | 33 | // Select all rects and bind data 34 | let rects = d3.select(this.chartArea).selectAll('rect').data(this.props.data); 35 | 36 | // Use the .enter() method to get your entering elements, and assign their positions 37 | rects.enter().append('rect') 38 | .on('mouseover', (d) => { 39 | this.props.update(d.label) 40 | }) 41 | .merge(rects) 42 | .attr('fill', (d) => this.props.color) 43 | .attr('label', (d) => d.label) 44 | .style('fill-opacity', 0.3) 45 | .attr('width', (d) => this.xScale(d.value)) 46 | .attr('height', this.yScale.bandwidth()) 47 | .attr('y', (d) => this.yScale(d.label)); 48 | 49 | // Use the .exit() and .remove() methods to remove elements that are no longer in the data 50 | rects.exit().remove(); 51 | 52 | // Add hovers using the d3-tip library 53 | d3.select(this.chartArea).call(tip); 54 | } 55 | updateAxes() { 56 | let xAxisFunction = d3.axisBottom() 57 | .scale(this.xScale) 58 | .ticks(5, 's'); 59 | 60 | let yAxisFunction = d3.axisLeft() 61 | .scale(this.yScale) 62 | .ticks(5, 's'); 63 | 64 | d3.select(this.xAxis) 65 | .call(xAxisFunction); 66 | 67 | d3.select(this.yAxis) 68 | .call(yAxisFunction); 69 | } 70 | update() { 71 | this.updateScales(); 72 | this.updateAxes(); 73 | this.updatePoints(); 74 | } 75 | render() { 76 | return ( 77 |
78 | 79 | {this.props.title} 80 | { this.chartArea = node; }} 81 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`} /> 82 | 83 | {/* Axes */} 84 | { this.xAxis = node; }} 85 | transform={`translate(${this.props.margin.left}, ${this.props.height - this.props.margin.bottom})`}> 86 | { this.yAxis = node; }} 87 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`}> 88 | 89 | {/* Axis labels */} 90 | {this.props.xTitle} 92 | 93 |
94 | 95 | ) 96 | } 97 | } 98 | 99 | BarChart.defaultProps = { 100 | data: [{ x: 10, y: 20 }, { x: 15, y: 35 }], 101 | width: 300, 102 | height: 300, 103 | radius: 5, 104 | color: "blue", 105 | margin: { 106 | left: 120, 107 | right: 10, 108 | top: 20, 109 | bottom: 50 110 | }, 111 | xTitle: "X Title", 112 | yTitle: "Y Title", 113 | }; -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/js/ScatterPlot.js: -------------------------------------------------------------------------------- 1 | // Scatterplot 2 | class ScatterPlot extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | // Graph width and height - accounting for margins 6 | this.drawWidth = this.props.width - this.props.margin.left - this.props.margin.right; 7 | this.drawHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; 8 | 9 | } 10 | componentDidMount() { 11 | this.update(); 12 | } 13 | // Whenever the component updates, select the from the DOM, and use D3 to manipulte circles 14 | componentDidUpdate() { 15 | this.update(); 16 | } 17 | updateScales() { 18 | // Calculate limits 19 | let xMin = d3.min(this.props.data, (d) => +d.x * .9); 20 | let xMax = d3.max(this.props.data, (d) => +d.x * 1.1); 21 | let yMin = d3.min(this.props.data, (d) => +d.y * .9); 22 | let yMax = d3.max(this.props.data, (d) => +d.y * 1.1); 23 | 24 | // Define scales 25 | this.xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.drawWidth]) 26 | this.yScale = d3.scaleLinear().domain([yMax, yMin]).range([0, this.drawHeight]) 27 | } 28 | updatePoints() { 29 | // Define hovers 30 | // Add tip 31 | let tip = d3.tip().attr('class', 'd3-tip').html(function (d) { 32 | return d.label; 33 | }); 34 | 35 | // Select all circles and bind data 36 | let circles = d3.select(this.chartArea).selectAll('circle').data(this.props.data); 37 | 38 | // Use the .enter() method to get your entering elements, and assign their positions 39 | circles.enter().append('circle') 40 | .merge(circles) 41 | .attr('r', (d) => this.props.radius) 42 | .attr('fill', (d) => this.props.color) 43 | .attr('label', (d) => d.label) 44 | .style('fill-opacity', 0.3) 45 | .transition().duration(500) 46 | .attr('cx', (d) => this.xScale(d.x)) 47 | .attr('cy', (d) => this.yScale(d.y)) 48 | .style('stroke', "black") 49 | .style('stroke-width', (d) => d.selected == true ? "3px" : "0px") 50 | 51 | 52 | // Use the .exit() and .remove() methods to remove elements that are no longer in the data 53 | circles.exit().remove(); 54 | 55 | // Add hovers using the d3-tip library 56 | d3.select(this.chartArea).call(tip); 57 | } 58 | updateAxes() { 59 | let xAxisFunction = d3.axisBottom() 60 | .scale(this.xScale) 61 | .ticks(5, 's'); 62 | 63 | let yAxisFunction = d3.axisLeft() 64 | .scale(this.yScale) 65 | .ticks(5, 's'); 66 | 67 | d3.select(this.xAxis) 68 | .call(xAxisFunction); 69 | 70 | d3.select(this.yAxis) 71 | .call(yAxisFunction); 72 | } 73 | update() { 74 | this.updateScales(); 75 | this.updateAxes(); 76 | this.updatePoints(); 77 | } 78 | render() { 79 | return ( 80 |
81 | 82 | {this.props.title} 83 | { this.chartArea = node; }} 84 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`} /> 85 | 86 | {/* Axes */} 87 | { this.xAxis = node; }} 88 | transform={`translate(${this.props.margin.left}, ${this.props.height - this.props.margin.bottom})`}> 89 | { this.yAxis = node; }} 90 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`}> 91 | 92 | {/* Axis labels */} 93 | {this.props.xTitle} 95 | 96 | {this.props.yTitle} 98 | 99 |
100 | 101 | ) 102 | } 103 | } 104 | 105 | ScatterPlot.defaultProps = { 106 | data: [{ x: 10, y: 20 }, { x: 15, y: 35 }], 107 | width: 300, 108 | height: 300, 109 | radius: 5, 110 | color: "blue", 111 | margin: { 112 | left: 50, 113 | right: 10, 114 | top: 20, 115 | bottom: 50 116 | }, 117 | xTitle: "X Title", 118 | yTitle: "Y Title", 119 | }; -------------------------------------------------------------------------------- /10-lifting-up-state-exercise/js/ScatterPlot_solution.js: -------------------------------------------------------------------------------- 1 | // Scatterplot 2 | class ScatterPlot extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | // Graph width and height - accounting for margins 6 | this.drawWidth = this.props.width - this.props.margin.left - this.props.margin.right; 7 | this.drawHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; 8 | 9 | } 10 | componentDidMount() { 11 | this.update(); 12 | } 13 | // Whenever the component updates, select the from the DOM, and use D3 to manipulte circles 14 | componentDidUpdate() { 15 | this.update(); 16 | } 17 | updateScales() { 18 | // Calculate limits 19 | let xMin = d3.min(this.props.data, (d) => +d.x * .9); 20 | let xMax = d3.max(this.props.data, (d) => +d.x * 1.1); 21 | let yMin = d3.min(this.props.data, (d) => +d.y * .9); 22 | let yMax = d3.max(this.props.data, (d) => +d.y * 1.1); 23 | 24 | // Define scales 25 | this.xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.drawWidth]) 26 | this.yScale = d3.scaleLinear().domain([yMax, yMin]).range([0, this.drawHeight]) 27 | } 28 | updatePoints() { 29 | // Define hovers 30 | // Add tip 31 | let tip = d3.tip().attr('class', 'd3-tip').html(function (d) { 32 | return d.label; 33 | }); 34 | 35 | // Select all circles and bind data 36 | let circles = d3.select(this.chartArea).selectAll('circle').data(this.props.data); 37 | 38 | // Use the .enter() method to get your entering elements, and assign their positions 39 | circles.enter().append('circle') 40 | .merge(circles) 41 | .attr('r', (d) => this.props.radius) 42 | .attr('fill', (d) => this.props.color) 43 | .attr('label', (d) => d.label) 44 | .on('mouseover', (d) => this.props.update(d.id)) 45 | .style('fill-opacity', 0.3) 46 | .transition().duration(500) 47 | .attr('cx', (d) => this.xScale(d.x)) 48 | .attr('cy', (d) => this.yScale(d.y)) 49 | .style('stroke', "black") 50 | .style('stroke-width', (d) => d.selected == true ? "3px" : "0px") 51 | 52 | 53 | // Use the .exit() and .remove() methods to remove elements that are no longer in the data 54 | circles.exit().remove(); 55 | 56 | // Add hovers using the d3-tip library 57 | d3.select(this.chartArea).call(tip); 58 | } 59 | updateAxes() { 60 | let xAxisFunction = d3.axisBottom() 61 | .scale(this.xScale) 62 | .ticks(5, 's'); 63 | 64 | let yAxisFunction = d3.axisLeft() 65 | .scale(this.yScale) 66 | .ticks(5, 's'); 67 | 68 | d3.select(this.xAxis) 69 | .call(xAxisFunction); 70 | 71 | d3.select(this.yAxis) 72 | .call(yAxisFunction); 73 | } 74 | update() { 75 | this.updateScales(); 76 | this.updateAxes(); 77 | this.updatePoints(); 78 | } 79 | render() { 80 | return ( 81 |
82 | 83 | {this.props.title} 84 | { this.chartArea = node; }} 85 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`} /> 86 | 87 | {/* Axes */} 88 | { this.xAxis = node; }} 89 | transform={`translate(${this.props.margin.left}, ${this.props.height - this.props.margin.bottom})`}> 90 | { this.yAxis = node; }} 91 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`}> 92 | 93 | {/* Axis labels */} 94 | {this.props.xTitle} 96 | 97 | {this.props.yTitle} 99 | 100 |
101 | 102 | ) 103 | } 104 | } 105 | 106 | ScatterPlot.defaultProps = { 107 | data: [{ x: 10, y: 20 }, { x: 15, y: 35 }], 108 | width: 300, 109 | height: 300, 110 | radius: 5, 111 | color: "blue", 112 | margin: { 113 | left: 50, 114 | right: 10, 115 | top: 20, 116 | bottom: 50 117 | }, 118 | xTitle: "X Title", 119 | yTitle: "Y Title", 120 | }; -------------------------------------------------------------------------------- /08-scatter-exercise/js/ScatterPlot_solution.js: -------------------------------------------------------------------------------- 1 | // Scatterplot 2 | class ScatterPlot extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | // Graph width and height - accounting for margins 6 | this.drawWidth = this.props.width - this.props.margin.left - this.props.margin.right; 7 | this.drawHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; 8 | 9 | } 10 | componentDidMount() { 11 | this.update(); 12 | } 13 | // Whenever the component updates, select the from the DOM, and use D3 to manipulte circles 14 | componentDidUpdate() { 15 | this.update(); 16 | } 17 | updateScales() { 18 | // Calculate limits 19 | let xMin = d3.min(this.props.data, (d) => +d.x * .9); 20 | let xMax = d3.max(this.props.data, (d) => +d.x * 1.1); 21 | let yMin = d3.min(this.props.data, (d) => +d.y * .9); 22 | let yMax = d3.max(this.props.data, (d) => +d.y * 1.1); 23 | 24 | // Define scales 25 | this.xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.drawWidth]) 26 | this.yScale = d3.scaleLinear().domain([yMax, yMin]).range([0, this.drawHeight]) 27 | } 28 | updatePoints() { 29 | // Define hovers 30 | // Add tip 31 | let tip = d3.tip().attr('class', 'd3-tip').html(function (d) { 32 | return d.label; 33 | }); 34 | 35 | // Select all circles and bind data 36 | let circles = d3.select(this.chartArea).selectAll('circle').data(this.props.data); 37 | 38 | // Use the .enter() method to get your entering elements, and assign their positions 39 | circles.enter().append('circle') 40 | .merge(circles) 41 | .attr('r', (d) => this.props.radius) 42 | .attr('fill', (d) => this.props.color) 43 | .attr('label', (d) => d.label) 44 | .on('mouseover', tip.show) 45 | .on('mouseout', tip.hide) 46 | .style('fill-opacity', 0.3) 47 | .transition().duration(500) 48 | .attr('cx', (d) => this.xScale(d.x)) 49 | .attr('cy', (d) => this.yScale(d.y)) 50 | .style('stroke', "black") 51 | .style('stroke-width', (d) => d.selected == true ? "3px" : "0px") 52 | 53 | 54 | // Use the .exit() and .remove() methods to remove elements that are no longer in the data 55 | circles.exit().remove(); 56 | 57 | // Add hovers using the d3-tip library 58 | d3.select(this.chartArea).call(tip); 59 | } 60 | updateAxes() { 61 | let xAxisFunction = d3.axisBottom() 62 | .scale(this.xScale) 63 | .ticks(5, 's'); 64 | 65 | let yAxisFunction = d3.axisLeft() 66 | .scale(this.yScale) 67 | .ticks(5, 's'); 68 | 69 | d3.select(this.xAxis) 70 | .call(xAxisFunction); 71 | 72 | d3.select(this.yAxis) 73 | .call(yAxisFunction); 74 | } 75 | update() { 76 | this.updateScales(); 77 | this.updateAxes(); 78 | this.updatePoints(); 79 | } 80 | render() { 81 | return ( 82 |
83 | 84 | {this.props.title} 85 | { this.chartArea = node; }} 86 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`} /> 87 | 88 | {/* Axes */} 89 | { this.xAxis = node; }} 90 | transform={`translate(${this.props.margin.left}, ${this.props.height - this.props.margin.bottom})`}> 91 | { this.yAxis = node; }} 92 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`}> 93 | 94 | {/* Axis labels */} 95 | {this.props.xTitle} 97 | 98 | {this.props.yTitle} 100 | 101 |
102 | 103 | ) 104 | } 105 | } 106 | 107 | ScatterPlot.defaultProps = { 108 | data: [{ x: 10, y: 20 }, { x: 15, y: 35 }], 109 | width: 300, 110 | height: 300, 111 | radius: 5, 112 | color: "blue", 113 | margin: { 114 | left: 50, 115 | right: 10, 116 | top: 20, 117 | bottom: 50 118 | }, 119 | xTitle: "X Title", 120 | yTitle: "Y Title", 121 | }; -------------------------------------------------------------------------------- /09-small-multiples-exercise/js/ScatterPlot.js: -------------------------------------------------------------------------------- 1 | // Scatterplot 2 | class ScatterPlot extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | // Graph width and height - accounting for margins 6 | this.drawWidth = this.props.width - this.props.margin.left - this.props.margin.right; 7 | this.drawHeight = this.props.height - this.props.margin.top - this.props.margin.bottom; 8 | 9 | } 10 | componentDidMount() { 11 | this.update(); 12 | } 13 | // Whenever the component updates, select the from the DOM, and use D3 to manipulte circles 14 | componentDidUpdate() { 15 | this.update(); 16 | } 17 | updateScales() { 18 | // Calculate limits 19 | let xMin = d3.min(this.props.data, (d) => +d.x * .9); 20 | let xMax = d3.max(this.props.data, (d) => +d.x * 1.1); 21 | let yMin = d3.min(this.props.data, (d) => +d.y * .9); 22 | let yMax = d3.max(this.props.data, (d) => +d.y * 1.1); 23 | 24 | // Define scales 25 | this.xScale = d3.scaleLinear().domain([xMin, xMax]).range([0, this.drawWidth]) 26 | this.yScale = d3.scaleLinear().domain([yMax, yMin]).range([0, this.drawHeight]) 27 | } 28 | updatePoints() { 29 | // Define hovers 30 | // Add tip 31 | let tip = d3.tip().attr('class', 'd3-tip').html(function (d) { 32 | return d.label; 33 | }); 34 | 35 | // Select all circles and bind data 36 | let circles = d3.select(this.chartArea).selectAll('circle').data(this.props.data); 37 | 38 | // Use the .enter() method to get your entering elements, and assign their positions 39 | circles.enter().append('circle') 40 | .merge(circles) 41 | .attr('r', (d) => this.props.radius) 42 | .attr('fill', (d) => this.props.color) 43 | .attr('label', (d) => d.label) 44 | .on('mouseover', tip.show) 45 | .on('mouseout', tip.hide) 46 | .style('fill-opacity', 0.3) 47 | .transition().duration(500) 48 | .attr('cx', (d) => this.xScale(d.x)) 49 | .attr('cy', (d) => this.yScale(d.y)) 50 | .style('stroke', "black") 51 | .style('stroke-width', (d) => d.selected == true ? "3px" : "0px") 52 | 53 | 54 | // Use the .exit() and .remove() methods to remove elements that are no longer in the data 55 | circles.exit().remove(); 56 | 57 | // Add hovers using the d3-tip library 58 | d3.select(this.chartArea).call(tip); 59 | } 60 | updateAxes() { 61 | let xAxisFunction = d3.axisBottom() 62 | .scale(this.xScale) 63 | .ticks(5, 's'); 64 | 65 | let yAxisFunction = d3.axisLeft() 66 | .scale(this.yScale) 67 | .ticks(5, 's'); 68 | 69 | d3.select(this.xAxis) 70 | .call(xAxisFunction); 71 | 72 | d3.select(this.yAxis) 73 | .call(yAxisFunction); 74 | } 75 | update() { 76 | this.updateScales(); 77 | this.updateAxes(); 78 | this.updatePoints(); 79 | } 80 | render() { 81 | return ( 82 |
83 | 84 | {this.props.title} 85 | { this.chartArea = node; }} 86 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`} /> 87 | 88 | {/* Axes */} 89 | { this.xAxis = node; }} 90 | transform={`translate(${this.props.margin.left}, ${this.props.height - this.props.margin.bottom})`}> 91 | { this.yAxis = node; }} 92 | transform={`translate(${this.props.margin.left}, ${this.props.margin.top})`}> 93 | 94 | {/* Axis labels */} 95 | {this.props.xTitle} 97 | 98 | {this.props.yTitle} 100 | 101 |
102 | 103 | ) 104 | } 105 | } 106 | 107 | ScatterPlot.defaultProps = { 108 | data: [{ x: 10, y: 20 }, { x: 15, y: 35 }], 109 | width: 300, 110 | height: 300, 111 | radius: 5, 112 | color: "blue", 113 | margin: { 114 | left: 50, 115 | right: 10, 116 | top: 20, 117 | bottom: 50 118 | }, 119 | xTitle: "X Title", 120 | yTitle: "Y Title", 121 | }; -------------------------------------------------------------------------------- /09-small-multiples-exercise/js/App_solution.js: -------------------------------------------------------------------------------- 1 | // Application file 2 | class App extends React.Component { 3 | constructor(props) { 4 | super(props); 5 | 6 | // Set initial state 7 | this.state = { 8 | data: [], 9 | xVar: "percollege", 10 | yVar: "percbelowpoverty", 11 | search: '', 12 | radius: 3, 13 | color: '#081AFF' 14 | }; 15 | } 16 | componentDidMount() { 17 | // Load data when the component mounts 18 | d3.csv("data/midwest.csv", (err, data) => { 19 | this.setState({ data: data }); 20 | }); 21 | } 22 | render() { 23 | // Get list of possible x and y variables 24 | let options = this.state.data.length === 0 ? [] : Object.keys(this.state.data[0]); 25 | options = options.filter((d) => d != "county" && d != "state"); 26 | 27 | // Store all of the data to be plotted 28 | let allData = this.state.data.map((d) => { 29 | return { 30 | x: d[this.state.xVar], 31 | y: d[this.state.yVar], 32 | label: d.county + ", " + d.state, 33 | group: d.state, 34 | selected: d.county.toLowerCase().match(this.state.search.toLowerCase()) != null && this.state.search !== '' 35 | }; 36 | }); 37 | 38 | // Nest the data to create different charts by state 39 | let nestedData = d3.nest().key((d) => d.group) 40 | .entries(allData); 41 | 42 | return ( 43 |
44 |
45 |

09-small-multiples-exercise

46 |

Building small multiples with React + D3

47 | (all exercises) 48 |
49 |
50 |
51 | 52 | {/* X Variable Select Menu */} 53 |
54 | 55 | 60 |
61 | 62 | {/* Y Variable Select Menu */} 63 |
64 | 65 | 70 |
71 | 72 | {/* Radius Slider */} 73 |
74 | 75 | this.setState({ radius: d.target.value })} /> 76 |
77 | 78 | {/* Color Picker */} 79 |
80 | 81 | this.setState({ color: d.target.value })} /> 82 |
83 | 84 | {/* Search Input */} 85 |
86 | this.setState({ search: d.target.value })} /> 87 |
88 |
89 | 90 | {/* Render scatter plots */} 91 | { 92 | nestedData.map((group) => { 93 | return 101 | }) 102 | } 103 |
104 |
105 | ) 106 | } 107 | } 108 | 109 | // Render application 110 | ReactDOM.render( 111 | , 112 | document.getElementById('root') 113 | ); -------------------------------------------------------------------------------- /06-state-exercise/js/solution.js: -------------------------------------------------------------------------------- 1 | // Application file 2 | class App extends React.Component { 3 | 4 | constructor(props) { 5 | super(props); 6 | 7 | // Set initial state 8 | this.state = { 9 | data: [], 10 | variable: "percollege", 11 | nShow: 10, 12 | sort:"ascending" 13 | }; 14 | } 15 | 16 | componentDidMount() { 17 | // Load data when the component mounts 18 | d3.csv("data/midwest.csv", (err, data) => { 19 | this.setState({ data: data }); 20 | }); 21 | } 22 | render() { 23 | // Create a variable `options` storing the list of possible variables to show. 24 | // These should be the **object keys** from the first element in your data, excluding "county" and "state". 25 | let options = this.state.data.length === 0 ? [] : Object.keys(this.state.data[0]); 26 | options = options.filter((d) => d != "county" && d != "state"); 27 | 28 | // Create a variable `allData` in which you store the current value of interest 29 | // (based on `this.state.variable`), as well as the state and county 30 | let allData = this.state.data.map((d) => { 31 | return { 32 | value: +d[this.state.variable], 33 | state: d.state, 34 | county: d.county 35 | }; 36 | }); 37 | 38 | // Store the mean of the current value in a variable `mean` 39 | let mean = d3.mean(allData, (d) => d.value) || 0; 40 | 41 | // Store the top N values (based on `this.state.nShow`) from the data in a variable. 42 | // Observatiosn should be sorted by the current sorting variable (`this.state.variable`) 43 | // in either ascending or descending order (`this.state.sort`). 44 | let topData = allData.sort((a, b) => { 45 | return this.state.sort == "ascending" ? a.value - b.value : b.value - a.value; 46 | }).filter((d, i) => i < this.state.nShow) 47 | 48 | 49 | // Return an HTML node to render 50 | return ( 51 |
52 |
53 |
54 | 55 | {/* Create a Select Menu to determine which variable is shown in the table */} 56 |
57 | 58 | 63 |
64 | 65 | {/*Create a Slider to control how many rows are shown in the table */} 66 |
67 | 68 | this.setState({ nShow: d.target.value })} /> 69 |
70 | 71 | {/*Create a Button Group to control if rows in the table are shown in ascending or descending order */} 72 |
73 | {["ascending", "descending"].map((d) =>{ 74 | return 78 | })} 79 |
80 |
81 | 82 | {/* Display the average value (`mean`) in a paragraph element */} 83 |

Average {this.state.variable}: {mean.toFixed(1) + "%"}

84 | 85 | {/* Show a table of the top N counties */} 86 | 87 | 88 | 89 | 90 | 91 | 92 | {topData.map((d, i) => { 93 | return ( 94 | 95 | 96 | 97 | 98 | ) 99 | }) 100 | 101 | } 102 | 103 |
County{this.state.variable}
{d.county + ", " + d.state}{d.value.toFixed(1) + "%"}
104 |
105 |
106 | ) 107 | } 108 | } 109 | 110 | // Render application 111 | ReactDOM.render( 112 | , 113 | document.getElementById('root') 114 | ); -------------------------------------------------------------------------------- /08-scatter-exercise/README.md: -------------------------------------------------------------------------------- 1 | # 08-scatter-exercise 2 | In this exercise, you'll follow the instructions below to build an interactive scatterplot in React: 3 | 4 | ![Complete scatter plot](img/complete.png) 5 | 6 | In doing so, you'll create a React `` component in the `ScatterPlot.js` file. The component that you create will be rendered by your `` component created in the `App.js` file. See the complete code in the `ScatterPlot_solution.js` and `App_solution.js` files. 7 | 8 | ## Instructions 9 | The necessary CSS and HTML code are already written for you in this exercise, as is _some of_ the JavaScript code. The current structure of the application has an `` component rendering a `` component, each defined in their respective files. You'll need to edit _both files_ to complete this exercises. 10 | 11 | ### The `` Component 12 | The `` component, defined in `ScatterPlot.js`, is the component that defines a scatterplot -- it is currently being rendered by the `` component. You'll need to edit the following methods to have the component properly render a ScatterPlot. 13 | 14 | #### `updateScales()` Method 15 | In this method, you'll define the functions for `this.xScale()` and `this.yScale()`, used to draw your scatter plot. To do this, you'll need to: 16 | 17 | - **Calculate limits**: find the minimum/maximum x and y values in the data (i.e., `xMin`, `xMax`, etc). 18 | - **Define scales**: set the values of `this.xScale()` and `this.yScale()` using the `d3.scaleLinear()` method and the limits calculated in the previous step. You'll also want to use `this.drawWidth` and `this.drawHeight` to appropriately set the ranges of your scales. 19 | 20 | #### `updatePoints()` Method 21 | In the `updatePoints()` method of your component, you should update the position of the circles using the D3 data-join process (i.e., _enter_, _exit_, and _update_). To do so, follow these steps: 22 | 23 | - **Bind data**: select the `this.chartArea` element, then select all of the circle elements inside of it and bind your data (`this.props.data`) to the selection. 24 | - **Append and position elements**: using `enter()` and `merge()`, append new elements (circles), and set the visual attributes of all elements (i.e., `cx`, `cy`, `radius`, `fill`, etc.). You can use any transitions you typically would using D3. If you want to use the `d3-tip` library for hovers, use the following code: 25 | 26 | ```javascript 27 | circles.attr('label', (d) => d.label) 28 | .on('mouseover', tip.show) 29 | .on('mouseout', tip.hide) 30 | 31 | // Add hovers using the d3-tip library 32 | d3.select(this.chartArea).call(tip); 33 | ``` 34 | - **Exit and remove elements**: to complete the D3 update pattern, you can `exit()` and `remove()` any elements that are no longer present in the dataset. 35 | 36 | 37 | #### `updateAxes()` Method 38 | In your `updateAxes()` method, you'll select the visual elements (i.e., `` elements) rendered by React, and call your D3 axis functions on those ``s. 39 | 40 | - **Define axis functions**: using `d3.axisBottom()` and `d3.axisLeft()`, create _functions_ that describe how to draw your _axes_ using your _scales_ (`this.xScale()` and `this.yScale()`). 41 | - **Draw axes**: use D3 to _select_ your axes (`` elements rendered by React), and call the axis functions defined above to draw your axes. 42 | 43 | 44 | #### `update()` Method 45 | Because you want to re-render your chart when your component _mounts_, as well as whenever your component _updates_, it makes sense to wrap the above 3 functions in a single `update()` method. 46 | 47 | - In your `update()` method, call your `updateScales()`, `updateAxes()`, and `updatePoints()` methods 48 | 49 | #### `render()` Method 50 | The `render()` method returns the building blocks of your chart. While these are already returned in the proper locations, they **are not** exposed as variables for D3 to manipulate. For each axis `` and your plotting `` do the following: 51 | 52 | - **Expose the DOM node**: in order to have D3 manipulate each DOM element, you'll need to expose it as a variable. To do so, use the following syntax (where `VARNAME` is the name of the element you wish to expose, such as `xAxis`): 53 | 54 | ```javascript 55 | { this.VARNAME = node; }} 56 | ``` 57 | 58 | ### The `` Component 59 | Your `` component (in your `App.js` file) is already fairly structured; however there are a number of pieces that you'll need to complete. 60 | 61 | #### `componentDidMount()` Method 62 | In your `componentDidMount()` method, you'll need to do the following: 63 | 64 | - Load your data from the `data/midwest.csv` file, and, when you have successfully loaded the data, **set your state** of the `data` property to the loaded data. 65 | 66 | #### `render()` Method 67 | In your `render` method, you'll compute data to pass to the components that you render. 68 | 69 | - **Compute `allData`**: Create an _array of objects_ `allData` that stores data for your `` component. Each object should have an `x`, `y`, and `label` property that you create using the current state of your App (i.e., using `this.state.xVar`, etc.). You should do this by iterating through `this.state.data`. 70 | 71 | Then, in your `return` statement, make the following changes: 72 | - **Y Variable Select**: mirroring the X variable select, create a ` 73 | {options.map((d) => { 74 | return 75 | })} 76 | 77 | 78 | 79 | {/* Y Variable Select Menu */} 80 |
81 | 82 | 87 |
88 | 89 | {/* Radius Slider */} 90 |
91 | 92 | this.setState({ radius: d.target.value })} /> 93 |
94 | 95 | {/* Color Picker */} 96 |
97 | 98 | this.setState({ color: d.target.value })} /> 99 |
100 | 101 | {/* Search Input */} 102 |
103 | this.setState({ search: d.target.value })} /> 104 |
105 | 106 | 107 | {/* Render scatter plots */} 108 | 116 | 117 | 118 | {/* Render bar chart */} 119 | 127 | 128 | 129 | ) 130 | } 131 | } 132 | 133 | // Render application 134 | ReactDOM.render( 135 | , 136 | document.getElementById('root') 137 | ); -------------------------------------------------------------------------------- /06-state-exercise/README.md: -------------------------------------------------------------------------------- 1 | # 06-state-exercise 2 | In this exercise, you'll follow the instructions below to build a data exploration tool in React: 3 | 4 | ![Complete todo list](img/complete.png) 5 | 6 | In doing so, you'll **manage the state** of a React component that you create. See the solution in the `solution.js` file. 7 | 8 | ## Instructions 9 | The necessary CSS and HTML code are already written for you in this exercise, as is _some of_ the JavaScript code. In your `App.js` file, complete the following steps: 10 | 11 | ### `componentDidMount()` Method 12 | When the component mounts, use the `d3.csv()` method to load the `.csv` file stored in `"data/midwest.csv"`. In the callback function for the method (i.e., once you load the data), **update the state** such that the `data` property (i.e., `this.state.data`) is equal to the data loaded by `d3.csv()`. Make sure to use the `this.setState()` method (don't try to set the value of the state directly). This will trigger a re-rendering of your `App` component. To see if it worked, you can add a `console.log()` statement to the `render()` method to see the state of your component. 13 | 14 | 15 | ### `render()` Method 16 | In the `render()` method, you will first compute some values of interest that you want to display, then you will _return_ an HTML element that contains the information you wish to render on the DOM. 17 | 18 | #### Computing values in your `render()` method 19 | It's fairly common to use the state of your component to determine what you wish to render. One challenge to note is that, when the component first renders (but _before_ the data is loaded), your data will be an empty array. This makes it difficult to compute values of interest. Luckily, it's easy to use a **ternary operator** to conditionally capture information of interest: 20 | 21 | ```javascript 22 | // Use a ternary operator to capture information of interest 23 | // If the data hasn't loaded, return an empty array. 24 | // Otherwise, get the keys of the first data element 25 | let options = this.state.data.length === 0 ? [] : Object.keys(this.state.data[0]); 26 | ``` 27 | 28 | You'll need to store the following information in variables (you can obviously call them whatever you want, but these are the variable names in the `solution.js` file): 29 | 30 | - `options`: Create a variable `options` storing the list of possible variables to display in the table (these are the columns in your `.csv` file). These should be the **object keys** from the first element in your data (see above), excluding "county" and "state". 31 | - `allData`: Create a variable `allData` in which you store the current values of interest (based on `this.state.variable`), as well as the state and county. In other words, this should be an **array of objects** with three keys: `value` (the _value_ of whatever is stored in the `this.state.variable` key), `state`, and `county`. 32 | - `mean`: Store the _mean_ of the `value` key in your `allData` array in a variable `mean`. I suggest using `d3.mean()` to do this. 33 | - `topData`: Store the top N values (based on `this.state.nShow`) from the `allData` array in a variable. Observations should be sorted by the current variable being displayed (`this.state.variable`) in either ascending or descending order (based on `this.state.sort`). You can do this using the array methods `.sort()` and `.filter()` (in that order). 34 | 35 | #### Returning elements in your `render()` method 36 | There is already a DOM element returned by the `render()` method. Inside that node, you'll create the following elements. Note, because the `ReactDOM.render()` method is being called, you should be able to see your changes appear as you make them. 37 | 38 | ___Controls___ 39 | - **Select Menu**: Create a Select Menu (i.e., `` element. Assign an `onChange()` method to the ``) to control how many rows are shown in the table. The _value_ of the slider should be `this.state.nShow`. When the slider updates (i.e., `onChange()`), you should use the `this.setState()` method to change the state of the `nShow` value. This will change the number of values stored in `topData`, updating the table you will render (described below). 41 | - **Button Group**: Create a Button Group (i.e., a `
` with two `