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

Spike: Layout/breadcrumbs in Pages router #144

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"next": "13.4.19",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router-dom": "^6.23.1",
"request-ip": "^3.3.0",
"ts-node": "10.9.1",
"typescript": "4.9.5",
Expand Down
79 changes: 79 additions & 0 deletions src/components/layout/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import {
TemplateAppContainer,
Breadcrumbs,
DSProvider,
SkipNavigation,
useFeedbackBox,
} from "@nypl/design-system-react-components";
import React from "react";
import { type PropsWithChildren } from "react";
import Header from "../header/header";
import NotificationBanner from "../notificationBanner/notificationBanner";

interface LayoutProps {
activePage: string;
breadcrumbs?: { text: string; url: string }[];
}

const Layout = ({
children,
activePage,
breadcrumbs,
}: PropsWithChildren<LayoutProps>) => {
const [view, setView] = React.useState("form");
const { onOpen, isOpen, onClose, FeedbackBox } = useFeedbackBox();
const onSubmit = async (values) => {
try {
const response = await fetch("/api/feedback", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(values),
});

if (response.ok) {
// ...
setView("confirmation");
} else {
setView("error");
}
} catch (error) {
// Reject the promise according to your application.
// And then call:
setView("error");
}
};

const onFormClose = () => {
if (isOpen) {
onClose();
setView("form");
}
};
return (
<DSProvider>
<SkipNavigation />
<NotificationBanner />
<Header />
{activePage === "home" || activePage === "about" ? (
children
) : (
<>
<Breadcrumbs breadcrumbsData={breadcrumbs} />
<TemplateAppContainer contentPrimary={children} />
</>
)}
<FeedbackBox
showCategoryField
onSubmit={onSubmit}
onClose={onFormClose}
title="Feedback"
view={view}
/>
</DSProvider>
);
};

export default Layout;
4 changes: 2 additions & 2 deletions src/data/dcNavLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ export const dcNavLinks = [
text: "Items",
},
{
href: `${DC_URL}/collections`,
href: `/collections`,
text: "Collections",
},
{
href: `${DC_URL}/divisions`,
href: `/divisions`,
text: "Divisions",
},
{
Expand Down
48 changes: 3 additions & 45 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,17 @@
// import '@/styles/globals.css'
import type { AppProps } from "next/app";
import Head from "next/head";
import {
DSProvider,
useFeedbackBox,
} from "@nypl/design-system-react-components";
import React from "react";
import { useEffect } from "react";
import { useRouter } from "next/router";
import Script from "next/script";
import { trackVirtualPageView } from "../utils/utils";
import appConfig from "../../appConfig";
import { ADOBE_EMBED_URL, DC_URL } from "../config/constants";
import ContextProvider from "./breadcrumbsContext";

export default function App({ Component, pageProps }: AppProps) {
const router = useRouter();
const [view, setView] = React.useState("form");
const { onOpen, isOpen, onClose, FeedbackBox } = useFeedbackBox();
const onSubmit = async (values) => {
try {
const response = await fetch("/api/feedback", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(values),
});

if (response.ok) {
// ...
setView("confirmation");
} else {
setView("error");
}
} catch (error) {
// Reject the promise according to your application.
// And then call:
setView("error");
}
};

const onFormClose = () => {
if (isOpen) {
onClose();
setView("form");
}
};

// Track page view events to Adobe Analytics
useEffect(() => {
Expand Down Expand Up @@ -123,16 +88,9 @@ export default function App({ Component, pageProps }: AppProps) {
type="text/javascript"
src={`/newrelic/` + appConfig.environment + `.js`}
/>
<DSProvider>
<ContextProvider>
<Component {...pageProps} />
<FeedbackBox
showCategoryField
onSubmit={onSubmit}
onClose={onFormClose}
title="Feedback"
view={view}
/>
</DSProvider>
</ContextProvider>
</>
);
}
12 changes: 4 additions & 8 deletions src/pages/about/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Box, SkipNavigation } from "@nypl/design-system-react-components";
import Header from "@/components/header/header";
import NotificationBanner from "@/components/notificationBanner/notificationBanner";
import { Box } from "@nypl/design-system-react-components";
import aboutPageElements from "@/data/aboutPageElements";
import Head from "next/head";
import Layout from "@/components/layout/layout";

export default function About() {
function createSection(heading: React.JSX.Element, body: React.JSX.Element) {
Expand All @@ -15,10 +14,7 @@ export default function About() {
}

return (
<>
<SkipNavigation />
<NotificationBanner />
<Header />
<Layout activePage="about">
<Head>
<title>About NYPL Digital Collections</title>
<meta
Expand All @@ -41,6 +37,6 @@ export default function About() {
createSection(section.heading, section.body)
)}
</Box>
</>
</Layout>
);
}
37 changes: 37 additions & 0 deletions src/pages/breadcrumbsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { createContext, useReducer } from "react";

export const breadcrumbsContext = createContext(null);

const actionTypes = {
SET_BREADCRUMBS: "SET_BREADCRUMBS",
};

const reducer = (breadcrumbs, action) => {
switch (action.type) {
case "SET_BREADCRUMBS":
return [...breadcrumbs, action.newCrumb];
default:
return breadcrumbs;
}
};

function ContextProvider({ children }) {
const [breadcrumbs, dispatch] = useReducer(reducer, [
{ text: "Home", url: "/" },
]);

const setBreadcrumbs = (newCrumb) => {
dispatch({
type: actionTypes.SET_BREADCRUMBS,
newCrumb,
});
};

return (
<breadcrumbsContext.Provider value={{ breadcrumbs, setBreadcrumbs }}>
{children}
</breadcrumbsContext.Provider>
);
}

export default ContextProvider;
34 changes: 34 additions & 0 deletions src/pages/collections/[id].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useContext, useEffect } from "react";
import Layout from "@/components/layout/layout";
import { Heading } from "@nypl/design-system-react-components";
import { breadcrumbsContext } from "../breadcrumbsContext";

const Collection = () => {
const { breadcrumbs, setBreadcrumbs } = useContext(breadcrumbsContext);
useEffect(() => {
setBreadcrumbs({ text: "A Collection", url: "/collections/collection" });
}, []);
return (
<Layout activePage="collection" breadcrumbs={breadcrumbs}>
<Heading>
<>Collection</>
</Heading>
</Layout>
);
};

// Imagine an API call somewhere in here
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried passing this mocked API-fetched data to the component? How would this change if you use this breadcrumbs array as a prop in Collection, i.e. const Collection = ({ breadcrumbs }) => {.....

You could use the last object in the array as the value that is passed to setBreadcrumbs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, sorry for the confusion of trying to represent two approaches within the same file but yes imagining that as a prop to Collections. If that's the case though, I wouldn't use the context at all

export const getServerSideProps = async () => {
let breadcrumbs = [
{ text: "Home", url: "/" },
{ text: "Collections", url: "/collcetions" },
{ text: "A Collection", url: "/collections/collection" },
];
return {
props: {
breadcrumbs,
},
};
};

export default Collection;
Loading
Loading