@lumx/react 3.20.1-alpha.9 → 3.21.1-alpha.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/_internal/index.js +20 -13
- package/_internal/index.js.map +1 -1
- package/index.d.ts +60 -33
- package/index.js +2941 -3091
- package/index.js.map +1 -1
- package/package.json +5 -9
- package/src/components/alert-dialog/AlertDialog.stories.tsx +1 -1
- package/src/components/alert-dialog/AlertDialog.test.tsx +2 -0
- package/src/components/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/autocomplete/Autocomplete.test.tsx +2 -0
- package/src/components/autocomplete/Autocomplete.tsx +1 -1
- package/src/components/autocomplete/AutocompleteMultiple.stories.tsx +1 -1
- package/src/components/autocomplete/AutocompleteMultiple.test.tsx +2 -0
- package/src/components/autocomplete/AutocompleteMultiple.tsx +1 -1
- package/src/components/avatar/Avatar.stories.tsx +2 -0
- package/src/components/avatar/Avatar.test.tsx +2 -0
- package/src/components/avatar/Avatar.tsx +1 -1
- package/src/components/badge/Badge.stories.tsx +1 -0
- package/src/components/badge/Badge.test.tsx +2 -0
- package/src/components/badge/Badge.tsx +1 -1
- package/src/components/badge/BadgeWrapper.stories.tsx +1 -0
- package/src/components/badge/BadgeWrapper.test.tsx +2 -0
- package/src/components/badge/BadgeWrapper.tsx +1 -1
- package/src/components/button/Button.stories.tsx +1 -0
- package/src/components/button/Button.test.tsx +2 -0
- package/src/components/button/Button.tsx +2 -0
- package/src/components/button/ButtonGroup.stories.tsx +2 -0
- package/src/components/button/ButtonGroup.test.tsx +2 -0
- package/src/components/button/ButtonGroup.tsx +2 -0
- package/src/components/button/ButtonRoot.tsx +1 -1
- package/src/components/button/IconButton.test.tsx +2 -0
- package/src/components/button/IconButton.tsx +2 -0
- package/src/components/checkbox/Checkbox.test.tsx +2 -0
- package/src/components/chip/Chip.stories.tsx +2 -0
- package/src/components/chip/Chip.test.tsx +2 -0
- package/src/components/chip/Chip.tsx +1 -1
- package/src/components/chip/ChipGroup.stories.tsx +2 -0
- package/src/components/chip/ChipGroup.test.tsx +2 -0
- package/src/components/chip/ChipGroup.tsx +1 -1
- package/src/components/comment-block/CommentBlock.stories.tsx +1 -0
- package/src/components/comment-block/CommentBlock.test.tsx +1 -0
- package/src/components/comment-block/CommentBlock.tsx +1 -1
- package/src/components/date-picker/DatePicker.test.tsx +2 -0
- package/src/components/date-picker/DatePicker.tsx +1 -1
- package/src/components/date-picker/DatePickerControlled.test.tsx +2 -0
- package/src/components/date-picker/DatePickerField.test.tsx +2 -0
- package/src/components/dialog/Dialog.test.tsx +2 -0
- package/src/components/divider/Divider.test.tsx +2 -0
- package/src/components/divider/Divider.tsx +2 -0
- package/src/components/drag-handle/DragHandle.test.tsx +2 -0
- package/src/components/drag-handle/DragHandle.tsx +2 -0
- package/src/components/dropdown/Dropdown.stories.tsx +1 -1
- package/src/components/dropdown/Dropdown.tsx +1 -1
- package/src/components/expansion-panel/ExpansionPanel.test.tsx +2 -0
- package/src/components/flag/Flag.test.tsx +2 -0
- package/src/components/flag/Flag.tsx +2 -0
- package/src/components/flex-box/FlexBox.stories.tsx +2 -0
- package/src/components/flex-box/FlexBox.test.tsx +1 -0
- package/src/components/flex-box/FlexBox.tsx +1 -1
- package/src/components/generic-block/GenericBlock.test.tsx +1 -1
- package/src/components/grid/Grid.tsx +2 -0
- package/src/components/grid/GridItem.tsx +2 -0
- package/src/components/grid-column/GridColumn.stories.tsx +1 -0
- package/src/components/grid-column/GridColumn.test.jsx +2 -0
- package/src/components/grid-column/GridColumn.tsx +1 -1
- package/src/components/heading/Heading.stories.tsx +1 -0
- package/src/components/heading/Heading.test.tsx +2 -0
- package/src/components/heading/Heading.tsx +2 -0
- package/src/components/heading/HeadingLevelProvider.tsx +1 -1
- package/src/components/icon/Icon.test.tsx +2 -0
- package/src/components/icon/Icon.tsx +120 -7
- package/src/components/image-block/ImageBlock.stories.tsx +2 -0
- package/src/components/image-block/ImageBlock.test.tsx +1 -0
- package/src/components/image-block/ImageBlock.tsx +1 -1
- package/src/components/image-block/ImageCaption.tsx +1 -1
- package/src/components/image-lightbox/ImageLightbox.stories.tsx +1 -0
- package/src/components/image-lightbox/ImageLightbox.test.tsx +2 -0
- package/src/components/image-lightbox/types.ts +2 -0
- package/src/components/inline-list/InlineList.stories.tsx +1 -0
- package/src/components/inline-list/InlineList.test.tsx +2 -0
- package/src/components/inline-list/InlineList.tsx +1 -1
- package/src/components/input-helper/InputHelper.test.tsx +2 -0
- package/src/components/input-helper/InputHelper.tsx +1 -1
- package/src/components/input-label/InputLabel.stories.tsx +2 -0
- package/src/components/input-label/InputLabel.test.tsx +2 -0
- package/src/components/input-label/InputLabel.tsx +1 -1
- package/src/components/lightbox/Lightbox.test.tsx +2 -0
- package/src/components/lightbox/Lightbox.tsx +1 -1
- package/src/components/link/Link.stories.tsx +1 -0
- package/src/components/link/Link.test.tsx +2 -0
- package/src/components/link/Link.tsx +2 -0
- package/src/components/link-preview/LinkPreview.test.tsx +2 -0
- package/src/components/link-preview/LinkPreview.tsx +2 -0
- package/src/components/list/List.stories.tsx +1 -1
- package/src/components/list/List.test.tsx +2 -0
- package/src/components/list/List.tsx +1 -1
- package/src/components/list/ListDivider.test.tsx +2 -0
- package/src/components/list/ListDivider.tsx +2 -0
- package/src/components/list/ListItem.test.tsx +2 -0
- package/src/components/list/ListItem.tsx +1 -1
- package/src/components/list/ListSubheader.test.tsx +2 -0
- package/src/components/list/ListSubheader.tsx +1 -1
- package/src/components/message/Message.test.tsx +1 -0
- package/src/components/message/Message.tsx +1 -1
- package/src/components/mosaic/Mosaic.test.tsx +2 -0
- package/src/components/mosaic/Mosaic.tsx +1 -1
- package/src/components/navigation/Navigation.stories.tsx +2 -0
- package/src/components/navigation/Navigation.test.tsx +2 -0
- package/src/components/navigation/Navigation.tsx +2 -0
- package/src/components/navigation/NavigationItem.test.tsx +2 -0
- package/src/components/navigation/NavigationItem.tsx +1 -1
- package/src/components/navigation/NavigationSection.test.tsx +2 -0
- package/src/components/navigation/NavigationSection.tsx +1 -1
- package/src/components/notification/Notification.test.tsx +2 -0
- package/src/components/notification/Notification.tsx +1 -1
- package/src/components/popover/Popover.test.tsx +2 -0
- package/src/components/popover/Popover.tsx +1 -1
- package/src/components/popover/usePopoverStyle.tsx +1 -1
- package/src/components/popover-dialog/PopoverDialog.test.tsx +1 -0
- package/src/components/popover-dialog/PopoverDialog.tsx +2 -0
- package/src/components/post-block/PostBlock.test.tsx +2 -0
- package/src/components/post-block/PostBlock.tsx +1 -1
- package/src/components/progress/Progress.tsx +2 -0
- package/src/components/progress/ProgressCircular.stories.tsx +1 -0
- package/src/components/progress/ProgressCircular.test.tsx +2 -0
- package/src/components/progress/ProgressCircular.tsx +2 -0
- package/src/components/progress/ProgressLinear.test.tsx +2 -0
- package/src/components/progress/ProgressLinear.tsx +2 -0
- package/src/components/progress-tracker/ProgressTracker.stories.tsx +1 -1
- package/src/components/progress-tracker/ProgressTracker.test.tsx +2 -0
- package/src/components/progress-tracker/ProgressTrackerProvider.test.tsx +2 -0
- package/src/components/progress-tracker/ProgressTrackerProvider.tsx +1 -1
- package/src/components/progress-tracker/ProgressTrackerStep.test.tsx +2 -0
- package/src/components/progress-tracker/ProgressTrackerStep.tsx +1 -1
- package/src/components/progress-tracker/ProgressTrackerStepPanel.test.tsx +2 -0
- package/src/components/progress-tracker/ProgressTrackerStepPanel.tsx +2 -0
- package/src/components/radio-button/RadioButton.test.tsx +2 -0
- package/src/components/radio-button/RadioButton.tsx +1 -1
- package/src/components/radio-button/RadioGroup.stories.tsx +1 -1
- package/src/components/radio-button/RadioGroup.test.tsx +2 -0
- package/src/components/radio-button/RadioGroup.tsx +1 -1
- package/src/components/select/Select.stories.tsx +1 -1
- package/src/components/select/Select.test.tsx +2 -0
- package/src/components/select/Select.tsx +1 -1
- package/src/components/select/SelectMultiple.stories.tsx +1 -1
- package/src/components/select/SelectMultiple.test.tsx +2 -0
- package/src/components/select/SelectMultiple.tsx +1 -1
- package/src/components/select/WithSelectContext.tsx +1 -1
- package/src/components/select/constants.ts +1 -1
- package/src/components/side-navigation/SideNavigation.test.tsx +2 -0
- package/src/components/side-navigation/SideNavigation.tsx +1 -1
- package/src/components/side-navigation/SideNavigationItem.test.tsx +2 -0
- package/src/components/side-navigation/SideNavigationItem.tsx +1 -1
- package/src/components/skeleton/SkeletonCircle.test.tsx +2 -0
- package/src/components/skeleton/SkeletonCircle.tsx +2 -0
- package/src/components/skeleton/SkeletonRectangle.test.tsx +2 -0
- package/src/components/skeleton/SkeletonRectangle.tsx +2 -0
- package/src/components/skeleton/SkeletonTypography.stories.tsx +2 -0
- package/src/components/skeleton/SkeletonTypography.test.tsx +2 -0
- package/src/components/skeleton/SkeletonTypography.tsx +1 -1
- package/src/components/slider/Slider.test.tsx +2 -0
- package/src/components/slider/Slider.tsx +1 -1
- package/src/components/slideshow/Slideshow.stories.tsx +1 -0
- package/src/components/slideshow/Slideshow.test.tsx +2 -0
- package/src/components/slideshow/SlideshowControls.stories.tsx +2 -0
- package/src/components/slideshow/SlideshowItem.tsx +2 -0
- package/src/components/slideshow/useSlideFocusManagement.tsx +1 -1
- package/src/components/switch/Switch.test.tsx +2 -0
- package/src/components/switch/Switch.tsx +1 -1
- package/src/components/table/Table.test.tsx +2 -0
- package/src/components/table/Table.tsx +2 -0
- package/src/components/table/TableBody.test.tsx +2 -0
- package/src/components/table/TableBody.tsx +2 -0
- package/src/components/table/TableCell.test.tsx +2 -0
- package/src/components/table/TableCell.tsx +2 -0
- package/src/components/table/TableHeader.test.tsx +2 -0
- package/src/components/table/TableHeader.tsx +2 -0
- package/src/components/table/TableRow.test.tsx +2 -0
- package/src/components/table/TableRow.tsx +2 -0
- package/src/components/tabs/Tab.test.tsx +2 -0
- package/src/components/tabs/Tab.tsx +1 -1
- package/src/components/tabs/TabList.test.tsx +2 -0
- package/src/components/tabs/TabPanel.test.tsx +2 -0
- package/src/components/tabs/TabPanel.tsx +2 -0
- package/src/components/tabs/TabProvider.test.tsx +2 -0
- package/src/components/tabs/TabProvider.tsx +1 -1
- package/src/components/tabs/Tabs.stories.tsx +1 -1
- package/src/components/text/Text.stories.tsx +1 -1
- package/src/components/text/Text.test.tsx +2 -0
- package/src/components/text/Text.tsx +2 -0
- package/src/components/text-field/RawInputText.stories.tsx +18 -0
- package/src/components/text-field/RawInputText.test.tsx +59 -0
- package/src/components/text-field/RawInputText.tsx +69 -0
- package/src/components/text-field/RawInputTextarea.stories.tsx +22 -0
- package/src/components/text-field/RawInputTextarea.test.tsx +64 -0
- package/src/components/text-field/RawInputTextarea.tsx +74 -0
- package/src/components/text-field/TextField.test.tsx +11 -2
- package/src/components/text-field/TextField.tsx +40 -210
- package/src/components/text-field/constants.ts +16 -0
- package/src/components/text-field/index.ts +3 -1
- package/src/components/text-field/useFitRowsToContent.ts +42 -0
- package/src/components/thumbnail/Thumbnail.test.tsx +2 -0
- package/src/components/toolbar/Toolbar.tsx +1 -1
- package/src/components/tooltip/Tooltip.stories.tsx +1 -2
- package/src/components/tooltip/Tooltip.test.tsx +1 -1
- package/src/components/uploader/Uploader.test.tsx +2 -0
- package/src/components/user-block/UserBlock.stories.tsx +2 -0
- package/src/components/user-block/UserBlock.test.tsx +2 -0
- package/src/hooks/useId.test.tsx +1 -0
- package/src/hooks/useInfiniteScroll.tsx +1 -1
- package/src/hooks/usePreviousValue.ts +1 -0
- package/src/stories/decorators/withChromaticForceScreenSize.tsx +1 -0
- package/src/stories/decorators/withNestedProps.tsx +1 -0
- package/src/stories/decorators/withThemedBackground.tsx +2 -0
- package/src/stories/decorators/withWrapper.tsx +2 -0
- package/src/stories/utils/CustomLink.tsx +1 -0
- package/src/testing/utils/ThemeSentinel.tsx +1 -0
- package/src/utils/ClickAwayProvider/ClickAwayProvider.stories.jsx +1 -1
- package/src/utils/ClickAwayProvider/ClickAwayProvider.tsx +1 -1
- package/src/utils/MaterialThemeSwitcher/MaterialThemeSwitcher.tsx +1 -1
- package/src/utils/Portal/Portal.test.tsx +1 -0
- package/src/utils/Portal/PortalProvider.stories.jsx +1 -0
- package/src/utils/Portal/PortalProvider.test.tsx +1 -0
- package/src/utils/disabled/DisabledStateProvider.stories.tsx +2 -0
- package/src/utils/react/OnBeforeUnmount.tsx +1 -1
- package/src/utils/react/RawClickable.tsx +1 -1
- package/src/utils/react/skipRender.tsx +2 -2
- package/src/utils/react/wrapChildrenIconWithSpaces.test.tsx +1 -1
- package/src/utils/type/HasPolymorphicAs.ts +2 -0
- package/utils/index.d.ts +7 -7
- package/utils/index.js +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { ComponentProps, ChangeEventHandler, SyntheticEvent, useRef, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
|
|
5
|
+
import { Theme, useTheme } from '@lumx/react';
|
|
6
|
+
import { forwardRef } from '@lumx/react/utils/react/forwardRef';
|
|
7
|
+
import { useMergeRefs } from '@lumx/react/utils/react/mergeRefs';
|
|
8
|
+
import type { HasClassName, HasTheme } from '@lumx/core/js/types';
|
|
9
|
+
|
|
10
|
+
import { handleBasicClasses } from '@lumx/core/js/utils';
|
|
11
|
+
|
|
12
|
+
import { useFitRowsToContent } from './useFitRowsToContent';
|
|
13
|
+
import { INPUT_NATIVE_CLASSNAME } from './constants';
|
|
14
|
+
|
|
15
|
+
type NativeTextareaProps = ComponentProps<'textarea'>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Defines the props of the component.
|
|
19
|
+
*/
|
|
20
|
+
export interface RawInputTextareaProps extends Omit<NativeTextareaProps, 'value' | 'onChange'>, HasTheme, HasClassName {
|
|
21
|
+
minimumRows?: number;
|
|
22
|
+
value?: string;
|
|
23
|
+
onChange?: (value: string, name?: string, event?: SyntheticEvent) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Component default props.
|
|
28
|
+
*/
|
|
29
|
+
export const DEFAULT_PROPS: Partial<RawInputTextareaProps> = {
|
|
30
|
+
minimumRows: 2,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Raw input text area component
|
|
35
|
+
* (textarea element without any decoration)
|
|
36
|
+
*/
|
|
37
|
+
export const RawInputTextarea = forwardRef<Omit<RawInputTextareaProps, 'type'>, HTMLTextAreaElement>((props, ref) => {
|
|
38
|
+
const defaultTheme = useTheme() || Theme.light;
|
|
39
|
+
const {
|
|
40
|
+
className,
|
|
41
|
+
theme = defaultTheme,
|
|
42
|
+
minimumRows = DEFAULT_PROPS.minimumRows as number,
|
|
43
|
+
value,
|
|
44
|
+
name,
|
|
45
|
+
onChange,
|
|
46
|
+
...forwardedProps
|
|
47
|
+
} = props;
|
|
48
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
49
|
+
|
|
50
|
+
const rows = useFitRowsToContent(minimumRows, textareaRef, value);
|
|
51
|
+
|
|
52
|
+
const handleChange: ChangeEventHandler<HTMLTextAreaElement> = useCallback(
|
|
53
|
+
(evt) => {
|
|
54
|
+
onChange?.(evt.target.value, name, evt);
|
|
55
|
+
},
|
|
56
|
+
[onChange, name],
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<textarea
|
|
61
|
+
{...forwardedProps}
|
|
62
|
+
name={name}
|
|
63
|
+
className={classNames(
|
|
64
|
+
className,
|
|
65
|
+
handleBasicClasses({ prefix: INPUT_NATIVE_CLASSNAME, theme }),
|
|
66
|
+
`${INPUT_NATIVE_CLASSNAME}--textarea`,
|
|
67
|
+
)}
|
|
68
|
+
ref={useMergeRefs(ref, textareaRef)}
|
|
69
|
+
onChange={handleChange}
|
|
70
|
+
value={value}
|
|
71
|
+
rows={rows}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Mock } from 'vitest';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
2
4
|
import camelCase from 'lodash/camelCase';
|
|
3
5
|
|
|
4
6
|
import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
|
|
@@ -15,6 +17,7 @@ import partition from 'lodash/partition';
|
|
|
15
17
|
import userEvent from '@testing-library/user-event';
|
|
16
18
|
|
|
17
19
|
import { isFocusVisible } from '@lumx/react/utils/browser/isFocusVisible';
|
|
20
|
+
import { createRef } from 'react';
|
|
18
21
|
import { TextField, TextFieldProps } from './TextField';
|
|
19
22
|
|
|
20
23
|
const CLASSNAME = TextField.className as string;
|
|
@@ -52,7 +55,8 @@ describe(`<${TextField.displayName}>`, () => {
|
|
|
52
55
|
|
|
53
56
|
describe('Render', () => {
|
|
54
57
|
it('should render defaults', () => {
|
|
55
|
-
const
|
|
58
|
+
const inputRef = createRef<HTMLInputElement>();
|
|
59
|
+
const { element, inputNative } = setup({ id: 'fixedId', inputRef });
|
|
56
60
|
expect(element).toBeInTheDocument();
|
|
57
61
|
|
|
58
62
|
expect(element).not.toHaveClass(`${CLASSNAME}--is-valid`);
|
|
@@ -65,13 +69,16 @@ describe(`<${TextField.displayName}>`, () => {
|
|
|
65
69
|
|
|
66
70
|
expect(element).toHaveClass(`${CLASSNAME}--theme-light`);
|
|
67
71
|
expect(inputNative.tagName).toBe('INPUT');
|
|
72
|
+
expect(inputRef.current).toBe(inputNative);
|
|
68
73
|
});
|
|
69
74
|
|
|
70
75
|
it('should render textarea', () => {
|
|
71
|
-
const
|
|
76
|
+
const inputRef = createRef<HTMLTextAreaElement>();
|
|
77
|
+
const { element, inputNative } = setup({ id: 'fixedId', multiline: true, inputRef });
|
|
72
78
|
expect(element).toBeInTheDocument();
|
|
73
79
|
|
|
74
80
|
expect(inputNative.tagName).toBe('TEXTAREA');
|
|
81
|
+
expect(inputRef.current).toBe(inputNative);
|
|
75
82
|
});
|
|
76
83
|
});
|
|
77
84
|
|
|
@@ -174,6 +181,8 @@ describe(`<${TextField.displayName}>`, () => {
|
|
|
174
181
|
expect(error).toHaveTextContent('error');
|
|
175
182
|
expect(helper).toHaveTextContent('helper');
|
|
176
183
|
expect(inputNative).toHaveAttribute('aria-describedby', expect.stringContaining('aria-description'));
|
|
184
|
+
expect(inputNative).toHaveAttribute('aria-describedby', expect.stringContaining('text-field-error-'));
|
|
185
|
+
expect(inputNative).toHaveAttribute('aria-describedby', expect.stringContaining('text-field-helper-'));
|
|
177
186
|
});
|
|
178
187
|
});
|
|
179
188
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { ReactNode, Ref, RefObject, SyntheticEvent,
|
|
1
|
+
import React, { ReactNode, Ref, RefObject, SyntheticEvent, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import get from 'lodash/get';
|
|
5
4
|
|
|
6
5
|
import { mdiAlertCircle, mdiCheckCircle, mdiCloseCircle } from '@lumx/icons';
|
|
7
6
|
import {
|
|
@@ -17,7 +16,7 @@ import {
|
|
|
17
16
|
Theme,
|
|
18
17
|
} from '@lumx/react';
|
|
19
18
|
import { GenericProps, HasTheme } from '@lumx/react/utils/type';
|
|
20
|
-
import {
|
|
19
|
+
import { handleBasicClasses } from '@lumx/core/js/utils/className';
|
|
21
20
|
import { mergeRefs } from '@lumx/react/utils/react/mergeRefs';
|
|
22
21
|
import { useId } from '@lumx/react/hooks/useId';
|
|
23
22
|
import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
|
|
@@ -25,6 +24,10 @@ import { forwardRef } from '@lumx/react/utils/react/forwardRef';
|
|
|
25
24
|
import { useDisableStateProps } from '@lumx/react/utils/disabled/useDisableStateProps';
|
|
26
25
|
import { HasAriaDisabled } from '@lumx/react/utils/type/HasAriaDisabled';
|
|
27
26
|
|
|
27
|
+
import { CLASSNAME, COMPONENT_NAME } from './constants';
|
|
28
|
+
import { RawInputText } from './RawInputText';
|
|
29
|
+
import { RawInputTextarea } from './RawInputTextarea';
|
|
30
|
+
|
|
28
31
|
/**
|
|
29
32
|
* Defines the props of the component.
|
|
30
33
|
*/
|
|
@@ -86,21 +89,6 @@ export interface TextFieldProps extends GenericProps, HasTheme, HasAriaDisabled
|
|
|
86
89
|
onFocus?(event: React.FocusEvent): void;
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
/**
|
|
90
|
-
* Component display name.
|
|
91
|
-
*/
|
|
92
|
-
const COMPONENT_NAME = 'TextField';
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Component default class name and class prefix.
|
|
96
|
-
*/
|
|
97
|
-
const CLASSNAME = getRootClassName(COMPONENT_NAME);
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Default minimum number of rows in the multiline mode.
|
|
101
|
-
*/
|
|
102
|
-
const DEFAULT_MIN_ROWS = 2;
|
|
103
|
-
|
|
104
92
|
/**
|
|
105
93
|
* Component default props.
|
|
106
94
|
*/
|
|
@@ -108,149 +96,6 @@ const DEFAULT_PROPS: Partial<TextFieldProps> = {
|
|
|
108
96
|
type: 'text',
|
|
109
97
|
};
|
|
110
98
|
|
|
111
|
-
/**
|
|
112
|
-
* Hook that allows to calculate the number of rows needed for a text area.
|
|
113
|
-
* @param minimumRows Minimum number of rows that we want to display.
|
|
114
|
-
* @return rows to be used and a callback to recalculate
|
|
115
|
-
*/
|
|
116
|
-
const useComputeNumberOfRows = (
|
|
117
|
-
minimumRows: number,
|
|
118
|
-
): {
|
|
119
|
-
/** number of rows to be used on the text area */
|
|
120
|
-
rows: number;
|
|
121
|
-
/**
|
|
122
|
-
* Callback in order to recalculate the number of rows due to a change on the text area
|
|
123
|
-
*/
|
|
124
|
-
recomputeNumberOfRows(target: Element): void;
|
|
125
|
-
} => {
|
|
126
|
-
const [rows, setRows] = useState(minimumRows);
|
|
127
|
-
|
|
128
|
-
const recompute = (target: Element) => {
|
|
129
|
-
/**
|
|
130
|
-
* HEAD's UP! This part is a little bit tricky. The idea here is to only
|
|
131
|
-
* display the necessary rows on the textarea. In order to dynamically adjust
|
|
132
|
-
* the height on that field, we need to:
|
|
133
|
-
* 1. Set the current amount of rows to the minimum. That will make the scroll appear.
|
|
134
|
-
* 2. With that, we will have the `scrollHeight`, meaning the height of the container adjusted to the current content
|
|
135
|
-
* 3. With the scroll height, we can figure out how many rows we need to use by dividing the scroll height
|
|
136
|
-
* by the line height.
|
|
137
|
-
* 4. With that number, we can readjust the number of rows on the text area. We need to do that here, if we leave that to
|
|
138
|
-
* the state change through React, there are some scenarios (resize, hitting ENTER or BACKSPACE which add or remove lines)
|
|
139
|
-
* when we will not see the update and the rows will be resized to the minimum.
|
|
140
|
-
* 5. In case there is any other update on the component that changes the UI, we need to keep the number of rows
|
|
141
|
-
* on the state in order to allow React to re-render. Therefore, we save them using `useState`
|
|
142
|
-
*/
|
|
143
|
-
// eslint-disable-next-line no-param-reassign
|
|
144
|
-
(target as HTMLTextAreaElement).rows = minimumRows;
|
|
145
|
-
let currentRows = target.scrollHeight / (target.clientHeight / minimumRows);
|
|
146
|
-
currentRows = currentRows >= minimumRows ? currentRows : minimumRows;
|
|
147
|
-
// eslint-disable-next-line no-param-reassign
|
|
148
|
-
(target as HTMLTextAreaElement).rows = currentRows;
|
|
149
|
-
|
|
150
|
-
setRows(currentRows);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
recomputeNumberOfRows: recompute,
|
|
155
|
-
rows,
|
|
156
|
-
};
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
interface InputNativeProps {
|
|
160
|
-
id?: string;
|
|
161
|
-
inputRef?: TextFieldProps['inputRef'];
|
|
162
|
-
isDisabled?: boolean;
|
|
163
|
-
'aria-disabled'?: boolean;
|
|
164
|
-
isRequired?: boolean;
|
|
165
|
-
readOnly?: boolean;
|
|
166
|
-
multiline?: boolean;
|
|
167
|
-
maxLength?: number;
|
|
168
|
-
placeholder?: string;
|
|
169
|
-
rows: number;
|
|
170
|
-
type: TextFieldProps['type'];
|
|
171
|
-
name?: string;
|
|
172
|
-
value?: string;
|
|
173
|
-
setFocus(focus: boolean): void;
|
|
174
|
-
recomputeNumberOfRows(target: Element): void;
|
|
175
|
-
onChange(value: string, name?: string, event?: SyntheticEvent): void;
|
|
176
|
-
onFocus?(value: React.FocusEvent): void;
|
|
177
|
-
onBlur?(value: React.FocusEvent): void;
|
|
178
|
-
hasError?: boolean;
|
|
179
|
-
describedById?: string;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const renderInputNative: React.FC<InputNativeProps> = (props) => {
|
|
183
|
-
const {
|
|
184
|
-
id,
|
|
185
|
-
isRequired,
|
|
186
|
-
placeholder,
|
|
187
|
-
multiline,
|
|
188
|
-
value,
|
|
189
|
-
setFocus,
|
|
190
|
-
onChange,
|
|
191
|
-
onFocus,
|
|
192
|
-
onBlur,
|
|
193
|
-
inputRef,
|
|
194
|
-
rows,
|
|
195
|
-
recomputeNumberOfRows,
|
|
196
|
-
type,
|
|
197
|
-
name,
|
|
198
|
-
hasError,
|
|
199
|
-
describedById,
|
|
200
|
-
...forwardedProps
|
|
201
|
-
} = props;
|
|
202
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
203
|
-
const ref = useRef<HTMLElement>(null);
|
|
204
|
-
|
|
205
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
// Recompute the number of rows for the first rendering
|
|
208
|
-
if (multiline && ref && ref.current) {
|
|
209
|
-
recomputeNumberOfRows(ref.current);
|
|
210
|
-
}
|
|
211
|
-
}, [ref, multiline, recomputeNumberOfRows, value]);
|
|
212
|
-
|
|
213
|
-
const onTextFieldFocus = (event: React.FocusEvent) => {
|
|
214
|
-
onFocus?.(event);
|
|
215
|
-
setFocus(true);
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
const onTextFieldBlur = (event: React.FocusEvent) => {
|
|
219
|
-
onBlur?.(event);
|
|
220
|
-
setFocus(false);
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
const handleChange = (event: React.ChangeEvent) => {
|
|
224
|
-
onChange(get(event, 'target.value'), name, event);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
const Component = multiline ? 'textarea' : 'input';
|
|
228
|
-
const inputProps: any = {
|
|
229
|
-
...forwardedProps,
|
|
230
|
-
id,
|
|
231
|
-
className: multiline
|
|
232
|
-
? `${CLASSNAME}__input-native ${CLASSNAME}__input-native--textarea`
|
|
233
|
-
: `${CLASSNAME}__input-native ${CLASSNAME}__input-native--text`,
|
|
234
|
-
placeholder,
|
|
235
|
-
value,
|
|
236
|
-
name,
|
|
237
|
-
required: isRequired,
|
|
238
|
-
onFocus: onTextFieldFocus,
|
|
239
|
-
onBlur: onTextFieldBlur,
|
|
240
|
-
onChange: handleChange,
|
|
241
|
-
'aria-invalid': hasError ? 'true' : undefined,
|
|
242
|
-
'aria-describedby': describedById,
|
|
243
|
-
readOnly: forwardedProps.readOnly || forwardedProps['aria-disabled'],
|
|
244
|
-
ref: mergeRefs(inputRef as any, ref) as any,
|
|
245
|
-
};
|
|
246
|
-
if (multiline) {
|
|
247
|
-
inputProps.rows = rows;
|
|
248
|
-
} else {
|
|
249
|
-
inputProps.type = type;
|
|
250
|
-
}
|
|
251
|
-
return <Component {...inputProps} />;
|
|
252
|
-
};
|
|
253
|
-
|
|
254
99
|
/**
|
|
255
100
|
* TextField component.
|
|
256
101
|
*
|
|
@@ -310,7 +155,6 @@ export const TextField = forwardRef<TextFieldProps, HTMLDivElement>((props, ref)
|
|
|
310
155
|
const describedById = describedByIds.length === 0 ? undefined : describedByIds.join(' ');
|
|
311
156
|
|
|
312
157
|
const [isFocus, setFocus] = useState(false);
|
|
313
|
-
const { rows, recomputeNumberOfRows } = useComputeNumberOfRows(multiline ? minimumRows || DEFAULT_MIN_ROWS : 0);
|
|
314
158
|
const valueLength = (value || '').length;
|
|
315
159
|
const isNotEmpty = valueLength > 0;
|
|
316
160
|
|
|
@@ -339,6 +183,36 @@ export const TextField = forwardRef<TextFieldProps, HTMLDivElement>((props, ref)
|
|
|
339
183
|
}
|
|
340
184
|
};
|
|
341
185
|
|
|
186
|
+
const inputProps = {
|
|
187
|
+
id: textFieldId,
|
|
188
|
+
ref: inputRef as any,
|
|
189
|
+
...disabledStateProps,
|
|
190
|
+
...forwardedProps,
|
|
191
|
+
required: isRequired,
|
|
192
|
+
maxLength,
|
|
193
|
+
onBlur(evt: React.FocusEvent) {
|
|
194
|
+
setFocus(false);
|
|
195
|
+
onBlur?.(evt);
|
|
196
|
+
},
|
|
197
|
+
onFocus(evt: React.FocusEvent) {
|
|
198
|
+
setFocus(true);
|
|
199
|
+
onFocus?.(evt);
|
|
200
|
+
},
|
|
201
|
+
placeholder,
|
|
202
|
+
value,
|
|
203
|
+
onChange,
|
|
204
|
+
name,
|
|
205
|
+
'aria-invalid': hasError || undefined,
|
|
206
|
+
'aria-describedby': describedById,
|
|
207
|
+
readOnly: forwardedProps.readOnly || disabledStateProps['aria-disabled'],
|
|
208
|
+
theme,
|
|
209
|
+
};
|
|
210
|
+
const input = multiline ? (
|
|
211
|
+
<RawInputTextarea {...inputProps} minimumRows={minimumRows} />
|
|
212
|
+
) : (
|
|
213
|
+
<RawInputText type={type} {...inputProps} />
|
|
214
|
+
);
|
|
215
|
+
|
|
342
216
|
return (
|
|
343
217
|
<div
|
|
344
218
|
ref={ref}
|
|
@@ -395,58 +269,14 @@ export const TextField = forwardRef<TextFieldProps, HTMLDivElement>((props, ref)
|
|
|
395
269
|
/>
|
|
396
270
|
)}
|
|
397
271
|
|
|
398
|
-
{chips
|
|
272
|
+
{chips ? (
|
|
399
273
|
<div className={`${CLASSNAME}__chips`}>
|
|
400
274
|
{chips}
|
|
401
275
|
|
|
402
|
-
{
|
|
403
|
-
id: textFieldId,
|
|
404
|
-
inputRef,
|
|
405
|
-
...disabledStateProps,
|
|
406
|
-
isRequired,
|
|
407
|
-
maxLength,
|
|
408
|
-
multiline,
|
|
409
|
-
onBlur,
|
|
410
|
-
onChange,
|
|
411
|
-
onFocus,
|
|
412
|
-
placeholder,
|
|
413
|
-
recomputeNumberOfRows,
|
|
414
|
-
rows,
|
|
415
|
-
setFocus,
|
|
416
|
-
type,
|
|
417
|
-
value,
|
|
418
|
-
name,
|
|
419
|
-
hasError,
|
|
420
|
-
describedById,
|
|
421
|
-
...forwardedProps,
|
|
422
|
-
})}
|
|
423
|
-
</div>
|
|
424
|
-
)}
|
|
425
|
-
|
|
426
|
-
{!chips && (
|
|
427
|
-
<div className={`${CLASSNAME}__input-wrapper`}>
|
|
428
|
-
{renderInputNative({
|
|
429
|
-
id: textFieldId,
|
|
430
|
-
inputRef,
|
|
431
|
-
...disabledStateProps,
|
|
432
|
-
isRequired,
|
|
433
|
-
maxLength,
|
|
434
|
-
multiline,
|
|
435
|
-
onBlur,
|
|
436
|
-
onChange,
|
|
437
|
-
onFocus,
|
|
438
|
-
placeholder,
|
|
439
|
-
recomputeNumberOfRows,
|
|
440
|
-
rows,
|
|
441
|
-
setFocus,
|
|
442
|
-
type,
|
|
443
|
-
value,
|
|
444
|
-
name,
|
|
445
|
-
hasError,
|
|
446
|
-
describedById,
|
|
447
|
-
...forwardedProps,
|
|
448
|
-
})}
|
|
276
|
+
{input}
|
|
449
277
|
</div>
|
|
278
|
+
) : (
|
|
279
|
+
<div className={`${CLASSNAME}__input-wrapper`}>{input}</div>
|
|
450
280
|
)}
|
|
451
281
|
|
|
452
282
|
{(isValid || hasError) && (
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getRootClassName } from '@lumx/core/js/utils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Component display name.
|
|
5
|
+
*/
|
|
6
|
+
export const COMPONENT_NAME = 'TextField';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Component default class name and class prefix.
|
|
10
|
+
*/
|
|
11
|
+
export const CLASSNAME = getRootClassName(COMPONENT_NAME);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Input native element class name.
|
|
15
|
+
*/
|
|
16
|
+
export const INPUT_NATIVE_CLASSNAME = `${CLASSNAME}__input-native`;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fit textarea rows to its content
|
|
5
|
+
*/
|
|
6
|
+
export function useFitRowsToContent(
|
|
7
|
+
minimumRows: number,
|
|
8
|
+
textareaRef: React.RefObject<HTMLTextAreaElement>,
|
|
9
|
+
value: string | undefined,
|
|
10
|
+
) {
|
|
11
|
+
const [rows, setRows] = useState(minimumRows);
|
|
12
|
+
React.useLayoutEffect(() => {
|
|
13
|
+
const { current: textarea } = textareaRef;
|
|
14
|
+
if (!textarea) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Save the current number of rows to restore it later and avoid a flicker.
|
|
19
|
+
const previousRows = textarea.rows;
|
|
20
|
+
|
|
21
|
+
// Set the rows to the minimum to get a baseline for row height.
|
|
22
|
+
// This is necessary to get a consistent row height calculation.
|
|
23
|
+
textarea.rows = minimumRows;
|
|
24
|
+
const rowHeight = textarea.clientHeight / minimumRows;
|
|
25
|
+
|
|
26
|
+
// Set rows to 1 to get the smallest possible textarea, which forces
|
|
27
|
+
// scrollHeight to be the actual content height.
|
|
28
|
+
textarea.rows = 1;
|
|
29
|
+
const { scrollHeight } = textarea;
|
|
30
|
+
|
|
31
|
+
// Restore the rows to the previous value.
|
|
32
|
+
textarea.rows = previousRows;
|
|
33
|
+
|
|
34
|
+
// Calculate the number of rows required to display the content.
|
|
35
|
+
// Fallback to `minimumRows` if `rowHeight` is 0.
|
|
36
|
+
const requiredRows = rowHeight > 0 ? Math.ceil(scrollHeight / rowHeight) : minimumRows;
|
|
37
|
+
|
|
38
|
+
// Update the rows state with the new calculated value.
|
|
39
|
+
setRows(Math.max(requiredRows, minimumRows));
|
|
40
|
+
}, [value, minimumRows, textareaRef]);
|
|
41
|
+
return rows;
|
|
42
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
|
|
3
1
|
import { Button, Dialog, Dropdown, Placement, Tooltip } from '@lumx/react';
|
|
2
|
+
import React, { useState } from 'react';
|
|
4
3
|
import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
|
|
5
4
|
import { withChromaticForceScreenSize } from '@lumx/react/stories/decorators/withChromaticForceScreenSize';
|
|
6
5
|
import { ARIA_LINK_MODES } from '@lumx/react/components/tooltip/constants';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { MockInstance } from 'vitest';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
|
|
3
|
-
import { MockInstance } from 'vitest';
|
|
4
4
|
import { Button } from '@lumx/react';
|
|
5
5
|
import { screen, render } from '@testing-library/react';
|
|
6
6
|
import { queryAllByTagName, queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
/* eslint-disable jsx-a11y/anchor-is-valid */
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
2
4
|
import { mdiMenuDown, mdiStar } from '@lumx/icons';
|
|
3
5
|
import { Badge, ColorPalette, Icon, IconButton, Link, Orientation, Size, Text } from '@lumx/react';
|
|
4
6
|
import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
|
package/src/hooks/useId.test.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
3
|
-
import { useCallback, useRef, useState } from 'react';
|
|
3
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
4
4
|
import { Button } from '@lumx/react';
|
|
5
5
|
import { ClickAwayProvider } from '@lumx/react/utils/ClickAwayProvider';
|
|
6
6
|
import { initDemoShadowDOMPortal } from '@lumx/react/stories/utils/initDemoShadowDOMPortal';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, RefObject, useContext, useEffect, useMemo, useRef } from 'react';
|
|
1
|
+
import React, { createContext, RefObject, useContext, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { ClickAwayParameters, useClickAway } from '@lumx/react/hooks/useClickAway';
|
|
3
3
|
|
|
4
4
|
interface ContextValue {
|