Building CampaignHawk: Popouts and Radio Buttons (Part 13)

At this point, we have tooltips pop out when you hover over an icon in the navbar and all of our icons trigger a modal. What we want to be able to do is click on the icon for “Data Layers” and trigger a popout that gives us a few options. When one of these options is clicked, it will trigger a function that creates and renders a data layer on top of our map. It should look something like the popout from the wireframes, shown below.

I suppose a good place to start would be to disable modals on the buttons that shouldn’t trigger a modal. Since we don’t have many icons that trigger a modal, we can do this the lazy way and hard-code them.

Within our SidenavIcons component, let’s make a function that sets state based on a switch statement that only returns a modal if we want the icon to launch a modal. I would prefer to keep this inline with the other events, so let’s contain it in a self-invoking anonymous function.

onClick={(() => {
switch (item.description) {
case "Add Volunteer": return this.props.showModal.bind(null, item.description);
case "View Volunteers": return this.props.showModal.bind(null, item.description);
case "View as List": return this.props.showModal.bind(null, item.description);
case "Leaderboard": return this.props.showModal.bind(null, item.description);
default: return null;
}
})()}

The next thing we should do is make our popout. I’m going to make this component in a separate file called Popout.jsx and create a component called SidenavPopout. While we’re at it, I’m going to move Sidenav and SidenavTooltip into their own files. Since these objects are all global, it doesn’t matter where they live.

We want that popout to render within our Sidenav component, just above our SidenavTooltip component. As it turns out, a popout is basically the same as a tooltip in this instance. All we have to do is change one line in our style to make it work for our popouts:

min-height: 3rem;

Then we can add popout-container to the same style grouping:

.sidenav-tooltip, .popout-container {

Now we need to connect it. We’re basically just going to copy the same triggers from the tooltip. There’s a lot of code, so I’m just going to leave this link here if you want to dig into it, but its basically the same thing we did for the tooltip.

The biggest thing we need to change is the popoutY, which can no longer be set to the center of the icon like the tooltip. We need to center it with the tail, so we will change the “divide by 2” to “add 10”:

popoutY: e.nativeEvent.target.offsetTop + (e.nativeEvent.target.offsetHeight + 10) + "px"

Another thing we need to do is set showTooltip to false within our showPopout function so the tooltip disappears.

this.setState({
showTooltip: false,

This doesn’t entirely solve the problem of the tooltip because it still shows up when we hover over it again, but it will do for now. We will eventually want to implement something like the code below to make sure the event is not triggered later on:

e.target.removeEventListener('mouseover', e, false);

So now we want to trigger the popout only on click of particular icons. We can handle that in our switch statement in SidenavIcons by adding another case:

case "Data Layers": return this.props.showPopout.bind(null, item.description);

With some placeholder text, our popout now looks like the image below.

That’s pretty close. First we should add the content, which in this case will be three inputs with type radio.

We have a couple options for aligning these radio buttons. We can use flexbox or we can use an unordered list. They both accomplish the same thing, but an unordered list is more semantic.

Let’s change the content of our popout by making a new component called DataLayerPopoutContent with an unordered list of three radio buttons with labels. Every new popout for our sidenav will need its own component at some point, but for now, we only need the one.

DataLayerPopoutContent = React.createClass({
render() {
return (
<div className='popout-content'>
<ul>
<li>
<input type='radio'
name='data-layer-group'
id='no-data'
defaultChecked="checked" />
<label htmlFor='no-data'>No Data</label>
</li>
<li>
<input type='radio'
name='data-layer-group'
id='voter-data2' />
<label htmlFor='voter-data2'>Voter Data 2</label>
</li>
<li>
<input type='radio'
name='data-layer-group'
id='voter-data3' />
<label htmlFor='voter-data3'>Voter Data 3</label>
</li>
</ul>
</div>
)
}
})

A few things worth noting. We’re setting the default state of the first radio button, No Data, to checked. React does not like the normal way of writing it, so you need to use defaultChecked.

Labels are also associated with an input based on the id of the input and the for field of the label. But React doesn’t like for, so you have to use htmlFor in order for the label to work appropriately.

At this point, our radio buttons look like hell because of the styling we put on all input boxes. We will fix that in the next article because there’s a lot more that goes into styling radio buttons than you might think.

Next Steps

Fix styling of radio buttons and set radio buttons to trigger data layers. That might also be a good time to start playing around with different data and maybe start styling our maps based on data inputs.