Published
Edited
Nov 9, 2020
1 fork
4 stars
Insert cell
Insert cell
// Track the section
mutable currentSection = 0
Insert cell
chart = {
const container = d3.create("div").attr("class", "scroll-container");

const narrativeContainer = container
.append("div")
.attr("class", "narrative-container")
.on("scroll", () => {
// Compute section
const totalHeight = narrativeContainer.node().getBoundingClientRect().top;
const offset = narrative.node().getBoundingClientRect().y;
const section = Math.floor((totalHeight - offset + 10) / sectionHeight);
update(section);
});

const narrative = narrativeContainer.append("div").attr("class", "narrative");

// Add text
narrative
.selectAll(".section")
.data(descriptions)
.join("div")
.attr("class", "section")
.append("text")
.text(d => d);

// Visualization container
const vis = container.append("div").attr("class", "vis-container");

// On building the chart, create the svg and axis elements
const svg = vis
.append("svg")
.attr("width", chartWidth)
.attr("height", height);

// Axes
const xAxis = svg
.append("g")
.attr("class", "xaxis")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));

const yAxis = svg
.append("g")
.attr("class", "yaxis")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y));

const title = svg
.append("text")
.attr("class", "chart_title")
.attr("transform", `translate(${width / 2},15)`)
.text("")
.style("text-anchor", "middle");

// G for annotations
const annotationG = svg.append("g").attr("class", "annotation-group");
// Update function
const update = (section, initialBuild = false) => {
if (section === mutable currentSection && initialBuild === false) return;
mutable currentSection = section;
let color = '';
let input_state = "";
switch (section) {
case 0:
input_state = "Iowa";
color = "gray";
break;
case 1:
input_state = "Mississippi";
color = "pink";
break;
case 2:
input_state = "Maine";
color = "green";
break;
case 3:
input_state = "California";
color = "black";
break;
default:
input_state = "Utah";
color = "blue";
break;
}
// vis.style("background-color", color);

const data = all_data.filter(d => d.name == input_state);

// Update the scales to the new data
x.domain([0, d3.max(data, d => d.pct)]);

// Update the axes
svg
.select(".xaxis")
.transition()
.duration(500)
.call(d3.axisBottom(x).tickFormat(d3.format(".0%")));

svg
.select(".yaxis")
.transition()
.duration(500)
.call(d3.axisLeft(y).tickFormat(customTickFormatter));

// Join the data to a selection of rectangles and transition their attributes
const rects = svg.selectAll("rect").data(data, d => d.group);

// Uncomment to add points
// const points = svg.selectAll("circle").data(data, d => d.group);
// points
// .join("circle")
// .attr("cx", d => x(d.pct))
// .attr("cy", d => y(d.group))
// .attr("r", 5);
// Enter // update // exit as necessary
// See: https://observablehq.com/@d3/selection-join
rects.join(
enter =>
enter
.append("rect")
.attr("x", x(0))
.attr("y", d => y(d.group))
.attr("height", d => y.bandwidth())
.attr("width", d => {
return x(d.pct) - x(0);
}),
update =>
update
.transition()
.duration(500)
.attr("width", d => x(d.pct) - x(0))
);

// Set the title
title.text(
`Percentage of people in ${input_state} living in each income bracket`
);

const annotations = [
{
note: {
bgPadding: 20,

title: "",
label: `Percentage of people living below 10K is ${d3.format(".0%")(
data[0].pct
)}`
},
x: x(data[0].pct),
y: y(data[0].group) + y.bandwidth() / 2,
color: "red",
className: "show-bg",
dy: 0,
dx: 100,
subject: { radius: 50, radiusPadding: 10 }
}
];

// const makeAnnotations = d3_annotation.annotation(annotations);
const makeAnnotations = d3_annotation
.annotation()
.editMode(true)
.type(d3_annotation.annotationLabel)
.annotations(annotations);

annotationG
.raise()

.call(makeAnnotations)
.attr("class", "annotation-group")

.style("font-size", "10px");
};
update(0, true);

return container.node();
}
Insert cell
settings = md`## Settings`
Insert cell
// Descriptions for each section
descriptions = d3.range(0, 5).map(d => "Section " + d)
Insert cell
fullHeight = 400
Insert cell
sectionHeight = 200
Insert cell
narrativeWidth = 200
Insert cell
chartWidth = 900 - narrativeWidth
Insert cell
Insert cell
my_x = d3.scaleLinear().range([margin.left, chartWidth - margin.right])
Insert cell
import { drawChart, chart_data } from "@info474/d3-small-multiples"
Insert cell
d3_annotation = require("https://cdnjs.cloudflare.com/ajax/libs/d3-annotation/2.3.0/d3-annotation.min.js")
Insert cell
import { x, y, margin, height, width, all_data, customTickFormatter } with {
fullHeight as height,
chartWidth as width,
my_x as x
} from "@info474/d3-chart-updates-in-observable"
Insert cell
html`<style>

.scroll-container {
width:${width}px;
height:${fullHeight}px;
}

.narrative-container {
height:${fullHeight}px;
overflow-y:scroll;
float:left;
}

.narrative {
width:${narrativeWidth}px;
}

.vis-container {
height:${fullHeight}px;
width:${width - narrativeWidth - 20}px;
float:right;
}

.section {
height: ${sectionHeight}px;
}
.section:last-of-type {
height:${fullHeight}px;
}
</style>`
Insert cell
d3 = require("d3@5")
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