@faststore/core 2.1.84 → 2.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/.turbo/turbo-build.log +9 -7
  2. package/.turbo/turbo-test.log +25 -0
  3. package/@generated/graphql/index.ts +183 -79
  4. package/@generated/graphql/persisted.json +5 -5
  5. package/@generated/graphql/schema.graphql +1053 -0
  6. package/api/index.ts +15 -0
  7. package/cms/faststore/sections.json +2 -32
  8. package/codegen.yml +2 -1
  9. package/generate.sh +71 -0
  10. package/index.ts +15 -0
  11. package/jest.config.js +6 -0
  12. package/package.json +33 -18
  13. package/src/components/ThirdPartyScripts/ThirdPartyScripts.tsx +1 -2
  14. package/src/components/cms/RenderSections.tsx +4 -5
  15. package/src/components/navigation/Navbar/Navbar.tsx +4 -3
  16. package/src/components/navigation/NavbarSlider/NavbarSlider.tsx +5 -4
  17. package/src/components/product/ProductGrid/ProductGrid.tsx +4 -3
  18. package/src/components/sections/Breadcrumb/Breadcrumb.tsx +17 -22
  19. package/src/components/sections/CrossSellingShelf/CrossSellingShelf.tsx +8 -6
  20. package/src/components/sections/Footer/Footer.tsx +2 -14
  21. package/src/components/sections/Navbar/Navbar.tsx +0 -4
  22. package/src/components/sections/ProductDetails/ProductDetails.tsx +23 -33
  23. package/src/components/sections/ProductGallery/ProductGallery.tsx +15 -26
  24. package/src/components/sections/ProductShelf/ProductShelf.tsx +1 -1
  25. package/src/components/sections/ProductTiles/ProductTiles.tsx +4 -3
  26. package/src/components/templates/ProductListingPage/ProductListing.tsx +95 -0
  27. package/src/components/templates/ProductListingPage/ProductListingPage.tsx +34 -68
  28. package/src/components/templates/SearchPage/SearchPage.tsx +81 -0
  29. package/src/components/templates/SearchPage/index.ts +2 -0
  30. package/src/components/ui/ProductGallery/ProductGallery.tsx +24 -16
  31. package/src/components/ui/ProductGallery/ProductGalleryPage.tsx +8 -7
  32. package/src/components/ui/ProductGallery/useDelayedFacets.ts +3 -3
  33. package/src/components/ui/ProductShelf/ProductShelf.tsx +3 -2
  34. package/src/customizations/GlobalOverrides.tsx +0 -2
  35. package/src/customizations/fragments/ClientProduct.ts +9 -0
  36. package/src/customizations/fragments/ClientProductGallery.ts +19 -0
  37. package/src/customizations/fragments/ClientProducts.ts +19 -0
  38. package/src/customizations/fragments/ServerCollectionPage.ts +9 -0
  39. package/src/customizations/fragments/ServerProductPage.ts +9 -0
  40. package/src/customizations/graphql/thirdParty/resolvers/index.ts +3 -0
  41. package/src/customizations/graphql/vtex/resolvers/index.ts +3 -0
  42. package/src/pages/[...slug].tsx +5 -3
  43. package/src/pages/[slug]/p.tsx +38 -18
  44. package/src/pages/s.tsx +22 -34
  45. package/src/sdk/graphql/useQuery.ts +17 -13
  46. package/src/sdk/overrides/PageProvider.tsx +78 -0
  47. package/src/sdk/product/useLocalizedVariables.ts +33 -0
  48. package/src/sdk/product/usePageProductsQuery.ts +139 -0
  49. package/src/{components/sections/ProductGallery/useGalleryQuery.ts → sdk/product/useProductGalleryQuery.ts} +12 -13
  50. package/src/sdk/product/{useProduct.ts → useProductQuery.ts} +10 -7
  51. package/src/sdk/product/useProductsPrefetch.ts +72 -0
  52. package/src/sdk/product/useProductsQuery.ts +17 -63
  53. package/src/server/generator/generateGraphQLSchemaFile.ts +3 -0
  54. package/src/server/generator/schema.ts +82 -0
  55. package/src/server/index.ts +34 -21
  56. package/src/server/options.ts +16 -0
  57. package/test/server/index.test.ts +146 -0
  58. package/.next/BUILD_ID +0 -1
  59. package/.next/build-manifest.json +0 -129
  60. package/.next/cache/.tsbuildinfo +0 -1
  61. package/.next/cache/config.json +0 -7
  62. package/.next/cache/eslint/.cache_1gneedd +0 -1
  63. package/.next/cache/next-server.js.nft.json +0 -1
  64. package/.next/cache/webpack/client-production/0.pack +0 -0
  65. package/.next/cache/webpack/client-production/index.pack +0 -0
  66. package/.next/cache/webpack/server-production/0.pack +0 -0
  67. package/.next/cache/webpack/server-production/index.pack +0 -0
  68. package/.next/export-marker.json +0 -1
  69. package/.next/images-manifest.json +0 -1
  70. package/.next/next-server.js.nft.json +0 -1
  71. package/.next/package.json +0 -1
  72. package/.next/prerender-manifest.json +0 -1
  73. package/.next/react-loadable-manifest.json +0 -44
  74. package/.next/required-server-files.json +0 -1
  75. package/.next/routes-manifest.json +0 -1
  76. package/.next/server/chunks/143.js +0 -106
  77. package/.next/server/chunks/177.js +0 -120
  78. package/.next/server/chunks/183.js +0 -94
  79. package/.next/server/chunks/184.js +0 -61
  80. package/.next/server/chunks/186.js +0 -113
  81. package/.next/server/chunks/289.js +0 -239
  82. package/.next/server/chunks/312.js +0 -697
  83. package/.next/server/chunks/350.js +0 -143
  84. package/.next/server/chunks/483.js +0 -650
  85. package/.next/server/chunks/487.js +0 -9142
  86. package/.next/server/chunks/53.js +0 -61
  87. package/.next/server/chunks/530.js +0 -626
  88. package/.next/server/chunks/576.js +0 -94
  89. package/.next/server/chunks/650.js +0 -9142
  90. package/.next/server/chunks/676.js +0 -32
  91. package/.next/server/chunks/693.js +0 -58
  92. package/.next/server/chunks/71.js +0 -1254
  93. package/.next/server/chunks/74.js +0 -4065
  94. package/.next/server/chunks/753.js +0 -509
  95. package/.next/server/chunks/779.js +0 -58
  96. package/.next/server/chunks/825.js +0 -4039
  97. package/.next/server/chunks/854.js +0 -72
  98. package/.next/server/chunks/859.js +0 -959
  99. package/.next/server/chunks/907.js +0 -1911
  100. package/.next/server/chunks/933.js +0 -517
  101. package/.next/server/chunks/98.js +0 -124
  102. package/.next/server/chunks/988.js +0 -211
  103. package/.next/server/chunks/font-manifest.json +0 -1
  104. package/.next/server/font-manifest.json +0 -1
  105. package/.next/server/middleware-build-manifest.js +0 -1
  106. package/.next/server/middleware-manifest.json +0 -6
  107. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  108. package/.next/server/pages/404.js +0 -386
  109. package/.next/server/pages/404.js.nft.json +0 -1
  110. package/.next/server/pages/500.js +0 -388
  111. package/.next/server/pages/500.js.nft.json +0 -1
  112. package/.next/server/pages/[...slug].js +0 -1005
  113. package/.next/server/pages/[...slug].js.nft.json +0 -1
  114. package/.next/server/pages/[slug]/p.js +0 -2269
  115. package/.next/server/pages/[slug]/p.js.nft.json +0 -1
  116. package/.next/server/pages/_app.js +0 -280
  117. package/.next/server/pages/_app.js.nft.json +0 -1
  118. package/.next/server/pages/_document.js +0 -374
  119. package/.next/server/pages/_document.js.nft.json +0 -1
  120. package/.next/server/pages/_error.js +0 -164
  121. package/.next/server/pages/_error.js.nft.json +0 -1
  122. package/.next/server/pages/account.js +0 -363
  123. package/.next/server/pages/account.js.nft.json +0 -1
  124. package/.next/server/pages/api/graphql.js +0 -365
  125. package/.next/server/pages/api/graphql.js.nft.json +0 -1
  126. package/.next/server/pages/api/health/live.js +0 -31
  127. package/.next/server/pages/api/health/live.js.nft.json +0 -1
  128. package/.next/server/pages/api/health/ready.js +0 -31
  129. package/.next/server/pages/api/health/ready.js.nft.json +0 -1
  130. package/.next/server/pages/api/preview.js +0 -148
  131. package/.next/server/pages/api/preview.js.nft.json +0 -1
  132. package/.next/server/pages/checkout.js +0 -363
  133. package/.next/server/pages/checkout.js.nft.json +0 -1
  134. package/.next/server/pages/en-US/404.html +0 -81
  135. package/.next/server/pages/en-US/404.json +0 -1
  136. package/.next/server/pages/en-US/500.html +0 -81
  137. package/.next/server/pages/en-US/500.json +0 -1
  138. package/.next/server/pages/en-US/account.html +0 -81
  139. package/.next/server/pages/en-US/account.json +0 -1
  140. package/.next/server/pages/en-US/checkout.html +0 -81
  141. package/.next/server/pages/en-US/checkout.json +0 -1
  142. package/.next/server/pages/en-US/login.html +0 -81
  143. package/.next/server/pages/en-US/login.json +0 -1
  144. package/.next/server/pages/en-US/s.html +0 -81
  145. package/.next/server/pages/en-US/s.json +0 -1
  146. package/.next/server/pages/en-US.html +0 -81
  147. package/.next/server/pages/en-US.json +0 -1
  148. package/.next/server/pages/index.js +0 -439
  149. package/.next/server/pages/index.js.nft.json +0 -1
  150. package/.next/server/pages/login.js +0 -368
  151. package/.next/server/pages/login.js.nft.json +0 -1
  152. package/.next/server/pages/s.js +0 -466
  153. package/.next/server/pages/s.js.nft.json +0 -1
  154. package/.next/server/pages-manifest.json +0 -18
  155. package/.next/server/webpack-api-runtime.js +0 -229
  156. package/.next/server/webpack-runtime.js +0 -229
  157. package/.next/static/UFCNzAvjHLcTNmP8sQK5l/_buildManifest.js +0 -1
  158. package/.next/static/UFCNzAvjHLcTNmP8sQK5l/_ssgManifest.js +0 -1
  159. package/.next/static/chunks/143.dd8a556e6957baa1.js +0 -1
  160. package/.next/static/chunks/148.582eaa81293ee470.js +0 -1
  161. package/.next/static/chunks/238-39630714f5d70169.js +0 -1
  162. package/.next/static/chunks/243-b4f8e506d156ee41.js +0 -1
  163. package/.next/static/chunks/530.ec68de379130c11e.js +0 -1
  164. package/.next/static/chunks/548-4ee971bf7bdb3e81.js +0 -1
  165. package/.next/static/chunks/603-072c8fb73ac35775.js +0 -1
  166. package/.next/static/chunks/651.7142f31ce1e052b3.js +0 -1
  167. package/.next/static/chunks/709.7bc5a25ce30abda6.js +0 -1
  168. package/.next/static/chunks/738-a5ff304828f20cbf.js +0 -1
  169. package/.next/static/chunks/741.52f7fb873418346f.js +0 -1
  170. package/.next/static/chunks/98.97381d2021f86cd9.js +0 -1
  171. package/.next/static/chunks/988.afda042dd9ba11d1.js +0 -1
  172. package/.next/static/chunks/framework-dfd14d7ce6600b03.js +0 -1
  173. package/.next/static/chunks/main-fd466221927468fd.js +0 -1
  174. package/.next/static/chunks/pages/404-af78f7cd1d3c1f60.js +0 -1
  175. package/.next/static/chunks/pages/500-f6346ca5f9dc4fef.js +0 -1
  176. package/.next/static/chunks/pages/[...slug]-3f4e5f74ff9436ec.js +0 -1
  177. package/.next/static/chunks/pages/[slug]/p-8e20c206fb84d184.js +0 -1
  178. package/.next/static/chunks/pages/_app-895781b1c7b5bf56.js +0 -1
  179. package/.next/static/chunks/pages/_error-a7a0c1d9bfbb4f38.js +0 -1
  180. package/.next/static/chunks/pages/account-05bd79fb78365e88.js +0 -1
  181. package/.next/static/chunks/pages/checkout-c973786e68f25a39.js +0 -1
  182. package/.next/static/chunks/pages/index-73110361a184aa10.js +0 -1
  183. package/.next/static/chunks/pages/login-8deb9243376b6aa1.js +0 -1
  184. package/.next/static/chunks/pages/s-94691e1fdc9521a4.js +0 -1
  185. package/.next/static/chunks/polyfills-c67a75d1b6f99dc8.js +0 -1
  186. package/.next/static/chunks/webpack-504557a0a7373460.js +0 -1
  187. package/.next/static/css/20e4a3a45cdd65f4.css +0 -1
  188. package/.next/static/css/4b7138899cd07c63.css +0 -1
  189. package/.next/static/css/527e334fa69cf40a.css +0 -1
  190. package/.next/static/css/6e1a7434f061d0ef.css +0 -1
  191. package/.next/static/css/9e76fef1c9ca89af.css +0 -1
  192. package/.next/static/css/a2eefb25a4608343.css +0 -1
  193. package/.next/static/css/cb7d1fcea42fab9c.css +0 -1
  194. package/.next/static/css/df588bb98c0b0ca6.css +0 -1
  195. package/.next/static/css/e3b039e8f5daf95f.css +0 -1
  196. package/.next/static/css/f0e2d1b8832e935d.css +0 -1
  197. package/.next/trace +0 -80
  198. package/public/~partytown/debug/partytown-atomics.js +0 -556
  199. package/public/~partytown/debug/partytown-media.js +0 -374
  200. package/public/~partytown/debug/partytown-sandbox-sw.js +0 -543
  201. package/public/~partytown/debug/partytown-sw.js +0 -59
  202. package/public/~partytown/debug/partytown-ww-atomics.js +0 -1789
  203. package/public/~partytown/debug/partytown-ww-sw.js +0 -1781
  204. package/public/~partytown/debug/partytown.js +0 -72
  205. package/public/~partytown/partytown-atomics.js +0 -2
  206. package/public/~partytown/partytown-media.js +0 -2
  207. package/public/~partytown/partytown-sw.js +0 -2
  208. package/public/~partytown/partytown.js +0 -2
  209. package/src/components/ui/ProductGallery/usePageProducts.ts +0 -48
  210. package/src/customizations/components/overrides/ThirdPartyScripts.tsx +0 -9
  211. package/src/customizations/scripts/ThirdPartyScripts.tsx +0 -8
@@ -1,19 +1,20 @@
1
1
  import { mark } from 'src/sdk/tests/mark'
2
2
 
3
- import { ServerCollectionPageQueryQuery } from '@generated/graphql'
4
3
  import ProductGallery, {
5
4
  ProductGalleryProps,
6
5
  } from 'src/components/ui/ProductGallery/ProductGallery'
7
- import { SearchPageContextType } from 'src/pages/s'
8
6
  import Section from '../Section'
9
7
  import EmptyGallery from './EmptyGallery'
10
8
  import styles from './section.module.scss'
11
- import { useGalleryQuery } from './useGalleryQuery'
12
-
13
- type ProductGalleryContext = ServerCollectionPageQueryQuery['collection']
9
+ import {
10
+ PLPContext,
11
+ SearchPageContext,
12
+ isPLP,
13
+ isSearchPage,
14
+ usePage,
15
+ } from 'src/sdk/overrides/PageProvider'
14
16
 
15
17
  export interface ProductGallerySectionProps {
16
- context?: ProductGalleryContext
17
18
  searchTermLabel?: ProductGalleryProps['searchTermLabel']
18
19
  totalCountLabel?: ProductGalleryProps['totalCountLabel']
19
20
  filter: ProductGalleryProps['filter']
@@ -24,28 +25,17 @@ export interface ProductGallerySectionProps {
24
25
  productCard?: ProductGalleryProps['productCard']
25
26
  }
26
27
 
27
- const isSearch = (x: any): x is SearchPageContextType =>
28
- x === undefined || x?.title != undefined || x?.searchTerm != undefined
29
- const isCollection = (
30
- x: any
31
- ): x is ServerCollectionPageQueryQuery['collection'] =>
32
- x?.seo != undefined && x?.seo != null && x?.sku == undefined
33
-
34
- function ProductGallerySection({
35
- context,
36
- ...otherProps
37
- }: ProductGallerySectionProps) {
38
- const [title, searchTerm] = isSearch(context)
39
- ? [context?.title, context?.searchTerm]
40
- : isCollection(context)
41
- ? [context?.seo?.title]
28
+ function ProductGallerySection({ ...otherProps }: ProductGallerySectionProps) {
29
+ const context = usePage<SearchPageContext | PLPContext>()
30
+ const [title, searchTerm] = isSearchPage(context)
31
+ ? [context?.data?.title, context?.data?.searchTerm]
32
+ : isPLP(context)
33
+ ? [context?.data?.collection?.seo?.title]
42
34
  : ['']
43
35
 
44
- const { data: productGalleryData } = useGalleryQuery()
45
- const totalCount =
46
- productGalleryData?.search.products.pageInfo.totalCount ?? 0
36
+ const totalCount = context?.data?.search?.products?.pageInfo?.totalCount ?? 0
47
37
 
48
- if (productGalleryData && totalCount === 0) {
38
+ if (context?.data?.search?.products && totalCount === 0) {
49
39
  return (
50
40
  <Section className={`${styles.section} section-product-gallery`}>
51
41
  <section data-testid="product-gallery" data-fs-product-listing>
@@ -62,7 +52,6 @@ function ProductGallerySection({
62
52
  <ProductGallery
63
53
  title={title}
64
54
  searchTerm={searchTerm}
65
- productGalleryData={productGalleryData}
66
55
  totalCount={totalCount}
67
56
  {...otherProps}
68
57
  />
@@ -1,8 +1,8 @@
1
1
  import { useInView } from 'react-intersection-observer'
2
2
  import Section from '../Section'
3
3
 
4
- import styles from './section.module.scss'
5
4
  import ProductShelf, { ProductShelfProps } from 'src/components/ui/ProductShelf'
5
+ import styles from './section.module.scss'
6
6
 
7
7
  function ProductShelfSection({
8
8
  ...otherProps
@@ -1,7 +1,7 @@
1
1
  import { useEffect, useRef } from 'react'
2
2
  import { useInView } from 'react-intersection-observer'
3
3
 
4
- import type { ProductsQueryQueryVariables } from '@generated/graphql'
4
+ import type { ClientProductsQueryQueryVariables } from '@generated/graphql'
5
5
  import ProductCard from 'src/components/product/ProductCard'
6
6
  import ProductTilesSkeleton from 'src/components/skeletons/ProductTilesSkeleton'
7
7
  import Tiles, { Tile } from 'src/components/ui/Tiles'
@@ -12,7 +12,7 @@ import Section from '../Section'
12
12
 
13
13
  import styles from './section.module.scss'
14
14
 
15
- interface ProductTilesProps extends Partial<ProductsQueryQueryVariables> {
15
+ interface ProductTilesProps extends Partial<ClientProductsQueryQueryVariables> {
16
16
  title: string
17
17
  }
18
18
 
@@ -56,7 +56,8 @@ const getSizes = (products: number, idx: number) => {
56
56
  const ProductTiles = ({ title, ...variables }: ProductTilesProps) => {
57
57
  const viewedOnce = useRef(false)
58
58
  const { ref, inView } = useInView()
59
- const products = useProductsQuery(variables)
59
+ const data = useProductsQuery(variables)
60
+ const products = data?.search?.products
60
61
  const productEdges = products?.edges ?? []
61
62
 
62
63
  const { sendViewItemListEvent } = useViewItemListEvent({
@@ -0,0 +1,95 @@
1
+ import type { ServerCollectionPageQueryQuery } from '@generated/graphql'
2
+ import Breadcrumb from 'src/components/sections/Breadcrumb'
3
+ import Hero from 'src/components/sections/Hero'
4
+ import ProductGallery from 'src/components/sections/ProductGallery'
5
+ import ProductShelf from 'src/components/sections/ProductShelf'
6
+ import ScrollToTopButton from 'src/components/sections/ScrollToTopButton'
7
+ import { ITEMS_PER_PAGE } from 'src/constants'
8
+ import deepmerge from 'deepmerge'
9
+ import { useSearch } from '@faststore/sdk'
10
+
11
+ import type { ComponentType } from 'react'
12
+ import RenderSections from 'src/components/cms/RenderSections'
13
+ import CUSTOM_COMPONENTS from 'src/customizations/components'
14
+ import { PLPContentType } from 'src/server/cms'
15
+
16
+ import {
17
+ useCreateUseGalleryPage,
18
+ UseGalleryPageContext,
19
+ } from 'src/sdk/product/usePageProductsQuery'
20
+ import { useProductGalleryQuery } from 'src/sdk/product/useProductGalleryQuery'
21
+ import PageProvider, { PLPContext } from 'src/sdk/overrides/PageProvider'
22
+
23
+ export type ProductListingPageProps = {
24
+ data: ServerCollectionPageQueryQuery
25
+ page: PLPContentType
26
+ }
27
+
28
+ /**
29
+ * Sections: Components imported from each store's custom components and '../components/sections' only.
30
+ * Do not import or render components from any other folder in here.
31
+ */
32
+ const COMPONENTS: Record<string, ComponentType<any>> = {
33
+ Breadcrumb,
34
+ Hero,
35
+ ProductGallery,
36
+ ProductShelf,
37
+ ...CUSTOM_COMPONENTS,
38
+ }
39
+
40
+ // Array merging strategy from deepmerge that makes client arrays overwrite server array
41
+ // https://www.npmjs.com/package/deepmerge
42
+ const overwriteMerge = (_, sourceArray) => sourceArray
43
+
44
+ export default function ProductListing({
45
+ page: { sections, settings },
46
+ data: server,
47
+ }: ProductListingPageProps) {
48
+ const {
49
+ state: { sort, term, selectedFacets },
50
+ } = useSearch()
51
+ const itemsPerPage = settings?.productGallery?.itemsPerPage ?? ITEMS_PER_PAGE
52
+
53
+ const { data: pageProductGalleryData } = useProductGalleryQuery({
54
+ term,
55
+ sort,
56
+ selectedFacets,
57
+ itemsPerPage,
58
+ })
59
+
60
+ const { pages, useGalleryPage } = useCreateUseGalleryPage()
61
+
62
+ const context = {
63
+ data: {
64
+ ...deepmerge(
65
+ { ...server },
66
+ { ...pageProductGalleryData },
67
+ { arrayMerge: overwriteMerge }
68
+ ),
69
+ pages,
70
+ },
71
+ } as PLPContext
72
+
73
+ return (
74
+ <>
75
+ {/*
76
+ WARNING: Do not import or render components from any
77
+ other folder than '../components/sections' in here.
78
+
79
+ This is necessary to keep the integration with the CMS
80
+ easy and consistent, enabling the change and reorder
81
+ of elements on this page.
82
+
83
+ If needed, wrap your component in a <Section /> component
84
+ (not the HTML tag) before rendering it here.
85
+ */}
86
+ <PageProvider context={context}>
87
+ <UseGalleryPageContext.Provider value={useGalleryPage}>
88
+ <RenderSections sections={sections} components={COMPONENTS} />
89
+ </UseGalleryPageContext.Provider>
90
+ </PageProvider>
91
+
92
+ <ScrollToTopButton />
93
+ </>
94
+ )
95
+ }
@@ -9,38 +9,21 @@ import { useRouter } from 'next/router'
9
9
  import { useMemo } from 'react'
10
10
 
11
11
  import type { ServerCollectionPageQueryQuery } from '@generated/graphql'
12
- import Breadcrumb from 'src/components/sections/Breadcrumb'
13
- import Hero from 'src/components/sections/Hero'
14
- import ProductGallery from 'src/components/sections/ProductGallery'
15
- import ProductShelf from 'src/components/sections/ProductShelf'
16
- import ScrollToTopButton from 'src/components/sections/ScrollToTopButton'
17
12
  import { ITEMS_PER_PAGE } from 'src/constants'
18
13
  import { useApplySearchState } from 'src/sdk/search/state'
19
14
 
20
- import type { ComponentType } from 'react'
21
- import RenderSections from 'src/components/cms/RenderSections'
22
- import CUSTOM_COMPONENTS from 'src/customizations/components'
23
15
  import { PLPContentType } from 'src/server/cms'
24
16
 
25
17
  import storeConfig from '../../../../faststore.config'
18
+ import ProductListing from './ProductListing'
26
19
 
27
- export type ProductListingPageProps = ServerCollectionPageQueryQuery & {
20
+ export type ProductListingPageProps = {
21
+ data: ServerCollectionPageQueryQuery
28
22
  page: PLPContentType
29
23
  }
30
24
 
31
- /**
32
- * Sections: Components imported from each store's custom components and '../components/sections' only.
33
- * Do not import or render components from any other folder in here.
34
- */
35
- const COMPONENTS: Record<string, ComponentType<any>> = {
36
- Breadcrumb,
37
- Hero,
38
- ProductGallery,
39
- ProductShelf,
40
- ...CUSTOM_COMPONENTS,
41
- }
42
-
43
- type UseSearchParams = ServerCollectionPageQueryQuery & {
25
+ type UseSearchParams = {
26
+ collection: ServerCollectionPageQueryQuery['collection']
44
27
  sort: SearchState['sort']
45
28
  }
46
29
  const useSearchParams = ({
@@ -71,14 +54,15 @@ const useSearchParams = ({
71
54
  }
72
55
 
73
56
  export default function ProductListingPage({
74
- page: { sections, settings },
75
- ...otherProps
57
+ page: plpContentType,
58
+ data: server,
76
59
  }: ProductListingPageProps) {
77
- const { collection } = otherProps
60
+ const { settings } = plpContentType
61
+ const collection = server.collection
78
62
  const router = useRouter()
79
63
  const applySearchState = useApplySearchState()
80
64
  const searchParams = useSearchParams({
81
- ...otherProps,
65
+ collection,
82
66
  sort: settings?.productGallery?.sortBySelection as SearchState['sort'],
83
67
  })
84
68
 
@@ -90,49 +74,31 @@ export default function ProductListingPage({
90
74
  const sortQuery = !!sort ? `${separator}sort=${sort}` : ''
91
75
  const [pathname] = router.asPath.split('?')
92
76
  const canonical = `${storeConfig.storeUrl}${pathname}${pageQuery}${sortQuery}`
77
+ const itemsPerPage = settings?.productGallery?.itemsPerPage ?? ITEMS_PER_PAGE
93
78
 
94
79
  return (
95
- <>
96
- <SearchProvider
97
- onChange={applySearchState}
98
- itemsPerPage={settings?.productGallery?.itemsPerPage ?? ITEMS_PER_PAGE}
99
- {...searchParams}
100
- >
101
- {/* SEO */}
102
- <NextSeo
103
- title={title}
104
- description={description}
105
- titleTemplate={storeConfig.seo.titleTemplate}
106
- canonical={canonical}
107
- openGraph={{
108
- type: 'website',
109
- title,
110
- description,
111
- }}
112
- />
113
- <BreadcrumbJsonLd
114
- itemListElements={collection?.breadcrumbList.itemListElement ?? []}
115
- />
116
-
117
- {/*
118
- WARNING: Do not import or render components from any
119
- other folder than '../components/sections' in here.
120
-
121
- This is necessary to keep the integration with the CMS
122
- easy and consistent, enabling the change and reorder
123
- of elements on this page.
124
-
125
- If needed, wrap your component in a <Section /> component
126
- (not the HTML tag) before rendering it here.
127
- */}
128
- <RenderSections
129
- context={collection}
130
- sections={sections}
131
- components={COMPONENTS}
132
- />
133
-
134
- <ScrollToTopButton />
135
- </SearchProvider>
136
- </>
80
+ <SearchProvider
81
+ onChange={applySearchState}
82
+ itemsPerPage={itemsPerPage}
83
+ {...searchParams}
84
+ >
85
+ {/* SEO */}
86
+ <NextSeo
87
+ title={title}
88
+ description={description}
89
+ titleTemplate={storeConfig.seo.titleTemplate}
90
+ canonical={canonical}
91
+ openGraph={{
92
+ type: 'website',
93
+ title,
94
+ description,
95
+ }}
96
+ />
97
+ <BreadcrumbJsonLd
98
+ itemListElements={collection?.breadcrumbList.itemListElement ?? []}
99
+ />
100
+
101
+ <ProductListing page={plpContentType} data={server} />
102
+ </SearchProvider>
137
103
  )
138
104
  }
@@ -0,0 +1,81 @@
1
+ import { useSearch } from '@faststore/sdk'
2
+ import type { ComponentType } from 'react'
3
+
4
+ import Breadcrumb from 'src/components/sections/Breadcrumb'
5
+ import ProductGallery from 'src/components/sections/ProductGallery'
6
+ import { ITEMS_PER_PAGE } from 'src/constants'
7
+ import CUSTOM_COMPONENTS from 'src/customizations/components'
8
+ import RenderSections from 'src/components/cms/RenderSections'
9
+ import { SearchContentType } from 'src/server/cms'
10
+ import { useProductGalleryQuery } from 'src/sdk/product/useProductGalleryQuery'
11
+ import PageProvider, { SearchPageContext } from 'src/sdk/overrides/PageProvider'
12
+ import {
13
+ useCreateUseGalleryPage,
14
+ UseGalleryPageContext,
15
+ } from 'src/sdk/product/usePageProductsQuery'
16
+ import { SearchPageContextType } from 'src/pages/s'
17
+
18
+ /**
19
+ * Sections: Components imported from each store's custom components and '../components/sections' only.
20
+ * Do not import or render components from any other folder in here.
21
+ */
22
+ const COMPONENTS: Record<string, ComponentType<any>> = {
23
+ Breadcrumb,
24
+ ProductGallery,
25
+ ...CUSTOM_COMPONENTS,
26
+ }
27
+
28
+ export type SearchPageProps = {
29
+ data: SearchPageContextType
30
+ page: SearchContentType
31
+ }
32
+
33
+ function SearchPage({
34
+ page: { sections, settings },
35
+ data: server,
36
+ }: SearchPageProps) {
37
+ const {
38
+ state: { sort, term, selectedFacets },
39
+ } = useSearch()
40
+ const itemsPerPage = settings?.productGallery?.itemsPerPage ?? ITEMS_PER_PAGE
41
+
42
+ const { data: pageProductGalleryData } = useProductGalleryQuery({
43
+ term,
44
+ sort,
45
+ selectedFacets,
46
+ itemsPerPage,
47
+ })
48
+
49
+ const { pages, useGalleryPage } = useCreateUseGalleryPage()
50
+
51
+ const context = {
52
+ data: {
53
+ ...server,
54
+ ...pageProductGalleryData,
55
+ pages,
56
+ },
57
+ } as SearchPageContext
58
+
59
+ return (
60
+ <>
61
+ {/*
62
+ WARNING: Do not import or render components from any
63
+ other folder than '../components/sections' in here.
64
+
65
+ This is necessary to keep the integration with the CMS
66
+ easy and consistent, enabling the change and reorder
67
+ of elements on this page.
68
+
69
+ If needed, wrap your component in a <Section /> component
70
+ (not the HTML tag) before rendering it here.
71
+ */}
72
+ <PageProvider context={context}>
73
+ <UseGalleryPageContext.Provider value={useGalleryPage}>
74
+ <RenderSections sections={sections} components={COMPONENTS} />
75
+ </UseGalleryPageContext.Provider>
76
+ </PageProvider>
77
+ </>
78
+ )
79
+ }
80
+
81
+ export default SearchPage
@@ -0,0 +1,2 @@
1
+ export { default } from './SearchPage'
2
+ export type { SearchPageProps } from './SearchPage'
@@ -9,31 +9,34 @@ import Sort from 'src/components/search/Sort'
9
9
  import FilterSkeleton from 'src/components/skeletons/FilterSkeleton'
10
10
  import ProductGridSkeleton from 'src/components/skeletons/ProductGridSkeleton'
11
11
 
12
- import { ProductGalleryQueryQuery } from '@generated/graphql'
13
12
  import { ProductCardProps } from 'src/components/product/ProductCard'
14
13
  import { FilterSliderProps } from 'src/components/search/Filter/FilterSlider'
15
14
  import { SortProps } from 'src/components/search/Sort/Sort'
16
- import { useDelayedFacets } from './useDelayedFacets'
17
- import { useDelayedPagination } from './useDelayedPagination'
18
- import { useProductsPrefetch } from './usePageProducts'
19
15
  import {
20
- MobileFilterButton,
16
+ FilterButtonSkeleton,
21
17
  FilterIcon,
18
+ LinkButtonNext,
19
+ LinkButtonPrev,
20
+ MobileFilterButton,
22
21
  PrevIcon,
23
22
  ResultsCountSkeleton,
24
23
  SortSkeleton,
25
- FilterButtonSkeleton,
26
- LinkButtonPrev,
27
- LinkButtonNext,
28
24
  } from 'src/components/sections/ProductGallery/Overrides'
25
+ import { useDelayedFacets } from './useDelayedFacets'
26
+ import { useDelayedPagination } from './useDelayedPagination'
27
+ import {
28
+ PLPContext,
29
+ SearchPageContext,
30
+ usePage,
31
+ } from 'src/sdk/overrides/PageProvider'
32
+ import { useProductsPrefetch } from 'src/sdk/product/useProductsPrefetch'
29
33
 
30
- const GalleryPage = lazy(() => import('./ProductGalleryPage'))
34
+ const ProductGalleryPage = lazy(() => import('./ProductGalleryPage'))
31
35
  const GalleryPageSkeleton = <ProductGridSkeleton loading />
32
36
 
33
37
  export interface ProductGalleryProps {
34
38
  title?: string
35
39
  searchTerm?: string
36
- productGalleryData?: ProductGalleryQueryQuery
37
40
  totalCount?: number
38
41
  searchTermLabel?: string
39
42
  totalCountLabel?: string
@@ -69,7 +72,6 @@ export interface ProductGalleryProps {
69
72
  function ProductGallery({
70
73
  title,
71
74
  searchTerm,
72
- productGalleryData,
73
75
  totalCount,
74
76
  searchTermLabel,
75
77
  totalCountLabel,
@@ -80,13 +82,18 @@ function ProductGallery({
80
82
  productCard,
81
83
  }: ProductGalleryProps) {
82
84
  const { openFilter } = useUI()
83
- const { pages, addNextPage, addPrevPage } = useSearch()
84
- const facets = useDelayedFacets(productGalleryData)
85
+ const { pages, addNextPage, addPrevPage, itemsPerPage } = useSearch()
86
+ const context = usePage<SearchPageContext | PLPContext>()
87
+ const data = context?.data
88
+ const facets = useDelayedFacets(data) ?? []
85
89
  const { next, prev } = useDelayedPagination(totalCount)
86
90
 
87
91
  useProductsPrefetch(prev ? prev.cursor : null)
88
92
  useProductsPrefetch(next ? next.cursor : null)
89
93
 
94
+ const hasFacets =
95
+ Boolean(data?.search?.facets) && data.search.facets.length > 0
96
+
90
97
  return (
91
98
  <section data-testid="product-gallery" data-fs-product-listing>
92
99
  {searchTerm && (
@@ -115,7 +122,7 @@ function ProductGallery({
115
122
  {...ResultsCountSkeleton.props}
116
123
  // Dynamic props shouldn't be overridable
117
124
  // This decision can be reviewed later if needed
118
- loading={!productGalleryData}
125
+ loading={!data?.search}
119
126
  >
120
127
  <h2 data-testid="total-product-count">
121
128
  {totalCount} {totalCountLabel}
@@ -212,14 +219,15 @@ function ProductGallery({
212
219
  </div>
213
220
  )}
214
221
  {/* Render ALL products */}
215
- {productGalleryData ? (
222
+ {hasFacets ? (
216
223
  <Suspense fallback={GalleryPageSkeleton}>
217
224
  {pages.map((page) => (
218
- <GalleryPage
225
+ <ProductGalleryPage
219
226
  key={`gallery-page-${page}`}
220
227
  page={page}
221
228
  title={title}
222
229
  productCard={productCard}
230
+ itemsPerPage={itemsPerPage}
223
231
  />
224
232
  ))}
225
233
  </Suspense>
@@ -1,20 +1,21 @@
1
- import { useSearch } from '@faststore/sdk'
2
-
3
1
  import ProductGrid from 'src/components/product/ProductGrid'
4
2
  import Sentinel from 'src/sdk/search/Sentinel'
5
3
 
6
4
  import { ProductCardProps } from 'src/components/product/ProductCard'
7
- import { useProducts } from './usePageProducts'
5
+ import { memo } from 'react'
6
+ import { useGalleryPage } from 'src/sdk/product/usePageProductsQuery'
8
7
 
9
8
  interface Props {
10
9
  page: number
11
10
  title: string
12
11
  productCard?: Pick<ProductCardProps, 'showDiscountBadge' | 'bordered'>
12
+ itemsPerPage: number
13
13
  }
14
14
 
15
- function GalleryPage({ page, title, productCard }: Props) {
16
- const products = useProducts(page) ?? []
17
- const { itemsPerPage } = useSearch()
15
+ function ProductGalleryPage({ page, title, productCard, itemsPerPage }: Props) {
16
+ const { data } = useGalleryPage(page)
17
+
18
+ const products = data?.search?.products?.edges ?? []
18
19
 
19
20
  return (
20
21
  <>
@@ -34,4 +35,4 @@ function GalleryPage({ page, title, productCard }: Props) {
34
35
  )
35
36
  }
36
37
 
37
- export default GalleryPage
38
+ export default memo(ProductGalleryPage)
@@ -1,16 +1,16 @@
1
1
  import { useMemo, useRef } from 'react'
2
2
 
3
3
  import type {
4
+ ClientProductGalleryQueryQuery,
4
5
  Filter_FacetsFragment,
5
- ProductGalleryQueryQuery,
6
6
  } from '@generated/graphql'
7
7
 
8
- export const useDelayedFacets = (data?: ProductGalleryQueryQuery) => {
8
+ export const useDelayedFacets = (data?: ClientProductGalleryQueryQuery) => {
9
9
  const facets = useRef<Filter_FacetsFragment[]>([])
10
10
 
11
11
  return useMemo(() => {
12
12
  if (data) {
13
- facets.current = data.search.facets
13
+ facets.current = data.search?.facets
14
14
  }
15
15
 
16
16
  return facets.current
@@ -45,12 +45,13 @@ function ProductShelf({
45
45
  bordered = ProductCard.props.bordered,
46
46
  showDiscountBadge = ProductCard.props.showDiscountBadge,
47
47
  } = {},
48
- ...variables
48
+ ...otherProps
49
49
  }: ProductShelfProps) {
50
50
  const titleId = textToKebabCase(title)
51
51
  const id = useId()
52
52
  const viewedOnce = useRef(false)
53
- const products = useProductsQuery(variables)
53
+ const data = useProductsQuery(otherProps)
54
+ const products = data?.search?.products
54
55
  const productEdges = products?.edges ?? []
55
56
  const aspectRatio = 1
56
57
 
@@ -1,11 +1,9 @@
1
1
  import WebFontsOverrides from 'src/customizations/components/overrides/WebFonts'
2
2
  import { default as CoreWebFonts } from 'src/fonts/WebFonts'
3
- import ThirdPartyScriptsOverrides from 'src/customizations/components/overrides/ThirdPartyScripts'
4
3
 
5
4
  const Components = {
6
5
  WebFonts: CoreWebFonts,
7
6
  ...WebFontsOverrides.components,
8
- ...ThirdPartyScriptsOverrides.components,
9
7
  }
10
8
 
11
9
  export const WebFonts = Components.WebFonts
@@ -0,0 +1,9 @@
1
+ import { gql } from '@faststore/graphql-utils'
2
+
3
+ export const fragment = gql`
4
+ fragment ClientProduct on Query {
5
+ product(locator: $locator) {
6
+ id: productID
7
+ }
8
+ }
9
+ `
@@ -0,0 +1,19 @@
1
+ import { gql } from '@faststore/graphql-utils'
2
+
3
+ export const fragment = gql`
4
+ fragment ClientProductGallery on Query {
5
+ search(
6
+ first: $first
7
+ after: $after
8
+ sort: $sort
9
+ term: $term
10
+ selectedFacets: $selectedFacets
11
+ ) {
12
+ products {
13
+ pageInfo {
14
+ totalCount
15
+ }
16
+ }
17
+ }
18
+ }
19
+ `
@@ -0,0 +1,19 @@
1
+ import { gql } from '@faststore/graphql-utils'
2
+
3
+ export const fragment = gql`
4
+ fragment ClientProducts on Query {
5
+ search(
6
+ first: $first
7
+ after: $after
8
+ sort: $sort
9
+ term: $term
10
+ selectedFacets: $selectedFacets
11
+ ) {
12
+ products {
13
+ pageInfo {
14
+ totalCount
15
+ }
16
+ }
17
+ }
18
+ }
19
+ `
@@ -0,0 +1,9 @@
1
+ import { gql } from '@faststore/graphql-utils'
2
+
3
+ export const fragment = gql`
4
+ fragment ServerCollectionPage on Query {
5
+ collection(slug: $slug) {
6
+ id
7
+ }
8
+ }
9
+ `