@lumx/react 3.11.4-alpha.0 → 3.12.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/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.11.4-alpha.0",
10
- "@lumx/icons": "^3.11.4-alpha.0",
9
+ "@lumx/core": "^3.12.1-alpha.0",
10
+ "@lumx/icons": "^3.12.1-alpha.0",
11
11
  "@popperjs/core": "^2.5.4",
12
12
  "body-scroll-lock": "^3.1.5",
13
13
  "classnames": "^2.3.2",
@@ -15,17 +15,17 @@
15
15
  "react-popper": "^2.2.4"
16
16
  },
17
17
  "devDependencies": {
18
- "@babel/core": "^7.18.13",
18
+ "@babel/core": "^7.26.10",
19
19
  "@babel/plugin-proposal-class-properties": "^7.18.6",
20
- "@babel/plugin-proposal-export-default-from": "^7.18.10",
20
+ "@babel/plugin-proposal-export-default-from": "^7.25.9",
21
21
  "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
22
- "@babel/plugin-proposal-object-rest-spread": "^7.18.9",
23
- "@babel/plugin-proposal-optional-chaining": "^7.18.9",
22
+ "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
23
+ "@babel/plugin-proposal-optional-chaining": "^7.21.0",
24
24
  "@babel/plugin-proposal-private-methods": "^7.18.6",
25
- "@babel/plugin-proposal-private-property-in-object": "^7.18.6",
26
- "@babel/preset-env": "^7.18.10",
27
- "@babel/preset-react": "^7.18.6",
28
- "@babel/preset-typescript": "^7.18.6",
25
+ "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
26
+ "@babel/preset-env": "^7.26.9",
27
+ "@babel/preset-react": "^7.26.3",
28
+ "@babel/preset-typescript": "^7.26.0",
29
29
  "@rollup/plugin-babel": "^6.0.4",
30
30
  "@rollup/plugin-commonjs": "^19.0.2",
31
31
  "@rollup/plugin-node-resolve": "16.0.0",
@@ -110,5 +110,5 @@
110
110
  "build:storybook": "storybook build"
111
111
  },
112
112
  "sideEffects": false,
113
- "version": "3.11.4-alpha.0"
113
+ "version": "3.12.1-alpha.0"
114
114
  }
@@ -72,10 +72,11 @@ describe(`<${Button.displayName}>`, () => {
72
72
  forwardAttributes: 'button',
73
73
  forwardRef: 'button',
74
74
  applyTheme: {
75
- affects: [{ element: 'button' }],
75
+ affects: [{ element: 'button' }, { not: { element: 'icons' } }],
76
76
  viaProp: true,
77
77
  viaContext: true,
78
78
  defaultTheme: 'light',
79
+ defaultProps: { rightIcon: mdiPlus, leftIcon: mdiCheck },
79
80
  },
80
81
  });
81
82
  });
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import classNames from 'classnames';
4
4
  import isEmpty from 'lodash/isEmpty';
5
5
 
6
- import { Emphasis, Icon, Size, Theme, Text } from '@lumx/react';
6
+ import { Emphasis, Icon, Size, Theme, Text, ThemeProvider } from '@lumx/react';
7
7
  import { isComponent } from '@lumx/react/utils/type';
8
8
  import { getBasicClass, getRootClassName } from '@lumx/react/utils/className';
9
9
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
@@ -73,9 +73,19 @@ export const Button = forwardRef<ButtonProps, HTMLButtonElement | HTMLAnchorElem
73
73
  className={buttonClassName}
74
74
  variant="button"
75
75
  >
76
- {leftIcon && !isEmpty(leftIcon) && <Icon icon={leftIcon} />}
76
+ {leftIcon && !isEmpty(leftIcon) && (
77
+ // Theme is handled in the button scss
78
+ <ThemeProvider value={undefined}>
79
+ <Icon icon={leftIcon} />
80
+ </ThemeProvider>
81
+ )}
77
82
  {children && (isComponent(Text)(children) ? children : <span>{children}</span>)}
78
- {rightIcon && !isEmpty(rightIcon) && <Icon icon={rightIcon} />}
83
+ {rightIcon && !isEmpty(rightIcon) && (
84
+ // Theme is handled in the button scss
85
+ <ThemeProvider value={undefined}>
86
+ <Icon icon={rightIcon} />
87
+ </ThemeProvider>
88
+ )}
79
89
  </ButtonRoot>
80
90
  );
81
91
  });
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
+ import { Typography } from '@lumx/react';
4
+
3
5
  import { InputLabel } from './InputLabel';
4
6
 
5
7
  export default {
@@ -36,3 +38,10 @@ export const Default = {};
36
38
  export const IsRequired = {
37
39
  args: { isRequired: true },
38
40
  };
41
+
42
+ /**
43
+ * Default input label
44
+ */
45
+ export const WithCustomTypography = {
46
+ args: { typography: Typography.subtitle1 },
47
+ };
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
 
3
- import { Theme } from '@lumx/react';
3
+ import { Theme, Typography } from '@lumx/react';
4
4
  import { getByClassName } from '@lumx/react/testing/utils/queries';
5
+ import { getTypographyClassName } from '@lumx/react/utils/className';
5
6
  import { render } from '@testing-library/react';
6
7
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
7
8
  import { InputLabel, InputLabelProps } from './InputLabel';
@@ -38,6 +39,12 @@ describe(`<${InputLabel.displayName}>`, () => {
38
39
  expect(label).toHaveClass(`${CLASSNAME}--theme-dark`);
39
40
  expect(label).toHaveClass(`${CLASSNAME}--is-required`);
40
41
  });
42
+
43
+ it('should render typography', () => {
44
+ const { label } = setup({ children: 'The label', typography: Typography.body1 });
45
+ expect(label).toHaveClass(CLASSNAME);
46
+ expect(label).toHaveClass(getTypographyClassName(Typography.body1));
47
+ });
41
48
  });
42
49
 
43
50
  commonTestsSuiteRTL(setup, {
@@ -2,9 +2,9 @@ import React, { ReactNode } from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
5
- import { Theme } from '@lumx/react';
5
+ import { Theme, Typography } from '@lumx/react';
6
6
  import { GenericProps, HasTheme } from '@lumx/react/utils/type';
7
- import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
7
+ import { getRootClassName, handleBasicClasses, getTypographyClassName } from '@lumx/react/utils/className';
8
8
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
9
9
  import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
10
10
 
@@ -12,6 +12,8 @@ import { useTheme } from '@lumx/react/utils/theme/ThemeContext';
12
12
  * Defines the props of the component.
13
13
  */
14
14
  export interface InputLabelProps extends GenericProps, HasTheme {
15
+ /** Typography variant. */
16
+ typography?: Typography;
15
17
  /** Label content. */
16
18
  children: string | ReactNode;
17
19
  /** Native htmlFor property. */
@@ -44,14 +46,19 @@ const DEFAULT_PROPS: Partial<InputLabelProps> = {};
44
46
  */
45
47
  export const InputLabel = forwardRef<InputLabelProps, HTMLLabelElement>((props, ref) => {
46
48
  const defaultTheme = useTheme() || Theme.light;
47
- const { children, className, htmlFor, isRequired, theme = defaultTheme, ...forwardedProps } = props;
49
+ const { children, className, htmlFor, isRequired, theme = defaultTheme, typography, ...forwardedProps } = props;
50
+ const typographyClass = typography && getTypographyClassName(typography);
48
51
 
49
52
  return (
50
53
  <label
51
54
  ref={ref}
52
55
  {...forwardedProps}
53
56
  htmlFor={htmlFor}
54
- className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, isRequired, theme }))}
57
+ className={classNames(
58
+ className,
59
+ handleBasicClasses({ prefix: CLASSNAME, isRequired, theme, hasCustomTypography: Boolean(typography) }),
60
+ typographyClass,
61
+ )}
55
62
  >
56
63
  {children}
57
64
  </label>
@@ -1,4 +1,13 @@
1
- import { ColorPalette, ColorVariant, Link, Typography, TypographyInterface, TypographyTitleCustom } from '@lumx/react';
1
+ import {
2
+ ColorPalette,
3
+ ColorVariant,
4
+ FlexBox,
5
+ Icon,
6
+ Link,
7
+ Typography,
8
+ TypographyInterface,
9
+ TypographyTitleCustom,
10
+ } from '@lumx/react';
2
11
  import React from 'react';
3
12
  import { getSelectArgType } from '@lumx/react/stories/controls/selectArgType';
4
13
  import { colorArgType, colorVariantArgType } from '@lumx/react/stories/controls/color';
@@ -6,6 +15,9 @@ import { iconArgType } from '@lumx/react/stories/controls/icons';
6
15
  import { withCombinations } from '@lumx/react/stories/decorators/withCombinations';
7
16
  import { withUndefined } from '@lumx/react/stories/controls/withUndefined';
8
17
  import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
18
+ import { withWrapper } from '@lumx/react/stories/decorators/withWrapper';
19
+ import { withThemedBackground } from '@lumx/react/stories/decorators/withThemedBackground';
20
+ import { mdiEarth, mdiFoodApple, mdiPencil } from '@lumx/icons/override/generated';
9
21
 
10
22
  const linkTypographies = { ...TypographyInterface, ...TypographyTitleCustom };
11
23
 
@@ -29,28 +41,6 @@ export const Default = {
29
41
  args: { href: 'https://example.com', target: '_blank' },
30
42
  };
31
43
 
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
-
54
44
  /**
55
45
  * Use custom component instead of <a> or <button>
56
46
  */
@@ -76,10 +66,51 @@ export const WithCustomizableTypography = {
76
66
  </>
77
67
  ),
78
68
  };
69
+
70
+ /**
71
+ * Show state combinations
72
+ */
73
+ export const AllStates = {
74
+ argTypes: {
75
+ isDisabled: { control: false },
76
+ },
77
+ decorators: [
78
+ withThemedBackground(),
79
+ withCombinations({
80
+ combinations: {
81
+ sections: {
82
+ Default: {},
83
+ 'with icon': {
84
+ children: ['Link', <Icon key="icon" icon={mdiEarth} />, 'with icon'],
85
+ },
86
+ },
87
+ cols: {
88
+ Default: {},
89
+ Disabled: { isDisabled: true },
90
+ Focused: { 'data-focus-visible-added': true },
91
+ Hovered: { 'data-lumx-hover': true },
92
+ },
93
+ rows: {
94
+ Default: {},
95
+ 'color=red': { color: 'red' },
96
+ 'theme=dark': { theme: 'dark' },
97
+ 'theme=dark & color=red': { theme: 'dark', color: 'red' },
98
+ },
99
+ },
100
+ }),
101
+ withWrapper({ orientation: 'horizontal', vAlign: 'space-evenly', wrap: true, gap: 'huge' }, FlexBox),
102
+ ],
103
+ };
104
+
79
105
  /**
80
106
  * Show all typographies
81
107
  */
82
108
  export const AllTypography = {
109
+ args: {
110
+ children: ['Link', <Icon key="icon" icon={mdiEarth} />, 'with icon'],
111
+ rightIcon: mdiPencil,
112
+ leftIcon: mdiFoodApple,
113
+ },
83
114
  argTypes: {
84
115
  typography: { control: false },
85
116
  },
@@ -15,11 +15,9 @@ 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`);
19
18
  const rightIcon = queryByClassName(link, `${CLASSNAME}__right-icon`);
20
19
  const leftIcon = queryByClassName(link, `${CLASSNAME}__left-icon`);
21
-
22
- return { props, link, content, rightIcon, leftIcon };
20
+ return { props, link, rightIcon, leftIcon };
23
21
  };
24
22
 
25
23
  describe(`<${Link.displayName}>`, () => {
@@ -42,14 +40,12 @@ describe(`<${Link.displayName}>`, () => {
42
40
  color: ColorPalette.primary,
43
41
  colorVariant: ColorVariant.D1,
44
42
  });
45
- expect(link.className).toMatchInlineSnapshot(
46
- '"lumx-link lumx-link--color-primary lumx-link--color-variant-D1"',
47
- );
43
+ expect(link.className).toBe('lumx-link lumx-link--color-primary lumx-link--color-variant-D1');
48
44
  });
49
45
 
50
46
  it('should render typography', () => {
51
- const { content } = setup({ href: 'https://google.com', typography: Typography.title });
52
- expect(content?.className).toMatchInlineSnapshot('undefined');
47
+ const { link } = setup({ href: 'https://google.com', typography: Typography.title });
48
+ expect(link.className).toBe('lumx-link lumx-typography-title');
53
49
  });
54
50
 
55
51
  it('should render a button', () => {
@@ -59,6 +55,12 @@ describe(`<${Link.displayName}>`, () => {
59
55
  expect(link).toBe(screen.queryByRole('button', { name }));
60
56
  });
61
57
 
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
+
62
64
  it('should render with icons', () => {
63
65
  const { rightIcon, leftIcon } = setup({ rightIcon: mdiPlus, leftIcon: mdiCheck });
64
66
  expect(rightIcon).toBeInTheDocument();
@@ -1,14 +1,12 @@
1
- import React, { RefObject, useMemo } from 'react';
2
-
3
- import isEmpty from 'lodash/isEmpty';
1
+ import React from 'react';
4
2
 
5
3
  import classNames from 'classnames';
6
4
 
7
- import { ColorPalette, ColorVariant, Icon, Size, Typography } from '@lumx/react';
5
+ import { ColorPalette, ColorVariant, Icon, Typography } from '@lumx/react';
8
6
  import { GenericProps } from '@lumx/react/utils/type';
9
- import { getRootClassName, handleBasicClasses } from '@lumx/react/utils/className';
10
- import { renderLink } from '@lumx/react/utils/react/renderLink';
7
+ import { getRootClassName, getTypographyClassName, handleBasicClasses } from '@lumx/react/utils/className';
11
8
  import { forwardRef } from '@lumx/react/utils/react/forwardRef';
9
+ import { wrapChildrenIconWithSpaces } from '@lumx/react/utils/react/wrapChildrenIconWithSpaces';
12
10
 
13
11
  type HTMLAnchorProps = React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
14
12
 
@@ -24,11 +22,17 @@ export interface LinkProps extends GenericProps {
24
22
  href?: HTMLAnchorProps['href'];
25
23
  /** Whether the component is disabled or not. */
26
24
  isDisabled?: boolean;
27
- /** Left icon (SVG path). */
25
+ /**
26
+ * Left icon (SVG path).
27
+ * @deprecated Just nest `<Icon />` in the children
28
+ */
28
29
  leftIcon?: string;
29
30
  /** Custom react component for the link (can be used to inject react router Link). */
30
31
  linkAs?: 'a' | any;
31
- /** Right icon (SVG path). */
32
+ /**
33
+ * Right icon (SVG path).
34
+ * @deprecated Just nest `<Icon />` in the children
35
+ */
32
36
  rightIcon?: string;
33
37
  /** Link target. */
34
38
  target?: HTMLAnchorProps['target'];
@@ -48,36 +52,6 @@ const COMPONENT_NAME = 'Link';
48
52
  */
49
53
  const CLASSNAME = getRootClassName(COMPONENT_NAME);
50
54
 
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
-
81
55
  /**
82
56
  * Link component.
83
57
  *
@@ -101,58 +75,37 @@ export const Link = forwardRef<LinkProps, HTMLAnchorElement | HTMLButtonElement>
101
75
  typography,
102
76
  ...forwardedProps
103
77
  } = props;
104
- const renderedChildren = useMemo(
105
- () => (
106
- <>
107
- {leftIcon && !isEmpty(leftIcon) && (
108
- <Icon icon={leftIcon} className={`${CLASSNAME}__left-icon`} size={getIconSize(typography)} />
109
- )}
110
-
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
- );
128
78
 
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
- );
79
+ const isLink = linkAs || href;
80
+ const Component = isLink && !isDisabled ? linkAs || 'a' : 'button';
81
+ const baseProps: React.ComponentProps<typeof Component> = {};
82
+ if (Component === 'button') {
83
+ baseProps.type = 'button';
84
+ baseProps.disabled = isDisabled;
85
+ } else if (isLink) {
86
+ baseProps.href = href;
87
+ baseProps.target = target;
145
88
  }
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,
89
+
90
+ return (
91
+ <Component
92
+ ref={ref}
93
+ {...forwardedProps}
94
+ {...baseProps}
95
+ className={classNames(
96
+ className,
97
+ handleBasicClasses({ prefix: CLASSNAME, color, colorVariant }),
98
+ typography && getTypographyClassName(typography),
99
+ )}
100
+ >
101
+ {wrapChildrenIconWithSpaces(
102
+ <>
103
+ {leftIcon && <Icon icon={leftIcon} className={`${CLASSNAME}__left-icon`} />}
104
+ {children}
105
+ {rightIcon && <Icon icon={rightIcon} className={`${CLASSNAME}__right-icon`} />}
106
+ </>,
107
+ )}
108
+ </Component>
156
109
  );
157
110
  });
158
111
  Link.displayName = COMPONENT_NAME;
@@ -1,9 +1,9 @@
1
- import React, { Children, Fragment } from 'react';
1
+ import React from 'react';
2
2
 
3
3
  import classNames from 'classnames';
4
4
 
5
- import { Icon, ColorPalette, ColorVariant, Typography, WhiteSpace } from '@lumx/react';
6
- import { GenericProps, TextElement, isComponent } from '@lumx/react/utils/type';
5
+ import { ColorPalette, ColorVariant, Typography, WhiteSpace } from '@lumx/react';
6
+ import { GenericProps, TextElement } from '@lumx/react/utils/type';
7
7
  import {
8
8
  getFontColorClassName,
9
9
  getRootClassName,
@@ -13,6 +13,7 @@ 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';
16
17
 
17
18
  /**
18
19
  * Defines the props of the component.
@@ -132,14 +133,7 @@ export const Text = forwardRef<TextProps>((props, ref) => {
132
133
  style={{ ...truncateLinesStyle, ...whiteSpaceStyle, ...style }}
133
134
  {...forwardedProps}
134
135
  >
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
- })}
136
+ {wrapChildrenIconWithSpaces(children)}
143
137
  </Component>
144
138
  );
145
139
  });
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { mdiTranslate } from '@lumx/icons';
3
- import { Chip, IconButton, TextField } from '@lumx/react';
3
+ import { Chip, IconButton, TextField, Typography } from '@lumx/react';
4
4
  import { withValueOnChange } from '@lumx/react/stories/decorators/withValueOnChange';
5
5
  import { loremIpsum } from '@lumx/react/stories/utils/lorem';
6
6
 
@@ -47,6 +47,20 @@ export const LabelAndHelper = {
47
47
  },
48
48
  };
49
49
 
50
+ /**
51
+ * With custom label and helper
52
+ */
53
+ export const CustomLabelAndHelper = {
54
+ args: {
55
+ ...Default.args,
56
+ label: 'Textfield label',
57
+ labelProps: {
58
+ typography: Typography.subtitle1,
59
+ },
60
+ helper: loremIpsum('tiny'),
61
+ },
62
+ };
63
+
50
64
  /**
51
65
  * With clear button
52
66
  */
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
 
3
- import { mdiStar } from '@lumx/icons';
4
- import { Badge, ColorPalette, Icon, Orientation, Size } from '@lumx/react';
3
+ import { mdiMenuDown, mdiStar } from '@lumx/icons';
4
+ import { Badge, ColorPalette, Icon, IconButton, Orientation, Size, Text } from '@lumx/react';
5
5
  import { CustomLink } from '@lumx/react/stories/utils/CustomLink';
6
6
 
7
7
  import { AVATAR_IMAGES } from '@lumx/react/stories/controls/image';
@@ -31,11 +31,38 @@ export const AvatarAndName = {
31
31
  args: { ...AvatarOnly.args, name: 'Emmitt O. Lum' },
32
32
  };
33
33
 
34
+ /** Avatar and children */
35
+ export const AvatarAndCustomName = {
36
+ args: { ...AvatarOnly.args, name: <Text as="span">Emmitt O. Lum</Text> },
37
+ };
38
+
34
39
  /** Avatar, name and secondary fields */
35
40
  export const AvatarAndNameAndSecondaryFields = {
36
41
  args: { ...AvatarAndName.args, fields: ['Creative developer', 'Denpasar'] },
37
42
  };
38
43
 
44
+ /** With Right component */
45
+ export const WithAfter = {
46
+ args: {
47
+ ...AvatarAndName.args,
48
+ fields: ['Creative developer', 'Denpasar'],
49
+ after: <IconButton label="View" icon={mdiMenuDown} emphasis="low" />,
50
+ },
51
+ };
52
+
53
+ /** With after component */
54
+ export const WithAdditionalFields = {
55
+ args: {
56
+ ...AvatarAndName.args,
57
+ fields: ['Creative developer', 'Denpasar'],
58
+ additionalFields: (
59
+ <Text as="span" typography="body1">
60
+ Works at the Toronto office
61
+ </Text>
62
+ ),
63
+ },
64
+ };
65
+
39
66
  /** Size variants */
40
67
  export const SizesAndOrientations = {
41
68
  ...AvatarAndNameAndSecondaryFields,
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { commonTestsSuiteRTL, SetupRenderOptions } from '@lumx/react/testing/utils';
4
- import { render, within } from '@testing-library/react';
4
+ import { render, within, screen } from '@testing-library/react';
5
5
  import { getByClassName, queryByClassName } from '@lumx/react/testing/utils/queries';
6
- import { Thumbnail } from '@lumx/react';
6
+ import { Text, Thumbnail } from '@lumx/react';
7
7
  import userEvent from '@testing-library/user-event';
8
8
 
9
9
  import { UserBlock, UserBlockProps } from './UserBlock';
@@ -22,8 +22,9 @@ const setup = (propsOverride: Partial<UserBlockProps> = {}, { wrapper }: SetupRe
22
22
  const avatar = queryByClassName(userBlock, `${CLASSNAME}__avatar`);
23
23
  const thumbnail = avatar && queryByClassName(avatar, Thumbnail.className as string);
24
24
  const fields = queryByClassName(userBlock, `${CLASSNAME}__fields`);
25
+ const after = queryByClassName(userBlock, `${CLASSNAME}__after`);
25
26
 
26
- return { props, userBlock, name, avatar, thumbnail, fields };
27
+ return { props, userBlock, name, avatar, thumbnail, fields, after };
27
28
  };
28
29
 
29
30
  describe(`<${UserBlock.displayName}>`, () => {
@@ -78,6 +79,17 @@ describe(`<${UserBlock.displayName}>`, () => {
78
79
  expect(within(fields as any).getByText('Field 1')).toBeInTheDocument();
79
80
  expect(within(fields as any).getByText('Field 2')).toBeInTheDocument();
80
81
  });
82
+
83
+ it('should render additional fields', () => {
84
+ setup({ additionalFields: <Text as="span">Works in Toronto</Text> });
85
+ expect(screen.queryByText(/works in toronto/i)).toBeInTheDocument();
86
+ });
87
+
88
+ it('should render after', () => {
89
+ const { after } = setup({ after: <Text as="span">After</Text> });
90
+ expect(after).toBeInTheDocument();
91
+ expect(screen.queryByText(/after/i)).toBeInTheDocument();
92
+ });
81
93
  });
82
94
 
83
95
  // Common tests suite.