Skip to content

Commit

Permalink
feat: add navigation to shopper store context
Browse files Browse the repository at this point in the history
  • Loading branch information
field123 committed Oct 24, 2023
1 parent e8121e9 commit 328517c
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 12 deletions.
6 changes: 5 additions & 1 deletion packages/react-shopper-hooks/lib/store/store-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { emitter } from "@lib/event/event-context"
import { CartProvider } from "@lib/cart"
import { createContext, Dispatch, SetStateAction, useState } from "react"
import type { Moltin as EPCCClient } from "@moltin/sdk"
import { NavigationNode } from "@elasticpath/shopper-common"

interface StoreState {
client: EPCCClient
setClient: Dispatch<SetStateAction<EPCCClient>>
nav?: NavigationNode[]
}

export const StoreProviderContext = createContext<StoreState | null>(null)
Expand All @@ -22,7 +24,9 @@ export const StoreProvider = ({
const [client, setClient] = useState(initialClient)

return (
<StoreProviderContext.Provider value={{ client, setClient }}>
<StoreProviderContext.Provider
value={{ client, setClient, nav: storeContext?.nav }}
>
<PGRProvider>
<CartProvider
cart={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import {
Cart,
CartIncluded,
Moltin as EPCCClient,
ResourceIncluded
ResourceIncluded,
} from "@moltin/sdk"
import { ReactNode } from "react"
import { NavigationNode } from "@elasticpath/shopper-common"

export interface StoreProviderProps {
storeContext?: StoreContext
Expand All @@ -13,14 +14,6 @@ export interface StoreProviderProps {
resolveCartId: () => string
}

export interface NavigationNode {
name: string
slug: string
href: string
id: string
children: NavigationNode[]
}

interface StoreContextBase {
nav: NavigationNode[]
}
Expand Down
4 changes: 3 additions & 1 deletion packages/react-shopper-hooks/lib/store/use-store.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useContext } from "react"
import { StoreProviderContext } from "@lib/store/store-provider"
import { Moltin } from "@moltin/sdk"
import { NavigationNode } from "@elasticpath/shopper-common"

export function useStore(): { client: Moltin } {
export function useStore(): { client: Moltin; nav?: NavigationNode[] } {
const ctx = useContext(StoreProviderContext)

if (!ctx) {
Expand All @@ -13,5 +14,6 @@ export function useStore(): { client: Moltin } {

return {
client: ctx.client,
nav: ctx.nav,
}
}
3 changes: 2 additions & 1 deletion packages/shopper-common/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./products"
export * from "./shared"
export * from "./shared"
export * from "./navigation"
89 changes: 89 additions & 0 deletions packages/shopper-common/src/navigation/build-navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { Hierarchy, Moltin as EPCCClient } from "@moltin/sdk"
import {
getHierarchies,
getHierarchyChildren,
getHierarchyNodes,
} from "./services/hierarchy"
import { ISchema, NavigationNode } from "./navigation-types"

export async function buildSiteNavigation(
client: EPCCClient,
): Promise<NavigationNode[]> {
// Fetch hierarchies to be used as top level nav
const hierarchies = await getHierarchies(client)
return constructTree(hierarchies, client)
}

/**
* Construct hierarchy tree, limited to 5 hierarchies at the top level
*/
function constructTree(
hierarchies: Hierarchy[],
client: EPCCClient,
): Promise<NavigationNode[]> {
const tree = hierarchies
.slice(0, 4)
.map((hierarchy) =>
createNode({
name: hierarchy.attributes.name,
id: hierarchy.id,
slug: hierarchy.attributes.slug,
}),
)
.map(async (hierarchy) => {
// Fetch first-level nav ('parent nodes') - the direct children of each hierarchy
const directChildren = await getHierarchyChildren(hierarchy.id, client)
// Fetch all nodes in each hierarchy (i.e. all 'child nodes' belonging to a hierarchy)
const allNodes = await getHierarchyNodes(hierarchy.id, client)

// Build 2nd level by finding all 'child nodes' belonging to each first level featured-nodes
const directs = directChildren.slice(0, 4).map((child) => {
const children: ISchema[] = allNodes
.filter((node) => node?.relationships?.parent.data.id === child.id)
.map((node) =>
createNode({
name: node.attributes.name,
id: node.id,
slug: node.attributes.slug,
hrefBase: `${hierarchy.href}/${child.attributes.slug}`,
}),
)

return createNode({
name: child.attributes.name,
id: child.id,
slug: child.attributes.slug,
hrefBase: hierarchy.href,
children,
})
})

return { ...hierarchy, children: directs }
})

return Promise.all(tree)
}

interface CreateNodeDefinition {
name: string
id: string
slug?: string
hrefBase?: string
children?: ISchema[]
}

function createNode({
name,
id,
slug = "missing-slug",
hrefBase = "",
children = [],
}: CreateNodeDefinition): ISchema {
return {
name,
id,
slug,
href: `${hrefBase}/${slug}`,
children,
}
}
2 changes: 2 additions & 0 deletions packages/shopper-common/src/navigation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./build-navigation"
export * from "./navigation-types"
15 changes: 15 additions & 0 deletions packages/shopper-common/src/navigation/navigation-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface ISchema {
name: string
slug: string
href: string
id: string
children: ISchema[]
}

export interface NavigationNode {
name: string
slug: string
href: string
id: string
children: NavigationNode[]
}
28 changes: 28 additions & 0 deletions packages/shopper-common/src/navigation/services/hierarchy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Node, Hierarchy } from "@moltin/sdk"
import { Moltin as EPCCClient } from "@moltin/sdk"

export async function getHierarchies(client: EPCCClient): Promise<Hierarchy[]> {
const result = await client.ShopperCatalog.Hierarchies.All()
return result.data
}

export async function getHierarchyChildren(
hierarchyId: string,
client: EPCCClient,
): Promise<Node[]> {
const result = await client.ShopperCatalog.Hierarchies.GetHierarchyChildren({
hierarchyId,
})
return result.data
}

export async function getHierarchyNodes(
hierarchyId: string,
client: EPCCClient,
): Promise<Node[]> {
const result = await client.ShopperCatalog.Hierarchies.GetHierarchyNodes({
hierarchyId,
})

return result.data
}

0 comments on commit 328517c

Please sign in to comment.