Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamp Spotlight state landing page #477

Merged
merged 15 commits into from
Sep 13, 2021
20 changes: 6 additions & 14 deletions spotlight-client/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe("navigation", () => {
test("tenant home", () => {
expect.hasAssertions();
const targetPath = "/us-nd";
const lookupArgs = ["heading", { name: /North Dakota/, level: 1 }] as const;
const lookupArgs = ["heading", { name: /DOCR/, level: 1 }] as const;

return verifyWithNavigation({ targetPath, lookupArgs });
});
Expand Down Expand Up @@ -128,7 +128,7 @@ describe("navigation", () => {
const homeLink = inNav.getByRole("link", { name: "Spotlight" });
const tenantLink = inNav.getByRole("link", { name: "North Dakota" });
const sentencingLink = await screen.findByRole("link", {
name: "Sentencing",
name: "Sentencing Data",
});

fireEvent.click(sentencingLink);
Expand All @@ -142,20 +142,12 @@ describe("navigation", () => {

fireEvent.click(tenantLink);
await waitFor(async () =>
expect(await screen.findByTestId("PageTitle")).toHaveTextContent(
"Explore correctional data from North Dakota."
)
expect(await screen.findByTestId("PageTitle")).toHaveTextContent("DOCR")
);

const disparitiesLink = screen.getByRole("link", {
name: "Racial Disparities",
});
fireEvent.click(disparitiesLink);
await waitFor(async () =>
expect(await screen.findByTestId("PageTitle")).toHaveTextContent(
"Racial Disparities"
)
);
expect(
screen.queryByText("Racial Disparities Data")
).not.toBeInTheDocument();

fireEvent.click(homeLink);
await waitFor(async () =>
Expand Down
20 changes: 14 additions & 6 deletions spotlight-client/src/MetricVizMapper/MetricVizMapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,29 @@ import ProgramParticipationCurrentMetric from "../contentModels/ProgramParticipa
import VizProgramParticipationCurrent from "../VizProgramParticipationCurrent";

type MetricVizMapperProps = {
metric: Metric<MetricRecord>;
metric: Metric<MetricRecord> | undefined;
preview?: boolean;
};

const MetricVizMapper: React.FC<MetricVizMapperProps> = ({ metric }) => {
const MetricVizMapper: React.FC<MetricVizMapperProps> = ({
metric,
preview,
}) => {
if (metric instanceof HistoricalPopulationBreakdownMetric) {
return <VizHistoricalPopulationBreakdown metric={metric} />;
return (
<VizHistoricalPopulationBreakdown preview={preview} metric={metric} />
);
}
if (metric instanceof PopulationBreakdownByLocationMetric) {
return <VizPopulationBreakdownByLocation metric={metric} />;
return (
<VizPopulationBreakdownByLocation preview={preview} metric={metric} />
);
}
if (metric instanceof DemographicsByCategoryMetric) {
if (metric.id === "PrisonStayLengthAggregate") {
return <VizPrisonStayLengths metric={metric} />;
}
return <VizDemographicsByCategory metric={metric} />;
return <VizDemographicsByCategory preview={preview} metric={metric} />;
}
if (metric instanceof RecidivismRateMetric) {
if (metric.id === "PrisonRecidivismRateSingleFollowupHistorical") {
Expand All @@ -65,7 +73,7 @@ const MetricVizMapper: React.FC<MetricVizMapperProps> = ({ metric }) => {
return <VizSupervisionSuccessRate metric={metric} />;
}
if (metric instanceof ProgramParticipationCurrentMetric) {
return <VizProgramParticipationCurrent metric={metric} />;
return <VizProgramParticipationCurrent preview={preview} metric={metric} />;
}

// there are no other metric types, so this should only be reached when developing new ones
Expand Down
99 changes: 81 additions & 18 deletions spotlight-client/src/OtherNarrativeLinks/OtherNarrativeLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import { animated, useSpring } from "react-spring/web.cjs";
import styled from "styled-components/macro";
import { track } from "../analytics";
import { TenantId } from "../contentApi/types";
import SystemNarrative from "../contentModels/SystemNarrative";
import { Narrative } from "../contentModels/types";
import MetricVizMapper from "../MetricVizMapper";
import getUrlForResource from "../routerUtils/getUrlForResource";
import { useDataStore } from "../StoreProvider";
import { breakpoints, colors } from "../UiLibrary";
Expand All @@ -34,7 +36,9 @@ import Arrow from "../UiLibrary/Arrow";

const Wrapper = styled.div`
/* prevents the trailing grid gaps from pushing other stuff around */
overflow: hidden;
@media (max-width: ${breakpoints.tablet[0]}px) {
overflow: hidden;
}
`;

const LinkList = styled.ul`
Expand All @@ -49,43 +53,79 @@ const LinkList = styled.ul`
const LinkListItem = styled.li`
/* creates gaps */
border: 0 solid transparent;
border-width: 0 ${rem(32)} ${rem(32)} 0;
border-width: 0 ${rem(32)} 0 0;
flex: 0 0 auto;
white-space: nowrap;
/* use width to create 1-4 columns, depending on screen size */
width: 100%;

@media (min-width: ${breakpoints.tablet[0]}px) {
width: calc(100% / 2);
width: calc(100% / 1);
}

@media (min-width: ${breakpoints.desktop[0]}px) {
width: calc(100% / 3);
width: calc(100% / 1);
}

@media (min-width: ${breakpoints.xl[0]}px) {
width: calc(100% / 4);
width: calc(100% / 2);
}

a {
border-top: 1px solid ${colors.rule};
color: ${colors.text};
display: block;
padding-right: ${rem(8)};
padding-top: ${rem(32)};
padding-top: ${rem(24)};
padding-bottom: ${rem(24)};
text-decoration: none;
width: 100%;
}

a:not(:last-child) {
border-bottom: 1px solid ${colors.rule};
}
`;

const LinkText = styled.span`
white-space: normal;
`;

const ChartTitle = styled.span`
font-size: ${rem(16)};
`;

const ChartPreview = styled.div`
padding-top: ${rem(16)};
`;

const ChartPreviewComponent: React.FC<{
narrative: SystemNarrative;
}> = ({ narrative }) => {
if (narrative.preview)
return (
<>
<ChartTitle>{narrative.previewTitle}</ChartTitle>
<ChartPreview>
<MetricVizMapper
preview
metric={
narrative.sections.find(
(section) => section.metric.id === narrative.preview
)?.metric
}
/>
</ChartPreview>
</>
);
return null;
};

const NarrativeLink: React.FC<{
narrative: Narrative;
narrativeToPreview?: SystemNarrative;
tenantId: TenantId;
}> = observer(({ narrative, tenantId }) => {
}> = observer(({ narrative, narrativeToPreview, tenantId }) => {
const [animationStyles, setAnimationStyles] = useSpring(() => ({
opacity: 0,
from: { opacity: 0 },
Expand All @@ -109,11 +149,14 @@ const NarrativeLink: React.FC<{
onMouseOut={() => setAnimationStyles({ opacity: 0 })}
onBlur={() => setAnimationStyles({ opacity: 0 })}
>
<LinkText>{narrative.title}</LinkText>&nbsp;
<LinkText>{narrative.title} Data</LinkText>&nbsp;
<animated.span style={animationStyles}>
<Arrow color={colors.link} direction="right" />
</animated.span>
</Link>
{narrativeToPreview && (
<ChartPreviewComponent narrative={narrativeToPreview} />
)}
</LinkListItem>
);
});
Expand All @@ -122,7 +165,9 @@ const NarrativeLink: React.FC<{
* Produces a grid of links to available narratives for the current tenant.
* If there is a current narrative selected, it will be excluded from the grid.
*/
const OtherNarrativeLinks = (): React.ReactElement | null => {
const OtherNarrativeLinks: React.FC<{
chartsPreview?: boolean;
}> = ({ chartsPreview }): React.ReactElement | null => {
const {
tenant,
tenantStore: { currentNarrativeTypeId },
Expand All @@ -138,18 +183,36 @@ const OtherNarrativeLinks = (): React.ReactElement | null => {
return narrative.id !== currentNarrativeTypeId;
});

const narrativesToPreview = [
...Object.values(tenant.systemNarratives),
].filter((narrative): narrative is SystemNarrative => {
if (narrative === undefined) return false;
return narrative.id !== currentNarrativeTypeId;
});

return (
<Wrapper>
<LinkList>
{narrativesToDisplay.map((narrative) => {
return (
<NarrativeLink
key={narrative.id}
tenantId={tenant.id}
narrative={narrative}
/>
);
})}
{chartsPreview
? narrativesToPreview.map((narrative) => {
return (
<NarrativeLink
key={narrative.id}
tenantId={tenant.id}
narrative={narrative}
narrativeToPreview={narrative}
/>
);
})
: narrativesToDisplay.map((narrative) => {
return (
<NarrativeLink
key={narrative.id}
tenantId={tenant.id}
narrative={narrative}
/>
);
})}
</LinkList>
</Wrapper>
);
Expand Down
17 changes: 6 additions & 11 deletions spotlight-client/src/PageTenant/PageTenant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import styled from "styled-components/macro";
import { NAV_BAR_HEIGHT } from "../constants";
import OtherNarrativeLinks from "../OtherNarrativeLinks";
import { useDataStore } from "../StoreProvider";
import { breakpoints, CopyBlock, PageSection, PageTitle } from "../UiLibrary";
import { breakpoints, PageSection, PageTitle } from "../UiLibrary";
import withRouteSync from "../withRouteSync";

const Introduction = styled(PageSection)`
Expand All @@ -35,6 +35,7 @@ const Introduction = styled(PageSection)`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/* try to keep the links "above the fold" */
min-height: calc(100vh - ${rem(NAV_BAR_HEIGHT)} - ${rem(130)});

Expand All @@ -48,13 +49,8 @@ const Introduction = styled(PageSection)`
const Links = styled(PageSection)``;

const Title = styled(PageTitle)`
max-width: ${rem(760)};
`;

const Description = styled(CopyBlock)`
font-size: ${rem(20)};
line-height: 1.7;
max-width: ${rem(760)};
font-size: ${rem(40)};
max-width: ${rem(1100)};
`;

const PageTenant: React.FC<RouteComponentProps> = () => {
Expand All @@ -66,11 +62,10 @@ const PageTenant: React.FC<RouteComponentProps> = () => {
// tenant may be briefly undefined during initial page load
<article>
<Introduction>
<Title>{tenant.landingPageTitle}</Title>
macfarlandian marked this conversation as resolved.
Show resolved Hide resolved
<Description>{HTMLReactParser(tenant.description)}</Description>
<Title>{HTMLReactParser(tenant.description)}</Title>
</Introduction>
<Links>
<OtherNarrativeLinks />
<OtherNarrativeLinks chartsPreview />
</Links>
</article>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ test("includes links to other narratives", () => {
const nav = screen.getByRole("navigation", { name: "data narratives" });

const otherLink = within(nav).getByRole("link", {
name: mockContentFixture.systemNarratives.Sentencing?.title,
name: `${mockContentFixture.systemNarratives.Sentencing?.title} Data`,
});

expect(otherLink).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { TenantContent } from "../../contentApi/types";

const content: TenantContent = {
name: "Test Tenant",
landingPageTitle: "test landing page title",
description: "test tenant description",
coBrandingCopy: "test tenant co-branding",
feedbackUrl: "https://example.com/feedback",
Expand Down Expand Up @@ -47,6 +46,8 @@ const content: TenantContent = {
systemNarratives: {
Parole: {
title: "test parole narrative",
previewTitle: "test parole subtitle",
preview: "ParolePopulationCurrent",
introduction: "test parole introduction",
sections: [
{
Expand All @@ -63,6 +64,8 @@ const content: TenantContent = {
},
Sentencing: {
title: "test sentencing narrative",
previewTitle: "test sentencing subtitle",
preview: "SentencePopulationCurrent",
introduction:
'test sentencing introduction <a href="https://example.com">intro link</a>',
sections: [
Expand Down
1 change: 1 addition & 0 deletions spotlight-client/src/SystemNarrativePage/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@

// eslint-disable-next-line import/prefer-default-export
macfarlandian marked this conversation as resolved.
Show resolved Hide resolved
export const X_PADDING = 176;
export const X_PADDING_TABLET = 80;
6 changes: 5 additions & 1 deletion spotlight-client/src/UiLibrary/PageSection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import { rem } from "polished";
import styled from "styled-components/macro";
import { NAV_BAR_HEIGHT } from "../constants";
import { X_PADDING } from "../SystemNarrativePage/constants";
import { X_PADDING, X_PADDING_TABLET } from "../SystemNarrativePage/constants";
import breakpoints from "./breakpoints";

/**
Expand All @@ -31,6 +31,10 @@ export const PageSection = styled.section`
padding: 0 ${rem(16)};

@media screen and (min-width: ${breakpoints.tablet[0]}px) {
padding: 0 ${rem(X_PADDING_TABLET)};
}

@media screen and (min-width: ${breakpoints.desktop[0]}px) {
padding: 0 ${rem(X_PADDING)};
}
`;
Expand Down
Loading