From 328517ce50e0d3a0334d3a789c8e72d0eee68394 Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 24 Oct 2023 17:16:29 +0200 Subject: [PATCH 1/8] feat: add navigation to shopper store context --- .../lib/store/store-provider.tsx | 6 +- .../lib/store/types/store-context-types.ts | 11 +-- .../lib/store/use-store.tsx | 4 +- packages/shopper-common/src/index.ts | 3 +- .../src/navigation/build-navigation.ts | 89 +++++++++++++++++++ .../shopper-common/src/navigation/index.ts | 2 + .../src/navigation/navigation-types.ts | 15 ++++ .../src/navigation/services/hierarchy.ts | 28 ++++++ 8 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 packages/shopper-common/src/navigation/build-navigation.ts create mode 100644 packages/shopper-common/src/navigation/index.ts create mode 100644 packages/shopper-common/src/navigation/navigation-types.ts create mode 100644 packages/shopper-common/src/navigation/services/hierarchy.ts diff --git a/packages/react-shopper-hooks/lib/store/store-provider.tsx b/packages/react-shopper-hooks/lib/store/store-provider.tsx index 37056270..ea8c83c9 100644 --- a/packages/react-shopper-hooks/lib/store/store-provider.tsx +++ b/packages/react-shopper-hooks/lib/store/store-provider.tsx @@ -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> + nav?: NavigationNode[] } export const StoreProviderContext = createContext(null) @@ -22,7 +24,9 @@ export const StoreProvider = ({ const [client, setClient] = useState(initialClient) return ( - + string } -export interface NavigationNode { - name: string - slug: string - href: string - id: string - children: NavigationNode[] -} - interface StoreContextBase { nav: NavigationNode[] } diff --git a/packages/react-shopper-hooks/lib/store/use-store.tsx b/packages/react-shopper-hooks/lib/store/use-store.tsx index ce90976f..53dc9180 100644 --- a/packages/react-shopper-hooks/lib/store/use-store.tsx +++ b/packages/react-shopper-hooks/lib/store/use-store.tsx @@ -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) { @@ -13,5 +14,6 @@ export function useStore(): { client: Moltin } { return { client: ctx.client, + nav: ctx.nav, } } diff --git a/packages/shopper-common/src/index.ts b/packages/shopper-common/src/index.ts index e7c7e32f..7e9d9ac8 100644 --- a/packages/shopper-common/src/index.ts +++ b/packages/shopper-common/src/index.ts @@ -1,2 +1,3 @@ export * from "./products" -export * from "./shared" \ No newline at end of file +export * from "./shared" +export * from "./navigation" diff --git a/packages/shopper-common/src/navigation/build-navigation.ts b/packages/shopper-common/src/navigation/build-navigation.ts new file mode 100644 index 00000000..e5850922 --- /dev/null +++ b/packages/shopper-common/src/navigation/build-navigation.ts @@ -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 { + // 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 { + 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, + } +} diff --git a/packages/shopper-common/src/navigation/index.ts b/packages/shopper-common/src/navigation/index.ts new file mode 100644 index 00000000..63f5a445 --- /dev/null +++ b/packages/shopper-common/src/navigation/index.ts @@ -0,0 +1,2 @@ +export * from "./build-navigation" +export * from "./navigation-types" diff --git a/packages/shopper-common/src/navigation/navigation-types.ts b/packages/shopper-common/src/navigation/navigation-types.ts new file mode 100644 index 00000000..502030f8 --- /dev/null +++ b/packages/shopper-common/src/navigation/navigation-types.ts @@ -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[] +} diff --git a/packages/shopper-common/src/navigation/services/hierarchy.ts b/packages/shopper-common/src/navigation/services/hierarchy.ts new file mode 100644 index 00000000..b3d7b6a9 --- /dev/null +++ b/packages/shopper-common/src/navigation/services/hierarchy.ts @@ -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 { + const result = await client.ShopperCatalog.Hierarchies.All() + return result.data +} + +export async function getHierarchyChildren( + hierarchyId: string, + client: EPCCClient, +): Promise { + const result = await client.ShopperCatalog.Hierarchies.GetHierarchyChildren({ + hierarchyId, + }) + return result.data +} + +export async function getHierarchyNodes( + hierarchyId: string, + client: EPCCClient, +): Promise { + const result = await client.ShopperCatalog.Hierarchies.GetHierarchyNodes({ + hierarchyId, + }) + + return result.data +} From 80d758371b871ed4966f02fbf2d7cad2a9f175bc Mon Sep 17 00:00:00 2001 From: Robert Field Date: Tue, 24 Oct 2023 17:17:02 +0200 Subject: [PATCH 2/8] chore: changeset --- .changeset/tough-bugs-run.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/tough-bugs-run.md diff --git a/.changeset/tough-bugs-run.md b/.changeset/tough-bugs-run.md new file mode 100644 index 00000000..3d94f993 --- /dev/null +++ b/.changeset/tough-bugs-run.md @@ -0,0 +1,6 @@ +--- +"@elasticpath/react-shopper-hooks": minor +"@elasticpath/shopper-common": minor +--- + +add navigation to shopper store context From c29e09b73f17e617c39d5368b24d9389c1c92bca Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:20:40 +0000 Subject: [PATCH 3/8] chore: generated latest examples --- examples/algolia/package.json | 2 +- examples/basic/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/algolia/package.json b/examples/algolia/package.json index acd9db51..c4b6a963 100644 --- a/examples/algolia/package.json +++ b/examples/algolia/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@algolia/react-instantsearch-widget-color-refinement-list": "^1.4.7", - "@elasticpath/react-shopper-hooks": "^0.4.0", + "@elasticpath/react-shopper-hooks": "^0.3.2", "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.0.18", "@moltin/sdk": "^25.0.2", diff --git a/examples/basic/package.json b/examples/basic/package.json index 9d35209f..c02d3770 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -20,7 +20,7 @@ "start:e2e": "NODE_ENV=test next start" }, "dependencies": { - "@elasticpath/react-shopper-hooks": "^0.4.0", + "@elasticpath/react-shopper-hooks": "^0.3.2", "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.0.18", "@moltin/sdk": "^25.0.2", From 4ed4e9f2c8ac1ec715339676b1c266de3c8a91f6 Mon Sep 17 00:00:00 2001 From: Robert Field Date: Wed, 25 Oct 2023 16:42:06 +0200 Subject: [PATCH 4/8] build: migrated from yarn to pnpm - Having issue managing the yarn workspace moved to pnpm --- .github/workflows/generate-examples.yml | 23 +- .github/workflows/release.yml | 13 +- examples/algolia/package.json | 25 +- .../src/components/search/MobileFilters.tsx | 3 - .../src/components/search/SearchModal.tsx | 3 - .../src/components/search/SearchPage.tsx | 3 - examples/basic/package.json | 2 +- package.json | 23 +- packages/composable-cli/package.json | 13 +- .../integration-hub/create-trpc-client.ts | 6 +- .../src/lib/detect-package-manager.ts | 2 +- packages/composable-common/package.json | 8 +- packages/composable-common/tsconfig.json | 1 + .../package.json | 9 +- packages/d2c-schematics/package.json | 5 +- .../search/MobileFilters.tsx.template | 3 - .../search/SearchModal.tsx.template | 3 - .../components/search/SearchPage.tsx.template | 3 - .../product-list-page-algolia/index.ts | 12 + .../d2c-schematics/utility/latest-versions.ts | 6 + .../utility/latest-versions/package.json | 3 +- .../workspace/files/package.json.template | 2 +- packages/d2c-schematics/workspace/index.ts | 6 +- packages/shopper-common/package.json | 7 +- pnpm-lock.yaml | 21955 ++++++++++++++++ pnpm-workspace.yaml | 3 + yarn.lock | 19149 -------------- 27 files changed, 22072 insertions(+), 19219 deletions(-) create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml delete mode 100644 yarn.lock diff --git a/.github/workflows/generate-examples.yml b/.github/workflows/generate-examples.yml index 527056c2..fb8a076a 100644 --- a/.github/workflows/generate-examples.yml +++ b/.github/workflows/generate-examples.yml @@ -32,10 +32,15 @@ jobs: - name: Checkout Repo uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + with: + version: 8 + - name: Setup Node.js 16.x uses: actions/setup-node@v3 with: node-version: 16.x + cache: pnpm - name: Make .env.examples file (used in examples configuration.ts) run: | @@ -48,13 +53,13 @@ jobs: cat ./packages/composable-cli/.env - name: Install Dependencies - run: yarn install + run: pnpm install - name: Build schematic schema types - run: yarn generate + run: pnpm generate - name: Build packages/* - run: yarn build:packages + run: pnpm build:packages - name: Report "Run packages unit and Integration tests" starting uses: dflydev/check-runs-action@v1 @@ -64,7 +69,7 @@ jobs: status: in_progress - name: Run packages unit and Integration tests - run: yarn test:packages + run: pnpm test:packages id: packages-unit-int-tests continue-on-error: true @@ -86,7 +91,7 @@ jobs: - name: Generate latest examples using script id: generate-examples - run: yarn scaffold:local + run: pnpm scaffold:local continue-on-error: true - name: Report "Generate latest examples using script" conclusion @@ -109,7 +114,7 @@ jobs: add: '*' - name: Install additional dependencies - run: yarn install + run: pnpm install - name: Report "Run examples unit/integration tests" starting uses: dflydev/check-runs-action@v1 @@ -120,7 +125,7 @@ jobs: - name: Run examples unit/integration tests id: examples-unit-int-tests - run: yarn test + run: pnpm test continue-on-error: true - name: Report "Run examples unit/integration tests" conclusion @@ -139,7 +144,7 @@ jobs: echo NEXT_PUBLIC_CI=true >> ./examples/basic/.env.test - name: Build everything - run: yarn build:e2e + run: pnpm build:e2e - name: Install playwright browsers run: npx playwright install --with-deps @@ -152,7 +157,7 @@ jobs: status: in_progress - name: Run e2e tests for latest examples - run: yarn test:e2e + run: pnpm test:e2e id: examples-e2e-tests continue-on-error: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7f1fb0d..813ed36e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,12 +13,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@v2 + uses: actions/checkout@v3 + + - uses: pnpm/action-setup@v2 + with: + version: 8 - name: Setup Node.js 16.x - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: 16.x + cache: 'pnpm' - name: Make .env file for composable-cli package run: | @@ -27,14 +32,14 @@ jobs: cat ./packages/composable-cli/.env - name: Install Dependencies - run: yarn + run: pnpm install - name: Create Release Pull Request or Publish to npm id: changesets uses: changesets/action@v1 with: # This expects you to have a script called release which does a build for your packages and calls changeset publish - publish: yarn release + publish: pnpm release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/examples/algolia/package.json b/examples/algolia/package.json index c4b6a963..2ca0d349 100644 --- a/examples/algolia/package.json +++ b/examples/algolia/package.json @@ -21,7 +21,7 @@ }, "dependencies": { "@algolia/react-instantsearch-widget-color-refinement-list": "^1.4.7", - "@elasticpath/react-shopper-hooks": "^0.3.2", + "@elasticpath/react-shopper-hooks": "0.4.0", "@headlessui/react": "^1.7.17", "@heroicons/react": "^2.0.18", "@moltin/sdk": "^25.0.2", @@ -46,30 +46,31 @@ "devDependencies": { "@babel/core": "^7.18.10", "@next/bundle-analyzer": "13.0.4", + "@playwright/test": "^1.28.1", "@svgr/webpack": "^6.3.1", + "@testing-library/jest-dom": "^6.1.3", + "@testing-library/react": "^14.0.0", "@types/node": "18.7.3", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", + "@types/react-toastify": "^4.1.0", + "@vitest/coverage-istanbul": "^0.34.5", + "autoprefixer": "^10.4.14", "babel-loader": "^8.2.5", "eslint": "^8.49.0", "eslint-config-next": "^13.5.2", "eslint-config-prettier": "^9.0.0", "eslint-plugin-react": "^7.33.2", - "vite": "^4.2.1", - "vitest": "^0.34.5", - "@vitest/coverage-istanbul": "^0.34.5", - "@testing-library/jest-dom": "^6.1.3", - "@testing-library/react": "^14.0.0", - "@playwright/test": "^1.28.1", + "instantsearch.js": "4.56.8", "lint-staged": "^13.0.3", + "postcss": "^8.4.30", "prettier": "^3.0.3", "prettier-eslint": "^15.0.1", "prettier-eslint-cli": "^7.1.0", - "typescript": "^5.2.2", - "tailwindcss": "^3.3.3", - "autoprefixer": "^10.4.14", - "postcss": "^8.4.30", "prettier-plugin-tailwindcss": "^0.5.4", - "@types/react-toastify": "^4.1.0" + "tailwindcss": "^3.3.3", + "typescript": "^5.2.2", + "vite": "^4.2.1", + "vitest": "^0.34.5" } } \ No newline at end of file diff --git a/examples/algolia/src/components/search/MobileFilters.tsx b/examples/algolia/src/components/search/MobileFilters.tsx index 9cd980fa..6c563eaa 100644 --- a/examples/algolia/src/components/search/MobileFilters.tsx +++ b/examples/algolia/src/components/search/MobileFilters.tsx @@ -86,9 +86,6 @@ export default function MobileFilters({ diff --git a/examples/algolia/src/components/search/SearchModal.tsx b/examples/algolia/src/components/search/SearchModal.tsx index c4de7fae..e07c8237 100644 --- a/examples/algolia/src/components/search/SearchModal.tsx +++ b/examples/algolia/src/components/search/SearchModal.tsx @@ -153,9 +153,6 @@ export const SearchModal = (): JSX.Element => {