@graphcommerce/magento-product 7.0.0-canary.21 → 7.0.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 (62) hide show
  1. package/CHANGELOG.md +235 -1
  2. package/Config.graphqls +12 -2
  3. package/components/AddProductsToCart/AddProductsToCart.graphql +1 -0
  4. package/components/AddProductsToCart/AddProductsToCartError.tsx +2 -3
  5. package/components/AddProductsToCart/AddProductsToCartForm.tsx +1 -2
  6. package/components/AddProductsToCart/AddProductsToCartQuantity.tsx +3 -2
  7. package/components/AddProductsToCart/useAddProductsToCartAction.ts +2 -3
  8. package/components/AddProductsToCart/useFormAddProductsToCart.ts +3 -1
  9. package/components/JsonLdProduct/JsonLdProduct.graphql +1 -1
  10. package/components/JsonLdProduct/ProductPageJsonLd.tsx +12 -0
  11. package/components/JsonLdProduct/index.ts +2 -0
  12. package/components/ProductCustomizable/ProductCustomizable.tsx +4 -5
  13. package/components/ProductFiltersPro/PriceSlider.tsx +7 -2
  14. package/components/ProductFiltersPro/ProductFilterEqualChip.tsx +1 -1
  15. package/components/ProductFiltersPro/ProductFilterEqualSection.tsx +36 -37
  16. package/components/ProductFiltersPro/ProductFilterRangeChip.tsx +1 -1
  17. package/components/ProductFiltersPro/ProductFilterRangeSection.tsx +19 -23
  18. package/components/ProductFiltersPro/ProductFiltersPro.tsx +35 -18
  19. package/components/ProductFiltersPro/ProductFiltersProAggregations.tsx +5 -6
  20. package/components/ProductFiltersPro/ProductFiltersProAllFiltersChip.tsx +20 -43
  21. package/components/ProductFiltersPro/ProductFiltersProAllFiltersSidebar.tsx +32 -0
  22. package/components/ProductFiltersPro/ProductFiltersProClearAll.tsx +48 -0
  23. package/components/ProductFiltersPro/ProductFiltersProLayoutSidebar.tsx +100 -0
  24. package/components/ProductFiltersPro/ProductFiltersProLimitChip.tsx +2 -2
  25. package/components/ProductFiltersPro/ProductFiltersProLimitSection.tsx +35 -32
  26. package/components/ProductFiltersPro/ProductFiltersProSortChip.tsx +3 -2
  27. package/components/ProductFiltersPro/ProductFiltersProSortSection.tsx +31 -31
  28. package/components/ProductFiltersPro/index.ts +3 -0
  29. package/components/ProductFiltersPro/useClearAllFiltersHandler.ts +31 -0
  30. package/components/ProductListCount/ProductListCount.tsx +12 -6
  31. package/components/ProductListFilters/FilterEqualType.tsx +1 -0
  32. package/components/ProductListFilters/FilterRangeType.tsx +1 -0
  33. package/components/ProductListItems/ProductListItemsBase.tsx +58 -38
  34. package/components/ProductListItems/filterTypes.tsx +4 -5
  35. package/components/ProductListLink/ProductListLink.tsx +1 -0
  36. package/components/ProductListPagination/ProductListPagination.tsx +14 -7
  37. package/components/ProductListPrice/ProductListPrice.tsx +1 -1
  38. package/components/ProductListPrice/index.ts +2 -0
  39. package/components/ProductPage/ProductPageAddToCartRow.tsx +3 -2
  40. package/components/ProductPageBreadcrumb/ProductPageBreadcrumb.tsx +2 -2
  41. package/components/ProductPageDescription/ProductPageDescription.graphql +1 -0
  42. package/components/ProductPageDescription/ProductPageDescription.tsx +6 -4
  43. package/components/ProductPageGallery/ProductPageGallery.graphql +1 -0
  44. package/components/ProductPageGallery/ProductPageGallery.tsx +6 -8
  45. package/components/ProductPageMeta/ProductPageMeta.graphql +5 -3
  46. package/components/ProductPageMeta/ProductPageMeta.tsx +18 -21
  47. package/components/ProductPageName/ProductPageName.graphql +4 -0
  48. package/components/ProductPageName/ProductPageName.tsx +10 -0
  49. package/components/ProductPageName/index.ts +1 -0
  50. package/components/ProductPagePrice/ProductPagePrice.graphql +1 -0
  51. package/components/ProductPagePrice/ProductPagePrice.tsx +2 -2
  52. package/components/ProductShortDescription/ProductShortDescription.graphql +1 -0
  53. package/components/ProductShortDescription/ProductShortDescription.tsx +6 -2
  54. package/components/ProductSpecs/ProductSpecs.tsx +7 -2
  55. package/components/index.ts +5 -2
  56. package/hooks/useProductListLink.ts +2 -2
  57. package/hooks/useProductListLinkReplace.ts +2 -2
  58. package/index.ts +1 -0
  59. package/package.json +16 -15
  60. package/components/AddProductsToCart/AddProductsToCartIndex.tsx +0 -3
  61. package/components/ProductFiltersPro/useFilterActions.ts +0 -61
  62. package/components/ProductListItems/ProductListItems.tsx +0 -6
@@ -1,7 +1,9 @@
1
1
  import { useForm, UseFormProps, UseFormReturn } from '@graphcommerce/ecommerce-ui'
2
2
  import { useMemoObject } from '@graphcommerce/next-ui'
3
- import React, { BaseSyntheticEvent, createContext, useContext, useEffect, useMemo } from 'react'
3
+ import { useEventCallback } from '@mui/material'
4
+ import React, { BaseSyntheticEvent, createContext, useContext, useMemo } from 'react'
4
5
  import { useProductListLinkReplace } from '../../hooks/useProductListLinkReplace'
6
+ import { ProductListFiltersFragment } from '../ProductListFilters/ProductListFilters.gql'
5
7
  import {
6
8
  ProductFilterParams,
7
9
  ProductListParams,
@@ -9,7 +11,12 @@ import {
9
11
  toProductListParams,
10
12
  } from '../ProductListItems/filterTypes'
11
13
 
12
- type FilterFormContextProps = {
14
+ type DataProps = {
15
+ filterTypes: Record<string, string | undefined>
16
+ appliedAggregations?: ProductListFiltersFragment['aggregations']
17
+ } & ProductListFiltersFragment
18
+
19
+ type FilterFormContextProps = DataProps & {
13
20
  /**
14
21
  * Watch and formState are known to cause performance issues.
15
22
  *
@@ -32,30 +39,40 @@ export const useProductFiltersPro = () => {
32
39
  export type FilterFormProviderProps = Omit<
33
40
  UseFormProps<ProductFilterParams>,
34
41
  'values' | 'defaultValues'
35
- > & { children: React.ReactNode; params: ProductListParams }
42
+ > & {
43
+ children: React.ReactNode
44
+ params: ProductListParams
45
+ } & DataProps
36
46
 
37
47
  export function ProductFiltersPro(props: FilterFormProviderProps) {
38
- const { children, params, ...formProps } = props
48
+ const { children, params, aggregations, appliedAggregations, filterTypes, ...formProps } = props
39
49
 
40
- const fitlerParams = useMemoObject(toFilterParams(params))
41
- const form = useForm<ProductFilterParams>({
42
- values: fitlerParams,
43
- ...formProps,
44
- })
45
- const { handleSubmit } = form
50
+ const defaultValues = useMemoObject(toFilterParams(params))
51
+ const form = useForm<ProductFilterParams>({ defaultValues, ...formProps })
46
52
 
47
53
  const push = useProductListLinkReplace({ scroll: false })
48
- const submit = handleSubmit(async (formValues) =>
49
- push({ ...toProductListParams(formValues), currentPage: 1 }),
54
+ const submit = useEventCallback(
55
+ form.handleSubmit(async (formValues) =>
56
+ push({ ...toProductListParams(formValues), currentPage: 1 }),
57
+ ),
58
+ )
59
+
60
+ const filterFormContext: FilterFormContextProps = useMemo(
61
+ () => ({
62
+ form,
63
+ params: defaultValues,
64
+ submit,
65
+ appliedAggregations,
66
+ filterTypes,
67
+ aggregations,
68
+ }),
69
+ [form, defaultValues, submit, appliedAggregations, filterTypes, aggregations],
50
70
  )
51
71
 
52
72
  return (
53
- <FilterFormContext.Provider
54
- value={useMemo(() => ({ form, params: fitlerParams, submit }), [form, fitlerParams, submit])}
55
- >
56
- <form noValidate onSubmit={submit}>
57
- {children}
58
- </form>
73
+ <FilterFormContext.Provider value={filterFormContext}>
74
+ <form noValidate onSubmit={submit} id='products' />
75
+ {children}
59
76
  </FilterFormContext.Provider>
60
77
  )
61
78
  }
@@ -10,14 +10,12 @@ export type FilterProps = {
10
10
  export type FilterRenderer = Record<string, React.FC<FilterProps>>
11
11
 
12
12
  export type ProductFiltersProAggregationsProps = {
13
- filterTypes: Record<string, string | undefined>
14
13
  renderer?: FilterRenderer
15
- appliedAggregations?: ProductListFiltersFragment['aggregations']
16
- } & ProductListFiltersFragment
14
+ }
17
15
 
18
16
  export function ProductFiltersProAggregations(props: ProductFiltersProAggregationsProps) {
19
- const { aggregations, appliedAggregations, filterTypes, renderer } = props
20
- const { params } = useProductFiltersPro()
17
+ const { renderer } = props
18
+ const { params, aggregations, appliedAggregations, filterTypes } = useProductFiltersPro()
21
19
 
22
20
  return (
23
21
  <>
@@ -31,8 +29,9 @@ export function ProductFiltersProAggregations(props: ProductFiltersProAggregatio
31
29
  const Component = renderer?.[filterType]
32
30
  if (!Component) {
33
31
  if (process.env.NODE_ENV === 'development') {
32
+ // eslint-disable-next-line no-console
34
33
  console.log(
35
- `The renderer for fitlerType ${filterType} can not be found, please add it to the renderer prop: renderer={{ ${filterType}: (props) => <>MYRenderer</> }}}}`,
34
+ `The renderer for filterType ${filterType} can not be found, please add it to the renderer prop: renderer={{ ${filterType}: (props) => <>MYRenderer</> }}}}`,
36
35
  )
37
36
  }
38
37
  return null
@@ -1,12 +1,5 @@
1
- import {
2
- ChipOverlayOrPopper,
3
- ChipOverlayOrPopperProps,
4
- filterNonNullableKeys,
5
- } from '@graphcommerce/next-ui'
1
+ import { ChipOverlayOrPopper, ChipOverlayOrPopperProps } from '@graphcommerce/next-ui'
6
2
  import { Trans } from '@lingui/react'
7
- import { Box } from '@mui/material'
8
- import { activeAggregations } from './activeAggregations'
9
- import { applyAggregationCount } from './applyAggregationCount'
10
3
  import { ProductFilterEqualSection } from './ProductFilterEqualSection'
11
4
  import { ProductFilterRangeSection } from './ProductFilterRangeSection'
12
5
  import { useProductFiltersPro } from './ProductFiltersPro'
@@ -19,52 +12,44 @@ import {
19
12
  ProductFiltersProSortSection,
20
13
  ProductFiltersProSortSectionProps,
21
14
  } from './ProductFiltersProSortSection'
15
+ import { activeAggregations } from './activeAggregations'
16
+ import { applyAggregationCount } from './applyAggregationCount'
17
+ import { useClearAllFiltersAction } from './useClearAllFiltersHandler'
22
18
 
23
- type AllFiltersChip = ProductFiltersProAggregationsProps &
19
+ export type ProductFiltersProAllFiltersChipProps = ProductFiltersProAggregationsProps &
24
20
  ProductFiltersProSortSectionProps &
25
21
  Omit<
26
22
  ChipOverlayOrPopperProps,
27
23
  'label' | 'selected' | 'selectedLabel' | 'onApply' | 'onReset' | 'onClose' | 'children'
28
24
  >
29
25
 
30
- export function ProductFiltersProAllFiltersChip(props: AllFiltersChip) {
31
- const {
32
- filterTypes,
33
- aggregations,
34
- appliedAggregations: aggregationsCount,
35
- sort_fields,
36
- total_count,
37
- renderer,
38
- ...rest
39
- } = props
26
+ const defaultRenderer = {
27
+ FilterRangeTypeInput: ProductFilterRangeSection,
28
+ FilterEqualTypeInput: ProductFilterEqualSection,
29
+ }
30
+
31
+ export function ProductFiltersProAllFiltersChip(props: ProductFiltersProAllFiltersChipProps) {
32
+ const { sort_fields, total_count, renderer, ...rest } = props
40
33
 
41
- const { form, submit, params } = useProductFiltersPro()
34
+ const { submit, params, aggregations, appliedAggregations } = useProductFiltersPro()
42
35
  const { sort } = params
43
36
 
44
37
  const activeFilters = activeAggregations(
45
- applyAggregationCount(aggregations, aggregationsCount, params),
38
+ applyAggregationCount(aggregations, appliedAggregations, params),
46
39
  params,
47
40
  ).map(({ label }) => label)
48
41
 
49
42
  const allFilters = [...activeFilters, sort].filter(Boolean)
50
43
  const hasFilters = allFilters.length > 0
51
44
 
45
+ const clearAll = useClearAllFiltersAction()
46
+
52
47
  return (
53
48
  <ChipOverlayOrPopper
54
49
  label={<Trans id='All filters' />}
55
50
  chipProps={{ variant: 'outlined' }}
56
51
  onApply={submit}
57
- onReset={
58
- hasFilters
59
- ? () => {
60
- form.setValue('filters', { category_uid: params.filters.category_uid })
61
- form.setValue('currentPage', 1)
62
- form.setValue('sort', null)
63
- form.setValue('dir', null)
64
- return submit()
65
- }
66
- : undefined
67
- }
52
+ onReset={hasFilters ? clearAll : undefined}
68
53
  onClose={submit}
69
54
  selectedLabel={allFilters}
70
55
  selected={hasFilters}
@@ -73,19 +58,11 @@ export function ProductFiltersProAllFiltersChip(props: AllFiltersChip) {
73
58
  {...rest}
74
59
  >
75
60
  {() => (
76
- <Box sx={(theme) => ({ display: 'grid', rowGap: theme.spacings.sm })}>
61
+ <>
77
62
  <ProductFiltersProSortSection sort_fields={sort_fields} total_count={total_count} />
78
63
  <ProductFiltersProLimitSection />
79
- <ProductFiltersProAggregations
80
- filterTypes={filterTypes}
81
- aggregations={aggregations}
82
- appliedAggregations={aggregationsCount}
83
- renderer={{
84
- FilterRangeTypeInput: ProductFilterRangeSection,
85
- FilterEqualTypeInput: ProductFilterEqualSection,
86
- }}
87
- />
88
- </Box>
64
+ <ProductFiltersProAggregations renderer={{ ...defaultRenderer, ...renderer }} />
65
+ </>
89
66
  )}
90
67
  </ChipOverlayOrPopper>
91
68
  )
@@ -0,0 +1,32 @@
1
+ import { Box, SxProps, Theme } from '@mui/material'
2
+ import { ProductFilterEqualSection } from './ProductFilterEqualSection'
3
+ import { ProductFilterRangeSection } from './ProductFilterRangeSection'
4
+ import {
5
+ ProductFiltersProAggregations,
6
+ ProductFiltersProAggregationsProps,
7
+ } from './ProductFiltersProAggregations'
8
+ import { ProductFiltersProLimitSection } from './ProductFiltersProLimitSection'
9
+ import {
10
+ ProductFiltersProSortSection,
11
+ ProductFiltersProSortSectionProps,
12
+ } from './ProductFiltersProSortSection'
13
+
14
+ export type ProductFiltersProAllFiltersSidebarProps = ProductFiltersProAggregationsProps &
15
+ ProductFiltersProSortSectionProps & { sx?: SxProps<Theme> }
16
+
17
+ const defaultRenderer = {
18
+ FilterRangeTypeInput: ProductFilterRangeSection,
19
+ FilterEqualTypeInput: ProductFilterEqualSection,
20
+ }
21
+
22
+ export function ProductFiltersProAllFiltersSidebar(props: ProductFiltersProAllFiltersSidebarProps) {
23
+ const { sort_fields, total_count, renderer, sx = [] } = props
24
+
25
+ return (
26
+ <Box sx={[{ display: { xs: 'none', md: 'grid' } }, ...(Array.isArray(sx) ? sx : [sx])]}>
27
+ <ProductFiltersProSortSection sort_fields={sort_fields} total_count={total_count} />
28
+ <ProductFiltersProLimitSection />
29
+ <ProductFiltersProAggregations renderer={{ ...defaultRenderer, ...renderer }} />
30
+ </Box>
31
+ )
32
+ }
@@ -0,0 +1,48 @@
1
+ import { Button } from '@graphcommerce/next-ui'
2
+ import { Trans } from '@lingui/react'
3
+ import { SxProps, Theme } from '@mui/material'
4
+ import { useProductFiltersPro } from './ProductFiltersPro'
5
+ import { ProductFiltersProAggregationsProps } from './ProductFiltersProAggregations'
6
+ import { activeAggregations } from './activeAggregations'
7
+ import { applyAggregationCount } from './applyAggregationCount'
8
+ import { useClearAllFiltersAction } from './useClearAllFiltersHandler'
9
+
10
+ type AllFiltersSidebar = ProductFiltersProAggregationsProps & {
11
+ sx?: SxProps<Theme>
12
+ }
13
+
14
+ export function ProductFiltersProClearAll(props: AllFiltersSidebar) {
15
+ const { sx = [] } = props
16
+
17
+ const { params, aggregations, appliedAggregations } = useProductFiltersPro()
18
+ const { sort } = params
19
+
20
+ const clearAll = useClearAllFiltersAction()
21
+
22
+ const activeFilters = activeAggregations(
23
+ applyAggregationCount(aggregations, appliedAggregations, params),
24
+ params,
25
+ ).map(({ label }) => label)
26
+
27
+ const allFilters = [...activeFilters, sort].filter(Boolean)
28
+ const hasFilters = allFilters.length > 0
29
+
30
+ if (!hasFilters) return null
31
+
32
+ return (
33
+ <Button
34
+ sx={sx}
35
+ fullWidth
36
+ variant='pill'
37
+ size='medium'
38
+ color='inherit'
39
+ disableElevation
40
+ onClick={(e) => {
41
+ e.preventDefault()
42
+ return clearAll()
43
+ }}
44
+ >
45
+ <Trans id='Clear all filters' />
46
+ </Button>
47
+ )
48
+ }
@@ -0,0 +1,100 @@
1
+ import { FormAutoSubmit } from '@graphcommerce/ecommerce-ui'
2
+ import { extendableComponent, StickyBelowHeader } from '@graphcommerce/next-ui'
3
+ import { Box, Container, Theme, useMediaQuery } from '@mui/material'
4
+ import React from 'react'
5
+ import { useProductFiltersPro } from './ProductFiltersPro'
6
+
7
+ export type ProductFiltersProLayoutSidebarProps = {
8
+ items: React.ReactNode
9
+ clearAll?: React.ReactNode
10
+ horizontalFilters: React.ReactNode
11
+ sidebarFilters?: React.ReactNode
12
+ count?: React.ReactNode
13
+ pagination: React.ReactNode
14
+ header?: React.ReactNode
15
+ } & Partial<OwnerProps>
16
+
17
+ type OwnerProps = {
18
+ headerPosition: 'before'
19
+ }
20
+
21
+ const name = 'ProductFiltersProLayoutSidebar' as const
22
+ const parts = ['root', 'content'] as const
23
+ const { withState } = extendableComponent<OwnerProps, typeof name, typeof parts>(name, parts)
24
+
25
+ export function ProductFiltersProLayoutSidebar(props: ProductFiltersProLayoutSidebarProps) {
26
+ const {
27
+ items,
28
+ clearAll,
29
+ horizontalFilters,
30
+ count,
31
+ pagination,
32
+ sidebarFilters,
33
+ header,
34
+ headerPosition = 'before',
35
+ } = props
36
+
37
+ const { form, submit } = useProductFiltersPro()
38
+ const classes = withState({ headerPosition })
39
+
40
+ // We only need to auto-submit when the layout is not sidebar and we're viewing on desktop
41
+ const m = useMediaQuery<Theme>((t) => t.breakpoints.down('md'), { defaultMatches: false })
42
+ const autoSubmitDisabled = m
43
+
44
+ return (
45
+ <>
46
+ {headerPosition === 'before' ? header : null}
47
+
48
+ <FormAutoSubmit control={form.control} disabled={autoSubmitDisabled} submit={submit} />
49
+
50
+ <StickyBelowHeader sx={{ display: { md: 'none' } }}>{horizontalFilters}</StickyBelowHeader>
51
+
52
+ <Container
53
+ maxWidth={false}
54
+ className={classes.content}
55
+ sx={(theme) => ({
56
+ display: 'grid',
57
+ gridTemplate: {
58
+ xs: `
59
+ "beforeContent" auto
60
+ "items" auto
61
+ "afterContent" auto
62
+ `,
63
+ md: `
64
+ "topleft beforeContent" auto
65
+ "sidebar items" min-content
66
+ "sidebar afterContent" 1fr
67
+ /300px auto
68
+ `,
69
+ },
70
+
71
+ columnGap: { md: theme.spacings.md, xl: theme.spacings.xxl },
72
+
73
+ '& .ProductListItemsBase-root.sizeNormal': {
74
+ gridTemplateColumns: {
75
+ xs: 'repeat(2, 1fr)',
76
+ lg: 'repeat(3, 1fr)',
77
+ xl: 'repeat(4, 1fr)',
78
+ },
79
+ },
80
+ })}
81
+ >
82
+ <Box gridArea='topleft' sx={{ display: { xs: 'none', md: 'block' }, alignSelf: 'center' }}>
83
+ {clearAll}
84
+ </Box>
85
+ {sidebarFilters && (
86
+ <Box gridArea='sidebar' sx={{ display: { xs: 'none', md: 'block' } }}>
87
+ {sidebarFilters}
88
+ </Box>
89
+ )}
90
+
91
+ <Box gridArea='beforeContent' sx={{ mt: { md: 0 } }}>
92
+ {count}
93
+ </Box>
94
+ <Box gridArea='items'>{items}</Box>
95
+
96
+ {pagination && <Box gridArea='afterContent'>{pagination}</Box>}
97
+ </Container>
98
+ </>
99
+ )
100
+ }
@@ -50,8 +50,8 @@ export function ProductFiltersProLimitChip(props: ProductFiltersProLimitChipProp
50
50
  onReset={
51
51
  activePageSize
52
52
  ? () => {
53
- form.resetField('pageSize', { defaultValue: null })
54
- form.resetField('currentPage', { defaultValue: 1 })
53
+ form.setValue('pageSize', null)
54
+ form.setValue('currentPage', 1)
55
55
  return submit()
56
56
  }
57
57
  : undefined
@@ -3,17 +3,18 @@ import { useQuery } from '@graphcommerce/graphql'
3
3
  import { StoreConfigDocument } from '@graphcommerce/magento-store'
4
4
  import {
5
5
  ActionCard,
6
+ ActionCardAccordion,
6
7
  ActionCardItemBase,
7
8
  ActionCardListForm,
8
- SectionHeader,
9
+ Button,
9
10
  } from '@graphcommerce/next-ui'
10
11
  import { Trans } from '@lingui/react'
11
- import { Box, Button } from '@mui/material'
12
12
  import { useMemo } from 'react'
13
13
  import { useProductFiltersPro } from './ProductFiltersPro'
14
14
 
15
- export type ProductFiltersProLimitSectionProps = object
15
+ export type ProductFiltersProLimitSectionProps = Record<string, unknown>
16
16
 
17
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
17
18
  export function ProductFiltersProLimitSection(props: ProductFiltersProLimitSectionProps) {
18
19
  const { form } = useProductFiltersPro()
19
20
  const { control } = form
@@ -36,34 +37,36 @@ export function ProductFiltersProLimitSection(props: ProductFiltersProLimitSecti
36
37
  if (options.length <= 1) return null
37
38
 
38
39
  return (
39
- <Box sx={{ my: 2 }}>
40
- <SectionHeader
41
- labelLeft={<Trans id='Per page' />}
42
- sx={{ mt: 0 }}
43
- labelRight={
44
- activePageSize ? (
45
- <Button
46
- variant='inline'
47
- color='primary'
48
- onClick={() => {
49
- form.resetField('pageSize', { defaultValue: null })
50
- form.resetField('currentPage', { defaultValue: 1 })
51
- }}
52
- >
53
- <Trans id='Clear' />
54
- </Button>
55
- ) : undefined
56
- }
57
- />
58
- <ActionCardListForm
59
- control={control}
60
- name='pageSize'
61
- layout='list'
62
- variant='default'
63
- size='medium'
64
- render={ActionCard}
65
- items={options}
66
- />
67
- </Box>
40
+ <ActionCardAccordion
41
+ defaultExpanded={!!activePageSize}
42
+ summary={<Trans id='Per page' />}
43
+ details={
44
+ <ActionCardListForm
45
+ sx={{ mb: 2 }}
46
+ render={ActionCard}
47
+ name='pageSize'
48
+ control={control}
49
+ layout='list'
50
+ variant='default'
51
+ size='medium'
52
+ items={options}
53
+ />
54
+ }
55
+ right={
56
+ activePageSize ? (
57
+ <Button
58
+ variant='inline'
59
+ color='primary'
60
+ onClick={(e) => {
61
+ e.stopPropagation()
62
+ form.setValue('pageSize', null)
63
+ form.setValue('currentPage', 1)
64
+ }}
65
+ >
66
+ <Trans id='Clear' />
67
+ </Button>
68
+ ) : undefined
69
+ }
70
+ />
68
71
  )
69
72
  }
@@ -49,8 +49,9 @@ export function ProductFiltersProSortChip(props: ProductListActionSortProps) {
49
49
  onReset={
50
50
  activeSort
51
51
  ? () => {
52
- form.resetField('sort', { defaultValue: null })
53
- form.resetField('currentPage', { defaultValue: 1 })
52
+ form.setValue('sort', null)
53
+ form.setValue('dir', null)
54
+ form.setValue('currentPage', 1)
54
55
  return submit()
55
56
  }
56
57
  : undefined
@@ -3,13 +3,12 @@ import { useQuery } from '@graphcommerce/graphql'
3
3
  import { StoreConfigDocument } from '@graphcommerce/magento-store'
4
4
  import {
5
5
  ActionCard,
6
+ ActionCardAccordion,
6
7
  ActionCardListForm,
7
8
  Button,
8
9
  filterNonNullableKeys,
9
- SectionHeader,
10
10
  } from '@graphcommerce/next-ui'
11
11
  import { Trans } from '@lingui/react'
12
- import { Box } from '@mui/material'
13
12
  import { useMemo } from 'react'
14
13
  import { ProductListSortFragment } from '../ProductListSort/ProductListSort.gql'
15
14
  import { useProductFiltersPro } from './ProductFiltersPro'
@@ -36,34 +35,35 @@ export function ProductFiltersProSortSection(props: ProductFiltersProSortSection
36
35
  )
37
36
 
38
37
  return (
39
- <Box sx={{ my: 2 }}>
40
- <SectionHeader
41
- labelLeft={<Trans id='Sort By' />}
42
- sx={{ mt: 0 }}
43
- labelRight={
44
- activeSort ? (
45
- <Button
46
- variant='inline'
47
- color='primary'
48
- onClick={() => {
49
- form.resetField('sort', { defaultValue: null })
50
- form.resetField('currentPage', { defaultValue: 1 })
51
- }}
52
- >
53
- <Trans id='Clear' />
54
- </Button>
55
- ) : undefined
56
- }
57
- />
58
- <ActionCardListForm
59
- control={control}
60
- name='sort'
61
- layout='list'
62
- variant='default'
63
- size='medium'
64
- render={ActionCard}
65
- items={options}
66
- />
67
- </Box>
38
+ <ActionCardAccordion
39
+ defaultExpanded={!!activeSort}
40
+ summary={<Trans id='Sort By' />}
41
+ details={
42
+ <ActionCardListForm
43
+ control={control}
44
+ name='sort'
45
+ layout='list'
46
+ variant='default'
47
+ size='medium'
48
+ render={ActionCard}
49
+ items={options}
50
+ />
51
+ }
52
+ right={
53
+ activeSort ? (
54
+ <Button
55
+ color='primary'
56
+ onClick={(e) => {
57
+ e.stopPropagation()
58
+ form.setValue('sort', null)
59
+ form.setValue('dir', null)
60
+ form.setValue('currentPage', 1)
61
+ }}
62
+ >
63
+ <Trans id='Clear' />
64
+ </Button>
65
+ ) : undefined
66
+ }
67
+ />
68
68
  )
69
69
  }
@@ -2,7 +2,10 @@ export * from './ProductFilterEqualChip'
2
2
  export * from './ProductFilterRangeChip'
3
3
  export * from './ProductFiltersPro'
4
4
  export * from './ProductFiltersProAllFiltersChip'
5
+ export * from './ProductFiltersProAllFiltersSidebar'
5
6
  export * from './ProductFiltersProChips'
7
+ export * from './ProductFiltersProClearAll'
8
+ export * from './ProductFiltersProLayoutSidebar'
6
9
  export * from './ProductFiltersProLimitChip'
7
10
  export * from './ProductFiltersProLimitSection'
8
11
  export * from './ProductFiltersProSortChip'
@@ -0,0 +1,31 @@
1
+ import { useCallback } from 'react'
2
+ import { ProductFilterParams } from '../ProductListItems/filterTypes'
3
+ import { useProductFiltersPro } from './ProductFiltersPro'
4
+
5
+ export function useClearAllFiltersAction() {
6
+ const { form, submit } = useProductFiltersPro()
7
+ const { reset, getValues } = form
8
+
9
+ return useCallback(() => {
10
+ const currentValues = getValues()
11
+ const cleanedFilters: ProductFilterParams['filters'] = Object.fromEntries(
12
+ Object.keys(currentValues.filters).map((key) => [key, null]),
13
+ )
14
+
15
+ reset(
16
+ {
17
+ ...currentValues,
18
+ pageSize: null,
19
+ currentPage: 1,
20
+ dir: null,
21
+ sort: null,
22
+ filters: {
23
+ ...cleanedFilters,
24
+ category_uid: getValues('filters.category_uid'),
25
+ },
26
+ },
27
+ { keepSubmitCount: true, keepIsValid: true },
28
+ )
29
+ return submit()
30
+ }, [getValues, reset, submit])
31
+ }