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

Add nested links to mobile nav #1973

Merged
merged 6 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 20 additions & 12 deletions src/components/Header/Nav/ProductNavigation/Mobile/Category.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from "react"
import { ProductItem } from "../../config"
import { ProductItem, SubProducts, SubProductItem } from "../../config"
import { clsx } from "../../utils"
import { CaretRightIcon } from "./CaretRightIcon"
import styles from "./category.module.css"
import { SubProducts } from "./ProductNavigation"

type ListItemProps = {
item: ProductItem
Expand All @@ -20,20 +19,29 @@ const Item = React.forwardRef<HTMLAnchorElement, ListItemProps>(
</span>
</>
)

const handleProductClick = () => {
const subProductItems = subProducts as unknown as SubProductItem[]
const mappedSubProducts: SubProducts = {
label,
items: subProductItems.map((subProductItem) => ({
label: subProductItem.label,
href: subProductItem.href || "#",
pages: subProductItem.items.map((item) => ({
label: item.label,
href: item.href || "/",
children: item.children || [],
})),
})),
}
onProductClick(mappedSubProducts)
}

return subProducts ? (
<button
className={clsx(styles.link, "product-link")}
style={{ marginTop: "var(--space-0x)" }}
onClick={() =>
onProductClick({
...subProducts,
items:
subProducts.items?.map((item) => ({
...item,
href: item.href || "/",
})) || [],
})
}
onClick={handleProductClick}
data-testid="sub-product-navigation-trigger-mobile"
>
{itemComponent}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ProductsNav } from "../../config"
import { ProductsNav, SubProducts } from "../../config"
import { Category } from "./Category"
import { SubProducts } from "./ProductNavigation"

type Props = {
onProductClick: (subProducts: SubProducts) => void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import * as Dialog from "@radix-ui/react-dialog"
import React, { useEffect } from "react"
import { ProductsNav } from "../../config"
import { ProductsNav, SubProducts } from "../../config"
import { SearchTrigger } from "../../NavBar"
import { isMatchedPath } from "../../isMatchedPath"
import { clsx } from "../../utils"
import { CaretIcon } from "../CaretIcon"
import { extendRadixComponent } from "../extendRadixComponent"
import { BottomBar } from "./BottomBar"
import { ProductContent } from "./ProductContent"
import styles from "./productNavigation.module.css"
import { SubProductContent } from "./SubProductContent"

type Page = {
label: string
href: string
}

export type SubProducts = {
label: string
items: { label: string; href: string; pages?: Page[] }[]
}
import styles from "./productNavigation.module.css"

type Props = {
searchTrigger?: SearchTrigger
Expand All @@ -36,7 +26,7 @@ export function ProductNavigation({ productsNav, path }: Props) {
const [open, setOpen] = React.useState(false)
const [subProducts, setSubProducts] = React.useState<SubProducts | undefined>(undefined)
const [showSearch, setShowSearch] = React.useState(false)
const [producsSlidePosition, setProductsSlidePosition] = React.useState<"main" | "submenu">("main")
const [productsSlidePosition, setProductsSlidePosition] = React.useState<"main" | "submenu">("main")
const closeButtonRef = React.useRef(null)

useEffect(() => {
Expand All @@ -47,23 +37,27 @@ export function ProductNavigation({ productsNav, path }: Props) {
if (foundSubProduct) {
const subProduct = foundSubProduct.items.find((item) => item.subProducts && isMatchedPath(path, item.href))

if (subProduct?.subProducts?.items) {
const safeSubProducts: SubProducts = {
label: subProduct.subProducts.label,
items: subProduct.subProducts.items.map((item) => ({
label: item.label,
href: item.href || "#",
pages:
item.pages?.map((page) => ({
label: page.label,
href: page.href,
})) || [],
if (subProduct?.subProducts && Array.isArray(subProduct.subProducts)) {
const items = subProduct.subProducts.map((subProductItem) => ({
label: subProductItem.label,
href: "#",
pages: subProductItem.items.map((page) => ({
label: page.label,
href: page.href,
children: page.children || [],
})),
}))

const safeSubProducts: SubProducts = {
label: subProduct.label,
items,
}

setSubProducts(safeSubProducts)
setProductsSlidePosition("submenu")
}
} else {
setSubProducts(undefined)
}
}, [path, productsNav])

Expand All @@ -74,13 +68,15 @@ export function ProductNavigation({ productsNav, path }: Props) {

const onSubproductClick = () => {
setProductsSlidePosition("main")
setSubProducts(undefined)
}

const handleOpenChange = (newOpenState: boolean) => {
setOpen(newOpenState)
if (!newOpenState) {
setProductsSlidePosition("main")
setShowSearch(false)
setSubProducts(undefined)
}
}

Expand Down Expand Up @@ -114,7 +110,7 @@ export function ProductNavigation({ productsNav, path }: Props) {
overflow: "hidden",
}}
>
<div className={clsx(styles.content, styles[producsSlidePosition])}>
<div className={clsx(styles.content, styles[productsSlidePosition])}>
<ul className={clsx(styles.productContent)}>
<ProductContent onProductClick={onProductClick} productsNav={productsNav} />
</ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import { clsx } from "../../utils"
import React, { useEffect, useRef } from "react"
import { BackArrowIcon } from "./BackArrowIcon"
import { Page } from "../../config"
import styles from "./subProductContent.module.css"

type Page = {
label: string
href: string
}

type Props = {
onSubproductClick: () => void
subProducts?: {
Expand All @@ -16,8 +12,41 @@ type Props = {
currentPath: string
}

const renderPages = (pages: Page[], currentPath: string, indent: boolean) => {
return pages.map(({ label, href, children }) => {
const adjustedHref = "/" + href
const isActive = currentPath.replace(/\/$/, "") === adjustedHref.replace(/\/$/, "")

const linkRef = useRef<HTMLAnchorElement>(null)

useEffect(() => {
if (isActive && linkRef.current) {
linkRef.current.scrollIntoView({ behavior: "smooth", block: "center" })
}
}, [isActive])

const linkStyle = {
backgroundColor: isActive ? "var(--blue-100)" : "transparent",
color: isActive ? "var(--blue-600)" : "inherit",
fontWeight: isActive ? "500" : "normal",
marginLeft: indent ? "20px" : "0",
}

return (
<React.Fragment key={label}>
<a ref={linkRef} style={linkStyle} className={`${styles.link} subproduct-link`} href={adjustedHref}>
{label}
</a>
{children && renderPages(children, currentPath, true)}
</React.Fragment>
)
})
}

export const SubProductContent = ({ subProducts, onSubproductClick, currentPath }: Props) => {
if (!subProducts) return null
if (!subProducts) {
return null
}

return (
<>
Expand All @@ -28,22 +57,7 @@ export const SubProductContent = ({ subProducts, onSubproductClick, currentPath
{subProducts.items.map(({ label, pages }) => (
<div key={label}>
<h3 className={styles.section}>{label}</h3>
{pages?.map(({ label, href }) => {
const adjustedHref = "/" + href
const isActive = currentPath.replace(/\/$/, "") === adjustedHref.replace(/\/$/, "")

const linkStyle = {
backgroundColor: isActive ? "var(--blue-100)" : "transparent",
color: isActive ? "var(--blue-600)" : "inherit",
fontWeight: isActive ? "500" : "normal",
}

return (
<a key={label} style={linkStyle} className={`${styles.link} subproduct-link`} href={adjustedHref}>
{label}
</a>
)
})}
{pages && renderPages(pages, currentPath, false)}
</div>
))}
</>
Expand Down
26 changes: 20 additions & 6 deletions src/components/Header/Nav/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@ type LinksConfig = {
actionButton?: { label: string; href: string }
}

export type Item = { label: string; icon?: string; href: string }

export type Page = {
label: string
href: string
children?: Page[]
}

export type ProductItem = Item & {
subProducts?: {
export type Item = { label: string; icon?: string; href: string; children?: Page[] }

export type SubProductItem = {
label: string
href?: string
items: {
label: string
items?: { label: string; href?: string; pages?: Page[] }[]
}
href: string
children?: Page[]
}[]
}

export type ProductItem = Item & {
subProducts?: SubProductItem[]
}

export type ProductsNav = {
Expand All @@ -26,11 +34,17 @@ export type ProductsNav = {
}[]
}

export type SubProducts = {
label: string
items: { label: string; href: string; pages?: Page[] }[]
}

export type SubProductsNavItem = {
label: string
icon?: string
href: string
hideFromDropdown?: boolean
pages?: Page[]
}

export type SubProductsNav = SubProductsNavItem[]
Expand Down
Loading