@lumx/react 3.19.1-alpha.7 → 3.19.1-alpha.8
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 +5 -6
- package/index.js +2400 -2440
- package/index.js.map +1 -1
- package/package.json +10 -13
- package/src/components/alert-dialog/AlertDialog.test.tsx +3 -2
- package/src/components/autocomplete/Autocomplete.test.tsx +3 -3
- package/src/components/button/Button.test.tsx +9 -9
- package/src/components/button/ButtonRoot.tsx +6 -36
- package/src/components/checkbox/Checkbox.test.tsx +3 -3
- package/src/components/chip/Chip.test.tsx +19 -17
- package/src/components/date-picker/DatePicker.test.tsx +3 -3
- package/src/components/date-picker/DatePickerControlled.test.tsx +6 -6
- package/src/components/date-picker/DatePickerField.test.tsx +3 -3
- package/src/components/dialog/Dialog.test.tsx +4 -4
- package/src/components/dropdown/Dropdown.test.tsx +3 -3
- package/src/components/expansion-panel/ExpansionPanel.test.tsx +6 -5
- package/src/components/icon/Icon.stories.tsx +30 -4
- package/src/components/icon/Icon.test.tsx +85 -2
- package/src/components/icon/Icon.tsx +118 -7
- package/src/components/image-lightbox/ImageLightbox.test.tsx +11 -7
- package/src/components/link/Link.test.tsx +13 -11
- package/src/components/link/Link.tsx +9 -20
- package/src/components/list/ListItem.test.tsx +6 -8
- package/src/components/message/Message.test.tsx +1 -1
- package/src/components/mosaic/Mosaic.test.tsx +3 -3
- package/src/components/navigation/NavigationItem.tsx +6 -10
- package/src/components/navigation/NavigationSection.tsx +4 -3
- package/src/components/notification/Notification.test.tsx +4 -3
- package/src/components/popover-dialog/PopoverDialog.test.tsx +1 -1
- package/src/components/radio-button/RadioButton.test.tsx +3 -3
- package/src/components/select/Select.test.tsx +8 -7
- package/src/components/select/SelectMultiple.test.tsx +5 -5
- package/src/components/side-navigation/SideNavigationItem.test.tsx +2 -2
- package/src/components/side-navigation/SideNavigationItem.tsx +22 -27
- package/src/components/slider/Slider.test.tsx +1 -1
- package/src/components/switch/Switch.test.tsx +5 -5
- package/src/components/table/TableCell.test.tsx +1 -1
- package/src/components/text-field/TextField.test.tsx +9 -8
- package/src/components/thumbnail/Thumbnail.test.tsx +29 -5
- package/src/components/thumbnail/Thumbnail.tsx +11 -11
- package/src/components/tooltip/Tooltip.test.tsx +14 -8
- package/src/components/uploader/Uploader.test.tsx +2 -2
- package/src/components/user-block/UserBlock.test.tsx +1 -1
- package/src/untypped-modules.d.ts +4 -0
- package/src/utils/Portal/PortalProvider.test.tsx +1 -1
- package/src/utils/date/getYearDisplayName.test.ts +1 -1
- package/src/utils/disabled/useDisableStateProps.test.tsx +2 -2
- package/src/utils/react/RawClickable.test.tsx +153 -0
- package/src/utils/react/RawClickable.tsx +65 -0
- package/src/utils/type/HasRequiredLinkHref.ts +1 -0
- package/src/utils/type/index.ts +1 -0
- package/utils/index.d.ts +1 -1
- package/utils/index.js +1 -1
- package/src/utils/react/renderButtonOrLink.tsx +0 -16
|
@@ -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 React, { 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
|
+
);
|
|
@@ -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
|
@@ -71,7 +71,7 @@ type DisabledStateProviderProps = DisabledStateContextValue & {
|
|
|
71
71
|
* Disabled state provider.
|
|
72
72
|
* All nested LumX Design System components inherit this disabled state.
|
|
73
73
|
*/
|
|
74
|
-
declare function DisabledStateProvider({ children, ...value }: DisabledStateProviderProps): JSX.Element;
|
|
74
|
+
declare function DisabledStateProvider({ children, ...value }: DisabledStateProviderProps): React.JSX.Element;
|
|
75
75
|
/**
|
|
76
76
|
* Get DisabledState context value
|
|
77
77
|
*/
|
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
|
-
};
|