@lumx/react 2.2.5 → 2.2.8

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 (73) hide show
  1. package/esm/_internal/AutocompleteMultiple.js.map +1 -1
  2. package/esm/_internal/DatePickerField.js.map +1 -1
  3. package/esm/_internal/Icon2.js +5 -2
  4. package/esm/_internal/Icon2.js.map +1 -1
  5. package/esm/_internal/Lightbox2.js +6 -4
  6. package/esm/_internal/Lightbox2.js.map +1 -1
  7. package/esm/_internal/List2.js.map +1 -1
  8. package/esm/_internal/Notification2.js +2 -1
  9. package/esm/_internal/Notification2.js.map +1 -1
  10. package/esm/_internal/SelectMultiple.js.map +1 -1
  11. package/esm/_internal/SlideshowControls.js +234 -94
  12. package/esm/_internal/SlideshowControls.js.map +1 -1
  13. package/esm/_internal/Thumbnail2.js +1 -1
  14. package/esm/_internal/Thumbnail2.js.map +1 -1
  15. package/esm/_internal/Tooltip2.js +110 -113
  16. package/esm/_internal/Tooltip2.js.map +1 -1
  17. package/esm/_internal/autocomplete.js +0 -1
  18. package/esm/_internal/autocomplete.js.map +1 -1
  19. package/esm/_internal/button.js +0 -1
  20. package/esm/_internal/button.js.map +1 -1
  21. package/esm/_internal/constants.js.map +1 -1
  22. package/esm/_internal/date-picker.js +0 -1
  23. package/esm/_internal/date-picker.js.map +1 -1
  24. package/esm/_internal/expansion-panel.js +0 -1
  25. package/esm/_internal/expansion-panel.js.map +1 -1
  26. package/esm/_internal/getRootClassName.js +19 -1
  27. package/esm/_internal/getRootClassName.js.map +1 -1
  28. package/esm/_internal/lightbox.js +0 -1
  29. package/esm/_internal/lightbox.js.map +1 -1
  30. package/esm/_internal/mergeRefs.js.map +1 -1
  31. package/esm/_internal/select.js +0 -1
  32. package/esm/_internal/select.js.map +1 -1
  33. package/esm/_internal/side-navigation.js +0 -1
  34. package/esm/_internal/side-navigation.js.map +1 -1
  35. package/esm/_internal/slideshow.js +1 -1
  36. package/esm/_internal/text-field.js +0 -1
  37. package/esm/_internal/text-field.js.map +1 -1
  38. package/esm/_internal/tooltip.js +0 -1
  39. package/esm/_internal/tooltip.js.map +1 -1
  40. package/esm/_internal/useRovingTabIndex.js.map +1 -1
  41. package/esm/index.js +1 -1
  42. package/package.json +4 -4
  43. package/src/components/alert-dialog/AlertDialog.test.tsx +1 -0
  44. package/src/components/autocomplete/AutocompleteMultiple.tsx +3 -1
  45. package/src/components/button/__snapshots__/IconButton.test.tsx.snap +0 -5
  46. package/src/components/icon/Icon.tsx +6 -2
  47. package/src/components/image-block/ImageBlock.stories.tsx +1 -2
  48. package/src/components/lightbox/Lightbox.stories.tsx +1 -0
  49. package/src/components/lightbox/Lightbox.tsx +5 -3
  50. package/src/components/notification/Notification.tsx +1 -0
  51. package/src/components/select/SelectMultiple.tsx +0 -1
  52. package/src/components/slideshow/Slideshow.stories.tsx +1 -1
  53. package/src/components/slideshow/Slideshow.tsx +76 -112
  54. package/src/components/slideshow/SlideshowControls.stories.tsx +18 -12
  55. package/src/components/slideshow/SlideshowControls.tsx +11 -7
  56. package/src/components/slideshow/SlideshowItem.tsx +4 -1
  57. package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +52 -17
  58. package/src/components/tabs/state.ts +0 -1
  59. package/src/components/thumbnail/Thumbnail.stories.tsx +25 -1
  60. package/src/components/thumbnail/Thumbnail.test.tsx +9 -1
  61. package/src/components/thumbnail/Thumbnail.tsx +3 -0
  62. package/src/components/thumbnail/__snapshots__/Thumbnail.test.tsx.snap +26 -0
  63. package/src/components/tooltip/Tooltip.tsx +1 -2
  64. package/src/components/tooltip/useTooltipOpen.tsx +90 -91
  65. package/src/constants.ts +7 -1
  66. package/src/hooks/useFocusWithin.ts +33 -0
  67. package/src/hooks/useSlideshowControls.ts +213 -0
  68. package/src/utils/browserDoesNotSupportHover.test.js +24 -0
  69. package/src/utils/browserDoesNotSupportHover.ts +2 -0
  70. package/src/utils/index.tsx +0 -2
  71. package/src/utils/mergeRefs.ts +1 -1
  72. package/types.d.ts +62 -8
  73. package/src/utils/htmlDecode.ts +0 -13
@@ -1,11 +1,10 @@
1
- import React, { CSSProperties, forwardRef, useCallback, useEffect, useState } from 'react';
1
+ import React, { CSSProperties, forwardRef } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
5
5
  import { SlideshowControls, SlideshowControlsProps, Theme } from '@lumx/react';
6
-
7
- import { AUTOPLAY_DEFAULT_INTERVAL, FULL_WIDTH_PERCENT } from '@lumx/react/components/slideshow/constants';
8
- import { useInterval } from '@lumx/react/hooks/useInterval';
6
+ import { DEFAULT_OPTIONS } from '@lumx/react/hooks/useSlideshowControls';
7
+ import { FULL_WIDTH_PERCENT } from '@lumx/react/components/slideshow/constants';
9
8
  import { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';
10
9
  import { mergeRefs } from '@lumx/react/utils/mergeRefs';
11
10
 
@@ -39,6 +38,10 @@ export interface SlideshowProps extends GenericProps {
39
38
  theme?: Theme;
40
39
  /** Callback when slide changes */
41
40
  onChange?(index: number): void;
41
+ /** slideshow HTML id attribute */
42
+ id?: string;
43
+ /** slides wrapper HTML id attribute */
44
+ slidesId?: string;
42
45
  }
43
46
 
44
47
  /**
@@ -55,9 +58,7 @@ const CLASSNAME = getRootClassName(COMPONENT_NAME);
55
58
  * Component default props.
56
59
  */
57
60
  const DEFAULT_PROPS: Partial<SlideshowProps> = {
58
- activeIndex: 0,
59
- groupBy: 1,
60
- interval: AUTOPLAY_DEFAULT_INTERVAL,
61
+ ...DEFAULT_OPTIONS,
61
62
  theme: Theme.light,
62
63
  };
63
64
 
@@ -80,122 +81,76 @@ export const Slideshow: Comp<SlideshowProps, HTMLDivElement> = forwardRef((props
80
81
  onChange,
81
82
  slideshowControlsProps,
82
83
  theme,
84
+ id,
85
+ slidesId,
83
86
  ...forwardedProps
84
87
  } = props;
85
- const [currentIndex, setCurrentIndex] = useState(activeIndex as number);
86
- // Use state instead of a ref to make the slideshow controls update directly when the element is set.
87
- const [element, setElement] = useState<HTMLDivElement>();
88
-
89
88
  // Number of slideshow items.
90
89
  const itemsCount = React.Children.count(children);
91
- // Number of slides when using groupBy prop.
92
- const slidesCount = Math.ceil(itemsCount / Math.min(groupBy as number, itemsCount));
93
- // Inline style of wrapper element.
94
- const wrapperStyle: CSSProperties = { transform: `translateX(-${FULL_WIDTH_PERCENT * currentIndex}%)` };
95
-
96
- // Change current index to display next slide.
97
- const goToNextSlide = useCallback(
98
- (loopback = true) => {
99
- setCurrentIndex((index) => {
100
- if (loopback && index === slidesCount - 1) {
101
- // Loopback to the start.
102
- return 0;
103
- }
104
- if (index < slidesCount - 1) {
105
- // Next slide.
106
- return index + 1;
107
- }
108
- return index;
109
- });
110
- },
111
- [slidesCount, setCurrentIndex],
112
- );
113
-
114
- // Change current index to display previous slide.
115
- const goToPreviousSlide = useCallback(
116
- (loopback = true) => {
117
- setCurrentIndex((index) => {
118
- if (loopback && index === 0) {
119
- // Loopback to the end.
120
- return slidesCount - 1;
121
- }
122
- if (index > 0) {
123
- // Previous slide.
124
- return index - 1;
125
- }
126
- return index;
127
- });
128
- },
129
- [slidesCount, setCurrentIndex],
130
- );
131
-
132
- // Auto play
133
- const [isAutoPlaying, setIsAutoPlaying] = useState(Boolean(autoPlay));
134
- // Start
135
- useInterval(goToNextSlide, isAutoPlaying && slidesCount > 1 ? (interval as number) : null);
136
-
137
- // Reset current index if it become invalid.
138
- useEffect(() => {
139
- if (currentIndex > slidesCount - 1) {
140
- setCurrentIndex(DEFAULT_PROPS.activeIndex as number);
141
- }
142
- }, [currentIndex, slidesCount]);
143
-
144
- // Handle click on a bullet to go to a specific slide.
145
- const handleControlGotToSlide = useCallback(
146
- (index: number) => {
147
- setIsAutoPlaying(false);
148
-
149
- if (index >= 0 && index < slidesCount) {
150
- setCurrentIndex(index);
151
- }
152
- },
153
- [slidesCount, setCurrentIndex],
154
- );
155
90
 
156
- // Handle click or keyboard event to go to next slide.
157
- const handleControlNextSlide = useCallback(
158
- (loopback = true) => {
159
- setIsAutoPlaying(false);
160
- goToNextSlide(loopback);
161
- },
162
- [goToNextSlide],
163
- );
164
-
165
- // Handle click or keyboard event to go to previous slide.
166
- const handleControlPreviousSlide = useCallback(
167
- (loopback = true) => {
168
- setIsAutoPlaying(false);
169
- goToPreviousSlide(loopback);
170
- },
171
- [goToPreviousSlide],
172
- );
173
-
174
- // If the activeIndex props changes, update the current slide
175
- useEffect(() => {
176
- setCurrentIndex(activeIndex as number);
177
- }, [activeIndex]);
91
+ const {
92
+ activeIndex: currentIndex,
93
+ slideshowId,
94
+ setSlideshow,
95
+ isAutoPlaying,
96
+ slideshowSlidesId,
97
+ setIsAutoPlaying,
98
+ startIndexVisible,
99
+ endIndexVisible,
100
+ slidesCount,
101
+ onNextClick,
102
+ onPaginationClick,
103
+ onPreviousClick,
104
+ slideshow,
105
+ } = SlideshowControls.useSlideshowControls({
106
+ activeIndex,
107
+ defaultActiveIndex: DEFAULT_PROPS.activeIndex as number,
108
+ autoPlay: Boolean(autoPlay),
109
+ itemsCount,
110
+ groupBy,
111
+ id,
112
+ interval,
113
+ onChange,
114
+ slidesId,
115
+ });
178
116
 
179
- // If the slide changes, with autoplay for example, trigger "onChange"
180
- useEffect(() => {
181
- if (!onChange) return;
182
- onChange(currentIndex);
183
- }, [currentIndex, onChange]);
117
+ // Inline style of wrapper element.
118
+ const wrapperStyle: CSSProperties = { transform: `translateX(-${FULL_WIDTH_PERCENT * currentIndex}%)` };
184
119
 
185
120
  /* eslint-disable jsx-a11y/no-noninteractive-tabindex */
186
121
  return (
187
- <div
188
- ref={mergeRefs(ref, setElement)}
122
+ <section
123
+ id={slideshowId}
124
+ ref={mergeRefs(ref, setSlideshow)}
189
125
  {...forwardedProps}
190
126
  className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, theme }), {
191
127
  [`${CLASSNAME}--fill-height`]: fillHeight,
192
128
  [`${CLASSNAME}--group-by-${groupBy}`]: Boolean(groupBy),
193
129
  })}
194
- tabIndex={0}
130
+ aria-roledescription="carousel"
131
+ aria-live={isAutoPlaying ? 'off' : 'polite'}
195
132
  >
196
- <div className={`${CLASSNAME}__slides`}>
133
+ <div
134
+ id={slideshowSlidesId}
135
+ className={`${CLASSNAME}__slides`}
136
+ onMouseEnter={() => setIsAutoPlaying(false)}
137
+ onMouseLeave={() => setIsAutoPlaying(Boolean(autoPlay))}
138
+ >
197
139
  <div className={`${CLASSNAME}__wrapper`} style={wrapperStyle}>
198
- {children}
140
+ {React.Children.map(children, (child: React.ReactNode, index: number) => {
141
+ if (React.isValidElement(child)) {
142
+ const isCurrentlyVisible = index >= startIndexVisible && index <= endIndexVisible;
143
+
144
+ return React.cloneElement(child, {
145
+ style: !isCurrentlyVisible
146
+ ? { visibility: 'hidden', ...(child.props.style || {}) }
147
+ : child.props.style || {},
148
+ 'aria-hidden': !isCurrentlyVisible,
149
+ });
150
+ }
151
+
152
+ return null;
153
+ })}
199
154
  </div>
200
155
  </div>
201
156
 
@@ -204,18 +159,27 @@ export const Slideshow: Comp<SlideshowProps, HTMLDivElement> = forwardRef((props
204
159
  <SlideshowControls
205
160
  {...slideshowControlsProps}
206
161
  activeIndex={currentIndex}
207
- onPaginationClick={handleControlGotToSlide}
208
- onNextClick={handleControlNextSlide}
209
- onPreviousClick={handleControlPreviousSlide}
162
+ onPaginationClick={onPaginationClick}
163
+ onNextClick={onNextClick}
164
+ onPreviousClick={onPreviousClick}
210
165
  slidesCount={slidesCount}
211
- parentRef={element}
166
+ parentRef={slideshow}
212
167
  theme={theme}
168
+ nextButtonProps={{
169
+ 'aria-controls': slideshowSlidesId,
170
+ ...slideshowControlsProps.nextButtonProps,
171
+ }}
172
+ previousButtonProps={{
173
+ 'aria-controls': slideshowSlidesId,
174
+ ...slideshowControlsProps.previousButtonProps,
175
+ }}
213
176
  />
214
177
  </div>
215
178
  )}
216
- </div>
179
+ </section>
217
180
  );
218
181
  });
182
+
219
183
  Slideshow.displayName = COMPONENT_NAME;
220
184
  Slideshow.className = CLASSNAME;
221
185
  Slideshow.defaultProps = DEFAULT_PROPS;
@@ -6,13 +6,15 @@ import { thumbnailsKnob } from '@lumx/react/stories/knobs/thumbnailsKnob';
6
6
  export default { title: 'LumX components/slideshow/Slideshow controls' };
7
7
 
8
8
  export const Simple = () => {
9
- const slidesCount = 9;
10
- const [activeIndex, setActiveIndex] = React.useState(0);
11
- const maxIndex = slidesCount - 1;
12
-
13
- const onNextClick = () => setActiveIndex(activeIndex === maxIndex ? 0 : activeIndex + 1);
14
- const onPreviousClick = () => setActiveIndex(activeIndex === 0 ? maxIndex : activeIndex - 1);
15
- const onPaginationClick = (index: number) => setActiveIndex(index);
9
+ const {
10
+ activeIndex,
11
+ slidesCount,
12
+ onNextClick,
13
+ onPreviousClick,
14
+ onPaginationClick,
15
+ } = SlideshowControls.useSlideshowControls({
16
+ itemsCount: 9,
17
+ });
16
18
 
17
19
  return (
18
20
  <SlideshowControls
@@ -29,15 +31,19 @@ export const Simple = () => {
29
31
 
30
32
  export const ControllingSlideshow = ({ theme }: any) => {
31
33
  const items = thumbnailsKnob(6);
32
- const [activeIndex, setActiveIndex] = React.useState(0);
33
- const maxIndex = items.length - 1;
34
34
  const slideshowStyle = {
35
35
  width: '50%',
36
36
  };
37
37
 
38
- const onNextClick = () => setActiveIndex(activeIndex === maxIndex ? 0 : activeIndex + 1);
39
- const onPreviousClick = () => setActiveIndex(activeIndex === 0 ? maxIndex : activeIndex - 1);
40
- const onPaginationClick = (index: number) => setActiveIndex(index);
38
+ const {
39
+ activeIndex,
40
+ setActiveIndex,
41
+ onNextClick,
42
+ onPreviousClick,
43
+ onPaginationClick,
44
+ } = SlideshowControls.useSlideshowControls({
45
+ itemsCount: 6,
46
+ });
41
47
 
42
48
  return (
43
49
  <div style={{ height: 400, width: 500, position: 'relative' }}>
@@ -7,6 +7,7 @@ import { mdiChevronLeft, mdiChevronRight } from '@lumx/icons';
7
7
  import { Emphasis, IconButton, IconButtonProps, Theme } from '@lumx/react';
8
8
  import { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';
9
9
  import { WINDOW } from '@lumx/react/constants';
10
+ import { useSlideshowControls, DEFAULT_OPTIONS } from '@lumx/react/hooks/useSlideshowControls';
10
11
 
11
12
  import { useSwipeNavigate } from './useSwipeNavigate';
12
13
  import { useKeyNavigate } from './useKeyNavigate';
@@ -64,7 +65,7 @@ const DEFAULT_PROPS: Partial<SlideshowControlsProps> = {
64
65
  * @param ref Component ref.
65
66
  * @return React element.
66
67
  */
67
- export const SlideshowControls: Comp<SlideshowControlsProps, HTMLDivElement> = forwardRef((props, ref) => {
68
+ const InternalSlideshowControls: Comp<SlideshowControlsProps, HTMLDivElement> = forwardRef((props, ref) => {
68
69
  const {
69
70
  activeIndex,
70
71
  className,
@@ -116,7 +117,6 @@ export const SlideshowControls: Comp<SlideshowControlsProps, HTMLDivElement> = f
116
117
  color={theme === Theme.dark ? 'light' : 'dark'}
117
118
  emphasis={Emphasis.low}
118
119
  onClick={onPreviousClick}
119
- tabIndex={-1}
120
120
  />
121
121
  <div className={`${CLASSNAME}__pagination`}>
122
122
  <div className={`${CLASSNAME}__pagination-items`} style={wrapperStyle}>
@@ -143,7 +143,6 @@ export const SlideshowControls: Comp<SlideshowControlsProps, HTMLDivElement> = f
143
143
  key={index}
144
144
  type="button"
145
145
  onClick={() => onPaginationClick?.(index)}
146
- tabIndex={-1}
147
146
  />
148
147
  );
149
148
  }),
@@ -158,11 +157,16 @@ export const SlideshowControls: Comp<SlideshowControlsProps, HTMLDivElement> = f
158
157
  color={theme === Theme.dark ? 'light' : 'dark'}
159
158
  emphasis={Emphasis.low}
160
159
  onClick={onNextClick}
161
- tabIndex={-1}
162
160
  />
163
161
  </div>
164
162
  );
165
163
  });
166
- SlideshowControls.displayName = COMPONENT_NAME;
167
- SlideshowControls.className = CLASSNAME;
168
- SlideshowControls.defaultProps = DEFAULT_PROPS;
164
+
165
+ InternalSlideshowControls.displayName = COMPONENT_NAME;
166
+ InternalSlideshowControls.className = CLASSNAME;
167
+ InternalSlideshowControls.defaultProps = DEFAULT_PROPS;
168
+
169
+ export const SlideshowControls = Object.assign(InternalSlideshowControls, {
170
+ useSlideshowControls,
171
+ useSlideshowControlsDefaultOptions: DEFAULT_OPTIONS,
172
+ });
@@ -32,17 +32,20 @@ export const SlideshowItem: Comp<SlideshowItemProps, HTMLDivElement> = forwardRe
32
32
  return (
33
33
  <div
34
34
  ref={ref}
35
- {...forwardedProps}
36
35
  className={classNames(
37
36
  className,
38
37
  handleBasicClasses({
39
38
  prefix: CLASSNAME,
40
39
  }),
41
40
  )}
41
+ aria-roledescription="slide"
42
+ role="group"
43
+ {...forwardedProps}
42
44
  >
43
45
  {children}
44
46
  </div>
45
47
  );
46
48
  });
49
+
47
50
  SlideshowItem.displayName = COMPONENT_NAME;
48
51
  SlideshowItem.className = CLASSNAME;
@@ -2,17 +2,22 @@
2
2
 
3
3
  exports[`<Slideshow> Snapshots and structure should render story 'Simple' 1`] = `
4
4
  Array [
5
- <div
5
+ <section
6
+ aria-live="polite"
7
+ aria-roledescription="carousel"
6
8
  className="lumx-slideshow lumx-slideshow--theme-light lumx-slideshow--group-by-1"
9
+ id="slideshow1"
7
10
  style={
8
11
  Object {
9
12
  "width": "50%",
10
13
  }
11
14
  }
12
- tabIndex={0}
13
15
  >
14
16
  <div
15
17
  className="lumx-slideshow__slides"
18
+ id="slideshow-slides2"
19
+ onMouseEnter={[Function]}
20
+ onMouseLeave={[Function]}
16
21
  >
17
22
  <div
18
23
  className="lumx-slideshow__wrapper"
@@ -23,7 +28,9 @@ Array [
23
28
  }
24
29
  >
25
30
  <SlideshowItem
26
- key="/demo-assets/landscape1.jpg-0"
31
+ aria-hidden={false}
32
+ key=".$/demo-assets/landscape1.jpg-0"
33
+ style={Object {}}
27
34
  >
28
35
  <ImageBlock
29
36
  align="left"
@@ -34,12 +41,15 @@ Array [
34
41
  thumbnailProps={
35
42
  Object {
36
43
  "aspectRatio": "horizontal",
44
+ "loading": "eager",
37
45
  }
38
46
  }
39
47
  />
40
48
  </SlideshowItem>
41
49
  <SlideshowItem
42
- key="/demo-assets/landscape1-s200.jpg-1"
50
+ aria-hidden={false}
51
+ key=".$/demo-assets/landscape1-s200.jpg-1"
52
+ style={Object {}}
43
53
  >
44
54
  <ImageBlock
45
55
  align="left"
@@ -50,12 +60,19 @@ Array [
50
60
  thumbnailProps={
51
61
  Object {
52
62
  "aspectRatio": "horizontal",
63
+ "loading": "eager",
53
64
  }
54
65
  }
55
66
  />
56
67
  </SlideshowItem>
57
68
  <SlideshowItem
58
- key="/demo-assets/landscape2.jpg-2"
69
+ aria-hidden={true}
70
+ key=".$/demo-assets/landscape2.jpg-2"
71
+ style={
72
+ Object {
73
+ "visibility": "hidden",
74
+ }
75
+ }
59
76
  >
60
77
  <ImageBlock
61
78
  align="left"
@@ -66,12 +83,19 @@ Array [
66
83
  thumbnailProps={
67
84
  Object {
68
85
  "aspectRatio": "horizontal",
86
+ "loading": "eager",
69
87
  }
70
88
  }
71
89
  />
72
90
  </SlideshowItem>
73
91
  <SlideshowItem
74
- key="/demo-assets/landscape3.jpg-3"
92
+ aria-hidden={true}
93
+ key=".$/demo-assets/landscape3.jpg-3"
94
+ style={
95
+ Object {
96
+ "visibility": "hidden",
97
+ }
98
+ }
75
99
  >
76
100
  <ImageBlock
77
101
  align="left"
@@ -82,12 +106,19 @@ Array [
82
106
  thumbnailProps={
83
107
  Object {
84
108
  "aspectRatio": "horizontal",
109
+ "loading": "eager",
85
110
  }
86
111
  }
87
112
  />
88
113
  </SlideshowItem>
89
114
  <SlideshowItem
90
- key="/demo-assets/portrait1.jpg-4"
115
+ aria-hidden={true}
116
+ key=".$/demo-assets/portrait1.jpg-4"
117
+ style={
118
+ Object {
119
+ "visibility": "hidden",
120
+ }
121
+ }
91
122
  >
92
123
  <ImageBlock
93
124
  align="left"
@@ -98,12 +129,19 @@ Array [
98
129
  thumbnailProps={
99
130
  Object {
100
131
  "aspectRatio": "horizontal",
132
+ "loading": "eager",
101
133
  }
102
134
  }
103
135
  />
104
136
  </SlideshowItem>
105
137
  <SlideshowItem
106
- key="/demo-assets/portrait1-s200.jpg-5"
138
+ aria-hidden={true}
139
+ key=".$/demo-assets/portrait1-s200.jpg-5"
140
+ style={
141
+ Object {
142
+ "visibility": "hidden",
143
+ }
144
+ }
107
145
  >
108
146
  <ImageBlock
109
147
  align="left"
@@ -114,6 +152,7 @@ Array [
114
152
  thumbnailProps={
115
153
  Object {
116
154
  "aspectRatio": "horizontal",
155
+ "loading": "eager",
117
156
  }
118
157
  }
119
158
  />
@@ -127,6 +166,7 @@ Array [
127
166
  activeIndex={0}
128
167
  nextButtonProps={
129
168
  Object {
169
+ "aria-controls": "slideshow-slides2",
130
170
  "label": "Next",
131
171
  }
132
172
  }
@@ -135,6 +175,7 @@ Array [
135
175
  onPreviousClick={[Function]}
136
176
  previousButtonProps={
137
177
  Object {
178
+ "aria-controls": "slideshow-slides2",
138
179
  "label": "Previous",
139
180
  }
140
181
  }
@@ -142,11 +183,12 @@ Array [
142
183
  theme="light"
143
184
  />
144
185
  </div>
145
- </div>,
186
+ </section>,
146
187
  <div
147
188
  className="lumx-slideshow-controls lumx-slideshow-controls--theme-light lumx-slideshow-controls--has-infinite-pagination"
148
189
  >
149
190
  <IconButton
191
+ aria-controls="slideshow-slides4"
150
192
  className="lumx-slideshow-controls__navigation"
151
193
  color="dark"
152
194
  emphasis="low"
@@ -154,7 +196,6 @@ Array [
154
196
  label="Previous"
155
197
  onClick={[Function]}
156
198
  size="m"
157
- tabIndex={-1}
158
199
  theme="light"
159
200
  />
160
201
  <div
@@ -172,47 +213,42 @@ Array [
172
213
  className="lumx-slideshow-controls__pagination-item lumx-slideshow-controls__pagination-item--is-active"
173
214
  key="0"
174
215
  onClick={[Function]}
175
- tabIndex={-1}
176
216
  type="button"
177
217
  />
178
218
  <button
179
219
  className="lumx-slideshow-controls__pagination-item"
180
220
  key="1"
181
221
  onClick={[Function]}
182
- tabIndex={-1}
183
222
  type="button"
184
223
  />
185
224
  <button
186
225
  className="lumx-slideshow-controls__pagination-item"
187
226
  key="2"
188
227
  onClick={[Function]}
189
- tabIndex={-1}
190
228
  type="button"
191
229
  />
192
230
  <button
193
231
  className="lumx-slideshow-controls__pagination-item"
194
232
  key="3"
195
233
  onClick={[Function]}
196
- tabIndex={-1}
197
234
  type="button"
198
235
  />
199
236
  <button
200
237
  className="lumx-slideshow-controls__pagination-item lumx-slideshow-controls__pagination-item--is-on-edge"
201
238
  key="4"
202
239
  onClick={[Function]}
203
- tabIndex={-1}
204
240
  type="button"
205
241
  />
206
242
  <button
207
243
  className="lumx-slideshow-controls__pagination-item lumx-slideshow-controls__pagination-item--is-out-range"
208
244
  key="5"
209
245
  onClick={[Function]}
210
- tabIndex={-1}
211
246
  type="button"
212
247
  />
213
248
  </div>
214
249
  </div>
215
250
  <IconButton
251
+ aria-controls="slideshow-slides4"
216
252
  className="lumx-slideshow-controls__navigation"
217
253
  color="dark"
218
254
  emphasis="low"
@@ -220,7 +256,6 @@ Array [
220
256
  label="Next"
221
257
  onClick={[Function]}
222
258
  size="m"
223
- tabIndex={-1}
224
259
  theme="light"
225
260
  />
226
261
  </div>,
@@ -1,5 +1,4 @@
1
1
  import { Dispatch, createContext, useCallback, useContext, useEffect, useMemo } from 'react';
2
- import { clamp } from '@lumx/react/utils/clamp';
3
2
  import { uid } from 'uid';
4
3
 
5
4
  type TabType = 'tab' | 'tabPanel';
@@ -18,7 +18,6 @@ import { enumKnob } from '@lumx/react/stories/knobs/enumKnob';
18
18
  import { focusKnob } from '@lumx/react/stories/knobs/focusKnob';
19
19
  import { sizeKnob } from '@lumx/react/stories/knobs/sizeKnob';
20
20
  import { action } from '@storybook/addon-actions';
21
- import classNames from 'classnames';
22
21
  import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
23
22
 
24
23
  export default { title: 'LumX components/thumbnail/Thumbnail' };
@@ -84,6 +83,31 @@ export const WithBadge = () => {
84
83
  );
85
84
  };
86
85
 
86
+ export const WithCustomImageClassName = () => {
87
+ const thumbnailSize = sizeKnob('Size', Size.l);
88
+ const variant = select('Variant', ThumbnailVariant, ThumbnailVariant.rounded);
89
+ const badgeColor = select('Badge color', ColorPalette, ColorPalette.primary);
90
+ const activateFallback = boolean('Activate fallback', false);
91
+ const image = imageKnob();
92
+ return (
93
+ <Thumbnail
94
+ alt="Image alt text"
95
+ image={activateFallback ? '' : image}
96
+ variant={variant}
97
+ aspectRatio={AspectRatio.square}
98
+ size={thumbnailSize}
99
+ badge={
100
+ <Badge color={badgeColor}>
101
+ <Icon icon={mdiAbTesting} />
102
+ </Badge>
103
+ }
104
+ imgProps={{
105
+ className: 'custom-image-class-name',
106
+ }}
107
+ />
108
+ );
109
+ };
110
+
87
111
  export const FocusPoint = () => {
88
112
  const focusPoint = { x: focusKnob('Focus X ', -0.2), y: focusKnob('Focus Y', -0.3) };
89
113
  const aspectRatio = enumKnob('Aspect ratio', [undefined, ...Object.values(AspectRatio)], AspectRatio.wide);
@@ -5,7 +5,14 @@ import 'jest-enzyme';
5
5
  import { commonTestsSuite, itShouldRenderStories } from '@lumx/react/testing/utils';
6
6
 
7
7
  import { Thumbnail, ThumbnailProps } from './Thumbnail';
8
- import { Clickable, ClickableCustomLink, ClickableLink, Default, WithBadge } from './Thumbnail.stories';
8
+ import {
9
+ Clickable,
10
+ ClickableCustomLink,
11
+ ClickableLink,
12
+ Default,
13
+ WithBadge,
14
+ WithCustomImageClassName,
15
+ } from './Thumbnail.stories';
9
16
 
10
17
  const CLASSNAME = Thumbnail.className as string;
11
18
 
@@ -28,6 +35,7 @@ describe(`<${Thumbnail.displayName}>`, () => {
28
35
  ClickableLink,
29
36
  ClickableCustomLink,
30
37
  WithBadge,
38
+ WithCustomImageClassName,
31
39
  },
32
40
  Thumbnail,
33
41
  );