@transferwise/components 0.0.0-experimental-eec707b → 0.0.0-experimental-dc27730

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.
Files changed (51) hide show
  1. package/build/index.esm.js +29 -90
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +32 -93
  4. package/build/index.js.map +1 -1
  5. package/build/types/avatarWrapper/AvatarWrapper.d.ts +18 -28
  6. package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
  7. package/build/types/avatarWrapper/index.d.ts +1 -1
  8. package/build/types/avatarWrapper/index.d.ts.map +1 -1
  9. package/build/types/badge/Badge.d.ts +2 -2
  10. package/build/types/badge/Badge.d.ts.map +1 -1
  11. package/build/types/badge/index.d.ts +2 -1
  12. package/build/types/badge/index.d.ts.map +1 -1
  13. package/build/types/common/index.d.ts +1 -1
  14. package/build/types/common/propsValues/profileType.d.ts +2 -0
  15. package/build/types/common/propsValues/profileType.d.ts.map +1 -1
  16. package/build/types/common/propsValues/sentiment.d.ts +1 -0
  17. package/build/types/common/propsValues/sentiment.d.ts.map +1 -1
  18. package/build/types/image/Image.d.ts +17 -21
  19. package/build/types/image/Image.d.ts.map +1 -1
  20. package/build/types/image/index.d.ts +1 -1
  21. package/build/types/image/index.d.ts.map +1 -1
  22. package/build/types/inlineAlert/InlineAlert.d.ts +12 -15
  23. package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
  24. package/build/types/inlineAlert/index.d.ts +1 -1
  25. package/build/types/inlineAlert/index.d.ts.map +1 -1
  26. package/build/types/promoCard/PromoCard.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/avatarWrapper/AvatarWrapper.spec.tsx +105 -0
  29. package/src/avatarWrapper/AvatarWrapper.story.tsx +1 -41
  30. package/src/avatarWrapper/{AvatarWrapper.js → AvatarWrapper.tsx} +25 -56
  31. package/src/avatarWrapper/__snapshots__/{AvatarWrapper.spec.js.snap → AvatarWrapper.spec.tsx.snap} +76 -76
  32. package/src/badge/Badge.tsx +2 -2
  33. package/src/badge/index.ts +2 -0
  34. package/src/common/index.js +1 -1
  35. package/src/common/propsValues/profileType.ts +3 -0
  36. package/src/common/propsValues/sentiment.ts +10 -0
  37. package/src/image/{Image.spec.js → Image.spec.tsx} +5 -5
  38. package/src/image/{Image.story.js → Image.story.tsx} +3 -3
  39. package/src/image/Image.tsx +65 -0
  40. package/src/inlineAlert/{InlineAlert.story.js → InlineAlert.story.tsx} +3 -2
  41. package/src/inlineAlert/InlineAlert.tsx +59 -0
  42. package/src/promoCard/PromoCard.tsx +1 -6
  43. package/src/promoCard/__snapshots__/PromoCardGroup.spec.tsx.snap +2 -2
  44. package/src/avatarWrapper/AvatarWrapper.spec.js +0 -81
  45. package/src/badge/index.js +0 -1
  46. package/src/image/Image.js +0 -78
  47. package/src/inlineAlert/InlineAlert.js +0 -62
  48. /package/src/avatarWrapper/{index.js → index.ts} +0 -0
  49. /package/src/image/{index.js → index.ts} +0 -0
  50. /package/src/inlineAlert/{InlineAlert.spec.js → InlineAlert.spec.tsx} +0 -0
  51. /package/src/inlineAlert/{index.js → index.ts} +0 -0
@@ -1,6 +1,79 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`FlowNavigationAvatar with a badge status icon passed renders the badge 1`] = `
3
+ exports[`FlowNavigationAvatar with a name AND profileType FlowNavigationAvatar with a name AND profileType AND avatar url renders the image 1`] = `
4
+ <div
5
+ class="tw-avatar tw-avatar--48 tw-avatar--thumbnail"
6
+ >
7
+ <div
8
+ class="tw-avatar__content"
9
+ >
10
+ <img
11
+ alt="avatar"
12
+ src="https://wise.com"
13
+ />
14
+ </div>
15
+ </div>
16
+ `;
17
+
18
+ exports[`FlowNavigationAvatar with a name AND profileType FlowNavigationAvatar with a name AND profileType renders as BUSINESS profile type with an icon 1`] = `
19
+ <div
20
+ class="tw-avatar tw-avatar--48 tw-avatar--icon"
21
+ >
22
+ <div
23
+ class="tw-avatar__content"
24
+ >
25
+ <span
26
+ aria-hidden="true"
27
+ class="tw-icon tw-icon-briefcase "
28
+ data-testid="briefcase-icon"
29
+ role="presentation"
30
+ >
31
+ <svg
32
+ fill="currentColor"
33
+ focusable="false"
34
+ height="24"
35
+ viewBox="0 0 24 24"
36
+ width="24"
37
+ >
38
+ <path
39
+ d="M21.428 6.429H18V3.857A.86.86 0 0 0 17.143 3H6.857A.86.86 0 0 0 6 3.857V6.43H2.571a.86.86 0 0 0-.857.857v12.857a.86.86 0 0 0 .857.857h18.857a.86.86 0 0 0 .858-.857V7.286a.86.86 0 0 0-.858-.857ZM7.714 4.714h8.572V6.43H7.714V4.714Zm8.572 3.429v11.143H7.714V8.143h8.572Zm-12.857 0H6v11.143H3.429V8.143ZM20.57 19.286H18V8.143h2.571v11.143Z"
40
+ />
41
+ </svg>
42
+ </span>
43
+ </div>
44
+ </div>
45
+ `;
46
+
47
+ exports[`FlowNavigationAvatar with a name AND profileType FlowNavigationAvatar with a name AND profileType renders as PERSONAL profile type with an icon 1`] = `
48
+ <div
49
+ class="tw-avatar tw-avatar--48 tw-avatar--icon"
50
+ >
51
+ <div
52
+ class="tw-avatar__content"
53
+ >
54
+ <span
55
+ aria-hidden="true"
56
+ class="tw-icon tw-icon-person "
57
+ data-testid="person-icon"
58
+ role="presentation"
59
+ >
60
+ <svg
61
+ fill="currentColor"
62
+ focusable="false"
63
+ height="24"
64
+ viewBox="0 0 24 24"
65
+ width="24"
66
+ >
67
+ <path
68
+ d="M15.257 14.014A5.922 5.922 0 0 0 18 9c0-3.3-2.7-6-6-6S6 5.7 6 9c0 2.1 1.071 3.943 2.743 5.014-2.614 1.243-4.457 3.9-4.457 6.986H6c0-3.3 2.7-6 6-6s6 2.7 6 6h1.714c0-3.086-1.842-5.743-4.457-6.986ZM7.714 9A4.298 4.298 0 0 1 12 4.714 4.298 4.298 0 0 1 16.286 9 4.298 4.298 0 0 1 12 13.286 4.298 4.298 0 0 1 7.714 9Z"
69
+ />
70
+ </svg>
71
+ </span>
72
+ </div>
73
+ </div>
74
+ `;
75
+
76
+ exports[`FlowNavigationAvatar with a name AND profileType with a badge status icon passed renders the badge 1`] = `
4
77
  <div
5
78
  class="tw-badge tw-badge-border-light tw-badge-sm"
6
79
  >
@@ -64,7 +137,7 @@ exports[`FlowNavigationAvatar with a badge status icon passed renders the badge
64
137
  </div>
65
138
  `;
66
139
 
67
- exports[`FlowNavigationAvatar with a badge url passed renders the badge 1`] = `
140
+ exports[`FlowNavigationAvatar with a name AND profileType with a badge url passed renders the badge 1`] = `
68
141
  <div
69
142
  class="tw-badge tw-badge-border-light tw-badge-lg"
70
143
  >
@@ -109,80 +182,7 @@ exports[`FlowNavigationAvatar with a badge url passed renders the badge 1`] = `
109
182
  </div>
110
183
  `;
111
184
 
112
- exports[`FlowNavigationAvatar with a name AND profileType AND avatar url renders the image 1`] = `
113
- <div
114
- class="tw-avatar tw-avatar--48 tw-avatar--thumbnail"
115
- >
116
- <div
117
- class="tw-avatar__content"
118
- >
119
- <img
120
- alt="avatar"
121
- src="https://wise.com"
122
- />
123
- </div>
124
- </div>
125
- `;
126
-
127
- exports[`FlowNavigationAvatar with a name AND profileType renders as BUSINESS profile type with an icon 1`] = `
128
- <div
129
- class="tw-avatar tw-avatar--48 tw-avatar--icon"
130
- >
131
- <div
132
- class="tw-avatar__content"
133
- >
134
- <span
135
- aria-hidden="true"
136
- class="tw-icon tw-icon-briefcase "
137
- data-testid="briefcase-icon"
138
- role="presentation"
139
- >
140
- <svg
141
- fill="currentColor"
142
- focusable="false"
143
- height="24"
144
- viewBox="0 0 24 24"
145
- width="24"
146
- >
147
- <path
148
- d="M21.428 6.429H18V3.857A.86.86 0 0 0 17.143 3H6.857A.86.86 0 0 0 6 3.857V6.43H2.571a.86.86 0 0 0-.857.857v12.857a.86.86 0 0 0 .857.857h18.857a.86.86 0 0 0 .858-.857V7.286a.86.86 0 0 0-.858-.857ZM7.714 4.714h8.572V6.43H7.714V4.714Zm8.572 3.429v11.143H7.714V8.143h8.572Zm-12.857 0H6v11.143H3.429V8.143ZM20.57 19.286H18V8.143h2.571v11.143Z"
149
- />
150
- </svg>
151
- </span>
152
- </div>
153
- </div>
154
- `;
155
-
156
- exports[`FlowNavigationAvatar with a name AND profileType renders as PERSONAL profile type with an icon 1`] = `
157
- <div
158
- class="tw-avatar tw-avatar--48 tw-avatar--icon"
159
- >
160
- <div
161
- class="tw-avatar__content"
162
- >
163
- <span
164
- aria-hidden="true"
165
- class="tw-icon tw-icon-person "
166
- data-testid="person-icon"
167
- role="presentation"
168
- >
169
- <svg
170
- fill="currentColor"
171
- focusable="false"
172
- height="24"
173
- viewBox="0 0 24 24"
174
- width="24"
175
- >
176
- <path
177
- d="M15.257 14.014A5.922 5.922 0 0 0 18 9c0-3.3-2.7-6-6-6S6 5.7 6 9c0 2.1 1.071 3.943 2.743 5.014-2.614 1.243-4.457 3.9-4.457 6.986H6c0-3.3 2.7-6 6-6s6 2.7 6 6h1.714c0-3.086-1.842-5.743-4.457-6.986ZM7.714 9A4.298 4.298 0 0 1 12 4.714 4.298 4.298 0 0 1 16.286 9 4.298 4.298 0 0 1 12 13.286 4.298 4.298 0 0 1 7.714 9Z"
178
- />
179
- </svg>
180
- </span>
181
- </div>
182
- </div>
183
- `;
184
-
185
- exports[`FlowNavigationAvatar with nothing passed renders a personal icon 1`] = `
185
+ exports[`FlowNavigationAvatar with a name AND profileType with nothing passed renders a personal icon 1`] = `
186
186
  <div
187
187
  class="tw-avatar tw-avatar--48 tw-avatar--icon"
188
188
  >
@@ -12,7 +12,7 @@ import {
12
12
  CommonProps,
13
13
  } from '../common';
14
14
 
15
- type Props = {
15
+ export type BadgeProps = {
16
16
  badge: ReactNode;
17
17
  children: ReactNode;
18
18
  size?: SizeSmall | SizeMedium | SizeLarge;
@@ -25,7 +25,7 @@ const Badge = ({
25
25
  size = Size.SMALL,
26
26
  border = Theme.LIGHT,
27
27
  children,
28
- }: Props): ReactElement => {
28
+ }: BadgeProps): ReactElement => {
29
29
  const classes: string = classNames(
30
30
  'tw-badge',
31
31
  {
@@ -0,0 +1,2 @@
1
+ export { default } from './Badge';
2
+ export type { BadgeProps } from './Badge';
@@ -16,7 +16,7 @@ export * from './propsValues/position';
16
16
  export { Layout } from './propsValues/layouts';
17
17
  export * from './propsValues/status';
18
18
  export { Sentiment } from './propsValues/sentiment';
19
- export { ProfileType } from './propsValues/profileType';
19
+ export * from './propsValues/profileType';
20
20
  export { Variant } from './propsValues/variant';
21
21
  export * from './propsValues/scroll';
22
22
  export { MarkdownNodeType } from './propsValues/markdownNodeType';
@@ -2,3 +2,6 @@ export enum ProfileType {
2
2
  BUSINESS = 'BUSINESS',
3
3
  PERSONAL = 'PERSONAL',
4
4
  }
5
+
6
+ export type ProfileTypeBusiness = 'BUSINESS';
7
+ export type ProfileTypePersonal = 'PERSONAL';
@@ -18,3 +18,13 @@ export enum Sentiment {
18
18
  */
19
19
  SUCCESS = 'success',
20
20
  }
21
+
22
+ export type SentimentString =
23
+ | 'negative'
24
+ | 'neutral'
25
+ | 'positive'
26
+ | 'warning'
27
+ | 'pending'
28
+ | 'info'
29
+ | 'error'
30
+ | 'success';
@@ -2,11 +2,11 @@ import '@testing-library/jest-dom';
2
2
  import * as useHasIntersectedUtils from '../common/hooks/useHasIntersected/useHasIntersected';
3
3
  import { render, fireEvent, screen } from '../test-utils';
4
4
 
5
- import { EmptyTransparentImage } from './Image';
5
+ import { EmptyTransparentImage, ImageProps } from './Image';
6
6
 
7
7
  import Image from '.';
8
8
 
9
- const props = {
9
+ const props: ImageProps = {
10
10
  id: 'id',
11
11
  src: 'https://www.a-valid-src.png/',
12
12
  alt: 'test',
@@ -23,7 +23,7 @@ describe('Image', () => {
23
23
  it(`renders image`, () => {
24
24
  render(<Image {...props} loading="eager" />);
25
25
 
26
- const image = screen.getByRole('img');
26
+ const image: HTMLImageElement = screen.getByRole('img');
27
27
 
28
28
  expect(image).toBeInTheDocument();
29
29
  expect(image.src).toStrictEqual(props.src);
@@ -37,7 +37,7 @@ describe('Image', () => {
37
37
  jest.spyOn(useHasIntersectedUtils, 'useHasIntersected').mockReturnValue([false]);
38
38
  render(<Image {...props} />);
39
39
 
40
- const image = screen.getByRole('img');
40
+ const image: HTMLImageElement = screen.getByRole('img');
41
41
 
42
42
  expect(image).toBeInTheDocument();
43
43
  expect(image.src).toStrictEqual(EmptyTransparentImage);
@@ -48,7 +48,7 @@ describe('Image', () => {
48
48
 
49
49
  render(<Image {...props} />);
50
50
 
51
- const image = screen.getByRole('img');
51
+ const image: HTMLImageElement = screen.getByRole('img');
52
52
 
53
53
  expect(image).toBeInTheDocument();
54
54
  expect(image.src).toStrictEqual(props.src);
@@ -33,7 +33,7 @@ export const Basic = () => {
33
33
  shrink={shrink}
34
34
  stretch={stretch}
35
35
  onLoad={action('load 1')}
36
- onError={(error) => action(error)}
36
+ onError={action('error')}
37
37
  />
38
38
  <Image
39
39
  alt="test"
@@ -44,7 +44,7 @@ export const Basic = () => {
44
44
  shrink={shrink}
45
45
  stretch={stretch}
46
46
  onLoad={action('load 2')}
47
- onError={(error) => action(error)}
47
+ onError={action('error')}
48
48
  />
49
49
  <Image
50
50
  alt="test"
@@ -55,7 +55,7 @@ export const Basic = () => {
55
55
  shrink={shrink}
56
56
  stretch={stretch}
57
57
  onLoad={action('load 3')}
58
- onError={(error) => action(error)}
58
+ onError={action('error')}
59
59
  />
60
60
  </>
61
61
  );
@@ -0,0 +1,65 @@
1
+ import classnames from 'classnames';
2
+ import { useRef } from 'react';
3
+
4
+ import { useHasIntersected } from '../common/hooks';
5
+
6
+ export interface ImageProps {
7
+ alt: string;
8
+ src: string;
9
+ id?: string;
10
+ onLoad?: () => void;
11
+ onError?: () => void;
12
+ className?: string;
13
+ loading?: 'lazy' | 'eager';
14
+ stretch?: boolean;
15
+ role?: string;
16
+ shrink?: boolean;
17
+ }
18
+
19
+ export const EmptyTransparentImage =
20
+ 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
21
+
22
+ const Image = ({
23
+ id,
24
+ src,
25
+ alt,
26
+ onLoad,
27
+ onError,
28
+ className,
29
+ loading,
30
+ stretch = true,
31
+ role,
32
+ shrink = true,
33
+ }: ImageProps) => {
34
+ const elementReference = useRef<HTMLImageElement>(null);
35
+ const [hasIntersected] = useHasIntersected({ elRef: elementReference, loading });
36
+ let imageSource = src;
37
+ let imageOnLoad = onLoad;
38
+
39
+ if (loading === 'lazy' && !hasIntersected) {
40
+ imageSource = EmptyTransparentImage;
41
+ imageOnLoad = undefined;
42
+ }
43
+
44
+ return (
45
+ <img
46
+ ref={elementReference}
47
+ id={id}
48
+ alt={alt}
49
+ src={imageSource}
50
+ className={classnames([
51
+ 'tw-image',
52
+ {
53
+ 'tw-image__stretch': stretch,
54
+ 'tw-image__shrink': shrink,
55
+ },
56
+ className,
57
+ ])}
58
+ role={role}
59
+ onLoad={imageOnLoad}
60
+ onError={onError}
61
+ />
62
+ );
63
+ };
64
+
65
+ export default Image;
@@ -1,14 +1,15 @@
1
1
  import { select, text } from '@storybook/addon-knobs';
2
+ import { Meta } from '@storybook/react';
2
3
 
3
4
  import { Sentiment } from '../common';
4
5
  import { Input } from '../inputs/Input';
5
6
 
6
- import InlineAlert from './InlineAlert';
7
+ import InlineAlert, { InlineAlertProps } from './InlineAlert';
7
8
 
8
9
  export default {
9
10
  component: InlineAlert,
10
11
  title: 'Forms/InlineAlert',
11
- };
12
+ } as Meta<InlineAlertProps>;
12
13
 
13
14
  export const Basic = () => {
14
15
  const type = select(
@@ -0,0 +1,59 @@
1
+ import { AlertCircle as AlertCircleIcon } from '@transferwise/icons';
2
+ import { useTheme } from '@wise/components-theming';
3
+ import classNames from 'classnames';
4
+ import { ReactNode } from 'react';
5
+
6
+ import withArrow, { AlertArrowPosition } from '../alert/withArrow';
7
+ import { Sentiment } from '../common';
8
+ import { SentimentString } from '../common/propsValues/sentiment';
9
+
10
+ export interface InlineAlertProps {
11
+ id?: string;
12
+ type?: Sentiment | SentimentString;
13
+ className?: string;
14
+ children: ReactNode;
15
+ }
16
+
17
+ const typeClassMap: Record<Sentiment.ERROR | Sentiment.NEGATIVE, string> = {
18
+ [Sentiment.ERROR]: 'danger',
19
+ [Sentiment.NEGATIVE]: 'danger',
20
+ };
21
+
22
+ const InlineAlert = ({ id, type = Sentiment.NEUTRAL, className, children }: InlineAlertProps) => {
23
+ const { isModern } = useTheme();
24
+
25
+ const typeClass = `alert-${typeClassMap[type as Sentiment.ERROR | Sentiment.NEGATIVE] || type}`;
26
+
27
+ if (isModern) {
28
+ return (
29
+ <div role="alert" id={id} className={classNames('alert alert-detach', typeClass, className)}>
30
+ {(type === 'error' || type === 'negative') && <AlertCircleIcon />}
31
+ <div>{children}</div>
32
+ </div>
33
+ );
34
+ }
35
+
36
+ const getAlertContents = ({
37
+ children,
38
+ className,
39
+ }: {
40
+ children: ReactNode;
41
+ className?: string;
42
+ }) => {
43
+ return (
44
+ <div
45
+ role="alert"
46
+ id={id}
47
+ className={classNames('alert alert-detach p-x-2 p-y-1', typeClass, className)}
48
+ >
49
+ {children}
50
+ </div>
51
+ );
52
+ };
53
+
54
+ const AlertWithArrow = withArrow(getAlertContents, AlertArrowPosition.TOP_LEFT);
55
+
56
+ return <AlertWithArrow {...{ id, type, className, children }} />;
57
+ };
58
+
59
+ export default InlineAlert;
@@ -326,12 +326,7 @@ const PromoCard: FunctionComponent<PromoCardProps> = forwardRef(
326
326
  <Body className="np-Card-description">{description}</Body>
327
327
  {imageSource && (
328
328
  <div className={classNames('np-Card-image', { imageClass })}>
329
- <Image
330
- src={imageSource}
331
- alt={imageAlt}
332
- role={!imageAlt ? 'presentation' : null}
333
- loading="lazy"
334
- />
329
+ <Image src={imageSource} alt={imageAlt || ''} loading="lazy" />
335
330
  </div>
336
331
  )}
337
332
  <PromoCardIndicator label={indicatorLabel} icon={icon} />
@@ -31,8 +31,8 @@ exports[`PromoCardGroup matches snapshot 1`] = `
31
31
  class="np-Card-image"
32
32
  >
33
33
  <img
34
+ alt=""
34
35
  class="tw-image tw-image__stretch tw-image__shrink"
35
- role="presentation"
36
36
  src="https://via.placeholder.com/150"
37
37
  />
38
38
  </div>
@@ -65,8 +65,8 @@ exports[`PromoCardGroup matches snapshot 1`] = `
65
65
  class="np-Card-image"
66
66
  >
67
67
  <img
68
+ alt=""
68
69
  class="tw-image tw-image__stretch tw-image__shrink"
69
- role="presentation"
70
70
  src="https://via.placeholder.com/150"
71
71
  />
72
72
  </div>
@@ -1,81 +0,0 @@
1
- import { Size } from '../common';
2
- import { render, screen } from '../test-utils';
3
-
4
- import Avatar from '.';
5
-
6
- const name = 'Elizabeth Alexandra Mary Windsor';
7
-
8
- describe('FlowNavigationAvatar', () => {
9
- describe('with a name', () => {
10
- it('shows the initials for a long name', () => {
11
- render(<Avatar name={name} />);
12
-
13
- expect(screen.getByText('EW')).toBeInTheDocument();
14
- });
15
-
16
- it('shows the first letter for a mononym', () => {
17
- render(<Avatar name="Michelangelo" />);
18
-
19
- expect(screen.getByText('M')).toBeInTheDocument();
20
- });
21
-
22
- describe('AND profileType', () => {
23
- it('renders as BUSINESS profile type with an icon', async () => {
24
- const { container } = render(<Avatar name={name} profileType="BUSINESS" />);
25
-
26
- expect(container.firstChild).toMatchSnapshot();
27
- });
28
-
29
- it('renders as PERSONAL profile type with an icon', async () => {
30
- const { container } = render(<Avatar name={name} profileType="PERSONAL" />);
31
-
32
- expect(container.firstChild).toMatchSnapshot();
33
- });
34
-
35
- describe('AND avatar url', () => {
36
- it('renders the image', async () => {
37
- const { container } = render(
38
- <Avatar
39
- url="https://wise.com"
40
- name={name}
41
- profileType="BUSINESS"
42
- avatarProps={{ theme: 'dark' }}
43
- />,
44
- );
45
-
46
- expect(container.firstChild).toMatchSnapshot();
47
- });
48
- });
49
- });
50
- });
51
-
52
- describe('with nothing passed', () => {
53
- it('renders a personal icon', () => {
54
- const { container } = render(<Avatar />);
55
-
56
- expect(container.firstChild).toMatchSnapshot();
57
- });
58
- });
59
-
60
- describe('with a badge url passed', () => {
61
- it('renders the badge', () => {
62
- const { container } = render(
63
- <Avatar
64
- badgeUrl="https://badge.com"
65
- badgeAltText="badge alt text"
66
- badgeProps={{ size: Size.LARGE }}
67
- />,
68
- );
69
-
70
- expect(container.firstChild).toMatchSnapshot();
71
- });
72
- });
73
-
74
- describe('with a badge status icon passed', () => {
75
- it('renders the badge', () => {
76
- const { container } = render(<Avatar badgeStatusIcon="positive" />);
77
-
78
- expect(container.firstChild).toMatchSnapshot();
79
- });
80
- });
81
- });
@@ -1 +0,0 @@
1
- export { default } from './Badge';
@@ -1,78 +0,0 @@
1
- import classnames from 'classnames';
2
- import PropTypes from 'prop-types';
3
- import { useRef } from 'react';
4
-
5
- import { useHasIntersected } from '../common/hooks';
6
-
7
- /**
8
- * Image component provides a wrapper for image tag. Optional lazy loading functionalities can also be enabled passing prop loading = "lazy.
9
- * Once element intersects viewport image loads.
10
- *
11
- * @param {string} [alt=""] - string that contains the alt text. Required.
12
- * @param {string} [src=""] - string that contains the src text. Required.
13
- * @param {string} [id=""] - string that contains the id text.
14
- * @param {Function} [onLoad=()=>{}] - function that contains load callback. Doesn't fire on SSR https://github.com/facebook/react/issues/15446
15
- * @param {Function} [onError=()=>{}] - function that contains error callback. Doesn't fire on SSR https://github.com/facebook/react/issues/15446
16
- * @param {boolean} [loading="lazy"] - This attribute follows the loading API attribute. If set to eager image will load immediately just like a normal image tag otherwise it will lazy load.
17
- * @usage `<Image alt="alt" src="src" id="id1" onLoad={()=>{}} onError={())=>{}} loading="lazy" />`
18
- */
19
-
20
- export const EmptyTransparentImage =
21
- 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
22
-
23
- const Image = ({ id, src, alt, onLoad, onError, className, loading, stretch, role, shrink }) => {
24
- const elementReference = useRef();
25
- const [hasIntersected] = useHasIntersected({ elRef: elementReference, loading });
26
- let imageSource = src;
27
- let imageOnLoad = onLoad;
28
-
29
- if (loading === 'lazy' && !hasIntersected) {
30
- imageSource = EmptyTransparentImage;
31
- imageOnLoad = null;
32
- }
33
-
34
- return (
35
- <img
36
- ref={elementReference}
37
- id={id}
38
- alt={alt}
39
- src={imageSource}
40
- className={classnames([
41
- 'tw-image',
42
- {
43
- 'tw-image__stretch': stretch,
44
- 'tw-image__shrink': shrink,
45
- },
46
- className,
47
- ])}
48
- role={role}
49
- onLoad={imageOnLoad}
50
- onError={onError}
51
- />
52
- );
53
- };
54
-
55
- Image.propTypes = {
56
- id: PropTypes.string,
57
- src: PropTypes.string.isRequired,
58
- alt: PropTypes.string.isRequired,
59
- onLoad: PropTypes.func,
60
- onError: PropTypes.func,
61
- className: PropTypes.string,
62
- loading: PropTypes.oneOf(['lazy', 'eager']),
63
- stretch: PropTypes.bool,
64
- shrink: PropTypes.bool,
65
- role: PropTypes.string,
66
- };
67
-
68
- Image.defaultProps = {
69
- id: null,
70
- className: '',
71
- onLoad: () => {},
72
- onError: () => {},
73
- loading: 'eager',
74
- stretch: true,
75
- shrink: true,
76
- };
77
-
78
- export default Image;