@lumx/react 3.10.0 → 3.10.1-alpha.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 (146) hide show
  1. package/_internal/index.d.ts +1 -1
  2. package/index.d.ts +24 -16
  3. package/index.js +313 -293
  4. package/index.js.map +1 -1
  5. package/package.json +3 -3
  6. package/src/components/alert-dialog/AlertDialog.tsx +3 -4
  7. package/src/components/autocomplete/Autocomplete.test.tsx +9 -2
  8. package/src/components/autocomplete/Autocomplete.tsx +7 -4
  9. package/src/components/autocomplete/AutocompleteMultiple.test.tsx +9 -2
  10. package/src/components/autocomplete/AutocompleteMultiple.tsx +10 -7
  11. package/src/components/avatar/Avatar.test.tsx +14 -4
  12. package/src/components/avatar/Avatar.tsx +7 -5
  13. package/src/components/badge/Badge.tsx +7 -4
  14. package/src/components/badge/BadgeWrapper.tsx +7 -4
  15. package/src/components/button/Button.test.tsx +9 -3
  16. package/src/components/button/Button.tsx +8 -5
  17. package/src/components/button/ButtonGroup.tsx +4 -3
  18. package/src/components/button/ButtonRoot.tsx +4 -3
  19. package/src/components/button/IconButton.test.tsx +9 -3
  20. package/src/components/button/IconButton.tsx +16 -5
  21. package/src/components/checkbox/Checkbox.test.tsx +9 -3
  22. package/src/components/checkbox/Checkbox.tsx +8 -7
  23. package/src/components/chip/Chip.test.tsx +14 -4
  24. package/src/components/chip/Chip.tsx +11 -11
  25. package/src/components/chip/ChipGroup.tsx +5 -4
  26. package/src/components/comment-block/CommentBlock.test.tsx +9 -3
  27. package/src/components/comment-block/CommentBlock.tsx +7 -6
  28. package/src/components/date-picker/DatePicker.tsx +5 -3
  29. package/src/components/date-picker/DatePickerControlled.tsx +6 -3
  30. package/src/components/date-picker/DatePickerField.test.tsx +9 -3
  31. package/src/components/date-picker/DatePickerField.tsx +4 -3
  32. package/src/components/dialog/Dialog.test.tsx +17 -4
  33. package/src/components/dialog/Dialog.tsx +65 -61
  34. package/src/components/divider/Divider.test.tsx +9 -3
  35. package/src/components/divider/Divider.tsx +8 -7
  36. package/src/components/drag-handle/DragHandle.test.tsx +38 -0
  37. package/src/components/drag-handle/DragHandle.tsx +7 -4
  38. package/src/components/dropdown/Dropdown.tsx +4 -3
  39. package/src/components/expansion-panel/ExpansionPanel.test.tsx +12 -3
  40. package/src/components/expansion-panel/ExpansionPanel.tsx +8 -7
  41. package/src/components/flag/Flag.test.tsx +14 -4
  42. package/src/components/flag/Flag.tsx +9 -7
  43. package/src/components/flex-box/FlexBox.tsx +8 -5
  44. package/src/components/generic-block/GenericBlock.tsx +4 -1
  45. package/src/components/grid/Grid.tsx +4 -3
  46. package/src/components/grid/GridItem.tsx +4 -3
  47. package/src/components/grid-column/GridColumn.tsx +5 -5
  48. package/src/components/heading/Heading.tsx +8 -4
  49. package/src/components/icon/Icon.test.tsx +13 -4
  50. package/src/components/icon/Icon.tsx +18 -5
  51. package/src/components/image-block/ImageBlock.test.tsx +12 -4
  52. package/src/components/image-block/ImageBlock.tsx +7 -5
  53. package/src/components/image-lightbox/ImageLightbox.tsx +4 -3
  54. package/src/components/inline-list/InlineList.tsx +4 -3
  55. package/src/components/input-helper/InputHelper.test.tsx +14 -4
  56. package/src/components/input-helper/InputHelper.tsx +10 -6
  57. package/src/components/input-label/InputLabel.test.tsx +14 -4
  58. package/src/components/input-label/InputLabel.tsx +11 -8
  59. package/src/components/lightbox/Lightbox.test.tsx +17 -6
  60. package/src/components/lightbox/Lightbox.tsx +12 -8
  61. package/src/components/link/Link.tsx +4 -3
  62. package/src/components/link-preview/LinkPreview.test.tsx +9 -3
  63. package/src/components/link-preview/LinkPreview.tsx +7 -5
  64. package/src/components/list/List.tsx +7 -5
  65. package/src/components/list/ListDivider.tsx +4 -3
  66. package/src/components/list/ListItem.tsx +4 -3
  67. package/src/components/list/ListSubheader.tsx +4 -3
  68. package/src/components/message/Message.tsx +7 -4
  69. package/src/components/mosaic/Mosaic.test.tsx +9 -3
  70. package/src/components/mosaic/Mosaic.tsx +11 -9
  71. package/src/components/navigation/Navigation.test.tsx +18 -9
  72. package/src/components/navigation/Navigation.tsx +13 -5
  73. package/src/components/navigation/NavigationItem.tsx +4 -4
  74. package/src/components/navigation/NavigationSection.test.tsx +19 -6
  75. package/src/components/navigation/NavigationSection.tsx +4 -4
  76. package/src/components/notification/Notification.tsx +7 -8
  77. package/src/components/popover/Popover.test.tsx +18 -4
  78. package/src/components/popover/Popover.tsx +5 -3
  79. package/src/components/popover-dialog/PopoverDialog.tsx +5 -3
  80. package/src/components/post-block/PostBlock.test.tsx +9 -3
  81. package/src/components/post-block/PostBlock.tsx +7 -8
  82. package/src/components/progress/Progress.tsx +8 -6
  83. package/src/components/progress/ProgressCircular.test.tsx +9 -16
  84. package/src/components/progress/ProgressCircular.tsx +7 -6
  85. package/src/components/progress/ProgressLinear.test.tsx +13 -18
  86. package/src/components/progress/ProgressLinear.tsx +8 -8
  87. package/src/components/progress-tracker/ProgressTracker.tsx +5 -3
  88. package/src/components/progress-tracker/ProgressTrackerStep.tsx +5 -5
  89. package/src/components/progress-tracker/ProgressTrackerStepPanel.tsx +24 -24
  90. package/src/components/radio-button/RadioButton.test.tsx +9 -3
  91. package/src/components/radio-button/RadioButton.tsx +8 -8
  92. package/src/components/radio-button/RadioGroup.tsx +4 -3
  93. package/src/components/select/Select.test.tsx +9 -3
  94. package/src/components/select/Select.tsx +30 -28
  95. package/src/components/select/SelectMultiple.test.tsx +9 -3
  96. package/src/components/select/SelectMultiple.tsx +112 -108
  97. package/src/components/select/WithSelectContext.tsx +8 -6
  98. package/src/components/side-navigation/SideNavigation.tsx +7 -5
  99. package/src/components/side-navigation/SideNavigationItem.tsx +4 -5
  100. package/src/components/skeleton/SkeletonCircle.test.tsx +9 -3
  101. package/src/components/skeleton/SkeletonCircle.tsx +9 -7
  102. package/src/components/skeleton/SkeletonRectangle.test.tsx +9 -3
  103. package/src/components/skeleton/SkeletonRectangle.tsx +8 -5
  104. package/src/components/skeleton/SkeletonTypography.test.tsx +9 -3
  105. package/src/components/skeleton/SkeletonTypography.tsx +9 -7
  106. package/src/components/slider/Slider.test.tsx +9 -3
  107. package/src/components/slider/Slider.tsx +7 -7
  108. package/src/components/slideshow/Slides.tsx +9 -5
  109. package/src/components/slideshow/Slideshow.test.tsx +9 -3
  110. package/src/components/slideshow/Slideshow.tsx +8 -5
  111. package/src/components/slideshow/SlideshowControls.tsx +7 -5
  112. package/src/components/slideshow/SlideshowItem.tsx +4 -3
  113. package/src/components/slideshow/SlideshowItemGroup.tsx +5 -4
  114. package/src/components/switch/Switch.test.tsx +9 -3
  115. package/src/components/switch/Switch.tsx +7 -7
  116. package/src/components/table/Table.test.tsx +9 -3
  117. package/src/components/table/Table.tsx +8 -8
  118. package/src/components/table/TableBody.tsx +4 -3
  119. package/src/components/table/TableCell.tsx +5 -5
  120. package/src/components/table/TableHeader.tsx +4 -3
  121. package/src/components/table/TableRow.tsx +4 -3
  122. package/src/components/tabs/Tab.tsx +4 -3
  123. package/src/components/tabs/TabList.test.tsx +9 -3
  124. package/src/components/tabs/TabList.tsx +18 -6
  125. package/src/components/tabs/TabPanel.tsx +7 -5
  126. package/src/components/text/Text.tsx +6 -4
  127. package/src/components/text-field/TextField.test.tsx +9 -3
  128. package/src/components/text-field/TextField.tsx +7 -5
  129. package/src/components/thumbnail/Thumbnail.test.tsx +9 -3
  130. package/src/components/thumbnail/Thumbnail.tsx +8 -7
  131. package/src/components/toolbar/Toolbar.tsx +4 -3
  132. package/src/components/tooltip/Tooltip.tsx +4 -3
  133. package/src/components/uploader/Uploader.test.tsx +9 -3
  134. package/src/components/uploader/Uploader.tsx +18 -5
  135. package/src/components/user-block/UserBlock.test.tsx +9 -3
  136. package/src/components/user-block/UserBlock.tsx +9 -6
  137. package/src/index.ts +1 -0
  138. package/src/testing/utils/ThemeSentinel.tsx +11 -0
  139. package/src/testing/utils/commonTestsSuiteRTL.tsx +191 -0
  140. package/src/utils/react/forwardRef.ts +10 -0
  141. package/src/utils/react/forwardRefPolymorphic.ts +10 -0
  142. package/src/utils/theme/ThemeContext.ts +16 -0
  143. package/src/utils/theme/invertTheme.ts +4 -0
  144. package/src/testing/utils/commonTestsSuiteRTL.ts +0 -64
  145. package/src/utils/ThemeContext.ts +0 -4
  146. package/src/utils/forwardRefPolymorphic.ts +0 -9
@@ -0,0 +1,191 @@
1
+ import isEmpty from 'lodash/isEmpty';
2
+
3
+ import { GenericProps } from '@lumx/react/utils/type';
4
+ import { queryByClassName } from '@lumx/react/testing/utils/queries';
5
+ import React from 'react';
6
+ import { Theme } from '@lumx/react';
7
+ import { RenderOptions } from '@testing-library/react';
8
+ import { ThemeProvider } from '@lumx/react/utils/theme/ThemeContext';
9
+ import castArray from 'lodash/castArray';
10
+ import { invertTheme } from '@lumx/react/utils/theme/invertTheme';
11
+
12
+ interface CommonSetup {
13
+ props: GenericProps;
14
+ }
15
+
16
+ type AffectConfig<E> = { element: E; classModifier?: 'color' | 'theme'; inverted?: boolean };
17
+ type Not<E> = { not: E };
18
+
19
+ interface Options<S extends CommonSetup> {
20
+ baseClassName: string;
21
+ forwardClassName?: keyof S;
22
+ forwardAttributes?: keyof S;
23
+ forwardRef?: keyof S;
24
+ applyTheme?: {
25
+ /** Element(s) to which we apply the theme class */
26
+ affects: Array<AffectConfig<keyof S> | Not<AffectConfig<keyof S>>>;
27
+ /** Apply theme via theme prop */
28
+ viaProp: boolean;
29
+ /** Apply theme via theme context */
30
+ viaContext: boolean;
31
+ /** Apply a default theme if no prop or context was provided */
32
+ defaultTheme?: Theme;
33
+ };
34
+ }
35
+
36
+ export type RenderWrapper = RenderOptions['wrapper'];
37
+ export type SetupRenderOptions = { wrapper?: RenderWrapper };
38
+ export type SetupFunction<S extends CommonSetup> = (
39
+ props?: GenericProps,
40
+ options?: SetupRenderOptions,
41
+ ) => S | Promise<S>;
42
+
43
+ /**
44
+ * Common tests on components
45
+ * - Check base class name and class name forwarding
46
+ * - Check props forwarding
47
+ */
48
+ export function commonTestsSuiteRTL<S extends CommonSetup>(setup: SetupFunction<S>, options: Options<S>): void {
49
+ if (isEmpty(options)) {
50
+ return;
51
+ }
52
+ const { baseClassName, forwardClassName, forwardAttributes, forwardRef, applyTheme } = options;
53
+ describe('Common tests suite', () => {
54
+ it('should render with base class name', async () => {
55
+ await setup();
56
+ expect(queryByClassName(document.body, baseClassName)).toBeInTheDocument();
57
+ });
58
+
59
+ if (forwardClassName) {
60
+ it('should forward any CSS class', async () => {
61
+ const modifiedProps = {
62
+ className: 'component component--is-tested',
63
+ };
64
+ const wrappers = await setup(modifiedProps);
65
+ expect(wrappers[forwardClassName]).toHaveClass(modifiedProps.className);
66
+ });
67
+ }
68
+
69
+ if (forwardAttributes) {
70
+ it('should forward any other prop', async () => {
71
+ const modifiedProps = {
72
+ winter: 'is coming',
73
+ };
74
+ const wrappers = await setup(modifiedProps);
75
+ expect(wrappers[forwardAttributes]).toHaveAttribute('winter', modifiedProps.winter);
76
+ });
77
+ }
78
+
79
+ if (forwardRef) {
80
+ it('should forward ref', async () => {
81
+ const ref = React.createRef();
82
+ const wrappers = await setup({ ref });
83
+ expect(ref.current).toBe(wrappers[forwardRef]);
84
+ });
85
+ }
86
+
87
+ if (applyTheme) {
88
+ describe('theme', () => {
89
+ const { affects, defaultTheme, viaProp, viaContext } = applyTheme;
90
+ const testElements = affects.map((configOrNot) => {
91
+ let shouldHaveModifier: boolean = true;
92
+ let config: AffectConfig<any>;
93
+ if ('not' in configOrNot) {
94
+ shouldHaveModifier = false;
95
+ config = configOrNot.not;
96
+ } else config = configOrNot;
97
+
98
+ const {
99
+ element,
100
+ classModifier = 'theme',
101
+ inverted = false,
102
+ }: AffectConfig<any> = typeof config === 'object' ? config : { element: config };
103
+ return {
104
+ element,
105
+ getExpectedClassModifier: (theme: Theme) =>
106
+ `--${classModifier}-${inverted ? invertTheme(theme) : theme}`,
107
+ shouldHaveModifier,
108
+ apply: shouldHaveModifier ? 'apply' : 'not apply',
109
+ };
110
+ });
111
+
112
+ const expectTheme = (
113
+ wrappers: any,
114
+ { element, getExpectedClassModifier, shouldHaveModifier }: any,
115
+ theme: Theme,
116
+ override: { shouldHaveModifier?: boolean } = {},
117
+ ) => {
118
+ for (const wrapper of castArray(wrappers[element])) {
119
+ let expected: any = expect(wrapper.className);
120
+ if (override.shouldHaveModifier === false || !shouldHaveModifier) {
121
+ expected = expected.not;
122
+ }
123
+ expected.toContain(getExpectedClassModifier(theme));
124
+ }
125
+ };
126
+
127
+ if (defaultTheme) {
128
+ it.each(testElements)(
129
+ `should $apply default theme (${defaultTheme}) to \`$element\``,
130
+ async (affectedElement) => {
131
+ const wrappers = await setup();
132
+ expectTheme(wrappers, affectedElement, defaultTheme);
133
+ },
134
+ );
135
+ }
136
+
137
+ // Only the elements that are affected by theme
138
+ const affectedElements = testElements.filter((e) => e.shouldHaveModifier);
139
+ if (!defaultTheme && affectedElements.length) {
140
+ it.each(affectedElements)(
141
+ `should not apply default theme (${defaultTheme}) to \`$element\``,
142
+ async (affectedElement) => {
143
+ const wrappers = await setup();
144
+ expectTheme(wrappers, affectedElement, Theme.light, { shouldHaveModifier: false });
145
+ expectTheme(wrappers, affectedElement, Theme.dark, { shouldHaveModifier: false });
146
+ },
147
+ );
148
+ }
149
+
150
+ if (viaProp) {
151
+ const theme = invertTheme(defaultTheme || Theme.light);
152
+ it.each(testElements)(
153
+ `should $apply prop theme=${theme} to \`$element\``,
154
+ async (affectedElement) => {
155
+ const wrappers = await setup({ theme });
156
+ expectTheme(wrappers, affectedElement, theme);
157
+ },
158
+ );
159
+ }
160
+
161
+ const contextTheme = invertTheme(defaultTheme || Theme.light);
162
+ it.each(testElements)(
163
+ `should $apply context theme=${contextTheme} to \`$element\``,
164
+ async (affectedElement) => {
165
+ const Wrapper = ({ children }: any) => (
166
+ <ThemeProvider value={contextTheme}>{children}</ThemeProvider>
167
+ );
168
+ const wrappers = await setup({}, { wrapper: Wrapper });
169
+ expectTheme(wrappers, affectedElement, contextTheme, {
170
+ shouldHaveModifier: !viaContext ? false : undefined,
171
+ });
172
+ },
173
+ );
174
+
175
+ if (viaProp && viaContext) {
176
+ const propTheme = invertTheme(contextTheme);
177
+ it.each(testElements)(
178
+ `should $apply prop theme=${propTheme} to \`$element\` overriding the context theme=${contextTheme}`,
179
+ async (affectedElement) => {
180
+ const Wrapper = ({ children }: any) => (
181
+ <ThemeProvider value={contextTheme}>{children}</ThemeProvider>
182
+ );
183
+ const wrappers = await setup({ theme: propTheme }, { wrapper: Wrapper });
184
+ expectTheme(wrappers, affectedElement, propTheme);
185
+ },
186
+ );
187
+ }
188
+ });
189
+ }
190
+ });
191
+ }
@@ -0,0 +1,10 @@
1
+ import React, { type ReactNode, type ForwardedRef } from 'react';
2
+ import type { Comp } from '../type';
3
+
4
+ type ForwardRef = <P, T = HTMLElement>(render: (props: P, ref: ForwardedRef<T>) => ReactNode) => Comp<P, T>;
5
+
6
+ /**
7
+ * React.forwardRef but re-typed to attach some custom metadata on our components.
8
+ */
9
+ /*#__NO_SIDE_EFFECTS__*/
10
+ export const forwardRef = React.forwardRef as ForwardRef;
@@ -0,0 +1,10 @@
1
+ import React, { type ElementType } from 'react';
2
+ import type { ComponentRef } from '@lumx/react/utils/type';
3
+
4
+ type ForwardRefPolymorphic = <E extends ElementType, P extends { as?: E }>(
5
+ render: (props: P, ref: ComponentRef<E>) => React.ReactNode,
6
+ ) => (props: P & { ref?: ComponentRef<E> }) => React.ReactNode;
7
+
8
+ /** Same as `React.forwardRef` but inferring Ref type from the `as` prop. */
9
+ /*#__NO_SIDE_EFFECTS__*/
10
+ export const forwardRefPolymorphic = React.forwardRef as ForwardRefPolymorphic;
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import type { Theme } from '@lumx/react';
3
+
4
+ type ThemeContextValue = Theme | undefined;
5
+ export const ThemeContext = React.createContext<ThemeContextValue>(undefined);
6
+
7
+ /** Provide a theme context to all children. */
8
+ export const ThemeProvider = ThemeContext.Provider as React.FC<{
9
+ value: ThemeContextValue;
10
+ children?: React.ReactNode;
11
+ }>;
12
+
13
+ /** Get the theme in the current context. */
14
+ export function useTheme(): ThemeContextValue {
15
+ return React.useContext(ThemeContext);
16
+ }
@@ -0,0 +1,4 @@
1
+ import type { Theme } from '@lumx/react';
2
+
3
+ /** Invert the color of the given theme. */
4
+ export const invertTheme = (theme: Theme): Theme => (theme === 'light' ? 'dark' : 'light');
@@ -1,64 +0,0 @@
1
- import isEmpty from 'lodash/isEmpty';
2
-
3
- import { GenericProps } from '@lumx/react/utils/type';
4
- import { queryByClassName } from '@lumx/react/testing/utils/queries';
5
- import React from 'react';
6
-
7
- interface CommonSetup {
8
- props: GenericProps;
9
- }
10
-
11
- interface Options<S extends CommonSetup> {
12
- baseClassName: string;
13
- forwardClassName?: keyof S;
14
- forwardAttributes?: keyof S;
15
- forwardRef?: keyof S;
16
- }
17
-
18
- type SetupFunction<S extends CommonSetup> = (props?: GenericProps) => S | Promise<S>;
19
-
20
- /**
21
- * Common tests on components
22
- * - Check base class name and class name forwarding
23
- * - Check props forwarding
24
- */
25
- export function commonTestsSuiteRTL<S extends CommonSetup>(setup: SetupFunction<S>, options: Options<S>): void {
26
- if (isEmpty(options)) {
27
- return;
28
- }
29
- const { baseClassName, forwardClassName, forwardAttributes, forwardRef } = options;
30
- describe('Common tests suite', () => {
31
- it('should render with base class name', async () => {
32
- await setup();
33
- expect(queryByClassName(document.body, baseClassName)).toBeInTheDocument();
34
- });
35
-
36
- if (forwardClassName) {
37
- it('should forward any CSS class', async () => {
38
- const modifiedProps = {
39
- className: 'component component--is-tested',
40
- };
41
- const wrappers = await setup(modifiedProps);
42
- expect(wrappers[forwardClassName]).toHaveClass(modifiedProps.className);
43
- });
44
- }
45
-
46
- if (forwardAttributes) {
47
- it('should forward any other prop', async () => {
48
- const modifiedProps = {
49
- winter: 'is coming',
50
- };
51
- const wrappers = await setup(modifiedProps);
52
- expect(wrappers[forwardAttributes]).toHaveAttribute('winter', modifiedProps.winter);
53
- });
54
- }
55
-
56
- if (forwardRef) {
57
- it('should forward ref', async () => {
58
- const ref = React.createRef();
59
- const wrappers = await setup({ ref });
60
- expect(ref.current).toBe(wrappers[forwardRef]);
61
- });
62
- }
63
- });
64
- }
@@ -1,4 +0,0 @@
1
- import { createContext } from 'react';
2
- import { Theme } from '..';
3
-
4
- export const ThemeContext = createContext<Theme | undefined>(undefined);
@@ -1,9 +0,0 @@
1
- import React, { ElementType } from 'react';
2
- import { ComponentRef } from '@lumx/react/utils/type';
3
-
4
- /** Same as `React.forwardRef` but inferring Ref type from the `as` prop. */
5
- export function forwardRefPolymorphic<E extends ElementType, P extends { as?: E }>(
6
- render: (props: P, ref: ComponentRef<E>) => React.ReactElement | null,
7
- ): (props: P & { ref?: ComponentRef<E> }) => React.ReactElement | null {
8
- return React.forwardRef(render as any) as any;
9
- }