@faststore/components 2.0.123-alpha.0 → 2.0.132-alpha.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 (60) hide show
  1. package/dist/atoms/Button/Button.js +7 -6
  2. package/dist/atoms/Button/Button.js.map +1 -1
  3. package/dist/hooks/index.d.ts +3 -0
  4. package/dist/hooks/index.js +2 -0
  5. package/dist/hooks/index.js.map +1 -1
  6. package/dist/hooks/useSlideVisibility.d.ts +9 -0
  7. package/dist/hooks/useSlideVisibility.js +29 -0
  8. package/dist/hooks/useSlideVisibility.js.map +1 -0
  9. package/dist/hooks/useSlider.d.ts +64 -0
  10. package/dist/hooks/useSlider.js +103 -0
  11. package/dist/hooks/useSlider.js.map +1 -0
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +1 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/molecules/Alert/Alert.js +2 -4
  16. package/dist/molecules/Alert/Alert.js.map +1 -1
  17. package/dist/molecules/Carousel/Carousel.d.ts +54 -0
  18. package/dist/molecules/Carousel/Carousel.js +184 -0
  19. package/dist/molecules/Carousel/Carousel.js.map +1 -0
  20. package/dist/molecules/Carousel/CarouselBullets.d.ts +39 -0
  21. package/dist/molecules/Carousel/CarouselBullets.js +12 -0
  22. package/dist/molecules/Carousel/CarouselBullets.js.map +1 -0
  23. package/dist/molecules/Carousel/CarouselItem.d.ts +11 -0
  24. package/dist/molecules/Carousel/CarouselItem.js +18 -0
  25. package/dist/molecules/Carousel/CarouselItem.js.map +1 -0
  26. package/dist/molecules/Carousel/index.d.ts +6 -0
  27. package/dist/molecules/Carousel/index.js +4 -0
  28. package/dist/molecules/Carousel/index.js.map +1 -0
  29. package/dist/molecules/IconButton/IconButton.js +2 -2
  30. package/dist/molecules/IconButton/IconButton.js.map +1 -1
  31. package/dist/molecules/InputField/InputField.js +1 -1
  32. package/dist/molecules/InputField/InputField.js.map +1 -1
  33. package/dist/molecules/LinkButton/LinkButton.js +4 -3
  34. package/dist/molecules/LinkButton/LinkButton.js.map +1 -1
  35. package/dist/molecules/QuantitySelector/QuantitySelector.js +2 -2
  36. package/dist/molecules/QuantitySelector/QuantitySelector.js.map +1 -1
  37. package/dist/molecules/RegionBar/RegionBar.js +2 -3
  38. package/dist/molecules/RegionBar/RegionBar.js.map +1 -1
  39. package/dist/molecules/SearchInputField/SearchInputField.js +1 -1
  40. package/dist/molecules/SearchInputField/SearchInputField.js.map +1 -1
  41. package/dist/organisms/ImageGallery/ImageGallerySelector.js +2 -2
  42. package/dist/organisms/ImageGallery/ImageGallerySelector.js.map +1 -1
  43. package/package.json +2 -2
  44. package/src/atoms/Button/Button.tsx +13 -11
  45. package/src/hooks/index.ts +10 -0
  46. package/src/hooks/useSlideVisibility.ts +59 -0
  47. package/src/hooks/useSlider.ts +209 -0
  48. package/src/index.ts +12 -0
  49. package/src/molecules/Alert/Alert.tsx +2 -6
  50. package/src/molecules/Carousel/Carousel.tsx +398 -0
  51. package/src/molecules/Carousel/CarouselBullets.tsx +95 -0
  52. package/src/molecules/Carousel/CarouselItem.tsx +53 -0
  53. package/src/molecules/Carousel/index.ts +8 -0
  54. package/src/molecules/IconButton/IconButton.tsx +2 -0
  55. package/src/molecules/InputField/InputField.tsx +1 -1
  56. package/src/molecules/LinkButton/LinkButton.tsx +5 -3
  57. package/src/molecules/QuantitySelector/QuantitySelector.tsx +2 -0
  58. package/src/molecules/RegionBar/RegionBar.tsx +1 -2
  59. package/src/molecules/SearchInputField/SearchInputField.tsx +1 -1
  60. package/src/organisms/ImageGallery/ImageGallerySelector.tsx +1 -3
@@ -0,0 +1,398 @@
1
+ import type {
2
+ CSSProperties,
3
+ KeyboardEvent,
4
+ PropsWithChildren,
5
+ ReactNode,
6
+ UIEvent,
7
+ } from 'react'
8
+ import React, { useMemo, useRef } from 'react'
9
+ import type { SwipeableProps } from 'react-swipeable'
10
+
11
+ import { Icon, IconButton } from '../..'
12
+ import { useSlider } from '../../hooks'
13
+ import CarouselBullets from './CarouselBullets'
14
+ import CarouselItem from './CarouselItem'
15
+
16
+ const createTransformValues = (infinite: boolean, totalItems: number) => {
17
+ const transformMap: Record<number, number> = {}
18
+ const slideWidth = 100 / totalItems
19
+
20
+ for (let idx = 0; idx < totalItems; ++idx) {
21
+ const currIdx = infinite ? idx - 1 : idx
22
+ const transformValue = -(slideWidth * idx)
23
+
24
+ transformMap[currIdx] = transformValue
25
+ }
26
+
27
+ return transformMap
28
+ }
29
+
30
+ export interface CarouselProps extends SwipeableProps {
31
+ /**
32
+ * ID of the current instance of the component.
33
+ */
34
+ id?: string
35
+ /**
36
+ * ID to find this component in testing tools (e.g.: cypress, testing library, and jest).
37
+ */
38
+ testId?: string
39
+ /**
40
+ * Returns the value of element's class content attribute.
41
+ */
42
+ className?: string
43
+ /**
44
+ * Whether or not the Carousel is infinite slide/scroll. Only for the `slide` variant.
45
+ * @default true
46
+ */
47
+ infiniteMode?: boolean
48
+ /**
49
+ * Specifies which navigation elements should be visible.
50
+ * @default complete
51
+ */
52
+ controls?: 'complete' | 'navigationArrows' | 'paginationBullets'
53
+ /**
54
+ * Specifies the slide transition. Only for the `slide` variant
55
+ */
56
+ transition?: {
57
+ duration: number
58
+ property: string
59
+ delay?: number
60
+ timing?: string
61
+ }
62
+ /**
63
+ * Specifies the number of items per page.
64
+ * @default 1
65
+ */
66
+ itemsPerPage?: number
67
+ /**
68
+ * Specifies the Carousel track variant.
69
+ * @default slide
70
+ */
71
+ variant?: 'slide' | 'scroll'
72
+ /**
73
+ * Specifies the navigation icons.
74
+ */
75
+ navigationIcons?: {
76
+ left?: ReactNode
77
+ right?: ReactNode
78
+ }
79
+ }
80
+
81
+ function Carousel({
82
+ infiniteMode = true,
83
+ controls = 'complete',
84
+ testId = 'fs-carousel',
85
+ transition = {
86
+ duration: 400,
87
+ property: 'transform',
88
+ },
89
+ children,
90
+ className,
91
+ id = 'fs-carousel',
92
+ variant = 'slide',
93
+ itemsPerPage = 1,
94
+ navigationIcons = undefined,
95
+ ...swipeableConfigOverrides
96
+ }: PropsWithChildren<CarouselProps>) {
97
+ const carouselTrackRef = useRef<HTMLUListElement>(null)
98
+ const isSlideCarousel = variant === 'slide'
99
+ const isScrollCarousel = variant === 'scroll'
100
+ const childrenArray = React.Children.toArray(children)
101
+ const childrenCount = childrenArray.length
102
+ const numberOfSlides = infiniteMode ? childrenCount + 2 : childrenCount
103
+ const slidingTransition = `${transition.property} ${transition.duration}ms ${
104
+ transition.timing ?? ''
105
+ } ${transition.delay ?? ''}`
106
+
107
+ const { handlers, slide, sliderState, sliderDispatch } = useSlider({
108
+ itemsPerPage,
109
+ infiniteMode,
110
+ totalItems: childrenCount,
111
+ shouldSlideOnSwipe: isSlideCarousel,
112
+ ...swipeableConfigOverrides,
113
+ })
114
+
115
+ const pagesCount = Math.ceil(childrenCount / sliderState.itemsPerPage)
116
+
117
+ const showNavigationArrows =
118
+ pagesCount !== 1 &&
119
+ (controls === 'complete' || controls === 'navigationArrows')
120
+
121
+ const showPaginationBullets =
122
+ pagesCount !== 1 &&
123
+ (controls === 'complete' || controls === 'paginationBullets')
124
+
125
+ const transformValues = useMemo(
126
+ () => createTransformValues(infiniteMode, numberOfSlides),
127
+ [numberOfSlides, infiniteMode]
128
+ )
129
+
130
+ const postRenderedSlides =
131
+ infiniteMode && children ? childrenArray.slice(0, 1) : []
132
+
133
+ const preRenderedSlides =
134
+ infiniteMode && children ? childrenArray.slice(childrenCount - 1) : []
135
+
136
+ const slides = preRenderedSlides.concat(
137
+ (children as any) ?? [],
138
+ postRenderedSlides
139
+ )
140
+
141
+ const slideCarouselTrackStyle: CSSProperties = useMemo(
142
+ () => ({
143
+ display: 'flex',
144
+ width: `${numberOfSlides * 100}%`,
145
+ transition: sliderState.sliding ? slidingTransition : undefined,
146
+ transform: `translate3d(${
147
+ transformValues[sliderState.currentPage]
148
+ }%, 0, 0)`,
149
+ }),
150
+ [
151
+ numberOfSlides,
152
+ transformValues,
153
+ slidingTransition,
154
+ sliderState.sliding,
155
+ sliderState.currentPage,
156
+ ]
157
+ )
158
+
159
+ const scrollCarouselTrackStyle: CSSProperties = useMemo(
160
+ () => ({
161
+ width: '100%',
162
+ display: 'block',
163
+ overflowX: 'scroll',
164
+ whiteSpace: 'nowrap',
165
+ }),
166
+ []
167
+ )
168
+
169
+ const carouselTrackStyle =
170
+ ((isSlideCarousel && slideCarouselTrackStyle) as CSSProperties) ||
171
+ ((isScrollCarousel && scrollCarouselTrackStyle) as CSSProperties)
172
+
173
+ const slidePrevious = () => {
174
+ if (
175
+ sliderState.sliding ||
176
+ (!infiniteMode && sliderState.currentPage === 0)
177
+ ) {
178
+ return
179
+ }
180
+
181
+ slide('previous', sliderDispatch)
182
+ }
183
+
184
+ const slideNext = () => {
185
+ if (
186
+ sliderState.sliding ||
187
+ (!infiniteMode && sliderState.currentPage === childrenCount - 1)
188
+ ) {
189
+ return
190
+ }
191
+
192
+ slide('next', sliderDispatch)
193
+ }
194
+
195
+ const onScrollTrack = (event: UIEvent) => {
196
+ if (isSlideCarousel || itemsPerPage > 1) {
197
+ return
198
+ }
199
+
200
+ const itemWidth = Number(event.currentTarget.firstElementChild?.scrollWidth)
201
+ const scrollOffset = event.currentTarget?.scrollLeft
202
+ const formatter = scrollOffset > itemWidth / 2 ? Math.round : Math.floor
203
+ const page = formatter(scrollOffset / itemWidth)
204
+
205
+ slide(page, sliderDispatch)
206
+ }
207
+
208
+ const onTransitionTrackEnd = () => {
209
+ sliderDispatch({
210
+ type: 'STOP_SLIDE',
211
+ })
212
+
213
+ if (infiniteMode && sliderState.currentItem >= childrenCount) {
214
+ sliderDispatch({
215
+ type: 'GO_TO_PAGE',
216
+ payload: {
217
+ pageIndex: 0,
218
+ shouldSlide: false,
219
+ },
220
+ })
221
+ }
222
+
223
+ if (infiniteMode && sliderState.currentItem < 0) {
224
+ sliderDispatch({
225
+ type: 'GO_TO_PAGE',
226
+ payload: {
227
+ pageIndex: sliderState.totalPages - 1,
228
+ shouldSlide: false,
229
+ },
230
+ })
231
+ }
232
+ }
233
+
234
+ const onScrollPagination = async (
235
+ index: number,
236
+ slideDirection?: 'previous' | 'next'
237
+ ) => {
238
+ if (slideDirection === 'previous' && sliderState.currentPage === 0) {
239
+ return
240
+ }
241
+
242
+ if (
243
+ slideDirection === 'next' &&
244
+ sliderState.currentPage === sliderState.totalPages - 1
245
+ ) {
246
+ return
247
+ }
248
+
249
+ let scrollOffset
250
+ const carouselItemsWidth = Number(
251
+ carouselTrackRef.current?.firstElementChild?.clientWidth
252
+ )
253
+
254
+ if (itemsPerPage > 1) {
255
+ scrollOffset = index * carouselItemsWidth * itemsPerPage
256
+ } else {
257
+ scrollOffset = index * carouselItemsWidth - carouselItemsWidth * 0.125
258
+ }
259
+
260
+ carouselTrackRef.current?.scrollTo({
261
+ left: scrollOffset,
262
+ behavior: 'smooth',
263
+ })
264
+
265
+ slide(index, sliderDispatch)
266
+ }
267
+
268
+ // accessible behavior for tablist
269
+ const handleBulletsKeyDown = (event: KeyboardEvent) => {
270
+ switch (event.key) {
271
+ case 'ArrowLeft': {
272
+ isSlideCarousel && slidePrevious()
273
+ isScrollCarousel &&
274
+ onScrollPagination(sliderState.currentPage - 1, 'previous')
275
+ break
276
+ }
277
+
278
+ case 'ArrowRight': {
279
+ isSlideCarousel && slideNext()
280
+ isScrollCarousel &&
281
+ onScrollPagination(sliderState.currentPage + 1, 'next')
282
+ break
283
+ }
284
+
285
+ case 'Home': {
286
+ slide(0, sliderDispatch)
287
+ break
288
+ }
289
+
290
+ case 'End': {
291
+ slide(childrenCount - 1, sliderDispatch)
292
+ break
293
+ }
294
+
295
+ default:
296
+ }
297
+ }
298
+
299
+ return (
300
+ <section
301
+ id={id}
302
+ data-fs-carousel
303
+ className={className}
304
+ data-testid={testId}
305
+ aria-label="carousel"
306
+ aria-roledescription="carousel"
307
+ >
308
+ <div
309
+ data-fs-carousel-track-container
310
+ style={{
311
+ width: '100%',
312
+ overflow: 'hidden',
313
+ display: isScrollCarousel ? 'block' : undefined,
314
+ }}
315
+ {...handlers}
316
+ >
317
+ <ul
318
+ aria-live="polite"
319
+ ref={carouselTrackRef}
320
+ style={carouselTrackStyle}
321
+ data-fs-carousel-track
322
+ onScroll={onScrollTrack}
323
+ onTransitionEnd={onTransitionTrackEnd}
324
+ >
325
+ {slides.map((currentSlide, idx) => (
326
+ <CarouselItem
327
+ id={id}
328
+ index={idx}
329
+ key={String(idx)}
330
+ state={sliderState}
331
+ totalItems={childrenCount}
332
+ infiniteMode={infiniteMode}
333
+ isScrollCarousel={isScrollCarousel}
334
+ >
335
+ {currentSlide}
336
+ </CarouselItem>
337
+ ))}
338
+ </ul>
339
+ </div>
340
+
341
+ {showNavigationArrows && (
342
+ <div data-fs-carousel-controls>
343
+ <IconButton
344
+ data-fs-carousel-control="left"
345
+ aria-controls={id}
346
+ aria-label="previous"
347
+ icon={
348
+ navigationIcons?.left ?? (
349
+ <Icon name="ArrowLeft" width={20} height={20} weight="bold" />
350
+ )
351
+ }
352
+ onClick={() => {
353
+ isSlideCarousel && slidePrevious()
354
+ isScrollCarousel &&
355
+ onScrollPagination(sliderState.currentPage - 1, 'previous')
356
+ }}
357
+ />
358
+ <IconButton
359
+ data-fs-carousel-control="right"
360
+ aria-controls={id}
361
+ aria-label="next"
362
+ icon={
363
+ navigationIcons?.right ?? (
364
+ <Icon name="ArrowRight" width={20} height={20} weight="bold" />
365
+ )
366
+ }
367
+ onClick={() => {
368
+ isSlideCarousel && slideNext()
369
+ isScrollCarousel &&
370
+ onScrollPagination(sliderState.currentPage + 1, 'next')
371
+ }}
372
+ />
373
+ </div>
374
+ )}
375
+
376
+ {showPaginationBullets && (
377
+ <CarouselBullets
378
+ id={id}
379
+ tabIndex={0}
380
+ activeBullet={sliderState.currentPage}
381
+ totalQuantity={pagesCount}
382
+ onKeyDown={handleBulletsKeyDown}
383
+ onClick={async (_, idx) => {
384
+ isSlideCarousel &&
385
+ !sliderState.sliding &&
386
+ slide(idx, sliderDispatch)
387
+
388
+ isScrollCarousel && onScrollPagination(idx)
389
+ }}
390
+ onFocus={(event) => event.currentTarget.focus()}
391
+ ariaControlsGenerator={(idx) => `${id}-carousel-item-${idx}`}
392
+ />
393
+ )}
394
+ </section>
395
+ )
396
+ }
397
+
398
+ export default Carousel
@@ -0,0 +1,95 @@
1
+ import type { HTMLAttributes, MouseEvent } from 'react'
2
+ import React, { forwardRef, useMemo } from 'react'
3
+
4
+ import { Button } from '../..'
5
+
6
+ export interface CarouselBulletsProps
7
+ extends Omit<HTMLAttributes<HTMLDivElement>, 'onClick' | 'role'> {
8
+ /**
9
+ * ID of the current instance of the component.
10
+ */
11
+ id: string
12
+ /**
13
+ * Number of bullets that should be rendered.
14
+ */
15
+ totalQuantity: number
16
+ /**
17
+ * The currently active bullet (zero-indexed).
18
+ */
19
+ activeBullet: number
20
+ /**
21
+ * Event handler for clicks on each bullet. The handler will be called with
22
+ * the index of the bullet that received the click.
23
+ */
24
+ onClick: (e: MouseEvent, bulletIdx: number) => void
25
+ /**
26
+ * ID to find this component in testing tools (e.g.: cypress,
27
+ * testing-library, and jest).
28
+ */
29
+ testId?: string
30
+ /**
31
+ * Function that should be used to generate the aria-label attribute added
32
+ * to each bullet that is inactive. It receives the bullet index as an
33
+ * argument so that it can be interpolated in the generated string.
34
+ */
35
+ ariaLabelGenerator?: (id: string, index: number, isActive: boolean) => string
36
+ /**
37
+ * Function that should be used to generate the aria-controls attribute added
38
+ * to each bullet. It receives the bullet index as argument and should return a string.
39
+ */
40
+ ariaControlsGenerator?: (index: number) => string
41
+ }
42
+
43
+ const defaultAriaLabel = (id: string, idx: number, isActive: boolean) =>
44
+ isActive ? `Current page from ${id}` : `Go to page ${idx + 1} from ${id}`
45
+
46
+ const CarouselBullets = forwardRef<HTMLDivElement, CarouselBulletsProps>(
47
+ function Bullets(
48
+ {
49
+ id,
50
+ totalQuantity,
51
+ activeBullet,
52
+ onClick,
53
+ testId = 'fs-carousel-bullets',
54
+ ariaLabelGenerator = defaultAriaLabel,
55
+ ariaControlsGenerator,
56
+ ...otherProps
57
+ },
58
+ ref
59
+ ) {
60
+ const bulletIndexes = useMemo(
61
+ () => Array(totalQuantity).fill(0),
62
+ [totalQuantity]
63
+ )
64
+
65
+ return (
66
+ <div
67
+ ref={ref}
68
+ data-fs-carousel-bullets
69
+ data-testid={testId}
70
+ role="tablist"
71
+ {...otherProps}
72
+ >
73
+ {bulletIndexes.map((_, idx) => {
74
+ const isActive = activeBullet === idx
75
+
76
+ return (
77
+ <Button
78
+ key={`${id}-${idx}`}
79
+ role="tab"
80
+ tabIndex={-1}
81
+ data-fs-carousel-bullet
82
+ testId={`${testId}-bullet`}
83
+ onClick={(e) => onClick(e, idx)}
84
+ aria-label={ariaLabelGenerator(id, idx, isActive)}
85
+ aria-controls={ariaControlsGenerator?.(idx)}
86
+ aria-selected={isActive}
87
+ />
88
+ )
89
+ })}
90
+ </div>
91
+ )
92
+ }
93
+ )
94
+
95
+ export default CarouselBullets
@@ -0,0 +1,53 @@
1
+ import type { CSSProperties, HTMLAttributes, PropsWithChildren } from 'react'
2
+ import React from 'react'
3
+ import { SliderState, useSlideVisibility } from '../../hooks'
4
+
5
+ export interface CarouselItemProps extends HTMLAttributes<HTMLLIElement> {
6
+ index: number
7
+ totalItems: number
8
+ state: SliderState
9
+ infiniteMode: boolean
10
+ isScrollCarousel: boolean
11
+ }
12
+
13
+ function CarouselItem({
14
+ id,
15
+ index,
16
+ state,
17
+ children,
18
+ totalItems,
19
+ infiniteMode,
20
+ isScrollCarousel,
21
+ }: PropsWithChildren<CarouselItemProps>) {
22
+ const { isItemVisible, shouldRenderItem } = useSlideVisibility({
23
+ totalItems,
24
+ currentSlide: state.currentItem,
25
+ itemsPerPage: state.itemsPerPage,
26
+ })
27
+
28
+ const style =
29
+ ((!isScrollCarousel && { width: '100%' }) as CSSProperties) ||
30
+ ((isScrollCarousel && {
31
+ maxWidth: '60%',
32
+ display: 'inline-block',
33
+ }) as CSSProperties)
34
+
35
+ const shouldDisplayItem =
36
+ isScrollCarousel || shouldRenderItem(index - Number(infiniteMode))
37
+
38
+ return (
39
+ <li
40
+ style={style}
41
+ data-fs-carousel-item
42
+ aria-roledescription="slide"
43
+ id={`${id}-carousel-item-${index}`}
44
+ data-fs-carousel-item-visible={
45
+ isItemVisible(index - Number(infiniteMode)) || undefined
46
+ }
47
+ >
48
+ {shouldDisplayItem ? children : null}
49
+ </li>
50
+ )
51
+ }
52
+
53
+ export default CarouselItem
@@ -0,0 +1,8 @@
1
+ export { default } from './Carousel'
2
+ export type { CarouselProps } from './Carousel'
3
+
4
+ export { default as CarouselItem } from './CarouselItem'
5
+ export type { CarouselItemProps } from './CarouselItem'
6
+
7
+ export { default as CarouselBullets } from './CarouselBullets'
8
+ export type { CarouselBulletsProps } from './CarouselBullets'
@@ -26,6 +26,7 @@ const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
26
26
  children,
27
27
  testId = 'fs-icon-button',
28
28
  'aria-label': ariaLabel,
29
+ size = 'regular',
29
30
  variant,
30
31
  ...otherProps
31
32
  },
@@ -40,6 +41,7 @@ const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
40
41
  icon={icon}
41
42
  aria-label={ariaLabel}
42
43
  testId={testId}
44
+ size={size}
43
45
  {...otherProps}
44
46
  >
45
47
  {children}
@@ -107,7 +107,7 @@ const InputField = ({
107
107
  {shouldDisplayButton &&
108
108
  (displayClearButton || error ? (
109
109
  <IconButton
110
- data-fs-button-size="small"
110
+ size="small"
111
111
  aria-label="Clear Field"
112
112
  icon={<Icon name="XCircle" />}
113
113
  onClick={() => {
@@ -40,9 +40,11 @@ function LinkButton({
40
40
  data-testid={testId}
41
41
  {...otherProps}
42
42
  >
43
- {React.isValidElement(icon) && iconPosition === 'left' && icon}
44
- {children}
45
- {React.isValidElement(icon) && iconPosition === 'right' && icon}
43
+ <div data-fs-button-wrapper>
44
+ {React.isValidElement(icon) && iconPosition === 'left' && <span data-fs-button-icon>{icon}</span>}
45
+ {children && <span>{children}</span>}
46
+ {React.isValidElement(icon) && iconPosition === 'right' && <span data-fs-button-icon>{icon}</span>}
47
+ </div>
46
48
  </a>
47
49
  )
48
50
  }
@@ -94,6 +94,7 @@ const QuantitySelector = ({
94
94
  disabled={isLeftDisabled || disabled}
95
95
  onClick={decrease}
96
96
  testId={`${testId}-left-button`}
97
+ size="small"
97
98
  />
98
99
  <Input
99
100
  data-quantity-selector-input
@@ -111,6 +112,7 @@ const QuantitySelector = ({
111
112
  icon={<Icon name="Plus" width={16} height={16} weight="bold" />}
112
113
  onClick={increase}
113
114
  testId={`${testId}-right-button`}
115
+ size="small"
114
116
  />
115
117
  </div>
116
118
  )
@@ -20,7 +20,7 @@ const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
20
20
  ) {
21
21
  return (
22
22
  <div ref={ref} data-fs-region-bar {...otherProps}>
23
- <Button onClick={onButtonClick}>
23
+ <Button onClick={onButtonClick} iconPosition="right" icon={<Icon name="CaretRight" />}>
24
24
  <Icon name="MapPin" />
25
25
  {postalCode ? (
26
26
  <>
@@ -30,7 +30,6 @@ const RegionBar = forwardRef<HTMLDivElement, RegionBarProps>(function RegionBar(
30
30
  ) : (
31
31
  <span data-fs-region-bar-message>Set your location</span>
32
32
  )}
33
- <Icon name="CaretRight" />
34
33
  </Button>
35
34
  </div>
36
35
  )
@@ -88,10 +88,10 @@ const SearchInputField = forwardRef<
88
88
  {...otherProps}
89
89
  />
90
90
  <IconButton
91
- data-fs-icon-button="null"
92
91
  type="submit"
93
92
  aria-label="Submit Search"
94
93
  icon={buttonIcon ?? <Icon name="MagnifyingGlass" />}
94
+ size="small"
95
95
  {...buttonProps}
96
96
  />
97
97
  </form>
@@ -95,7 +95,6 @@ function ImageGallerySelector({
95
95
  data-fs-image-gallery-selector-control-button
96
96
  aria-label={navigationButtonLeftAriaLabel}
97
97
  icon={<Icon name="ArrowLeft" />}
98
- size="small"
99
98
  onClick={() =>
100
99
  moveScroll(elementsRef.current, -SCROLL_MARGIN_VALUE)
101
100
  }
@@ -105,7 +104,7 @@ function ImageGallerySelector({
105
104
  <div data-fs-image-gallery-selector-elements ref={elementsRef}>
106
105
  {images.map((image, idx) => {
107
106
  return (
108
- <InView
107
+ <InView
109
108
  key={idx}
110
109
  onChange={(inView) => inViewChange(idx, inView)}>
111
110
  <Button
@@ -134,7 +133,6 @@ function ImageGallerySelector({
134
133
  data-fs-image-gallery-selector-control-button
135
134
  aria-label={navigationButtonRightAriaLabel}
136
135
  icon={<Icon name="ArrowLeft" />}
137
- size="small"
138
136
  onClick={() =>
139
137
  moveScroll(elementsRef.current, +SCROLL_MARGIN_VALUE)
140
138
  }