Skip to content

Commit

Permalink
feat: blog tags page redesign (#546)
Browse files Browse the repository at this point in the history
Co-authored-by: Tushar Mathur <[email protected]>
  • Loading branch information
mehulmathur16 and tusharmath authored Nov 2, 2024
1 parent 112f3db commit 9044d58
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 0 deletions.
116 changes: 116 additions & 0 deletions src/components/blog/TagSelectionModal/TagSelectionModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React, {useEffect, useMemo, useState} from "react"
import {blogTagsMapping} from "@site/src/constants"
import clsx from "clsx"
import {X, Search} from "lucide-react"
import Link from "@docusaurus/Link"
import styles from "./styles.module.css"

interface TagSelectionModalProps {
open: boolean
onClose?: () => void
}

const TagSelectionModal: React.FC<TagSelectionModalProps> = ({open, onClose}) => {
const [query, setQuery] = useState("")

useEffect(() => {
if (typeof window === "undefined") return

if (open) {
document.body.style.overflow = "hidden"
} else {
document.body.style.overflow = "visible"
}
}, [open])

const getSearchResults = () => {
const results: Record<string, BlogTag[]> = {}
const lowerCaseQuery = query.toLowerCase()

for (const [category, tags] of Object.entries(blogTagsMapping)) {
const matches = tags.filter((tag) => tag.label.toLowerCase().startsWith(lowerCaseQuery))
if (matches.length) {
results[category] = matches
}
}

return results
}

const handleModalClose = () => {
setQuery("")

if (onClose) {
onClose()
}
}

const handleQueryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value)
}

const searchResults = useMemo(() => {
return getSearchResults()
}, [query])

return (
<>
{open ? (
<>
{/* Overlay */}
<div
className={clsx("block lg:hidden fixed inset-0 bg-black bg-opacity-50", styles.modalOverlay)}
onClick={handleModalClose}
></div>

{/* Modal Container */}
<div
className={clsx(
"absolute w-full lg:w-4/12 h-full overflow-scroll right-0 bg-white rounded-xl lg:rounded-none lg:border lg:border-solid lg:border-tailCall-border-light-500 px-4 py-8 lg:px-10 lg:py-8 flex flex-col gap-8",
styles.modalContainer,
)}
>
<div className="flex items-center justify-between">
<span className="text-title-medium lg:text-title-large text-black">Explore All Tags</span>
<X width={24} height={24} className="cursor-pointer" onClick={handleModalClose} />
</div>
<div className="flex flex-col gap-5 pb-36">
<div className="flex items-center gap-3 border border-solid border-tailCall-border-light-500 rounded-lg py-3 px-6">
<Search width={20} height={20} className="text-tailCall-light-500" />
<input
name="tag"
type="text"
value={query}
onChange={handleQueryChange}
placeholder="Search Tags"
className="text-black placeholder:text-tailCall-light-500 border-none outline-none text-content-small"
/>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-x-8 gap-y-8 lg:gap-y-10">
{Object.keys(searchResults).map((category: string) => {
return (
<div className="flex flex-col gap-3 lg:gap-4" key={category}>
<span className="text-title-tiny lg:text-title-small text-black">{category}</span>
{searchResults?.[category]?.map((tag: BlogTag) => (
<Link
key={tag.label}
to={tag.permalink}
onClick={handleModalClose}
className="text-content-small text-black px-3 py-1 border border-solid border-tailCall-border-light-500 rounded-3xl w-fit cursor-pointer hover:no-underline"
>
{tag.label}
</Link>
))}
</div>
)
})}
</div>
</div>
</div>
</>
) : null}
</>
)
}

export default TagSelectionModal
9 changes: 9 additions & 0 deletions src/components/blog/TagSelectionModal/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.modalContainer {
z-index: calc(var(--ifm-z-index-fixed) + 2);
top: var(--ifm-navbar-height);
box-shadow: -56px 32px 48px 0px rgba(0, 0, 0, 0.08);
}

.modalOverlay {
z-index: calc(var(--ifm-z-index-fixed) + 1);
}
41 changes: 41 additions & 0 deletions src/constants/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,44 @@ export const testimonials: CustomerFeedback[] = [
department: "Ops",
},
]

export const blogTagsMapping: Record<string, BlogTag[]> = {
"Technologies and Frameworks": [
{label: "Angular", permalink: "/blog/tags/angular"},
{label: "Apollo client", permalink: "/blog/tags/apollo-client"},
{label: "JavaScript", permalink: "/blog/tags/java-script"},
{label: "Node.js", permalink: "/blog/tags/node-js"},
{label: "URQL", permalink: "/blog/tags/urql"},
{label: "Villus", permalink: "/blog/tags/villus"},
{label: "Vue", permalink: "/blog/tags/vue"},
],
"Debugging and Tooling": [
{label: "debugging", permalink: "/blog/tags/debugging"},
{label: "IDE", permalink: "/blog/tags/ide"},
{label: "Tooling", permalink: "/blog/tags/tooling"},
],
"API Concepts and Tools": [
{label: "API", permalink: "/blog/tags/api"},
{label: "Fetch API", permalink: "/blog/tags/fetch-api"},
{label: "GraphiQL", permalink: "/blog/tags/graphi-ql"},
{label: "GraphQL", permalink: "/blog/tags/graph-ql"},
{label: "OpenAPI", permalink: "/blog/tags/open-api"},
{label: "Microservice", permalink: "/blog/tags/microservice"},
{label: "Microservices", permalink: "/blog/tags/microservices"},
{label: "Backend-for-Frontend", permalink: "/blog/tags/backend-for-frontend"},
],
"Schema and Introspection": [
{label: "Introspection", permalink: "/blog/tags/introspection"},
{label: "Schema", permalink: "/blog/tags/schema"},
],
"General Terms": [
{label: "Performance", permalink: "/blog/tags/performance"},
{label: "Scalability", permalink: "/blog/tags/scalability"},
{label: "Security", permalink: "/blog/tags/security"},
{label: "Strategy", permalink: "/blog/tags/strategy"},
{label: "Migration", permalink: "/blog/tags/migration"},
{label: "Design", permalink: "/blog/tags/design"},
{label: "Flexibility", permalink: "/blog/tags/flexibility"},
],
"Development Practices": [{label: "Best Practices", permalink: "/blog/tags/best-practices"}],
}
95 changes: 95 additions & 0 deletions src/theme/BlogTagsPostsPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, {useCallback, useState} from "react"
import clsx from "clsx"
import {translate} from "@docusaurus/Translate"
import {PageMetadata, HtmlClassNameProvider, ThemeClassNames, usePluralForm} from "@docusaurus/theme-common"
import SearchMetadata from "@theme/SearchMetadata"
import type {Props} from "@theme/BlogTagsPostsPage"
import BlogPostList from "../BlogPostList"
import Layout from "@theme/Layout"
import TagSelectionModal from "@site/src/components/blog/TagSelectionModal/TagSelectionModal"

// Very simple pluralization: probably good enough for now
function useBlogPostsPlural() {
const {selectMessage} = usePluralForm()
return (count: number) =>
selectMessage(
count,
translate(
{
id: "theme.blog.post.plurals",
description:
'Pluralized label for "{count} posts". Use as much plural forms (separated by "|") as your language support (see https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)',
message: "One post|{count} posts",
},
{count},
),
)
}

function useBlogTagsPostsPageTitle(tag: Props["tag"]): string {
const blogPostsPlural = useBlogPostsPlural()
return translate(
{
id: "theme.blog.tagTitle",
description: "The title of the page for a blog tag",
message: '{nPosts} tagged with "{tagName}"',
},
{nPosts: blogPostsPlural(tag.count), tagName: tag.label},
)
}

function BlogTagsPostsPageMetadata({tag}: Props): JSX.Element {
const title = useBlogTagsPostsPageTitle(tag)
return (
<>
<PageMetadata title={title} description={tag.description} />
<SearchMetadata tag="blog_tags_posts" />
</>
)
}

function BlogTagsPostsPageContent({tag, items}: Props): JSX.Element {
const [showTagsModal, setShowTagsModal] = useState(false)

const openTagSelectionModal = useCallback(() => {
setShowTagsModal(true)
}, [])

const closeTagSelectionModal = useCallback(() => {
setShowTagsModal(false)
}, [])

return (
<Layout>
<div className="container mx-auto mt-3 mb-10 md:my-8 px-4 md:w-8/12">
<div className="flex flex-col md:flex-row gap-2 mb-5">
<span className="text-title-medium text-tailCall-light-600">Results for</span>
<span className="flex items-center justify-between flex-1">
<span className="text-content-small px-3 py-1 text-tailCall-dark-100 rounded-full bg-tailCall-light-200">
{tag.label}
</span>
<span
className="text-content-small text-tailCall-dark-500 underline cursor-pointer"
onClick={openTagSelectionModal}
>
See all Tags
</span>
</span>
</div>
<BlogPostList items={items} />
</div>
<TagSelectionModal open={showTagsModal} onClose={closeTagSelectionModal} />
</Layout>
)
}

export default function BlogTagsPostsPage(props: Props): JSX.Element {
return (
<HtmlClassNameProvider
className={clsx(ThemeClassNames.wrapper.blogPages, ThemeClassNames.page.blogTagPostListPage)}
>
<BlogTagsPostsPageMetadata {...props} />
<BlogTagsPostsPageContent {...props} />
</HtmlClassNameProvider>
)
}
5 changes: 5 additions & 0 deletions src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ type CustomerFeedback = {
department?: string
}

type BlogTag = {
label: string
permalink: string
}

declare module "docusaurus-lunr-search/src/theme/SearchBar"
declare module "react-platform-js"

Expand Down

0 comments on commit 9044d58

Please sign in to comment.