diff --git a/public-dashboard-client/src/page-prison/PagePrison.js b/public-dashboard-client/src/page-prison/PagePrison.js
index 2d096336..d8539554 100644
--- a/public-dashboard-client/src/page-prison/PagePrison.js
+++ b/public-dashboard-client/src/page-prison/PagePrison.js
@@ -101,7 +101,12 @@ export default function PagePrison() {
// doing this inside the render loop rather than in an effect
// to prevent an intermediate state from flashing on the chart;
// the current value check avoids an infinite render loop
- if (!singleCohortSelected && recidivismDimension !== DIMENSION_KEYS.total) {
+ if (
+ !singleCohortSelected &&
+ // we don't need to reset the dimension if no cohorts are selected
+ selectedCohorts.length > 1 &&
+ recidivismDimension !== DIMENSION_KEYS.total
+ ) {
setRecidivismDimension(DIMENSION_KEYS.total);
}
diff --git a/public-dashboard-client/src/tooltip/Tooltip.js b/public-dashboard-client/src/tooltip/Tooltip.js
index f8556851..28d4c353 100644
--- a/public-dashboard-client/src/tooltip/Tooltip.js
+++ b/public-dashboard-client/src/tooltip/Tooltip.js
@@ -1,3 +1,20 @@
+// Recidiviz - a data platform for criminal justice reform
+// Copyright (C) 2020 Recidiviz, Inc.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+// =============================================================================
+
import PropTypes from "prop-types";
import React from "react";
import styled from "styled-components";
@@ -23,6 +40,17 @@ const TooltipWrapper = styled.div`
}
`;
+const LabelColorSwatch = styled.div`
+ background-color: ${(props) => props.color};
+ /* border: 1px solid ${(props) => props.theme.colors.bodyLight}; */
+ border-radius: 0.5em;
+ display: inline-block;
+ height: 0.8em;
+ margin-right: 0.5em;
+ vertical-align: baseline;
+ width: 0.8em;
+`;
+
const TooltipTitle = styled.div`
color: ${(props) => props.theme.colors.infoPanelTitle};
@@ -53,7 +81,10 @@ const TooltipRecord = styled.div`
const TooltipLabel = styled.div`
.InfoPanel & {
font-size: 16px;
- opacity: 0.6;
+
+ .TooltipLabel__text {
+ opacity: 0.6;
+ }
}
`;
const TooltipValue = styled.div`
@@ -91,9 +122,14 @@ export const Tooltip = ({ title, records }) => {
{title}
- {records.map(({ label, value, pct }, i) => (
+ {records.map(({ color, label, value, pct }, i) => (
- {label && {label}}
+ {label && (
+
+ {color && }
+ {label}
+
+ )}
{typeof value === "number" ? formatAsNumber(value) : value}
@@ -110,6 +146,7 @@ export const Tooltip = ({ title, records }) => {
Tooltip.propTypes = {
records: PropTypes.arrayOf(
PropTypes.shape({
+ color: PropTypes.string,
label: PropTypes.string,
value: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
.isRequired,
diff --git a/public-dashboard-client/src/viz-recidivism-rates/RecidivismRatesChart.js b/public-dashboard-client/src/viz-recidivism-rates/RecidivismRatesChart.js
index 06e8ccaa..33eb2d31 100644
--- a/public-dashboard-client/src/viz-recidivism-rates/RecidivismRatesChart.js
+++ b/public-dashboard-client/src/viz-recidivism-rates/RecidivismRatesChart.js
@@ -105,7 +105,7 @@ export default function RecidivismRatesChart({ data, highlightedCohort }) {
lines={data}
margin={MARGIN}
otherChartProps={{
- xExtent: [1, 10],
+ xExtent: [0, 10],
}}
size={[width, 475]}
tooltipControllerProps={{
@@ -113,11 +113,12 @@ export default function RecidivismRatesChart({ data, highlightedCohort }) {
const currentPeriod = d.followupYears;
return {
title: `${currentPeriod} year${
- currentPeriod > 1 ? "s" : ""
+ currentPeriod === 1 ? "" : "s"
} since release`,
records: d.points
.filter((p) => p.data.followupYears === currentPeriod)
.map((p) => ({
+ color: p.parentLine.color,
label: p.parentLine.label,
pct: p.data.recidivismRate,
value: `${p.data.recidivated_releases} of ${p.data.releases}`,
diff --git a/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.js b/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.js
index e79dbce2..ba4dd3d0 100644
--- a/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.js
+++ b/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.js
@@ -31,16 +31,36 @@ import {
} from "../utils";
import RecidivismRatesChart from "./RecidivismRatesChart";
+/**
+ * adds an initial record to each series for year zero with rate zero.
+ * Helps to make the initial point in the line chart more visible,
+ * especially for lines that would otherwise only have one point.
+ */
+function prependZero(records) {
+ const zeroRecord = {
+ ...records[0],
+ followupYears: 0,
+ recidivated_releases: "0",
+ recidivismRate: 0,
+ };
+ return [zeroRecord, ...records];
+}
+
/**
* When multiple cohorts are selected, or a single cohort and the `total` dimension,
* will return one data series per cohort.
* Otherwise (i.e., a single cohort and some dimensional breakdown is selected),
* will return one data series per demographic subgroup.
*/
-function prepareChartData({ data, dimension, selectedCohorts }) {
+function prepareChartData({
+ data,
+ dimension,
+ highlightedCohort,
+ selectedCohorts,
+}) {
const showDemographics =
selectedCohorts &&
- selectedCohorts.length === 1 &&
+ selectedCohorts.length <= 1 &&
dimension !== DIMENSION_KEYS.total;
const filteredData = data.filter(recordIsTotalByDimension(dimension));
@@ -54,11 +74,11 @@ function prepareChartData({ data, dimension, selectedCohorts }) {
),
(d) => d[DIMENSION_DATA_KEYS[dimension]]
),
- ([key, value]) => {
+ ([key, records]) => {
return {
key,
label: DIMENSION_MAPPINGS.get(dimension).get(key),
- coordinates: value,
+ coordinates: prependZero(records),
};
}
)
@@ -68,11 +88,11 @@ function prepareChartData({ data, dimension, selectedCohorts }) {
return (
Array.from(
group(filteredData, (d) => d.releaseCohort),
- ([key, value]) => {
+ ([key, records]) => {
return {
key,
label: key,
- coordinates: value,
+ coordinates: prependZero(records),
};
}
)
@@ -83,6 +103,10 @@ function prepareChartData({ data, dimension, selectedCohorts }) {
if (!selectedCohorts) {
return true;
}
+ // highlighted cohort is included even if it's not selected
+ if (highlightedCohort && highlightedCohort.label === record.label) {
+ return true;
+ }
return selectedCohorts.some(({ id }) => id === record.label);
})
);
@@ -94,6 +118,7 @@ export default function VizRecidivismRates({
const chartData = prepareChartData({
data: recidivismRates,
dimension,
+ highlightedCohort,
selectedCohorts,
});
diff --git a/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.test.js b/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.test.js
index 986f2d5c..ef65efaa 100644
--- a/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.test.js
+++ b/public-dashboard-client/src/viz-recidivism-rates/VizRecidivismRates.test.js
@@ -83,6 +83,22 @@ test("renders one line per cohort", () => {
expect(getMainByLabelText("7 lines in a line chart")).toBeVisible();
});
+test("renders the highlighted cohort even if it's not selected", () => {
+ const dimension = DIMENSION_KEYS.total;
+
+ render(
+
+ );
+ expect(getMainByLabelText("6 lines in a line chart")).toBeVisible();
+});
+
test("renders one line per demographic subgroup", () => {
const dimension = DIMENSION_KEYS.race;
render(