@faststore/components 3.65.0 → 3.68.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 (78) hide show
  1. package/dist/cjs/hooks/UIProvider.d.ts +15 -0
  2. package/dist/cjs/hooks/UIProvider.d.ts.map +1 -1
  3. package/dist/cjs/hooks/UIProvider.js +34 -1
  4. package/dist/cjs/hooks/UIProvider.js.map +1 -1
  5. package/dist/cjs/hooks/index.d.ts +3 -3
  6. package/dist/cjs/hooks/index.d.ts.map +1 -1
  7. package/dist/cjs/hooks/index.js +4 -3
  8. package/dist/cjs/hooks/index.js.map +1 -1
  9. package/dist/cjs/molecules/Accordion/Accordion.d.ts.map +1 -1
  10. package/dist/cjs/molecules/Accordion/Accordion.js +5 -1
  11. package/dist/cjs/molecules/Accordion/Accordion.js.map +1 -1
  12. package/dist/cjs/molecules/Modal/Modal.d.ts.map +1 -1
  13. package/dist/cjs/molecules/Modal/Modal.js +10 -15
  14. package/dist/cjs/molecules/Modal/Modal.js.map +1 -1
  15. package/dist/cjs/molecules/RadioField/RadioField.d.ts +5 -4
  16. package/dist/cjs/molecules/RadioField/RadioField.d.ts.map +1 -1
  17. package/dist/cjs/molecules/RadioField/RadioField.js +1 -1
  18. package/dist/cjs/molecules/RadioField/RadioField.js.map +1 -1
  19. package/dist/cjs/molecules/RegionBar/RegionBar.d.ts +30 -3
  20. package/dist/cjs/molecules/RegionBar/RegionBar.d.ts.map +1 -1
  21. package/dist/cjs/molecules/RegionBar/RegionBar.js +9 -8
  22. package/dist/cjs/molecules/RegionBar/RegionBar.js.map +1 -1
  23. package/dist/cjs/organisms/Filter/FilterFacetBooleanItem.d.ts +8 -3
  24. package/dist/cjs/organisms/Filter/FilterFacetBooleanItem.d.ts.map +1 -1
  25. package/dist/cjs/organisms/Filter/FilterFacetBooleanItem.js +3 -3
  26. package/dist/cjs/organisms/Filter/FilterFacetBooleanItem.js.map +1 -1
  27. package/dist/cjs/organisms/Filter/FilterFacets.d.ts +5 -1
  28. package/dist/cjs/organisms/Filter/FilterFacets.d.ts.map +1 -1
  29. package/dist/cjs/organisms/Filter/FilterFacets.js +4 -2
  30. package/dist/cjs/organisms/Filter/FilterFacets.js.map +1 -1
  31. package/dist/cjs/organisms/Filter/FilterSlider.d.ts +15 -1
  32. package/dist/cjs/organisms/Filter/FilterSlider.d.ts.map +1 -1
  33. package/dist/cjs/organisms/Filter/FilterSlider.js +15 -7
  34. package/dist/cjs/organisms/Filter/FilterSlider.js.map +1 -1
  35. package/dist/esm/hooks/UIProvider.d.ts +15 -0
  36. package/dist/esm/hooks/UIProvider.d.ts.map +1 -1
  37. package/dist/esm/hooks/UIProvider.js +33 -0
  38. package/dist/esm/hooks/UIProvider.js.map +1 -1
  39. package/dist/esm/hooks/index.d.ts +3 -3
  40. package/dist/esm/hooks/index.d.ts.map +1 -1
  41. package/dist/esm/hooks/index.js +2 -2
  42. package/dist/esm/hooks/index.js.map +1 -1
  43. package/dist/esm/molecules/Accordion/Accordion.d.ts.map +1 -1
  44. package/dist/esm/molecules/Accordion/Accordion.js +5 -1
  45. package/dist/esm/molecules/Accordion/Accordion.js.map +1 -1
  46. package/dist/esm/molecules/Modal/Modal.d.ts.map +1 -1
  47. package/dist/esm/molecules/Modal/Modal.js +10 -15
  48. package/dist/esm/molecules/Modal/Modal.js.map +1 -1
  49. package/dist/esm/molecules/RadioField/RadioField.d.ts +5 -4
  50. package/dist/esm/molecules/RadioField/RadioField.d.ts.map +1 -1
  51. package/dist/esm/molecules/RadioField/RadioField.js +1 -1
  52. package/dist/esm/molecules/RadioField/RadioField.js.map +1 -1
  53. package/dist/esm/molecules/RegionBar/RegionBar.d.ts +30 -3
  54. package/dist/esm/molecules/RegionBar/RegionBar.d.ts.map +1 -1
  55. package/dist/esm/molecules/RegionBar/RegionBar.js +9 -8
  56. package/dist/esm/molecules/RegionBar/RegionBar.js.map +1 -1
  57. package/dist/esm/organisms/Filter/FilterFacetBooleanItem.d.ts +8 -3
  58. package/dist/esm/organisms/Filter/FilterFacetBooleanItem.d.ts.map +1 -1
  59. package/dist/esm/organisms/Filter/FilterFacetBooleanItem.js +4 -4
  60. package/dist/esm/organisms/Filter/FilterFacetBooleanItem.js.map +1 -1
  61. package/dist/esm/organisms/Filter/FilterFacets.d.ts +5 -1
  62. package/dist/esm/organisms/Filter/FilterFacets.d.ts.map +1 -1
  63. package/dist/esm/organisms/Filter/FilterFacets.js +4 -2
  64. package/dist/esm/organisms/Filter/FilterFacets.js.map +1 -1
  65. package/dist/esm/organisms/Filter/FilterSlider.d.ts +15 -1
  66. package/dist/esm/organisms/Filter/FilterSlider.d.ts.map +1 -1
  67. package/dist/esm/organisms/Filter/FilterSlider.js +15 -7
  68. package/dist/esm/organisms/Filter/FilterSlider.js.map +1 -1
  69. package/package.json +2 -2
  70. package/src/hooks/UIProvider.tsx +57 -0
  71. package/src/hooks/index.ts +10 -5
  72. package/src/molecules/Accordion/Accordion.tsx +5 -1
  73. package/src/molecules/Modal/Modal.tsx +9 -7
  74. package/src/molecules/RadioField/RadioField.tsx +13 -5
  75. package/src/molecules/RegionBar/RegionBar.tsx +71 -16
  76. package/src/organisms/Filter/FilterFacetBooleanItem.tsx +41 -19
  77. package/src/organisms/Filter/FilterFacets.tsx +11 -1
  78. package/src/organisms/Filter/FilterSlider.tsx +50 -15
@@ -1 +1 @@
1
- {"version":3,"file":"FilterSlider.d.ts","sourceRoot":"","sources":["../../../../src/organisms/Filter/FilterSlider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAA;AAC9D,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,YAAY,EAKlB,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAE1E,MAAM,WAAW,iBAAkB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACvE;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,SAAS,EAAE,kBAAkB,CAAA;IAC7B;;OAEG;IACH,IAAI,EAAE,kBAAkB,CAAA;IACxB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IACpC;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IACpC;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,iBAAS,YAAY,CAAC,EACpB,KAAK,EACL,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,OAAO,EACP,GAAG,UAAU,EACd,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,qBAyCtC;AAED,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"FilterSlider.d.ts","sourceRoot":"","sources":["../../../../src/organisms/Filter/FilterSlider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAA;AAC9D,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,YAAY,EAKlB,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAE1E,MAAM,WAAW,iBAAkB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACvE;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,SAAS,EAAE,kBAAkB,CAAA;IAC7B;;OAEG;IACH,IAAI,EAAE,kBAAkB,CAAA;IACxB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IACpC;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IACpC;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAA;IAC3B;;OAEG;IACH,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,iBAAS,YAAY,CAAC,EACpB,MAA2B,EAC3B,KAAK,EACL,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACT,MAAa,EACb,GAAG,UAAU,EACd,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,qBA2DtC;AAED,eAAe,YAAY,CAAA"}
@@ -1,9 +1,17 @@
1
1
  import React from 'react';
2
2
  import { Button, SlideOver, SlideOverHeader, useFadeEffect, useUI, } from '../../';
3
- function FilterSlider({ title, size, direction, children, applyBtnProps, clearBtnProps, overlayProps, onClose, ...otherProps }) {
3
+ function FilterSlider({ testId = 'fs-filter-slider', title, size, direction, children, applyBtnProps, clearBtnProps, overlayProps, onClose, onDismiss, footer = true, ...otherProps }) {
4
4
  const { fade, fadeOut } = useFadeEffect();
5
- const { closeFilter } = useUI();
6
- return (React.createElement(SlideOver, { "data-fs-filter-slider": true, isOpen: true, fade: fade, onDismiss: fadeOut, size: size, direction: direction, onTransitionEnd: () => fade === 'out' && closeFilter(), overlayProps: overlayProps, ...otherProps },
5
+ const { closeFilter, closeRegionSlider } = useUI();
6
+ return (React.createElement(SlideOver, { "data-fs-filter-slider": true, isOpen: true, fade: fade, onDismiss: () => {
7
+ fadeOut();
8
+ onDismiss?.();
9
+ }, size: size, direction: direction, onTransitionEnd: () => {
10
+ if (fade === 'out') {
11
+ closeFilter();
12
+ closeRegionSlider();
13
+ }
14
+ }, testId: testId, overlayProps: overlayProps, ...otherProps },
7
15
  React.createElement("div", { "data-fs-filter-slider-content": true },
8
16
  React.createElement(SlideOverHeader, { onClose: () => {
9
17
  onClose();
@@ -11,12 +19,12 @@ function FilterSlider({ title, size, direction, children, applyBtnProps, clearBt
11
19
  } },
12
20
  React.createElement("h2", { "data-fs-filter-slider-title": true }, title)),
13
21
  children),
14
- React.createElement("footer", { "data-fs-filter-slider-footer": true },
15
- React.createElement(Button, { "data-fs-filter-slider-footer-button-clear": true, ...clearBtnProps }),
16
- React.createElement(Button, { "data-fs-filter-slider-footer-button-apply": true, "data-testid": "filter-slider-button-apply", ...applyBtnProps, onClick: (e) => {
22
+ footer && (React.createElement("footer", { "data-fs-filter-slider-footer": true },
23
+ clearBtnProps && (React.createElement(Button, { "data-fs-filter-slider-footer-button-clear": true, ...clearBtnProps })),
24
+ applyBtnProps && (React.createElement(Button, { "data-fs-filter-slider-footer-button-apply": true, testId: `${testId}-button-apply`, ...applyBtnProps, onClick: (e) => {
17
25
  applyBtnProps?.onClick?.(e);
18
26
  fadeOut();
19
- } }))));
27
+ } }))))));
20
28
  }
21
29
  export default FilterSlider;
22
30
  //# sourceMappingURL=FilterSlider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilterSlider.js","sourceRoot":"","sources":["../../../../src/organisms/Filter/FilterSlider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EACL,MAAM,EAGN,SAAS,EACT,eAAe,EACf,aAAa,EACb,KAAK,GACN,MAAM,QAAQ,CAAA;AAmCf,SAAS,YAAY,CAAC,EACpB,KAAK,EACL,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,OAAO,EACP,GAAG,UAAU,EACwB;IACrC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE,CAAA;IACzC,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,CAAA;IAE/B,OAAO,CACL,oBAAC,SAAS,mCAER,MAAM,QACN,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,OAAO,EAClB,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,GAAG,EAAE,CAAC,IAAI,KAAK,KAAK,IAAI,WAAW,EAAE,EACtD,YAAY,EAAE,YAAY,KACtB,UAAU;QAEd;YACE,oBAAC,eAAe,IACd,OAAO,EAAE,GAAG,EAAE;oBACZ,OAAO,EAAE,CAAA;oBACT,OAAO,EAAE,CAAA;gBACX,CAAC;gBAED,mEAAiC,KAAK,CAAM,CAC5B;YACjB,QAAQ,CACL;QACN;YACE,oBAAC,MAAM,0DAA+C,aAAa,GAAI;YACvE,oBAAC,MAAM,sEAEO,4BAA4B,KACpC,aAAa,EACjB,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;oBACb,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;oBAC3B,OAAO,EAAE,CAAA;gBACX,CAAC,GACD,CACK,CACC,CACb,CAAA;AACH,CAAC;AAED,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"FilterSlider.js","sourceRoot":"","sources":["../../../../src/organisms/Filter/FilterSlider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EACL,MAAM,EAGN,SAAS,EACT,eAAe,EACf,aAAa,EACb,KAAK,GACN,MAAM,QAAQ,CAAA;AAiDf,SAAS,YAAY,CAAC,EACpB,MAAM,GAAG,kBAAkB,EAC3B,KAAK,EACL,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,aAAa,EACb,aAAa,EACb,YAAY,EACZ,OAAO,EACP,SAAS,EACT,MAAM,GAAG,IAAI,EACb,GAAG,UAAU,EACwB;IACrC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,aAAa,EAAE,CAAA;IACzC,MAAM,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,KAAK,EAAE,CAAA;IAElD,OAAO,CACL,oBAAC,SAAS,mCAER,MAAM,QACN,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,GAAG,EAAE;YACd,OAAO,EAAE,CAAA;YACT,SAAS,EAAE,EAAE,CAAA;QACf,CAAC,EACD,IAAI,EAAE,IAAI,EACV,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,GAAG,EAAE;YACpB,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;gBACnB,WAAW,EAAE,CAAA;gBACb,iBAAiB,EAAE,CAAA;YACrB,CAAC;QACH,CAAC,EACD,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,YAAY,KACtB,UAAU;QAEd;YACE,oBAAC,eAAe,IACd,OAAO,EAAE,GAAG,EAAE;oBACZ,OAAO,EAAE,CAAA;oBACT,OAAO,EAAE,CAAA;gBACX,CAAC;gBAED,mEAAiC,KAAK,CAAM,CAC5B;YACjB,QAAQ,CACL;QACL,MAAM,IAAI,CACT;YACG,aAAa,IAAI,CAChB,oBAAC,MAAM,0DAED,aAAa,GACjB,CACH;YACA,aAAa,IAAI,CAChB,oBAAC,MAAM,uDAEL,MAAM,EAAE,GAAG,MAAM,eAAe,KAC5B,aAAa,EACjB,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;oBACb,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;oBAC3B,OAAO,EAAE,CAAA;gBACX,CAAC,GACD,CACH,CACM,CACV,CACS,CACb,CAAA;AACH,CAAC;AAED,eAAe,YAAY,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/components",
3
- "version": "3.65.0",
3
+ "version": "3.68.0",
4
4
  "main": "dist/cjs/index.js",
5
5
  "module": "dist/esm/index.js",
6
6
  "typings": "dist/esm/index.d.ts",
@@ -56,5 +56,5 @@
56
56
  "volta": {
57
57
  "extends": "../../package.json"
58
58
  },
59
- "gitHead": "1dea73f0a9afa63aa0edcc3eb8433bd1155f3e74"
59
+ "gitHead": "ecc700eec58eaa22c6ae8c5f655c204ecf18f598"
60
60
  }
@@ -13,6 +13,21 @@ export interface Popover {
13
13
  triggerRef?: RefObject<HTMLElement>
14
14
  }
15
15
 
16
+ export const regionSliderTypes = {
17
+ setLocation: 'setLocation',
18
+ changeLocation: 'changeLocation',
19
+ changePickupPoint: 'changePickupPoint',
20
+ globalChangePickupPoint: 'globalChangePickupPoint',
21
+ } as const
22
+
23
+ type RegionSliderType =
24
+ (typeof regionSliderTypes)[keyof typeof regionSliderTypes]
25
+
26
+ export type RegionSlider = {
27
+ type: RegionSliderType | 'none'
28
+ isOpen: boolean
29
+ }
30
+
16
31
  interface State {
17
32
  /** Cart sidebar */
18
33
  cart: boolean
@@ -26,6 +41,8 @@ interface State {
26
41
  toasts: Toast[]
27
42
  /** Region Popover */
28
43
  popover: Popover
44
+ /** Region slider */
45
+ regionSlider: RegionSlider
29
46
  }
30
47
 
31
48
  type UIElement = 'navbar' | 'cart' | 'modal' | 'filter'
@@ -56,6 +73,13 @@ type Action =
56
73
  | {
57
74
  type: 'closePopover'
58
75
  }
76
+ | {
77
+ type: 'openRegionSlider'
78
+ payload: RegionSliderType
79
+ }
80
+ | {
81
+ type: 'closeRegionSlider'
82
+ }
59
83
 
60
84
  const reducer = (state: State, action: Action): State => {
61
85
  const { type } = action
@@ -127,6 +151,30 @@ const reducer = (state: State, action: Action): State => {
127
151
  }
128
152
  }
129
153
 
154
+ case 'openRegionSlider': {
155
+ document.body.classList.add('no-scroll')
156
+
157
+ return {
158
+ ...state,
159
+ regionSlider: {
160
+ type: action.payload,
161
+ isOpen: true,
162
+ },
163
+ }
164
+ }
165
+ case 'closeRegionSlider':
166
+ if (!state.filter) {
167
+ document.body.classList.remove('no-scroll')
168
+ }
169
+
170
+ return {
171
+ ...state,
172
+ regionSlider: {
173
+ type: 'none',
174
+ isOpen: false,
175
+ },
176
+ }
177
+
130
178
  default:
131
179
  throw new Error(`Action ${type} not implemented`)
132
180
  }
@@ -142,6 +190,10 @@ const initializer = (): State => ({
142
190
  isOpen: false,
143
191
  triggerRef: undefined,
144
192
  },
193
+ regionSlider: {
194
+ type: 'none',
195
+ isOpen: false,
196
+ },
145
197
  })
146
198
 
147
199
  interface Context extends State {
@@ -157,6 +209,8 @@ interface Context extends State {
157
209
  popToast: () => void
158
210
  openPopover: (popover: Popover) => void
159
211
  closePopover: () => void
212
+ openRegionSlider: (type: RegionSliderType) => void
213
+ closeRegionSlider: () => void
160
214
  }
161
215
 
162
216
  const UIContext = createContext<Context | undefined>(undefined)
@@ -180,6 +234,9 @@ function UIProvider({ children }: PropsWithChildren<unknown>) {
180
234
  openPopover: (popover: Popover) =>
181
235
  dispatch({ type: 'openPopover', payload: popover }),
182
236
  closePopover: () => dispatch({ type: 'closePopover' }),
237
+ openRegionSlider: (type: RegionSliderType) =>
238
+ dispatch({ type: 'openRegionSlider', payload: type }),
239
+ closeRegionSlider: () => dispatch({ type: 'closeRegionSlider' }),
183
240
  }),
184
241
  []
185
242
  )
@@ -1,15 +1,20 @@
1
- export { default as UIProvider, Toast as ToastProps, useUI } from './UIProvider'
1
+ export {
2
+ regionSliderTypes,
3
+ Toast as ToastProps,
4
+ default as UIProvider,
5
+ useUI,
6
+ } from './UIProvider'
2
7
  export { useFadeEffect } from './useFadeEffect'
3
8
  export { useOnClickOutside } from './useOnClickOutside'
9
+ export { useScrollDirection } from './useScrollDirection'
4
10
  export { useSearch } from './useSearch'
5
11
  export { useSKUMatrix } from './useSKUMatrix'
6
- export { useScrollDirection } from './useScrollDirection'
7
12
  export { useSlider } from './useSlider'
8
13
  export type {
9
- UseSliderArgs,
10
- SliderState,
11
- SliderDispatch,
12
14
  SlideDirection,
15
+ SliderDispatch,
16
+ SliderState,
17
+ UseSliderArgs,
13
18
  } from './useSlider'
14
19
  export { useSlideVisibility } from './useSlideVisibility'
15
20
  export { useTrapFocus } from './useTrapFocus'
@@ -37,7 +37,11 @@ const Accordion = forwardRef<HTMLDivElement, AccordionProps>(function Accordion(
37
37
  ) {
38
38
  const childrenWithIndex = React.Children.map(
39
39
  children as ReactElement,
40
- (child, index) => cloneElement(child, { index: child.props.index ?? index })
40
+ (child, index) => {
41
+ if (!child) return
42
+
43
+ return cloneElement(child, { index: child.props.index ?? index })
44
+ }
41
45
  )
42
46
 
43
47
  const context = {
@@ -108,13 +108,15 @@ const Modal = ({
108
108
  {...overlayProps}
109
109
  >
110
110
  <ModalContent
111
- // TODO:
112
- // Using onTransitionEnd can lead to unexpected behavior. Since ModalContentPure uses the useTrapFocus hook,
113
- // focus is automatically moved to the first focusable element inside the modal—typically the Close Button.
114
- // This causes the onTransitionEnd event listener to attach to the Close Button instead of the ModalContent.
115
- // As a result, we may end up listening to the transition of the button (e.g., its focus animation) rather than
116
- // the intended transform transition of the modal content, which can introduce bugs during modal animations.
117
- onTransitionEnd={() => {
111
+ onTransitionEnd={(e) => {
112
+ // Checks if the event wast triggered by this modal or is a bubble event
113
+ if ((e.target as HTMLElement)?.dataset?.testid !== testId) return
114
+ // TODO:
115
+ // Using onTransitionEnd can lead to unexpected behavior. Since ModalContentPure uses the useTrapFocus hook,
116
+ // focus is automatically moved to the first focusable element inside the modal—typically the Close Button.
117
+ // This causes the onTransitionEnd event listener to attach to the Close Button instead of the ModalContent.
118
+ // As a result, we may end up listening to the transition of the button (e.g., its focus animation) rather than
119
+ // the intended transform transition of the modal content, which can introduce bugs during modal animations.
118
120
  if (fade === 'out') {
119
121
  closeModal()
120
122
  } else if (fade === 'in' && onEntered) {
@@ -1,10 +1,13 @@
1
- import type { HTMLAttributes } from 'react'
1
+ import type { HTMLAttributes, ReactNode, InputHTMLAttributes } from 'react'
2
2
  import React, { forwardRef } from 'react'
3
3
 
4
4
  import Label from '../../atoms/Label'
5
5
  import Radio from '../../atoms/Radio'
6
6
 
7
- export interface RadioFieldProps extends HTMLAttributes<HTMLDivElement> {
7
+ type EnhancedRadioFieldProps = HTMLAttributes<HTMLDivElement> &
8
+ Pick<InputHTMLAttributes<HTMLInputElement>, 'checked'>
9
+
10
+ export interface RadioFieldProps extends EnhancedRadioFieldProps {
8
11
  /**
9
12
  * ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
10
13
  */
@@ -14,9 +17,9 @@ export interface RadioFieldProps extends HTMLAttributes<HTMLDivElement> {
14
17
  */
15
18
  id: string
16
19
  /**
17
- * The text displayed to identify the input radio.
20
+ * The text or component displayed to identify the input radio.
18
21
  */
19
- label: string
22
+ label: string | ReactNode
20
23
  /**
21
24
  * The value to identify the input radio.
22
25
  */
@@ -34,7 +37,12 @@ const RadioField = forwardRef<HTMLDivElement, RadioFieldProps>(
34
37
  ) {
35
38
  return (
36
39
  <div ref={ref} data-fs-radio-field data-testid={testId}>
37
- <Radio id={id} value={value ?? label} name={name} {...otherProps} />
40
+ <Radio
41
+ id={id}
42
+ value={typeof label === 'string' ? label : value}
43
+ name={name}
44
+ {...otherProps}
45
+ />
38
46
  <Label htmlFor={id}>{label}</Label>
39
47
  </div>
40
48
  )
@@ -15,11 +15,11 @@ export interface RegionBarProps
15
15
  */
16
16
  postalCode?: string
17
17
  /**
18
- * Function called when button is clicked.
18
+ * Function called when location button is clicked.
19
19
  */
20
20
  onButtonClick?: () => void
21
21
  /**
22
- * A React component that will be rendered as an icon.
22
+ * A React component that will be rendered as the location icon.
23
23
  */
24
24
  icon?: ReactNode
25
25
  /**
@@ -27,7 +27,8 @@ export interface RegionBarProps
27
27
  */
28
28
  label: string
29
29
  /**
30
- * Specifies a label for the edit text.
30
+ * Specifies a label for the edit location text.
31
+ * @deprecated
31
32
  */
32
33
  editLabel?: string
33
34
  /**
@@ -39,18 +40,51 @@ export interface RegionBarProps
39
40
  * @default true
40
41
  */
41
42
  shouldDisplayPostalCode?: boolean
43
+ /**
44
+ * Properties of the global filter button.
45
+ */
46
+ filterButton?: {
47
+ /**
48
+ * A React component that will be rendered as the filter icon.
49
+ */
50
+ icon?: ReactNode
51
+ /**
52
+ * Specifies a label for the filter text.
53
+ */
54
+ label?: string
55
+ /**
56
+ * The current selected filter name.
57
+ */
58
+ selectedFilter?: string
59
+ /**
60
+ * Boolean to control whether the filter button should be visible or not.
61
+ * @default false
62
+ */
63
+ shouldDisplayFilterButton?: boolean
64
+ /**
65
+ * Function called when filter button is clicked.
66
+ */
67
+ onClick?: () => void
68
+ }
42
69
  }
43
70
 
44
71
  const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
45
72
  {
46
73
  city,
47
74
  postalCode,
48
- icon,
49
- label,
50
- editLabel,
75
+ icon: locationIcon,
76
+ label: locationLabel,
77
+ editLabel: _ = undefined,
51
78
  buttonIcon,
52
- onButtonClick,
79
+ onButtonClick: onLocationButtonClick,
53
80
  shouldDisplayPostalCode = true,
81
+ filterButton: {
82
+ icon: filterIcon,
83
+ label: filterLabel,
84
+ selectedFilter,
85
+ shouldDisplayFilterButton = false,
86
+ onClick: onFilterButtonClick,
87
+ } = {},
54
88
  ...otherProps
55
89
  },
56
90
  ref
@@ -59,23 +93,44 @@ const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
59
93
  <div ref={ref} data-fs-region-bar {...otherProps}>
60
94
  <Button
61
95
  variant="tertiary"
62
- iconPosition="right"
63
- onClick={onButtonClick}
64
- icon={buttonIcon}
96
+ iconPosition={buttonIcon ? 'right' : undefined}
97
+ onClick={onLocationButtonClick}
98
+ icon={buttonIcon ?? undefined}
65
99
  >
66
- {!!icon && icon}
100
+ {!!locationIcon && locationIcon}
67
101
  {city && postalCode ? (
68
- <>
69
- <span data-fs-region-bar-postal-code>
102
+ <div data-fs-region-bar-location>
103
+ <span
104
+ data-fs-region-bar-postal-code
105
+ data-fs-region-bar-location-city
106
+ >
70
107
  {city}
108
+ </span>
109
+ <span data-fs-region-bar-location-postal-code>
71
110
  {shouldDisplayPostalCode && `, ${postalCode}`}
72
111
  </span>
73
- {!!editLabel && <span data-fs-region-bar-cta>{editLabel}</span>}
74
- </>
112
+ </div>
75
113
  ) : (
76
- <span data-fs-region-bar-message>{label}</span>
114
+ <span data-fs-region-bar-message data-fs-region-bar-location-message>
115
+ {locationLabel}
116
+ </span>
77
117
  )}
78
118
  </Button>
119
+ {shouldDisplayFilterButton && (
120
+ <Button
121
+ variant="tertiary"
122
+ iconPosition={buttonIcon ? 'right' : undefined}
123
+ onClick={onFilterButtonClick}
124
+ icon={buttonIcon ?? undefined}
125
+ >
126
+ {!!filterIcon && filterIcon}
127
+ {selectedFilter ? (
128
+ <span data-fs-region-bar-filter>{selectedFilter}</span>
129
+ ) : (
130
+ <span data-fs-region-bar-filter-message>{filterLabel}</span>
131
+ )}
132
+ </Button>
133
+ )}
79
134
  </div>
80
135
  )
81
136
  })
@@ -1,5 +1,6 @@
1
+ import type { ReactNode } from 'react'
1
2
  import React from 'react'
2
- import { Badge, Checkbox, Label } from '../..'
3
+ import { Badge, Checkbox, Label, RadioField } from '../..'
3
4
  import type { OnFacetChange } from './Filter'
4
5
 
5
6
  export interface FilterFacetBooleanItemProps {
@@ -10,7 +11,7 @@ export interface FilterFacetBooleanItemProps {
10
11
  /**
11
12
  * The text displayed to identify the Boolean Facet Item.
12
13
  */
13
- label: string
14
+ label: string | ReactNode
14
15
  /**
15
16
  * Value to be emitted when Checkbox is clicked.
16
17
  */
@@ -22,7 +23,7 @@ export interface FilterFacetBooleanItemProps {
22
23
  /**
23
24
  * Counter badge shown besides the Facet Item.
24
25
  */
25
- quantity: number
26
+ quantity?: number
26
27
  /**
27
28
  * ID to identify the Checkbox and corresponding label.
28
29
  */
@@ -35,6 +36,10 @@ export interface FilterFacetBooleanItemProps {
35
36
  * This function is called when `Checkbox` from the facet changes.
36
37
  */
37
38
  onFacetChange: OnFacetChange
39
+ /**
40
+ * Type of the Facet Item.
41
+ */
42
+ type?: 'checkbox' | 'radio'
38
43
  }
39
44
 
40
45
  function FilterFacetBooleanItem({
@@ -45,26 +50,43 @@ function FilterFacetBooleanItem({
45
50
  quantity,
46
51
  facetKey,
47
52
  label,
53
+ type = 'checkbox',
48
54
  onFacetChange,
49
55
  }: FilterFacetBooleanItemProps) {
50
56
  return (
51
57
  <li key={id} data-fs-filter-list-item>
52
- <Checkbox
53
- id={id}
54
- checked={selected}
55
- onChange={() => onFacetChange({ key: facetKey, value }, 'BOOLEAN')}
56
- data-fs-filter-list-item-checkbox
57
- data-testid={`${testId}-accordion-panel-checkbox`}
58
- data-value={value}
59
- data-quantity={quantity}
60
- />
61
- <Label
62
- htmlFor={id}
63
- className="text__title-mini-alt"
64
- data-fs-filter-list-item-label
65
- >
66
- {label} <Badge data-fs-filter-list-item-badge>{quantity}</Badge>
67
- </Label>
58
+ {type === 'checkbox' ? (
59
+ <>
60
+ <Checkbox
61
+ id={id}
62
+ checked={selected}
63
+ onChange={() => onFacetChange({ key: facetKey, value }, 'BOOLEAN')}
64
+ data-fs-filter-list-item-checkbox
65
+ data-testid={`${testId}-accordion-panel-checkbox`}
66
+ data-value={value}
67
+ data-quantity={quantity}
68
+ />
69
+ <Label
70
+ htmlFor={id}
71
+ className="text__title-mini-alt"
72
+ data-fs-filter-list-item-label
73
+ >
74
+ {label} <Badge data-fs-filter-list-item-badge>{quantity}</Badge>
75
+ </Label>
76
+ </>
77
+ ) : (
78
+ <RadioField
79
+ id={id}
80
+ name={facetKey}
81
+ value={value}
82
+ checked={selected}
83
+ onChange={() => onFacetChange({ key: facetKey, value }, 'BOOLEAN')}
84
+ data-fs-filter-list-item-radio
85
+ data-testid={`${testId}-accordion-panel-radio`}
86
+ data-value={value}
87
+ label={label}
88
+ />
89
+ )}
68
90
  </li>
69
91
  )
70
92
  }
@@ -18,6 +18,10 @@ export interface FilterFacetsProps {
18
18
  * The text displayed to identify the Facet.
19
19
  */
20
20
  label: string
21
+ /**
22
+ * The description displayed to identify the Facet.
23
+ */
24
+ description?: string
21
25
  }
22
26
 
23
27
  function FilterFacets({
@@ -26,6 +30,7 @@ function FilterFacets({
26
30
  index,
27
31
  children,
28
32
  type,
33
+ description,
29
34
  }: PropsWithChildren<FilterFacetsProps>) {
30
35
  return (
31
36
  <AccordionItem
@@ -39,7 +44,12 @@ function FilterFacets({
39
44
  <AccordionButton testId={`${testId}-accordion-button`}>
40
45
  {label}
41
46
  </AccordionButton>
42
- <AccordionPanel>{children}</AccordionPanel>
47
+ <AccordionPanel>
48
+ {description && (
49
+ <span data-fs-filter-accordion-item-description>{description}</span>
50
+ )}
51
+ {children}
52
+ </AccordionPanel>
43
53
  </AccordionItem>
44
54
  )
45
55
  }
@@ -14,6 +14,11 @@ import {
14
14
  import type { SlideOverDirection, SlideOverWidthSize } from '../SlideOver'
15
15
 
16
16
  export interface FilterSliderProps extends HTMLAttributes<HTMLDivElement> {
17
+ /**
18
+ * ID to find this component in testing tools (e.g.: cypress,
19
+ * testing-library, and jest).
20
+ */
21
+ testId?: string
17
22
  /**
18
23
  * Title for the FilterSlider component.
19
24
  */
@@ -42,9 +47,19 @@ export interface FilterSliderProps extends HTMLAttributes<HTMLDivElement> {
42
47
  * Function called when Close Button is clicked.
43
48
  */
44
49
  onClose: () => void
50
+ /**
51
+ * This function is called whenever the user clicks outside the slider content.
52
+ */
53
+ onDismiss?: () => void
54
+ /**
55
+ * Display FilterSlider footer with buttons.
56
+ * @default true
57
+ */
58
+ footer?: boolean
45
59
  }
46
60
 
47
61
  function FilterSlider({
62
+ testId = 'fs-filter-slider',
48
63
  title,
49
64
  size,
50
65
  direction,
@@ -53,20 +68,31 @@ function FilterSlider({
53
68
  clearBtnProps,
54
69
  overlayProps,
55
70
  onClose,
71
+ onDismiss,
72
+ footer = true,
56
73
  ...otherProps
57
74
  }: PropsWithChildren<FilterSliderProps>) {
58
75
  const { fade, fadeOut } = useFadeEffect()
59
- const { closeFilter } = useUI()
76
+ const { closeFilter, closeRegionSlider } = useUI()
60
77
 
61
78
  return (
62
79
  <SlideOver
63
80
  data-fs-filter-slider
64
81
  isOpen
65
82
  fade={fade}
66
- onDismiss={fadeOut}
83
+ onDismiss={() => {
84
+ fadeOut()
85
+ onDismiss?.()
86
+ }}
67
87
  size={size}
68
88
  direction={direction}
69
- onTransitionEnd={() => fade === 'out' && closeFilter()}
89
+ onTransitionEnd={() => {
90
+ if (fade === 'out') {
91
+ closeFilter()
92
+ closeRegionSlider()
93
+ }
94
+ }}
95
+ testId={testId}
70
96
  overlayProps={overlayProps}
71
97
  {...otherProps}
72
98
  >
@@ -81,18 +107,27 @@ function FilterSlider({
81
107
  </SlideOverHeader>
82
108
  {children}
83
109
  </div>
84
- <footer data-fs-filter-slider-footer>
85
- <Button data-fs-filter-slider-footer-button-clear {...clearBtnProps} />
86
- <Button
87
- data-fs-filter-slider-footer-button-apply
88
- data-testid="filter-slider-button-apply"
89
- {...applyBtnProps}
90
- onClick={(e) => {
91
- applyBtnProps?.onClick?.(e)
92
- fadeOut()
93
- }}
94
- />
95
- </footer>
110
+ {footer && (
111
+ <footer data-fs-filter-slider-footer>
112
+ {clearBtnProps && (
113
+ <Button
114
+ data-fs-filter-slider-footer-button-clear
115
+ {...clearBtnProps}
116
+ />
117
+ )}
118
+ {applyBtnProps && (
119
+ <Button
120
+ data-fs-filter-slider-footer-button-apply
121
+ testId={`${testId}-button-apply`}
122
+ {...applyBtnProps}
123
+ onClick={(e) => {
124
+ applyBtnProps?.onClick?.(e)
125
+ fadeOut()
126
+ }}
127
+ />
128
+ )}
129
+ </footer>
130
+ )}
96
131
  </SlideOver>
97
132
  )
98
133
  }