@lumx/react 2.1.9 → 2.2.1

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 (68) hide show
  1. package/esm/_internal/Avatar2.js +7 -2
  2. package/esm/_internal/Avatar2.js.map +1 -1
  3. package/esm/_internal/Slider2.js +21 -2
  4. package/esm/_internal/Slider2.js.map +1 -1
  5. package/esm/_internal/Thumbnail2.js +181 -782
  6. package/esm/_internal/Thumbnail2.js.map +1 -1
  7. package/esm/_internal/Tooltip2.js +0 -5
  8. package/esm/_internal/Tooltip2.js.map +1 -1
  9. package/esm/_internal/UserBlock.js +41 -17
  10. package/esm/_internal/UserBlock.js.map +1 -1
  11. package/esm/_internal/avatar.js +0 -3
  12. package/esm/_internal/avatar.js.map +1 -1
  13. package/esm/_internal/comment-block.js +0 -3
  14. package/esm/_internal/comment-block.js.map +1 -1
  15. package/esm/_internal/image-block.js +0 -3
  16. package/esm/_internal/image-block.js.map +1 -1
  17. package/esm/_internal/link-preview.js +0 -3
  18. package/esm/_internal/link-preview.js.map +1 -1
  19. package/esm/_internal/mdi.js +2 -2
  20. package/esm/_internal/mdi.js.map +1 -1
  21. package/esm/_internal/mosaic.js +0 -3
  22. package/esm/_internal/mosaic.js.map +1 -1
  23. package/esm/_internal/post-block.js +0 -3
  24. package/esm/_internal/post-block.js.map +1 -1
  25. package/esm/_internal/slider.js +1 -2
  26. package/esm/_internal/slider.js.map +1 -1
  27. package/esm/_internal/thumbnail.js +1 -4
  28. package/esm/_internal/thumbnail.js.map +1 -1
  29. package/esm/_internal/types.js +1 -0
  30. package/esm/_internal/types.js.map +1 -1
  31. package/esm/_internal/user-block.js +2 -3
  32. package/esm/_internal/user-block.js.map +1 -1
  33. package/esm/index.js +2 -4
  34. package/esm/index.js.map +1 -1
  35. package/package.json +4 -4
  36. package/src/components/avatar/Avatar.stories.tsx +30 -53
  37. package/src/components/avatar/Avatar.tsx +9 -0
  38. package/src/components/avatar/__snapshots__/Avatar.test.tsx.snap +220 -357
  39. package/src/components/image-block/__snapshots__/ImageBlock.test.tsx.snap +1 -1
  40. package/src/components/mosaic/__snapshots__/Mosaic.test.tsx.snap +30 -30
  41. package/src/components/post-block/__snapshots__/PostBlock.test.tsx.snap +1 -1
  42. package/src/components/slideshow/__snapshots__/Slideshow.test.tsx.snap +10 -10
  43. package/src/components/table/__snapshots__/Table.test.tsx.snap +3 -3
  44. package/src/components/thumbnail/Thumbnail.stories.tsx +428 -52
  45. package/src/components/thumbnail/Thumbnail.test.tsx +8 -2
  46. package/src/components/thumbnail/Thumbnail.tsx +84 -47
  47. package/src/components/thumbnail/__snapshots__/Thumbnail.test.tsx.snap +28 -81
  48. package/src/components/thumbnail/index.ts +1 -0
  49. package/src/components/thumbnail/useFocusPointStyle.tsx +89 -0
  50. package/src/components/thumbnail/useImageLoad.ts +24 -23
  51. package/src/components/tooltip/Tooltip.stories.tsx +7 -4
  52. package/src/components/tooltip/useInjectTooltipRef.tsx +1 -3
  53. package/src/components/user-block/UserBlock.stories.tsx +65 -105
  54. package/src/components/user-block/UserBlock.test.tsx +6 -0
  55. package/src/components/user-block/UserBlock.tsx +50 -25
  56. package/src/components/user-block/__snapshots__/UserBlock.test.tsx.snap +113 -144
  57. package/src/stories/generated/Badge/Demos.stories.tsx +1 -0
  58. package/src/stories/generated/Flag/Demos.stories.tsx +6 -0
  59. package/src/stories/generated/List/Demos.stories.tsx +2 -0
  60. package/src/stories/generated/Thumbnail/Demos.stories.tsx +1 -0
  61. package/src/stories/knobs/focusKnob.ts +1 -1
  62. package/src/stories/knobs/image.ts +35 -3
  63. package/src/stories/utils/CustomLink.tsx +7 -0
  64. package/types.d.ts +21 -4
  65. package/esm/_internal/clamp.js +0 -22
  66. package/esm/_internal/clamp.js.map +0 -1
  67. package/src/components/thumbnail/useClickable.ts +0 -26
  68. package/src/components/thumbnail/useFocusPoint.ts +0 -154
@@ -12,30 +12,59 @@ import {
12
12
  Thumbnail,
13
13
  ThumbnailVariant,
14
14
  } from '@lumx/react';
15
- import { imageKnob, IMAGES } from '@lumx/react/stories/knobs/image';
16
- import { htmlDecode } from '@lumx/react/utils/htmlDecode';
15
+ import { IMAGE_SIZES, imageKnob, IMAGES } from '@lumx/react/stories/knobs/image';
17
16
  import { boolean, select, text } from '@storybook/addon-knobs';
18
17
  import { enumKnob } from '@lumx/react/stories/knobs/enumKnob';
19
18
  import { focusKnob } from '@lumx/react/stories/knobs/focusKnob';
20
19
  import { sizeKnob } from '@lumx/react/stories/knobs/sizeKnob';
20
+ import { action } from '@storybook/addon-actions';
21
+ import classNames from 'classnames';
22
+ import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
21
23
 
22
24
  export default { title: 'LumX components/thumbnail/Thumbnail' };
23
25
 
24
- export const Default = () => <Thumbnail alt="Image alt text" image={imageKnob()} size={Size.xxl} />;
25
-
26
- export const Clickable = () => <Thumbnail alt="Click me" image={imageKnob()} size={Size.xxl} onClick={console.log} />;
27
-
28
- export const DefaultFallback = () => <Thumbnail alt="foo" image="foo" />;
26
+ const Resizable = ({ initialSize: { width, height }, children }: any) => (
27
+ <div style={{ border: '1px solid red', overflow: 'hidden', resize: 'both', width, height }}>{children}</div>
28
+ );
29
29
 
30
- export const IconFallback = () => <Thumbnail alt="foo" image="foo" fallback={mdiAbTesting} />;
30
+ /** Default thumbnail props (editable via knobs) */
31
+ export const Default = ({ theme }: any) => {
32
+ const alt = text('Alternative text', 'Image alt text');
33
+ const align = enumKnob(
34
+ 'Alignment',
35
+ [undefined, Alignment.center, Alignment.left, Alignment.right] as const,
36
+ undefined,
37
+ );
38
+ const aspectRatio = enumKnob('Aspect ratio', [undefined, ...Object.values(AspectRatio)], undefined);
39
+ const fillHeight = boolean('Fill Height', false);
40
+ const focusPoint = { x: focusKnob('Focus X'), y: focusKnob('Focus Y') };
41
+ const image = imageKnob('Image', IMAGES.landscape1);
42
+ const variant = select('Variant', ThumbnailVariant, ThumbnailVariant.squared);
43
+ const size = sizeKnob('Size', undefined);
44
+ const onClick = boolean('clickable?', false) ? action('onClick') : undefined;
45
+ const isLoading = boolean('Force loading', false);
46
+ const forceError = boolean('Force error', false);
31
47
 
32
- export const CustomFallback = () => (
33
- <Thumbnail alt="foo" image="foo" fallback={<Thumbnail alt="missing image" image="/logo.svg" />} />
34
- );
48
+ return (
49
+ <Thumbnail
50
+ alt={alt}
51
+ align={align}
52
+ aspectRatio={aspectRatio}
53
+ fillHeight={fillHeight}
54
+ focusPoint={focusPoint}
55
+ image={forceError ? 'foo' : image}
56
+ size={size}
57
+ theme={theme}
58
+ variant={variant}
59
+ onClick={onClick}
60
+ isLoading={isLoading}
61
+ />
62
+ );
63
+ };
35
64
 
36
65
  export const WithBadge = () => {
37
- const thumbnailSize = sizeKnob('Thumbnail size', Size.l);
38
- const variant = select('Thumbnail variant', ThumbnailVariant, ThumbnailVariant.rounded);
66
+ const thumbnailSize = sizeKnob('Size', Size.l);
67
+ const variant = select('Variant', ThumbnailVariant, ThumbnailVariant.rounded);
39
68
  const badgeColor = select('Badge color', ColorPalette, ColorPalette.primary);
40
69
  const activateFallback = boolean('Activate fallback', false);
41
70
  const image = imageKnob();
@@ -55,56 +84,403 @@ export const WithBadge = () => {
55
84
  );
56
85
  };
57
86
 
58
- export const ParentSizeConstraint = () => {
59
- const fillHeight = boolean('Fill Height', true);
60
- return Object.values(AspectRatio).map((aspectRatio) => (
61
- <FlexBox key={aspectRatio} orientation="horizontal" gap="huge">
62
- <h1>ratio: {aspectRatio}</h1>
87
+ export const FocusPoint = () => {
88
+ const focusPoint = { x: focusKnob('Focus X ', -0.2), y: focusKnob('Focus Y', -0.3) };
89
+ const aspectRatio = enumKnob('Aspect ratio', [undefined, ...Object.values(AspectRatio)], AspectRatio.wide);
90
+ const fillHeight = aspectRatio === AspectRatio.free;
91
+ return (
92
+ <>
93
+ <small>Focus point will delay the display of the image if the original image size is not accessible.</small>
94
+ <Resizable initialSize={{ height: 200, width: 300 }}>
95
+ <Thumbnail
96
+ alt="Image"
97
+ image={IMAGES.portrait1s200}
98
+ aspectRatio={aspectRatio}
99
+ fillHeight={fillHeight}
100
+ focusPoint={focusPoint}
101
+ style={{ width: '100%' }}
102
+ />
103
+ </Resizable>
63
104
 
64
- <div style={{ border: '1px solid red', width: 200, height: 400, resize: 'both', overflow: 'auto' }}>
65
- <Thumbnail alt="Grid" image="/demo-assets/grid.jpg" aspectRatio={aspectRatio} fillHeight={fillHeight} />
105
+ <small>Providing the width & height in imgProps should avoid the delay shown above</small>
106
+ <Resizable initialSize={{ height: 200, width: 300 }}>
107
+ <Thumbnail
108
+ alt="Image"
109
+ image={IMAGES.portrait2}
110
+ imgProps={IMAGE_SIZES.portrait2}
111
+ fillHeight={fillHeight}
112
+ aspectRatio={aspectRatio}
113
+ focusPoint={focusPoint}
114
+ style={{ width: '100%' }}
115
+ />
116
+ </Resizable>
117
+ </>
118
+ );
119
+ };
120
+
121
+ export const Clickable = () => (
122
+ <Thumbnail alt="Click me" image={imageKnob()} size={sizeKnob('Size', Size.xxl)} onClick={action('onClick')} />
123
+ );
124
+
125
+ export const ClickableLink = () => (
126
+ <Thumbnail
127
+ alt="Click me"
128
+ image={imageKnob()}
129
+ size={sizeKnob('Size', Size.xxl)}
130
+ linkProps={{ href: 'https://google.fr' }}
131
+ />
132
+ );
133
+
134
+ export const ClickableCustomLink = () => (
135
+ <Thumbnail
136
+ alt="Click me"
137
+ image={imageKnob()}
138
+ size={sizeKnob('Size', Size.xxl)}
139
+ linkAs={CustomLink}
140
+ linkProps={{ href: 'https://google.fr', className: 'custom-class-name' }}
141
+ />
142
+ );
143
+
144
+ export const FillHeight = () => {
145
+ const parentStyle = {
146
+ width: 600,
147
+ height: 240,
148
+ border: '1px solid red',
149
+ overflow: 'hidden',
150
+ resize: 'both',
151
+ } as const;
152
+ return (
153
+ <>
154
+ <h2>Default</h2>
155
+ <div style={parentStyle}>
156
+ <Thumbnail alt="" image={IMAGES.landscape1s200} fillHeight />
157
+ </div>
158
+ <h2>Ratio wide</h2>
159
+ <div style={parentStyle}>
160
+ <Thumbnail alt="" image={IMAGES.landscape1s200} fillHeight aspectRatio="wide" />
161
+ </div>
162
+ <h2>Ratio vertical</h2>
163
+ <div style={parentStyle}>
164
+ <Thumbnail alt="" image={IMAGES.landscape1s200} fillHeight aspectRatio="vertical" />
165
+ </div>
166
+ <h2>Ratio free</h2>
167
+ <div style={parentStyle}>
168
+ <Thumbnail alt="" image={IMAGES.landscape1s200} fillHeight aspectRatio="free" />
66
169
  </div>
170
+ </>
171
+ );
172
+ };
67
173
 
68
- <div style={{ border: '1px solid red', width: 300, height: 300, resize: 'both', overflow: 'auto' }}>
69
- <Thumbnail alt="Grid" image="/demo-assets/grid.jpg" aspectRatio={aspectRatio} fillHeight={fillHeight} />
174
+ export const Original = () => (
175
+ <>
176
+ <h1>Ratio: Original</h1>
177
+ <h2>Default</h2>
178
+ <table>
179
+ <tr>
180
+ <th>Landscape</th>
181
+ <th>
182
+ Landscape <small>(with original size)</small>
183
+ </th>
184
+ <th>Portrait</th>
185
+ <th>
186
+ Portrait <small>(with original size)</small>
187
+ </th>
188
+ </tr>
189
+ <tr>
190
+ <td>
191
+ <Thumbnail alt="" image={IMAGES.landscape1} />
192
+ </td>
193
+ <td>
194
+ <Thumbnail alt="" image={IMAGES.landscape1} imgProps={IMAGE_SIZES.landscape1} />
195
+ </td>
196
+ <td>
197
+ <Thumbnail alt="" image={IMAGES.portrait1} />
198
+ </td>
199
+ <td>
200
+ <Thumbnail alt="" image={IMAGES.portrait1} imgProps={IMAGE_SIZES.portrait1} />
201
+ </td>
202
+ </tr>
203
+ </table>
204
+ <h2>Constrained parent size</h2>
205
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
206
+ <div className="parent" style={{ width: 220 }}>
207
+ <Thumbnail alt="" image={IMAGES.landscape1} />
208
+ </div>
209
+ <div className="parent" style={{ width: 220 }}>
210
+ <Thumbnail alt="" image={IMAGES.portrait1} />
70
211
  </div>
212
+ </FlexBox>
213
+ <h2>With size</h2>
214
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
215
+ <Thumbnail alt="" image={IMAGES.landscape1} size="xxl" />
216
+ <Thumbnail alt="" image={IMAGES.portrait1} size="xxl" />
217
+ </FlexBox>
218
+ <h2>With size & smaller image</h2>
219
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
220
+ <Thumbnail alt="" image={IMAGES.landscape1s200} size="xxl" />
221
+ <Thumbnail alt="" image={IMAGES.portrait1s200} size="xxl" />
222
+ </FlexBox>
223
+ <h2>With size & smaller image & fill height</h2>
224
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
225
+ <Thumbnail alt="" image={IMAGES.landscape1s200} size="xxl" fillHeight />
226
+ <Thumbnail alt="" image={IMAGES.portrait1s200} size="xxl" fillHeight />
227
+ </FlexBox>
228
+ <h2>Constrained parent size & smaller image & fill height</h2>
229
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
230
+ <div className="parent" style={{ width: 220 }}>
231
+ <Thumbnail alt="" image={IMAGES.landscape1s200} fillHeight />
232
+ </div>
233
+ <div className="parent" style={{ width: 220 }}>
234
+ <Thumbnail alt="" image={IMAGES.portrait1s200} fillHeight />
235
+ </div>
236
+ </FlexBox>
237
+ </>
238
+ );
71
239
 
72
- <div style={{ border: '1px solid red', width: 400, height: 200, resize: 'both', overflow: 'auto' }}>
73
- <Thumbnail alt="Grid" image="/demo-assets/grid.jpg" aspectRatio={aspectRatio} fillHeight={fillHeight} />
240
+ export const Vertical = () => (
241
+ <>
242
+ <h1>Ratio: vertical</h1>
243
+ <h2>Default</h2>
244
+ <small>Unsupported use case (thumbnail size is undefined)</small>
245
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
246
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1} />
247
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1} />
248
+ </FlexBox>
249
+ <h2>Constraint parent size</h2>
250
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
251
+ <div className="parent" style={{ width: 220 }}>
252
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1} />
253
+ </div>
254
+ <div className="parent" style={{ width: 220 }}>
255
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1} />
74
256
  </div>
75
257
  </FlexBox>
76
- ));
77
- };
258
+ <h2>Constraint parent size & smaller image</h2>
259
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
260
+ <div className="parent" style={{ width: 220 }}>
261
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1s200} />
262
+ </div>
263
+ <div className="parent" style={{ width: 220 }}>
264
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1s200} />
265
+ </div>
266
+ </FlexBox>
267
+ <h2>With size</h2>
268
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
269
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1} size="xxl" />
270
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1} size="xxl" />
271
+ </FlexBox>
272
+ <h2>With size & smaller image</h2>
273
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
274
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1s200} size="xxl" />
275
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1s200} size="xxl" />
276
+ </FlexBox>
277
+ <h2>With size & smaller image & fill height</h2>
278
+ <small>Unsupported use case (use ratio free with fill height)</small>
279
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
280
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1s200} size="xxl" fillHeight />
281
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1s200} size="xxl" fillHeight />
282
+ </FlexBox>
283
+ <h2>Constrained parent size & smaller image & fill height</h2>
284
+ <small>Unsupported use case (use ratio free with fill height)</small>
285
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
286
+ <div className="parent" style={{ width: 220 }}>
287
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.landscape1s200} fillHeight />
288
+ </div>
289
+ <div className="parent" style={{ width: 220 }}>
290
+ <Thumbnail alt="" aspectRatio="vertical" image={IMAGES.portrait1s200} fillHeight />
291
+ </div>
292
+ </FlexBox>
293
+ </>
294
+ );
78
295
 
79
- export const Knobs = ({ theme }: any) => {
80
- const alt = text('Alternative text', 'Image alt text');
81
- const align = enumKnob(
82
- 'Alignment',
83
- [undefined, Alignment.center, Alignment.left, Alignment.right] as const,
84
- undefined,
85
- );
86
- const aspectRatio = enumKnob('Aspect ratio', [undefined, ...Object.values(AspectRatio)], undefined);
87
- const crossOrigin = enumKnob('CORS', [undefined, 'anonymous', 'use-credentials'] as const, undefined);
88
- const fillHeight = boolean('Fill Height', false);
89
- const focusPoint = { x: focusKnob('Focus X'), y: focusKnob('Focus Y') };
90
- const image = imageKnob('Image', IMAGES.landscape1);
91
- const variant = select('Variant', ThumbnailVariant, ThumbnailVariant.squared);
92
- const size = sizeKnob('Size', Size.xxl);
93
- const onClick = boolean('clickable?', false) ? () => console.log('ok') : undefined;
296
+ export const Wide = () => (
297
+ <>
298
+ <h1>Ratio: wide</h1>
299
+ <h2>Default</h2>
300
+ <small>Unsupported use case (thumbnail size is undefined)</small>
301
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
302
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1} />
303
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1} />
304
+ </FlexBox>
305
+ <h2>Constrained parent size</h2>
306
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
307
+ <div className="parent" style={{ width: 220 }}>
308
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1} />
309
+ </div>
310
+ <div className="parent" style={{ width: 220 }}>
311
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1} />
312
+ </div>
313
+ </FlexBox>
314
+ <h2>Constrained parent size & smaller image</h2>
315
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
316
+ <div className="parent" style={{ width: 220 }}>
317
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1s200} />
318
+ </div>
319
+ <div className="parent" style={{ width: 220 }}>
320
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1s200} />
321
+ </div>
322
+ </FlexBox>
323
+ <h2>With size</h2>
324
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
325
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1} size="xxl" />
326
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1} size="xxl" />
327
+ </FlexBox>
328
+ <h2>With size & smaller image</h2>
329
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
330
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1s200} size="xxl" />
331
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1s200} size="xxl" />
332
+ </FlexBox>
333
+ <h2>With size & smaller image & fill height</h2>
334
+ <small>Unsupported use case (use ratio free with fill height)</small>
335
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
336
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1s200} size="xxl" fillHeight />
337
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1s200} size="xxl" fillHeight />
338
+ </FlexBox>
339
+ <h2>Constrained parent size & smaller image & fill height</h2>
340
+ <small>Unsupported use case (use ratio free with fill height)</small>
341
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
342
+ <div className="parent" style={{ width: 220 }}>
343
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.landscape1s200} fillHeight />
344
+ </div>
345
+ <div className="parent" style={{ width: 220 }}>
346
+ <Thumbnail alt="" aspectRatio="wide" image={IMAGES.portrait1s200} fillHeight />
347
+ </div>
348
+ </FlexBox>
349
+ </>
350
+ );
94
351
 
95
- return (
352
+ export const Square = () => (
353
+ <>
354
+ <h1>Ratio: square</h1>
355
+ <h2>Default</h2>
356
+ <small>Unsupported use case (thumbnail size is undefined)</small>
357
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
358
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1} />
359
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1} />
360
+ </FlexBox>
361
+ <h2>Constrained parent size</h2>
362
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
363
+ <div className="parent" style={{ width: 220 }}>
364
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1} />
365
+ </div>
366
+ <div className="parent" style={{ width: 220 }}>
367
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1} />
368
+ </div>
369
+ </FlexBox>
370
+ <h2>Constrained parent size & smaller image</h2>
371
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
372
+ <div className="parent" style={{ width: 220 }}>
373
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} />
374
+ </div>
375
+ <div className="parent" style={{ width: 220 }}>
376
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} />
377
+ </div>
378
+ </FlexBox>
379
+ <h2>With size</h2>
380
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
381
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1} size="xxl" />
382
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1} size="xxl" />
383
+ </FlexBox>
384
+ <h2>With size & smaller image</h2>
385
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
386
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} size="xxl" />
387
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} size="xxl" />
388
+ </FlexBox>
389
+ <h2>With size & smaller image & fill height</h2>
390
+ <small>Unsupported use case (use ratio free with fill height)</small>
391
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
392
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} size="xxl" fillHeight />
393
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} size="xxl" fillHeight />
394
+ </FlexBox>
395
+ <h2>Constrained parent size & smaller image & fill height</h2>
396
+ <small>Unsupported use case (use ratio free with fill height)</small>
397
+ <FlexBox orientation="horizontal" vAlign="center" gap="huge">
398
+ <div className="parent" style={{ width: 220 }}>
399
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.landscape1s200} fillHeight />
400
+ </div>
401
+ <div className="parent" style={{ width: 220 }}>
402
+ <Thumbnail alt="" aspectRatio="square" image={IMAGES.portrait1s200} fillHeight />
403
+ </div>
404
+ </FlexBox>
405
+ </>
406
+ );
407
+
408
+ export const IsLoading = ({ theme }: any) => (
409
+ <FlexBox
410
+ orientation="horizontal"
411
+ vAlign="center"
412
+ marginAuto={['left', 'right']}
413
+ style={{ border: '1px solid red', width: 900, height: 700, resize: 'both', overflow: 'auto' }}
414
+ >
96
415
  <Thumbnail
97
- alt={alt}
98
- align={align}
99
- aspectRatio={aspectRatio}
100
- crossOrigin={crossOrigin}
101
- fillHeight={fillHeight}
102
- focusPoint={focusPoint}
103
- image={htmlDecode(image)}
104
- size={size}
105
416
  theme={theme}
106
- variant={variant}
107
- onClick={onClick}
417
+ alt="Image alt text"
418
+ image={IMAGES.landscape2}
419
+ isLoading={boolean('Force loading', true)}
420
+ fillHeight={boolean('Fill Height', false)}
421
+ size={sizeKnob('Size', undefined)}
108
422
  />
423
+ </FlexBox>
424
+ );
425
+
426
+ export const ErrorFallbackVariants = ({ theme }: any) => {
427
+ const isLoading = boolean('Force loading', false);
428
+ const variant = select('Variant', ThumbnailVariant, undefined);
429
+ const base = { alt: 'foo', image: 'foo', isLoading, variant, theme } as const;
430
+ const imageFallback = <img src="/logo.svg" alt="logo" />;
431
+ const imgProps = { width: 50, height: 50 };
432
+
433
+ return (
434
+ <>
435
+ <h2>Default</h2>
436
+ <FlexBox orientation="horizontal" gap="big">
437
+ <Thumbnail {...base} />
438
+ <Thumbnail {...base} fallback={mdiAbTesting} />
439
+ <Thumbnail {...base} fallback={imageFallback} />
440
+ </FlexBox>
441
+ <h2>
442
+ With original image size <small>(50x50)</small>
443
+ </h2>
444
+ <FlexBox orientation="horizontal" gap="big">
445
+ <Thumbnail {...base} imgProps={imgProps} />
446
+ <Thumbnail {...base} fallback={mdiAbTesting} imgProps={imgProps} />
447
+ <Thumbnail {...base} fallback={imageFallback} imgProps={imgProps} />
448
+ </FlexBox>
449
+ <h2>With size</h2>
450
+ <FlexBox orientation="horizontal" gap="big">
451
+ <Thumbnail {...base} size="xl" />
452
+ <Thumbnail {...base} size="xl" fallback={mdiAbTesting} />
453
+ <Thumbnail {...base} size="xl" fallback={imageFallback} />
454
+ </FlexBox>
455
+ <h2>With size & ratio</h2>
456
+ <FlexBox orientation="horizontal" gap="big">
457
+ <Thumbnail {...base} size="xl" aspectRatio="wide" />
458
+ <Thumbnail {...base} size="xl" aspectRatio="wide" fallback={mdiAbTesting} />
459
+ <Thumbnail {...base} size="xl" aspectRatio="wide" fallback={imageFallback} />
460
+ </FlexBox>
461
+ <h2>
462
+ With original size <small>(50x50)</small> & ratio
463
+ </h2>
464
+ <small>Unsupported use case (thumbnail size is undefined)</small>
465
+ <FlexBox orientation="horizontal" gap="big">
466
+ <Thumbnail {...base} imgProps={imgProps} aspectRatio="wide" />
467
+ <Thumbnail {...base} imgProps={imgProps} aspectRatio="wide" fallback={mdiAbTesting} />
468
+ <Thumbnail {...base} imgProps={imgProps} aspectRatio="wide" fallback={imageFallback} />
469
+ </FlexBox>
470
+ <h2>
471
+ With original size <small>(50x50)</small> & ratio & constrained parent size
472
+ </h2>
473
+ <FlexBox orientation="horizontal" gap="big">
474
+ <div className="parent" style={{ width: 220 }}>
475
+ <Thumbnail {...base} imgProps={imgProps} aspectRatio="wide" />
476
+ </div>
477
+ <div className="parent" style={{ width: 220 }}>
478
+ <Thumbnail {...base} imgProps={imgProps} aspectRatio="wide" fallback={mdiAbTesting} />
479
+ </div>
480
+ <div className="parent" style={{ width: 220 }}>
481
+ <Thumbnail {...base} imgProps={imgProps} aspectRatio="wide" fallback={imageFallback} />
482
+ </div>
483
+ </FlexBox>
484
+ </>
109
485
  );
110
486
  };
@@ -5,7 +5,7 @@ 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, CustomFallback, Default, DefaultFallback, IconFallback, WithBadge } from './Thumbnail.stories';
8
+ import { Clickable, ClickableCustomLink, ClickableLink, Default, WithBadge } from './Thumbnail.stories';
9
9
 
10
10
  const CLASSNAME = Thumbnail.className as string;
11
11
 
@@ -22,7 +22,13 @@ describe(`<${Thumbnail.displayName}>`, () => {
22
22
  // 1. Test render via snapshot.
23
23
  describe('Snapshots and structure', () => {
24
24
  itShouldRenderStories(
25
- { Default, Clickable, DefaultFallback, CustomFallback, IconFallback, WithBadge },
25
+ {
26
+ Default,
27
+ Clickable,
28
+ ClickableLink,
29
+ ClickableCustomLink,
30
+ WithBadge,
31
+ },
26
32
  Thumbnail,
27
33
  );
28
34
  });