@faststore/core 2.0.152-alpha.0 → 2.0.154-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 (186) hide show
  1. package/.turbo/turbo-build.log +6 -8
  2. package/CHANGELOG.md +12 -0
  3. package/cms/faststore/content-types.json +96 -2
  4. package/cms/faststore/sections.json +179 -8
  5. package/package.json +4 -4
  6. package/src/components/cms/GlobalSections.tsx +3 -25
  7. package/src/components/cms/RenderSections.tsx +37 -2
  8. package/src/components/navigation/Navbar/Navbar.tsx +14 -8
  9. package/src/components/product/ProductGrid/ProductGrid.tsx +8 -3
  10. package/src/components/search/Filter/Filter.tsx +22 -3
  11. package/src/components/search/Filter/FilterSlider.tsx +14 -4
  12. package/src/components/search/SearchDropdown/SearchDropdown.tsx +17 -7
  13. package/src/components/search/SearchInput/SearchInput.tsx +10 -5
  14. package/src/components/search/SearchTop/SearchTop.tsx +17 -3
  15. package/src/components/search/Sort/Sort.tsx +21 -3
  16. package/src/components/sections/CrossSellingShelf/CrossSellingShelf.tsx +17 -3
  17. package/src/components/sections/Navbar/Navbar.tsx +5 -0
  18. package/src/components/sections/ProductGallery/ProductGallery.tsx +44 -154
  19. package/src/components/sections/ProductShelf/ProductShelf.tsx +7 -76
  20. package/src/components/sections/Section/section.scss +0 -7
  21. package/src/components/ui/ProductGallery/ProductGallery.tsx +220 -0
  22. package/src/components/{sections → ui}/ProductGallery/ProductGalleryPage.tsx +9 -2
  23. package/src/components/ui/ProductGallery/index.ts +1 -0
  24. package/src/components/{sections → ui}/ProductShelf/Overrides.tsx +2 -2
  25. package/src/components/ui/ProductShelf/ProductShelf.tsx +106 -0
  26. package/src/components/ui/ProductShelf/index.ts +2 -0
  27. package/src/pages/[...slug].tsx +35 -24
  28. package/src/pages/s.tsx +38 -13
  29. package/src/sdk/search/formatSearchPath.ts +8 -1
  30. package/src/server/cms.ts +11 -2
  31. package/.next/BUILD_ID +0 -1
  32. package/.next/build-manifest.json +0 -132
  33. package/.next/cache/.tsbuildinfo +0 -1
  34. package/.next/cache/config.json +0 -7
  35. package/.next/cache/eslint/.cache_1gneedd +0 -1
  36. package/.next/cache/next-server.js.nft.json +0 -1
  37. package/.next/cache/webpack/client-production/0.pack +0 -0
  38. package/.next/cache/webpack/client-production/index.pack +0 -0
  39. package/.next/cache/webpack/server-production/0.pack +0 -0
  40. package/.next/cache/webpack/server-production/index.pack +0 -0
  41. package/.next/export-marker.json +0 -1
  42. package/.next/images-manifest.json +0 -1
  43. package/.next/next-server.js.nft.json +0 -1
  44. package/.next/package.json +0 -1
  45. package/.next/prerender-manifest.json +0 -1
  46. package/.next/react-loadable-manifest.json +0 -48
  47. package/.next/required-server-files.json +0 -1
  48. package/.next/routes-manifest.json +0 -1
  49. package/.next/server/chunks/104.js +0 -703
  50. package/.next/server/chunks/123.js +0 -58
  51. package/.next/server/chunks/143.js +0 -106
  52. package/.next/server/chunks/183.js +0 -80
  53. package/.next/server/chunks/247.js +0 -61
  54. package/.next/server/chunks/280.js +0 -324
  55. package/.next/server/chunks/287.js +0 -58
  56. package/.next/server/chunks/312.js +0 -670
  57. package/.next/server/chunks/336.js +0 -821
  58. package/.next/server/chunks/350.js +0 -142
  59. package/.next/server/chunks/368.js +0 -253
  60. package/.next/server/chunks/401.js +0 -7241
  61. package/.next/server/chunks/431.js +0 -7241
  62. package/.next/server/chunks/502.js +0 -600
  63. package/.next/server/chunks/557.js +0 -132
  64. package/.next/server/chunks/576.js +0 -80
  65. package/.next/server/chunks/597.js +0 -169
  66. package/.next/server/chunks/608.js +0 -644
  67. package/.next/server/chunks/644.js +0 -235
  68. package/.next/server/chunks/664.js +0 -3401
  69. package/.next/server/chunks/676.js +0 -32
  70. package/.next/server/chunks/701.js +0 -87
  71. package/.next/server/chunks/74.js +0 -2674
  72. package/.next/server/chunks/746.js +0 -225
  73. package/.next/server/chunks/82.js +0 -371
  74. package/.next/server/chunks/854.js +0 -72
  75. package/.next/server/chunks/859.js +0 -959
  76. package/.next/server/chunks/874.js +0 -487
  77. package/.next/server/chunks/886.js +0 -120
  78. package/.next/server/chunks/907.js +0 -1723
  79. package/.next/server/chunks/98.js +0 -124
  80. package/.next/server/chunks/font-manifest.json +0 -1
  81. package/.next/server/font-manifest.json +0 -1
  82. package/.next/server/middleware-build-manifest.js +0 -1
  83. package/.next/server/middleware-manifest.json +0 -6
  84. package/.next/server/middleware-react-loadable-manifest.js +0 -1
  85. package/.next/server/pages/404.js +0 -393
  86. package/.next/server/pages/404.js.nft.json +0 -1
  87. package/.next/server/pages/500.js +0 -395
  88. package/.next/server/pages/500.js.nft.json +0 -1
  89. package/.next/server/pages/[...slug].js +0 -718
  90. package/.next/server/pages/[...slug].js.nft.json +0 -1
  91. package/.next/server/pages/[slug]/p.js +0 -2458
  92. package/.next/server/pages/[slug]/p.js.nft.json +0 -1
  93. package/.next/server/pages/_app.js +0 -368
  94. package/.next/server/pages/_app.js.nft.json +0 -1
  95. package/.next/server/pages/_document.js +0 -304
  96. package/.next/server/pages/_document.js.nft.json +0 -1
  97. package/.next/server/pages/_error.js +0 -164
  98. package/.next/server/pages/_error.js.nft.json +0 -1
  99. package/.next/server/pages/account.js +0 -370
  100. package/.next/server/pages/account.js.nft.json +0 -1
  101. package/.next/server/pages/api/graphql.js +0 -315
  102. package/.next/server/pages/api/graphql.js.nft.json +0 -1
  103. package/.next/server/pages/api/preview.js +0 -118
  104. package/.next/server/pages/api/preview.js.nft.json +0 -1
  105. package/.next/server/pages/checkout.js +0 -370
  106. package/.next/server/pages/checkout.js.nft.json +0 -1
  107. package/.next/server/pages/en-US/404.html +0 -81
  108. package/.next/server/pages/en-US/404.json +0 -1
  109. package/.next/server/pages/en-US/500.html +0 -81
  110. package/.next/server/pages/en-US/500.json +0 -1
  111. package/.next/server/pages/en-US/account.html +0 -81
  112. package/.next/server/pages/en-US/account.json +0 -1
  113. package/.next/server/pages/en-US/checkout.html +0 -81
  114. package/.next/server/pages/en-US/checkout.json +0 -1
  115. package/.next/server/pages/en-US/login.html +0 -81
  116. package/.next/server/pages/en-US/login.json +0 -1
  117. package/.next/server/pages/en-US/s.html +0 -81
  118. package/.next/server/pages/en-US/s.json +0 -1
  119. package/.next/server/pages/en-US.html +0 -81
  120. package/.next/server/pages/en-US.json +0 -1
  121. package/.next/server/pages/index.js +0 -966
  122. package/.next/server/pages/index.js.nft.json +0 -1
  123. package/.next/server/pages/login.js +0 -375
  124. package/.next/server/pages/login.js.nft.json +0 -1
  125. package/.next/server/pages/s.js +0 -457
  126. package/.next/server/pages/s.js.nft.json +0 -1
  127. package/.next/server/pages-manifest.json +0 -16
  128. package/.next/server/webpack-api-runtime.js +0 -229
  129. package/.next/server/webpack-runtime.js +0 -229
  130. package/.next/static/chunks/143.dd8a556e6957baa1.js +0 -1
  131. package/.next/static/chunks/170.c17ce564bb568265.js +0 -1
  132. package/.next/static/chunks/226.b57e10ad9932f88c.js +0 -1
  133. package/.next/static/chunks/327-43715af4f3fffaf6.js +0 -1
  134. package/.next/static/chunks/336.0846f48eccce57e4.js +0 -1
  135. package/.next/static/chunks/366-c0bfd9890048babf.js +0 -1
  136. package/.next/static/chunks/377-61e89c711b136605.js +0 -1
  137. package/.next/static/chunks/391-47b923ef44945418.js +0 -1
  138. package/.next/static/chunks/495.8ffebac98e9475dc.js +0 -1
  139. package/.next/static/chunks/502.ae82de1669112b15.js +0 -1
  140. package/.next/static/chunks/545-1d3d08edfd2ee4c6.js +0 -1
  141. package/.next/static/chunks/597.c5cf3fbeae5ceb63.js +0 -1
  142. package/.next/static/chunks/651.7142f31ce1e052b3.js +0 -1
  143. package/.next/static/chunks/741.52f7fb873418346f.js +0 -1
  144. package/.next/static/chunks/98.97381d2021f86cd9.js +0 -1
  145. package/.next/static/chunks/framework-dfd14d7ce6600b03.js +0 -1
  146. package/.next/static/chunks/main-fd466221927468fd.js +0 -1
  147. package/.next/static/chunks/pages/404-459452495a0df278.js +0 -1
  148. package/.next/static/chunks/pages/500-008e30c48eceebed.js +0 -1
  149. package/.next/static/chunks/pages/[...slug]-a351612e92518155.js +0 -1
  150. package/.next/static/chunks/pages/[slug]/p-fa35dd93d53f75f6.js +0 -1
  151. package/.next/static/chunks/pages/_app-3e4e7e579cb0681a.js +0 -1
  152. package/.next/static/chunks/pages/_error-a7a0c1d9bfbb4f38.js +0 -1
  153. package/.next/static/chunks/pages/account-46263f0c100c3eae.js +0 -1
  154. package/.next/static/chunks/pages/checkout-20bb3710b24df3b6.js +0 -1
  155. package/.next/static/chunks/pages/index-49b7ee570f7cc4b7.js +0 -1
  156. package/.next/static/chunks/pages/login-f9d316d261fcc062.js +0 -1
  157. package/.next/static/chunks/pages/s-890c0c4b88484224.js +0 -1
  158. package/.next/static/chunks/polyfills-c67a75d1b6f99dc8.js +0 -1
  159. package/.next/static/chunks/webpack-976c8f7df8e3dea3.js +0 -1
  160. package/.next/static/css/1323734429a8aa40.css +0 -1
  161. package/.next/static/css/13a4da555ff5e3be.css +0 -1
  162. package/.next/static/css/2e00f7ba49c754b3.css +0 -1
  163. package/.next/static/css/7d822a137c54a781.css +0 -1
  164. package/.next/static/css/a0feab89b7648c5c.css +0 -1
  165. package/.next/static/css/a13a9f9cd349d906.css +0 -1
  166. package/.next/static/css/a45618030b16a245.css +0 -1
  167. package/.next/static/css/bde408cc006e64f8.css +0 -1
  168. package/.next/static/css/d462d9478ce00021.css +0 -1
  169. package/.next/static/css/e02cdad8fc000339.css +0 -1
  170. package/.next/static/d4K49oW-EfaWNefvNv5AZ/_buildManifest.js +0 -1
  171. package/.next/static/d4K49oW-EfaWNefvNv5AZ/_ssgManifest.js +0 -1
  172. package/.next/trace +0 -69
  173. package/public/~partytown/debug/partytown-atomics.js +0 -556
  174. package/public/~partytown/debug/partytown-media.js +0 -374
  175. package/public/~partytown/debug/partytown-sandbox-sw.js +0 -543
  176. package/public/~partytown/debug/partytown-sw.js +0 -59
  177. package/public/~partytown/debug/partytown-ww-atomics.js +0 -1789
  178. package/public/~partytown/debug/partytown-ww-sw.js +0 -1781
  179. package/public/~partytown/debug/partytown.js +0 -72
  180. package/public/~partytown/partytown-atomics.js +0 -2
  181. package/public/~partytown/partytown-media.js +0 -2
  182. package/public/~partytown/partytown-sw.js +0 -2
  183. package/public/~partytown/partytown.js +0 -2
  184. /package/src/components/{sections → ui}/ProductGallery/useDelayedFacets.ts +0 -0
  185. /package/src/components/{sections → ui}/ProductGallery/useDelayedPagination.ts +0 -0
  186. /package/src/components/{sections → ui}/ProductGallery/usePageProducts.ts +0 -0
@@ -15,7 +15,7 @@ import type { useFilter } from './useFilter'
15
15
 
16
16
  import styles from './section.module.scss'
17
17
 
18
- interface Props {
18
+ export interface FilterSliderProps {
19
19
  /**
20
20
  * ID to find this component in testing tools (e.g.: cypress,
21
21
  * testing-library, and jest).
@@ -29,6 +29,14 @@ interface Props {
29
29
  * Title for the `Filter` component.
30
30
  */
31
31
  title?: string
32
+ /**
33
+ * CMS defined label for the clear button component.
34
+ */
35
+ clearButtonLabel?: string
36
+ /**
37
+ * CMS defined label for the apply button component.
38
+ */
39
+ applyButtonLabel?: string
32
40
  }
33
41
 
34
42
  function FilterSlider({
@@ -38,7 +46,9 @@ function FilterSlider({
38
46
  expanded,
39
47
  selected,
40
48
  title,
41
- }: Props & ReturnType<typeof useFilter>) {
49
+ clearButtonLabel,
50
+ applyButtonLabel,
51
+ }: FilterSliderProps & ReturnType<typeof useFilter>) {
42
52
  const { resetInfiniteScroll, setState, state } = useSearch()
43
53
 
44
54
  return (
@@ -52,7 +62,7 @@ function FilterSlider({
52
62
  clearBtnProps={{
53
63
  variant: 'secondary',
54
64
  onClick: () => dispatch({ type: 'selectFacets', payload: [] }),
55
- children: 'Clear All',
65
+ children: clearButtonLabel ?? 'Clear All',
56
66
  }}
57
67
  applyBtnProps={{
58
68
  variant: 'primary',
@@ -65,7 +75,7 @@ function FilterSlider({
65
75
  page: 0,
66
76
  })
67
77
  },
68
- children: 'Apply',
78
+ children: applyButtonLabel ?? 'Apply',
69
79
  }}
70
80
  onClose={() => {
71
81
  dispatch({
@@ -1,27 +1,28 @@
1
1
  import {
2
+ SearchProducts,
2
3
  SearchAutoComplete as UISearchAutoComplete,
3
4
  SearchAutoCompleteTerm as UISearchAutoCompleteTerm,
4
5
  SearchDropdown as UISearchDropdown,
5
- SearchProducts,
6
6
  useSearch,
7
7
  } from '@faststore/ui'
8
8
 
9
9
  import { SearchHistory } from '../SearchHistory'
10
10
  import { SearchTop } from '../SearchTop'
11
11
 
12
+ import { SearchState } from '@faststore/sdk'
13
+ import { ProductSummary_ProductFragment } from '@generated/graphql'
12
14
  import SearchProductItem from 'src/components/search/SearchProductItem'
13
15
  import { formatSearchPath } from 'src/sdk/search/formatSearchPath'
14
- import { ProductSummary_ProductFragment } from '@generated/graphql'
15
16
 
16
- function SearchDropdown({ ...otherProps }) {
17
+ function SearchDropdown({ sort, ...otherProps }) {
17
18
  const {
18
19
  values: { onSearchSelection, products, term, terms },
19
20
  } = useSearch()
20
21
 
21
22
  return (
22
23
  <UISearchDropdown {...otherProps}>
23
- <SearchHistory />
24
- <SearchTop />
24
+ <SearchHistory sort={sort} />
25
+ <SearchTop sort={sort} />
25
26
  <UISearchAutoComplete>
26
27
  {terms?.map(({ value: suggestion }) => (
27
28
  <UISearchAutoCompleteTerm
@@ -29,9 +30,18 @@ function SearchDropdown({ ...otherProps }) {
29
30
  term={term}
30
31
  suggestion={suggestion}
31
32
  linkProps={{
32
- href: formatSearchPath(suggestion),
33
+ href: formatSearchPath({
34
+ term: suggestion,
35
+ sort: sort as SearchState['sort'],
36
+ }),
33
37
  onClick: () =>
34
- onSearchSelection?.(suggestion, formatSearchPath(suggestion)),
38
+ onSearchSelection?.(
39
+ suggestion,
40
+ formatSearchPath({
41
+ term: suggestion,
42
+ sort: sort as SearchState['sort'],
43
+ })
44
+ ),
35
45
  }}
36
46
  />
37
47
  ))}
@@ -1,20 +1,20 @@
1
- import type { SearchEvent } from '@faststore/sdk'
1
+ import type { SearchEvent, SearchState } from '@faststore/sdk'
2
2
  import { sendAnalyticsEvent } from '@faststore/sdk'
3
3
  import type {
4
4
  SearchInputFieldProps as UISearchInputFieldProps,
5
5
  SearchInputFieldRef as UISearchInputFieldRef,
6
6
  } from '@faststore/ui'
7
7
  import {
8
+ SearchProviderContextValue,
8
9
  SearchInput as UISearchInput,
9
10
  SearchInputField as UISearchInputField,
10
- SearchProviderContextValue,
11
11
  } from '@faststore/ui'
12
12
  import { useRouter } from 'next/router'
13
13
  import type { CSSProperties } from 'react'
14
14
  import {
15
+ Suspense,
15
16
  forwardRef,
16
17
  lazy,
17
- Suspense,
18
18
  useDeferredValue,
19
19
  useImperativeHandle,
20
20
  useRef,
@@ -34,6 +34,7 @@ export type SearchInputProps = {
34
34
  onSearchClick?: () => void
35
35
  buttonTestId?: string
36
36
  containerStyle?: CSSProperties
37
+ sort?: string
37
38
  } & Omit<UISearchInputFieldProps, 'onSubmit'>
38
39
 
39
40
  export type SearchInputRef = UISearchInputFieldRef & {
@@ -53,6 +54,7 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
53
54
  onSearchClick,
54
55
  buttonTestId = 'fs-search-button',
55
56
  containerStyle,
57
+ sort,
56
58
  ...otherProps
57
59
  },
58
60
  ref
@@ -103,7 +105,10 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
103
105
  placeholder="Search everything at the store"
104
106
  onChange={(e) => setSearchQuery(e.target.value)}
105
107
  onSubmit={(term) => {
106
- const path = formatSearchPath(term)
108
+ const path = formatSearchPath({
109
+ term,
110
+ sort: sort as SearchState['sort'],
111
+ })
107
112
 
108
113
  onSearchSelection(term, path)
109
114
  router.push(path)
@@ -115,7 +120,7 @@ const SearchInput = forwardRef<SearchInputRef, SearchInputProps>(
115
120
 
116
121
  {searchDropdownVisible && (
117
122
  <Suspense fallback={null}>
118
- <SearchDropdown />
123
+ <SearchDropdown sort={sort} />
119
124
  </Suspense>
120
125
  )}
121
126
  </UISearchInput>
@@ -5,6 +5,7 @@ import {
5
5
  } from '@faststore/ui'
6
6
  import type { HTMLAttributes } from 'react'
7
7
 
8
+ import { SearchState } from '@faststore/sdk'
8
9
  import type { StoreSuggestionTerm } from '@generated/graphql'
9
10
  import { formatSearchPath } from 'src/sdk/search/formatSearchPath'
10
11
  import useTopSearch from 'src/sdk/search/useTopSearch'
@@ -14,9 +15,13 @@ export interface SearchTopProps extends HTMLAttributes<HTMLDivElement> {
14
15
  * List of top searched items
15
16
  */
16
17
  topTerms?: StoreSuggestionTerm[]
18
+ /**
19
+ * Default sort by value
20
+ */
21
+ sort?: string
17
22
  }
18
23
 
19
- function SearchTop({ topTerms, ...otherProps }: SearchTopProps) {
24
+ function SearchTop({ topTerms, sort, ...otherProps }: SearchTopProps) {
20
25
  const {
21
26
  values: { onSearchSelection },
22
27
  } = useSearch()
@@ -34,9 +39,18 @@ function SearchTop({ topTerms, ...otherProps }: SearchTopProps) {
34
39
  value={term.value}
35
40
  index={index}
36
41
  linkProps={{
37
- href: formatSearchPath(term.value),
42
+ href: formatSearchPath({
43
+ term: term.value,
44
+ sort: sort as SearchState['sort'],
45
+ }),
38
46
  onClick: () =>
39
- onSearchSelection?.(term.value, formatSearchPath(term.value)),
47
+ onSearchSelection?.(
48
+ term.value,
49
+ formatSearchPath({
50
+ term: term.value,
51
+ sort: sort as SearchState['sort'],
52
+ })
53
+ ),
40
54
  }}
41
55
  />
42
56
  ))}
@@ -13,16 +13,34 @@ const OptionsMap = {
13
13
  }
14
14
 
15
15
  const keys = Object.keys(OptionsMap) as Array<keyof typeof OptionsMap>
16
+ export interface SortProps {
17
+ label?: string
18
+ options?: {
19
+ price_desc?: string
20
+ price_asc?: string
21
+ orders_desc?: string
22
+ name_asc?: string
23
+ name_desc?: string
24
+ release_desc?: string
25
+ discount_desc?: string
26
+ score_desc?: string
27
+ }
28
+ }
16
29
 
17
- function Sort() {
30
+ function Sort({ label = '', options = OptionsMap }: SortProps) {
18
31
  const { state, setState } = useSearch()
19
32
 
33
+ const optionsMap = Object.keys(options).reduce((acc, currentKey) => {
34
+ acc[currentKey] = options[currentKey] ?? OptionsMap[currentKey]
35
+ return acc
36
+ }, {})
37
+
20
38
  return (
21
39
  <SelectField
22
40
  id="sort-select"
23
41
  className="sort / text__title-mini-alt"
24
- label="Sort by"
25
- options={OptionsMap}
42
+ label={label}
43
+ options={optionsMap}
26
44
  onChange={(e) => {
27
45
  const sort = keys[e.target.selectedIndex]
28
46
 
@@ -1,8 +1,10 @@
1
1
  import { useMemo } from 'react'
2
2
 
3
3
  import type { ProductDetailsFragment_ProductFragment } from '@generated/graphql'
4
-
5
- import ProductShelf from '../ProductShelf'
4
+ import UIProductShelf from 'src/components/ui/ProductShelf'
5
+ import { useInView } from 'react-intersection-observer'
6
+ import styles from '../ProductShelf/section.module.scss'
7
+ import Section from '../Section'
6
8
 
7
9
  interface Props {
8
10
  items: number
@@ -12,13 +14,25 @@ interface Props {
12
14
  }
13
15
 
14
16
  const CrossSellingShelf = ({ items, title, context, kind }: Props) => {
17
+ const { ref, inView } = useInView()
18
+
15
19
  const selectedFacets = useMemo(
16
20
  () => [{ key: kind, value: context.isVariantOf.productGroupID }],
17
21
  [kind, context.isVariantOf.productGroupID]
18
22
  )
19
23
 
20
24
  return (
21
- <ProductShelf first={items} title={title} selectedFacets={selectedFacets} />
25
+ <Section
26
+ className={`${styles.section} section-product-shelf layout__section`}
27
+ ref={ref}
28
+ >
29
+ <UIProductShelf
30
+ inView={inView}
31
+ first={items}
32
+ title={title}
33
+ selectedFacets={selectedFacets}
34
+ />
35
+ </Section>
22
36
  )
23
37
  }
24
38
 
@@ -14,6 +14,9 @@ export interface NavbarProps {
14
14
  alt: string
15
15
  src: string
16
16
  }
17
+ searchInput: {
18
+ sort: string
19
+ }
17
20
  signInButton: {
18
21
  icon: {
19
22
  alt: string
@@ -50,6 +53,7 @@ export interface NavbarProps {
50
53
 
51
54
  function Navbar({
52
55
  logo,
56
+ searchInput,
53
57
  cartIcon,
54
58
  signInButton,
55
59
  navigation: {
@@ -69,6 +73,7 @@ function Navbar({
69
73
  home={home}
70
74
  menu={menu}
71
75
  logo={logo}
76
+ searchInput={searchInput}
72
77
  cart={cartIcon}
73
78
  links={pageLinks}
74
79
  signIn={{ button: signInButton }}
@@ -1,49 +1,51 @@
1
- import { useSearch } from '@faststore/sdk'
2
- import {
3
- Button as UIButton,
4
- LinkButton as UILinkButton,
5
- Skeleton as UISkeleton,
6
- } from '@faststore/ui'
7
- import { NextSeo } from 'next-seo'
8
- import type { MouseEvent } from 'react'
9
- import { Suspense, lazy } from 'react'
10
-
11
- import { Icon, useUI } from '@faststore/ui'
12
- import Filter from 'src/components/search/Filter'
13
- import Sort from 'src/components/search/Sort'
14
- import FilterSkeleton from 'src/components/skeletons/FilterSkeleton'
15
- import ProductGridSkeleton from 'src/components/skeletons/ProductGridSkeleton'
16
1
  import { mark } from 'src/sdk/tests/mark'
17
2
 
3
+ import { ServerCollectionPageQueryQuery } from '@generated/graphql'
4
+ import ProductGallery, {
5
+ ProductGalleryProps,
6
+ } from 'src/components/ui/ProductGallery/ProductGallery'
7
+ import { SearchPageContextType } from 'src/pages/s'
18
8
  import Section from '../Section'
19
9
  import EmptyGallery from './EmptyGallery'
20
10
  import styles from './section.module.scss'
21
- import { useDelayedFacets } from './useDelayedFacets'
22
- import { useDelayedPagination } from './useDelayedPagination'
23
11
  import { useGalleryQuery } from './useGalleryQuery'
24
- import { useProductsPrefetch } from './usePageProducts'
25
12
 
26
- const GalleryPage = lazy(() => import('./ProductGalleryPage'))
27
- const GalleryPageSkeleton = <ProductGridSkeleton loading />
13
+ type ProductGalleryContext = ServerCollectionPageQueryQuery['collection']
28
14
 
29
- interface Props {
30
- title: string
31
- searchTerm?: string
15
+ export interface ProductGallerySectionProps {
16
+ context?: ProductGalleryContext
17
+ searchTermLabel?: ProductGalleryProps['searchTermLabel']
18
+ totalCountLabel?: ProductGalleryProps['totalCountLabel']
19
+ filter: ProductGalleryProps['filter']
20
+ previousPageButton?: ProductGalleryProps['previousPageButton']
21
+ itemsPerPage?: ProductGalleryProps['itemsPerPage']
22
+ loadMorePageButton?: ProductGalleryProps['loadMorePageButton']
23
+ sortBySelector?: ProductGalleryProps['sortBySelector']
24
+ productCard?: ProductGalleryProps['productCard']
32
25
  }
33
26
 
34
- function ProductGallery({ title, searchTerm }: Props) {
35
- const { openFilter } = useUI()
36
- const { pages, addNextPage, addPrevPage } = useSearch()
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
37
33
 
38
- const { data } = useGalleryQuery()
39
- const facets = useDelayedFacets(data)
40
- const totalCount = data?.search.products.pageInfo.totalCount ?? 0
41
- const { next, prev } = useDelayedPagination(totalCount)
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]
42
+ : ['']
42
43
 
43
- useProductsPrefetch(prev ? prev.cursor : null)
44
- useProductsPrefetch(next ? next.cursor : null)
44
+ const { data: productGalleryData } = useGalleryQuery()
45
+ const totalCount =
46
+ productGalleryData?.search.products.pageInfo.totalCount ?? 0
45
47
 
46
- if (data && totalCount === 0) {
48
+ if (productGalleryData && totalCount === 0) {
47
49
  return (
48
50
  <Section
49
51
  className={`${styles.section} section-product-gallery layout__content`}
@@ -59,128 +61,16 @@ function ProductGallery({ title, searchTerm }: Props) {
59
61
  <Section
60
62
  className={`${styles.section} section-product-gallery layout__content-full`}
61
63
  >
62
- <section data-testid="product-gallery" data-fs-product-listing>
63
- {searchTerm && (
64
- <header
65
- data-fs-product-listing-search-term
66
- className="layout__content"
67
- >
68
- <h1>
69
- Showing results for: <span>{searchTerm}</span>
70
- </h1>
71
- </header>
72
- )}
73
- <div data-fs-product-listing-content-grid className="layout__content">
74
- <div data-fs-product-listing-filters>
75
- <FilterSkeleton loading={facets?.length === 0}>
76
- <Filter facets={facets} />
77
- </FilterSkeleton>
78
- </div>
79
- <div data-fs-product-listing-results-count data-count={totalCount}>
80
- <UISkeleton
81
- data-fs-product-listing-results-count-skeleton
82
- loading={!data}
83
- size={{ width: '100%', height: '1.5rem' }}
84
- >
85
- <h2 data-testid="total-product-count">{totalCount} Results</h2>
86
- </UISkeleton>
87
- </div>
88
- <div data-fs-product-listing-sort>
89
- <UISkeleton
90
- data-fs-product-listing-sort-skeleton
91
- loading={facets?.length === 0}
92
- size={{ width: 'auto', height: '1.5rem' }}
93
- >
94
- <Sort />
95
- </UISkeleton>
96
- <UISkeleton
97
- data-fs-product-listing-filter-button-skeleton
98
- loading={facets?.length === 0}
99
- size={{ width: '6rem', height: '1.5rem' }}
100
- >
101
- <UIButton
102
- variant="tertiary"
103
- data-testid="open-filter-button"
104
- icon={<Icon name="FadersHorizontal" width={16} height={16} />}
105
- iconPosition="left"
106
- aria-label="Open Filters"
107
- onClick={openFilter}
108
- >
109
- Filters
110
- </UIButton>
111
- </UISkeleton>
112
- </div>
113
- <div data-fs-product-listing-results>
114
- {/* Add link to previous page. This helps on SEO */}
115
- {prev !== false && (
116
- <div data-fs-product-listing-pagination="top">
117
- <NextSeo
118
- additionalLinkTags={[{ rel: 'prev', href: prev.link }]}
119
- />
120
- <UILinkButton
121
- onClick={(e: MouseEvent<HTMLElement>) => {
122
- e.currentTarget.blur()
123
- e.preventDefault()
124
- addPrevPage()
125
- }}
126
- href={prev.link}
127
- rel="prev"
128
- variant="secondary"
129
- iconPosition="left"
130
- icon={
131
- <Icon
132
- name="ArrowLeft"
133
- width={16}
134
- height={16}
135
- weight="bold"
136
- />
137
- }
138
- >
139
- Previous Page
140
- </UILinkButton>
141
- </div>
142
- )}
143
- {/* Render ALL products */}
144
- {data ? (
145
- <Suspense fallback={GalleryPageSkeleton}>
146
- {pages.map((page) => (
147
- <GalleryPage
148
- key={`gallery-page-${page}`}
149
- page={page}
150
- title={title}
151
- />
152
- ))}
153
- </Suspense>
154
- ) : (
155
- GalleryPageSkeleton
156
- )}
157
- {/* Add link to next page. This helps on SEO */}
158
- {next !== false && (
159
- <div data-fs-product-listing-pagination="bottom">
160
- <NextSeo
161
- additionalLinkTags={[{ rel: 'next', href: next.link }]}
162
- />
163
- <UILinkButton
164
- testId="show-more"
165
- onClick={(e: MouseEvent<HTMLElement>) => {
166
- e.currentTarget.blur()
167
- e.preventDefault()
168
- addNextPage()
169
- }}
170
- href={next.link}
171
- rel="next"
172
- variant="secondary"
173
- >
174
- Load more products
175
- </UILinkButton>
176
- </div>
177
- )}
178
- </div>
179
- </div>
180
- </section>
64
+ <ProductGallery
65
+ title={title}
66
+ searchTerm={searchTerm}
67
+ productGalleryData={productGalleryData}
68
+ totalCount={totalCount}
69
+ {...otherProps}
70
+ />
181
71
  </Section>
182
72
  )
183
73
  }
184
74
 
185
- ProductGallery.displayName = 'ProductGallery'
186
- export default mark(ProductGallery)
75
+ ProductGallerySection.displayName = 'ProductGallery'
76
+ export default mark(ProductGallerySection)
@@ -1,91 +1,22 @@
1
- import { useEffect, useId, useRef } from 'react'
2
1
  import { useInView } from 'react-intersection-observer'
3
-
4
- import { ProductShelf as UIProductShelf } from '@faststore/ui'
5
-
6
- import type { ProductsQueryQueryVariables } from '@generated/graphql'
7
- import ProductShelfSkeleton from 'src/components/skeletons/ProductShelfSkeleton'
8
- import { useViewItemListEvent } from 'src/sdk/analytics/hooks/useViewItemListEvent'
9
- import { useProductsQuery } from 'src/sdk/product/useProductsQuery'
10
- import { textToKebabCase } from 'src/utils/utilities'
11
-
12
- import Carousel from '../../ui/Carousel'
13
2
  import Section from '../Section'
14
- import { Components } from './Overrides'
15
- const { ProductCard } = Components
16
3
 
17
4
  import styles from './section.module.scss'
5
+ import ProductShelf, { ProductShelfProps } from 'src/components/ui/ProductShelf'
18
6
 
19
- interface ProductShelfProps extends Partial<ProductsQueryQueryVariables> {
20
- title: string
21
- withDivisor?: boolean
22
- }
23
-
24
- function ProductShelf({
25
- title,
26
- withDivisor = false,
27
- ...variables
28
- }: ProductShelfProps) {
29
- const titleId = textToKebabCase(title)
30
- const id = useId()
31
- const viewedOnce = useRef(false)
7
+ function ProductShelfSection({
8
+ ...otherProps
9
+ }: Omit<ProductShelfProps, 'inView'>) {
32
10
  const { ref, inView } = useInView()
33
- const products = useProductsQuery(variables)
34
- const productEdges = products?.edges ?? []
35
- const aspectRatio = 1
36
-
37
- const { sendViewItemListEvent } = useViewItemListEvent({
38
- products: productEdges,
39
- title,
40
- page: 0,
41
- pageSize: 0,
42
- })
43
-
44
- useEffect(() => {
45
- if (inView && !viewedOnce.current && productEdges.length) {
46
- sendViewItemListEvent()
47
-
48
- viewedOnce.current = true
49
- }
50
- }, [inView, productEdges.length, sendViewItemListEvent])
51
-
52
- if (products?.edges.length === 0) {
53
- return null
54
- }
55
11
 
56
12
  return (
57
13
  <Section
58
- className={`${styles.section} section-product-shelf layout__section ${
59
- withDivisor ? 'section__divisor' : ''
60
- }`}
14
+ className={`${styles.section} section-product-shelf layout__section`}
61
15
  ref={ref}
62
16
  >
63
- <h2 className="text__title-section layout__content">{title}</h2>
64
- <ProductShelfSkeleton
65
- aspectRatio={aspectRatio}
66
- loading={products === undefined}
67
- >
68
- <UIProductShelf>
69
- <Carousel id={titleId || id}>
70
- {productEdges.map((product, idx) => (
71
- <ProductCard
72
- bordered
73
- key={`${product.node.id}`}
74
- product={product.node}
75
- index={idx + 1}
76
- aspectRatio={aspectRatio}
77
- imgProps={{
78
- width: 216,
79
- height: 216,
80
- sizes: '(max-width: 768px) 42vw, 30vw',
81
- }}
82
- />
83
- ))}
84
- </Carousel>
85
- </UIProductShelf>
86
- </ProductShelfSkeleton>
17
+ <ProductShelf inView={inView} {...otherProps} />
87
18
  </Section>
88
19
  )
89
20
  }
90
21
 
91
- export default ProductShelf
22
+ export default ProductShelfSection
@@ -7,10 +7,3 @@
7
7
  margin-bottom: var(--fs-spacing-3);
8
8
  }
9
9
  }
10
-
11
- .section__divisor {
12
- padding-top: var(--fs-spacing-5);
13
- border-top: var(--fs-border-width) solid var(--fs-border-color-light);
14
-
15
- @include media(">=notebook") { padding-top: var(--fs-spacing-9); }
16
- }