Skip to content

Commit

Permalink
Tenant homepage (#344)
Browse files Browse the repository at this point in the history
  • Loading branch information
macfarlandian committed Mar 2, 2021
1 parent 51a1a65 commit 06fe86a
Show file tree
Hide file tree
Showing 21 changed files with 563 additions and 281 deletions.
59 changes: 15 additions & 44 deletions spotlight-client/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
ByRoleOptions,
within,
fireEvent,
waitFor,
} from "@testing-library/react";
import testContent from "./contentApi/sources/us_nd";
import { renderNavigableApp } from "./testUtils";
Expand Down Expand Up @@ -69,14 +68,6 @@ describe("navigation", () => {
return verifyWithNavigation({ targetPath, lookupArgs });
});

test("narratives page", () => {
expect.hasAssertions();
const targetPath = "/us-nd/collections";
const lookupArgs = ["heading", { name: "Collections", level: 1 }] as const;

return verifyWithNavigation({ targetPath, lookupArgs });
});

test("single narrative page", () => {
expect.hasAssertions();
const targetPath = "/us-nd/collections/prison";
Expand All @@ -91,53 +82,33 @@ describe("navigation", () => {
return verifyWithNavigation({ targetPath, lookupArgs });
});

test("nav bar", async () => {
const dataPortalLabel = "Explore";
const narrativesLabel = "Collections";

test("links", async () => {
const {
history: { navigate },
} = renderNavigableApp();
const inNav = within(screen.getByRole("navigation"));

expect(
inNav.queryByRole("link", { name: dataPortalLabel })
).not.toBeInTheDocument();
expect(
inNav.queryByRole("link", { name: narrativesLabel })
).not.toBeInTheDocument();

await act(() => navigate("/us-nd"));
const homeLink = inNav.getByRole("link", { name: "Spotlight" });
const tenantLink = inNav.getByRole("link", { name: "North Dakota" });
const narrativesLink = inNav.getByRole("link", { name: narrativesLabel });
const sentencingLink = screen.getByRole("link", { name: "Sentencing" });

const verifyNavLinks = () => {
expect(homeLink).toBeInTheDocument();
expect(tenantLink).toBeInTheDocument();
expect(narrativesLink).toBeInTheDocument();
};

fireEvent.click(narrativesLink);
await waitFor(() =>
expect(
screen.getByRole("heading", { name: "Collections", level: 1 })
).toBeInTheDocument()
);
verifyNavLinks();
fireEvent.click(sentencingLink);
expect(
await screen.findByRole("heading", { name: "Sentencing", level: 1 })
).toBeInTheDocument();

fireEvent.click(tenantLink);
await waitFor(() =>
expect(
screen.getByRole("heading", { name: "North Dakota", level: 1 })
).toBeInTheDocument()
);
expect(
await screen.findByRole("heading", {
name: "Explore correctional data from North Dakota.",
level: 1,
})
).toBeInTheDocument();

fireEvent.click(homeLink);
await waitFor(() =>
expect(
screen.getByRole("heading", { name: "Spotlight", level: 1 })
).toBeInTheDocument()
);
expect(
await screen.findByRole("heading", { name: "Spotlight", level: 1 })
).toBeInTheDocument();
});
});
14 changes: 7 additions & 7 deletions spotlight-client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import { FOOTER_HEIGHT, NAV_BAR_HEIGHT } from "./constants";
import GlobalStyles from "./GlobalStyles";
import PageHome from "./PageHome";
import PageNarrative from "./PageNarrative";
import PageNarrativeList from "./PageNarrativeList";
import PageNotFound from "./PageNotFound";
import PageTenant from "./PageTenant";
import { NarrativesSlug } from "./routerUtils/types";
import ScrollManager from "./ScrollManager";
import SiteFooter from "./SiteFooter";
import SiteNavigation from "./SiteNavigation";
import StoreProvider from "./StoreProvider";
Expand All @@ -48,8 +48,8 @@ const PassThroughPage: React.FC<RouteComponentProps> = ({ children }) => (
<>{children}</>
);

const Main = styled.div.attrs((props) => ({ role: "main" }))`
margin-top: ${rem(NAV_BAR_HEIGHT)};
const Main = styled.div.attrs({ role: "main" })`
padding-top: ${rem(NAV_BAR_HEIGHT)};
min-height: calc(100vh - ${rem(NAV_BAR_HEIGHT + FOOTER_HEIGHT)});
`;

Expand All @@ -69,14 +69,14 @@ const App: React.FC = () => {
<PageHome path="/" />
<PassThroughPage path="/:tenantId">
<PageTenant path="/" />
<PassThroughPage path={`/${NarrativesSlug}`}>
<PageNarrativeList path="/" />
<PageNarrative path="/:narrativeTypeId/*sectionNumber" />
</PassThroughPage>
<PageNarrative
path={`/${NarrativesSlug}/:narrativeTypeId/*sectionNumber`}
/>
<PageNotFound default />
</PassThroughPage>
<PageNotFound default />
</Router>
<ScrollManager />
</Main>
<SiteFooter />
<TooltipMobile />
Expand Down
36 changes: 35 additions & 1 deletion spotlight-client/src/DataStore/UiStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import { reactImmediately } from "../testUtils";
import RootStore from "./RootStore";
import type UiStore from "./UiStore";

let rootStore: RootStore;
let store: UiStore;

beforeEach(() => {
store = new RootStore().uiStore;
rootStore = new RootStore();
store = rootStore.uiStore;
});

test("set info panel contents", () => {
Expand Down Expand Up @@ -53,3 +55,35 @@ test("set info panel contents", () => {
expect(store.renderTooltipMobile).toBeUndefined();
});
});

test("current page ID", () => {
reactImmediately(() => {
expect(store.currentPageId).toBe("");
});

runInAction(() => {
rootStore.tenantStore.currentTenantId = "US_ND";
});

reactImmediately(() => {
expect(store.currentPageId).toBe("US_ND");
});

runInAction(() => {
rootStore.tenantStore.currentNarrativeTypeId = "Parole";
});

reactImmediately(() => {
expect(store.currentPageId).toBe("US_ND::Parole");
});

runInAction(() => {
rootStore.tenantStore.currentTenantId = undefined;
});

reactImmediately(() => {
expect(store.currentPageId).toBe("");
});

expect.hasAssertions();
});
20 changes: 20 additions & 0 deletions spotlight-client/src/DataStore/UiStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,24 @@ export default class UiStore {
this.tooltipMobileData = undefined;
this.renderTooltipMobile = undefined;
}

/**
* An easily watchable indicator of what page we're on
* (because not all routes are necessarily pages)
*/
get currentPageId(): string {
const idParts: string[] = [];

const { tenant, narrative } = this.rootStore;

if (tenant) {
idParts.push(tenant.id);

if (narrative) {
idParts.push(narrative.id);
}
}

return idParts.join("::");
}
}
76 changes: 3 additions & 73 deletions spotlight-client/src/NarrativeFooter/NarrativeFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,8 @@ import { Link } from "@reach/router";
import { observer } from "mobx-react-lite";
import { rem } from "polished";
import React from "react";
import { animated, useSpring } from "react-spring/web.cjs";
import styled from "styled-components/macro";
import { TenantId } from "../contentApi/types";
import SystemNarrative from "../contentModels/SystemNarrative";
import OtherNarrativeLinks from "../OtherNarrativeLinks";
import getUrlForResource from "../routerUtils/getUrlForResource";
import { useDataStore } from "../StoreProvider";
import { colors, typefaces } from "../UiLibrary";
Expand All @@ -49,83 +47,15 @@ const Heading = styled.h2`
letter-spacing: -0.04em;
`;

const LinkList = styled.ul`
display: flex;
font-size: ${rem(24)};
line-height: 1.5;
margin: ${rem(48)} -${rem(16)};
`;

const LinkListItem = styled.li`
border-top: 1px solid ${colors.rule};
flex: 1 1 auto;
margin: 0 ${rem(16)};
a {
color: ${colors.text};
display: block;
padding-top: ${rem(32)};
text-decoration: none;
width: 100%;
}
`;

const FooterLink: React.FC<{
narrative: SystemNarrative;
tenantId: TenantId;
}> = ({ narrative, tenantId }) => {
const [animationStyles, setAnimationStyles] = useSpring(() => ({
opacity: 0,
from: { opacity: 0 },
}));

return (
<LinkListItem>
<Link
to={getUrlForResource({
page: "narrative",
params: { tenantId, narrativeTypeId: narrative.id },
})}
onMouseOver={() => setAnimationStyles({ opacity: 1 })}
onFocus={() => setAnimationStyles({ opacity: 1 })}
onMouseOut={() => setAnimationStyles({ opacity: 0 })}
onBlur={() => setAnimationStyles({ opacity: 0 })}
>
{narrative.title}{" "}
<animated.span style={animationStyles}>
<Arrow color={colors.link} direction="right" />
</animated.span>
</Link>
</LinkListItem>
);
};

const Footer: React.FC = () => {
const {
tenant,
tenantStore: { currentNarrativeTypeId },
} = useDataStore();
const { tenant } = useDataStore();

if (!tenant) return null;

const narrativesToDisplay = Object.values(tenant.systemNarratives).filter(
(narrative) => narrative && narrative.id !== currentNarrativeTypeId
) as SystemNarrative[]; // this assertion is safe because undefined items were filtered out

return (
<Container aria-label="collections">
<Heading>Continue Reading</Heading>
<LinkList>
{narrativesToDisplay.map((narrative) => {
return (
<FooterLink
key={narrative.id}
tenantId={tenant.id}
narrative={narrative}
/>
);
})}
</LinkList>
<OtherNarrativeLinks />
<Link
className="NarrativeFooter__BackLink"
to={getUrlForResource({
Expand Down

0 comments on commit 06fe86a

Please sign in to comment.