@transferwise/components 45.13.0 → 45.14.1
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/build/index.esm.js +152 -91
- package/build/index.esm.js.map +1 -1
- package/build/index.js +156 -94
- package/build/index.js.map +1 -1
- package/build/main.css +1 -1
- package/build/styles/inputs/InputGroup.css +1 -0
- package/build/styles/main.css +1 -1
- package/build/types/avatarWrapper/AvatarWrapper.d.ts +18 -28
- package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
- package/build/types/avatarWrapper/index.d.ts +1 -1
- package/build/types/avatarWrapper/index.d.ts.map +1 -1
- package/build/types/badge/Badge.d.ts +2 -2
- package/build/types/badge/Badge.d.ts.map +1 -1
- package/build/types/badge/index.d.ts +2 -1
- package/build/types/badge/index.d.ts.map +1 -1
- package/build/types/body/Body.d.ts +2 -2
- package/build/types/common/closeButton/CloseButton.d.ts +1 -1
- package/build/types/common/hooks/useEffectEvent.d.ts +2 -0
- package/build/types/common/hooks/useEffectEvent.d.ts.map +1 -0
- package/build/types/common/hooks/useResizeObserver.d.ts +3 -0
- package/build/types/common/hooks/useResizeObserver.d.ts.map +1 -0
- package/build/types/common/index.d.ts +1 -1
- package/build/types/common/panel/Panel.d.ts +1 -1
- package/build/types/common/propsValues/profileType.d.ts +2 -0
- package/build/types/common/propsValues/profileType.d.ts.map +1 -1
- package/build/types/common/propsValues/sentiment.d.ts +1 -0
- package/build/types/common/propsValues/sentiment.d.ts.map +1 -1
- package/build/types/common/responsivePanel/ResponsivePanel.d.ts +1 -1
- package/build/types/dateLookup/tableLink/TableLink.d.ts.map +1 -1
- package/build/types/dateLookup/yearCalendar/YearCalendar.d.ts.map +1 -1
- package/build/types/dimmer/Dimmer.d.ts +1 -1
- package/build/types/image/Image.d.ts +17 -21
- package/build/types/image/Image.d.ts.map +1 -1
- package/build/types/image/index.d.ts +1 -1
- package/build/types/image/index.d.ts.map +1 -1
- package/build/types/index.d.ts +2 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inlineAlert/InlineAlert.d.ts +12 -15
- package/build/types/inlineAlert/InlineAlert.d.ts.map +1 -1
- package/build/types/inlineAlert/index.d.ts +1 -1
- package/build/types/inlineAlert/index.d.ts.map +1 -1
- package/build/types/inputs/Input.d.ts +4 -8
- package/build/types/inputs/Input.d.ts.map +1 -1
- package/build/types/inputs/InputGroup.d.ts +21 -0
- package/build/types/inputs/InputGroup.d.ts.map +1 -0
- package/build/types/inputs/TextArea.d.ts +4 -7
- package/build/types/inputs/TextArea.d.ts.map +1 -1
- package/build/types/promoCard/PromoCard.d.ts.map +1 -1
- package/build/types/select/searchBox/SearchBox.d.ts +1 -1
- package/build/types/utilities/cssValueWithUnit.d.ts +2 -0
- package/build/types/utilities/cssValueWithUnit.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/avatarWrapper/AvatarWrapper.spec.tsx +105 -0
- package/src/avatarWrapper/AvatarWrapper.story.tsx +1 -41
- package/src/avatarWrapper/{AvatarWrapper.js → AvatarWrapper.tsx} +25 -56
- package/src/avatarWrapper/__snapshots__/{AvatarWrapper.spec.js.snap → AvatarWrapper.spec.tsx.snap} +76 -76
- package/src/badge/Badge.tsx +2 -2
- package/src/badge/index.ts +2 -0
- package/src/common/hooks/useEffectEvent.ts +22 -0
- package/src/common/hooks/useResizeObserver.ts +22 -0
- package/src/common/index.js +1 -1
- package/src/common/propsValues/profileType.ts +3 -0
- package/src/common/propsValues/sentiment.ts +10 -0
- package/src/image/{Image.spec.js → Image.spec.tsx} +5 -5
- package/src/image/{Image.story.js → Image.story.tsx} +3 -3
- package/src/image/Image.tsx +65 -0
- package/src/index.ts +2 -0
- package/src/inlineAlert/{InlineAlert.story.js → InlineAlert.story.tsx} +3 -2
- package/src/inlineAlert/InlineAlert.tsx +59 -0
- package/src/inputs/Input.tsx +13 -7
- package/src/inputs/InputGroup.css +1 -0
- package/src/inputs/InputGroup.less +61 -0
- package/src/inputs/InputGroup.story.tsx +73 -0
- package/src/inputs/InputGroup.tsx +142 -0
- package/src/inputs/TextArea.tsx +7 -6
- package/src/main.css +1 -1
- package/src/main.less +1 -0
- package/src/promoCard/PromoCard.tsx +1 -6
- package/src/promoCard/__snapshots__/PromoCardGroup.spec.tsx.snap +2 -2
- package/src/utilities/cssValueWithUnit.ts +3 -0
- package/src/avatarWrapper/AvatarWrapper.spec.js +0 -81
- package/src/badge/index.js +0 -1
- package/src/image/Image.js +0 -78
- package/src/inlineAlert/InlineAlert.js +0 -62
- /package/src/avatarWrapper/{index.js → index.ts} +0 -0
- /package/src/image/{index.js → index.ts} +0 -0
- /package/src/inlineAlert/{InlineAlert.spec.js → InlineAlert.spec.tsx} +0 -0
- /package/src/inlineAlert/{index.js → index.ts} +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { BadgeProps } from '../badge';
|
|
2
|
+
import { Sentiment } from '../common';
|
|
3
|
+
import { Size, ProfileType } from '../common';
|
|
4
|
+
import { render, screen } from '../test-utils';
|
|
5
|
+
|
|
6
|
+
import AvatarWrapper from '.';
|
|
7
|
+
|
|
8
|
+
const name = 'Elizabeth Alexandra Mary Windsor';
|
|
9
|
+
|
|
10
|
+
describe('FlowNavigationAvatar', () => {
|
|
11
|
+
describe('with a name', () => {
|
|
12
|
+
it('shows the initials for a long name', () => {
|
|
13
|
+
render(<AvatarWrapper name={name} />);
|
|
14
|
+
|
|
15
|
+
expect(screen.getByText('EW')).toBeInTheDocument();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('shows the first letter for a mononym', () => {
|
|
19
|
+
render(<AvatarWrapper name="Zichaleangeol" />);
|
|
20
|
+
|
|
21
|
+
expect(screen.getByText('Z')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('AND profileType', () => {
|
|
25
|
+
describe('FlowNavigationAvatar', () => {
|
|
26
|
+
describe('with a name', () => {
|
|
27
|
+
it('shows the initials for a long name', () => {
|
|
28
|
+
render(<AvatarWrapper name={name} />);
|
|
29
|
+
|
|
30
|
+
expect(screen.getByText('EW')).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('shows the first letter for a mononym', () => {
|
|
34
|
+
render(<AvatarWrapper name="Michelangelo" />);
|
|
35
|
+
|
|
36
|
+
expect(screen.getByText('M')).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('AND profileType', () => {
|
|
40
|
+
it('renders as BUSINESS profile type with an icon', () => {
|
|
41
|
+
const { container } = render(
|
|
42
|
+
<AvatarWrapper name={name} profileType={ProfileType.BUSINESS} />,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('renders as PERSONAL profile type with an icon', () => {
|
|
49
|
+
const { container } = render(
|
|
50
|
+
<AvatarWrapper name={name} profileType={ProfileType.PERSONAL} />,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('AND avatar url', () => {
|
|
57
|
+
it('renders the image', () => {
|
|
58
|
+
const { container } = render(
|
|
59
|
+
<AvatarWrapper
|
|
60
|
+
url="https://wise.com"
|
|
61
|
+
name={name}
|
|
62
|
+
profileType={ProfileType.BUSINESS}
|
|
63
|
+
avatarProps={{ theme: 'dark' }}
|
|
64
|
+
/>,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('with nothing passed', () => {
|
|
75
|
+
it('renders a personal icon', () => {
|
|
76
|
+
const { container } = render(<AvatarWrapper />);
|
|
77
|
+
|
|
78
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('with a badge url passed', () => {
|
|
83
|
+
it('renders the badge', () => {
|
|
84
|
+
const { container } = render(
|
|
85
|
+
<AvatarWrapper
|
|
86
|
+
badgeUrl="https://badge.com"
|
|
87
|
+
badgeAltText="badge alt text"
|
|
88
|
+
badgeProps={{ size: Size.LARGE } as BadgeProps}
|
|
89
|
+
/>,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('with a badge status icon passed', () => {
|
|
97
|
+
it('renders the badge', () => {
|
|
98
|
+
const { container } = render(<AvatarWrapper badgeStatusIcon={Sentiment.POSITIVE} />);
|
|
99
|
+
|
|
100
|
+
expect(container.firstChild).toMatchSnapshot();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -9,50 +9,10 @@ import AvatarWrapper from './AvatarWrapper';
|
|
|
9
9
|
export default {
|
|
10
10
|
component: AvatarWrapper,
|
|
11
11
|
title: 'Content/AvatarWrapper',
|
|
12
|
-
argTypes: {
|
|
13
|
-
profileType: {
|
|
14
|
-
type: {
|
|
15
|
-
name: 'enum',
|
|
16
|
-
value: Object.keys(ProfileType),
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
badgeStatusIcon: {
|
|
20
|
-
type: {
|
|
21
|
-
name: 'enum',
|
|
22
|
-
value: [
|
|
23
|
-
Sentiment.POSITIVE,
|
|
24
|
-
Sentiment.NEGATIVE,
|
|
25
|
-
Sentiment.NEUTRAL,
|
|
26
|
-
Sentiment.WARNING,
|
|
27
|
-
Sentiment.PENDING,
|
|
28
|
-
],
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
tags: ['autodocs'],
|
|
33
12
|
} satisfies Meta<typeof AvatarWrapper>;
|
|
34
13
|
|
|
35
14
|
type Story = StoryObj<typeof AvatarWrapper>;
|
|
36
15
|
|
|
37
|
-
export const WithBadgeUrl: Story = {
|
|
38
|
-
args: {
|
|
39
|
-
badgeUrl: 'https://wise.com/public-resources/assets/brand/fast_flag_badge_personal.svg',
|
|
40
|
-
avatarProps: {
|
|
41
|
-
outlined: true,
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export const WithBadgeStatus: Story = {
|
|
47
|
-
args: {
|
|
48
|
-
profileType: ProfileType.BUSINESS,
|
|
49
|
-
badgeStatusIcon: Sentiment.PENDING,
|
|
50
|
-
avatarProps: {
|
|
51
|
-
outlined: false,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
16
|
export const All: Story = {
|
|
57
17
|
args: {
|
|
58
18
|
avatarProps: { outlined: true },
|
|
@@ -85,7 +45,7 @@ export const All: Story = {
|
|
|
85
45
|
</div>
|
|
86
46
|
Initials
|
|
87
47
|
<div>
|
|
88
|
-
<AvatarWrapper name="
|
|
48
|
+
<AvatarWrapper name="Any Aname" avatarProps={avatarProps} />
|
|
89
49
|
</div>
|
|
90
50
|
Default
|
|
91
51
|
<div>
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { Person as ProfileIcon, Briefcase as BriefcaseIcon } from '@transferwise/icons';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
2
|
import { useState, useEffect } from 'react';
|
|
4
3
|
|
|
5
|
-
import Avatar, { AvatarType } from '../avatar';
|
|
6
|
-
import Badge from '../badge';
|
|
7
|
-
import { ProfileType, Size, Sentiment } from '../common';
|
|
4
|
+
import Avatar, { AvatarProps, AvatarType } from '../avatar';
|
|
5
|
+
import Badge, { BadgeProps } from '../badge';
|
|
6
|
+
import { ProfileType, ProfileTypePersonal, ProfileTypeBusiness, Size, Sentiment } from '../common';
|
|
8
7
|
import StatusIcon from '../statusIcon/StatusIcon';
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
interface OptionalBadgeProps {
|
|
10
|
+
url?: string;
|
|
11
|
+
altText?: string;
|
|
12
|
+
statusIcon?: Sentiment;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const OptionalBadge = ({ url, altText, statusIcon, children, ...rest }: OptionalBadgeProps) => {
|
|
11
17
|
if (url) {
|
|
12
18
|
return (
|
|
13
19
|
<Badge badge={<img src={url} alt={altText} />} {...rest}>
|
|
@@ -22,27 +28,20 @@ const OptionalBadge = ({ url, altText, statusIcon, children, ...rest }) => {
|
|
|
22
28
|
</Badge>
|
|
23
29
|
);
|
|
24
30
|
}
|
|
25
|
-
return children
|
|
31
|
+
return <>{children}</>;
|
|
26
32
|
};
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
url
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
OptionalBadge.defaultProps = {
|
|
42
|
-
url: undefined,
|
|
43
|
-
altText: undefined,
|
|
44
|
-
statusIcon: undefined,
|
|
45
|
-
};
|
|
34
|
+
export interface AvatarWrapperProps {
|
|
35
|
+
url?: string;
|
|
36
|
+
profileType?: ProfileTypeBusiness | ProfileTypePersonal;
|
|
37
|
+
profileId?: string;
|
|
38
|
+
badgeUrl?: string;
|
|
39
|
+
badgeAltText?: string;
|
|
40
|
+
badgeStatusIcon?: Sentiment;
|
|
41
|
+
name?: string;
|
|
42
|
+
avatarProps?: AvatarProps;
|
|
43
|
+
badgeProps?: BadgeProps;
|
|
44
|
+
}
|
|
46
45
|
|
|
47
46
|
const AvatarWrapper = ({
|
|
48
47
|
url,
|
|
@@ -54,7 +53,7 @@ const AvatarWrapper = ({
|
|
|
54
53
|
name,
|
|
55
54
|
avatarProps,
|
|
56
55
|
badgeProps,
|
|
57
|
-
}) => {
|
|
56
|
+
}: AvatarWrapperProps) => {
|
|
58
57
|
const [hasImageLoadError, setImageLoadError] = useState(false);
|
|
59
58
|
const isBusinessProfile = profileType === ProfileType.BUSINESS;
|
|
60
59
|
|
|
@@ -103,7 +102,7 @@ const AvatarWrapper = ({
|
|
|
103
102
|
);
|
|
104
103
|
};
|
|
105
104
|
|
|
106
|
-
function getInitials(name) {
|
|
105
|
+
function getInitials(name: string) {
|
|
107
106
|
const allInitials = name
|
|
108
107
|
.split(' ')
|
|
109
108
|
.map((part) => part[0])
|
|
@@ -117,34 +116,4 @@ function getInitials(name) {
|
|
|
117
116
|
return allInitials[0] + allInitials.slice(-1);
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
AvatarWrapper.defaultProps = {
|
|
121
|
-
url: undefined,
|
|
122
|
-
profileType: undefined,
|
|
123
|
-
profileId: undefined,
|
|
124
|
-
badgeUrl: undefined,
|
|
125
|
-
badgeAltText: undefined,
|
|
126
|
-
badgeStatusIcon: undefined,
|
|
127
|
-
name: undefined,
|
|
128
|
-
avatarProps: {},
|
|
129
|
-
badgeProps: {},
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
AvatarWrapper.propTypes = {
|
|
133
|
-
url: PropTypes.string,
|
|
134
|
-
profileType: PropTypes.oneOf([ProfileType.PERSONAL, ProfileType.BUSINESS]),
|
|
135
|
-
profileId: PropTypes.number,
|
|
136
|
-
badgeUrl: PropTypes.string,
|
|
137
|
-
badgeAltText: PropTypes.string,
|
|
138
|
-
badgeStatusIcon: PropTypes.oneOf([
|
|
139
|
-
Sentiment.POSITIVE,
|
|
140
|
-
Sentiment.NEGATIVE,
|
|
141
|
-
Sentiment.NEUTRAL,
|
|
142
|
-
Sentiment.WARNING,
|
|
143
|
-
Sentiment.PENDING,
|
|
144
|
-
]),
|
|
145
|
-
name: PropTypes.string,
|
|
146
|
-
avatarProps: PropTypes.shape({}),
|
|
147
|
-
badgeProps: PropTypes.shape({}),
|
|
148
|
-
};
|
|
149
|
-
|
|
150
119
|
export default AvatarWrapper;
|
package/src/avatarWrapper/__snapshots__/{AvatarWrapper.spec.js.snap → AvatarWrapper.spec.tsx.snap}
RENAMED
|
@@ -1,6 +1,79 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`FlowNavigationAvatar with a
|
|
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
|
|
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
|
>
|
package/src/badge/Badge.tsx
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
CommonProps,
|
|
13
13
|
} from '../common';
|
|
14
14
|
|
|
15
|
-
type
|
|
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
|
-
}:
|
|
28
|
+
}: BadgeProps): ReactElement => {
|
|
29
29
|
const classes: string = classNames(
|
|
30
30
|
'tw-badge',
|
|
31
31
|
{
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Inspired by:
|
|
5
|
+
*
|
|
6
|
+
* - https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event
|
|
7
|
+
* - https://legacy.reactjs.org/docs/hooks-faq.html#how-to-read-an-often-changing-value-from-usecallback
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export function useEffectEvent<A extends unknown[], R>(
|
|
11
|
+
callback: (...args: A) => R,
|
|
12
|
+
): typeof callback {
|
|
13
|
+
const ref = useRef<typeof callback>(() => {
|
|
14
|
+
throw new Error('Cannot call an event handler while rendering.');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
ref.current = callback;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return useCallback((...args) => ref.current(...args), []);
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useEffectEvent } from './useEffectEvent';
|
|
4
|
+
|
|
5
|
+
export function useResizeObserver(
|
|
6
|
+
ref: React.RefObject<Element>,
|
|
7
|
+
callback: (entry: ResizeObserverEntry) => void,
|
|
8
|
+
) {
|
|
9
|
+
const handleCallback = useEffectEvent(callback);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (ref.current != null) {
|
|
12
|
+
const resizeObserver = new ResizeObserver(([entry]) => {
|
|
13
|
+
handleCallback(entry);
|
|
14
|
+
});
|
|
15
|
+
resizeObserver.observe(ref.current, { box: 'border-box' });
|
|
16
|
+
return () => {
|
|
17
|
+
resizeObserver.disconnect();
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return () => {};
|
|
21
|
+
}, [handleCallback, ref]);
|
|
22
|
+
}
|
package/src/common/index.js
CHANGED
|
@@ -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
|
|
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,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={
|
|
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={
|
|
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={
|
|
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;
|