diff --git a/package-lock.json b/package-lock.json index 484e9db4..c4f4d04a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,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", @@ -3313,6 +3314,14 @@ "streamx": "^2.15.0" } }, + "node_modules/@remix-run/router": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.1.tgz", + "integrity": "sha512-es2g3dq6Nb07iFxGk5GuHN20RwBZOsuDQN7izWIisUcv9r+d2C5jQxqmgkdebXgReWfiyUabcki6Fg77mSNrig==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", @@ -11458,6 +11467,36 @@ } } }, + "node_modules/react-router": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz", + "integrity": "sha512-fzcOaRF69uvqbbM7OhvQyBTFDVrrGlsFdS3AL+1KfIBtGETibHzi3FkoTRyiDJnWNc2VxrfvR+657ROHjaNjqQ==", + "dependencies": { + "@remix-run/router": "1.16.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.23.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.1.tgz", + "integrity": "sha512-utP+K+aSTtEdbWpC+4gxhdlPFwuEfDKq8ZrPFU65bbRJY+l706qjR7yaidBpo3MSeA/fzwbXWbKBI6ftOnP3OQ==", + "dependencies": { + "@remix-run/router": "1.16.1", + "react-router": "6.23.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", diff --git a/package.json b/package.json index 2a81dc18..674d0ba1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/layout/layout.tsx b/src/components/layout/layout.tsx new file mode 100644 index 00000000..44357f39 --- /dev/null +++ b/src/components/layout/layout.tsx @@ -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) => { + 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 ( + + + +
+ {activePage === "home" || activePage === "about" ? ( + children + ) : ( + <> + + + + )} + + + ); +}; + +export default Layout; diff --git a/src/data/dcNavLinks.ts b/src/data/dcNavLinks.ts index 425f96cd..da8e3923 100644 --- a/src/data/dcNavLinks.ts +++ b/src/data/dcNavLinks.ts @@ -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", }, { diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index c4493d3e..d4dd8461 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,10 +1,6 @@ // 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"; @@ -12,41 +8,10 @@ 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(() => { @@ -123,16 +88,9 @@ export default function App({ Component, pageProps }: AppProps) { type="text/javascript" src={`/newrelic/` + appConfig.environment + `.js`} /> - + - - + ); } diff --git a/src/pages/about/index.tsx b/src/pages/about/index.tsx index e67b9dd2..923c79e3 100644 --- a/src/pages/about/index.tsx +++ b/src/pages/about/index.tsx @@ -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) { @@ -15,10 +14,7 @@ export default function About() { } return ( - <> - - -
+ About NYPL Digital Collections - + ); } diff --git a/src/pages/breadcrumbsContext.tsx b/src/pages/breadcrumbsContext.tsx new file mode 100644 index 00000000..72ff1090 --- /dev/null +++ b/src/pages/breadcrumbsContext.tsx @@ -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 ( + + {children} + + ); +} + +export default ContextProvider; diff --git a/src/pages/collections/[id].tsx b/src/pages/collections/[id].tsx new file mode 100644 index 00000000..031d72f9 --- /dev/null +++ b/src/pages/collections/[id].tsx @@ -0,0 +1,32 @@ +import Layout from "@/components/layout/layout"; +import { Heading } from "@nypl/design-system-react-components"; + +const Collection = ({ breadcrumbs }) => { + // const { breadcrumbs, setBreadcrumbs } = useContext(breadcrumbsContext); + // useEffect(() => { + // setBreadcrumbs({ text: "A Collection", url: "/collections/collection" }); + // }, []); + return ( + + + <>Collection + + + ); +}; + +export const getServerSideProps = async () => { + // Fetch something + let breadcrumbs = [ + { text: "Home", url: "/" }, + { text: "Collections", url: "/collcetions" }, + { text: "A Collection", url: "/collections/collection" }, + ]; + return { + props: { + breadcrumbs, + }, + }; +}; + +export default Collection; diff --git a/src/pages/collections/index.tsx b/src/pages/collections/index.tsx new file mode 100644 index 00000000..a69a4696 --- /dev/null +++ b/src/pages/collections/index.tsx @@ -0,0 +1,61 @@ +import { useContext, useEffect } from "react"; +import Link from "next/link"; +import Head from "next/head"; +import Layout from "@/components/layout/layout"; +import { + Card, + CardContent, + CardHeading, + Heading, +} from "@nypl/design-system-react-components"; +import { breadcrumbsContext } from "../breadcrumbsContext"; + +export default function Collections() { + const { breadcrumbs, setBreadcrumbs } = useContext(breadcrumbsContext); + useEffect(() => { + setBreadcrumbs({ text: "All Collections", url: "/collections" }); + }, []); + return ( + <> + + All Collections + + + + All Collections + + + I am one collection + + + Go to collection page + + + + + I am another collection + + + Go to collection page + + + + + ); +} diff --git a/src/pages/divisions/[id].tsx b/src/pages/divisions/[id].tsx new file mode 100644 index 00000000..12f996a8 --- /dev/null +++ b/src/pages/divisions/[id].tsx @@ -0,0 +1,58 @@ +import { useContext, useEffect } from "react"; +import Layout from "@/components/layout/layout"; +import { + Card, + CardContent, + CardHeading, + Heading, + Link, +} from "@nypl/design-system-react-components"; +import { breadcrumbsContext } from "../breadcrumbsContext"; + +const Division = () => { + const { breadcrumbs, setBreadcrumbs } = useContext(breadcrumbsContext); + useEffect(() => { + setBreadcrumbs({ text: "A Division", url: "/divisions/division" }); + }, []); + return ( + + + <>Division + + + + I am one collection + + + Go to collection page + + + + + I am division + + + Go to another division + + + + ); +}; + +export default Division; diff --git a/src/pages/divisions/index.tsx b/src/pages/divisions/index.tsx new file mode 100644 index 00000000..d9b0333b --- /dev/null +++ b/src/pages/divisions/index.tsx @@ -0,0 +1,45 @@ +import { useContext, useEffect } from "react"; +import Link from "next/link"; +import Head from "next/head"; +import Layout from "@/components/layout/layout"; +import { + Card, + CardContent, + CardHeading, + Heading, +} from "@nypl/design-system-react-components"; +import { breadcrumbsContext } from "../breadcrumbsContext"; + +export default function Divisions() { + const { breadcrumbs, setBreadcrumbs } = useContext(breadcrumbsContext); + useEffect(() => { + setBreadcrumbs({ text: "All Divisions", url: "/divisions" }); + }, []); + return ( + <> + + All Collections + + + + All Divisions + + + I am one division + + + Go to division page + + + + + ); +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index ca267774..43735187 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,22 +1,18 @@ import CampaignHero from "../components/hero/campaignHero"; -import { - SkipNavigation, - TemplateAppContainer, -} from "@nypl/design-system-react-components"; -import Header from "@/components/header/header"; +import { TemplateAppContainer } from "@nypl/design-system-react-components"; import HomePageMainContent from "@/components/homePageMainContent/homePageMainContent"; import ExploreFurther from "@/components/exploreFurther/exploreFurther"; -import NotificationBanner from "@/components/notificationBanner/notificationBanner"; +import Layout from "@/components/layout/layout"; +import { useContext, useEffect } from "react"; +import { breadcrumbsContext } from "./breadcrumbsContext"; export default function Home(props: any = {}) { + const { breadcrumbs, setBreadcrumbs } = useContext(breadcrumbsContext); + useEffect(() => { + setBreadcrumbs([{ text: "Home", url: "/" }]); + }, []); return ( - <> - - {/** - * * @TODO: Header will need to be pulled into a reusable Layout component (DC Facelift phase 2) - * * Let this be @7emansell 's problem if possible **/} - -
+ @@ -26,6 +22,6 @@ export default function Home(props: any = {}) { contentPrimary={} /> - + ); }