@lumx/react 3.12.1-alpha.6 → 3.12.1-alpha.7
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 +2 -8
- package/index.js +74 -40
- package/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/link/Link.stories.tsx +23 -88
- package/src/components/link/Link.test.tsx +13 -25
- package/src/components/link/Link.tsx +88 -41
- package/src/components/text/Text.test.tsx +29 -9
- package/src/components/text/Text.tsx +11 -5
- package/src/stories/controls/icons.ts +0 -1
- package/src/stories/generated/Link/Demos.stories.tsx +8 -0
- package/src/utils/react/wrapChildrenIconWithSpaces.test.tsx +0 -37
- package/src/utils/react/wrapChildrenIconWithSpaces.tsx +0 -22
package/package.json
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"url": "https://github.com/lumapps/design-system/issues"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@lumx/core": "^3.12.1-alpha.
|
|
10
|
-
"@lumx/icons": "^3.12.1-alpha.
|
|
9
|
+
"@lumx/core": "^3.12.1-alpha.7",
|
|
10
|
+
"@lumx/icons": "^3.12.1-alpha.7",
|
|
11
11
|
"@popperjs/core": "^2.5.4",
|
|
12
12
|
"body-scroll-lock": "^3.1.5",
|
|
13
13
|
"classnames": "^2.3.2",
|
|
@@ -110,5 +110,5 @@
|
|
|
110
110
|
"build:storybook": "storybook build"
|
|
111
111
|
},
|
|
112
112
|
"sideEffects": false,
|
|
113
|
-
"version": "3.12.1-alpha.
|
|
113
|
+
"version": "3.12.1-alpha.7"
|
|
114
114
|
}
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ColorPalette,
|
|
3
|
-
ColorVariant,
|
|
4
|
-
FlexBox,
|
|
5
|
-
Icon,
|
|
6
|
-
Text,
|
|
7
|
-
Link,
|
|
8
|
-
Typography,
|
|
9
|
-
TypographyInterface,
|
|
10
|
-
TypographyTitleCustom,
|
|
11
|
-
} from '@lumx/react';
|
|
1
|
+
import { ColorPalette, ColorVariant, Link, Typography, TypographyInterface, TypographyTitleCustom } from '@lumx/react';
|
|
12
2
|
import React from 'react';
|
|
13
3
|
import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
|
|
14
4
|
import { colorArgType, colorVariantArgType } from '@lumx/react/stories/controls/color';
|
|
@@ -16,10 +6,6 @@ import { iconArgType } from '@lumx/react/stories/controls/icons';
|
|
|
16
6
|
import { withCombinations } from '@lumx/react/stories/decorators/withCombinations';
|
|
17
7
|
import { withUndefined } from '@lumx/react/stories/controls/withUndefined';
|
|
18
8
|
import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
|
|
19
|
-
import { withWrapper } from '@lumx/react/stories/decorators/withWrapper';
|
|
20
|
-
import { withThemedBackground } from '@lumx/react/stories/decorators/withThemedBackground';
|
|
21
|
-
import { mdiEarth, mdiFoodApple, mdiPencil } from '@lumx/icons/override/generated';
|
|
22
|
-
import { withNestedProps } from '@lumx/react/stories/decorators/withNestedProps';
|
|
23
9
|
|
|
24
10
|
const linkTypographies = { ...TypographyInterface, ...TypographyTitleCustom };
|
|
25
11
|
|
|
@@ -43,6 +29,28 @@ export const Default = {
|
|
|
43
29
|
args: { href: 'https://example.com', target: '_blank' },
|
|
44
30
|
};
|
|
45
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Disabled
|
|
34
|
+
*/
|
|
35
|
+
export const Disabled = {
|
|
36
|
+
args: { ...Default.args, children: 'Link (disabled)', isDisabled: true },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Using onClick transforms the link into a <button> in DOM
|
|
41
|
+
*/
|
|
42
|
+
export const ButtonLink = {
|
|
43
|
+
argTypes: { onClick: { action: true } },
|
|
44
|
+
args: { children: 'Button link' },
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Button link disabled
|
|
49
|
+
*/
|
|
50
|
+
export const ButtonLinkDisabled = {
|
|
51
|
+
args: { ...ButtonLink.args, children: 'Button link (disabled)', isDisabled: true },
|
|
52
|
+
};
|
|
53
|
+
|
|
46
54
|
/**
|
|
47
55
|
* Use custom component instead of <a> or <button>
|
|
48
56
|
*/
|
|
@@ -68,51 +76,10 @@ export const WithCustomizableTypography = {
|
|
|
68
76
|
</>
|
|
69
77
|
),
|
|
70
78
|
};
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Show state combinations
|
|
74
|
-
*/
|
|
75
|
-
export const AllStates = {
|
|
76
|
-
argTypes: {
|
|
77
|
-
isDisabled: { control: false },
|
|
78
|
-
},
|
|
79
|
-
decorators: [
|
|
80
|
-
withThemedBackground(),
|
|
81
|
-
withCombinations({
|
|
82
|
-
combinations: {
|
|
83
|
-
sections: {
|
|
84
|
-
Default: {},
|
|
85
|
-
'with icon': {
|
|
86
|
-
children: ['Link', <Icon key="icon" icon={mdiEarth} />, 'with icon'],
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
cols: {
|
|
90
|
-
Default: {},
|
|
91
|
-
Disabled: { isDisabled: true },
|
|
92
|
-
Focused: { 'data-focus-visible-added': true },
|
|
93
|
-
Hovered: { 'data-lumx-hover': true },
|
|
94
|
-
},
|
|
95
|
-
rows: {
|
|
96
|
-
Default: {},
|
|
97
|
-
'color=red': { color: 'red' },
|
|
98
|
-
'theme=dark': { theme: 'dark' },
|
|
99
|
-
'theme=dark & color=red': { theme: 'dark', color: 'red' },
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
}),
|
|
103
|
-
withWrapper({ orientation: 'horizontal', vAlign: 'space-evenly', wrap: true, gap: 'huge' }, FlexBox),
|
|
104
|
-
],
|
|
105
|
-
};
|
|
106
|
-
|
|
107
79
|
/**
|
|
108
80
|
* Show all typographies
|
|
109
81
|
*/
|
|
110
82
|
export const AllTypography = {
|
|
111
|
-
args: {
|
|
112
|
-
children: ['Link', <Icon key="icon" icon={mdiEarth} />, 'with icon'],
|
|
113
|
-
rightIcon: mdiPencil,
|
|
114
|
-
leftIcon: mdiFoodApple,
|
|
115
|
-
},
|
|
116
83
|
argTypes: {
|
|
117
84
|
typography: { control: false },
|
|
118
85
|
},
|
|
@@ -142,35 +109,3 @@ export const AllColors = {
|
|
|
142
109
|
}),
|
|
143
110
|
],
|
|
144
111
|
};
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Check how link inherit parent typography & color.
|
|
148
|
-
* Typography should be inherited.
|
|
149
|
-
* Color should not.
|
|
150
|
-
*/
|
|
151
|
-
export const ParentTypographyAndColor = {
|
|
152
|
-
args: {
|
|
153
|
-
children: ['Link', <Icon key="icon" icon={mdiEarth} />, 'with icon'],
|
|
154
|
-
'parent.typography': undefined,
|
|
155
|
-
'parent.color': undefined,
|
|
156
|
-
},
|
|
157
|
-
render: ({ parent: { typography, color }, ...args }: any) => (
|
|
158
|
-
<Text as="p" color={color} typography={typography}>
|
|
159
|
-
<Link {...args} />
|
|
160
|
-
</Text>
|
|
161
|
-
),
|
|
162
|
-
decorators: [
|
|
163
|
-
withNestedProps(),
|
|
164
|
-
withCombinations({
|
|
165
|
-
combinations: {
|
|
166
|
-
sections: { Button: {}, Link: { href: '#' } },
|
|
167
|
-
rows: {
|
|
168
|
-
Default: {},
|
|
169
|
-
'Parent typography=title': { 'parent.typography': 'title' },
|
|
170
|
-
'Parent color=red': { 'parent.color': 'red' },
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
}),
|
|
174
|
-
withWrapper({ orientation: 'horizontal', vAlign: 'space-evenly', wrap: true, gap: 'huge' }, FlexBox),
|
|
175
|
-
],
|
|
176
|
-
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import { ColorPalette, ColorVariant,
|
|
3
|
+
import { ColorPalette, ColorVariant, Typography } from '@lumx/react';
|
|
4
4
|
import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
|
|
5
|
-
import { getByClassName,
|
|
5
|
+
import { getByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
|
|
6
6
|
import { render, screen } from '@testing-library/react';
|
|
7
7
|
import { mdiCheck, mdiPlus } from '@lumx/icons';
|
|
8
8
|
import { Link, LinkProps } from './Link';
|
|
@@ -15,9 +15,11 @@ const CLASSNAME = Link.className as string;
|
|
|
15
15
|
const setup = (props: LinkProps = {}) => {
|
|
16
16
|
render(<Link {...props} />);
|
|
17
17
|
const link = getByClassName(document.body, CLASSNAME);
|
|
18
|
+
const content = queryByClassName(link, `${CLASSNAME}__content`);
|
|
18
19
|
const rightIcon = queryByClassName(link, `${CLASSNAME}__right-icon`);
|
|
19
20
|
const leftIcon = queryByClassName(link, `${CLASSNAME}__left-icon`);
|
|
20
|
-
|
|
21
|
+
|
|
22
|
+
return { props, link, content, rightIcon, leftIcon };
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
describe(`<${Link.displayName}>`, () => {
|
|
@@ -40,12 +42,14 @@ describe(`<${Link.displayName}>`, () => {
|
|
|
40
42
|
color: ColorPalette.primary,
|
|
41
43
|
colorVariant: ColorVariant.D1,
|
|
42
44
|
});
|
|
43
|
-
expect(link.className).
|
|
45
|
+
expect(link.className).toMatchInlineSnapshot(
|
|
46
|
+
'"lumx-link lumx-link--color-primary lumx-link--color-variant-D1"',
|
|
47
|
+
);
|
|
44
48
|
});
|
|
45
49
|
|
|
46
50
|
it('should render typography', () => {
|
|
47
|
-
const {
|
|
48
|
-
expect(
|
|
51
|
+
const { content } = setup({ href: 'https://google.com', typography: Typography.title });
|
|
52
|
+
expect(content?.className).toMatchInlineSnapshot('undefined');
|
|
49
53
|
});
|
|
50
54
|
|
|
51
55
|
it('should render a button', () => {
|
|
@@ -55,26 +59,10 @@ describe(`<${Link.displayName}>`, () => {
|
|
|
55
59
|
expect(link).toBe(screen.queryByRole('button', { name }));
|
|
56
60
|
});
|
|
57
61
|
|
|
58
|
-
it('should render disabled link as button', () => {
|
|
59
|
-
const name = 'Link';
|
|
60
|
-
const { link } = setup({ href: 'https://google.com', isDisabled: true, children: name });
|
|
61
|
-
expect(link).toBe(screen.queryByRole('button', { name }));
|
|
62
|
-
});
|
|
63
|
-
|
|
64
62
|
it('should render with icons', () => {
|
|
65
|
-
const {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
rightIcon: mdiPlus,
|
|
69
|
-
});
|
|
70
|
-
const icons = queryAllByClassName(link, Icon.className as string);
|
|
71
|
-
expect(icons).toHaveLength(3);
|
|
72
|
-
|
|
73
|
-
// Icons are all wrapped with spaces
|
|
74
|
-
for (const icon of icons) {
|
|
75
|
-
expect((icon.previousSibling as any).textContent).toEqual(' ');
|
|
76
|
-
expect((icon.nextSibling as any).textContent).toEqual(' ');
|
|
77
|
-
}
|
|
63
|
+
const { rightIcon, leftIcon } = setup({ rightIcon: mdiPlus, leftIcon: mdiCheck });
|
|
64
|
+
expect(rightIcon).toBeInTheDocument();
|
|
65
|
+
expect(leftIcon).toBeInTheDocument();
|
|
78
66
|
});
|
|
79
67
|
});
|
|
80
68
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { RefObject, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import isEmpty from 'lodash/isEmpty';
|
|
2
4
|
|
|
3
5
|
import classNames from 'classnames';
|
|
4
6
|
|
|
5
|
-
import { ColorPalette, ColorVariant, Icon, Typography } from '@lumx/react';
|
|
7
|
+
import { ColorPalette, ColorVariant, Icon, Size, Typography } from '@lumx/react';
|
|
6
8
|
import { GenericProps } from '@lumx/react/utils/type';
|
|
7
|
-
import { getRootClassName,
|
|
9
|
+
import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
|
|
10
|
+
import { renderLink } from '@lumx/react/utils/react/renderLink';
|
|
8
11
|
import { forwardRef } from '@lumx/react/utils/react/forwardRef';
|
|
9
|
-
import { wrapChildrenIconWithSpaces } from '@lumx/react/utils/react/wrapChildrenIconWithSpaces';
|
|
10
12
|
|
|
11
13
|
type HTMLAnchorProps = React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
|
|
12
14
|
|
|
@@ -22,17 +24,11 @@ export interface LinkProps extends GenericProps {
|
|
|
22
24
|
href?: HTMLAnchorProps['href'];
|
|
23
25
|
/** Whether the component is disabled or not. */
|
|
24
26
|
isDisabled?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* Left icon (SVG path).
|
|
27
|
-
* @deprecated Instead, simply nest `<Icon />` in the children
|
|
28
|
-
*/
|
|
27
|
+
/** Left icon (SVG path). */
|
|
29
28
|
leftIcon?: string;
|
|
30
29
|
/** Custom react component for the link (can be used to inject react router Link). */
|
|
31
30
|
linkAs?: 'a' | any;
|
|
32
|
-
/**
|
|
33
|
-
* Right icon (SVG path).
|
|
34
|
-
* @deprecated Instead, simply nest `<Icon />` in the children
|
|
35
|
-
*/
|
|
31
|
+
/** Right icon (SVG path). */
|
|
36
32
|
rightIcon?: string;
|
|
37
33
|
/** Link target. */
|
|
38
34
|
target?: HTMLAnchorProps['target'];
|
|
@@ -52,6 +48,36 @@ const COMPONENT_NAME = 'Link';
|
|
|
52
48
|
*/
|
|
53
49
|
const CLASSNAME = getRootClassName(COMPONENT_NAME);
|
|
54
50
|
|
|
51
|
+
const getIconSize = (typography?: Typography) => {
|
|
52
|
+
switch (typography) {
|
|
53
|
+
case Typography.display1:
|
|
54
|
+
return Size.m;
|
|
55
|
+
|
|
56
|
+
case Typography.headline:
|
|
57
|
+
case Typography.title:
|
|
58
|
+
case Typography.custom.title1:
|
|
59
|
+
case Typography.custom.title2:
|
|
60
|
+
case Typography.custom.title3:
|
|
61
|
+
case Typography.custom.title4:
|
|
62
|
+
case Typography.custom.title5:
|
|
63
|
+
case Typography.custom.title6:
|
|
64
|
+
case Typography.body2:
|
|
65
|
+
case Typography.subtitle2:
|
|
66
|
+
return Size.s;
|
|
67
|
+
|
|
68
|
+
case Typography.body1:
|
|
69
|
+
case Typography.subtitle1:
|
|
70
|
+
return Size.xs;
|
|
71
|
+
|
|
72
|
+
case Typography.caption:
|
|
73
|
+
case Typography.overline:
|
|
74
|
+
return Size.xxs;
|
|
75
|
+
|
|
76
|
+
default:
|
|
77
|
+
return Size.s;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
55
81
|
/**
|
|
56
82
|
* Link component.
|
|
57
83
|
*
|
|
@@ -75,37 +101,58 @@ export const Link = forwardRef<LinkProps, HTMLAnchorElement | HTMLButtonElement>
|
|
|
75
101
|
typography,
|
|
76
102
|
...forwardedProps
|
|
77
103
|
} = props;
|
|
104
|
+
const renderedChildren = useMemo(
|
|
105
|
+
() => (
|
|
106
|
+
<>
|
|
107
|
+
{leftIcon && !isEmpty(leftIcon) && (
|
|
108
|
+
<Icon icon={leftIcon} className={`${CLASSNAME}__left-icon`} size={getIconSize(typography)} />
|
|
109
|
+
)}
|
|
78
110
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
111
|
+
{children && (
|
|
112
|
+
<span
|
|
113
|
+
className={classNames(`${CLASSNAME}__content`, {
|
|
114
|
+
[`lumx-typography-${typography}`]: typography,
|
|
115
|
+
})}
|
|
116
|
+
>
|
|
117
|
+
{children}
|
|
118
|
+
</span>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
{rightIcon && !isEmpty(rightIcon) && (
|
|
122
|
+
<Icon icon={rightIcon} className={`${CLASSNAME}__right-icon`} size={getIconSize(typography)} />
|
|
123
|
+
)}
|
|
124
|
+
</>
|
|
125
|
+
),
|
|
126
|
+
[leftIcon, typography, children, rightIcon],
|
|
127
|
+
);
|
|
89
128
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
/**
|
|
130
|
+
* If there is no linkAs prop and no href, we returned a <button> instead of a <a>.
|
|
131
|
+
* If the component is disabled, we also returned a <button> since disabled is not compatible with <a>.
|
|
132
|
+
*/
|
|
133
|
+
if ((!linkAs && isEmpty(href)) || isDisabled) {
|
|
134
|
+
return (
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
{...forwardedProps}
|
|
138
|
+
ref={ref as RefObject<HTMLButtonElement>}
|
|
139
|
+
disabled={isDisabled}
|
|
140
|
+
className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, color, colorVariant }))}
|
|
141
|
+
>
|
|
142
|
+
{renderedChildren}
|
|
143
|
+
</button>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return renderLink(
|
|
147
|
+
{
|
|
148
|
+
linkAs,
|
|
149
|
+
...forwardedProps,
|
|
150
|
+
href,
|
|
151
|
+
target,
|
|
152
|
+
className: classNames(className, handleBasicClasses({ prefix: CLASSNAME, color, colorVariant })),
|
|
153
|
+
ref: ref as RefObject<HTMLAnchorElement>,
|
|
154
|
+
},
|
|
155
|
+
renderedChildren,
|
|
109
156
|
);
|
|
110
157
|
});
|
|
111
158
|
Link.displayName = COMPONENT_NAME;
|
|
@@ -4,7 +4,7 @@ import { commonTestsSuiteRTL } from '@lumx/react/testing/utils';
|
|
|
4
4
|
import { mdiEarth } from '@lumx/icons';
|
|
5
5
|
import { Icon } from '@lumx/react';
|
|
6
6
|
import { render } from '@testing-library/react';
|
|
7
|
-
import { getByClassName
|
|
7
|
+
import { getByClassName } from '@lumx/react/testing/utils/queries';
|
|
8
8
|
import { Text, TextProps } from '.';
|
|
9
9
|
|
|
10
10
|
const setup = (props: Partial<TextProps> = {}) => {
|
|
@@ -69,14 +69,34 @@ describe(`<${Text.displayName}>`, () => {
|
|
|
69
69
|
|
|
70
70
|
it('should wrap icons with spaces', () => {
|
|
71
71
|
const { element } = setup({ children: ['Some text', <Icon key="icon" icon={mdiEarth} />, 'with icon'] });
|
|
72
|
-
|
|
73
|
-
expect(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
// Spaces have been inserted around the icon.
|
|
73
|
+
expect(element).toMatchInlineSnapshot(`
|
|
74
|
+
<span
|
|
75
|
+
class="lumx-text"
|
|
76
|
+
>
|
|
77
|
+
Some text
|
|
78
|
+
|
|
79
|
+
<i
|
|
80
|
+
class="lumx-icon lumx-icon--no-shape lumx-icon--path"
|
|
81
|
+
>
|
|
82
|
+
<svg
|
|
83
|
+
aria-hidden="true"
|
|
84
|
+
height="1em"
|
|
85
|
+
preserveAspectRatio="xMidYMid meet"
|
|
86
|
+
style="vertical-align: -0.125em;"
|
|
87
|
+
viewBox="0 0 24 24"
|
|
88
|
+
width="1em"
|
|
89
|
+
>
|
|
90
|
+
<path
|
|
91
|
+
d="M17.9 17.39A2 2 0 0 0 16 16h-1v-3a1 1 0 0 0-1-1H8v-2h2a1 1 0 0 0 1-1V7h2a2 2 0 0 0 2-2v-.41a7.98 7.98 0 0 1 2.9 12.8M11 19.93a8 8 0 0 1-6.79-9.72L9 15v1a2 2 0 0 0 2 2m1-16A10 10 0 0 0 2 12a10 10 0 0 0 10 10 10 10 0 0 0 10-10A10 10 0 0 0 12 2"
|
|
92
|
+
fill="currentColor"
|
|
93
|
+
/>
|
|
94
|
+
</svg>
|
|
95
|
+
</i>
|
|
96
|
+
|
|
97
|
+
with icon
|
|
98
|
+
</span>
|
|
99
|
+
`);
|
|
80
100
|
});
|
|
81
101
|
|
|
82
102
|
it('should render dangerouslySetInnerHTML', () => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { Children, Fragment } from 'react';
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
|
|
5
|
-
import { ColorPalette, ColorVariant, Typography, WhiteSpace } from '@lumx/react';
|
|
6
|
-
import { GenericProps, TextElement } from '@lumx/react/utils/type';
|
|
5
|
+
import { Icon, ColorPalette, ColorVariant, Typography, WhiteSpace } from '@lumx/react';
|
|
6
|
+
import { GenericProps, TextElement, isComponent } from '@lumx/react/utils/type';
|
|
7
7
|
import {
|
|
8
8
|
getFontColorClassName,
|
|
9
9
|
getRootClassName,
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
import { useOverflowTooltipLabel } from '@lumx/react/hooks/useOverflowTooltipLabel';
|
|
14
14
|
import { useMergeRefs } from '@lumx/react/utils/react/mergeRefs';
|
|
15
15
|
import { forwardRef } from '@lumx/react/utils/react/forwardRef';
|
|
16
|
-
import { wrapChildrenIconWithSpaces } from '@lumx/react/utils/react/wrapChildrenIconWithSpaces';
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Defines the props of the component.
|
|
@@ -133,7 +132,14 @@ export const Text = forwardRef<TextProps>((props, ref) => {
|
|
|
133
132
|
style={{ ...truncateLinesStyle, ...whiteSpaceStyle, ...style }}
|
|
134
133
|
{...forwardedProps}
|
|
135
134
|
>
|
|
136
|
-
{
|
|
135
|
+
{children &&
|
|
136
|
+
Children.toArray(children).map((child, index) => {
|
|
137
|
+
// Force wrap spaces around icons to make sure they are never stuck against text.
|
|
138
|
+
if (isComponent(Icon)(child)) {
|
|
139
|
+
return <Fragment key={child.key || index}> {child} </Fragment>;
|
|
140
|
+
}
|
|
141
|
+
return child;
|
|
142
|
+
})}
|
|
137
143
|
</Component>
|
|
138
144
|
);
|
|
139
145
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File generated when storybook is started. Do not edit directly!
|
|
3
|
+
*/
|
|
4
|
+
export default { title: 'LumX components/link/Link Demos' };
|
|
5
|
+
|
|
6
|
+
export { App as Default } from './default';
|
|
7
|
+
export { App as Implicit } from './implicit';
|
|
8
|
+
export { App as Sizes } from './sizes';
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import React, { Fragment } from 'react';
|
|
2
|
-
|
|
3
|
-
import { Icon } from '@lumx/react';
|
|
4
|
-
import { mdiEarth, mdiFoodApple, mdiPencil } from '@lumx/icons';
|
|
5
|
-
import { wrapChildrenIconWithSpaces } from './wrapChildrenIconWithSpaces';
|
|
6
|
-
|
|
7
|
-
describe(wrapChildrenIconWithSpaces, () => {
|
|
8
|
-
it('should ignore null or undefined children', () => {
|
|
9
|
-
expect(wrapChildrenIconWithSpaces(undefined)).toBeUndefined();
|
|
10
|
-
expect(wrapChildrenIconWithSpaces(null)).toBeUndefined();
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should wrap icons with spaces', () => {
|
|
14
|
-
expect(
|
|
15
|
-
wrapChildrenIconWithSpaces(
|
|
16
|
-
<>
|
|
17
|
-
<Icon icon={mdiEarth} />a string
|
|
18
|
-
<>
|
|
19
|
-
some more string with
|
|
20
|
-
<Icon icon={mdiFoodApple} />
|
|
21
|
-
</>
|
|
22
|
-
{['array with', <Icon key="custom-key" icon={mdiPencil} />]}
|
|
23
|
-
</>,
|
|
24
|
-
),
|
|
25
|
-
).toEqual([
|
|
26
|
-
// prettier-ignore
|
|
27
|
-
<Fragment key=".0">
|
|
28
|
-
{' '}
|
|
29
|
-
<Icon key=".0" icon={mdiEarth} />{' '}a string
|
|
30
|
-
<Fragment key=".2">
|
|
31
|
-
some more string with{' '}<Icon key=".1" icon={mdiFoodApple} />{' '}
|
|
32
|
-
</Fragment>
|
|
33
|
-
array with{' '}<Icon key=".3:$custom-key" icon={mdiPencil} />{' '}
|
|
34
|
-
</Fragment>,
|
|
35
|
-
]);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import React, { Children } from 'react';
|
|
2
|
-
import { isComponentType } from '@lumx/react/utils/type';
|
|
3
|
-
import { Icon } from '@lumx/react';
|
|
4
|
-
|
|
5
|
-
/** Force wrap spaces around icons to make sure they are never stuck against text. */
|
|
6
|
-
export function wrapChildrenIconWithSpaces(children: React.ReactNode): React.ReactNode {
|
|
7
|
-
if (children === null || children === undefined) return undefined;
|
|
8
|
-
return Children.toArray(children).flatMap((child) => {
|
|
9
|
-
if (isComponentType(Icon)(child)) {
|
|
10
|
-
return [' ', child, ' '];
|
|
11
|
-
}
|
|
12
|
-
if (
|
|
13
|
-
React.isValidElement(child) &&
|
|
14
|
-
child.props &&
|
|
15
|
-
typeof child.props === 'object' &&
|
|
16
|
-
'children' in child.props
|
|
17
|
-
) {
|
|
18
|
-
return React.cloneElement(child, undefined, wrapChildrenIconWithSpaces(child.props.children));
|
|
19
|
-
}
|
|
20
|
-
return child;
|
|
21
|
-
});
|
|
22
|
-
}
|