Skip to content

Commit

Permalink
❗️ 404 content (#375)
Browse files Browse the repository at this point in the history
Co-authored-by: Brad Garropy <[email protected]>
  • Loading branch information
bradgarropy and bgarropy-atlassian authored Oct 1, 2023
1 parent a840dd3 commit 81c6a7d
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 35 deletions.
11 changes: 9 additions & 2 deletions src/components/FourOhFour/FourOhFour.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import {render, screen} from "@testing-library/react"

import FourOhFour from "~/components/FourOhFour"
import {mockPostFrontmatter, mockVideos} from "~/test-utils/mocks"

test("shows 404", () => {
render(<FourOhFour />)
expect(screen.getByText("404"))
render(<FourOhFour post={mockPostFrontmatter} videos={mockVideos} />)

expect(screen.getByText("There's nothing here."))
expect(screen.getByText(mockPostFrontmatter.title))

mockVideos.forEach(mockVideo => {
expect(screen.getByAltText(mockVideo.title))
})
})
25 changes: 22 additions & 3 deletions src/components/FourOhFour/FourOhFour.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
import type {FC} from "react"

const FourOhFour: FC = () => {
import LatestVideos from "~/components/LatestVideos"
import PostCard from "~/components/PostCard"
import type {PostFrontmatter} from "~/types/post"
import type {Video} from "~/types/video"

type FourOhFourProps = {
post: PostFrontmatter
videos: Video[]
}

const FourOhFour: FC<FourOhFourProps> = ({post, videos}) => {
return (
<div className="grid place-items-center">
<h1 className="font-heading text-9xl">404</h1>
<div className="grid h-full content-center">
<h1 className="mb-2 font-heading text-6xl font-black tracking-[-0.2rem]">
There&apos;s nothing here.
</h1>

<p className="mb-12">
But maybe you were looking for some of my recent content?
</p>

<PostCard className="mb-4" post={post} />
<LatestVideos latestVideos={videos} />
</div>
)
}
Expand Down
11 changes: 9 additions & 2 deletions src/components/PostCard/PostCard.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import Link from "@bradgarropy/next-link"
import classnames from "classnames"
import type {FC} from "react"

import PostMeta from "~/components/PostMeta"
import type {PostFrontmatter} from "~/types/post"

type PostCardProps = {
post: PostFrontmatter
className?: string
}

const PostCard: FC<PostCardProps> = ({post}) => {
const PostCard: FC<PostCardProps> = ({post, className}) => {
return (
<div className="rounded border-3 border-black px-8 py-8 shadow-box duration-300 hover:shadow-none dark:border-white dark:shadow-box-white hover:dark:shadow-none">
<div
className={classnames(
"rounded border-3 border-black px-8 py-8 shadow-box duration-300 hover:shadow-none dark:border-white dark:shadow-box-white hover:dark:shadow-none",
className,
)}
>
<h1 className="mb-10 font-heading text-3xl font-semibold max-[750px]:text-2xl">
<Link
to={`/blog/${post.slug}`}
Expand Down
27 changes: 25 additions & 2 deletions src/pages/404.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,39 @@
import SEO from "@bradgarropy/next-seo"
import type {GetStaticProps} from "next"
import type {FC} from "react"

import FourOhFour from "~/components/FourOhFour"
import Layout from "~/components/Layout"
import type {PostFrontmatter} from "~/types/post"
import type {Video} from "~/types/video"
import {getLatestPost} from "~/utils/posts"
import {getLatestVideos} from "~/utils/videos"

const NotFoundPage: FC = () => {
type NotFoundPageProps = {
latestPost: PostFrontmatter
latestVideos: Video[]
}

const NotFoundPage: FC<NotFoundPageProps> = ({latestPost, latestVideos}) => {
return (
<Layout>
<SEO title="🤷🏼‍♂️ not found" />
<FourOhFour />
<FourOhFour post={latestPost} videos={latestVideos} />
</Layout>
)
}

const getStaticProps: GetStaticProps = async () => {
const latestPost = getLatestPost()
const latestVideos = await getLatestVideos(2)

return {
props: {
latestPost,
latestVideos,
},
}
}

export default NotFoundPage
export {getStaticProps}
4 changes: 2 additions & 2 deletions src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ const IndexPage: FC<IndexPageProps> = ({
}

const getStaticProps: GetStaticProps = async () => {
const latestPosts = getLatestPosts()
const latestVideos = await getLatestVideos()
const latestPosts = getLatestPosts(3)
const latestVideos = await getLatestVideos(2)
const featuredProjects = await getFeaturedProjects()

return {
Expand Down
33 changes: 32 additions & 1 deletion src/utils/posts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "~/test-utils/mocks"
import {
getAllPosts,
getLatestPost,
getLatestPosts,
getPostBySlug,
getPostsByTag,
Expand All @@ -34,6 +35,21 @@ jest.mock("gatsby-remark-vscode", () => {
}
})

test("gets latest post", () => {
const mockReadDirSync = readdirSync as jest.Mock
mockReadDirSync.mockReturnValue(mockPostsPaths)

const mockMatterRead = matter.read as jest.Mock
mockMatterRead
.mockReturnValueOnce(mockPostsResponse[0])
.mockReturnValueOnce(mockPostsResponse[1])
.mockReturnValueOnce(mockPostsResponse[2])
.mockReturnValueOnce(mockPostsResponse[3])

const post = getLatestPost()
expect(post).toEqual(mockSortedPostsFrontmatter[0])
})

test("gets latest posts", () => {
const mockReadDirSync = readdirSync as jest.Mock
mockReadDirSync.mockReturnValue(mockPostsPaths)
Expand All @@ -45,10 +61,25 @@ test("gets latest posts", () => {
.mockReturnValueOnce(mockPostsResponse[2])
.mockReturnValueOnce(mockPostsResponse[3])

const posts = getLatestPosts()
const posts = getLatestPosts(3)
expect(posts).toEqual(mockSortedPostsFrontmatter.slice(0, 3))
})

test("gets particular number of latest posts", () => {
const mockReadDirSync = readdirSync as jest.Mock
mockReadDirSync.mockReturnValue(mockPostsPaths)

const mockMatterRead = matter.read as jest.Mock
mockMatterRead
.mockReturnValueOnce(mockPostsResponse[0])
.mockReturnValueOnce(mockPostsResponse[1])
.mockReturnValueOnce(mockPostsResponse[2])
.mockReturnValueOnce(mockPostsResponse[3])

const posts = getLatestPosts(2)
expect(posts).toEqual(mockSortedPostsFrontmatter.slice(0, 2))
})

test("gets all posts", () => {
const mockReadDirSync = readdirSync as jest.Mock
mockReadDirSync.mockReturnValue(mockPostsPaths)
Expand Down
30 changes: 11 additions & 19 deletions src/utils/posts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ const icons = {
life: "😎",
}

const getLatestPosts = (): PostFrontmatter[] => {
const getLatestPost = (): PostFrontmatter => {
const latestPosts = getLatestPosts(1)
const latestPost = latestPosts[0]

return latestPost
}

const getLatestPosts = (count?: number): PostFrontmatter[] => {
const postsPath = path.join(process.cwd(), "content/posts")

const posts = fs
Expand All @@ -29,28 +36,12 @@ const getLatestPosts = (): PostFrontmatter[] => {
return [...posts, post]
}, [])

const latestPosts = sortPostsByDate(posts).slice(0, 3)
const latestPosts = sortPostsByDate(posts).slice(0, count)
return latestPosts
}

const getAllPosts = (): PostFrontmatter[] => {
const postsPath = path.join(process.cwd(), "content/posts")

const posts = fs
// read directory of posts
.readdirSync(postsPath)

// create path to each markdown file
// read frontmatter from each post
.reduce<PostFrontmatter[]>((posts, slug) => {
const postPath = path.join(process.cwd(), `content/posts/${slug}`)
const file = matter.read(postPath)
const post = file.data as PostFrontmatter

return [...posts, post]
}, [])

const allPosts = sortPostsByDate(posts)
const allPosts = getLatestPosts()
return allPosts
}

Expand Down Expand Up @@ -211,6 +202,7 @@ const getRelatedPosts = (post: PostFrontmatter) => {

export {
getAllPosts,
getLatestPost,
getLatestPosts,
getPostBySlug,
getPostsByTag,
Expand Down
9 changes: 8 additions & 1 deletion src/utils/videos.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import {
mockYoutubeErrorResponse,
mockYoutubeResponse,
} from "~/test-utils/mocks"
import {getLatestVideos} from "~/utils/videos"
import {getLatestVideo, getLatestVideos} from "~/utils/videos"

jest.mock("@bradgarropy/http")

const mockGet = http.get as jest.Mock

test("gets latest video", async () => {
mockGet.mockResolvedValue(mockYoutubeResponse)

const latestVideo = await getLatestVideo()
expect(latestVideo).toEqual(mockVideos[0])
})

test("gets latest videos", async () => {
mockGet.mockResolvedValue(mockYoutubeResponse)

Expand Down
13 changes: 10 additions & 3 deletions src/utils/videos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,21 @@ type Thumbnail = {
height: number
}

const getLatestVideos = async (): Promise<Video[]> => {
const getLatestVideo = async (): Promise<Video> => {
const latestVideos = await getLatestVideos(1)
const latestVideo = latestVideos[0]

return latestVideo
}

const getLatestVideos = async (count = 2): Promise<Video[]> => {
const response = await http.get<YouTubeSearchResponse>(
"https://www.googleapis.com/youtube/v3/search",
{
params: {
key: process.env.YOUTUBE_API_KEY,
channelId: "UCgbFhcZKt36Upo7oxWlLEig",
maxResults: 2,
maxResults: count,
part: "snippet",
order: "date",
type: "video",
Expand Down Expand Up @@ -91,4 +98,4 @@ const getLatestVideos = async (): Promise<Video[]> => {
return videos
}

export {getLatestVideos}
export {getLatestVideo, getLatestVideos}

1 comment on commit 81c6a7d

@vercel
Copy link

@vercel vercel bot commented on 81c6a7d Oct 1, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.