@graphcommerce/magento-product 8.1.0-canary.26 → 8.1.0-canary.27
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 +7 -0
- package/components/JsonLdProduct/ProductPageJsonLd.tsx +1 -1
- package/components/ProductFiltersPro/ProductFiltersPro.tsx +27 -5
- package/components/ProductFiltersPro/ProductFiltersProAggregations.tsx +14 -0
- package/components/ProductFiltersPro/ProductFiltersProAllFiltersChip.tsx +4 -8
- package/components/ProductFiltersPro/ProductFiltersProAllFiltersSidebar.tsx +18 -8
- package/components/ProductFiltersPro/ProductFiltersProCategorySection.tsx +70 -0
- package/components/ProductFiltersPro/ProductFiltersProChips.tsx +10 -8
- package/components/ProductFiltersPro/ProductFiltersProLayoutSidebar.tsx +15 -7
- package/components/ProductFiltersPro/ProductFiltersProLimitSection.tsx +4 -1
- package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +6 -1
- package/components/ProductFiltersPro/index.ts +6 -0
- package/components/ProductFiltersPro/useProductFiltersProSort.tsx +4 -2
- package/components/ProductListFiltersContainer/ProductListFiltersContainer.tsx +2 -4
- package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.graphql +3 -0
- package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +3 -0
- package/components/ProductPageBreadcrumb/ProductPageBreadcrumbs.tsx +40 -0
- package/components/ProductPageBreadcrumb/index.ts +1 -0
- package/package.json +14 -13
package/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 8.1.0-canary.27
|
4
|
+
|
5
|
+
### Minor Changes
|
6
|
+
|
7
|
+
- [#2273](https://github.com/graphcommerce-org/graphcommerce/pull/2273) [`77955c5`](https://github.com/graphcommerce-org/graphcommerce/commit/77955c56ac8633ab1c5e0f3ddb25e3a87236e2bb) - Improve Breadcrumbs on Category and Product pages
|
8
|
+
([@Jessevdpoel](https://github.com/Jessevdpoel))
|
9
|
+
|
3
10
|
## 8.1.0-canary.26
|
4
11
|
|
5
12
|
## 8.1.0-canary.25
|
@@ -13,5 +13,5 @@ export function ProductPageJsonLd<T extends { '@type': string }, P extends JsonL
|
|
13
13
|
props: ProductPageJsonLdProps<T, P>,
|
14
14
|
) {
|
15
15
|
const { product, render } = props
|
16
|
-
return <JsonLd<T> item={render(product)} />
|
16
|
+
return <JsonLd<T> item={render(product)} keyVal='product-jsonld' />
|
17
17
|
}
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import { useForm, UseFormProps, UseFormReturn } from '@graphcommerce/ecommerce-ui'
|
1
|
+
import { FormAutoSubmit, useForm, UseFormProps, UseFormReturn } from '@graphcommerce/ecommerce-ui'
|
2
2
|
import { useMatchMediaMotionValue, useMemoObject } from '@graphcommerce/next-ui'
|
3
|
-
import { useEventCallback, useTheme } from '@mui/material'
|
3
|
+
import { Theme, useEventCallback, useMediaQuery, useTheme } from '@mui/material'
|
4
4
|
import { m, useTransform } from 'framer-motion'
|
5
5
|
import { useRouter } from 'next/router'
|
6
6
|
import React, { BaseSyntheticEvent, createContext, useContext, useMemo, useRef } from 'react'
|
@@ -43,12 +43,33 @@ export type FilterFormProviderProps = Omit<
|
|
43
43
|
> & {
|
44
44
|
children: React.ReactNode
|
45
45
|
params: ProductListParams
|
46
|
+
/**
|
47
|
+
* Whether the filter should scroll to the products list and whether to submit the form on change.
|
48
|
+
*/
|
49
|
+
autoSubmitMd?: boolean
|
46
50
|
} & DataProps
|
47
51
|
|
48
|
-
|
52
|
+
function AutoSubmitSidebarDesktop() {
|
53
|
+
const { form, submit } = useProductFiltersPro()
|
54
|
+
|
55
|
+
// We only need to auto-submit when the layout is not sidebar and we're viewing on desktop
|
56
|
+
const autoSubmitDisabled = useMediaQuery<Theme>((t) => t.breakpoints.down('md'), {
|
57
|
+
defaultMatches: false,
|
58
|
+
})
|
59
|
+
|
60
|
+
return <FormAutoSubmit control={form.control} disabled={autoSubmitDisabled} submit={submit} />
|
61
|
+
}
|
49
62
|
|
50
63
|
export function ProductFiltersPro(props: FilterFormProviderProps) {
|
51
|
-
const {
|
64
|
+
const {
|
65
|
+
children,
|
66
|
+
params,
|
67
|
+
aggregations,
|
68
|
+
appliedAggregations,
|
69
|
+
filterTypes,
|
70
|
+
autoSubmitMd = false,
|
71
|
+
...formProps
|
72
|
+
} = props
|
52
73
|
|
53
74
|
const defaultValues = useMemoObject(toFilterParams(params))
|
54
75
|
const form = useForm<ProductFilterParams>({ defaultValues, ...formProps })
|
@@ -58,7 +79,7 @@ export function ProductFiltersPro(props: FilterFormProviderProps) {
|
|
58
79
|
const theme = useTheme()
|
59
80
|
const isDesktop = useMatchMediaMotionValue('up', 'md')
|
60
81
|
const scrollMarginTop = useTransform(() => (isDesktop.get() ? 0 : theme.appShell.headerHeightSm))
|
61
|
-
const scroll = useTransform(() => !
|
82
|
+
const scroll = useTransform(() => !autoSubmitMd || isDesktop.get())
|
62
83
|
|
63
84
|
const submit = useEventCallback(
|
64
85
|
form.handleSubmit(async (formValues) => {
|
@@ -87,6 +108,7 @@ export function ProductFiltersPro(props: FilterFormProviderProps) {
|
|
87
108
|
return (
|
88
109
|
<FilterFormContext.Provider value={filterFormContext}>
|
89
110
|
<m.form ref={ref} noValidate onSubmit={submit} id='products' style={{ scrollMarginTop }} />
|
111
|
+
{autoSubmitMd && <AutoSubmitSidebarDesktop />}
|
90
112
|
{children}
|
91
113
|
</FilterFormContext.Provider>
|
92
114
|
)
|
@@ -1,4 +1,8 @@
|
|
1
1
|
import { ProductListFiltersFragment } from '../ProductListFilters/ProductListFilters.gql'
|
2
|
+
import { ProductFilterEqualChip } from './ProductFilterEqualChip'
|
3
|
+
import { ProductFilterEqualSection } from './ProductFilterEqualSection'
|
4
|
+
import { ProductFilterRangeChip } from './ProductFilterRangeChip'
|
5
|
+
import { ProductFilterRangeSection } from './ProductFilterRangeSection'
|
2
6
|
import { useProductFiltersPro } from './ProductFiltersPro'
|
3
7
|
import { excludeCategory } from './activeAggregations'
|
4
8
|
import { applyAggregationCount } from './applyAggregationCount'
|
@@ -13,6 +17,16 @@ export type ProductFiltersProAggregationsProps = {
|
|
13
17
|
renderer?: FilterRenderer
|
14
18
|
}
|
15
19
|
|
20
|
+
export const productFiltersProSectionRenderer = {
|
21
|
+
FilterRangeTypeInput: ProductFilterRangeSection,
|
22
|
+
FilterEqualTypeInput: ProductFilterEqualSection,
|
23
|
+
}
|
24
|
+
|
25
|
+
export const productFiltersProChipRenderer = {
|
26
|
+
FilterEqualTypeInput: ProductFilterEqualChip,
|
27
|
+
FilterRangeTypeInput: ProductFilterRangeChip,
|
28
|
+
}
|
29
|
+
|
16
30
|
export function ProductFiltersProAggregations(props: ProductFiltersProAggregationsProps) {
|
17
31
|
const { renderer } = props
|
18
32
|
const { params, aggregations, appliedAggregations, filterTypes } = useProductFiltersPro()
|
@@ -1,11 +1,10 @@
|
|
1
1
|
import { ChipOverlayOrPopper, ChipOverlayOrPopperProps } from '@graphcommerce/next-ui'
|
2
2
|
import { Trans } from '@lingui/react'
|
3
|
-
import { ProductFilterEqualSection } from './ProductFilterEqualSection'
|
4
|
-
import { ProductFilterRangeSection } from './ProductFilterRangeSection'
|
5
3
|
import { useProductFiltersPro } from './ProductFiltersPro'
|
6
4
|
import {
|
7
5
|
ProductFiltersProAggregations,
|
8
6
|
ProductFiltersProAggregationsProps,
|
7
|
+
productFiltersProSectionRenderer,
|
9
8
|
} from './ProductFiltersProAggregations'
|
10
9
|
import { ProductFiltersProLimitSection } from './ProductFiltersProLimitSection'
|
11
10
|
import {
|
@@ -23,11 +22,6 @@ export type ProductFiltersProAllFiltersChipProps = ProductFiltersProAggregations
|
|
23
22
|
'label' | 'selected' | 'selectedLabel' | 'onApply' | 'onReset' | 'onClose' | 'children'
|
24
23
|
>
|
25
24
|
|
26
|
-
const defaultRenderer = {
|
27
|
-
FilterRangeTypeInput: ProductFilterRangeSection,
|
28
|
-
FilterEqualTypeInput: ProductFilterEqualSection,
|
29
|
-
}
|
30
|
-
|
31
25
|
export function ProductFiltersProAllFiltersChip(props: ProductFiltersProAllFiltersChipProps) {
|
32
26
|
const { sort_fields, total_count, renderer, category, ...rest } = props
|
33
27
|
|
@@ -65,7 +59,9 @@ export function ProductFiltersProAllFiltersChip(props: ProductFiltersProAllFilte
|
|
65
59
|
category={category}
|
66
60
|
/>
|
67
61
|
<ProductFiltersProLimitSection />
|
68
|
-
<ProductFiltersProAggregations
|
62
|
+
<ProductFiltersProAggregations
|
63
|
+
renderer={{ ...productFiltersProSectionRenderer, ...renderer }}
|
64
|
+
/>
|
69
65
|
</>
|
70
66
|
)}
|
71
67
|
</ChipOverlayOrPopper>
|
@@ -4,7 +4,12 @@ import { ProductFilterRangeSection } from './ProductFilterRangeSection'
|
|
4
4
|
import {
|
5
5
|
ProductFiltersProAggregations,
|
6
6
|
ProductFiltersProAggregationsProps,
|
7
|
+
productFiltersProSectionRenderer,
|
7
8
|
} from './ProductFiltersProAggregations'
|
9
|
+
import {
|
10
|
+
ProductFiltersCategorySectionProps,
|
11
|
+
ProductFiltersProCategorySection,
|
12
|
+
} from './ProductFiltersProCategorySection'
|
8
13
|
import { ProductFiltersProLimitSection } from './ProductFiltersProLimitSection'
|
9
14
|
import {
|
10
15
|
ProductFiltersProSortSection,
|
@@ -12,25 +17,30 @@ import {
|
|
12
17
|
} from './ProductFiltersProSortSection'
|
13
18
|
|
14
19
|
export type ProductFiltersProAllFiltersSidebarProps = ProductFiltersProAggregationsProps &
|
15
|
-
ProductFiltersProSortSectionProps &
|
16
|
-
|
17
|
-
const defaultRenderer = {
|
18
|
-
FilterRangeTypeInput: ProductFilterRangeSection,
|
19
|
-
FilterEqualTypeInput: ProductFilterEqualSection,
|
20
|
-
}
|
20
|
+
ProductFiltersProSortSectionProps &
|
21
|
+
ProductFiltersCategorySectionProps & { sx?: SxProps<Theme> }
|
21
22
|
|
23
|
+
/**
|
24
|
+
* @deprecated Not used anymore
|
25
|
+
*
|
26
|
+
* @param props
|
27
|
+
* @returns
|
28
|
+
*/
|
22
29
|
export function ProductFiltersProAllFiltersSidebar(props: ProductFiltersProAllFiltersSidebarProps) {
|
23
|
-
const { sort_fields, total_count, renderer, sx = [], category } = props
|
30
|
+
const { sort_fields, total_count, renderer, sx = [], category, params } = props
|
24
31
|
|
25
32
|
return (
|
26
33
|
<Box sx={[{ display: { xs: 'none', md: 'grid' } }, ...(Array.isArray(sx) ? sx : [sx])]}>
|
34
|
+
<ProductFiltersProCategorySection category={category} params={params} />
|
27
35
|
<ProductFiltersProSortSection
|
28
36
|
sort_fields={sort_fields}
|
29
37
|
total_count={total_count}
|
30
38
|
category={category}
|
31
39
|
/>
|
32
40
|
<ProductFiltersProLimitSection />
|
33
|
-
<ProductFiltersProAggregations
|
41
|
+
<ProductFiltersProAggregations
|
42
|
+
renderer={{ ...productFiltersProSectionRenderer, ...renderer }}
|
43
|
+
/>
|
34
44
|
</Box>
|
35
45
|
)
|
36
46
|
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
import { UseCategoryTreeProps, useCategoryTree } from '@graphcommerce/magento-category'
|
2
|
+
import {
|
3
|
+
ActionCard,
|
4
|
+
ActionCardAccordion,
|
5
|
+
ActionCardList,
|
6
|
+
IconSvg,
|
7
|
+
iconChevronLeft,
|
8
|
+
responsiveVal,
|
9
|
+
} from '@graphcommerce/next-ui'
|
10
|
+
import { Trans } from '@lingui/react'
|
11
|
+
import { Box, SxProps, Theme } from '@mui/material'
|
12
|
+
import { useRouter } from 'next/router'
|
13
|
+
|
14
|
+
export type ProductFiltersCategorySectionProps = UseCategoryTreeProps & {
|
15
|
+
hideTitle?: boolean
|
16
|
+
sx?: SxProps<Theme>
|
17
|
+
}
|
18
|
+
|
19
|
+
export function ProductFiltersProCategorySection(props: ProductFiltersCategorySectionProps) {
|
20
|
+
const { hideTitle, sx } = props
|
21
|
+
const router = useRouter()
|
22
|
+
const categoryTree = useCategoryTree(props)
|
23
|
+
|
24
|
+
if (!categoryTree) return null
|
25
|
+
|
26
|
+
return (
|
27
|
+
<ActionCardAccordion
|
28
|
+
sx={[
|
29
|
+
hideTitle ? { '& .MuiAccordionSummary-root': { display: 'none' } } : {},
|
30
|
+
sx,
|
31
|
+
...(Array.isArray(sx) ? sx : [sx]),
|
32
|
+
]}
|
33
|
+
defaultExpanded
|
34
|
+
summary={<Trans id='Categories' />}
|
35
|
+
details={
|
36
|
+
<ActionCardList value='cat' variant='default'>
|
37
|
+
{categoryTree.map((item) => (
|
38
|
+
<ActionCard
|
39
|
+
{...item}
|
40
|
+
title={
|
41
|
+
item.isBack ? (
|
42
|
+
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
43
|
+
<IconSvg src={iconChevronLeft} size='medium' />
|
44
|
+
{item.title}
|
45
|
+
</Box>
|
46
|
+
) : (
|
47
|
+
item.title
|
48
|
+
)
|
49
|
+
}
|
50
|
+
sx={[
|
51
|
+
item.isBack ? {} : {},
|
52
|
+
{
|
53
|
+
'&.sizeSmall': { pl: responsiveVal(8 * item.indent, 12 * item.indent) },
|
54
|
+
'&.sizeMedium': { pl: responsiveVal(10 * item.indent, 14 * item.indent) },
|
55
|
+
'&.sizeLarge': { pl: responsiveVal(12 * item.indent, 16 * item.indent) },
|
56
|
+
'&.sizeResponsive': { pl: responsiveVal(8 * item.indent, 16 * item.indent) },
|
57
|
+
},
|
58
|
+
]}
|
59
|
+
value={item.href}
|
60
|
+
key={item.href}
|
61
|
+
selected={item.selected}
|
62
|
+
onClick={() => router.push(item.href)}
|
63
|
+
/>
|
64
|
+
))}
|
65
|
+
</ActionCardList>
|
66
|
+
}
|
67
|
+
right={undefined}
|
68
|
+
/>
|
69
|
+
)
|
70
|
+
}
|
@@ -1,16 +1,18 @@
|
|
1
|
-
import { ProductFilterEqualChip } from './ProductFilterEqualChip'
|
2
|
-
import { ProductFilterRangeChip } from './ProductFilterRangeChip'
|
3
1
|
import {
|
4
2
|
ProductFiltersProAggregations,
|
5
3
|
ProductFiltersProAggregationsProps,
|
4
|
+
productFiltersProChipRenderer,
|
6
5
|
} from './ProductFiltersProAggregations'
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
}
|
12
|
-
|
7
|
+
/**
|
8
|
+
* @deprecated Not used anymore, use `<ProductFiltersProAggregations renderer={productFiltersProChipRenderer}/>`
|
9
|
+
*/
|
13
10
|
export function ProductFiltersProFilterChips(props: ProductFiltersProAggregationsProps) {
|
14
11
|
const { renderer } = props
|
15
|
-
return
|
12
|
+
return (
|
13
|
+
<ProductFiltersProAggregations
|
14
|
+
{...props}
|
15
|
+
renderer={{ ...productFiltersProChipRenderer, ...renderer }}
|
16
|
+
/>
|
17
|
+
)
|
16
18
|
}
|
@@ -12,6 +12,7 @@ export type ProductFiltersProLayoutSidebarProps = {
|
|
12
12
|
count?: React.ReactNode
|
13
13
|
pagination: React.ReactNode
|
14
14
|
header?: React.ReactNode
|
15
|
+
children?: React.ReactNode
|
15
16
|
} & Partial<OwnerProps>
|
16
17
|
|
17
18
|
type OwnerProps = {
|
@@ -32,6 +33,7 @@ export function ProductFiltersProLayoutSidebar(props: ProductFiltersProLayoutSid
|
|
32
33
|
sidebarFilters,
|
33
34
|
header,
|
34
35
|
headerPosition = 'before',
|
36
|
+
children,
|
35
37
|
} = props
|
36
38
|
|
37
39
|
const { form, submit } = useProductFiltersPro()
|
@@ -47,8 +49,6 @@ export function ProductFiltersProLayoutSidebar(props: ProductFiltersProLayoutSid
|
|
47
49
|
|
48
50
|
<FormAutoSubmit control={form.control} disabled={autoSubmitDisabled} submit={submit} />
|
49
51
|
|
50
|
-
<StickyBelowHeader sx={{ display: { md: 'none' } }}>{horizontalFilters}</StickyBelowHeader>
|
51
|
-
|
52
52
|
<Container
|
53
53
|
maxWidth={false}
|
54
54
|
className={classes.content}
|
@@ -56,12 +56,16 @@ export function ProductFiltersProLayoutSidebar(props: ProductFiltersProLayoutSid
|
|
56
56
|
display: 'grid',
|
57
57
|
gridTemplate: {
|
58
58
|
xs: `
|
59
|
-
"
|
60
|
-
"
|
61
|
-
"
|
59
|
+
"content" auto
|
60
|
+
"horizontalFilters" auto
|
61
|
+
"beforeContent" auto
|
62
|
+
"items" auto
|
63
|
+
"afterContent" auto
|
62
64
|
`,
|
63
|
-
md: `
|
64
|
-
"topleft
|
65
|
+
md: `
|
66
|
+
"topleft content" auto
|
67
|
+
"sidebar content" auto
|
68
|
+
"sidebar beforeContent" auto
|
65
69
|
"sidebar items" min-content
|
66
70
|
"sidebar afterContent" 1fr
|
67
71
|
/300px auto
|
@@ -87,6 +91,10 @@ export function ProductFiltersProLayoutSidebar(props: ProductFiltersProLayoutSid
|
|
87
91
|
{sidebarFilters}
|
88
92
|
</Box>
|
89
93
|
)}
|
94
|
+
{children && <Box gridArea='content'>{children}</Box>}
|
95
|
+
<StickyBelowHeader sx={{ display: { md: 'none', gridArea: 'horizontalFilters' } }}>
|
96
|
+
{horizontalFilters}
|
97
|
+
</StickyBelowHeader>
|
90
98
|
|
91
99
|
<Box gridArea='beforeContent' sx={{ mt: { md: 0 } }}>
|
92
100
|
{count}
|
@@ -11,11 +11,13 @@ import {
|
|
11
11
|
import { Trans } from '@lingui/react'
|
12
12
|
import { useMemo } from 'react'
|
13
13
|
import { useProductFiltersPro } from './ProductFiltersPro'
|
14
|
+
import { SxProps, Theme } from '@mui/material'
|
14
15
|
|
15
|
-
export type ProductFiltersProLimitSectionProps =
|
16
|
+
export type ProductFiltersProLimitSectionProps = { sx?: SxProps<Theme> }
|
16
17
|
|
17
18
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
18
19
|
export function ProductFiltersProLimitSection(props: ProductFiltersProLimitSectionProps) {
|
20
|
+
const { sx } = props
|
19
21
|
const { form } = useProductFiltersPro()
|
20
22
|
const { control } = form
|
21
23
|
const activePageSize = useWatch({ control, name: 'pageSize' })
|
@@ -38,6 +40,7 @@ export function ProductFiltersProLimitSection(props: ProductFiltersProLimitSecti
|
|
38
40
|
|
39
41
|
return (
|
40
42
|
<ActionCardAccordion
|
43
|
+
sx={sx}
|
41
44
|
defaultExpanded={!!activePageSize}
|
42
45
|
summary={<Trans id='Per page' />}
|
43
46
|
details={
|
@@ -1,16 +1,21 @@
|
|
1
1
|
import { ActionCard, ActionCardAccordion, ActionCardListForm, Button } from '@graphcommerce/next-ui'
|
2
2
|
import { Trans } from '@lingui/react'
|
3
|
+
import { SxProps, Theme } from '@mui/material'
|
3
4
|
import { useProductFiltersPro } from './ProductFiltersPro'
|
4
5
|
import { UseProductFiltersProSortProps, useProductFiltersProSort } from './useProductFiltersProSort'
|
5
6
|
|
6
|
-
export type ProductFiltersProSortSectionProps = UseProductFiltersProSortProps
|
7
|
+
export type ProductFiltersProSortSectionProps = UseProductFiltersProSortProps & {
|
8
|
+
sx?: SxProps<Theme>
|
9
|
+
}
|
7
10
|
|
8
11
|
export function ProductFiltersProSortSection(props: ProductFiltersProSortSectionProps) {
|
12
|
+
const { sx } = props
|
9
13
|
const { form } = useProductFiltersPro()
|
10
14
|
const { options, showReset, selected } = useProductFiltersProSort(props)
|
11
15
|
|
12
16
|
return (
|
13
17
|
<ActionCardAccordion
|
18
|
+
sx={sx}
|
14
19
|
defaultExpanded={selected}
|
15
20
|
summary={<Trans id='Sort By' />}
|
16
21
|
details={
|
@@ -1,11 +1,17 @@
|
|
1
1
|
export * from './ProductFilterEqualChip'
|
2
|
+
export * from './ProductFilterEqualSection'
|
2
3
|
export * from './ProductFilterRangeChip'
|
4
|
+
export * from './ProductFilterRangeSection'
|
3
5
|
export * from './ProductFiltersPro'
|
4
6
|
export * from './ProductFiltersProAllFiltersChip'
|
5
7
|
export * from './ProductFiltersProAllFiltersSidebar'
|
8
|
+
export * from './ProductFiltersProCategorySection'
|
6
9
|
export * from './ProductFiltersProChips'
|
7
10
|
export * from './ProductFiltersProClearAll'
|
8
11
|
export * from './ProductFiltersProLayoutSidebar'
|
9
12
|
export * from './ProductFiltersProLimitChip'
|
10
13
|
export * from './ProductFiltersProLimitSection'
|
11
14
|
export * from './ProductFiltersProSortChip'
|
15
|
+
export * from './ProductFiltersProSortDirectionArrow'
|
16
|
+
export * from './ProductFiltersProAggregations'
|
17
|
+
export * from './ProductFiltersProSortSection'
|
@@ -27,7 +27,7 @@ export function useProductFiltersProSort(props: ProductListActionSortProps) {
|
|
27
27
|
() =>
|
28
28
|
filterNonNullableKeys(sort_fields?.options).map((o) =>
|
29
29
|
!category?.uid && o.value === 'position'
|
30
|
-
? { value: 'relevance', label: i18n._('Relevance') }
|
30
|
+
? { value: 'relevance', label: i18n._(/* i18n*/ 'Relevance') }
|
31
31
|
: o,
|
32
32
|
),
|
33
33
|
[category?.uid, sort_fields?.options],
|
@@ -41,7 +41,9 @@ export function useProductFiltersProSort(props: ProductListActionSortProps) {
|
|
41
41
|
|
42
42
|
const formSort = useWatch({ control, name: 'sort' })
|
43
43
|
const formDirection = useWatch({ control, name: 'dir' })
|
44
|
-
const showReset =
|
44
|
+
const showReset =
|
45
|
+
(formDirection !== null || formSort !== null) &&
|
46
|
+
Boolean(formSort !== defaultSortBy || formDirection === 'DESC')
|
45
47
|
const selected = Boolean(params.sort && (params.sort !== defaultSortBy || params.dir === 'DESC'))
|
46
48
|
|
47
49
|
const options = useMemo(
|
@@ -97,7 +97,7 @@ export function ProductListFiltersContainer(props: ProductListFiltersContainerPr
|
|
97
97
|
top: theme.page.vertical,
|
98
98
|
zIndex: 9,
|
99
99
|
margin: '0 auto',
|
100
|
-
|
100
|
+
|
101
101
|
[theme.breakpoints.down('md')]: {
|
102
102
|
textAlign: 'center',
|
103
103
|
maxWidth: 'unset',
|
@@ -136,8 +136,7 @@ export function ProductListFiltersContainer(props: ProductListFiltersContainerPr
|
|
136
136
|
className={classes.scroller}
|
137
137
|
hideScrollbar
|
138
138
|
sx={(theme) => ({
|
139
|
-
|
140
|
-
paddingRight: theme.page.horizontal,
|
139
|
+
px: theme.page.horizontal,
|
141
140
|
paddingBottom: '1px',
|
142
141
|
[theme.breakpoints.up('md')]: {
|
143
142
|
borderRadius: '99em',
|
@@ -145,7 +144,6 @@ export function ProductListFiltersContainer(props: ProductListFiltersContainerPr
|
|
145
144
|
paddingRight: '8px',
|
146
145
|
},
|
147
146
|
py: '5px',
|
148
|
-
|
149
147
|
columnGap: '6px',
|
150
148
|
gridAutoColumns: 'min-content',
|
151
149
|
})}
|
@@ -8,6 +8,9 @@ import { ProductPageBreadcrumbFragment } from './ProductPageBreadcrumb.gql'
|
|
8
8
|
type ProductPageBreadcrumbsProps = ProductPageBreadcrumbFragment &
|
9
9
|
Omit<BreadcrumbsProps, 'children'>
|
10
10
|
|
11
|
+
/**
|
12
|
+
* @deprecated Please use ProductPageBreadcrumbs
|
13
|
+
*/
|
11
14
|
export function ProductPageBreadcrumb(props: ProductPageBreadcrumbsProps) {
|
12
15
|
const { categories, name, ...breadcrumbProps } = props
|
13
16
|
const prev = usePrevPageRouter()
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import { usePrevPageRouter } from '@graphcommerce/framer-next-pages'
|
2
|
+
import { categoryToBreadcrumbs } from '@graphcommerce/magento-category'
|
3
|
+
import { Breadcrumbs } from '@graphcommerce/next-ui'
|
4
|
+
import { BreadcrumbsJsonLd } from '@graphcommerce/next-ui/Breadcrumbs/BreadcrumbsJsonLd'
|
5
|
+
import { jsonLdBreadcrumb } from '@graphcommerce/next-ui/Breadcrumbs/jsonLdBreadcrumb'
|
6
|
+
import { BreadcrumbsProps } from '@mui/material'
|
7
|
+
import { useRouter } from 'next/router'
|
8
|
+
import { BreadcrumbList } from 'schema-dts'
|
9
|
+
import { productPageCategory } from '../ProductPageCategory/productPageCategory'
|
10
|
+
import { ProductPageBreadcrumbFragment } from './ProductPageBreadcrumb.gql'
|
11
|
+
import { productLink } from '../../hooks/useProductLink'
|
12
|
+
|
13
|
+
export type ProductPageBreadcrumbsProps = Omit<BreadcrumbsProps, 'children'> & {
|
14
|
+
breadcrumbsAmount?: number
|
15
|
+
product: ProductPageBreadcrumbFragment
|
16
|
+
}
|
17
|
+
|
18
|
+
export function ProductPageBreadcrumbs(props: ProductPageBreadcrumbsProps) {
|
19
|
+
const { product, ...breadcrumbsProps } = props
|
20
|
+
const { categories } = product
|
21
|
+
const prev = usePrevPageRouter()
|
22
|
+
const router = useRouter()
|
23
|
+
|
24
|
+
const category =
|
25
|
+
categories?.find((c) => `/${c?.url_path}` === prev?.asPath) ?? productPageCategory(product)
|
26
|
+
|
27
|
+
if (!category || !product.name || !product.url_key) return null
|
28
|
+
|
29
|
+
const breadcrumbs = categoryToBreadcrumbs(category)
|
30
|
+
|
31
|
+
return (
|
32
|
+
<>
|
33
|
+
<BreadcrumbsJsonLd<BreadcrumbList>
|
34
|
+
breadcrumbs={[...breadcrumbs, { name: product.name, href: productLink(product) }]}
|
35
|
+
render={(bc) => ({ '@context': 'https://schema.org', ...jsonLdBreadcrumb(bc, router) })}
|
36
|
+
/>
|
37
|
+
<Breadcrumbs breadcrumbs={breadcrumbs} lastIsLink {...breadcrumbsProps} />
|
38
|
+
</>
|
39
|
+
)
|
40
|
+
}
|
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": "8.1.0-canary.
|
5
|
+
"version": "8.1.0-canary.27",
|
6
6
|
"sideEffects": false,
|
7
7
|
"prettier": "@graphcommerce/prettier-config-pwa",
|
8
8
|
"eslintConfig": {
|
@@ -18,18 +18,19 @@
|
|
18
18
|
"typescript": "5.3.3"
|
19
19
|
},
|
20
20
|
"peerDependencies": {
|
21
|
-
"@graphcommerce/ecommerce-ui": "^8.1.0-canary.
|
22
|
-
"@graphcommerce/eslint-config-pwa": "^8.1.0-canary.
|
23
|
-
"@graphcommerce/framer-next-pages": "^8.1.0-canary.
|
24
|
-
"@graphcommerce/framer-scroller": "^8.1.0-canary.
|
25
|
-
"@graphcommerce/graphql": "^8.1.0-canary.
|
26
|
-
"@graphcommerce/graphql-mesh": "^8.1.0-canary.
|
27
|
-
"@graphcommerce/image": "^8.1.0-canary.
|
28
|
-
"@graphcommerce/magento-cart": "^8.1.0-canary.
|
29
|
-
"@graphcommerce/magento-
|
30
|
-
"@graphcommerce/
|
31
|
-
"@graphcommerce/
|
32
|
-
"@graphcommerce/
|
21
|
+
"@graphcommerce/ecommerce-ui": "^8.1.0-canary.27",
|
22
|
+
"@graphcommerce/eslint-config-pwa": "^8.1.0-canary.27",
|
23
|
+
"@graphcommerce/framer-next-pages": "^8.1.0-canary.27",
|
24
|
+
"@graphcommerce/framer-scroller": "^8.1.0-canary.27",
|
25
|
+
"@graphcommerce/graphql": "^8.1.0-canary.27",
|
26
|
+
"@graphcommerce/graphql-mesh": "^8.1.0-canary.27",
|
27
|
+
"@graphcommerce/image": "^8.1.0-canary.27",
|
28
|
+
"@graphcommerce/magento-cart": "^8.1.0-canary.27",
|
29
|
+
"@graphcommerce/magento-category": "^8.1.0-canary.27",
|
30
|
+
"@graphcommerce/magento-store": "^8.1.0-canary.27",
|
31
|
+
"@graphcommerce/next-ui": "^8.1.0-canary.27",
|
32
|
+
"@graphcommerce/prettier-config-pwa": "^8.1.0-canary.27",
|
33
|
+
"@graphcommerce/typescript-config-pwa": "^8.1.0-canary.27",
|
33
34
|
"@lingui/core": "^4.2.1",
|
34
35
|
"@lingui/macro": "^4.2.1",
|
35
36
|
"@lingui/react": "^4.2.1",
|