@graphcommerce/magento-search 8.1.0-canary.9 → 9.0.0-canary.55

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,97 @@
1
1
  # Change Log
2
2
 
3
+ ## 9.0.0-canary.55
4
+
5
+ ## 9.0.0-canary.54
6
+
7
+ ## 8.1.0-canary.53
8
+
9
+ ## 8.1.0-canary.52
10
+
11
+ ## 8.1.0-canary.51
12
+
13
+ ## 8.1.0-canary.50
14
+
15
+ ## 8.1.0-canary.49
16
+
17
+ ## 8.1.0-canary.48
18
+
19
+ ## 8.1.0-canary.47
20
+
21
+ ## 8.1.0-canary.46
22
+
23
+ ## 8.1.0-canary.45
24
+
25
+ ## 8.1.0-canary.44
26
+
27
+ ## 8.1.0-canary.43
28
+
29
+ ## 8.1.0-canary.42
30
+
31
+ ## 8.1.0-canary.41
32
+
33
+ ## 8.1.0-canary.40
34
+
35
+ ## 8.1.0-canary.39
36
+
37
+ ## 8.1.0-canary.38
38
+
39
+ ## 8.1.0-canary.37
40
+
41
+ ## 8.1.0-canary.36
42
+
43
+ ## 8.1.0-canary.35
44
+
45
+ ## 8.1.0-canary.34
46
+
47
+ ## 8.1.0-canary.33
48
+
49
+ ## 8.1.0-canary.32
50
+
51
+ ## 8.1.0-canary.31
52
+
53
+ ## 8.1.0-canary.30
54
+
55
+ ## 8.1.0-canary.29
56
+
57
+ ## 8.1.0-canary.28
58
+
59
+ ## 8.1.0-canary.27
60
+
61
+ ## 8.1.0-canary.26
62
+
63
+ ## 8.1.0-canary.25
64
+
65
+ ## 8.1.0-canary.24
66
+
67
+ ## 8.1.0-canary.23
68
+
69
+ ## 8.1.0-canary.22
70
+
71
+ ## 8.1.0-canary.21
72
+
73
+ ## 8.1.0-canary.20
74
+
75
+ ## 8.1.0-canary.19
76
+
77
+ ## 8.1.0-canary.18
78
+
79
+ ## 8.1.0-canary.17
80
+
81
+ ## 8.1.0-canary.16
82
+
83
+ ## 8.1.0-canary.15
84
+
85
+ ## 8.1.0-canary.14
86
+
87
+ ## 8.1.0-canary.13
88
+
89
+ ## 8.1.0-canary.12
90
+
91
+ ## 8.1.0-canary.11
92
+
93
+ ## 8.1.0-canary.10
94
+
3
95
  ## 8.1.0-canary.9
4
96
 
5
97
  ## 8.1.0-canary.8
@@ -2,16 +2,14 @@ import { extendableComponent } from '@graphcommerce/next-ui'
2
2
  import { Trans } from '@lingui/react'
3
3
  import { Box, SxProps, Theme, Typography } from '@mui/material'
4
4
 
5
- export type NoSearchResultsProps = { search: string; sx?: SxProps<Theme> }
5
+ export type NoSearchResultsProps = { sx?: SxProps<Theme> }
6
6
 
7
7
  const name = 'NoSearchResults' as const
8
8
  const parts = ['root'] as const
9
9
  const { classes } = extendableComponent(name, parts)
10
10
 
11
11
  export function NoSearchResults(props: NoSearchResultsProps) {
12
- const { search, sx = [] } = props
13
-
14
- const term = `'${search}'`
12
+ const { sx = [] } = props
15
13
 
16
14
  return (
17
15
  <Box
@@ -26,7 +24,7 @@ export function NoSearchResults(props: NoSearchResultsProps) {
26
24
  ]}
27
25
  >
28
26
  <Typography variant='h5' align='center'>
29
- <Trans id="We couldn't find any results for {term}" values={{ term }} />
27
+ <Trans id="We couldn't find any products." />
30
28
  </Typography>
31
29
  <p>
32
30
  <Trans id='Try a different search' />
@@ -0,0 +1,170 @@
1
+ import type {
2
+ MenuQueryFragment,
3
+ CategoryTreeItem,
4
+ NavigationItemFragment,
5
+ } from '@graphcommerce/magento-category'
6
+ import {
7
+ ProductFiltersProCategoryAccordion,
8
+ ProductFiltersProCategoryAccordionProps,
9
+ useProductFiltersPro,
10
+ } from '@graphcommerce/magento-product'
11
+ import { filterNonNullableKeys } from '@graphcommerce/next-ui'
12
+ import { useMemo } from 'react'
13
+
14
+ type MenuItem = NavigationItemFragment & {
15
+ children?: Array<MenuItem | null | undefined> | null | undefined
16
+ }
17
+
18
+ type TreeItem = NavigationItemFragment & {
19
+ visible?: boolean
20
+ parent: TreeItem | undefined
21
+ children: TreeItem[]
22
+ }
23
+
24
+ function menuItemToTreeItem(item: MenuItem, parent: TreeItem | undefined): TreeItem {
25
+ const newItem: TreeItem = { ...item, parent, children: [] }
26
+ newItem.children = filterNonNullableKeys(item.children).map((child) =>
27
+ menuItemToTreeItem(child, newItem),
28
+ )
29
+ return newItem
30
+ }
31
+
32
+ function treeFind<U extends TreeItem>(tree: U, fn: (item: U) => boolean): U | undefined {
33
+ if (fn(tree)) return tree
34
+ for (const child of tree.children ?? []) {
35
+ const found = treeFind<U>(child as U, fn)
36
+ if (found) return found
37
+ }
38
+ return undefined
39
+ }
40
+
41
+ function treeFlatMap<U extends TreeItem, R>(
42
+ tree: U | undefined,
43
+ cb: (item: U, level: number) => R,
44
+ _level = 0,
45
+ ): R[] {
46
+ if (!tree) return []
47
+
48
+ const mapped = cb(tree, _level)
49
+ const children = tree.children.flatMap((child) => treeFlatMap(child as U, cb, _level + 1))
50
+ return [mapped, ...children]
51
+ }
52
+
53
+ function treeWalkFilter<U extends TreeItem>(
54
+ treeItem: U,
55
+ fn: (newTreeItem: U) => boolean,
56
+ ): U | undefined {
57
+ const children = treeItem.children.map((child) => treeWalkFilter(child as U, fn)).filter(Boolean)
58
+ const newTreeItem = { ...treeItem, children }
59
+ return children.length > 0 || fn(newTreeItem) ? newTreeItem : undefined
60
+ }
61
+
62
+ function treeWalk<U extends TreeItem>(root: U | undefined, fn: (item: U) => void) {
63
+ if (!root) return
64
+ root.children.map((child) => treeWalk(child as U, fn))
65
+ fn(root)
66
+ }
67
+
68
+ function allParents<U extends TreeItem>(item: U): U[] {
69
+ const parents = item.parent ? [item.parent, ...allParents(item.parent)] : []
70
+ return parents as U[]
71
+ }
72
+
73
+ function isParent<U extends TreeItem>(item: U, parent: U): boolean {
74
+ let p = parent.parent
75
+ while (p) {
76
+ if (p.uid === item.uid) return true
77
+ p = p.parent
78
+ }
79
+ return false
80
+ }
81
+
82
+ type ProductFiltersProCategorySectionSearchProps = Omit<
83
+ ProductFiltersProCategoryAccordionProps,
84
+ 'categoryTree' | 'onChange'
85
+ > & {
86
+ menu?: MenuQueryFragment['menu']
87
+ }
88
+
89
+ export function ProductFiltersProCategorySectionSearch(
90
+ props: ProductFiltersProCategorySectionSearchProps,
91
+ ) {
92
+ const { menu } = props
93
+ const { form, submit, params, aggregations, appliedAggregations } = useProductFiltersPro()
94
+ const currentFilter = params.filters.category_uid?.in
95
+
96
+ const categoryTree = useMemo(() => {
97
+ const rootCategory = menu?.items?.[0]
98
+ if (!rootCategory) return []
99
+
100
+ let tree: TreeItem | undefined = menuItemToTreeItem(rootCategory, undefined)
101
+
102
+ const currentCounts = aggregations?.find((a) => a?.attribute_code === 'category_uid')?.options
103
+
104
+ const activeItem = treeFind(tree, (item) => currentFilter?.includes(item.uid) ?? false) ?? tree
105
+
106
+ // Mark all parents as visible if they have a count.
107
+ treeWalk(tree, (item) => {
108
+ const count = currentCounts?.find((i) => item.uid === i?.value)?.count ?? null
109
+ if (!count) return
110
+
111
+ item.visible = true
112
+ allParents(item).forEach((p) => {
113
+ p.visible = true
114
+ })
115
+ })
116
+
117
+ tree = treeWalkFilter(tree, (item) => {
118
+ // If currently active
119
+ if (activeItem.uid === item.uid) return true
120
+
121
+ if (!item.include_in_menu) return false
122
+
123
+ // Show direct children of active item.
124
+ if (activeItem.uid === item.parent?.uid) return true
125
+
126
+ // Show siblings if there are are only a few children.
127
+ if (activeItem.children.length <= 5 && item.parent?.uid === activeItem.parent?.uid)
128
+ return true
129
+
130
+ return false
131
+ })
132
+
133
+ // Als een child een count heeft, dan alle parents ook een count geven
134
+
135
+ return treeFlatMap<TreeItem, CategoryTreeItem>(tree, (item, level) => {
136
+ const count = currentCounts?.find((i) => item.uid === i?.value)?.count ?? null
137
+
138
+ return {
139
+ uid: item.uid,
140
+ title: item.name,
141
+ value: item.url_path ?? '',
142
+ selected: currentFilter?.includes(item.uid) ?? false,
143
+ indent: level - 1,
144
+ count,
145
+ isBack: isParent(item, activeItem),
146
+ visible: item.visible,
147
+ }
148
+ })
149
+ .slice(1)
150
+ .filter((c) => c.visible)
151
+ }, [appliedAggregations, currentFilter, menu?.items])
152
+
153
+ if (!categoryTree) return null
154
+
155
+ return (
156
+ <ProductFiltersProCategoryAccordion
157
+ categoryTree={categoryTree}
158
+ {...props}
159
+ onChange={async (item) => {
160
+ form.setValue('filters', {
161
+ category_uid: {
162
+ in: item.uid === currentFilter?.[0] ? null : [item?.uid],
163
+ },
164
+ })
165
+
166
+ await submit()
167
+ }}
168
+ />
169
+ )
170
+ }
@@ -0,0 +1,36 @@
1
+ import { ProductListParams, useProductFiltersPro } from '@graphcommerce/magento-product'
2
+ import { useWatch } from '@graphcommerce/react-hook-form'
3
+ import { Trans } from '@lingui/macro'
4
+ import { Box } from '@mui/material'
5
+
6
+ type ProductFiltersProSearchHeaderProps = {
7
+ params: ProductListParams
8
+ /**
9
+ * Provide a text when there is no term searched
10
+ */
11
+ children: React.ReactNode
12
+ }
13
+
14
+ export function ProductFiltersProSearchTerm(props: ProductFiltersProSearchHeaderProps) {
15
+ const { params, children } = props
16
+ const { form } = useProductFiltersPro()
17
+ const resultSearch = params.search ?? ''
18
+ const targetSearch = useWatch({ control: form.control, name: 'search' }) ?? ''
19
+
20
+ const remaining = targetSearch.startsWith(resultSearch)
21
+ ? targetSearch.slice(resultSearch.length)
22
+ : ''
23
+
24
+ if (!resultSearch && !targetSearch) return children
25
+
26
+ const search = (
27
+ <>
28
+ <Box component='span'>{resultSearch}</Box>
29
+ <Box component='span' sx={{ color: 'text.disabled' }}>
30
+ {remaining}
31
+ </Box>
32
+ </>
33
+ )
34
+
35
+ return <Trans>Results for ‘{search}’</Trans>
36
+ }
@@ -0,0 +1,66 @@
1
+ import { IconSvg, iconSearch, showPageLoadIndicator } from '@graphcommerce/next-ui'
2
+ import { Fab, FabProps } from '@mui/material'
3
+ import { useMemo, useRef, useState } from 'react'
4
+ import { ProductFiltersProSearchInputProps } from './ProductFiltersProSearchInput'
5
+ import { useSearchPageAndParam } from './useSearchPageAndParam'
6
+ import dynamic from 'next/dynamic'
7
+
8
+ type ProductFiltersProSearchFieldProps = ProductFiltersProSearchInputProps & {
9
+ fab?: FabProps
10
+ }
11
+
12
+ const ProductFiltersProSearchInputLazy = dynamic(
13
+ async () => (await import('./ProductFiltersProSearchInput')).ProductFiltersProSearchOutlinedInput,
14
+ )
15
+
16
+ export function ProductFiltersProSearchField(props: ProductFiltersProSearchFieldProps) {
17
+ const { fab, formControl } = props
18
+
19
+ const [searchPage] = useSearchPageAndParam()
20
+ const [expanded, setExpanded] = useState(searchPage)
21
+ useMemo(() => {
22
+ if (expanded !== searchPage) setExpanded(searchPage)
23
+ }, [expanded, searchPage])
24
+
25
+ const visible = expanded || searchPage
26
+ const inputRef = useRef<HTMLInputElement>(null)
27
+
28
+ return (
29
+ <>
30
+ {visible && (
31
+ <ProductFiltersProSearchInputLazy
32
+ {...props}
33
+ formControl={formControl}
34
+ inputRef={inputRef}
35
+ buttonProps={{
36
+ onClick: () => {
37
+ setExpanded(false)
38
+ },
39
+ }}
40
+ onBlur={() => {
41
+ if (!searchPage && !showPageLoadIndicator.get()) setExpanded(false)
42
+ }}
43
+ />
44
+ )}
45
+ <Fab
46
+ onClick={() => {
47
+ setExpanded(true)
48
+ inputRef.current?.focus()
49
+ }}
50
+ color='inherit'
51
+ size='large'
52
+ {...fab}
53
+ sx={[
54
+ {
55
+ display: {
56
+ xs: visible ? 'none' : 'inline-flex',
57
+ },
58
+ },
59
+ ...(Array.isArray(fab?.sx) ? fab.sx : [fab?.sx]),
60
+ ]}
61
+ >
62
+ <IconSvg src={iconSearch} size='large' />
63
+ </Fab>
64
+ </>
65
+ )
66
+ }
@@ -0,0 +1,113 @@
1
+ import { globalFormContextRef } from '@graphcommerce/magento-product'
2
+ import { IconSvg, iconClose } from '@graphcommerce/next-ui'
3
+ import { t } from '@lingui/macro'
4
+ import {
5
+ ButtonBaseProps,
6
+ FormControl,
7
+ FormControlProps,
8
+ IconButton,
9
+ IconButtonProps,
10
+ InputBaseProps,
11
+ OutlinedInput,
12
+ OutlinedInputProps,
13
+ useForkRef,
14
+ } from '@mui/material'
15
+ import { useRouter } from 'next/router'
16
+ import { useEffect, useRef } from 'react'
17
+ import { useSearchPageAndParam } from './useSearchPageAndParam'
18
+
19
+ export function useProductFiltersProSearchInput<
20
+ P extends InputBaseProps & { buttonProps?: ButtonBaseProps },
21
+ >(props: P): P {
22
+ const { buttonProps = {}, inputRef } = props
23
+
24
+ const router = useRouter()
25
+ const [searchPage, searchParam] = useSearchPageAndParam()
26
+
27
+ const internalRef = useRef<HTMLInputElement>(null)
28
+ const ref = useForkRef(inputRef, internalRef)
29
+
30
+ useEffect(() => {
31
+ // When the user is not focussed on the search field and the value gets updated, update the form.
32
+ if (internalRef.current && internalRef.current !== document.activeElement && searchParam)
33
+ internalRef.current.value = searchParam
34
+ }, [searchParam])
35
+
36
+ const result: P = {
37
+ ...props,
38
+ inputRef: ref,
39
+ placeholder: t`Search all products...`,
40
+ name: 'search',
41
+ type: 'text',
42
+ onKeyDown: (e) => {
43
+ if (e.key === 'Enter') {
44
+ const context = globalFormContextRef.current
45
+ if (!context || !searchPage) {
46
+ return router.push(`/search/${e.currentTarget.value}`)
47
+ }
48
+ context.form.setValue('currentPage', 1)
49
+ context.form.setValue('search', e.currentTarget.value)
50
+ return context.submit()
51
+ }
52
+ props?.onKeyDown?.(e)
53
+ },
54
+ onChange: async (e) => {
55
+ const context = globalFormContextRef.current
56
+
57
+ // When we're not on the search page, we want to navigate as soon as possible.
58
+ // TODO: We only want to navigate once, and let the rest be handled by the search page.
59
+ if (!context || !searchPage) {
60
+ return router.push(`/search/${e.target.value}`)
61
+ }
62
+
63
+ context.form.setValue('currentPage', 1)
64
+ context.form.setValue('search', e.currentTarget.value)
65
+ await context.submit()
66
+
67
+ return props.onChange?.(e)
68
+ },
69
+ buttonProps: {
70
+ ...buttonProps,
71
+ onClick: async (e) => {
72
+ const context = globalFormContextRef.current
73
+
74
+ if (context?.form.getValues('search')) {
75
+ context.form.setValue('currentPage', 1)
76
+ context.form.setValue('search', '')
77
+ if (internalRef.current) internalRef.current.value = ''
78
+ await context.submit()
79
+ } else if (searchPage) {
80
+ router.back()
81
+ if (internalRef.current) internalRef.current.value = ''
82
+ } else {
83
+ buttonProps.onClick?.(e)
84
+ }
85
+ },
86
+ },
87
+ }
88
+ return result
89
+ }
90
+
91
+ export type ProductFiltersProSearchInputProps = OutlinedInputProps & {
92
+ formControl?: FormControlProps
93
+ buttonProps?: IconButtonProps
94
+ }
95
+
96
+ export function ProductFiltersProSearchOutlinedInput(props: ProductFiltersProSearchInputProps) {
97
+ const { buttonProps, formControl, size, ...rest } = useProductFiltersProSearchInput(props)
98
+
99
+ return (
100
+ <FormControl variant='outlined' size={size} {...formControl}>
101
+ <OutlinedInput
102
+ color='primary'
103
+ size={size}
104
+ endAdornment={
105
+ <IconButton color='inherit' size='small' {...buttonProps}>
106
+ <IconSvg src={iconClose} size='large' />
107
+ </IconButton>
108
+ }
109
+ {...rest}
110
+ />
111
+ </FormControl>
112
+ )
113
+ }
@@ -29,7 +29,9 @@ export function SearchForm(props: SearchFormProps) {
29
29
  const form = useForm({ defaultValues: { search } })
30
30
  const { handleSubmit, setValue, control } = form
31
31
 
32
- const submit = handleSubmit((formData) => router.replace(`/${urlHandle}/${formData.search}`))
32
+ const submit = handleSubmit((formData) =>
33
+ router.replace(`/${urlHandle}/${formData.search}`, undefined, { shallow: true }),
34
+ )
33
35
 
34
36
  const endAdornment = (
35
37
  <SearchFormAdornment
@@ -0,0 +1,12 @@
1
+ import { extractUrlQuery } from '@graphcommerce/magento-product'
2
+ import { useRouter } from 'next/router'
3
+
4
+ export function useSearchPageAndParam() {
5
+ const router = useRouter()
6
+
7
+ const path = router.asPath.startsWith('/c/') ? router.asPath.slice(3) : router.asPath.slice(1)
8
+ const [url, query] = extractUrlQuery({ url: path.split('#')[0].split('/') })
9
+ const searchParam = url?.startsWith('search') ? decodeURI(url.split('/')[1] ?? '') : null
10
+ const searchPage = router.asPath.startsWith('/search')
11
+ return [searchPage, searchParam] as const
12
+ }
@@ -0,0 +1,64 @@
1
+ import { useInContextQuery, useQuery } from '@graphcommerce/graphql'
2
+ import {
3
+ FilterFormProviderProps,
4
+ ProductFiltersDocument,
5
+ ProductFiltersQuery,
6
+ ProductListDocument,
7
+ ProductListParams,
8
+ ProductListQuery,
9
+ prefetchProductList,
10
+ toProductListParams,
11
+ useRouterFilterParams,
12
+ } from '@graphcommerce/magento-product'
13
+ import { StoreConfigDocument } from '@graphcommerce/magento-store'
14
+ import { useEventCallback } from '@mui/material'
15
+ import {
16
+ productListApplySearchDefaults,
17
+ useProductListApplySearchDefaults,
18
+ } from '../utils/productListApplySearchDefaults'
19
+
20
+ /**
21
+ * - Handles shallow routing requests
22
+ * - Handles customer specific product list queries
23
+ * - Creates a prefetch function to preload the product list
24
+ */
25
+ export function useProductList<
26
+ T extends ProductListQuery &
27
+ ProductFiltersQuery & {
28
+ params?: ProductListParams
29
+ },
30
+ >(props: T) {
31
+ const { params, shallow } = useRouterFilterParams(props)
32
+ const variables = useProductListApplySearchDefaults(params)
33
+ const result = useInContextQuery(ProductListDocument, { variables, skip: !shallow }, props)
34
+
35
+ const filters = useInContextQuery(
36
+ ProductFiltersDocument,
37
+ { variables: { search: params?.search }, skip: !shallow },
38
+ props,
39
+ )
40
+
41
+ const storeConfig = useQuery(StoreConfigDocument).data
42
+
43
+ const handleSubmit: NonNullable<FilterFormProviderProps['handleSubmit']> = useEventCallback(
44
+ async (formValues, next) => {
45
+ if (!storeConfig) return
46
+
47
+ await prefetchProductList(
48
+ productListApplySearchDefaults(toProductListParams(formValues), storeConfig),
49
+ next,
50
+ result.client,
51
+ true,
52
+ )
53
+ },
54
+ )
55
+
56
+ return {
57
+ ...props,
58
+ filters: filters.data.filters,
59
+ ...result.data,
60
+ params,
61
+ mask: result.mask,
62
+ handleSubmit,
63
+ }
64
+ }
package/index.ts CHANGED
@@ -1,22 +1,26 @@
1
- export * from './components/SearchDivider/SearchDivider'
2
- export * from './components/NoSearchResults/NoSearchResults'
1
+ export * from './CategorySearch.gql'
3
2
  export * from './components/CategorySearchResult/CategorySearchResult'
4
3
  export * from './components/CategorySearchResult/CategorySearchResults'
5
- export * from './components/SearchForm/SearchForm'
6
- export * from './components/SearchLink/SearchLink'
7
- export * from './CategorySearch.gql'
4
+ export * from './components/NoSearchResults/NoSearchResults'
8
5
  export * from './components/SearchButton/SearchButton'
9
6
  export * from './components/SearchContext/SearchContext'
7
+ export * from './components/SearchDivider/SearchDivider'
8
+ export * from './components/SearchForm/SearchForm'
9
+ export * from './components/SearchLink/SearchLink'
10
+ export * from './hooks/useProductList'
11
+ export * from './components/ProductFiltersPro/ProductFiltersProCategorySectionSearch'
12
+ export * from './components/ProductFiltersPro/ProductFiltersProSearchHeader'
10
13
 
11
14
  export {
12
- ProductListSort,
13
- ProductListFilters,
14
15
  ProductListCount,
16
+ ProductListFilters,
17
+ ProductListFiltersContainer,
15
18
  ProductListItemsBase,
16
19
  ProductListPagination,
17
- ProductListFiltersContainer,
18
20
  ProductListParamsProvider,
21
+ ProductListSort,
19
22
  } from '@graphcommerce/magento-product'
20
23
 
21
24
  export * from '@graphcommerce/magento-product/components/ProductFiltersPro'
22
25
  export * from './utils/productListApplySearchDefaults'
26
+ export * from './components/SearchForm/ProductFiltersProSearchField'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/magento-search",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "8.1.0-canary.9",
5
+ "version": "9.0.0-canary.55",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,16 +12,16 @@
12
12
  }
13
13
  },
14
14
  "peerDependencies": {
15
- "@graphcommerce/ecommerce-ui": "^8.1.0-canary.9",
16
- "@graphcommerce/eslint-config-pwa": "^8.1.0-canary.9",
17
- "@graphcommerce/graphql": "^8.1.0-canary.9",
18
- "@graphcommerce/image": "^8.1.0-canary.9",
19
- "@graphcommerce/magento-product": "^8.1.0-canary.9",
20
- "@graphcommerce/magento-store": "^8.1.0-canary.9",
21
- "@graphcommerce/next-ui": "^8.1.0-canary.9",
22
- "@graphcommerce/prettier-config-pwa": "^8.1.0-canary.9",
23
- "@graphcommerce/react-hook-form": "^8.1.0-canary.9",
24
- "@graphcommerce/typescript-config-pwa": "^8.1.0-canary.9",
15
+ "@graphcommerce/ecommerce-ui": "^9.0.0-canary.55",
16
+ "@graphcommerce/eslint-config-pwa": "^9.0.0-canary.55",
17
+ "@graphcommerce/graphql": "^9.0.0-canary.55",
18
+ "@graphcommerce/image": "^9.0.0-canary.55",
19
+ "@graphcommerce/magento-product": "^9.0.0-canary.55",
20
+ "@graphcommerce/magento-store": "^9.0.0-canary.55",
21
+ "@graphcommerce/next-ui": "^9.0.0-canary.55",
22
+ "@graphcommerce/prettier-config-pwa": "^9.0.0-canary.55",
23
+ "@graphcommerce/react-hook-form": "^9.0.0-canary.55",
24
+ "@graphcommerce/typescript-config-pwa": "^9.0.0-canary.55",
25
25
  "@lingui/core": "^4.2.1",
26
26
  "@lingui/macro": "^4.2.1",
27
27
  "@lingui/react": "^4.2.1",
@@ -1,18 +1,40 @@
1
- import { cloneDeep } from '@graphcommerce/graphql'
2
- import { ProductListParams } from '@graphcommerce/magento-product'
3
- import { StoreConfigQuery } from '@graphcommerce/magento-store'
1
+ import { cloneDeep, useQuery } from '@graphcommerce/graphql'
2
+ import { ProductListParams, ProductListQueryVariables } from '@graphcommerce/magento-product'
3
+ import { StoreConfigDocument, StoreConfigQuery } from '@graphcommerce/magento-store'
4
4
 
5
+ export function useProductListApplySearchDefaults(
6
+ params: ProductListParams | undefined,
7
+ ): ProductListQueryVariables | undefined {
8
+ const storeConfig = useQuery(StoreConfigDocument)
9
+
10
+ if (!params) return params
11
+
12
+ const newParams = cloneDeep(params)
13
+
14
+ if (!newParams.pageSize) newParams.pageSize = storeConfig.data?.storeConfig?.grid_per_page ?? 12
15
+
16
+ if (Object.keys(params.sort).length === 0) {
17
+ newParams.sort = { relevance: 'DESC' }
18
+ }
19
+
20
+ return newParams
21
+ }
22
+
23
+ export function productListApplySearchDefaults(
24
+ params: ProductListParams,
25
+ conf: StoreConfigQuery,
26
+ ): ProductListQueryVariables
5
27
  export function productListApplySearchDefaults(
6
28
  params: ProductListParams | undefined,
7
29
  conf: StoreConfigQuery,
8
- ) {
30
+ ): ProductListQueryVariables | undefined {
9
31
  if (!params) return params
10
32
  const newParams = cloneDeep(params)
11
33
 
12
34
  if (!newParams.pageSize) newParams.pageSize = conf.storeConfig?.grid_per_page ?? 12
13
35
 
14
36
  if (Object.keys(newParams.sort).length === 0) {
15
- newParams.sort = { relevance: 'ASC' }
37
+ newParams.sort = { relevance: 'DESC' }
16
38
  }
17
39
 
18
40
  return newParams