@lumx/react 3.8.2-alpha.1 → 3.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +1 -1
- package/index.js +55 -41
- package/index.js.map +1 -1
- package/package.json +4 -5
- package/src/components/alert-dialog/AlertDialog.test.tsx +1 -1
- package/src/components/alert-dialog/AlertDialog.tsx +3 -2
- package/src/components/checkbox/Checkbox.tsx +4 -3
- package/src/components/date-picker/DatePickerControlled.tsx +2 -3
- package/src/components/image-lightbox/ImageLightbox.test.tsx +10 -18
- package/src/components/image-lightbox/internal/ImageSlide.tsx +2 -2
- package/src/components/message/Message.tsx +1 -1
- package/src/components/navigation/NavigationSection.tsx +4 -3
- package/src/components/notification/constants.ts +1 -1
- package/src/components/radio-button/RadioButton.tsx +4 -3
- package/src/components/select/Select.test.tsx +1 -1
- package/src/components/select/SelectMultiple.test.tsx +1 -1
- package/src/components/select/WithSelectContext.tsx +4 -3
- package/src/components/side-navigation/SideNavigationItem.test.tsx +1 -1
- package/src/components/side-navigation/SideNavigationItem.tsx +2 -2
- package/src/components/slider/Slider.test.tsx +1 -1
- package/src/components/slider/Slider.tsx +3 -2
- package/src/components/switch/Switch.test.tsx +1 -1
- package/src/components/switch/Switch.tsx +4 -3
- package/src/components/tabs/state.ts +4 -6
- package/src/components/text-field/TextField.tsx +6 -15
- package/src/components/tooltip/Tooltip.test.tsx +1 -1
- package/src/components/tooltip/Tooltip.tsx +4 -4
- package/src/components/uploader/Uploader.tsx +3 -2
- package/src/components/user-block/UserBlock.stories.tsx +1 -1
- package/src/components/user-block/UserBlock.tsx +2 -2
- package/src/hooks/useId.test.tsx +23 -0
- package/src/hooks/useId.ts +15 -0
- package/src/hooks/{useElementSizeDependentOfWindowSize.ts → useSizeOnWindowResize.ts} +1 -3
- package/src/hooks/useSlideshowControls.ts +7 -4
- package/src/stories/generated/UserBlock/Demos.stories.tsx +1 -0
- package/src/utils/date/formatDayNumber.test.ts +12 -0
- package/src/utils/date/formatDayNumber.ts +5 -0
- package/src/utils/unref.ts +0 -0
package/package.json
CHANGED
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
"url": "https://github.com/lumapps/design-system/issues"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@lumx/core": "^3.
|
|
10
|
-
"@lumx/icons": "^3.
|
|
9
|
+
"@lumx/core": "^3.9.0",
|
|
10
|
+
"@lumx/icons": "^3.9.0",
|
|
11
11
|
"@popperjs/core": "^2.5.4",
|
|
12
12
|
"body-scroll-lock": "^3.1.5",
|
|
13
13
|
"classnames": "^2.3.2",
|
|
14
14
|
"react-is": ">=16.13.0",
|
|
15
|
-
"react-popper": "^2.2.4"
|
|
16
|
-
"uid": "^2.0.0"
|
|
15
|
+
"react-popper": "^2.2.4"
|
|
17
16
|
},
|
|
18
17
|
"devDependencies": {
|
|
19
18
|
"@babel/core": "^7.18.13",
|
|
@@ -111,5 +110,5 @@
|
|
|
111
110
|
"build:storybook": "storybook build"
|
|
112
111
|
},
|
|
113
112
|
"sideEffects": false,
|
|
114
|
-
"version": "3.
|
|
113
|
+
"version": "3.9.0"
|
|
115
114
|
}
|
|
@@ -5,7 +5,7 @@ import { queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
|
5
5
|
import { render } from '@testing-library/react';
|
|
6
6
|
import { AlertDialog, AlertDialogProps } from './AlertDialog';
|
|
7
7
|
|
|
8
|
-
jest.mock('
|
|
8
|
+
jest.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
9
9
|
|
|
10
10
|
const CLASSNAME = AlertDialog.className as string;
|
|
11
11
|
|
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
} from '@lumx/react';
|
|
17
17
|
|
|
18
18
|
import { mdiAlert, mdiAlertCircle, mdiCheckCircle, mdiInformation } from '@lumx/icons';
|
|
19
|
-
import { uid } from 'uid';
|
|
20
19
|
import { Comp } from '@lumx/react/utils/type';
|
|
21
20
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
21
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
22
22
|
|
|
23
23
|
export interface AlertDialogProps extends Omit<DialogProps, 'header' | 'footer'> {
|
|
24
24
|
/** Message variant. */
|
|
@@ -86,7 +86,8 @@ export const AlertDialog: Comp<AlertDialogProps, HTMLDivElement> = forwardRef((p
|
|
|
86
86
|
const { color, icon } = CONFIG[kind as Kind] || {};
|
|
87
87
|
|
|
88
88
|
// Define a unique ID to target title and description for aria attributes.
|
|
89
|
-
const
|
|
89
|
+
const generatedId = useId();
|
|
90
|
+
const uniqueId = id || generatedId;
|
|
90
91
|
const titleId = `${uniqueId}-title`;
|
|
91
92
|
const descriptionId = `${uniqueId}-description`;
|
|
92
93
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import React, { forwardRef, InputHTMLAttributes, ReactNode, SyntheticEvent
|
|
1
|
+
import React, { forwardRef, InputHTMLAttributes, ReactNode, SyntheticEvent } from 'react';
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import { uid } from 'uid';
|
|
5
4
|
|
|
6
5
|
import { mdiCheck, mdiMinus } from '@lumx/icons';
|
|
7
6
|
|
|
8
7
|
import { Icon, InputHelper, InputLabel, Theme } from '@lumx/react';
|
|
9
8
|
import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
|
|
10
9
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
10
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
11
11
|
import { useMergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -84,7 +84,8 @@ export const Checkbox: Comp<CheckboxProps, HTMLDivElement> = forwardRef((props,
|
|
|
84
84
|
...forwardedProps
|
|
85
85
|
} = props;
|
|
86
86
|
const localInputRef = React.useRef<HTMLInputElement>(null);
|
|
87
|
-
const
|
|
87
|
+
const generatedInputId = useId();
|
|
88
|
+
const inputId = id || generatedInputId;
|
|
88
89
|
|
|
89
90
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
90
91
|
if (onChange) {
|
|
@@ -12,6 +12,7 @@ import { usePreviousValue } from '@lumx/react/hooks/usePreviousValue';
|
|
|
12
12
|
import { getYearDisplayName } from '@lumx/react/utils/date/getYearDisplayName';
|
|
13
13
|
import { onEnterPressed } from '@lumx/react/utils/event';
|
|
14
14
|
import { addMonthResetDay } from '@lumx/react/utils/date/addMonthResetDay';
|
|
15
|
+
import { formatDayNumber } from '@lumx/react/utils/date/formatDayNumber';
|
|
15
16
|
import { CLASSNAME } from './constants';
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -198,9 +199,7 @@ export const DatePickerControlled: Comp<DatePickerControlledProps, HTMLDivElemen
|
|
|
198
199
|
type="button"
|
|
199
200
|
onClick={() => onChange(date)}
|
|
200
201
|
>
|
|
201
|
-
<span aria-hidden>
|
|
202
|
-
{date.toLocaleDateString(locale, { day: 'numeric' })}
|
|
203
|
-
</span>
|
|
202
|
+
<span aria-hidden>{formatDayNumber(locale, date)}</span>
|
|
204
203
|
<span className="visually-hidden">
|
|
205
204
|
{date.toLocaleDateString(locale, {
|
|
206
205
|
day: 'numeric',
|
|
@@ -5,7 +5,7 @@ import { render, within, screen } from '@testing-library/react';
|
|
|
5
5
|
import { getByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
6
6
|
import userEvent from '@testing-library/user-event';
|
|
7
7
|
import { useImageSize } from '@lumx/react/hooks/useImageSize';
|
|
8
|
-
import {
|
|
8
|
+
import { useSizeOnWindowResize } from '@lumx/react/hooks/useSizeOnWindowResize';
|
|
9
9
|
|
|
10
10
|
import { ImageLightbox } from './ImageLightbox';
|
|
11
11
|
import { ImageLightboxProps } from './types';
|
|
@@ -19,7 +19,7 @@ import Meta, {
|
|
|
19
19
|
} from './ImageLightbox.stories';
|
|
20
20
|
|
|
21
21
|
jest.mock('@lumx/react/hooks/useImageSize');
|
|
22
|
-
jest.mock('@lumx/react/hooks/
|
|
22
|
+
jest.mock('@lumx/react/hooks/useSizeOnWindowResize');
|
|
23
23
|
|
|
24
24
|
const CLASSNAME = ImageLightbox.className as string;
|
|
25
25
|
const baseProps = Meta.args;
|
|
@@ -56,7 +56,7 @@ const queries = {
|
|
|
56
56
|
describe(`<${ImageLightbox.displayName}>`, () => {
|
|
57
57
|
beforeEach(() => {
|
|
58
58
|
(useImageSize as any).mockReturnValue(null);
|
|
59
|
-
(
|
|
59
|
+
(useSizeOnWindowResize as any).mockReturnValue([null, jest.fn()]);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
describe('render', () => {
|
|
@@ -189,7 +189,7 @@ describe(`<${ImageLightbox.displayName}>`, () => {
|
|
|
189
189
|
const scrollAreaSize = { width: 600, height: 600 };
|
|
190
190
|
beforeEach(() => {
|
|
191
191
|
(useImageSize as any).mockImplementation((_: any, getInitialSize: any) => getInitialSize?.() || null);
|
|
192
|
-
(
|
|
192
|
+
(useSizeOnWindowResize as any).mockReturnValue([scrollAreaSize, jest.fn()]);
|
|
193
193
|
});
|
|
194
194
|
|
|
195
195
|
it('should use the image initial size', () => {
|
|
@@ -207,23 +207,15 @@ describe(`<${ImageLightbox.displayName}>`, () => {
|
|
|
207
207
|
});
|
|
208
208
|
|
|
209
209
|
it('should zoom on zoom button pressed', async () => {
|
|
210
|
-
|
|
211
|
-
const imageLightbox = queries.getImageLightbox();
|
|
212
|
-
|
|
213
|
-
// Initial image style
|
|
214
|
-
const image = queries.queryImage(imageLightbox, 'Image 1');
|
|
215
|
-
|
|
216
|
-
expect(image).toHaveStyle({
|
|
217
|
-
maxHeight: `${scrollAreaSize.height}px`,
|
|
218
|
-
maxWidth: `${scrollAreaSize.width}px`,
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// Update image size (simulate image loaded)
|
|
210
|
+
// Set image size (simulate image loaded)
|
|
222
211
|
const imageSize = { width: 500, height: 300 };
|
|
223
212
|
(useImageSize as any).mockReturnValue(imageSize);
|
|
224
|
-
rerender();
|
|
225
213
|
|
|
226
|
-
|
|
214
|
+
setup(SingleImageWithZoom.args);
|
|
215
|
+
const imageLightbox = queries.getImageLightbox();
|
|
216
|
+
|
|
217
|
+
// Image style
|
|
218
|
+
const image = queries.queryImage(imageLightbox, 'Image 1');
|
|
227
219
|
expect(image).toHaveStyle({ width: `${imageSize.width}px`, height: `${imageSize.height}px` });
|
|
228
220
|
|
|
229
221
|
// Scroll area is bigger than the image, it should not be focusable
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { SlideshowItem, Thumbnail } from '@lumx/react';
|
|
4
4
|
import { useMergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
5
|
-
import {
|
|
5
|
+
import { useSizeOnWindowResize } from '@lumx/react/hooks/useSizeOnWindowResize';
|
|
6
6
|
import { useImageSize } from '@lumx/react/hooks/useImageSize';
|
|
7
7
|
import { getPrefersReducedMotion } from '@lumx/react/utils/browser/getPrefersReducedMotion';
|
|
8
8
|
import { isEqual } from '@lumx/react/utils/object/isEqual';
|
|
@@ -30,7 +30,7 @@ export const ImageSlide = React.memo((props: ImageSlideProps) => {
|
|
|
30
30
|
|
|
31
31
|
// Get scroll area size
|
|
32
32
|
const scrollAreaRef = React.useRef<HTMLDivElement>(null);
|
|
33
|
-
const [scrollAreaSize, updateSize] =
|
|
33
|
+
const [scrollAreaSize, updateSize] = useSizeOnWindowResize(scrollAreaRef);
|
|
34
34
|
React.useEffect(() => {
|
|
35
35
|
// Update size when active
|
|
36
36
|
if (isActive) updateSize();
|
|
@@ -45,7 +45,7 @@ const CLASSNAME = getRootClassName(COMPONENT_NAME);
|
|
|
45
45
|
*/
|
|
46
46
|
const CONFIG = {
|
|
47
47
|
[Kind.error]: { color: ColorPalette.red, icon: mdiAlert },
|
|
48
|
-
[Kind.info]: { color: ColorPalette.
|
|
48
|
+
[Kind.info]: { color: ColorPalette.blue, icon: mdiInformation },
|
|
49
49
|
[Kind.success]: { color: ColorPalette.green, icon: mdiCheckCircle },
|
|
50
50
|
[Kind.warning]: { color: ColorPalette.yellow, icon: mdiAlertCircle },
|
|
51
51
|
};
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import React, { forwardRef, ReactNode, useRef, useState,
|
|
1
|
+
import React, { forwardRef, ReactNode, useRef, useState, useContext } from 'react';
|
|
2
2
|
|
|
3
3
|
import { mdiChevronDown, mdiChevronUp } from '@lumx/icons';
|
|
4
4
|
import { Icon, Size, Text, Orientation, Popover, Placement, Theme } from '@lumx/react';
|
|
5
|
-
import uniqueId from 'lodash/uniqueId';
|
|
6
5
|
import classNames from 'classnames';
|
|
7
6
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
8
7
|
import { HasClassName } from '@lumx/react/utils/type';
|
|
9
8
|
import { ThemeContext } from '@lumx/react/utils/ThemeContext';
|
|
9
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
10
|
+
|
|
10
11
|
import { CLASSNAME as ITEM_CLASSNAME } from './NavigationItem';
|
|
11
12
|
import { NavigationContext } from './context';
|
|
12
13
|
|
|
@@ -34,7 +35,7 @@ export const NavigationSection = Object.assign(
|
|
|
34
35
|
const { children, className, label, icon, ...forwardedProps } = props;
|
|
35
36
|
const [isOpen, setIsOpen] = useState(false);
|
|
36
37
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
37
|
-
const sectionId =
|
|
38
|
+
const sectionId = useId();
|
|
38
39
|
const { orientation } = useContext(NavigationContext) || {};
|
|
39
40
|
const theme = useContext(ThemeContext);
|
|
40
41
|
const isDropdown = orientation === Orientation.horizontal;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { forwardRef, ReactNode, SyntheticEvent, InputHTMLAttributes } from 'react';
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import { uid } from 'uid';
|
|
5
4
|
|
|
6
5
|
import { InputHelper, InputLabel, Theme } from '@lumx/react';
|
|
7
6
|
|
|
8
7
|
import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
|
|
9
8
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
9
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Defines the props of the component.
|
|
@@ -76,7 +76,8 @@ export const RadioButton: Comp<RadioButtonProps, HTMLDivElement> = forwardRef((p
|
|
|
76
76
|
inputProps,
|
|
77
77
|
...forwardedProps
|
|
78
78
|
} = props;
|
|
79
|
-
const
|
|
79
|
+
const generatedInputId = useId();
|
|
80
|
+
const inputId = id || generatedInputId;
|
|
80
81
|
|
|
81
82
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
82
83
|
if (onChange) {
|
|
@@ -14,7 +14,7 @@ import { Select, SelectProps, SelectVariant } from './Select';
|
|
|
14
14
|
const CLASSNAME = Select.className as string;
|
|
15
15
|
|
|
16
16
|
jest.mock('@lumx/react/utils/isFocusVisible');
|
|
17
|
-
jest.mock('
|
|
17
|
+
jest.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
@@ -18,7 +18,7 @@ import { SelectVariant } from './constants';
|
|
|
18
18
|
|
|
19
19
|
const CLASSNAME = SelectMultiple.className as string;
|
|
20
20
|
|
|
21
|
-
jest.mock('
|
|
21
|
+
jest.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import React, { Ref, useCallback,
|
|
3
|
-
import { uid } from 'uid';
|
|
2
|
+
import React, { Ref, useCallback, useRef } from 'react';
|
|
4
3
|
|
|
5
4
|
import { Placement } from '@lumx/react';
|
|
6
5
|
import { Kind, Theme } from '@lumx/react/components';
|
|
@@ -11,6 +10,7 @@ import { useListenFocus } from '@lumx/react/hooks/useListenFocus';
|
|
|
11
10
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
12
11
|
import { mergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
13
12
|
|
|
13
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
14
14
|
import { CoreSelectProps, SelectVariant } from './constants';
|
|
15
15
|
|
|
16
16
|
/** The display name of the component. */
|
|
@@ -56,7 +56,8 @@ export const WithSelectContext = (
|
|
|
56
56
|
}: CoreSelectProps,
|
|
57
57
|
ref: Ref<HTMLDivElement>,
|
|
58
58
|
): React.ReactElement => {
|
|
59
|
-
const
|
|
59
|
+
const generatedSelectId = useId();
|
|
60
|
+
const selectId = id || generatedSelectId;
|
|
60
61
|
const anchorRef = useRef<HTMLElement>(null);
|
|
61
62
|
const selectRef = useRef<HTMLDivElement>(null);
|
|
62
63
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
@@ -10,7 +10,7 @@ const CLASSNAME = SideNavigationItem.className as string;
|
|
|
10
10
|
|
|
11
11
|
const toggleButtonProps = { label: 'Toggle' };
|
|
12
12
|
|
|
13
|
-
jest.mock('
|
|
13
|
+
jest.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
@@ -2,7 +2,6 @@ import React, { Children, forwardRef, ReactNode } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import isEmpty from 'lodash/isEmpty';
|
|
5
|
-
import { uid } from 'uid';
|
|
6
5
|
|
|
7
6
|
import { mdiChevronDown, mdiChevronUp } from '@lumx/icons';
|
|
8
7
|
|
|
@@ -12,6 +11,7 @@ import { Comp, GenericProps, isComponent } from '@lumx/react/utils/type';
|
|
|
12
11
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
13
12
|
import { renderLink } from '@lumx/react/utils/renderLink';
|
|
14
13
|
import { renderButtonOrLink } from '@lumx/react/utils/renderButtonOrLink';
|
|
14
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Defines the props of the component.
|
|
@@ -95,7 +95,7 @@ export const SideNavigationItem: Comp<SideNavigationItemProps, HTMLLIElement> =
|
|
|
95
95
|
const shouldSplitActions = Boolean(onActionClick);
|
|
96
96
|
const showChildren = hasContent && isOpen;
|
|
97
97
|
|
|
98
|
-
const contentId =
|
|
98
|
+
const contentId = useId();
|
|
99
99
|
const ariaProps: any = {};
|
|
100
100
|
if (hasContent) {
|
|
101
101
|
ariaProps['aria-expanded'] = !!showChildren;
|
|
@@ -7,7 +7,7 @@ import { Slider, SliderProps } from './Slider';
|
|
|
7
7
|
|
|
8
8
|
const CLASSNAME = Slider.className as string;
|
|
9
9
|
|
|
10
|
-
jest.mock('
|
|
10
|
+
jest.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
11
11
|
|
|
12
12
|
const setup = (props: Partial<SliderProps> = {}) => {
|
|
13
13
|
render(<Slider {...(props as any)} />);
|
|
@@ -9,8 +9,8 @@ import useEventCallback from '@lumx/react/hooks/useEventCallback';
|
|
|
9
9
|
import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
|
|
10
10
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
11
11
|
|
|
12
|
-
import { uid } from 'uid';
|
|
13
12
|
import { clamp } from '@lumx/react/utils/clamp';
|
|
13
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Defines the props of the component.
|
|
@@ -111,7 +111,8 @@ export const Slider: Comp<SliderProps, HTMLDivElement> = forwardRef((props, ref)
|
|
|
111
111
|
value,
|
|
112
112
|
...forwardedProps
|
|
113
113
|
} = props;
|
|
114
|
-
const
|
|
114
|
+
const generatedId = useId();
|
|
115
|
+
const sliderId = id || generatedId;
|
|
115
116
|
const sliderLabelId = useMemo(() => `label-${sliderId}`, [sliderId]);
|
|
116
117
|
const sliderRef = useRef<HTMLDivElement>(null);
|
|
117
118
|
|
|
@@ -24,7 +24,7 @@ const setup = (propsOverride: SetupProps = {}) => {
|
|
|
24
24
|
return { switchWrapper, input, helper, label, props };
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
jest.mock('
|
|
27
|
+
jest.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
28
28
|
|
|
29
29
|
describe(`<${Switch.displayName}>`, () => {
|
|
30
30
|
describe('Props', () => {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import React, { Children, forwardRef, InputHTMLAttributes, SyntheticEvent
|
|
1
|
+
import React, { Children, forwardRef, InputHTMLAttributes, SyntheticEvent } from 'react';
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import { uid } from 'uid';
|
|
5
4
|
|
|
6
5
|
import isEmpty from 'lodash/isEmpty';
|
|
7
6
|
|
|
@@ -9,6 +8,7 @@ import { Alignment, InputHelper, InputLabel, Theme } from '@lumx/react';
|
|
|
9
8
|
|
|
10
9
|
import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
|
|
11
10
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
11
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Defines the props of the component.
|
|
@@ -75,7 +75,8 @@ export const Switch: Comp<SwitchProps, HTMLDivElement> = forwardRef((props, ref)
|
|
|
75
75
|
inputProps = {},
|
|
76
76
|
...forwardedProps
|
|
77
77
|
} = props;
|
|
78
|
-
const
|
|
78
|
+
const generatedInputId = useId();
|
|
79
|
+
const inputId = id || generatedInputId;
|
|
79
80
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
80
81
|
if (onChange) {
|
|
81
82
|
onChange(!isChecked, value, name, event);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Dispatch, createContext, useCallback, useContext, useEffect, useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
3
3
|
|
|
4
4
|
type TabType = 'tab' | 'tabPanel';
|
|
5
5
|
|
|
@@ -76,11 +76,9 @@ export const useTabProviderContext = (type: TabType, originalId?: string): undef
|
|
|
76
76
|
const [state, dispatch] = context;
|
|
77
77
|
|
|
78
78
|
// Current tab or tab panel id.
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
[],
|
|
83
|
-
);
|
|
79
|
+
const generatedId = useId();
|
|
80
|
+
const id = originalId || generatedId;
|
|
81
|
+
|
|
84
82
|
useEffect(
|
|
85
83
|
() => {
|
|
86
84
|
// On mount: register tab or tab panel id.
|
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
forwardRef,
|
|
3
|
-
ReactNode,
|
|
4
|
-
Ref,
|
|
5
|
-
RefObject,
|
|
6
|
-
SyntheticEvent,
|
|
7
|
-
useEffect,
|
|
8
|
-
useMemo,
|
|
9
|
-
useRef,
|
|
10
|
-
useState,
|
|
11
|
-
} from 'react';
|
|
1
|
+
import React, { forwardRef, ReactNode, Ref, RefObject, SyntheticEvent, useEffect, useRef, useState } from 'react';
|
|
12
2
|
|
|
13
3
|
import classNames from 'classnames';
|
|
14
4
|
import get from 'lodash/get';
|
|
15
|
-
import { uid } from 'uid';
|
|
16
5
|
|
|
17
6
|
import { mdiAlertCircle, mdiCheckCircle, mdiCloseCircle } from '@lumx/icons';
|
|
18
7
|
import {
|
|
@@ -30,6 +19,7 @@ import {
|
|
|
30
19
|
import { Comp, GenericProps, HasTheme } from '@lumx/react/utils/type';
|
|
31
20
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
32
21
|
import { mergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
22
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
33
23
|
|
|
34
24
|
/**
|
|
35
25
|
* Defines the props of the component.
|
|
@@ -296,7 +286,8 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
296
286
|
afterElement,
|
|
297
287
|
...forwardedProps
|
|
298
288
|
} = props;
|
|
299
|
-
const
|
|
289
|
+
const generatedTextFieldId = useId();
|
|
290
|
+
const textFieldId = id || generatedTextFieldId;
|
|
300
291
|
/** Keep a clean local input ref to manage focus */
|
|
301
292
|
const localInputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
|
|
302
293
|
/** Merge prop input ref and local input ref */
|
|
@@ -307,8 +298,8 @@ export const TextField: Comp<TextFieldProps, HTMLDivElement> = forwardRef((props
|
|
|
307
298
|
* we want to first use the most important one, which is the errorId. That way, screen readers will read first
|
|
308
299
|
* the error and then the helper
|
|
309
300
|
*/
|
|
310
|
-
const helperId = helper ? `text-field-helper-${
|
|
311
|
-
const errorId = error ? `text-field-error-${
|
|
301
|
+
const helperId = helper ? `text-field-helper-${generatedTextFieldId}` : undefined;
|
|
302
|
+
const errorId = error ? `text-field-error-${generatedTextFieldId}` : undefined;
|
|
312
303
|
const describedByIds = [errorId, helperId, forwardedProps['aria-describedby']].filter(Boolean);
|
|
313
304
|
const describedById = describedByIds.length === 0 ? undefined : describedByIds.join(' ');
|
|
314
305
|
|
|
@@ -12,7 +12,7 @@ import { Tooltip, TooltipProps } from './Tooltip';
|
|
|
12
12
|
const CLASSNAME = Tooltip.className as string;
|
|
13
13
|
|
|
14
14
|
jest.mock('@lumx/react/utils/isFocusVisible');
|
|
15
|
-
jest.mock('
|
|
15
|
+
jest.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
16
16
|
// Skip delays
|
|
17
17
|
jest.mock('@lumx/react/constants', () => ({
|
|
18
18
|
...jest.requireActual('@lumx/react/constants'),
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
-
import React, { forwardRef, ReactNode,
|
|
2
|
+
import React, { forwardRef, ReactNode, useState } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { usePopper } from 'react-popper';
|
|
5
|
-
import { uid } from 'uid';
|
|
6
5
|
|
|
7
6
|
import classNames from 'classnames';
|
|
8
7
|
|
|
@@ -11,8 +10,9 @@ import { Comp, GenericProps } from '@lumx/react/utils/type';
|
|
|
11
10
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
12
11
|
import { mergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
13
12
|
import { Placement } from '@lumx/react/components/popover';
|
|
14
|
-
|
|
15
13
|
import { TooltipContextProvider } from '@lumx/react/components/tooltip/context';
|
|
14
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
15
|
+
|
|
16
16
|
import { useInjectTooltipRef } from './useInjectTooltipRef';
|
|
17
17
|
import { useTooltipOpen } from './useTooltipOpen';
|
|
18
18
|
|
|
@@ -71,7 +71,7 @@ export const Tooltip: Comp<TooltipProps, HTMLDivElement> = forwardRef((props, re
|
|
|
71
71
|
return <TooltipContextProvider>{children}</TooltipContextProvider>;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const id =
|
|
74
|
+
const id = useId();
|
|
75
75
|
|
|
76
76
|
const [popperElement, setPopperElement] = useState<null | HTMLElement>(null);
|
|
77
77
|
const [anchorElement, setAnchorElement] = useState<null | HTMLElement>(null);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React, { forwardRef, MouseEventHandler } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
-
import uniqueId from 'lodash/uniqueId';
|
|
4
3
|
|
|
5
4
|
import { AspectRatio, Icon, Size, Theme } from '@lumx/react';
|
|
6
5
|
import { Comp, GenericProps, HasTheme, ValueOf } from '@lumx/react/utils/type';
|
|
7
6
|
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
8
7
|
import { useBooleanState } from '@lumx/react/hooks/useBooleanState';
|
|
8
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Uploader variants.
|
|
@@ -81,7 +81,8 @@ export const Uploader: Comp<UploaderProps> = forwardRef((props, ref) => {
|
|
|
81
81
|
// Adjust to square aspect ratio when using circle variants.
|
|
82
82
|
const adjustedAspectRatio = variant === UploaderVariant.circle ? AspectRatio.square : aspectRatio;
|
|
83
83
|
|
|
84
|
-
const
|
|
84
|
+
const generatedInputId = useId();
|
|
85
|
+
const inputId = fileInputProps?.id || generatedInputId;
|
|
85
86
|
const [isDragHovering, unsetDragHovering, setDragHovering] = useBooleanState(false);
|
|
86
87
|
const wrapper = fileInputProps
|
|
87
88
|
? { Component: 'label' as const, props: { htmlFor: inputId } as const }
|
|
@@ -9,7 +9,7 @@ import { withCombinations } from '@lumx/react/stories/decorators/withCombination
|
|
|
9
9
|
import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
|
|
10
10
|
import { UserBlock } from './UserBlock';
|
|
11
11
|
|
|
12
|
-
const sizes = [Size.s, Size.m, Size.l];
|
|
12
|
+
const sizes = [Size.xs, Size.s, Size.m, Size.l];
|
|
13
13
|
|
|
14
14
|
export default {
|
|
15
15
|
title: 'LumX components/user-block/UserBlock',
|
|
@@ -12,7 +12,7 @@ import { AvatarProps } from '../avatar/Avatar';
|
|
|
12
12
|
/**
|
|
13
13
|
* User block sizes.
|
|
14
14
|
*/
|
|
15
|
-
export type UserBlockSize = Extract<Size, 's' | 'm' | 'l'>;
|
|
15
|
+
export type UserBlockSize = Extract<Size, 'xs' | 's' | 'm' | 'l'>;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Defines the props of the component.
|
|
@@ -128,7 +128,7 @@ export const UserBlock: Comp<UserBlockProps, HTMLDivElement> = forwardRef((props
|
|
|
128
128
|
return <NameComponent {...nProps}>{name}</NameComponent>;
|
|
129
129
|
}, [avatarProps, isClickable, linkAs, linkProps, name, nameProps, onClick]);
|
|
130
130
|
|
|
131
|
-
const fieldsBlock: ReactNode = fields && componentSize !== Size.s && (
|
|
131
|
+
const fieldsBlock: ReactNode = fields && componentSize !== Size.s && componentSize !== Size.xs && (
|
|
132
132
|
<div className={`${CLASSNAME}__fields`}>
|
|
133
133
|
{fields.map((field: string, idx: number) => (
|
|
134
134
|
<span key={idx} className={`${CLASSNAME}__field`}>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useId } from '@lumx/react/hooks/useId';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
function setup() {
|
|
6
|
+
const Component = (): any => useId();
|
|
7
|
+
const result = render(<Component />);
|
|
8
|
+
const rerender = () => result.rerender(<Component />);
|
|
9
|
+
return { ...result, rerender };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe(useId, () => {
|
|
13
|
+
it('should render a unique id stable after re-renders', () => {
|
|
14
|
+
const result = setup();
|
|
15
|
+
// Id generated
|
|
16
|
+
const initialId = result.container.textContent;
|
|
17
|
+
expect(initialId).toMatch(/:lumx\d+:/);
|
|
18
|
+
|
|
19
|
+
// Id is stable after re-render
|
|
20
|
+
result.rerender();
|
|
21
|
+
expect(result.container.textContent).toEqual(initialId);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
let i = 0;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generate a unique id (for use in a11y or other id based DOM linking).
|
|
7
|
+
*
|
|
8
|
+
* (Tries to emulate React 18 useId hook, to remove once we upgrade React)
|
|
9
|
+
*/
|
|
10
|
+
export function useId() {
|
|
11
|
+
return React.useMemo(() => {
|
|
12
|
+
i += 1;
|
|
13
|
+
return `:lumx${i}:`;
|
|
14
|
+
}, []);
|
|
15
|
+
}
|
|
@@ -11,9 +11,7 @@ import { RectSize } from '@lumx/react/utils/type';
|
|
|
11
11
|
* @param elementRef Element to observe
|
|
12
12
|
* @return the size and a manual update callback
|
|
13
13
|
*/
|
|
14
|
-
export function
|
|
15
|
-
elementRef: React.RefObject<HTMLElement>,
|
|
16
|
-
): [RectSize | null, () => void] {
|
|
14
|
+
export function useSizeOnWindowResize(elementRef: React.RefObject<HTMLElement>): [RectSize | null, () => void] {
|
|
17
15
|
const [size, setSize] = React.useState<null | RectSize>(null);
|
|
18
16
|
const updateSize = React.useMemo(
|
|
19
17
|
() =>
|