Published
Edited
May 19, 2022
1 star
Insert cell
Insert cell
tableEmbedAllRaces = {
return html`
${styles}
<div class="graphic-container">
<h4>How competitive are the new districts? (All races)</h4>
<div class="ledein">A few recent statewide elections would have shaken out differently if votes had been counted in Montana's new congressional districts. A look at which recent candidates would and wouldn't have won in the newly drawn west and east.</div>
<div class="note">Source: MTFP analysis of <a href="https://leg.mt.gov/content/Districting/2020/Maps/Congressional/Tentative-Congressional-Plan-MT.pdf">the newly adopted</a> using <a href="https://sosmt.gov/elections/results/">data from the Montana Secretary of State.<a/></div>

${table(nested)}
</div>
`
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
table = (data) => {

const renderCandidate = (candidate, key, winnerKey) => {
const winnerClass = candidate[winnerKey] ? 'winner' : 'loser'
const partyClass = {'DEM': 'd', 'REP': 'r'}[candidate.PartyCode]

const formattedPercent = percentFormat(candidate[key])
const party = candidate.PartyCode.slice(0,1)[0]
const lastName = displayNames[candidate.NameOnBallot].split(' ').slice(-1,)[0]
const fullName = displayNames[candidate.NameOnBallot]
return `<div class="row-item ${winnerClass} ${partyClass}"><span class="percent">${formattedPercent}</span> ${lastName}</div>`
}
const racesRendered = data.map(race => {
const c1 = race.candidates[0]
const c2 = race.candidates[1]
return `<div class="row">

<div class="row-header-1">${race.year} ${displayRaces[race.position]}</div>
<div class="row-header-2">${displayNames[c1.NameOnBallot]} (${c1.PartyCode.slice(0,1)[0]}) v. ${displayNames[c2.NameOnBallot]} (${c2.PartyCode.slice(0,1)[0]})</div>

<div class="row-content">
<div class="row-col header">Statewide</div>
<div class="row-col header">CP-12 West</div>
<div class="row-col header">CP-12 East</div>
</div>
<div class="row-content">
<div class="row-col statewide">${race.candidates.map(d => renderCandidate(d, 'votes_percent', 'wasStatewideWinner')).join('')}</div>
<div class="row-col west">${race.candidates.map(d => renderCandidate(d, 'west_percent', 'wasWesternWinner')).join('')}</div>
<div class="row-col east">${race.candidates.map(d => renderCandidate(d, 'east_percent', 'wasEasternWinner')).join('')}</div>
</div>

</div>`
}).join('')
return `

<div class="map-headers"></div>
<div class="races">
${racesRendered}
</div>
`
}
Insert cell
styles = `<style>
.graphic-container {
font-family: Arial, sans-serif;
font-size: 14px;
max-width: 800px;
}
h4 {
font-size: 1.5em;
}
.ledein {
font-size: 1.1em;
line-height: 1.2em;
}
.note {
font-size: 0.8em;
font-style: italic;
}
.races {
display: flex;
flex-wrap: wrap;
}
.row {
flex: 1 1 350px;
margin: 5px;
margin-bottom: 10px;
padding: 5px;
border: 1px solid #ddd;
}
.row-header-1 {
font-weight: bold;
font-color: #444;
line-height: 0.9em;
}
.row-header-2 {
color: #666;
font-size: 0.9em;
margin-bottom: 0.2em;
}
.row-content {
display: flex;
flex-wrap: wrap;
}
.row-col {
flex: 0 1 30%;
margin-right: 0.2em;
}
.row-col.header {
font-style: italic;
font-size: 0.9em;
padding-left: 0.5em;
}
.row-col.statewide {
border: 2px solid #222;
margin-right: 1em;
}
.row-item {
padding: 0.3em 0.5em;
font-size: 12px;
}
.row-item.r {
background-color: #EAE3DA;
}
.row-item.r.winner {
background-color: #d73027;
color: white
}
.row-item.d {
background-color: #EAE3DA;
}
.row-item.d.winner {
background-color: #4575b4;
color: white
}
.percent {
font-weight: bold;
font-size: 15px;
}
</style>`
Insert cell
percentFormat = d3.format('.0%')
Insert cell
d3 = require('d3@6')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more