...

/

Add user controls for data slicing and dicing

Add user controls for data slicing and dicing

Now comes the fun part. All that extra effort we put into making our components aware of filtering, and it all comes down to this: User controls.

Here’s what we’re building:

widget

It’s a set of filters for users to slice and dice our visualization. The shortened dataset gives you 2 years, 12 job titles, and 50 US states. You’ll get 5 years and many more job titles with the full dataset.

We’re using the architecture we discussed earlier to make it work. Clicking buttons updates a filter function and communicates it all the way up to the App component. App then uses it to update this.state.filteredSalaries, which triggers a re-render and updates our dataviz.

widget

We’re building in 4 steps, top to bottom:

  1. Update App.js with filtering and a <Controls> render
  2. Build a Controls component, which builds the filter based on inputs
  3. Build a ControlRow component, which handles a row of buttons
  4. Build a Toggle component, which is a button

We’ll go through the files linearly. That makes them easier for me to explain and easier for you to understand, but that also means there’s going to be a long period where all you’re seeing is an error like this:

widget

If you want to see what’s up during this process, just remove an import or two and maybe a thing from render. For instance, it’s complaining about ControlRow in this screenshot. Remove the ControlRow import on top and delete <ControlRow ... /> from render. The error goes away, and you see what you’re doing.

Step 1: Update App.js

All right, you know the drill. Add imports, tweak some things, add to render. We have to import Controls, set up filtering, update the map’s zoom prop, and render a white rectangle and Controls.

The white rectangle makes it so the zoomed-in map doesn’t cover up the histogram. I’ll explain when we get there.

Press + to interact
// src/App.js
import MedianLine from './components/MedianLine';
// markua-start-insert
import Controls from './components/Controls';
// markua-end-insert
class App extends Component {
state = {
// ...
medianIncomes: [],
// markua-start-insert
salariesFilter: () => true,
// markua-end-insert
filteredBy: {
// ...
}
}
// ...
// markua-start-insert
updateDataFilter(filter, filteredBy) {
this.setState({
salariesFilter: filter,
filteredBy: filteredBy
});
}
// markua-end-insert
render() {
// ...
}
}

We import the Controls component and add a default salariesFilter function to this.state. The updateDataFilter method passes the filter function and filteredBy dictionary from arguments to App state. We’ll use it as a callback in Controls.

The rest of filtering setup happens in the render method.

Press + to interact
// src/App.js
class App extends Component {
// ...
render() {
// ...
// markua-start-delete
const filteredSalaries = this.state.techSalaries
// markua-end-delete
// markua-start-insert
const filteredSalaries = this.state.techSalaries
.filter(this.state.salariesFilter)
// markua-end-insert
// ...
let zoom = null,
medianHousehold = // ...
// markua-start-insert
if (this.state.filteredBy.USstate !== '*') {
zoom = this.state.filteredBy.USstate;
medianHousehold = d3.mean(this.state.medianIncomesByUSState[zoom],
d => d.medianIncome);
}
// markua-end-insert
// ...
}
}

We add a .filter call to filteredSalaries, which uses our salariesFilter method to throw out anything that doesn’t fit. Then we set up zoom if a US state was selected.

We built the CountyMap component to focus on a given US state. Finding the centroid of a polygon, re-centering the map, and increasing the sizing factor. It creates a nice zoom effect.

widget

And here’s the downside of this approach. SVG doesn’t know about element boundaries. It just renders stuff.

widget

See, it goes under the histogram. Let’s fix that and add the Controls render while we’re at it.

Press + to interact
// src/App.js
class App extends Component {
// ...
render() {
// ...
return (
<div //...>
<svg //...>
<CountyMap //... />
// markua-start-insert
<rect x="500" y="0"
width="600"
height="500"
style={{fill: 'white'}} />
// markua-end-insert
<Histogram //... />
<MedianLine //.. />
</svg>
// markua-start-insert
<Controls data={this.state.techSalaries}
updateDataFilter={this.updateDataFilter.bind(this)} />
// markua-end-insert
</div>
)
}
}
...
Access this course and 1400+ top-rated courses and projects.