@faststore/components 3.41.3 → 3.44.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 (86) hide show
  1. package/dist/cjs/hooks/UIProvider.d.ts +10 -1
  2. package/dist/cjs/hooks/UIProvider.js +24 -0
  3. package/dist/cjs/hooks/UIProvider.js.map +1 -1
  4. package/dist/cjs/hooks/index.d.ts +2 -1
  5. package/dist/cjs/hooks/index.js +5 -3
  6. package/dist/cjs/hooks/index.js.map +1 -1
  7. package/dist/cjs/hooks/useOnClickOutside.d.ts +4 -0
  8. package/dist/cjs/hooks/useOnClickOutside.js +33 -0
  9. package/dist/cjs/hooks/useOnClickOutside.js.map +1 -0
  10. package/dist/cjs/index.d.ts +2 -0
  11. package/dist/cjs/index.js +4 -2
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/molecules/Modal/Modal.d.ts +14 -6
  14. package/dist/cjs/molecules/Modal/Modal.js +13 -4
  15. package/dist/cjs/molecules/Modal/Modal.js.map +1 -1
  16. package/dist/cjs/molecules/Popover/Popover.d.ts +67 -0
  17. package/dist/cjs/molecules/Popover/Popover.js +65 -0
  18. package/dist/cjs/molecules/Popover/Popover.js.map +1 -0
  19. package/dist/cjs/molecules/Popover/index.d.ts +2 -0
  20. package/dist/cjs/molecules/Popover/index.js +9 -0
  21. package/dist/cjs/molecules/Popover/index.js.map +1 -0
  22. package/dist/cjs/molecules/RegionBar/RegionBar.d.ts +13 -4
  23. package/dist/cjs/molecules/RegionBar/RegionBar.js +5 -3
  24. package/dist/cjs/molecules/RegionBar/RegionBar.js.map +1 -1
  25. package/dist/cjs/molecules/SearchProducts/SearchProductItemContent.d.ts +24 -0
  26. package/dist/cjs/molecules/SearchProducts/SearchProductItemContent.js +9 -3
  27. package/dist/cjs/molecules/SearchProducts/SearchProductItemContent.js.map +1 -1
  28. package/dist/cjs/molecules/SearchProducts/SearchProductItemControl.d.ts +50 -0
  29. package/dist/cjs/molecules/SearchProducts/SearchProductItemControl.js +62 -0
  30. package/dist/cjs/molecules/SearchProducts/SearchProductItemControl.js.map +1 -0
  31. package/dist/cjs/organisms/RegionModal/RegionModal.d.ts +10 -1
  32. package/dist/cjs/organisms/RegionModal/RegionModal.js +15 -8
  33. package/dist/cjs/organisms/RegionModal/RegionModal.js.map +1 -1
  34. package/dist/cjs/organisms/SKUMatrix/SKUMatrixSidebar.js +1 -1
  35. package/dist/cjs/organisms/SKUMatrix/SKUMatrixSidebar.js.map +1 -1
  36. package/dist/cjs/organisms/ShippingSimulation/ShippingSimulation.d.ts +2 -2
  37. package/dist/esm/hooks/UIProvider.d.ts +10 -1
  38. package/dist/esm/hooks/UIProvider.js +24 -0
  39. package/dist/esm/hooks/UIProvider.js.map +1 -1
  40. package/dist/esm/hooks/index.d.ts +2 -1
  41. package/dist/esm/hooks/index.js +2 -1
  42. package/dist/esm/hooks/index.js.map +1 -1
  43. package/dist/esm/hooks/useOnClickOutside.d.ts +4 -0
  44. package/dist/esm/hooks/useOnClickOutside.js +29 -0
  45. package/dist/esm/hooks/useOnClickOutside.js.map +1 -0
  46. package/dist/esm/index.d.ts +2 -0
  47. package/dist/esm/index.js +1 -0
  48. package/dist/esm/index.js.map +1 -1
  49. package/dist/esm/molecules/Modal/Modal.d.ts +14 -6
  50. package/dist/esm/molecules/Modal/Modal.js +13 -4
  51. package/dist/esm/molecules/Modal/Modal.js.map +1 -1
  52. package/dist/esm/molecules/Popover/Popover.d.ts +67 -0
  53. package/dist/esm/molecules/Popover/Popover.js +62 -0
  54. package/dist/esm/molecules/Popover/Popover.js.map +1 -0
  55. package/dist/esm/molecules/Popover/index.d.ts +2 -0
  56. package/dist/esm/molecules/Popover/index.js +2 -0
  57. package/dist/esm/molecules/Popover/index.js.map +1 -0
  58. package/dist/esm/molecules/RegionBar/RegionBar.d.ts +13 -4
  59. package/dist/esm/molecules/RegionBar/RegionBar.js +5 -3
  60. package/dist/esm/molecules/RegionBar/RegionBar.js.map +1 -1
  61. package/dist/esm/molecules/SearchProducts/SearchProductItemContent.d.ts +24 -0
  62. package/dist/esm/molecules/SearchProducts/SearchProductItemContent.js +10 -4
  63. package/dist/esm/molecules/SearchProducts/SearchProductItemContent.js.map +1 -1
  64. package/dist/esm/molecules/SearchProducts/SearchProductItemControl.d.ts +50 -0
  65. package/dist/esm/molecules/SearchProducts/SearchProductItemControl.js +59 -0
  66. package/dist/esm/molecules/SearchProducts/SearchProductItemControl.js.map +1 -0
  67. package/dist/esm/organisms/RegionModal/RegionModal.d.ts +10 -1
  68. package/dist/esm/organisms/RegionModal/RegionModal.js +15 -8
  69. package/dist/esm/organisms/RegionModal/RegionModal.js.map +1 -1
  70. package/dist/esm/organisms/SKUMatrix/SKUMatrixSidebar.js +1 -1
  71. package/dist/esm/organisms/SKUMatrix/SKUMatrixSidebar.js.map +1 -1
  72. package/dist/esm/organisms/ShippingSimulation/ShippingSimulation.d.ts +2 -2
  73. package/package.json +2 -2
  74. package/src/hooks/UIProvider.tsx +48 -1
  75. package/src/hooks/index.ts +2 -1
  76. package/src/hooks/useOnClickOutside.ts +40 -0
  77. package/src/index.ts +2 -0
  78. package/src/molecules/Modal/Modal.tsx +29 -8
  79. package/src/molecules/Popover/Popover.tsx +209 -0
  80. package/src/molecules/Popover/index.tsx +2 -0
  81. package/src/molecules/RegionBar/RegionBar.tsx +21 -5
  82. package/src/molecules/SearchProducts/SearchProductItemContent.tsx +61 -10
  83. package/src/molecules/SearchProducts/SearchProductItemControl.tsx +199 -0
  84. package/src/organisms/RegionModal/RegionModal.tsx +29 -7
  85. package/src/organisms/SKUMatrix/SKUMatrixSidebar.tsx +1 -1
  86. package/src/organisms/ShippingSimulation/ShippingSimulation.tsx +2 -2
@@ -1,6 +1,6 @@
1
1
  export { default as UIProvider, Toast as ToastProps, useUI } from './UIProvider'
2
2
  export { useFadeEffect } from './useFadeEffect'
3
- export { useTrapFocus } from './useTrapFocus'
3
+ export { useOnClickOutside } from './useOnClickOutside'
4
4
  export { useSearch } from './useSearch'
5
5
  export { useSKUMatrix } from './useSKUMatrix'
6
6
  export { useScrollDirection } from './useScrollDirection'
@@ -12,3 +12,4 @@ export type {
12
12
  SlideDirection,
13
13
  } from './useSlider'
14
14
  export { useSlideVisibility } from './useSlideVisibility'
15
+ export { useTrapFocus } from './useTrapFocus'
@@ -0,0 +1,40 @@
1
+ import type { RefObject } from 'react'
2
+ import { useEffect } from 'react'
3
+
4
+ type Handler = (event: any) => void
5
+
6
+ export function useOnClickOutside<T extends HTMLElement = HTMLElement>(
7
+ ref: RefObject<T> | undefined,
8
+ handler: Handler
9
+ ) {
10
+ useEffect(
11
+ () => {
12
+ if (!ref?.current) return
13
+
14
+ const listener: Handler = (event) => {
15
+ if (!ref?.current || ref.current.contains(event.target)) {
16
+ return
17
+ }
18
+
19
+ handler(event)
20
+ }
21
+
22
+ document.addEventListener('mousedown', listener)
23
+ document.addEventListener('touchstart', listener)
24
+
25
+ return () => {
26
+ document.removeEventListener('mousedown', listener)
27
+ document.removeEventListener('touchstart', listener)
28
+ }
29
+ },
30
+ /**
31
+ * Add ref and handler to effect dependencies.
32
+ * It's worth noting that because passed in handler is a new
33
+ * function on every render that will cause this effect
34
+ * callback/cleanup to run every render. It's not a big deal
35
+ * but to optimize you can wrap handler in useCallback before
36
+ * passing it into this hook.
37
+ */
38
+ [ref, handler]
39
+ )
40
+ }
package/src/index.ts CHANGED
@@ -131,6 +131,8 @@ export type {
131
131
  } from './molecules/NavbarLinks'
132
132
  export { default as OrderSummary } from './molecules/OrderSummary'
133
133
  export type { OrderSummaryProps } from './molecules/OrderSummary'
134
+ export { default as Popover } from './molecules/Popover'
135
+ export type { PopoverProps } from './molecules/Popover'
134
136
  export {
135
137
  default as ProductCard,
136
138
  ProductCardImage,
@@ -20,7 +20,8 @@ export type ModalChildrenProps = {
20
20
 
21
21
  type ModalChildrenFunction = (props: ModalChildrenProps) => ReactNode
22
22
 
23
- export interface ModalProps extends Omit<ModalContentProps, 'children'> {
23
+ export interface ModalProps
24
+ extends Omit<ModalContentProps, 'children' | 'onEntered'> {
24
25
  /**
25
26
  * ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
26
27
  */
@@ -31,21 +32,29 @@ export interface ModalProps extends Omit<ModalContentProps, 'children'> {
31
32
  */
32
33
  'aria-labelledby'?: AriaAttributes['aria-label']
33
34
  /**
34
- * A boolean value that represents the state of the Modal
35
+ * A boolean value that represents the state of the Modal.
35
36
  */
36
37
  isOpen?: boolean
37
38
  /**
38
- * Event emitted when the modal is closed
39
+ * Event emitted when the modal is closed.
39
40
  */
40
41
  onDismiss?: () => void
41
42
  /**
42
- * Props forwarded to the `Overlay` component
43
+ * Callback function when the modal is opened.
44
+ */
45
+ onEntered?: () => void
46
+ /**
47
+ * Props forwarded to the `Overlay` component.
43
48
  */
44
49
  overlayProps?: OverlayProps
45
50
  /**
46
- * Children or function as a children
51
+ * Children or function as a children.
47
52
  */
48
53
  children: ModalChildrenFunction | ReactNode
54
+ /**
55
+ * Disable being closed using the Escape key.
56
+ */
57
+ disableEscapeKeyDown?: boolean
49
58
  }
50
59
 
51
60
  /*
@@ -60,13 +69,15 @@ const Modal = ({
60
69
  isOpen = true,
61
70
  onDismiss,
62
71
  overlayProps,
72
+ disableEscapeKeyDown = false,
73
+ onEntered,
63
74
  ...otherProps
64
75
  }: ModalProps) => {
65
76
  const { closeModal } = useUI()
66
77
  const { fade, fadeOut, fadeIn } = useFadeEffect()
67
78
 
68
79
  const handleBackdropClick = (event: MouseEvent) => {
69
- if (event.defaultPrevented) {
80
+ if (disableEscapeKeyDown || event.defaultPrevented) {
70
81
  return
71
82
  }
72
83
 
@@ -76,7 +87,11 @@ const Modal = ({
76
87
  }
77
88
 
78
89
  const handleBackdropKeyDown = (event: KeyboardEvent) => {
79
- if (event.key !== 'Escape' || event.defaultPrevented) {
90
+ if (
91
+ disableEscapeKeyDown ||
92
+ event.key !== 'Escape' ||
93
+ event.defaultPrevented
94
+ ) {
80
95
  return
81
96
  }
82
97
 
@@ -93,7 +108,13 @@ const Modal = ({
93
108
  {...overlayProps}
94
109
  >
95
110
  <ModalContent
96
- onTransitionEnd={() => fade === 'out' && closeModal()}
111
+ onTransitionEnd={() => {
112
+ if (fade === 'out') {
113
+ closeModal()
114
+ } else if (fade === 'in' && onEntered) {
115
+ onEntered()
116
+ }
117
+ }}
97
118
  data-fs-modal
98
119
  data-fs-modal-state={fade}
99
120
  testId={testId}
@@ -0,0 +1,209 @@
1
+ import React, {
2
+ forwardRef,
3
+ useCallback,
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
+ type HTMLAttributes,
8
+ type KeyboardEvent,
9
+ type ReactNode,
10
+ type RefObject,
11
+ } from 'react'
12
+ import Icon from '../../atoms/Icon'
13
+ import IconButton from '../IconButton'
14
+
15
+ import { useOnClickOutside, useUI } from '../../hooks'
16
+
17
+ /**
18
+ * Specifies Popover position.
19
+ */
20
+ export type Side = 'bottom' | 'top'
21
+
22
+ /**
23
+ * Specifies tooltip alignment.
24
+ */
25
+ export type Alignment = 'start' | 'center' | 'end'
26
+
27
+ /**
28
+ * Combines side + alignment (e.g., "top-start").
29
+ */
30
+ export type Placement = `${Side}-${Alignment}`
31
+
32
+ export interface PopoverProps
33
+ extends Omit<HTMLAttributes<HTMLDivElement>, 'content'> {
34
+ /**
35
+ * The Popover header's title.
36
+ */
37
+ title?: string
38
+ /**
39
+ * Content of the Popover.
40
+ */
41
+ content: ReactNode
42
+ /**
43
+ * Defines the side or side-alignment (e.g., "bottom-start", "bottom-center") of the Popover.
44
+ */
45
+ placement?: Placement
46
+ /**
47
+ * If the Popover can be closed by a button.
48
+ */
49
+ dismissible?: boolean
50
+ /**
51
+ * Called when the Popover is dismissed.
52
+ */
53
+ onDismiss?: () => void
54
+ /**
55
+ * Callback when the Popover is fully rendered and positioned.
56
+ */
57
+ onEntered?: () => void
58
+ /**
59
+ * Close button aria-label.
60
+ */
61
+ closeButtonAriaLabel?: string
62
+ /**
63
+ * Controls whether the Popover is open.
64
+ */
65
+ isOpen: boolean
66
+ /**
67
+ * ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
68
+ */
69
+ testId?: string
70
+ /**
71
+ * Offset value for top position (e.g.: 12).
72
+ * @default '8'
73
+ */
74
+ offsetTop?: number
75
+ /**
76
+ * Offset value for left position (e.g.: 12).
77
+ * @default '0'
78
+ */
79
+ offsetLeft?: number
80
+ /**
81
+ * Reference to the trigger element that opens the Popover.
82
+ */
83
+ triggerRef?: RefObject<HTMLElement>
84
+ }
85
+
86
+ const calculatePosition = (
87
+ rect: DOMRect,
88
+ placement: Placement,
89
+ offsetTop: number,
90
+ offsetLeft: number
91
+ ) => {
92
+ const { top, left, height } = rect
93
+
94
+ switch (true) {
95
+ case placement.startsWith('top'):
96
+ return {
97
+ top: top + height + window.scrollY - offsetTop,
98
+ left: left + window.scrollX + offsetLeft,
99
+ }
100
+ case placement.startsWith('bottom'):
101
+ return {
102
+ top: top + height + window.scrollY + offsetTop,
103
+ left: left + window.scrollX + offsetLeft,
104
+ }
105
+ default:
106
+ return { top: 0, left: 0 }
107
+ }
108
+ }
109
+
110
+ const Popover = forwardRef<HTMLDivElement, PopoverProps>(function Popover(
111
+ {
112
+ title,
113
+ content,
114
+ placement = 'bottom-start',
115
+ dismissible = false,
116
+ onDismiss,
117
+ isOpen,
118
+ triggerRef: propTriggerRef,
119
+ offsetTop = 8,
120
+ offsetLeft = 0,
121
+ closeButtonAriaLabel = 'Close Popover',
122
+ testId = 'fs-popover',
123
+ style,
124
+ onEntered,
125
+ ...otherProps
126
+ },
127
+ ref
128
+ ) {
129
+ // Use forwarded ref or internal ref for fallback
130
+ const popoverRef = ref || useRef<HTMLDivElement>(null)
131
+
132
+ const [popoverPosition, setPopoverPosition] = useState({ top: 0, left: 0 })
133
+ const { popover, closePopover } = useUI()
134
+
135
+ const contextTriggerRef = popover.triggerRef
136
+
137
+ // Use the propTriggerRef if provided, otherwise fallback to contextTriggerRef
138
+ const triggerRef = propTriggerRef || contextTriggerRef
139
+
140
+ useEffect(() => {
141
+ if (!isOpen || !triggerRef?.current) return
142
+
143
+ // Set the position according to the trigger element and placement
144
+ const rect = triggerRef.current.getBoundingClientRect()
145
+
146
+ setPopoverPosition(
147
+ calculatePosition(rect, placement, offsetTop, offsetLeft)
148
+ )
149
+
150
+ // Trigger the onEntered callback after positioning
151
+ if (onEntered) {
152
+ onEntered()
153
+ }
154
+ }, [isOpen, triggerRef, offsetTop, offsetLeft, placement])
155
+
156
+ const handleDismiss = useCallback(() => {
157
+ closePopover()
158
+ onDismiss?.()
159
+ }, [closePopover, onDismiss])
160
+
161
+ useOnClickOutside(
162
+ isOpen ? (popoverRef as RefObject<HTMLDivElement>) : undefined,
163
+ handleDismiss
164
+ )
165
+
166
+ const handleKeyDown = useCallback(
167
+ (event: KeyboardEvent<HTMLDivElement>) => {
168
+ if (event.key === 'Escape') {
169
+ handleDismiss()
170
+ }
171
+ },
172
+ [handleDismiss]
173
+ )
174
+
175
+ if (!isOpen) {
176
+ return null
177
+ }
178
+
179
+ return (
180
+ <div
181
+ data-fs-popover
182
+ role="dialog"
183
+ ref={popoverRef}
184
+ data-fs-popover-placement={placement}
185
+ onKeyDown={handleKeyDown}
186
+ data-testid={testId}
187
+ style={{ position: 'absolute', ...popoverPosition, ...style }}
188
+ {...otherProps}
189
+ >
190
+ <header data-fs-popover-header>
191
+ {title && <h3 data-fs-popover-header-title>{title}</h3>}
192
+ {dismissible && (
193
+ <IconButton
194
+ data-fs-popover-header-dismiss-button
195
+ size="small"
196
+ variant="tertiary"
197
+ icon={<Icon name="X" width={20} height={20} />}
198
+ aria-label={closeButtonAriaLabel}
199
+ onClick={handleDismiss}
200
+ />
201
+ )}
202
+ </header>
203
+ <div data-fs-popover-content>{content}</div>
204
+ <span data-fs-popover-indicator aria-hidden="true" />
205
+ </div>
206
+ )
207
+ })
208
+
209
+ export default Popover
@@ -0,0 +1,2 @@
1
+ export { default } from './Popover'
2
+ export type { PopoverProps } from './Popover'
@@ -1,11 +1,17 @@
1
- import type { HTMLAttributes, ReactNode } from 'react'
1
+ import type { HTMLAttributes, ReactNode, RefAttributes } from 'react'
2
2
  import React, { forwardRef } from 'react'
3
3
 
4
4
  import { Button } from '../../'
5
5
 
6
- export interface RegionBarProps extends HTMLAttributes<HTMLDivElement> {
6
+ export interface RegionBarProps
7
+ extends HTMLAttributes<HTMLDivElement>,
8
+ RefAttributes<HTMLDivElement> {
7
9
  /**
8
- * Postal code string to be display in the component
10
+ * City to be displayed in the component.
11
+ */
12
+ city?: string
13
+ /**
14
+ * Postal code string to be display in the component.
9
15
  */
10
16
  postalCode?: string
11
17
  /**
@@ -28,16 +34,23 @@ export interface RegionBarProps extends HTMLAttributes<HTMLDivElement> {
28
34
  * A React component that will be rendered as an icon.
29
35
  */
30
36
  buttonIcon?: ReactNode
37
+ /**
38
+ * Boolean to control whether postal code should be visible or not.
39
+ * @default true
40
+ */
41
+ shouldDisplayPostalCode?: boolean
31
42
  }
32
43
 
33
44
  const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
34
45
  {
46
+ city,
35
47
  postalCode,
36
48
  icon,
37
49
  label,
38
50
  editLabel,
39
51
  buttonIcon,
40
52
  onButtonClick,
53
+ shouldDisplayPostalCode = true,
41
54
  ...otherProps
42
55
  },
43
56
  ref
@@ -51,9 +64,12 @@ const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
51
64
  icon={buttonIcon}
52
65
  >
53
66
  {!!icon && icon}
54
- {postalCode ? (
67
+ {city && postalCode ? (
55
68
  <>
56
- <span data-fs-region-bar-postal-code>{postalCode}</span>
69
+ <span data-fs-region-bar-postal-code>
70
+ {city}
71
+ {shouldDisplayPostalCode && `, ${postalCode}`}
72
+ </span>
57
73
  {!!editLabel && <span data-fs-region-bar-cta>{editLabel}</span>}
58
74
  </>
59
75
  ) : (
@@ -1,5 +1,6 @@
1
- import React, { forwardRef } from 'react'
1
+ import React, { forwardRef, useCallback } from 'react'
2
2
  import { ProductPrice } from '../..'
3
+ import SearchProductItemControl from './SearchProductItemControl'
3
4
 
4
5
  import type { PriceDefinition } from '../../typings/PriceDefinition'
5
6
 
@@ -12,23 +13,73 @@ export interface SearchProductItemContentProps {
12
13
  * Specifies product's prices.
13
14
  */
14
15
  price: PriceDefinition
16
+ /**
17
+ * Quick order settings.
18
+ */
19
+ quickOrder?: {
20
+ enabled: boolean
21
+ outOfStockLabel: string
22
+ availability: boolean
23
+ hasVariants: boolean
24
+ skuMatrixControl: React.ReactNode
25
+ quantity: number
26
+ min?: number
27
+ max?: number
28
+ onChangeQuantity(value: number): void
29
+ buyProps?: {
30
+ onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
31
+ 'data-testid': string
32
+ 'data-sku': string
33
+ 'data-seller': string
34
+ }
35
+ }
36
+ /**
37
+ * Event emitted when value is out of the min and max bounds
38
+ */
39
+ onValidateBlur?: (min: number, maxValue: number, quantity: number) => void
15
40
  }
16
41
 
17
42
  const SearchProductItemContent = forwardRef<
18
43
  HTMLElement,
19
44
  SearchProductItemContentProps
20
- >(function SearchProductItemContent({ price, title, ...otherProps }, ref) {
45
+ >(function SearchProductItemContent(
46
+ { price, title, quickOrder, onValidateBlur, ...otherProps },
47
+ ref
48
+ ) {
49
+ const renderProductItemContent = useCallback(() => {
50
+ return (
51
+ <>
52
+ <p data-fs-search-product-item-title>{title}</p>
53
+ {price.value !== 0 && (
54
+ <ProductPrice
55
+ data-fs-search-product-item-prices
56
+ listPrice={price.listPrice}
57
+ value={price.value}
58
+ formatter={price.formatter}
59
+ />
60
+ )}
61
+ </>
62
+ )
63
+ }, [price.formatter, price.listPrice, price.value, title])
64
+
21
65
  return (
22
66
  <section ref={ref} data-fs-search-product-item-content {...otherProps}>
23
- <p data-fs-search-product-item-title>{title}</p>
67
+ {!quickOrder?.enabled && renderProductItemContent()}
24
68
 
25
- {price.value !== 0 && (
26
- <ProductPrice
27
- data-fs-search-product-item-prices
28
- listPrice={price.listPrice}
29
- value={price.value}
30
- formatter={price.formatter}
31
- />
69
+ {quickOrder?.enabled && (
70
+ <SearchProductItemControl
71
+ outOfStockLabel={quickOrder.outOfStockLabel}
72
+ availability={quickOrder.availability}
73
+ hasVariants={quickOrder.hasVariants}
74
+ skuMatrixControl={quickOrder.skuMatrixControl}
75
+ quantity={quickOrder.quantity}
76
+ onChangeQuantity={quickOrder.onChangeQuantity}
77
+ max={quickOrder.max}
78
+ onValidateBlur={onValidateBlur}
79
+ {...quickOrder.buyProps}
80
+ >
81
+ {renderProductItemContent()}
82
+ </SearchProductItemControl>
32
83
  )}
33
84
  </section>
34
85
  )