@graphcommerce/magento-product 9.0.0-canary.57 → 9.0.0-canary.59
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 +12 -0
- package/components/ProductFiltersPro/ProductFiltersProAggregations.tsx +17 -9
- package/components/ProductFiltersPro/ProductFiltersProSortChip.tsx +2 -0
- package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +2 -0
- package/components/ProductFiltersPro/useProductFiltersProSort.tsx +3 -1
- package/components/ProductListFilters/ProductListFilters.tsx +13 -19
- package/components/ProductListItems/ProductFilterTypes.graphql +8 -0
- package/components/ProductListItems/filterTypes.tsx +0 -2
- package/components/ProductListItems/filteredProductList.tsx +10 -10
- package/components/ProductListItems/getFilterTypes.ts +33 -4
- package/components/ProductListItems/productListApplyCategoryDefaults.ts +7 -1
- package/hooks/useProductList.ts +35 -10
- package/package.json +14 -14
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 9.0.0-canary.59
|
4
|
+
|
5
|
+
### Patch Changes
|
6
|
+
|
7
|
+
- [#2309](https://github.com/graphcommerce-org/graphcommerce/pull/2309) [`1fe4090`](https://github.com/graphcommerce-org/graphcommerce/commit/1fe409029671b841e582406251e45a9555fc78f9) - Hide SortChip or SortSection when there is only a single filter option. ([@Renzovh](https://github.com/Renzovh))
|
8
|
+
|
9
|
+
## 9.0.0-canary.58
|
10
|
+
|
11
|
+
### Patch Changes
|
12
|
+
|
13
|
+
- [#2328](https://github.com/graphcommerce-org/graphcommerce/pull/2328) [`ee04368`](https://github.com/graphcommerce-org/graphcommerce/commit/ee04368444f732e5541a595db6e2ef66d15add68) - Move to attributesList to get a list of filterable attributes instead of using an introspection query. `productFiltersProSectionRenderer` and `productFiltersProChipRenderer` keys now now one of `AttributeFrontendInputEnum`. ([@paales](https://github.com/paales))
|
14
|
+
|
3
15
|
## 9.0.0-canary.57
|
4
16
|
|
5
17
|
## 9.0.0-canary.56
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import { AttributeFrontendInputEnum } from '@graphcommerce/graphql-mesh'
|
1
2
|
import { ProductListFiltersFragment } from '../ProductListFilters/ProductListFilters.gql'
|
2
3
|
import { ProductFilterEqualChip } from './ProductFilterEqualChip'
|
3
4
|
import { ProductFilterEqualSection } from './ProductFilterEqualSection'
|
@@ -11,20 +12,24 @@ export type FilterProps = {
|
|
11
12
|
aggregation: NonNullable<NonNullable<ProductListFiltersFragment['aggregations']>[number]>
|
12
13
|
}
|
13
14
|
|
14
|
-
export type FilterRenderer = Record<
|
15
|
+
export type FilterRenderer = Record<AttributeFrontendInputEnum, React.FC<FilterProps>>
|
15
16
|
|
16
17
|
export type ProductFiltersProAggregationsProps = {
|
17
|
-
renderer?: FilterRenderer
|
18
|
+
renderer?: Partial<FilterRenderer>
|
18
19
|
}
|
19
20
|
|
20
|
-
export const productFiltersProSectionRenderer = {
|
21
|
-
|
22
|
-
|
21
|
+
export const productFiltersProSectionRenderer: Partial<FilterRenderer> = {
|
22
|
+
SELECT: ProductFilterEqualSection,
|
23
|
+
MULTISELECT: ProductFilterEqualSection,
|
24
|
+
BOOLEAN: ProductFilterEqualSection,
|
25
|
+
PRICE: ProductFilterRangeSection,
|
23
26
|
}
|
24
27
|
|
25
|
-
export const productFiltersProChipRenderer = {
|
26
|
-
|
27
|
-
|
28
|
+
export const productFiltersProChipRenderer: Partial<FilterRenderer> = {
|
29
|
+
SELECT: ProductFilterEqualChip,
|
30
|
+
MULTISELECT: ProductFilterEqualChip,
|
31
|
+
BOOLEAN: ProductFilterEqualChip,
|
32
|
+
PRICE: ProductFilterRangeChip,
|
28
33
|
}
|
29
34
|
|
30
35
|
export function ProductFiltersProAggregations(props: ProductFiltersProAggregationsProps) {
|
@@ -36,7 +41,10 @@ export function ProductFiltersProAggregations(props: ProductFiltersProAggregatio
|
|
36
41
|
{excludeCategory(applyAggregationCount(aggregations, appliedAggregations, params)).map(
|
37
42
|
(aggregation) => {
|
38
43
|
const filterType = filterTypes[aggregation.attribute_code]
|
39
|
-
if (!filterType)
|
44
|
+
if (!filterType) {
|
45
|
+
console.log('Filter not recognized', aggregation.attribute_code, filterTypes)
|
46
|
+
return null
|
47
|
+
}
|
40
48
|
|
41
49
|
const Component = renderer?.[filterType]
|
42
50
|
if (!Component) {
|
@@ -19,6 +19,8 @@ export function ProductFiltersProSortChip(props: ProductListActionSortProps) {
|
|
19
19
|
const { submit, form } = useProductFiltersPro()
|
20
20
|
const { options, showReset, selected, selectedLabel } = useProductFiltersProSort(props)
|
21
21
|
|
22
|
+
if ((options.length ?? 0) <= 1) return null
|
23
|
+
|
22
24
|
return (
|
23
25
|
<ChipOverlayOrPopper
|
24
26
|
{...rest}
|
@@ -13,6 +13,8 @@ export function ProductFiltersProSortSection(props: ProductFiltersProSortSection
|
|
13
13
|
const { form } = useProductFiltersPro()
|
14
14
|
const { options, showReset, selected } = useProductFiltersProSort(props)
|
15
15
|
|
16
|
+
if ((options.length ?? 0) <= 1) return null
|
17
|
+
|
16
18
|
return (
|
17
19
|
<ActionCardAccordion
|
18
20
|
sx={sx}
|
@@ -36,7 +36,9 @@ export function useProductFiltersProSort(props: ProductListActionSortProps) {
|
|
36
36
|
|
37
37
|
const conf = useQuery(StoreConfigDocument).data?.storeConfig
|
38
38
|
const defaultSortBy = (
|
39
|
-
category
|
39
|
+
category
|
40
|
+
? (category.default_sort_by ?? conf?.catalog_default_sort_by ?? 'position')
|
41
|
+
: 'relevance'
|
40
42
|
) as ProductFilterParams['sort']
|
41
43
|
|
42
44
|
const formSort = useWatch({ control, name: 'sort' })
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ChipMenuProps } from '@graphcommerce/next-ui'
|
2
|
-
import { FilterTypes } from '../ProductListItems/
|
2
|
+
import { FilterTypes } from '../ProductListItems/getFilterTypes'
|
3
3
|
import { FilterCheckboxType } from './FilterCheckboxType'
|
4
4
|
import { FilterEqualType } from './FilterEqualType'
|
5
5
|
import { FilterRangeType } from './FilterRangeType'
|
@@ -23,22 +23,16 @@ export function ProductListFilters(props: ProductFiltersProps) {
|
|
23
23
|
return null
|
24
24
|
|
25
25
|
switch (filterTypes[aggregation.attribute_code]) {
|
26
|
-
case '
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
{...aggregation}
|
37
|
-
{...chipMenuProps}
|
38
|
-
/>
|
39
|
-
)
|
40
|
-
}
|
41
|
-
|
26
|
+
case 'BOOLEAN':
|
27
|
+
return (
|
28
|
+
<FilterCheckboxType
|
29
|
+
key={aggregation.attribute_code}
|
30
|
+
{...aggregation}
|
31
|
+
{...chipMenuProps}
|
32
|
+
/>
|
33
|
+
)
|
34
|
+
case 'SELECT':
|
35
|
+
case 'MULTISELECT':
|
42
36
|
return (
|
43
37
|
<FilterEqualType
|
44
38
|
key={aggregation.attribute_code}
|
@@ -46,8 +40,7 @@ export function ProductListFilters(props: ProductFiltersProps) {
|
|
46
40
|
{...chipMenuProps}
|
47
41
|
/>
|
48
42
|
)
|
49
|
-
|
50
|
-
case 'FilterRangeTypeInput':
|
43
|
+
case 'PRICE':
|
51
44
|
return (
|
52
45
|
<FilterRangeType
|
53
46
|
key={aggregation.attribute_code}
|
@@ -56,6 +49,7 @@ export function ProductListFilters(props: ProductFiltersProps) {
|
|
56
49
|
/>
|
57
50
|
)
|
58
51
|
}
|
52
|
+
|
59
53
|
// console.log(
|
60
54
|
// 'Filter not recognized',
|
61
55
|
// aggregation.attribute_code,
|
@@ -67,8 +67,6 @@ export function isFilterTypeRange(filter: AnyFilterType): filter is FilterRangeT
|
|
67
67
|
)
|
68
68
|
}
|
69
69
|
|
70
|
-
export type FilterTypes = Partial<Record<string, string>>
|
71
|
-
|
72
70
|
export function toProductListParams(params: ProductFilterParams): ProductListParams {
|
73
71
|
const { sort, dir, filters, ...rest } = params
|
74
72
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import type {
|
2
2
|
FilterEqualTypeInput,
|
3
|
-
FilterMatchTypeInput,
|
4
3
|
FilterRangeTypeInput,
|
5
4
|
SortEnum,
|
6
5
|
} from '@graphcommerce/graphql-mesh'
|
7
|
-
import { useRouter } from 'next/router'
|
8
|
-
import { FilterTypes, ProductListParams } from './filterTypes'
|
9
6
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
10
7
|
import { equal } from '@wry/equality'
|
8
|
+
import { useRouter } from 'next/router'
|
9
|
+
import { ProductListParams } from './filterTypes'
|
10
|
+
import { FilterTypes } from './getFilterTypes'
|
11
11
|
|
12
12
|
export function parseParams(
|
13
13
|
url: string,
|
@@ -20,7 +20,7 @@ export function parseParams(
|
|
20
20
|
const typeMap = filterTypes
|
21
21
|
|
22
22
|
let error = false
|
23
|
-
query.reduce<string | undefined>((param, value) => {
|
23
|
+
query.map(decodeURI).reduce<string | undefined>((param, value) => {
|
24
24
|
// We parse everything in pairs, every second loop we parse
|
25
25
|
if (!param || param === 'q') return value
|
26
26
|
|
@@ -44,20 +44,20 @@ export function parseParams(
|
|
44
44
|
|
45
45
|
const [from, to] = value.split('-')
|
46
46
|
switch (typeMap[param]) {
|
47
|
-
case '
|
48
|
-
|
47
|
+
case 'BOOLEAN':
|
48
|
+
case 'SELECT':
|
49
|
+
case 'MULTISELECT':
|
50
|
+
categoryVariables.filters[param] = { in: value.split(',') } as FilterEqualTypeInput
|
49
51
|
return undefined
|
50
|
-
case '
|
52
|
+
case 'PRICE':
|
51
53
|
categoryVariables.filters[param] = {
|
52
54
|
...(from !== '*' && { from }),
|
53
55
|
...(to !== '*' && { to }),
|
54
56
|
} as FilterRangeTypeInput
|
55
57
|
return undefined
|
56
|
-
case 'FilterEqualTypeInput':
|
57
|
-
categoryVariables.filters[param] = { in: value.split(',') } as FilterEqualTypeInput
|
58
|
-
return undefined
|
59
58
|
}
|
60
59
|
|
60
|
+
// console.log('Filter not recognized', param, typeMap[param])
|
61
61
|
error = true
|
62
62
|
return undefined
|
63
63
|
}, undefined)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import { gql, ApolloClient, NormalizedCacheObject, TypedDocumentNode } from '@graphcommerce/graphql'
|
2
|
-
import type { Exact } from '@graphcommerce/graphql-mesh'
|
2
|
+
import type { AttributeFrontendInputEnum, Exact } from '@graphcommerce/graphql-mesh'
|
3
|
+
import { filterNonNullableKeys, nonNullable } from '@graphcommerce/next-ui'
|
4
|
+
import { ProductFilterTypesDocument } from './ProductFilterTypes.gql'
|
3
5
|
|
4
6
|
type FilterInputTypesQueryVariables = Exact<{ [key: string]: never }>
|
5
7
|
|
@@ -25,13 +27,40 @@ const FilterInputTypesDocument = gql`
|
|
25
27
|
}
|
26
28
|
` as TypedDocumentNode<FilterInputTypesQuery, FilterInputTypesQueryVariables>
|
27
29
|
|
30
|
+
export type FilterTypes = Partial<Record<string, AttributeFrontendInputEnum>>
|
31
|
+
|
28
32
|
export async function getFilterTypes(
|
29
33
|
client: ApolloClient<NormalizedCacheObject>,
|
30
|
-
|
34
|
+
isSearch: boolean = false,
|
35
|
+
): Promise<FilterTypes> {
|
36
|
+
if (import.meta.graphCommerce.magentoVersion >= 247) {
|
37
|
+
const types = await client.query({
|
38
|
+
query: ProductFilterTypesDocument,
|
39
|
+
variables: {
|
40
|
+
filters: isSearch ? { is_filterable_in_search: true } : {},
|
41
|
+
},
|
42
|
+
})
|
43
|
+
|
44
|
+
const typeMap: FilterTypes = Object.fromEntries(
|
45
|
+
filterNonNullableKeys(types.data.attributesList?.items, ['frontend_input'])
|
46
|
+
.map((i) => [i.code, i.frontend_input])
|
47
|
+
.filter(nonNullable),
|
48
|
+
)
|
49
|
+
|
50
|
+
return typeMap
|
51
|
+
}
|
52
|
+
|
31
53
|
const filterInputTypes = await client.query({ query: FilterInputTypesDocument })
|
32
54
|
|
33
|
-
const typeMap:
|
34
|
-
filterInputTypes.data?.__type.inputFields
|
55
|
+
const typeMap: FilterTypes = Object.fromEntries(
|
56
|
+
filterInputTypes.data?.__type.inputFields
|
57
|
+
.map<[string, AttributeFrontendInputEnum] | undefined>((field) => {
|
58
|
+
if (field.type.name === 'FilterEqualTypeInput') return [field.name, 'SELECT']
|
59
|
+
if (field.type.name === 'FilterRangeTypeInput') return [field.name, 'PRICE']
|
60
|
+
if (field.type.name === 'FilterMatchTypeInput') return [field.name, 'TEXT']
|
61
|
+
return undefined
|
62
|
+
})
|
63
|
+
.filter(nonNullable),
|
35
64
|
)
|
36
65
|
|
37
66
|
return typeMap
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import { cloneDeep, useQuery } from '@graphcommerce/graphql'
|
2
2
|
import { StoreConfigDocument, StoreConfigQuery } from '@graphcommerce/magento-store'
|
3
|
+
import { ProductListQueryVariables } from '../ProductList/ProductList.gql'
|
3
4
|
import { CategoryDefaultFragment } from './CategoryDefault.gql'
|
4
5
|
import { ProductListParams } from './filterTypes'
|
5
|
-
import { ProductListQueryVariables } from '../ProductList/ProductList.gql'
|
6
6
|
|
7
7
|
export function useProductListApplyCategoryDefaults(
|
8
8
|
params: ProductListParams | undefined,
|
@@ -66,3 +66,9 @@ export async function productListApplyCategoryDefaults(
|
|
66
66
|
|
67
67
|
return newParams
|
68
68
|
}
|
69
|
+
|
70
|
+
export function categoryDefaultsToProductListFilters(
|
71
|
+
variables: ProductListQueryVariables | undefined,
|
72
|
+
): ProductListQueryVariables {
|
73
|
+
return { ...variables, filters: { category_uid: variables?.filters?.category_uid } }
|
74
|
+
}
|
package/hooks/useProductList.ts
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import { debounce } from '@graphcommerce/ecommerce-ui'
|
2
2
|
import {
|
3
|
-
ApolloQueryResult,
|
4
3
|
ApolloClient,
|
5
4
|
useQuery,
|
6
5
|
useInContextQuery,
|
@@ -9,7 +8,12 @@ import {
|
|
9
8
|
import { StoreConfigDocument } from '@graphcommerce/magento-store'
|
10
9
|
import { showPageLoadIndicator } from '@graphcommerce/next-ui'
|
11
10
|
import { useEventCallback } from '@mui/material'
|
12
|
-
import {
|
11
|
+
import {
|
12
|
+
FilterFormProviderProps,
|
13
|
+
ProductFiltersDocument,
|
14
|
+
ProductFiltersQuery,
|
15
|
+
ProductFiltersQueryVariables,
|
16
|
+
} from '../components'
|
13
17
|
import {
|
14
18
|
ProductListDocument,
|
15
19
|
ProductListQuery,
|
@@ -20,6 +24,7 @@ import { ProductListParams, toProductListParams } from '../components/ProductLis
|
|
20
24
|
import { useRouterFilterParams } from '../components/ProductListItems/filteredProductList'
|
21
25
|
import {
|
22
26
|
productListApplyCategoryDefaults,
|
27
|
+
categoryDefaultsToProductListFilters,
|
23
28
|
useProductListApplyCategoryDefaults,
|
24
29
|
} from '../components/ProductListItems/productListApplyCategoryDefaults'
|
25
30
|
|
@@ -30,6 +35,7 @@ type Next = Parameters<NonNullable<FilterFormProviderProps['handleSubmit']>>[1]
|
|
30
35
|
export const prefetchProductList = debounce(
|
31
36
|
async (
|
32
37
|
variables: ProductListQueryVariables,
|
38
|
+
filtersVariables: ProductFiltersQueryVariables,
|
33
39
|
next: Next,
|
34
40
|
client: ApolloClient<any>,
|
35
41
|
shallow: boolean,
|
@@ -47,8 +53,7 @@ export const prefetchProductList = debounce(
|
|
47
53
|
const productFilters = client.query({
|
48
54
|
query: ProductFiltersDocument,
|
49
55
|
variables: {
|
50
|
-
|
51
|
-
search: variables.search,
|
56
|
+
...filtersVariables,
|
52
57
|
context,
|
53
58
|
},
|
54
59
|
})
|
@@ -91,16 +96,23 @@ export const prefetchProductList = debounce(
|
|
91
96
|
* - Handles customer specific product list queries
|
92
97
|
*/
|
93
98
|
export function useProductList<
|
94
|
-
T extends ProductListQuery &
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
T extends ProductListQuery &
|
100
|
+
ProductFiltersQuery & {
|
101
|
+
params?: ProductListParams
|
102
|
+
category?: CategoryDefaultFragment | null | undefined
|
103
|
+
},
|
98
104
|
>(props: T) {
|
99
105
|
const { category } = props
|
100
106
|
const { params, shallow } = useRouterFilterParams(props)
|
101
107
|
const variables = useProductListApplyCategoryDefaults(params, category)
|
102
108
|
|
103
109
|
const result = useInContextQuery(ProductListDocument, { variables, skip: !shallow }, props)
|
110
|
+
const filters = useInContextQuery(
|
111
|
+
ProductFiltersDocument,
|
112
|
+
{ variables: categoryDefaultsToProductListFilters(variables), skip: !shallow },
|
113
|
+
props,
|
114
|
+
)
|
115
|
+
|
104
116
|
const storeConfig = useQuery(StoreConfigDocument).data
|
105
117
|
|
106
118
|
const handleSubmit: NonNullable<FilterFormProviderProps['handleSubmit']> = useEventCallback(
|
@@ -115,9 +127,22 @@ export function useProductList<
|
|
115
127
|
|
116
128
|
const shallowNow =
|
117
129
|
JSON.stringify(vars.filters?.category_uid) === JSON.stringify(params?.filters.category_uid)
|
118
|
-
await prefetchProductList(
|
130
|
+
await prefetchProductList(
|
131
|
+
vars,
|
132
|
+
categoryDefaultsToProductListFilters(vars),
|
133
|
+
next,
|
134
|
+
result.client,
|
135
|
+
shallowNow,
|
136
|
+
)
|
119
137
|
},
|
120
138
|
)
|
121
139
|
|
122
|
-
return {
|
140
|
+
return {
|
141
|
+
...props,
|
142
|
+
filters: filters.data.filters,
|
143
|
+
...result.data,
|
144
|
+
params,
|
145
|
+
mask: result.mask,
|
146
|
+
handleSubmit,
|
147
|
+
}
|
123
148
|
}
|
package/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"name": "@graphcommerce/magento-product",
|
3
3
|
"homepage": "https://www.graphcommerce.org/",
|
4
4
|
"repository": "github:graphcommerce-org/graphcommerce",
|
5
|
-
"version": "9.0.0-canary.
|
5
|
+
"version": "9.0.0-canary.59",
|
6
6
|
"sideEffects": false,
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
8
8
|
"eslintConfig": {
|
@@ -18,19 +18,19 @@
|
|
18
18
|
"typescript": "5.5.3"
|
19
19
|
},
|
20
20
|
"peerDependencies": {
|
21
|
-
"@graphcommerce/ecommerce-ui": "^9.0.0-canary.
|
22
|
-
"@graphcommerce/eslint-config-pwa": "^9.0.0-canary.
|
23
|
-
"@graphcommerce/framer-next-pages": "^9.0.0-canary.
|
24
|
-
"@graphcommerce/framer-scroller": "^9.0.0-canary.
|
25
|
-
"@graphcommerce/graphql": "^9.0.0-canary.
|
26
|
-
"@graphcommerce/graphql-mesh": "^9.0.0-canary.
|
27
|
-
"@graphcommerce/image": "^9.0.0-canary.
|
28
|
-
"@graphcommerce/magento-cart": "^9.0.0-canary.
|
29
|
-
"@graphcommerce/magento-category": "^9.0.0-canary.
|
30
|
-
"@graphcommerce/magento-store": "^9.0.0-canary.
|
31
|
-
"@graphcommerce/next-ui": "^9.0.0-canary.
|
32
|
-
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.
|
33
|
-
"@graphcommerce/typescript-config-pwa": "^9.0.0-canary.
|
21
|
+
"@graphcommerce/ecommerce-ui": "^9.0.0-canary.59",
|
22
|
+
"@graphcommerce/eslint-config-pwa": "^9.0.0-canary.59",
|
23
|
+
"@graphcommerce/framer-next-pages": "^9.0.0-canary.59",
|
24
|
+
"@graphcommerce/framer-scroller": "^9.0.0-canary.59",
|
25
|
+
"@graphcommerce/graphql": "^9.0.0-canary.59",
|
26
|
+
"@graphcommerce/graphql-mesh": "^9.0.0-canary.59",
|
27
|
+
"@graphcommerce/image": "^9.0.0-canary.59",
|
28
|
+
"@graphcommerce/magento-cart": "^9.0.0-canary.59",
|
29
|
+
"@graphcommerce/magento-category": "^9.0.0-canary.59",
|
30
|
+
"@graphcommerce/magento-store": "^9.0.0-canary.59",
|
31
|
+
"@graphcommerce/next-ui": "^9.0.0-canary.59",
|
32
|
+
"@graphcommerce/prettier-config-pwa": "^9.0.0-canary.59",
|
33
|
+
"@graphcommerce/typescript-config-pwa": "^9.0.0-canary.59",
|
34
34
|
"@lingui/core": "^4.2.1",
|
35
35
|
"@lingui/macro": "^4.2.1",
|
36
36
|
"@lingui/react": "^4.2.1",
|