Skip to content

Commit

Permalink
update page title and send to trackers
Browse files Browse the repository at this point in the history
  • Loading branch information
macfarlandian committed Mar 16, 2021
1 parent 5baadb2 commit 0564ad4
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 86 deletions.
6 changes: 5 additions & 1 deletion spotlight-client/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,21 @@ describe("navigation", () => {
).toBeInTheDocument();
});

test("pageview tracking", async () => {
test.only("pageview tracking", async () => {
segmentMock.page.mockReset();

const {
history: { navigate },
} = renderNavigableApp();

expect(document.title).toBe("Spotlight by Recidiviz");
expect(segmentMock.page).toHaveBeenCalledTimes(1);

await act(() => navigate("/us-nd/collections/prison"));

expect(document.title).toBe(
"Prison — North Dakota — Spotlight by Recidiviz"
);
expect(segmentMock.page).toHaveBeenCalledTimes(2);

// in-page navigation doesn't trigger additional pageviews
Expand Down
2 changes: 2 additions & 0 deletions spotlight-client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import PageHome from "./PageHome";
import PageNarrative from "./PageNarrative";
import PageNotFound from "./PageNotFound";
import PageTenant from "./PageTenant";
import PageviewTracker from "./PageviewTracker";
import { NarrativesSlug } from "./routerUtils/types";
import ScrollManager from "./ScrollManager";
import SiteFooter from "./SiteFooter";
Expand Down Expand Up @@ -77,6 +78,7 @@ const App: React.FC = () => {
<PageNotFound default />
</Router>
<ScrollManager />
<PageviewTracker />
</Main>
<SiteFooter />
<TooltipMobile />
Expand Down
2 changes: 1 addition & 1 deletion spotlight-client/src/DataStore/RootStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default class RootStore {
userStore: UserStore;

constructor() {
makeObservable(this, { tenant: computed });
makeObservable(this, { tenant: computed, narrative: computed });

this.tenantStore = new TenantStore({ rootStore: this });

Expand Down
30 changes: 30 additions & 0 deletions spotlight-client/src/DataStore/UiStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,34 @@ export default class UiStore {

return idParts.join("::");
}

/**
* Constructs an appropriate string for the `title` tag,
* or returns undefined if the page is still loading and title cannot be determined yet
*/
get currentPageTitle(): string | undefined {
const titleParts: string[] = [];

const { tenant, narrative } = this.rootStore;

if (tenant) {
titleParts.unshift(tenant.name);

if (narrative) {
titleParts.unshift(narrative.title);
}
}

if (!titleParts.length) {
// this is valid if we are on the site homepage;
// otherwise it is an intermediate state that should not leak into reactions
if (window.location.pathname !== "/") {
return undefined;
}
}

titleParts.push("Spotlight by Recidiviz");

return titleParts.join(" — ");
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2020 Recidiviz, Inc.
// Copyright (C) 2021 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
Expand All @@ -15,28 +15,23 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import { RouteComponentProps } from "@reach/router";
import React, { useEffect } from "react";
import { observer } from "mobx-react-lite";
import { useEffect } from "react";
import { useDataStore } from "../StoreProvider";

/**
* A high-order component responsible for syncing relevant route parameters
* to the data store, so it can react to navigation.
* Passes all props through `RouteComponent`, with route parameter types narrowed
* from strings to unions of their valid values.
* All Reach Router route components should be wrapped in this HOC!
*/
const withTrackPageview = <Props extends RouteComponentProps>(
RouteComponent: React.FC<Props>
): React.FC<Props> => {
const WrappedRouteComponent: React.FC<Props> = (props) => {
useEffect(() => {
window.analytics.page();
}, []);
const PageviewTracker = (): null => {
const {
uiStore: { currentPageTitle },
} = useDataStore();

return <RouteComponent {...props} />;
};
useEffect(() => {
if (currentPageTitle) {
document.title = currentPageTitle;
window.analytics.page();
}
}, [currentPageTitle]);

return WrappedRouteComponent;
return null;
};

export default withTrackPageview;
export default observer(PageviewTracker);
18 changes: 18 additions & 0 deletions spotlight-client/src/PageviewTracker/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 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 <https://www.gnu.org/licenses/>.
// =============================================================================

export { default } from "./PageviewTracker";
30 changes: 26 additions & 4 deletions spotlight-client/src/withRouteSync/withRouteSync.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
// =============================================================================

import { RouteComponentProps } from "@reach/router";
import React from "react";
import { action } from "mobx";
import React, { useEffect } from "react";
import normalizeRouteParams from "../routerUtils/normalizeRouteParams";
import { RouteParams } from "../routerUtils/types";
import withTrackPageview from "./withTrackPageview";
import withUpdateStore from "./withUpdateStore";
import { useDataStore } from "../StoreProvider";

/**
* A high-order component responsible for syncing relevant route parameters
Expand All @@ -31,7 +32,28 @@ import withUpdateStore from "./withUpdateStore";
const withRouteSync = <Props extends RouteComponentProps & RouteParams>(
RouteComponent: React.FC<Props>
): React.FC<Props> => {
return withUpdateStore(withTrackPageview(RouteComponent));
const WrappedRouteComponent: React.FC<Props> = (props) => {
const { tenantStore } = useDataStore();

const normalizedProps = normalizeRouteParams(props);

useEffect(
action("sync route params", () => {
tenantStore.currentTenantId = normalizedProps.tenantId;
tenantStore.currentNarrativeTypeId = normalizedProps.narrativeTypeId;
})
);

return (
<RouteComponent
{...props}
tenantId={normalizedProps.tenantId}
narrativeTypeId={normalizedProps.narrativeTypeId}
/>
);
};

return WrappedRouteComponent;
};

export default withRouteSync;
59 changes: 0 additions & 59 deletions spotlight-client/src/withRouteSync/withUpdateStore.tsx

This file was deleted.

0 comments on commit 0564ad4

Please sign in to comment.