@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.
- package/CONTRIBUTING.md +1 -5
- package/README.md +0 -2
- package/_internal/index.js +20 -13
- package/_internal/index.js.map +1 -1
- package/index.d.ts +35 -35
- package/index.js +2407 -2447
- package/index.js.map +1 -1
- package/package.json +10 -13
- package/src/components/alert-dialog/AlertDialog.stories.tsx +1 -1
- package/src/components/alert-dialog/AlertDialog.test.tsx +3 -4
- package/src/components/autocomplete/Autocomplete.stories.tsx +1 -1
- package/src/components/autocomplete/Autocomplete.test.tsx +3 -5
- package/src/components/autocomplete/Autocomplete.tsx +1 -1
- package/src/components/autocomplete/AutocompleteMultiple.stories.tsx +1 -1
- package/src/components/autocomplete/AutocompleteMultiple.test.tsx +0 -2
- package/src/components/autocomplete/AutocompleteMultiple.tsx +1 -1
- package/src/components/avatar/Avatar.stories.tsx +0 -2
- package/src/components/avatar/Avatar.test.tsx +0 -2
- package/src/components/avatar/Avatar.tsx +1 -1
- package/src/components/badge/Badge.stories.tsx +0 -1
- package/src/components/badge/Badge.test.tsx +0 -2
- package/src/components/badge/Badge.tsx +1 -1
- package/src/components/badge/BadgeWrapper.stories.tsx +0 -1
- package/src/components/badge/BadgeWrapper.test.tsx +0 -2
- package/src/components/badge/BadgeWrapper.tsx +1 -1
- package/src/components/button/Button.stories.tsx +0 -1
- package/src/components/button/Button.test.tsx +9 -11
- package/src/components/button/Button.tsx +0 -2
- package/src/components/button/ButtonGroup.stories.tsx +0 -2
- package/src/components/button/ButtonGroup.test.tsx +0 -2
- package/src/components/button/ButtonGroup.tsx +0 -2
- package/src/components/button/ButtonRoot.tsx +7 -37
- package/src/components/button/IconButton.test.tsx +0 -2
- package/src/components/button/IconButton.tsx +0 -2
- package/src/components/checkbox/Checkbox.test.tsx +3 -5
- package/src/components/chip/Chip.stories.tsx +0 -2
- package/src/components/chip/Chip.test.tsx +19 -19
- package/src/components/chip/Chip.tsx +1 -1
- package/src/components/chip/ChipGroup.stories.tsx +0 -2
- package/src/components/chip/ChipGroup.test.tsx +0 -2
- package/src/components/chip/ChipGroup.tsx +1 -1
- package/src/components/comment-block/CommentBlock.stories.tsx +0 -1
- package/src/components/comment-block/CommentBlock.test.tsx +0 -1
- package/src/components/comment-block/CommentBlock.tsx +1 -1
- package/src/components/date-picker/DatePicker.test.tsx +3 -5
- package/src/components/date-picker/DatePicker.tsx +1 -1
- package/src/components/date-picker/DatePickerControlled.test.tsx +6 -8
- package/src/components/date-picker/DatePickerField.test.tsx +3 -5
- package/src/components/dialog/Dialog.test.tsx +4 -6
- package/src/components/divider/Divider.test.tsx +0 -2
- package/src/components/divider/Divider.tsx +0 -2
- package/src/components/drag-handle/DragHandle.test.tsx +0 -2
- package/src/components/drag-handle/DragHandle.tsx +0 -2
- package/src/components/dropdown/Dropdown.stories.tsx +1 -1
- package/src/components/dropdown/Dropdown.test.tsx +3 -3
- package/src/components/dropdown/Dropdown.tsx +1 -1
- package/src/components/expansion-panel/ExpansionPanel.test.tsx +6 -7
- package/src/components/flag/Flag.test.tsx +0 -2
- package/src/components/flag/Flag.tsx +0 -2
- package/src/components/flex-box/FlexBox.stories.tsx +0 -2
- package/src/components/flex-box/FlexBox.test.tsx +0 -1
- 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 +0 -2
- package/src/components/grid/GridItem.tsx +0 -2
- package/src/components/grid-column/GridColumn.stories.tsx +0 -1
- package/src/components/grid-column/GridColumn.test.jsx +0 -2
- package/src/components/grid-column/GridColumn.tsx +1 -1
- package/src/components/heading/Heading.stories.tsx +0 -1
- package/src/components/heading/Heading.test.tsx +0 -2
- package/src/components/heading/Heading.tsx +0 -2
- package/src/components/heading/HeadingLevelProvider.tsx +1 -1
- package/src/components/icon/Icon.stories.tsx +30 -4
- package/src/components/icon/Icon.test.tsx +85 -4
- package/src/components/icon/Icon.tsx +118 -9
- package/src/components/image-block/ImageBlock.stories.tsx +0 -2
- package/src/components/image-block/ImageBlock.test.tsx +0 -1
- 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 +0 -1
- package/src/components/image-lightbox/ImageLightbox.test.tsx +11 -9
- package/src/components/image-lightbox/types.ts +0 -2
- package/src/components/inline-list/InlineList.stories.tsx +0 -1
- package/src/components/inline-list/InlineList.test.tsx +0 -2
- package/src/components/inline-list/InlineList.tsx +1 -1
- package/src/components/input-helper/InputHelper.test.tsx +0 -2
- package/src/components/input-helper/InputHelper.tsx +1 -1
- package/src/components/input-label/InputLabel.stories.tsx +0 -2
- package/src/components/input-label/InputLabel.test.tsx +0 -2
- package/src/components/input-label/InputLabel.tsx +1 -1
- package/src/components/lightbox/Lightbox.test.tsx +0 -2
- package/src/components/lightbox/Lightbox.tsx +1 -1
- package/src/components/link/Link.stories.tsx +0 -1
- package/src/components/link/Link.test.tsx +13 -13
- package/src/components/link/Link.tsx +9 -22
- package/src/components/link-preview/LinkPreview.test.tsx +0 -2
- package/src/components/link-preview/LinkPreview.tsx +0 -2
- package/src/components/list/List.stories.tsx +1 -1
- package/src/components/list/List.test.tsx +0 -2
- package/src/components/list/List.tsx +1 -1
- package/src/components/list/ListDivider.test.tsx +0 -2
- package/src/components/list/ListDivider.tsx +0 -2
- package/src/components/list/ListItem.test.tsx +5 -7
- package/src/components/list/ListItem.tsx +1 -1
- package/src/components/list/ListSubheader.test.tsx +0 -2
- package/src/components/list/ListSubheader.tsx +1 -1
- package/src/components/message/Message.test.tsx +1 -2
- package/src/components/message/Message.tsx +1 -1
- package/src/components/mosaic/Mosaic.test.tsx +3 -5
- package/src/components/mosaic/Mosaic.tsx +1 -1
- package/src/components/navigation/Navigation.stories.tsx +0 -2
- package/src/components/navigation/Navigation.test.tsx +0 -2
- package/src/components/navigation/Navigation.tsx +0 -2
- package/src/components/navigation/NavigationItem.test.tsx +0 -2
- package/src/components/navigation/NavigationItem.tsx +7 -11
- package/src/components/navigation/NavigationSection.test.tsx +0 -2
- package/src/components/navigation/NavigationSection.tsx +5 -4
- package/src/components/notification/Notification.test.tsx +4 -5
- package/src/components/notification/Notification.tsx +1 -1
- package/src/components/popover/Popover.test.tsx +0 -2
- 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 -2
- package/src/components/popover-dialog/PopoverDialog.tsx +0 -2
- package/src/components/post-block/PostBlock.test.tsx +0 -2
- package/src/components/post-block/PostBlock.tsx +1 -1
- package/src/components/progress/Progress.tsx +0 -2
- package/src/components/progress/ProgressCircular.stories.tsx +0 -1
- package/src/components/progress/ProgressCircular.test.tsx +0 -2
- package/src/components/progress/ProgressCircular.tsx +0 -2
- package/src/components/progress/ProgressLinear.test.tsx +0 -2
- package/src/components/progress/ProgressLinear.tsx +0 -2
- package/src/components/progress-tracker/ProgressTracker.stories.tsx +1 -1
- package/src/components/progress-tracker/ProgressTracker.test.tsx +0 -2
- package/src/components/progress-tracker/ProgressTrackerProvider.test.tsx +0 -2
- package/src/components/progress-tracker/ProgressTrackerProvider.tsx +1 -1
- package/src/components/progress-tracker/ProgressTrackerStep.test.tsx +0 -2
- package/src/components/progress-tracker/ProgressTrackerStep.tsx +1 -1
- package/src/components/progress-tracker/ProgressTrackerStepPanel.test.tsx +0 -2
- package/src/components/progress-tracker/ProgressTrackerStepPanel.tsx +0 -2
- package/src/components/radio-button/RadioButton.test.tsx +3 -5
- 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 +0 -2
- 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 +8 -9
- package/src/components/select/Select.tsx +1 -1
- package/src/components/select/SelectMultiple.stories.tsx +1 -1
- package/src/components/select/SelectMultiple.test.tsx +5 -7
- 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 +0 -2
- package/src/components/side-navigation/SideNavigation.tsx +1 -1
- package/src/components/side-navigation/SideNavigationItem.test.tsx +2 -4
- package/src/components/side-navigation/SideNavigationItem.tsx +23 -28
- package/src/components/skeleton/SkeletonCircle.test.tsx +0 -2
- package/src/components/skeleton/SkeletonCircle.tsx +0 -2
- package/src/components/skeleton/SkeletonRectangle.test.tsx +0 -2
- package/src/components/skeleton/SkeletonRectangle.tsx +0 -2
- package/src/components/skeleton/SkeletonTypography.stories.tsx +0 -2
- package/src/components/skeleton/SkeletonTypography.test.tsx +0 -2
- package/src/components/skeleton/SkeletonTypography.tsx +1 -1
- package/src/components/slider/Slider.test.tsx +1 -3
- package/src/components/slider/Slider.tsx +1 -1
- package/src/components/slideshow/Slideshow.stories.tsx +0 -1
- package/src/components/slideshow/Slideshow.test.tsx +0 -2
- package/src/components/slideshow/SlideshowControls.stories.tsx +0 -2
- package/src/components/slideshow/SlideshowItem.tsx +0 -2
- package/src/components/slideshow/useSlideFocusManagement.tsx +1 -1
- package/src/components/switch/Switch.test.tsx +5 -7
- package/src/components/switch/Switch.tsx +1 -1
- package/src/components/table/Table.test.tsx +0 -2
- package/src/components/table/Table.tsx +0 -2
- package/src/components/table/TableBody.test.tsx +0 -2
- package/src/components/table/TableBody.tsx +0 -2
- package/src/components/table/TableCell.test.tsx +1 -3
- package/src/components/table/TableCell.tsx +0 -2
- package/src/components/table/TableHeader.test.tsx +0 -2
- package/src/components/table/TableHeader.tsx +0 -2
- package/src/components/table/TableRow.test.tsx +0 -2
- package/src/components/table/TableRow.tsx +0 -2
- package/src/components/tabs/Tab.test.tsx +0 -2
- package/src/components/tabs/Tab.tsx +1 -1
- package/src/components/tabs/TabList.test.tsx +0 -2
- package/src/components/tabs/TabPanel.test.tsx +0 -2
- package/src/components/tabs/TabPanel.tsx +0 -2
- package/src/components/tabs/TabProvider.test.tsx +0 -2
- 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 +0 -2
- package/src/components/text/Text.tsx +0 -2
- package/src/components/text-field/TextField.test.tsx +9 -10
- package/src/components/text-field/TextField.tsx +1 -1
- package/src/components/thumbnail/Thumbnail.test.tsx +29 -7
- package/src/components/thumbnail/Thumbnail.tsx +11 -11
- package/src/components/toolbar/Toolbar.tsx +1 -1
- package/src/components/tooltip/Tooltip.stories.tsx +2 -1
- package/src/components/tooltip/Tooltip.test.tsx +14 -8
- package/src/components/uploader/Uploader.test.tsx +2 -4
- package/src/components/user-block/UserBlock.stories.tsx +0 -2
- package/src/components/user-block/UserBlock.test.tsx +1 -3
- package/src/hooks/useId.test.tsx +0 -1
- package/src/hooks/useInfiniteScroll.tsx +1 -1
- package/src/hooks/usePreviousValue.ts +0 -1
- package/src/stories/decorators/withChromaticForceScreenSize.tsx +0 -1
- package/src/stories/decorators/withNestedProps.tsx +0 -1
- package/src/stories/decorators/withThemedBackground.tsx +0 -2
- package/src/stories/decorators/withWrapper.tsx +0 -2
- package/src/stories/utils/CustomLink.tsx +0 -1
- package/src/testing/utils/ThemeSentinel.tsx +0 -1
- package/src/untypped-modules.d.ts +4 -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 +0 -1
- package/src/utils/Portal/PortalProvider.stories.jsx +0 -1
- package/src/utils/Portal/PortalProvider.test.tsx +1 -2
- package/src/utils/date/getYearDisplayName.test.ts +1 -1
- package/src/utils/disabled/DisabledStateProvider.stories.tsx +0 -2
- package/src/utils/disabled/useDisableStateProps.test.tsx +2 -2
- package/src/utils/react/OnBeforeUnmount.tsx +1 -1
- package/src/utils/react/RawClickable.test.tsx +153 -0
- package/src/utils/react/RawClickable.tsx +65 -0
- package/src/utils/react/skipRender.tsx +2 -2
- package/src/utils/react/wrapChildrenIconWithSpaces.test.tsx +1 -1
- package/src/utils/type/HasPolymorphicAs.ts +0 -2
- package/src/utils/type/HasRequiredLinkHref.ts +1 -0
- package/src/utils/type/index.ts +1 -0
- package/utils/index.d.ts +6 -6
- package/utils/index.js +1 -1
- package/src/utils/react/renderButtonOrLink.tsx +0 -16
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
import { MockInstance } from 'vitest';
|
|
3
4
|
import { Button } from '@lumx/react';
|
|
4
5
|
import { screen, render } from '@testing-library/react';
|
|
5
6
|
import { queryAllByTagName, queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
@@ -12,13 +13,18 @@ import { Tooltip, TooltipProps } from './Tooltip';
|
|
|
12
13
|
|
|
13
14
|
const CLASSNAME = Tooltip.className as string;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
vi.mock('@lumx/react/utils/browser/isFocusVisible');
|
|
17
|
+
vi.mock('@lumx/react/hooks/useId', () => ({ useId: () => ':r1:' }));
|
|
17
18
|
// Skip delays
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
vi.mock('@lumx/react/constants', async (importActual) => {
|
|
20
|
+
const actual = (await importActual()) as Record<string, any>;
|
|
21
|
+
return {
|
|
22
|
+
...actual,
|
|
23
|
+
TOOLTIP_HOVER_DELAY: { open: 0, close: 0 },
|
|
24
|
+
VISUALLY_HIDDEN: actual.VISUALLY_HIDDEN,
|
|
25
|
+
CSS_PREFIX: actual.CSS_PREFIX,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
30
|
* Mounts the component and returns common DOM elements / data needed in multiple tests further down.
|
|
@@ -346,7 +352,7 @@ describe(`<${Tooltip.displayName}>`, () => {
|
|
|
346
352
|
});
|
|
347
353
|
|
|
348
354
|
it('should activate on anchor focus visible and close on escape', async () => {
|
|
349
|
-
(isFocusVisible as
|
|
355
|
+
(isFocusVisible as unknown as MockInstance).mockReturnValue(true);
|
|
350
356
|
let { tooltip } = await setup({
|
|
351
357
|
label: 'Tooltip label',
|
|
352
358
|
children: <Button>Anchor</Button>,
|
|
@@ -381,7 +387,7 @@ describe(`<${Tooltip.displayName}>`, () => {
|
|
|
381
387
|
});
|
|
382
388
|
|
|
383
389
|
it('should not activate on anchor focus if not visible', async () => {
|
|
384
|
-
(isFocusVisible as
|
|
390
|
+
(isFocusVisible as unknown as MockInstance).mockReturnValue(false);
|
|
385
391
|
let { tooltip } = await setup({
|
|
386
392
|
label: 'Tooltip label',
|
|
387
393
|
children: <Button>Anchor</Button>,
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
1
|
import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
|
|
4
2
|
|
|
5
3
|
import { render, screen } from '@testing-library/react';
|
|
@@ -89,9 +87,9 @@ describe(`<${Uploader.displayName}>`, () => {
|
|
|
89
87
|
${'button'} | ${{}}
|
|
90
88
|
${'button isDisabled '} | ${{ isDisabled: true }}
|
|
91
89
|
${'button aria-disabled'} | ${{ 'aria-disabled': true }}
|
|
92
|
-
${'file input '} | ${{ fileInputProps: { onChange:
|
|
90
|
+
${'file input '} | ${{ fileInputProps: { onChange: vi.fn() } }}
|
|
93
91
|
`('Events $name', ({ props }) => {
|
|
94
|
-
const onClick =
|
|
92
|
+
const onClick = vi.fn();
|
|
95
93
|
beforeEach(() => onClick.mockClear());
|
|
96
94
|
const assertClick = () => {
|
|
97
95
|
if (props.isDisabled || props['aria-disabled']) {
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
/* eslint-disable jsx-a11y/anchor-is-valid */
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
2
|
import { mdiMenuDown, mdiStar } from '@lumx/icons';
|
|
5
3
|
import { Badge, ColorPalette, Icon, IconButton, Link, Orientation, Size, Text } from '@lumx/react';
|
|
6
4
|
import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
1
|
import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
|
|
4
2
|
import { render, within, screen } from '@testing-library/react';
|
|
5
3
|
import { getByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
@@ -36,7 +34,7 @@ describe(`<${UserBlock.displayName}>`, () => {
|
|
|
36
34
|
});
|
|
37
35
|
|
|
38
36
|
it('should render button', async () => {
|
|
39
|
-
const onClick =
|
|
37
|
+
const onClick = vi.fn();
|
|
40
38
|
const { name, thumbnail } = setup({
|
|
41
39
|
onClick,
|
|
42
40
|
name: 'John Doe',
|
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
|
|
3
|
+
import { 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
|
|
1
|
+
import { createContext, RefObject, useContext, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { ClickAwayParameters, useClickAway } from '@lumx/react/hooks/useClickAway';
|
|
3
3
|
|
|
4
4
|
interface ContextValue {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { render, screen } from '@testing-library/react';
|
|
3
2
|
import { PortalInit, PortalProvider } from './PortalProvider';
|
|
4
3
|
import { Portal } from './Portal';
|
|
@@ -30,7 +29,7 @@ describe('PortalProvider', () => {
|
|
|
30
29
|
});
|
|
31
30
|
|
|
32
31
|
it('should call teardown on unmount', () => {
|
|
33
|
-
const teardownMock =
|
|
32
|
+
const teardownMock = vi.fn();
|
|
34
33
|
const portalContainer = document.createElement('div');
|
|
35
34
|
portalContainer.id = PORTAL_CONTAINER_ID;
|
|
36
35
|
document.body.appendChild(portalContainer);
|
|
@@ -4,7 +4,7 @@ describe(getYearDisplayName, () => {
|
|
|
4
4
|
beforeEach(() => {
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
6
6
|
// @ts-ignore
|
|
7
|
-
|
|
7
|
+
vi.spyOn(Intl, 'DisplayNames').mockImplementation(() => ({
|
|
8
8
|
resolvedOptions: () => ({
|
|
9
9
|
fallback: 'code',
|
|
10
10
|
locale: 'fr',
|
|
@@ -59,14 +59,14 @@ describe(useDisableStateProps.name, () => {
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
it('should forward onClick when not disabled', () => {
|
|
62
|
-
const onClick =
|
|
62
|
+
const onClick = vi.fn();
|
|
63
63
|
const { element } = setup({ onClick });
|
|
64
64
|
fireEvent.click(element);
|
|
65
65
|
expect(onClick).toHaveBeenCalled();
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
it('should not forward onClick when disabled', () => {
|
|
69
|
-
const onClick =
|
|
69
|
+
const onClick = vi.fn();
|
|
70
70
|
const { element } = setup({ disabled: true, onClick });
|
|
71
71
|
fireEvent.click(element);
|
|
72
72
|
expect(onClick).not.toHaveBeenCalled();
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { RawClickable, RawClickableProps } from './RawClickable';
|
|
5
|
+
import { CustomLink } from '../../stories/utils/CustomLink';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Mounts the component and returns common DOM elements / data needed in multiple tests.
|
|
9
|
+
*/
|
|
10
|
+
const setup = (props: RawClickableProps<any>) => {
|
|
11
|
+
render(<RawClickable {...props} data-testid="raw-element" />);
|
|
12
|
+
const element = screen.getByTestId('raw-element');
|
|
13
|
+
return { props, element };
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
describe(`<RawClickable>`, () => {
|
|
17
|
+
describe('as a button', () => {
|
|
18
|
+
it('should render a button by default', () => {
|
|
19
|
+
const { element } = setup({ as: 'button', children: 'Click me' });
|
|
20
|
+
expect(element.tagName).toBe('BUTTON');
|
|
21
|
+
expect(element).toHaveAttribute('type', 'button');
|
|
22
|
+
expect(screen.getByRole('button', { name: 'Click me' })).toBe(element);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should trigger onClick', async () => {
|
|
26
|
+
const onClick = vi.fn();
|
|
27
|
+
const { element } = setup({ as: 'button', children: 'Click me', onClick });
|
|
28
|
+
await userEvent.click(element);
|
|
29
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should be disabled with `disabled` prop', async () => {
|
|
33
|
+
const onClick = vi.fn();
|
|
34
|
+
const { element } = setup({ as: 'button', children: 'Click me', onClick, disabled: true });
|
|
35
|
+
expect(element).toBeDisabled();
|
|
36
|
+
await userEvent.click(element);
|
|
37
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should be disabled with `isDisabled` prop', async () => {
|
|
41
|
+
const onClick = vi.fn();
|
|
42
|
+
const { element } = setup({ as: 'button', children: 'Click me', onClick, isDisabled: true });
|
|
43
|
+
expect(element).toBeDisabled();
|
|
44
|
+
await userEvent.click(element);
|
|
45
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should be aria-disabled with `aria-disabled` prop', async () => {
|
|
49
|
+
const onClick = vi.fn();
|
|
50
|
+
const { element } = setup({ as: 'button', children: 'Click me', onClick, 'aria-disabled': true });
|
|
51
|
+
expect(element).not.toBeDisabled();
|
|
52
|
+
expect(element).toHaveAttribute('aria-disabled', 'true');
|
|
53
|
+
await userEvent.click(element);
|
|
54
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('as a link', () => {
|
|
59
|
+
const href = 'https://example.com';
|
|
60
|
+
|
|
61
|
+
it('should render a link with `href` prop', () => {
|
|
62
|
+
const { element } = setup({ as: 'a', children: 'Click me', href });
|
|
63
|
+
expect(element.tagName).toBe('A');
|
|
64
|
+
expect(element).toHaveAttribute('href', href);
|
|
65
|
+
expect(screen.getByRole('link', { name: 'Click me' })).toBe(element);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should trigger onClick', async () => {
|
|
69
|
+
const onClick = vi.fn((evt: any) => evt.preventDefault());
|
|
70
|
+
const { element } = setup({ as: 'a', children: 'Click me', href, onClick });
|
|
71
|
+
await userEvent.click(element);
|
|
72
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should be disabled with `disabled` prop', async () => {
|
|
76
|
+
const onClick = vi.fn();
|
|
77
|
+
const { element } = setup({ as: 'a', children: 'Click me', href, onClick, disabled: true });
|
|
78
|
+
expect(element).toHaveAttribute('aria-disabled', 'true');
|
|
79
|
+
expect(element).toHaveAttribute('tabindex', '-1');
|
|
80
|
+
await userEvent.click(element);
|
|
81
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should be disabled with `isDisabled` prop', async () => {
|
|
85
|
+
const onClick = vi.fn();
|
|
86
|
+
const { element } = setup({ as: 'a', children: 'Click me', href, onClick, isDisabled: true });
|
|
87
|
+
expect(element).toHaveAttribute('aria-disabled', 'true');
|
|
88
|
+
expect(element).toHaveAttribute('tabindex', '-1');
|
|
89
|
+
await userEvent.click(element);
|
|
90
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should be aria-disabled with `aria-disabled` prop', async () => {
|
|
94
|
+
const onClick = vi.fn();
|
|
95
|
+
const { element } = setup({ as: 'a', children: 'Click me', href, onClick, 'aria-disabled': true });
|
|
96
|
+
expect(element).toHaveAttribute('aria-disabled', 'true');
|
|
97
|
+
await userEvent.click(element);
|
|
98
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('as a custom component', () => {
|
|
103
|
+
it('should render a custom component with `linkAs` prop', () => {
|
|
104
|
+
const { element } = setup({ as: CustomLink, children: 'Click me' });
|
|
105
|
+
expect(element).toHaveAttribute('data-custom-link');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should trigger onClick', async () => {
|
|
109
|
+
const onClick = vi.fn();
|
|
110
|
+
const { element } = setup({ as: CustomLink, children: 'Click me', onClick });
|
|
111
|
+
expect(element).toHaveAttribute('data-custom-link');
|
|
112
|
+
await userEvent.click(element);
|
|
113
|
+
expect(onClick).toHaveBeenCalledTimes(1);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should be disabled with `disabled` prop', async () => {
|
|
117
|
+
const onClick = vi.fn();
|
|
118
|
+
const { element } = setup({ as: CustomLink, children: 'Click me', onClick, disabled: true });
|
|
119
|
+
expect(element).toHaveAttribute('data-custom-link');
|
|
120
|
+
expect(element).toHaveAttribute('aria-disabled', 'true');
|
|
121
|
+
expect(element).toHaveAttribute('tabindex', '-1');
|
|
122
|
+
await userEvent.click(element);
|
|
123
|
+
expect(onClick).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('prop forwarding', () => {
|
|
128
|
+
it('should forward className', () => {
|
|
129
|
+
const { element } = setup({ as: 'button', className: 'foo bar' });
|
|
130
|
+
expect(element).toHaveClass('foo bar');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('should forward ref and override type in button', () => {
|
|
134
|
+
const ref = React.createRef<HTMLButtonElement>();
|
|
135
|
+
const { element } = setup({ as: 'button', ref, type: 'submit' });
|
|
136
|
+
expect(element).toHaveAttribute('type', 'submit');
|
|
137
|
+
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should forward ref and override tabindex in link', () => {
|
|
141
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
|
142
|
+
const { element } = setup({ as: 'a', ref, href: '#', tabIndex: -1 });
|
|
143
|
+
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
|
144
|
+
expect(element).toHaveAttribute('tabindex', '-1');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('should forward ref to custom component', () => {
|
|
148
|
+
const ref = React.createRef<HTMLAnchorElement>();
|
|
149
|
+
setup({ as: CustomLink, ref });
|
|
150
|
+
expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AriaAttributes, ElementType } from 'react';
|
|
2
|
+
import { forwardRefPolymorphic } from '@lumx/react/utils/react/forwardRefPolymorphic';
|
|
3
|
+
import { ComponentRef, HasPolymorphicAs } from '@lumx/react/utils/type';
|
|
4
|
+
import { HasRequiredLinkHref } from '@lumx/react/utils/type/HasRequiredLinkHref';
|
|
5
|
+
|
|
6
|
+
type ClickableElement = 'a' | 'button' | ElementType;
|
|
7
|
+
|
|
8
|
+
type BaseClickableProps<E extends ClickableElement> = {
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
isDisabled?: boolean;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
'aria-disabled'?: AriaAttributes['aria-disabled'];
|
|
13
|
+
onClick?: React.MouseEventHandler<E>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type RawClickableProps<E extends ClickableElement> = HasPolymorphicAs<E> &
|
|
17
|
+
HasRequiredLinkHref<E> &
|
|
18
|
+
BaseClickableProps<E>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Render clickable element (link, button or custom element)
|
|
22
|
+
* (also does some basic disabled state handling)
|
|
23
|
+
*/
|
|
24
|
+
export const RawClickable = forwardRefPolymorphic(
|
|
25
|
+
<E extends ClickableElement>(props: RawClickableProps<E>, ref: ComponentRef<E>) => {
|
|
26
|
+
const {
|
|
27
|
+
children,
|
|
28
|
+
onClick,
|
|
29
|
+
disabled,
|
|
30
|
+
isDisabled = disabled,
|
|
31
|
+
'aria-disabled': ariaDisabled,
|
|
32
|
+
as,
|
|
33
|
+
...forwardedProps
|
|
34
|
+
} = props;
|
|
35
|
+
|
|
36
|
+
const isAnyDisabled = isDisabled || ariaDisabled === 'true' || ariaDisabled === true;
|
|
37
|
+
|
|
38
|
+
const Component = as as any;
|
|
39
|
+
let clickableProps;
|
|
40
|
+
if (Component === 'button') {
|
|
41
|
+
clickableProps = { type: forwardedProps.type || 'button', disabled: isDisabled };
|
|
42
|
+
} else {
|
|
43
|
+
clickableProps = { tabIndex: isDisabled ? '-1' : forwardedProps.tabIndex };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Component
|
|
48
|
+
ref={ref}
|
|
49
|
+
aria-disabled={isAnyDisabled || undefined}
|
|
50
|
+
{...forwardedProps}
|
|
51
|
+
{...clickableProps}
|
|
52
|
+
onClick={(event: any) => {
|
|
53
|
+
if (isAnyDisabled) {
|
|
54
|
+
event.stopPropagation();
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
onClick?.(event);
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
{children}
|
|
62
|
+
</Component>
|
|
63
|
+
);
|
|
64
|
+
},
|
|
65
|
+
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DOCUMENT } from '@lumx/react/constants';
|
|
2
|
-
import type { Comp } from '@lumx/react/utils/type';
|
|
3
1
|
import React from 'react';
|
|
4
2
|
|
|
3
|
+
import { DOCUMENT } from '@lumx/react/constants';
|
|
4
|
+
import type { Comp } from '@lumx/react/utils/type';
|
|
5
5
|
/**
|
|
6
6
|
* HOC component wrapping a component to skip render if predicate return falsy
|
|
7
7
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type HasRequiredLinkHref<E> = E extends 'a' ? { href: string } : Record<string, unknown>;
|
package/src/utils/type/index.ts
CHANGED
|
@@ -6,3 +6,4 @@ export type { HasPolymorphicAs } from './HasPolymorphicAs';
|
|
|
6
6
|
export { isComponent } from './isComponent';
|
|
7
7
|
export { isComponentType } from './isComponentType';
|
|
8
8
|
export type { MaybeElementOrRef } from './MaybeElementOrRef';
|
|
9
|
+
export type { HasRequiredLinkHref } from './HasRequiredLinkHref';
|
package/utils/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React__default, { RefObject } from 'react';
|
|
2
2
|
import { F as Falsy } from '../_internal/Falsy.js';
|
|
3
3
|
|
|
4
4
|
interface ClickAwayParameters {
|
|
@@ -40,23 +40,23 @@ type PortalInit = () => {
|
|
|
40
40
|
teardown?: () => void;
|
|
41
41
|
};
|
|
42
42
|
interface PortalProviderProps {
|
|
43
|
-
children?:
|
|
43
|
+
children?: React__default.ReactNode;
|
|
44
44
|
value: PortalInit;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
47
|
* Customize where <Portal> wrapped elements render (tooltip, popover, dialog, etc.)
|
|
48
48
|
*/
|
|
49
|
-
declare const PortalProvider:
|
|
49
|
+
declare const PortalProvider: React__default.FC<PortalProviderProps>;
|
|
50
50
|
|
|
51
51
|
interface PortalProps {
|
|
52
52
|
enabled?: boolean;
|
|
53
|
-
children:
|
|
53
|
+
children: React__default.ReactNode;
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
56
|
* Render children in a portal outside the current DOM position
|
|
57
57
|
* (defaults to `document.body` but can be customized with the PortalContextProvider)
|
|
58
58
|
*/
|
|
59
|
-
declare const Portal:
|
|
59
|
+
declare const Portal: React__default.FC<PortalProps>;
|
|
60
60
|
|
|
61
61
|
/** Disable state */
|
|
62
62
|
type DisabledStateContextValue = {
|
|
@@ -65,7 +65,7 @@ type DisabledStateContextValue = {
|
|
|
65
65
|
state: undefined | null;
|
|
66
66
|
};
|
|
67
67
|
type DisabledStateProviderProps = DisabledStateContextValue & {
|
|
68
|
-
children:
|
|
68
|
+
children: React__default.ReactNode;
|
|
69
69
|
};
|
|
70
70
|
/**
|
|
71
71
|
* Disabled state provider.
|
package/utils/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { C as ClickAwayProvider, D as DisabledStateProvider, P as Portal,
|
|
1
|
+
export { C as ClickAwayProvider, D as DisabledStateProvider, P as Portal, c as PortalProvider, u as useDisabledStateContext } from '../_internal/index.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import React, { ReactElement, ReactNode } from 'react';
|
|
2
|
-
import { renderLink } from './renderLink';
|
|
3
|
-
|
|
4
|
-
interface Props {
|
|
5
|
-
linkAs?: any;
|
|
6
|
-
href?: any;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Render <button> HTML component, fallbacks to `<a>` when a `href` is provided or a custom component with `linkAs`.
|
|
11
|
-
*/
|
|
12
|
-
export const renderButtonOrLink = <P extends Props>(props: P, ...children: ReactNode[]): ReactElement => {
|
|
13
|
-
const { linkAs, href, ...forwardedProps } = props;
|
|
14
|
-
if (linkAs || href) return renderLink(props, ...children);
|
|
15
|
-
return React.createElement('button', { type: 'button', ...forwardedProps }, ...children);
|
|
16
|
-
};
|