- {tenant && (
- <>
-
- setExpanded(false)}
- to={getUrlForResource({
- page: "tenant",
- params: { tenantId: tenant.id },
- })}
- >
- Home
-
-
- {Object.values(tenant.systemNarratives).map(
- (narrative) =>
- narrative && (
-
- setExpanded(false)}
- to={getUrlForResource({
- page: "narrative",
- params: {
- tenantId: tenant.id,
- narrativeTypeId: narrative.id,
- },
- })}
- >
- {narrative.title}
-
-
- )
- )}
- {tenant.racialDisparitiesNarrative && (
+
+
+ {tenant && (
+ <>
setExpanded(false)}
to={getUrlForResource({
- page: "narrative",
- params: {
- tenantId: tenant.id,
- narrativeTypeId: "RacialDisparities",
- },
+ page: "tenant",
+ params: { tenantId: tenant.id },
})}
>
- {tenant.racialDisparitiesNarrative.title}
+ Home
- )}
-
- Feedback
-
- >
- )}
-
+ {Object.values(tenant.systemNarratives).map(
+ (narrative) =>
+ narrative && (
+
+ setExpanded(false)}
+ to={getUrlForResource({
+ page: "narrative",
+ params: {
+ tenantId: tenant.id,
+ narrativeTypeId: narrative.id,
+ },
+ })}
+ >
+ {narrative.title}
+
+
+ )
+ )}
+ {tenant.racialDisparitiesNarrative && (
+
+ setExpanded(false)}
+ to={getUrlForResource({
+ page: "narrative",
+ params: {
+ tenantId: tenant.id,
+ narrativeTypeId: "RacialDisparities",
+ },
+ })}
+ >
+ {tenant.racialDisparitiesNarrative.title}
+
+
+ )}
+
+ Feedback
+
+ >
+ )}
+
+
);
};
diff --git a/spotlight-client/src/SystemNarrativePage/SystemNarrativePage.tsx b/spotlight-client/src/SystemNarrativePage/SystemNarrativePage.tsx
index ed5f517a..0a76d785 100644
--- a/spotlight-client/src/SystemNarrativePage/SystemNarrativePage.tsx
+++ b/spotlight-client/src/SystemNarrativePage/SystemNarrativePage.tsx
@@ -19,10 +19,8 @@ import HTMLReactParser from "html-react-parser";
import React from "react";
import SystemNarrative from "../contentModels/SystemNarrative";
import {
- Chevron,
NarrativeIntroContainer,
NarrativeIntroCopy,
- NarrativeScrollIndicator,
NarrativeSectionBody,
NarrativeSectionTitle,
NarrativeTitle,
@@ -44,11 +42,6 @@ const SystemNarrativePage: React.FC<{
{HTMLReactParser(narrative.introduction)}
-
- SCROLL
-
-
-
),
},
diff --git a/spotlight-client/src/UiLibrary/narrative.ts b/spotlight-client/src/UiLibrary/narrative.ts
index daf65b8f..88e4775d 100644
--- a/spotlight-client/src/UiLibrary/narrative.ts
+++ b/spotlight-client/src/UiLibrary/narrative.ts
@@ -18,7 +18,6 @@
import { rem } from "polished";
import styled from "styled-components/macro";
import breakpoints from "./breakpoints";
-import colors from "./colors";
import CopyBlock from "./CopyBlock";
import { FullScreenSection } from "./PageSection";
import PageTitle from "./PageTitle";
@@ -52,25 +51,6 @@ export const NarrativeIntroCopy = styled(CopyBlock)`
}
`;
-export const NarrativeScrollIndicator = styled.div`
- align-items: center;
- color: ${colors.caption};
- display: flex;
- flex-direction: column;
- font-size: ${rem(12)};
- font-weight: 500;
- letter-spacing: 0.05em;
- margin-top: ${rem(32)};
-
- @media screen and (min-width: ${breakpoints.tablet[0]}px) {
- margin-top: ${rem(144)};
- }
-
- span {
- margin-bottom: ${rem(16)};
- }
-`;
-
export const NarrativeSectionTitle = styled.h2`
font-family: ${typefaces.display};
font-size: ${rem(24)};
diff --git a/spotlight-client/src/VizControls/VizControls.tsx b/spotlight-client/src/VizControls/VizControls.tsx
index 07d188ea..b1e02fe1 100644
--- a/spotlight-client/src/VizControls/VizControls.tsx
+++ b/spotlight-client/src/VizControls/VizControls.tsx
@@ -35,15 +35,11 @@ const ControlsGroup = styled.div`
display: flex;
flex-wrap: wrap;
margin-bottom: ${rem(16)};
- padding-bottom: ${rem(16)};
`;
const FilterWrapper = styled.div`
- margin: 0 ${rem(16)};
-
- &:first-child {
- margin-left: 0;
- }
+ margin-bottom: ${rem(16)};
+ margin-right: ${rem(32)};
&:last-child {
margin-right: 0;
@@ -57,6 +53,7 @@ const Button = styled.button`
cursor: pointer;
font-size: ${rem(14)};
font-weight: 500;
+ margin-bottom: ${rem(16)};
padding: none;
`;
diff --git a/spotlight-client/src/VizRecidivismRateSingleFollowup/VizRecidivismRateSingleFollowup.tsx b/spotlight-client/src/VizRecidivismRateSingleFollowup/VizRecidivismRateSingleFollowup.tsx
index 573f6985..1946f0ee 100644
--- a/spotlight-client/src/VizRecidivismRateSingleFollowup/VizRecidivismRateSingleFollowup.tsx
+++ b/spotlight-client/src/VizRecidivismRateSingleFollowup/VizRecidivismRateSingleFollowup.tsx
@@ -16,7 +16,7 @@
// =============================================================================
import { observer } from "mobx-react-lite";
-import React from "react";
+import React, { useState } from "react";
import Measure from "react-measure";
import { animated, useSpring, useTransition } from "react-spring/web.cjs";
import styled from "styled-components/macro";
@@ -70,7 +70,12 @@ type VizRecidivismRateSingleFollowupProps = {
const VizRecidivismRateSingleFollowup: React.FC = ({
metric,
}) => {
- const { singleFollowupDemographics, demographicView, unknowns } = metric;
+ const {
+ demographicView,
+ followUpYears,
+ singleFollowupDemographics,
+ unknowns,
+ } = metric;
const [chartContainerStyles, setChartContainerStyles] = useSpring(() => ({
from: { height: singleChartHeight },
@@ -84,6 +89,14 @@ const VizRecidivismRateSingleFollowup: React.FC();
+ // this is NOT a sophisticated heuristic, but the bar labels
+ // (which are years) tend to get cramped around this size
+ const useAngledLabels =
+ typeof containerWidth === "number" &&
+ containerWidth < 400 &&
+ followUpYears === 1;
+
if (demographicView === "nofilter")
throw new Error(
"Unable to display this metric without demographic filter."
@@ -94,7 +107,10 @@ const VizRecidivismRateSingleFollowup: React.FC {
- if (bounds) setChartContainerStyles({ height: bounds.height });
+ if (bounds) {
+ setChartContainerStyles({ height: bounds.height });
+ setContainerWidth(bounds.width);
+ }
}}
>
{({ measureRef }) => (
@@ -115,6 +131,7 @@ const VizRecidivismRateSingleFollowup: React.FC.
// =============================================================================
+import { useId } from "@reach/auto-id";
import { scaleLinear } from "d3-scale";
import { rgba } from "polished";
import React, { useState } from "react";
@@ -26,7 +27,7 @@ import { $Keys } from "utility-types";
import ResponsiveTooltipController from "../charts/ResponsiveTooltipController";
import { formatAsNumber } from "../utils";
import MeasureWidth from "../MeasureWidth";
-import { animation, breakpoints, colors, typefaces } from "../UiLibrary";
+import { breakpoints, colors, typefaces } from "../UiLibrary";
export const CHART_BOTTOM_PADDING = 80;
export const CHART_HEIGHT = 500;
@@ -119,9 +120,9 @@ const targetColor = rgba(baseColor, 0.5);
const hoverColor = rgba(baseColor, 0.2);
-const Gradients = (
+const Gradients = ({ idPrefix }: { idPrefix: string | undefined }) => (
<>
-
+
-
+
-
+
@@ -221,6 +222,12 @@ export default function SingleStepSankey({
);
};
+ // some browsers (most notably iOS Safari) fail to render these gradients
+ // if this component appears more than once on the page, because the ids
+ // are then non-unique in the context of the HTML document. Thus the prefix,
+ // which should be unique per component instance.
+ const gradientIdPrefix = useId();
+
return (
{({ measureRef, width }) => (
@@ -232,10 +239,12 @@ export default function SingleStepSankey({
setHighlighted={setHighlighted}
>
}
baseMarkProps={{
transitionDuration: {
- fill: animation.defaultDuration,
+ // transitions don't work well with our gradient fills;
+ // less janky to just disable them
+ fill: 0,
},
}}
edges={edges}
@@ -243,7 +252,7 @@ export default function SingleStepSankey({
return {
fill: shouldFade(d)
? hoverColor
- : `url(#${d.source.id.toLowerCase()}Gradient)`,
+ : `url(#${gradientIdPrefix}${d.source.id.toLowerCase()}Gradient)`,
};
}}
margin={{ ...MARGIN, right: rightMargin }}
diff --git a/spotlight-client/src/charts/BarChartTrellis.tsx b/spotlight-client/src/charts/BarChartTrellis.tsx
index 6b55cae1..51fa3bf0 100644
--- a/spotlight-client/src/charts/BarChartTrellis.tsx
+++ b/spotlight-client/src/charts/BarChartTrellis.tsx
@@ -30,7 +30,7 @@ import MeasureWidth from "../MeasureWidth";
export const singleChartHeight = 300;
-const MARGIN = { top: 56, bottom: 64, left: 48, right: 0 };
+const MARGIN = { top: 56, bottom: 80, left: 48, right: 0 };
const ChartTitle = styled.text`
font-weight: 500;
@@ -52,6 +52,7 @@ type BarChartData = {
};
type BarChartTrellisProps = {
+ angledLabels?: boolean;
barAxisLabel?: string;
data: BarChartData[];
formatBarLabel?: (label: string) => string;
@@ -63,6 +64,7 @@ type BarChartTrellisProps = {
* with identical Y axis ranges and cross-highlighting.
*/
export function BarChartTrellis({
+ angledLabels,
barAxisLabel,
data,
formatBarLabel = (label) => label,
@@ -123,7 +125,13 @@ export function BarChartTrellis({
oAccessor="label"
// @ts-expect-error Semiotic types can't handle a styled component here but it's fine
oLabel={(barLabel) => (
-
+
{formatBarLabel(barLabel as string)}
)}
diff --git a/yarn.lock b/yarn.lock
index 05d40414..78a1fed0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1637,6 +1637,14 @@
"@reach/utils" "0.11.2"
tslib "^2.0.0"
+"@reach/auto-id@^0.15.0":
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.15.0.tgz#f46afebfc140b2099b95c7aec1f049d167d3833d"
+ integrity sha512-iACaCcZeqAhDf4OOwJpmHHS/LaRj9z3Ip8JmlhpCrFWV2YOIiiZk42amlBZX6CKH66Md+eriYZQk3TyAjk6Oxg==
+ dependencies:
+ "@reach/utils" "0.15.0"
+ tslib "^2.1.0"
+
"@reach/descendants@0.10.5":
version "0.10.5"
resolved "https://registry.yarnpkg.com/@reach/descendants/-/descendants-0.10.5.tgz#2611174e9e9b326dba548356221e2f8c8f5c8612"
@@ -1719,6 +1727,14 @@
tslib "^2.0.0"
warning "^4.0.3"
+"@reach/utils@0.15.0":
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/@reach/utils/-/utils-0.15.0.tgz#5b183d668f9bb900b2dec7a33c028a2a828d27b2"
+ integrity sha512-JHHN7T5ucFiuQbqkgv8ECbRWKfRiJxrO/xHR3fHf+f2C7mVs/KkJHhYtovS1iEapR4silygX9PY0+QUmHPOTYw==
+ dependencies:
+ tiny-warning "^1.0.3"
+ tslib "^2.1.0"
+
"@react-hook/change@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@react-hook/change/-/change-1.0.0.tgz#4604bc93d616fcc0311f528464edd7cb24083289"
@@ -15156,6 +15172,11 @@ tslib@^2.0.0, tslib@^2.0.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c"
integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==
+tslib@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
+ integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
+
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"