@lumx/react 3.11.4-alpha.0 → 3.12.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.0",
10
+ "@lumx/icons": "^3.12.0",
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.11.4-alpha.0"
113
+ "version": "3.12.0"
114
114
  }
@@ -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.
@@ -32,7 +32,7 @@ export interface UserBlockProps extends GenericProps, HasTheme {
32
32
  /** Multiple action toolbar content. */
33
33
  multipleActions?: ReactNode;
34
34
  /** User name. */
35
- name?: string;
35
+ name?: React.ReactNode;
36
36
  /** Props to pass to the name block. */
37
37
  nameProps?: GenericProps;
38
38
  /** Orientation. */
@@ -47,6 +47,10 @@ export interface UserBlockProps extends GenericProps, HasTheme {
47
47
  onMouseEnter?(): void;
48
48
  /** On mouse leave callback. */
49
49
  onMouseLeave?(): void;
50
+ /** Display additional fields below the original name and fields */
51
+ additionalFields?: React.ReactNode;
52
+ /** Display an additional element after the entire component. (to the right if orientation is horizontal, at the bottom if orientation is vertical) */
53
+ after?: React.ReactNode;
50
54
  }
51
55
 
52
56
  /**
@@ -92,6 +96,9 @@ export const UserBlock = forwardRef<UserBlockProps, HTMLDivElement>((props, ref)
92
96
  simpleAction,
93
97
  size,
94
98
  theme = defaultTheme,
99
+ children,
100
+ additionalFields,
101
+ after,
95
102
  ...forwardedProps
96
103
  } = props;
97
104
  let componentSize = size;
@@ -131,7 +138,9 @@ export const UserBlock = forwardRef<UserBlockProps, HTMLDivElement>((props, ref)
131
138
  return <NameComponent {...nProps}>{name}</NameComponent>;
132
139
  }, [avatarProps, isClickable, linkAs, linkProps, name, nameProps, onClick]);
133
140
 
134
- const fieldsBlock: ReactNode = fields && componentSize !== Size.s && componentSize !== Size.xs && (
141
+ const shouldDisplayFields = componentSize !== Size.s && componentSize !== Size.xs;
142
+
143
+ const fieldsBlock: ReactNode = fields && shouldDisplayFields && (
135
144
  <div className={`${CLASSNAME}__fields`}>
136
145
  {fields.map((field: string, idx: number) => (
137
146
  <span key={idx} className={`${CLASSNAME}__field`}>
@@ -164,16 +173,18 @@ export const UserBlock = forwardRef<UserBlockProps, HTMLDivElement>((props, ref)
164
173
  theme={theme}
165
174
  />
166
175
  )}
167
- {(fields || name) && (
176
+ {(fields || name || children || additionalFields) && (
168
177
  <div className={`${CLASSNAME}__wrapper`}>
169
- {nameBlock}
178
+ {children || nameBlock}
170
179
  {fieldsBlock}
180
+ {shouldDisplayFields ? additionalFields : null}
171
181
  </div>
172
182
  )}
173
183
  {shouldDisplayActions && simpleAction && <div className={`${CLASSNAME}__action`}>{simpleAction}</div>}
174
184
  {shouldDisplayActions && multipleActions && (
175
185
  <div className={`${CLASSNAME}__actions`}>{multipleActions}</div>
176
186
  )}
187
+ {after ? <div className={`${CLASSNAME}__after`}>{after}</div> : null}
177
188
  </div>
178
189
  );
179
190
  });