@lumx/react 3.20.1-alpha.2 → 3.20.1-alpha.21

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 (234) hide show
  1. package/CONTRIBUTING.md +1 -5
  2. package/README.md +0 -2
  3. package/_internal/index.js +20 -13
  4. package/_internal/index.js.map +1 -1
  5. package/index.d.ts +35 -35
  6. package/index.js +2407 -2447
  7. package/index.js.map +1 -1
  8. package/package.json +10 -13
  9. package/src/components/alert-dialog/AlertDialog.stories.tsx +1 -1
  10. package/src/components/alert-dialog/AlertDialog.test.tsx +3 -4
  11. package/src/components/autocomplete/Autocomplete.stories.tsx +1 -1
  12. package/src/components/autocomplete/Autocomplete.test.tsx +3 -5
  13. package/src/components/autocomplete/Autocomplete.tsx +1 -1
  14. package/src/components/autocomplete/AutocompleteMultiple.stories.tsx +1 -1
  15. package/src/components/autocomplete/AutocompleteMultiple.test.tsx +0 -2
  16. package/src/components/autocomplete/AutocompleteMultiple.tsx +1 -1
  17. package/src/components/avatar/Avatar.stories.tsx +0 -2
  18. package/src/components/avatar/Avatar.test.tsx +0 -2
  19. package/src/components/avatar/Avatar.tsx +1 -1
  20. package/src/components/badge/Badge.stories.tsx +0 -1
  21. package/src/components/badge/Badge.test.tsx +0 -2
  22. package/src/components/badge/Badge.tsx +1 -1
  23. package/src/components/badge/BadgeWrapper.stories.tsx +0 -1
  24. package/src/components/badge/BadgeWrapper.test.tsx +0 -2
  25. package/src/components/badge/BadgeWrapper.tsx +1 -1
  26. package/src/components/button/Button.stories.tsx +0 -1
  27. package/src/components/button/Button.test.tsx +9 -11
  28. package/src/components/button/Button.tsx +0 -2
  29. package/src/components/button/ButtonGroup.stories.tsx +0 -2
  30. package/src/components/button/ButtonGroup.test.tsx +0 -2
  31. package/src/components/button/ButtonGroup.tsx +0 -2
  32. package/src/components/button/ButtonRoot.tsx +7 -37
  33. package/src/components/button/IconButton.test.tsx +0 -2
  34. package/src/components/button/IconButton.tsx +0 -2
  35. package/src/components/checkbox/Checkbox.test.tsx +3 -5
  36. package/src/components/chip/Chip.stories.tsx +0 -2
  37. package/src/components/chip/Chip.test.tsx +19 -19
  38. package/src/components/chip/Chip.tsx +1 -1
  39. package/src/components/chip/ChipGroup.stories.tsx +0 -2
  40. package/src/components/chip/ChipGroup.test.tsx +0 -2
  41. package/src/components/chip/ChipGroup.tsx +1 -1
  42. package/src/components/comment-block/CommentBlock.stories.tsx +0 -1
  43. package/src/components/comment-block/CommentBlock.test.tsx +0 -1
  44. package/src/components/comment-block/CommentBlock.tsx +1 -1
  45. package/src/components/date-picker/DatePicker.test.tsx +3 -5
  46. package/src/components/date-picker/DatePicker.tsx +1 -1
  47. package/src/components/date-picker/DatePickerControlled.test.tsx +6 -8
  48. package/src/components/date-picker/DatePickerField.test.tsx +3 -5
  49. package/src/components/dialog/Dialog.test.tsx +4 -6
  50. package/src/components/divider/Divider.test.tsx +0 -2
  51. package/src/components/divider/Divider.tsx +0 -2
  52. package/src/components/drag-handle/DragHandle.test.tsx +0 -2
  53. package/src/components/drag-handle/DragHandle.tsx +0 -2
  54. package/src/components/dropdown/Dropdown.stories.tsx +1 -1
  55. package/src/components/dropdown/Dropdown.test.tsx +3 -3
  56. package/src/components/dropdown/Dropdown.tsx +1 -1
  57. package/src/components/expansion-panel/ExpansionPanel.test.tsx +6 -7
  58. package/src/components/flag/Flag.test.tsx +0 -2
  59. package/src/components/flag/Flag.tsx +0 -2
  60. package/src/components/flex-box/FlexBox.stories.tsx +0 -2
  61. package/src/components/flex-box/FlexBox.test.tsx +0 -1
  62. package/src/components/flex-box/FlexBox.tsx +1 -1
  63. package/src/components/generic-block/GenericBlock.test.tsx +1 -1
  64. package/src/components/grid/Grid.tsx +0 -2
  65. package/src/components/grid/GridItem.tsx +0 -2
  66. package/src/components/grid-column/GridColumn.stories.tsx +0 -1
  67. package/src/components/grid-column/GridColumn.test.jsx +0 -2
  68. package/src/components/grid-column/GridColumn.tsx +1 -1
  69. package/src/components/heading/Heading.stories.tsx +0 -1
  70. package/src/components/heading/Heading.test.tsx +0 -2
  71. package/src/components/heading/Heading.tsx +0 -2
  72. package/src/components/heading/HeadingLevelProvider.tsx +1 -1
  73. package/src/components/icon/Icon.stories.tsx +30 -4
  74. package/src/components/icon/Icon.test.tsx +85 -4
  75. package/src/components/icon/Icon.tsx +118 -9
  76. package/src/components/image-block/ImageBlock.stories.tsx +0 -2
  77. package/src/components/image-block/ImageBlock.test.tsx +0 -1
  78. package/src/components/image-block/ImageBlock.tsx +1 -1
  79. package/src/components/image-block/ImageCaption.tsx +1 -1
  80. package/src/components/image-lightbox/ImageLightbox.stories.tsx +0 -1
  81. package/src/components/image-lightbox/ImageLightbox.test.tsx +11 -9
  82. package/src/components/image-lightbox/types.ts +0 -2
  83. package/src/components/inline-list/InlineList.stories.tsx +0 -1
  84. package/src/components/inline-list/InlineList.test.tsx +0 -2
  85. package/src/components/inline-list/InlineList.tsx +1 -1
  86. package/src/components/input-helper/InputHelper.test.tsx +0 -2
  87. package/src/components/input-helper/InputHelper.tsx +1 -1
  88. package/src/components/input-label/InputLabel.stories.tsx +0 -2
  89. package/src/components/input-label/InputLabel.test.tsx +0 -2
  90. package/src/components/input-label/InputLabel.tsx +1 -1
  91. package/src/components/lightbox/Lightbox.test.tsx +0 -2
  92. package/src/components/lightbox/Lightbox.tsx +1 -1
  93. package/src/components/link/Link.stories.tsx +0 -1
  94. package/src/components/link/Link.test.tsx +13 -13
  95. package/src/components/link/Link.tsx +9 -22
  96. package/src/components/link-preview/LinkPreview.test.tsx +0 -2
  97. package/src/components/link-preview/LinkPreview.tsx +0 -2
  98. package/src/components/list/List.stories.tsx +1 -1
  99. package/src/components/list/List.test.tsx +0 -2
  100. package/src/components/list/List.tsx +1 -1
  101. package/src/components/list/ListDivider.test.tsx +0 -2
  102. package/src/components/list/ListDivider.tsx +0 -2
  103. package/src/components/list/ListItem.test.tsx +5 -7
  104. package/src/components/list/ListItem.tsx +1 -1
  105. package/src/components/list/ListSubheader.test.tsx +0 -2
  106. package/src/components/list/ListSubheader.tsx +1 -1
  107. package/src/components/message/Message.test.tsx +1 -2
  108. package/src/components/message/Message.tsx +1 -1
  109. package/src/components/mosaic/Mosaic.test.tsx +3 -5
  110. package/src/components/mosaic/Mosaic.tsx +1 -1
  111. package/src/components/navigation/Navigation.stories.tsx +0 -2
  112. package/src/components/navigation/Navigation.test.tsx +0 -2
  113. package/src/components/navigation/Navigation.tsx +0 -2
  114. package/src/components/navigation/NavigationItem.test.tsx +0 -2
  115. package/src/components/navigation/NavigationItem.tsx +7 -11
  116. package/src/components/navigation/NavigationSection.test.tsx +0 -2
  117. package/src/components/navigation/NavigationSection.tsx +5 -4
  118. package/src/components/notification/Notification.test.tsx +4 -5
  119. package/src/components/notification/Notification.tsx +1 -1
  120. package/src/components/popover/Popover.test.tsx +0 -2
  121. package/src/components/popover/Popover.tsx +1 -1
  122. package/src/components/popover/usePopoverStyle.tsx +1 -1
  123. package/src/components/popover-dialog/PopoverDialog.test.tsx +1 -2
  124. package/src/components/popover-dialog/PopoverDialog.tsx +0 -2
  125. package/src/components/post-block/PostBlock.test.tsx +0 -2
  126. package/src/components/post-block/PostBlock.tsx +1 -1
  127. package/src/components/progress/Progress.tsx +0 -2
  128. package/src/components/progress/ProgressCircular.stories.tsx +0 -1
  129. package/src/components/progress/ProgressCircular.test.tsx +0 -2
  130. package/src/components/progress/ProgressCircular.tsx +0 -2
  131. package/src/components/progress/ProgressLinear.test.tsx +0 -2
  132. package/src/components/progress/ProgressLinear.tsx +0 -2
  133. package/src/components/progress-tracker/ProgressTracker.stories.tsx +1 -1
  134. package/src/components/progress-tracker/ProgressTracker.test.tsx +0 -2
  135. package/src/components/progress-tracker/ProgressTrackerProvider.test.tsx +0 -2
  136. package/src/components/progress-tracker/ProgressTrackerProvider.tsx +1 -1
  137. package/src/components/progress-tracker/ProgressTrackerStep.test.tsx +0 -2
  138. package/src/components/progress-tracker/ProgressTrackerStep.tsx +1 -1
  139. package/src/components/progress-tracker/ProgressTrackerStepPanel.test.tsx +0 -2
  140. package/src/components/progress-tracker/ProgressTrackerStepPanel.tsx +0 -2
  141. package/src/components/radio-button/RadioButton.test.tsx +3 -5
  142. package/src/components/radio-button/RadioButton.tsx +1 -1
  143. package/src/components/radio-button/RadioGroup.stories.tsx +1 -1
  144. package/src/components/radio-button/RadioGroup.test.tsx +0 -2
  145. package/src/components/radio-button/RadioGroup.tsx +1 -1
  146. package/src/components/select/Select.stories.tsx +1 -1
  147. package/src/components/select/Select.test.tsx +8 -9
  148. package/src/components/select/Select.tsx +1 -1
  149. package/src/components/select/SelectMultiple.stories.tsx +1 -1
  150. package/src/components/select/SelectMultiple.test.tsx +5 -7
  151. package/src/components/select/SelectMultiple.tsx +1 -1
  152. package/src/components/select/WithSelectContext.tsx +1 -1
  153. package/src/components/select/constants.ts +1 -1
  154. package/src/components/side-navigation/SideNavigation.test.tsx +0 -2
  155. package/src/components/side-navigation/SideNavigation.tsx +1 -1
  156. package/src/components/side-navigation/SideNavigationItem.test.tsx +2 -4
  157. package/src/components/side-navigation/SideNavigationItem.tsx +23 -28
  158. package/src/components/skeleton/SkeletonCircle.test.tsx +0 -2
  159. package/src/components/skeleton/SkeletonCircle.tsx +0 -2
  160. package/src/components/skeleton/SkeletonRectangle.test.tsx +0 -2
  161. package/src/components/skeleton/SkeletonRectangle.tsx +0 -2
  162. package/src/components/skeleton/SkeletonTypography.stories.tsx +0 -2
  163. package/src/components/skeleton/SkeletonTypography.test.tsx +0 -2
  164. package/src/components/skeleton/SkeletonTypography.tsx +1 -1
  165. package/src/components/slider/Slider.test.tsx +1 -3
  166. package/src/components/slider/Slider.tsx +1 -1
  167. package/src/components/slideshow/Slideshow.stories.tsx +0 -1
  168. package/src/components/slideshow/Slideshow.test.tsx +0 -2
  169. package/src/components/slideshow/SlideshowControls.stories.tsx +0 -2
  170. package/src/components/slideshow/SlideshowItem.tsx +0 -2
  171. package/src/components/slideshow/useSlideFocusManagement.tsx +1 -1
  172. package/src/components/switch/Switch.test.tsx +5 -7
  173. package/src/components/switch/Switch.tsx +1 -1
  174. package/src/components/table/Table.test.tsx +0 -2
  175. package/src/components/table/Table.tsx +0 -2
  176. package/src/components/table/TableBody.test.tsx +0 -2
  177. package/src/components/table/TableBody.tsx +0 -2
  178. package/src/components/table/TableCell.test.tsx +1 -3
  179. package/src/components/table/TableCell.tsx +0 -2
  180. package/src/components/table/TableHeader.test.tsx +0 -2
  181. package/src/components/table/TableHeader.tsx +0 -2
  182. package/src/components/table/TableRow.test.tsx +0 -2
  183. package/src/components/table/TableRow.tsx +0 -2
  184. package/src/components/tabs/Tab.test.tsx +0 -2
  185. package/src/components/tabs/Tab.tsx +1 -1
  186. package/src/components/tabs/TabList.test.tsx +0 -2
  187. package/src/components/tabs/TabPanel.test.tsx +0 -2
  188. package/src/components/tabs/TabPanel.tsx +0 -2
  189. package/src/components/tabs/TabProvider.test.tsx +0 -2
  190. package/src/components/tabs/TabProvider.tsx +1 -1
  191. package/src/components/tabs/Tabs.stories.tsx +1 -1
  192. package/src/components/text/Text.stories.tsx +1 -1
  193. package/src/components/text/Text.test.tsx +0 -2
  194. package/src/components/text/Text.tsx +0 -2
  195. package/src/components/text-field/TextField.test.tsx +9 -10
  196. package/src/components/text-field/TextField.tsx +1 -1
  197. package/src/components/thumbnail/Thumbnail.test.tsx +29 -7
  198. package/src/components/thumbnail/Thumbnail.tsx +11 -11
  199. package/src/components/toolbar/Toolbar.tsx +1 -1
  200. package/src/components/tooltip/Tooltip.stories.tsx +2 -1
  201. package/src/components/tooltip/Tooltip.test.tsx +14 -8
  202. package/src/components/uploader/Uploader.test.tsx +2 -4
  203. package/src/components/user-block/UserBlock.stories.tsx +0 -2
  204. package/src/components/user-block/UserBlock.test.tsx +1 -3
  205. package/src/hooks/useId.test.tsx +0 -1
  206. package/src/hooks/useInfiniteScroll.tsx +1 -1
  207. package/src/hooks/usePreviousValue.ts +0 -1
  208. package/src/stories/decorators/withChromaticForceScreenSize.tsx +0 -1
  209. package/src/stories/decorators/withNestedProps.tsx +0 -1
  210. package/src/stories/decorators/withThemedBackground.tsx +0 -2
  211. package/src/stories/decorators/withWrapper.tsx +0 -2
  212. package/src/stories/utils/CustomLink.tsx +0 -1
  213. package/src/testing/utils/ThemeSentinel.tsx +0 -1
  214. package/src/untypped-modules.d.ts +4 -0
  215. package/src/utils/ClickAwayProvider/ClickAwayProvider.stories.jsx +1 -1
  216. package/src/utils/ClickAwayProvider/ClickAwayProvider.tsx +1 -1
  217. package/src/utils/MaterialThemeSwitcher/MaterialThemeSwitcher.tsx +1 -1
  218. package/src/utils/Portal/Portal.test.tsx +0 -1
  219. package/src/utils/Portal/PortalProvider.stories.jsx +0 -1
  220. package/src/utils/Portal/PortalProvider.test.tsx +1 -2
  221. package/src/utils/date/getYearDisplayName.test.ts +1 -1
  222. package/src/utils/disabled/DisabledStateProvider.stories.tsx +0 -2
  223. package/src/utils/disabled/useDisableStateProps.test.tsx +2 -2
  224. package/src/utils/react/OnBeforeUnmount.tsx +1 -1
  225. package/src/utils/react/RawClickable.test.tsx +153 -0
  226. package/src/utils/react/RawClickable.tsx +65 -0
  227. package/src/utils/react/skipRender.tsx +2 -2
  228. package/src/utils/react/wrapChildrenIconWithSpaces.test.tsx +1 -1
  229. package/src/utils/type/HasPolymorphicAs.ts +0 -2
  230. package/src/utils/type/HasRequiredLinkHref.ts +1 -0
  231. package/src/utils/type/index.ts +1 -0
  232. package/utils/index.d.ts +6 -6
  233. package/utils/index.js +1 -1
  234. package/src/utils/react/renderButtonOrLink.tsx +0 -16
@@ -1,11 +1,50 @@
1
- import React from 'react';
2
-
3
- import { Icon as UI, IconProps, IconSizes } from '@lumx/core/js/components/Icon';
1
+ import classNames from 'classnames';
4
2
 
3
+ import { mdiAlertCircle } from '@lumx/icons';
4
+ import { ColorPalette, ColorVariant, ColorWithVariants, Size, Theme } from '@lumx/react';
5
+ import { GenericProps, HasTheme } from '@lumx/react/utils/type';
6
+ import { getRootClassName, handleBasicClasses, resolveColorWithVariants } from '@lumx/core/js/utils/className';
5
7
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
6
8
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
7
9
 
8
- export type { IconProps, IconSizes };
10
+ export type IconSizes = Extract<Size, 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl'>;
11
+
12
+ /**
13
+ * Defines the props of the component.
14
+ */
15
+ export interface IconProps extends GenericProps, HasTheme {
16
+ /** Color variant. */
17
+ color?: ColorWithVariants;
18
+ /** Lightened or darkened variant of the selected icon color. */
19
+ colorVariant?: ColorVariant;
20
+ /** Whether the icon has a shape. */
21
+ hasShape?: boolean;
22
+ /**
23
+ * Icon (SVG path) draw code (`d` property of the `<path>` SVG element).
24
+ * See https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
25
+ */
26
+ icon: string;
27
+ /** Size variant. */
28
+ size?: IconSizes;
29
+ /** Sets an alternative text on the svg. Will set an `img` role to the svg. */
30
+ alt?: string;
31
+ }
32
+
33
+ /**
34
+ * Component display name.
35
+ */
36
+ const COMPONENT_NAME = 'Icon';
37
+
38
+ /**
39
+ * Component default class name and class prefix.
40
+ */
41
+ const CLASSNAME = getRootClassName(COMPONENT_NAME);
42
+
43
+ /**
44
+ * Component default props.
45
+ */
46
+ const DEFAULT_PROPS: Partial<IconProps> = {};
47
+
9
48
  /**
10
49
  * Icon component.
11
50
  *
@@ -15,10 +54,80 @@ export type { IconProps, IconSizes };
15
54
  */
16
55
  export const Icon = forwardRef<IconProps, HTMLElement>((props, ref) => {
17
56
  const defaultTheme = useTheme();
57
+ const {
58
+ className,
59
+ color: propColor,
60
+ colorVariant: propColorVariant,
61
+ hasShape,
62
+ icon,
63
+ size,
64
+ theme = defaultTheme,
65
+ alt,
66
+ ...forwardedProps
67
+ } = props;
68
+ const [color, colorVariant] = resolveColorWithVariants(propColor, propColorVariant);
18
69
 
19
- return <UI ref={ref} {...props} theme={props.theme || defaultTheme} />;
20
- });
70
+ // Color
71
+ let iconColor = color;
72
+ if (!iconColor && (hasShape || theme)) {
73
+ iconColor = theme === Theme.dark ? ColorPalette.light : ColorPalette.dark;
74
+ }
75
+
76
+ // Color variant
77
+ let iconColorVariant = colorVariant;
78
+ if (!iconColorVariant && hasShape && iconColor === ColorPalette.dark) {
79
+ iconColorVariant = 'L2';
80
+ }
21
81
 
22
- Icon.displayName = UI.displayName;
23
- Icon.className = UI.className;
24
- Icon.defaultProps = UI.defaultProps;
82
+ // Size
83
+ let iconSize = size;
84
+ if (size && hasShape) {
85
+ if (size === Size.xxs || size === Size.xs) {
86
+ iconSize = Size.s;
87
+ } else if (size === Size.xxl) {
88
+ iconSize = Size.xl;
89
+ }
90
+ } else if (hasShape) {
91
+ iconSize = Size.m;
92
+ }
93
+
94
+ return (
95
+ <i
96
+ ref={ref}
97
+ {...forwardedProps}
98
+ className={classNames(
99
+ className,
100
+ handleBasicClasses({
101
+ color: iconColor,
102
+ colorVariant: iconColorVariant,
103
+ hasShape,
104
+ prefix: CLASSNAME,
105
+ theme,
106
+ size: iconSize,
107
+ }),
108
+ !hasShape && `${CLASSNAME}--no-shape`,
109
+ !hasShape &&
110
+ iconColor === ColorPalette.yellow &&
111
+ icon === mdiAlertCircle &&
112
+ `${CLASSNAME}--has-dark-layer`,
113
+ `${CLASSNAME}--path`,
114
+ )}
115
+ >
116
+ <svg
117
+ aria-hidden={alt ? undefined : 'true'}
118
+ role={alt ? 'img' : undefined}
119
+ aria-label={alt}
120
+ height="1em"
121
+ preserveAspectRatio="xMidYMid meet"
122
+ style={{ verticalAlign: '-0.125em' }}
123
+ viewBox="0 0 24 24"
124
+ width="1em"
125
+ >
126
+ <path d={icon} fill="currentColor" />
127
+ </svg>
128
+ </i>
129
+ );
130
+ });
131
+ Icon.displayName = COMPONENT_NAME;
132
+ Icon.className = CLASSNAME;
133
+ Icon.defaultProps = DEFAULT_PROPS;
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import {
4
2
  Alignment,
5
3
  AspectRatio,
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
3
2
 
4
3
  import { render } from '@testing-library/react';
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import { ReactNode } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -1,4 +1,4 @@
1
- import React, { CSSProperties, ReactNode } from 'react';
1
+ import { CSSProperties, ReactNode } from 'react';
2
2
 
3
3
  import { FlexBox, HorizontalAlignment, Text, TextProps, useTheme } from '@lumx/react';
4
4
  import { HasPolymorphicAs, HasTheme } from '@lumx/react/utils/type';
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { IMAGES } from '@lumx/react/stories/controls/image';
3
2
  import { Button, Mosaic, ImageLightbox, ImageLightboxProps, Chip, ChipGroup } from '@lumx/react';
4
3
  import { withWrapper } from '@lumx/react/stories/decorators/withWrapper';
@@ -1,7 +1,5 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
4
- import { render, within, screen } from '@testing-library/react';
2
+ import { render, within, screen, waitFor } from '@testing-library/react';
5
3
  import { getByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
6
4
  import userEvent from '@testing-library/user-event';
7
5
  import { useImageSize } from '@lumx/react/hooks/useImageSize';
@@ -18,8 +16,8 @@ import Meta, {
18
16
  WithMosaicTrigger,
19
17
  } from './ImageLightbox.stories';
20
18
 
21
- jest.mock('@lumx/react/hooks/useImageSize');
22
- jest.mock('@lumx/react/hooks/useSizeOnWindowResize');
19
+ vi.mock('@lumx/react/hooks/useImageSize');
20
+ vi.mock('@lumx/react/hooks/useSizeOnWindowResize');
23
21
 
24
22
  const CLASSNAME = ImageLightbox.className as string;
25
23
  const baseProps = Meta.args;
@@ -56,7 +54,7 @@ const queries = {
56
54
  describe(`<${ImageLightbox.displayName}>`, () => {
57
55
  beforeEach(() => {
58
56
  (useImageSize as any).mockReturnValue(null);
59
- (useSizeOnWindowResize as any).mockReturnValue([null, jest.fn()]);
57
+ (useSizeOnWindowResize as any).mockReturnValue([null, vi.fn()]);
60
58
  });
61
59
 
62
60
  describe('render', () => {
@@ -159,7 +157,9 @@ describe(`<${ImageLightbox.displayName}>`, () => {
159
157
 
160
158
  // Close on escape
161
159
  await userEvent.keyboard('{escape}');
162
- expect(imageLightbox).not.toBeInTheDocument();
160
+ await waitFor(() => {
161
+ expect(imageLightbox).not.toBeInTheDocument();
162
+ });
163
163
 
164
164
  // Focus moved back to the trigger button
165
165
  expect(buttonTrigger).toHaveFocus();
@@ -185,7 +185,9 @@ describe(`<${ImageLightbox.displayName}>`, () => {
185
185
 
186
186
  // Close on escape
187
187
  await userEvent.keyboard('{escape}');
188
- expect(imageLightbox).not.toBeInTheDocument();
188
+ await waitFor(() => {
189
+ expect(imageLightbox).not.toBeInTheDocument();
190
+ });
189
191
 
190
192
  // Focus moved back to the trigger button
191
193
  expect(buttonTrigger).toHaveFocus();
@@ -196,7 +198,7 @@ describe(`<${ImageLightbox.displayName}>`, () => {
196
198
  const scrollAreaSize = { width: 600, height: 600 };
197
199
  beforeEach(() => {
198
200
  (useImageSize as any).mockImplementation((_: any, getInitialSize: any) => getInitialSize?.() || null);
199
- (useSizeOnWindowResize as any).mockReturnValue([scrollAreaSize, jest.fn()]);
201
+ (useSizeOnWindowResize as any).mockReturnValue([scrollAreaSize, vi.fn()]);
200
202
  });
201
203
 
202
204
  it('should use the image initial size', () => {
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import type { IconButtonProps, LightboxProps, SlideshowProps, ThumbnailProps } from '@lumx/react';
4
2
  import type { HasClassName } from '@lumx/react/utils/type';
5
3
  import type { ImageCaptionMetadata } from '@lumx/react/components/image-block/ImageCaption';
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { mdiEarth } from '@lumx/icons';
3
2
  import { Icon, Text } from '@lumx/react';
4
3
  import { withResizableBox } from '@lumx/react/stories/decorators/withResizableBox';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
4
2
  import { render, screen, within } from '@testing-library/react';
5
3
  import { queryByClassName } from '@lumx/react/testing/utils/queries';
@@ -1,4 +1,4 @@
1
- import React, { Children, isValidElement } from 'react';
1
+ import { Children, isValidElement } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { Kind, Theme } from '@lumx/react';
4
2
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
5
3
  import { getByClassName } from '@lumx/react/testing/utils/queries';
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import { ReactNode } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { Typography } from '@lumx/react';
4
2
 
5
3
  import { InputLabel } from './InputLabel';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { Theme, Typography } from '@lumx/react';
4
2
  import { getByClassName } from '@lumx/react/testing/utils/queries';
5
3
  import { getTypographyClassName } from '@lumx/core/js/utils/className';
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import { ReactNode } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
4
2
  import { queryByClassName } from '@lumx/react/testing/utils/queries';
5
3
  import { render, screen } from '@testing-library/react';
@@ -1,4 +1,4 @@
1
- import React, { RefObject, useRef, useEffect, AriaAttributes } from 'react';
1
+ import { RefObject, useRef, useEffect, AriaAttributes } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -10,7 +10,6 @@ import {
10
10
  TypographyTitleCustom,
11
11
  GenericBlock,
12
12
  } from '@lumx/react';
13
- import React from 'react';
14
13
  import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
15
14
  import { colorArgType, colorVariantArgType } from '@lumx/react/stories/controls/color';
16
15
  import { iconArgType } from '@lumx/react/stories/controls/icons';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { ColorPalette, ColorVariant, Icon, Typography } from '@lumx/react';
4
2
  import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
5
3
  import { getByClassName, queryAllByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
@@ -51,7 +49,7 @@ describe(`<${Link.displayName}>`, () => {
51
49
 
52
50
  it('should render a button', () => {
53
51
  const name = 'Link';
54
- const onClick = jest.fn();
52
+ const onClick = vi.fn();
55
53
  const { link } = setup({ onClick, children: name });
56
54
  expect(link).toBe(screen.queryByRole('button', { name }));
57
55
  });
@@ -75,7 +73,7 @@ describe(`<${Link.displayName}>`, () => {
75
73
 
76
74
  describe('Disabled state', () => {
77
75
  it('should render disabled button', async () => {
78
- const onClick = jest.fn();
76
+ const onClick = vi.fn();
79
77
  const { link } = setup({ children: 'Label', isDisabled: true, onClick });
80
78
  expect(link).toHaveAttribute('disabled');
81
79
  await userEvent.click(link);
@@ -83,25 +81,28 @@ describe(`<${Link.displayName}>`, () => {
83
81
  });
84
82
 
85
83
  it('should render disabled link', async () => {
86
- const onClick = jest.fn();
84
+ const onClick = vi.fn();
87
85
  const { link } = setup({ children: 'Label', isDisabled: true, href: 'https://example.com', onClick });
88
- // Disabled link do not exist so we fallback to a button
89
- expect(screen.queryByRole('link')).not.toBeInTheDocument();
90
- expect(link).toHaveAttribute('disabled');
86
+ expect(screen.queryByRole('link')).toBeInTheDocument();
87
+ expect(link).toHaveAttribute('aria-disabled');
88
+ // Simulate standard disabled state (not focusable)
89
+ expect(link).toHaveAttribute('tabindex', '-1');
91
90
  await userEvent.click(link);
92
91
  expect(onClick).not.toHaveBeenCalled();
93
92
  });
94
93
 
95
94
  it('should render aria-disabled button', async () => {
96
- const onClick = jest.fn();
95
+ const onClick = vi.fn();
97
96
  const { link } = setup({ children: 'Label', 'aria-disabled': true, onClick });
98
- expect(link).toHaveAttribute('aria-disabled');
97
+ expect(screen.queryByRole('button')).toBeInTheDocument();
98
+ expect(link).toHaveAttribute('aria-disabled', 'true');
99
+ expect(link).not.toHaveAttribute('tabindex');
99
100
  await userEvent.click(link);
100
101
  expect(onClick).not.toHaveBeenCalled();
101
102
  });
102
103
 
103
104
  it('should render aria-disabled link', async () => {
104
- const onClick = jest.fn();
105
+ const onClick = vi.fn();
105
106
  const { link } = setup({
106
107
  children: 'Label',
107
108
  'aria-disabled': true,
@@ -109,8 +110,7 @@ describe(`<${Link.displayName}>`, () => {
109
110
  onClick,
110
111
  });
111
112
  expect(link).toHaveAccessibleName('Label');
112
- // Disabled link do not exist so we fallback to a button
113
- expect(screen.queryByRole('link')).not.toBeInTheDocument();
113
+ expect(screen.queryByRole('link')).toBeInTheDocument();
114
114
  expect(link).toHaveAttribute('aria-disabled', 'true');
115
115
  await userEvent.click(link);
116
116
  expect(onClick).not.toHaveBeenCalled();
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import classNames from 'classnames';
4
2
 
5
3
  import { ColorVariant, ColorWithVariants, Icon, Typography } from '@lumx/react';
@@ -12,8 +10,9 @@ import {
12
10
  } from '@lumx/core/js/utils/className';
13
11
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
14
12
  import { wrapChildrenIconWithSpaces } from '@lumx/react/utils/react/wrapChildrenIconWithSpaces';
15
- import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
16
13
  import { HasAriaDisabled } from '@lumx/react/utils/type/HasAriaDisabled';
14
+ import { RawClickable } from '@lumx/react/utils/react/RawClickable';
15
+ import { useDisableStateProps } from '@lumx/react/utils/disabled';
17
16
 
18
17
  type HTMLAnchorProps = React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
19
18
 
@@ -67,38 +66,26 @@ const CLASSNAME = getRootClassName(COMPONENT_NAME);
67
66
  * @return React element.
68
67
  */
69
68
  export const Link = forwardRef<LinkProps, HTMLAnchorElement | HTMLButtonElement>((props, ref) => {
70
- const { isAnyDisabled, disabledStateProps, otherProps } = useDisableStateProps(props);
69
+ const { disabledStateProps, otherProps } = useDisableStateProps(props);
71
70
  const {
72
71
  children,
73
72
  className,
74
73
  color: propColor,
75
74
  colorVariant: propColorVariant,
76
- href,
77
75
  leftIcon,
78
- linkAs,
79
76
  rightIcon,
80
- target,
81
77
  typography,
78
+ linkAs,
82
79
  ...forwardedProps
83
80
  } = otherProps;
84
81
  const [color, colorVariant] = resolveColorWithVariants(propColor, propColorVariant);
85
82
 
86
- const isLink = linkAs || href;
87
- const Component = isLink && !isAnyDisabled ? linkAs || 'a' : 'button';
88
- const baseProps: React.ComponentProps<typeof Component> = {};
89
- if (Component === 'button') {
90
- baseProps.type = 'button';
91
- Object.assign(baseProps, disabledStateProps);
92
- } else if (isLink) {
93
- baseProps.href = href;
94
- baseProps.target = target;
95
- }
96
-
97
83
  return (
98
- <Component
99
- ref={ref}
84
+ <RawClickable
85
+ ref={ref as any}
86
+ as={linkAs || (forwardedProps.href ? 'a' : 'button')}
100
87
  {...forwardedProps}
101
- {...baseProps}
88
+ {...disabledStateProps}
102
89
  className={classNames(
103
90
  className,
104
91
  handleBasicClasses({ prefix: CLASSNAME, color, colorVariant, hasTypography: !!typography }),
@@ -112,7 +99,7 @@ export const Link = forwardRef<LinkProps, HTMLAnchorElement | HTMLButtonElement>
112
99
  {rightIcon && <Icon icon={rightIcon} className={`${CLASSNAME}__right-icon`} />}
113
100
  </>,
114
101
  )}
115
- </Component>
102
+ </RawClickable>
116
103
  );
117
104
  });
118
105
  Link.displayName = COMPONENT_NAME;
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
4
2
  import { Size, Theme } from '@lumx/core/js/constants';
5
3
  import { Thumbnail } from '@lumx/react';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import classNames from 'classnames';
4
2
 
5
3
  import {
@@ -1,4 +1,4 @@
1
- import React, { useRef } from 'react';
1
+ import { useRef } from 'react';
2
2
 
3
3
  import { mdiAccount, mdiOpenInNew } from '@lumx/icons';
4
4
  import { Icon, ListDivider, ListProps, ListSubheader, Size } from '@lumx/react';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
4
2
 
5
3
  import { render } from '@testing-library/react';
@@ -1,4 +1,4 @@
1
- import React, { Key, ReactNode, SyntheticEvent, useRef } from 'react';
1
+ import { Key, ReactNode, SyntheticEvent, useRef } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
4
2
 
5
3
  import { queryByClassName } from '@lumx/react/testing/utils/queries';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import classNames from 'classnames';
4
2
 
5
3
  import { GenericProps } from '@lumx/react/utils/type';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
4
2
  import { render, screen } from '@testing-library/react';
5
3
  import { getByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
@@ -29,7 +27,7 @@ describe(`<${ListItem.displayName}>`, () => {
29
27
  });
30
28
 
31
29
  it('should render as a button', () => {
32
- setup({ children: 'Label', onItemSelected: jest.fn() });
30
+ setup({ children: 'Label', onItemSelected: vi.fn() });
33
31
  expect(screen.getByRole('button', { name: 'Label' })).toBeInTheDocument();
34
32
  });
35
33
 
@@ -41,7 +39,7 @@ describe(`<${ListItem.displayName}>`, () => {
41
39
 
42
40
  describe('Disabled state', () => {
43
41
  it('should render disabled list item button', async () => {
44
- const onItemSelected = jest.fn();
42
+ const onItemSelected = vi.fn();
45
43
  const { link } = setup({ children: 'Label', isDisabled: true, onItemSelected });
46
44
  expect(link).toHaveAttribute('aria-disabled', 'true');
47
45
  // The `renderLink` util removes the onClick handler but `user-event` will also not fire events on disabled elements.
@@ -50,7 +48,7 @@ describe(`<${ListItem.displayName}>`, () => {
50
48
  });
51
49
 
52
50
  it('should render disabled list item link', async () => {
53
- const onItemSelected = jest.fn();
51
+ const onItemSelected = vi.fn();
54
52
  const { link } = setup({
55
53
  children: 'Label',
56
54
  isDisabled: true,
@@ -64,7 +62,7 @@ describe(`<${ListItem.displayName}>`, () => {
64
62
  });
65
63
 
66
64
  it('should render aria-disabled list item button', async () => {
67
- const onItemSelected = jest.fn();
65
+ const onItemSelected = vi.fn();
68
66
  const { link } = setup({ children: 'Label', 'aria-disabled': true, onItemSelected });
69
67
  expect(link).toHaveAttribute('aria-disabled', 'true');
70
68
  if (link) await userEvent.click(link);
@@ -72,7 +70,7 @@ describe(`<${ListItem.displayName}>`, () => {
72
70
  });
73
71
 
74
72
  it('should render aria-disabled list item link', async () => {
75
- const onItemSelected = jest.fn();
73
+ const onItemSelected = vi.fn();
76
74
  const { link } = setup({
77
75
  children: 'Label',
78
76
  'aria-disabled': true,
@@ -1,4 +1,4 @@
1
- import React, { ReactNode, Ref, SyntheticEvent, useMemo } from 'react';
1
+ import { ReactNode, Ref, SyntheticEvent, useMemo } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
  import isEmpty from 'lodash/isEmpty';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
4
2
  import { render } from '@testing-library/react';
5
3
  import { queryByClassName } from '@lumx/react/testing/utils/queries';
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import { ReactNode } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -1,7 +1,6 @@
1
1
  import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
2
2
  import { Kind } from '@lumx/react';
3
3
 
4
- import React from 'react';
5
4
  import { queryByRole, render } from '@testing-library/react';
6
5
  import { getByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
7
6
  import { mdiAbTesting } from '@lumx/icons';
@@ -54,7 +53,7 @@ describe(`<${Message.displayName}>`, () => {
54
53
  });
55
54
 
56
55
  it('should render close button', async () => {
57
- const onClick = jest.fn();
56
+ const onClick = vi.fn();
58
57
  const { closeButton } = setup({
59
58
  hasBackground: true,
60
59
  kind: 'info',
@@ -1,4 +1,4 @@
1
- import React, { ReactNode } from 'react';
1
+ import { ReactNode } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import { Mosaic, MosaicProps } from '@lumx/react/components/mosaic/Mosaic';
4
2
  import { render, screen, within } from '@testing-library/react';
5
3
  import { getByClassName, queryAllByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
@@ -32,7 +30,7 @@ describe(`<${Mosaic.displayName}>`, () => {
32
30
  expect(mosaic).toHaveClass(`${CLASSNAME}--has-${count}-thumbnail${count > 1 ? 's' : ''}`);
33
31
  expect(thumbnails.length).toBe(count);
34
32
  for (const thumbnail of thumbnails) {
35
- expect(within(thumbnail).queryByRole('img')).toBeInTheDocument();
33
+ expect(within(thumbnail).queryByAltText('')).toBeInTheDocument();
36
34
  }
37
35
  });
38
36
 
@@ -47,8 +45,8 @@ describe(`<${Mosaic.displayName}>`, () => {
47
45
  });
48
46
 
49
47
  it('should render clickable', async () => {
50
- const onClick = jest.fn();
51
- const onImageClick = jest.fn();
48
+ const onClick = vi.fn();
49
+ const onImageClick = vi.fn();
52
50
  const { thumbnails } = setup({
53
51
  thumbnails: generateThumbnails(6),
54
52
  onImageClick,
@@ -1,4 +1,4 @@
1
- import React, { MouseEventHandler, useMemo } from 'react';
1
+ import { MouseEventHandler, useMemo } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
  import take from 'lodash/take';
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
-
3
1
  import {
4
2
  mdiHome,
5
3
  mdiMessageTextOutline,