viewof threshold = Range([d3.min(thresholds), d3.max(thresholds)], {
step: 1,
value: 15,
format: (x) => "$" + x,
label: "Thresholds"
})
data = transpose(data_ojs)
thresholds = [...new Set(data.map((item) => item.low_wage_threshold))]
filtered = data.filter(d => {
return d.low_wage_threshold == threshold
})
// Update table data when filtered data changes
Reactable.setData('tbl', filtered)
of workers paid less than $ per hour
Plot.plot({
margin: 0,
width: 790,
height: 540,
x: {
axis: "top",
label: null,
ticks: null
},
y: {
label: null,
ticks: null
},
color: {
type: "threshold",
//scheme: "oranges",
range: ["#d9d9d9", "#fdd0a2", "#fdae6b", "#fd8d3c", "#e6550d", "#d94801"],
domain: [0, 0.15, 0.3, 0.45, 0.6]
},
marks: [
Plot.cell(filtered, {
x: "col",
y: "row",
fill: (d) => d.value,
rx: 10 // uncomment for circles
}),
Plot.text(filtered, {
x: "col",
y: "row",
fontWeight: "bold",
text: outcome == "share" ? (d) => `${d.state_abb}\n${d.share_map}` : (d) => `${d.state_abb}\n${d.count_map}`
})
]
})
Low-Wage Workforce Tracker, Economic Policy Institute, April 2023, https://economic.github.io/low_wage_workforce.
Notes: Analysis by Ben Zipperer of the Economic Policy Institute Current Population Survey extracts, April 2022 through March 2023. Wages include overtime, tips, and commissions. Data is not shown for states where the threshold less than $1 above the state’s minimum wage. Download the data shown in the figure above or the code that produces it.
Workers paid less than $ per hour, by state
function Range(range, options = {}) {
const [min, max] = range;
const {
className = "Range",
vertical = false,
label = null,
format = (x) => +x,
step = 1,
value = (min + max) / 2,
style = "",
labelStyle = "",
rangeStyle = "",
valueStyle = ""
} = options;
const rangeWrap = htl.html`<div class=${className} style="${style}"></div>`;
Object.assign(rangeWrap.style, {
display: "inline-flex",
position: "relative",
userSelect: "none"
});
const valueDisplay = htl.html`<output style="${valueStyle}">`;
Object.assign(valueDisplay.style, {
display: "inline-block"
});
const rangeInput = htl.html`<input type=range min=${min} max=${max} step=${step} value=${value} style=${rangeStyle}>`;
Object.assign(rangeInput.style, {
display: "inline-block"
});
if (vertical) {
rangeInput.setAttribute("orient", "vertical");
rangeInput.style.writingMode = "bt-lr"; /* IE */
rangeInput.style["-webkit-appearance"] = "slider-vertical"; /* WebKit */
rangeInput.style.width = "8px";
}
rangeWrap.append(rangeInput, valueDisplay);
if (label) rangeWrap.prepend(htl.html`<label style=${labelStyle}>${label}`);
rangeInput.oninput = () => {
valueDisplay.innerHTML = format(rangeInput.valueAsNumber);
rangeWrap.value = rangeWrap.valueAsNumber = +rangeInput.valueAsNumber;
rangeWrap.dispatchEvent(new CustomEvent("input"));
};
rangeInput.oninput();
return rangeWrap;
}
rangeStyles = htl.html`<style>
.Range, .Popup {
display: inline-flex;
align-items:center;
}
.Range input[type=range] {
width:100px;
}
.Range input[type=range][orient=vertical] {
width:8px;
height:100px;
}
.Range label {
margin-right: 5px;
}
.Range output {
margin-left: 5px;
}
.Popup button{
margin-right:10px;
}
</style>
`