@transferwise/components 46.80.0 → 46.81.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/build/avatar/Avatar.js +3 -0
- package/build/avatar/Avatar.js.map +1 -1
- package/build/avatar/Avatar.mjs +3 -0
- package/build/avatar/Avatar.mjs.map +1 -1
- package/build/avatarView/AvatarView.js +175 -0
- package/build/avatarView/AvatarView.js.map +1 -0
- package/build/avatarView/AvatarView.mjs +173 -0
- package/build/avatarView/AvatarView.mjs.map +1 -0
- package/build/avatarView/NotificationDot.js +59 -0
- package/build/avatarView/NotificationDot.js.map +1 -0
- package/build/avatarView/NotificationDot.mjs +57 -0
- package/build/avatarView/NotificationDot.mjs.map +1 -0
- package/build/avatarWrapper/AvatarWrapper.js +10 -4
- package/build/avatarWrapper/AvatarWrapper.js.map +1 -1
- package/build/avatarWrapper/AvatarWrapper.mjs +10 -4
- package/build/avatarWrapper/AvatarWrapper.mjs.map +1 -1
- package/build/badge/Badge.js +16 -4
- package/build/badge/Badge.js.map +1 -1
- package/build/badge/Badge.mjs +15 -3
- package/build/badge/Badge.mjs.map +1 -1
- package/build/badge/BadgeAssets.js +60 -0
- package/build/badge/BadgeAssets.js.map +1 -0
- package/build/badge/BadgeAssets.mjs +58 -0
- package/build/badge/BadgeAssets.mjs.map +1 -0
- package/build/common/circle/Circle.js +19 -1
- package/build/common/circle/Circle.js.map +1 -1
- package/build/common/circle/Circle.mjs +19 -1
- package/build/common/circle/Circle.mjs.map +1 -1
- package/build/i18n/zh-HK.json +5 -0
- package/build/i18n/zh-HK.json.js +5 -0
- package/build/i18n/zh-HK.json.js.map +1 -1
- package/build/i18n/zh-HK.json.mjs +5 -0
- package/build/i18n/zh-HK.json.mjs.map +1 -1
- package/build/index.js +18 -13
- package/build/index.js.map +1 -1
- package/build/index.mjs +10 -7
- package/build/index.mjs.map +1 -1
- package/build/main.css +74 -5
- package/build/styles/avatarView/AvatarView.css +36 -0
- package/build/styles/avatarView/NotificationDot.css +20 -0
- package/build/styles/badge/Badge.css +6 -5
- package/build/styles/common/circle/Circle.css +32 -0
- package/build/styles/main.css +74 -5
- package/build/types/avatar/Avatar.d.ts +3 -0
- package/build/types/avatar/Avatar.d.ts.map +1 -1
- package/build/types/avatarView/AvatarView.d.ts +26 -0
- package/build/types/avatarView/AvatarView.d.ts.map +1 -0
- package/build/types/avatarView/NotificationDot.d.ts +8 -0
- package/build/types/avatarView/NotificationDot.d.ts.map +1 -0
- package/build/types/avatarView/index.d.ts +3 -0
- package/build/types/avatarView/index.d.ts.map +1 -0
- package/build/types/avatarWrapper/AvatarWrapper.d.ts +3 -0
- package/build/types/avatarWrapper/AvatarWrapper.d.ts.map +1 -1
- package/build/types/badge/Badge.d.ts +9 -4
- package/build/types/badge/Badge.d.ts.map +1 -1
- package/build/types/badge/BadgeAssets.d.ts +14 -0
- package/build/types/badge/BadgeAssets.d.ts.map +1 -0
- package/build/types/badge/index.d.ts +2 -0
- package/build/types/badge/index.d.ts.map +1 -1
- package/build/types/common/circle/Circle.d.ts +2 -0
- package/build/types/common/circle/Circle.d.ts.map +1 -1
- package/build/types/index.d.ts +3 -1
- package/build/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/avatar/Avatar.tsx +3 -0
- package/src/avatarView/AvatarView.css +36 -0
- package/src/avatarView/AvatarView.less +27 -0
- package/src/avatarView/AvatarView.story.tsx +467 -0
- package/src/avatarView/AvatarView.tsx +171 -0
- package/src/avatarView/NotificationDot.css +20 -0
- package/src/avatarView/NotificationDot.less +24 -0
- package/src/avatarView/NotificationDot.tsx +35 -0
- package/src/avatarView/index.ts +2 -0
- package/src/avatarWrapper/AvatarWrapper.story.tsx +19 -0
- package/src/avatarWrapper/AvatarWrapper.tsx +3 -0
- package/src/badge/Badge.css +6 -5
- package/src/badge/Badge.less +4 -3
- package/src/badge/Badge.tsx +20 -6
- package/src/badge/BadgeAssets.tsx +61 -0
- package/src/badge/index.ts +3 -0
- package/src/circularButton/CircularButton.spec.tsx +0 -36
- package/src/common/circle/Circle.css +32 -0
- package/src/common/circle/Circle.less +35 -0
- package/src/common/circle/Circle.tsx +24 -1
- package/src/flowNavigation/FlowNavigation.story.tsx +19 -52
- package/src/i18n/zh-HK.json +5 -0
- package/src/index.ts +3 -0
- package/src/listItem/ListItem.story.tsx +5 -47
- package/src/main.css +74 -5
- package/src/main.less +1 -0
- package/src/overlayHeader/OverlayHeader.story.tsx +6 -14
- package/src/circularButton/__snapshots__/CircularButton.spec.tsx.snap +0 -381
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { HTMLAttributes } from 'react';
|
|
2
|
+
import { Props as AvatarViewProps } from './AvatarView';
|
|
3
|
+
|
|
4
|
+
type Props = Pick<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
5
|
+
avatarSize?: AvatarViewProps['size'];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Depending on avatar size, notifcation dot size and offset are different
|
|
10
|
+
*/
|
|
11
|
+
const MAP_STYLE_CONFIG = {
|
|
12
|
+
16: { size: 6, offset: 1 },
|
|
13
|
+
24: { size: 8, offset: 2 },
|
|
14
|
+
32: { size: 10, offset: 2 },
|
|
15
|
+
40: { size: 10, offset: 2 },
|
|
16
|
+
48: { size: 14, offset: 2 },
|
|
17
|
+
56: { size: 16, offset: 3 },
|
|
18
|
+
72: { size: 20, offset: 3 },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default function NotificationDot({ children, avatarSize = 48 }: Props) {
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
className="np-notification-dot"
|
|
25
|
+
style={{
|
|
26
|
+
// @ts-expect-error CSS custom props allowed
|
|
27
|
+
'--np-notification-dot-size': `${MAP_STYLE_CONFIG[avatarSize].size}px`,
|
|
28
|
+
'--np-notification-dot-offset': `${MAP_STYLE_CONFIG[avatarSize].offset}px`,
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<div className="np-notification-dot-badge" />
|
|
32
|
+
<div className="np-notification-dot-mask">{children}</div>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -99,3 +99,22 @@ export const All: Story = {
|
|
|
99
99
|
);
|
|
100
100
|
},
|
|
101
101
|
};
|
|
102
|
+
|
|
103
|
+
export const ProfileBrokenImageFallback: Story = {
|
|
104
|
+
parameters: {
|
|
105
|
+
chromatic: {
|
|
106
|
+
delay: 5000,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
render: () => {
|
|
110
|
+
const assetUrl = 'https://test.com/img-wrong-url.test';
|
|
111
|
+
return (
|
|
112
|
+
<>
|
|
113
|
+
<AvatarWrapper />
|
|
114
|
+
<AvatarWrapper url={assetUrl} />
|
|
115
|
+
<AvatarWrapper url={assetUrl} profileType={ProfileType.BUSINESS} />
|
|
116
|
+
<AvatarWrapper url={assetUrl} profileType={ProfileType.BUSINESS} name="Test Name" />
|
|
117
|
+
</>
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
};
|
package/src/badge/Badge.css
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
--badge-mask: 2px;
|
|
6
6
|
--badge-mask-offset: calc(var(--badge-size) / 2);
|
|
7
7
|
--badge-border-color: rgba(255, 255, 255, 0.08);
|
|
8
|
+
--badge-content-position: 0px;
|
|
8
9
|
}
|
|
9
10
|
.tw-badge.tw-badge-lg {
|
|
10
11
|
--badge-size: 24px;
|
|
@@ -14,8 +15,8 @@
|
|
|
14
15
|
--badge-border-color: rgba(0, 0, 0, 0.08);
|
|
15
16
|
}
|
|
16
17
|
.tw-badge > .tw-badge__children {
|
|
17
|
-
-webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
18
|
-
mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
18
|
+
-webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
19
|
+
mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
19
20
|
}
|
|
20
21
|
[dir="rtl"] .tw-badge > .tw-badge__children {
|
|
21
22
|
-webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
|
|
@@ -25,8 +26,8 @@
|
|
|
25
26
|
position: absolute;
|
|
26
27
|
width: var(--badge-size);
|
|
27
28
|
height: var(--badge-size);
|
|
28
|
-
bottom:
|
|
29
|
-
right:
|
|
29
|
+
bottom: var(--badge-content-position);
|
|
30
|
+
right: var(--badge-content-position);
|
|
30
31
|
box-sizing: border-box;
|
|
31
32
|
border-radius: 50%;
|
|
32
33
|
text-align: center;
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
user-select: none;
|
|
40
41
|
}
|
|
41
42
|
[dir="rtl"] .tw-badge > .tw-badge__content {
|
|
42
|
-
left:
|
|
43
|
+
left: var(--badge-content-position);
|
|
43
44
|
right: auto;
|
|
44
45
|
right: initial;
|
|
45
46
|
}
|
package/src/badge/Badge.less
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
--badge-mask: @badge-mask-sm;
|
|
18
18
|
--badge-mask-offset: calc(var(--badge-size) / 2);
|
|
19
19
|
--badge-border-color: @badge-border-light;
|
|
20
|
+
--badge-content-position: 0px;
|
|
20
21
|
|
|
21
22
|
&.tw-badge-lg {
|
|
22
23
|
--badge-size: @badge-size-lg;
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
|
|
34
35
|
mask-image:
|
|
35
36
|
radial-gradient(
|
|
36
|
-
circle at top calc(100% - var(--badge-mask-offset)) left calc(100% - var(--badge-mask-offset)),
|
|
37
|
+
circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left calc(100% - var(--badge-mask-offset) - var(--badge-content-position)),
|
|
37
38
|
transparent 0,
|
|
38
39
|
transparent calc(var(--badge-size) / 2 + var(--badge-mask)),
|
|
39
40
|
black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px)
|
|
@@ -54,8 +55,8 @@
|
|
|
54
55
|
position: absolute;
|
|
55
56
|
width: var(--badge-size);
|
|
56
57
|
height: var(--badge-size);
|
|
57
|
-
bottom:
|
|
58
|
-
.right(
|
|
58
|
+
bottom: var(--badge-content-position);
|
|
59
|
+
.right(var(--badge-content-position));
|
|
59
60
|
|
|
60
61
|
box-sizing: border-box;
|
|
61
62
|
border-radius: 50%;
|
package/src/badge/Badge.tsx
CHANGED
|
@@ -11,11 +11,12 @@ import {
|
|
|
11
11
|
ThemeLight,
|
|
12
12
|
CommonProps,
|
|
13
13
|
} from '../common';
|
|
14
|
+
import { BadgeAssetsProps } from '.';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
* @deprecated Use `
|
|
17
|
+
* @deprecated Use `16` or `24` instead.
|
|
17
18
|
*/
|
|
18
|
-
type DeprecatedSizes = SizeMedium;
|
|
19
|
+
type DeprecatedSizes = SizeSmall | SizeMedium | SizeLarge;
|
|
19
20
|
|
|
20
21
|
export type BadgeProps = {
|
|
21
22
|
badge: ReactNode;
|
|
@@ -23,11 +24,24 @@ export type BadgeProps = {
|
|
|
23
24
|
/**
|
|
24
25
|
* `md` is deprecated, it will fallback to `sm` instead.
|
|
25
26
|
*/
|
|
26
|
-
size?:
|
|
27
|
+
size?: DeprecatedSizes | BadgeAssetsProps['size'];
|
|
27
28
|
border?: ThemeDark | ThemeLight;
|
|
28
29
|
'aria-label'?: string;
|
|
30
|
+
style?: React.CSSProperties;
|
|
29
31
|
} & CommonProps;
|
|
30
32
|
|
|
33
|
+
const mapLegacySize = {
|
|
34
|
+
16: Size.SMALL,
|
|
35
|
+
24: Size.LARGE,
|
|
36
|
+
// medium is no longer exists, so we map it to small
|
|
37
|
+
[String(Size.MEDIUM)]: Size.SMALL,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Note: Badge component is not deprecated, we want stop it's direct usage on consumer side.
|
|
41
|
+
// Deprecation notice will hint consumers to migrate. Eventually the component will become internal.
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Use `<AvatarView badge={..} />` instead.
|
|
44
|
+
*/
|
|
31
45
|
const Badge = ({
|
|
32
46
|
badge,
|
|
33
47
|
className = undefined,
|
|
@@ -35,9 +49,9 @@ const Badge = ({
|
|
|
35
49
|
border = Theme.LIGHT,
|
|
36
50
|
'aria-label': ariaLabel,
|
|
37
51
|
children,
|
|
52
|
+
style,
|
|
38
53
|
}: BadgeProps) => {
|
|
39
|
-
|
|
40
|
-
const size = sizeProp === Size.MEDIUM ? Size.SMALL : sizeProp;
|
|
54
|
+
const size = mapLegacySize[sizeProp] ?? sizeProp;
|
|
41
55
|
const classes: string = clsx(
|
|
42
56
|
'tw-badge',
|
|
43
57
|
{
|
|
@@ -48,7 +62,7 @@ const Badge = ({
|
|
|
48
62
|
);
|
|
49
63
|
|
|
50
64
|
return (
|
|
51
|
-
<div aria-label={ariaLabel} className={classes}>
|
|
65
|
+
<div aria-label={ariaLabel} className={classes} style={style}>
|
|
52
66
|
<div className="tw-badge__children">{children}</div>
|
|
53
67
|
<div className="tw-badge__content">{badge}</div>
|
|
54
68
|
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import StatusIcon, { StatusIconProps } from '../statusIcon';
|
|
2
|
+
import { Flag } from '@wise/art';
|
|
3
|
+
import Circle from '../common/circle';
|
|
4
|
+
import Image from '../image';
|
|
5
|
+
import { Plus } from '@transferwise/icons';
|
|
6
|
+
|
|
7
|
+
export type Props = {
|
|
8
|
+
status?: StatusIconProps['sentiment'];
|
|
9
|
+
flagCode?: string;
|
|
10
|
+
imgSrc?: string;
|
|
11
|
+
icon?: React.ReactNode;
|
|
12
|
+
type?: 'action' | 'reference';
|
|
13
|
+
size?: 16 | 24;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Common pre-built badge variants.
|
|
18
|
+
*/
|
|
19
|
+
export default function BadgeAssets({
|
|
20
|
+
status,
|
|
21
|
+
flagCode,
|
|
22
|
+
imgSrc,
|
|
23
|
+
icon = null,
|
|
24
|
+
type = 'action',
|
|
25
|
+
size,
|
|
26
|
+
}: Props) {
|
|
27
|
+
if (status) {
|
|
28
|
+
return <StatusIcon sentiment={status} size={size} />;
|
|
29
|
+
}
|
|
30
|
+
if (flagCode) {
|
|
31
|
+
return (
|
|
32
|
+
<Circle size={size} fixedSize enableBorder>
|
|
33
|
+
<Flag code={flagCode} intrinsicSize={size} />
|
|
34
|
+
</Circle>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (imgSrc) {
|
|
38
|
+
return (
|
|
39
|
+
<Circle size={size} fixedSize enableBorder>
|
|
40
|
+
<Image src={imgSrc} alt="" />
|
|
41
|
+
</Circle>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (['action', 'reference'].includes(type)) {
|
|
45
|
+
return (
|
|
46
|
+
<Circle
|
|
47
|
+
size={size}
|
|
48
|
+
fixedSize
|
|
49
|
+
style={{
|
|
50
|
+
backgroundColor:
|
|
51
|
+
type === 'action'
|
|
52
|
+
? 'var(--color-interactive-accent)'
|
|
53
|
+
: 'var(--color-background-neutral)',
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{icon ?? <Plus />}
|
|
57
|
+
</Circle>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
package/src/badge/index.ts
CHANGED
|
@@ -34,10 +34,6 @@ describe('CircularButton', () => {
|
|
|
34
34
|
it('is not disabled', () => {
|
|
35
35
|
expect(screen.getByRole('button')).toBeEnabled();
|
|
36
36
|
});
|
|
37
|
-
|
|
38
|
-
it('renders a button of type accent and priority primary', () => {
|
|
39
|
-
expect(render(<CircularButton {...props} />).container).toMatchSnapshot();
|
|
40
|
-
});
|
|
41
37
|
});
|
|
42
38
|
|
|
43
39
|
describe('button attributes', () => {
|
|
@@ -69,36 +65,4 @@ describe('CircularButton', () => {
|
|
|
69
65
|
expect(onClick).toHaveBeenCalledTimes(0);
|
|
70
66
|
});
|
|
71
67
|
});
|
|
72
|
-
|
|
73
|
-
describe('types', () => {
|
|
74
|
-
it('renders accent buttons', () => {
|
|
75
|
-
expect(render(<CircularButton {...props} type={ACCENT} />).container).toMatchSnapshot();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('renders positive buttons', () => {
|
|
79
|
-
expect(render(<CircularButton {...props} type={POSITIVE} />).container).toMatchSnapshot();
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
it('renders negative buttons', () => {
|
|
83
|
-
expect(render(<CircularButton {...props} type={NEGATIVE} />).container).toMatchSnapshot();
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe('priorities', () => {
|
|
88
|
-
it('renders primary buttons', () => {
|
|
89
|
-
[ACCENT, POSITIVE, NEGATIVE].forEach((type) =>
|
|
90
|
-
expect(
|
|
91
|
-
render(<CircularButton {...props} priority={PRIMARY} type={type} />).container,
|
|
92
|
-
).toMatchSnapshot(),
|
|
93
|
-
);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('renders secondary buttons', () => {
|
|
97
|
-
[ACCENT, POSITIVE, NEGATIVE].forEach((type) =>
|
|
98
|
-
expect(
|
|
99
|
-
render(<CircularButton {...props} priority={SECONDARY} type={type} />).container,
|
|
100
|
-
).toMatchSnapshot(),
|
|
101
|
-
);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
68
|
});
|
|
@@ -4,8 +4,40 @@
|
|
|
4
4
|
width: var(--circle-size);
|
|
5
5
|
height: var(--circle-size);
|
|
6
6
|
flex-shrink: 0;
|
|
7
|
+
--circle-border-color: var(--color-border-neutral);
|
|
8
|
+
--circle-border-width: 1px;
|
|
9
|
+
font-size: var(--circle-font-size);
|
|
10
|
+
font-weight: 600;
|
|
11
|
+
font-weight: var(--font-weight-semi-bold);
|
|
12
|
+
line-height: 1;
|
|
13
|
+
}
|
|
14
|
+
.np-circle .np-display {
|
|
15
|
+
font-size: var(--circle-font-size);
|
|
7
16
|
}
|
|
8
17
|
.np-circle .tw-icon > svg {
|
|
9
18
|
height: var(--circle-icon-size);
|
|
10
19
|
width: var(--circle-icon-size);
|
|
11
20
|
}
|
|
21
|
+
.np-circle img,
|
|
22
|
+
.np-circle .wds-flag {
|
|
23
|
+
border-radius: 9999px;
|
|
24
|
+
border-radius: var(--radius-full);
|
|
25
|
+
width: 100%;
|
|
26
|
+
height: 100%;
|
|
27
|
+
-o-object-fit: cover;
|
|
28
|
+
object-fit: cover;
|
|
29
|
+
}
|
|
30
|
+
.np-circle-border {
|
|
31
|
+
position: relative;
|
|
32
|
+
}
|
|
33
|
+
.np-circle-border::after {
|
|
34
|
+
content: "";
|
|
35
|
+
position: absolute;
|
|
36
|
+
top: 0;
|
|
37
|
+
left: 0;
|
|
38
|
+
width: 100%;
|
|
39
|
+
height: 100%;
|
|
40
|
+
border-radius: 9999px;
|
|
41
|
+
border-radius: var(--radius-full);
|
|
42
|
+
box-shadow: inset 0 0 0 var(--circle-border-width) var(--circle-border-color);
|
|
43
|
+
}
|
|
@@ -4,9 +4,44 @@
|
|
|
4
4
|
height: var(--circle-size);
|
|
5
5
|
flex-shrink: 0;
|
|
6
6
|
|
|
7
|
+
--circle-border-color: var(--color-border-neutral);
|
|
8
|
+
--circle-border-width: 1px;
|
|
9
|
+
|
|
10
|
+
// circle like components have custom typography styles for for default (Inter) font
|
|
11
|
+
font-size: var(--circle-font-size);
|
|
12
|
+
font-weight: var(--font-weight-semi-bold);
|
|
13
|
+
line-height: 1;
|
|
14
|
+
|
|
15
|
+
// circle like components have custom typography styles for Wise Sand font
|
|
16
|
+
.np-display {
|
|
17
|
+
font-size: var(--circle-font-size);
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
// circle like components has custom icon sizes
|
|
8
21
|
.tw-icon > svg {
|
|
9
22
|
height: var(--circle-icon-size);
|
|
10
23
|
width: var(--circle-icon-size);
|
|
11
24
|
}
|
|
25
|
+
|
|
26
|
+
img,
|
|
27
|
+
.wds-flag {
|
|
28
|
+
border-radius: var(--radius-full);
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 100%;
|
|
31
|
+
object-fit: cover;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&-border {
|
|
35
|
+
position: relative;
|
|
36
|
+
&::after {
|
|
37
|
+
content: "";
|
|
38
|
+
position: absolute;
|
|
39
|
+
top: 0;
|
|
40
|
+
left: 0;
|
|
41
|
+
width: 100%;
|
|
42
|
+
height: 100%;
|
|
43
|
+
border-radius: var(--radius-full);
|
|
44
|
+
box-shadow: inset 0 0 0 var(--circle-border-width) var(--circle-border-color);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
12
47
|
}
|
|
@@ -19,6 +19,7 @@ export type Props = {
|
|
|
19
19
|
* as those can be dynamic a at certain viewport sizes
|
|
20
20
|
*/
|
|
21
21
|
fixedSize?: boolean;
|
|
22
|
+
enableBorder?: boolean;
|
|
22
23
|
} & HTMLAttributes<HTMLDivElement>;
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -34,12 +35,28 @@ const MAP_ICON_SIZE = {
|
|
|
34
35
|
72: 36,
|
|
35
36
|
};
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* circle like components have custom typography styles for for default (Inter) font
|
|
40
|
+
*
|
|
41
|
+
* circle size : font size (px)
|
|
42
|
+
*/
|
|
43
|
+
const MAP_FONT_SIZE = {
|
|
44
|
+
16: 8,
|
|
45
|
+
24: 12,
|
|
46
|
+
32: 14,
|
|
47
|
+
40: 18,
|
|
48
|
+
48: 22,
|
|
49
|
+
56: 26,
|
|
50
|
+
72: 30,
|
|
51
|
+
};
|
|
52
|
+
|
|
37
53
|
const Circle = forwardRef(function Circle(
|
|
38
54
|
{
|
|
39
55
|
as: Element = 'div',
|
|
40
56
|
children,
|
|
41
57
|
size = 48,
|
|
42
58
|
fixedSize = false,
|
|
59
|
+
enableBorder = false,
|
|
43
60
|
className,
|
|
44
61
|
style,
|
|
45
62
|
...props
|
|
@@ -57,9 +74,15 @@ const Circle = forwardRef(function Circle(
|
|
|
57
74
|
isTinyViewport && !fixedSize
|
|
58
75
|
? `${MAP_ICON_SIZE[size] / 2}px`
|
|
59
76
|
: `${MAP_ICON_SIZE[size]}px`,
|
|
77
|
+
'--circle-font-size': `${MAP_FONT_SIZE[size]}px`,
|
|
60
78
|
...style,
|
|
61
79
|
}}
|
|
62
|
-
className={clsx(
|
|
80
|
+
className={clsx(
|
|
81
|
+
'np-circle',
|
|
82
|
+
{ 'np-circle-border': enableBorder },
|
|
83
|
+
'd-flex align-items-center justify-content-center',
|
|
84
|
+
className,
|
|
85
|
+
)}
|
|
63
86
|
>
|
|
64
87
|
{children}
|
|
65
88
|
</Element>
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { boolean, select, text } from '@storybook/addon-knobs';
|
|
2
|
-
import { Person as ProfileIcon, Briefcase as BriefcaseIcon } from '@transferwise/icons';
|
|
3
2
|
import { useState } from 'react';
|
|
4
3
|
|
|
5
|
-
import
|
|
6
|
-
import AvatarWrapper from '../avatarWrapper';
|
|
4
|
+
import AvatarView from '../avatarView';
|
|
7
5
|
import Body from '../body';
|
|
8
6
|
import Button from '../button';
|
|
9
|
-
import { ProfileType,
|
|
7
|
+
import { ProfileType, Typography } from '../common';
|
|
10
8
|
import Logo from '../logo';
|
|
11
9
|
import OverlayHeader from '../overlayHeader';
|
|
12
10
|
|
|
@@ -19,43 +17,24 @@ export default {
|
|
|
19
17
|
component: FlowNavigation,
|
|
20
18
|
title: 'Navigation/FlowNavigation',
|
|
21
19
|
};
|
|
22
|
-
const avatarProfiles = {
|
|
23
|
-
'': null,
|
|
24
|
-
Business: <BriefcaseIcon />,
|
|
25
|
-
Profile: <ProfileIcon />,
|
|
26
|
-
};
|
|
27
|
-
type ProfileTypeKeys = keyof typeof ProfileType;
|
|
28
|
-
|
|
29
|
-
const getAvatarProfile = (showAvatar: string) =>
|
|
30
|
-
showAvatar in avatarProfiles ? avatarProfiles[showAvatar as keyof typeof avatarProfiles] : null;
|
|
31
20
|
|
|
32
21
|
export const Variants = () => {
|
|
33
22
|
const [activeStep, setActiveStep] = useState(2);
|
|
34
23
|
const [closed, setClosed] = useState(false);
|
|
35
|
-
const
|
|
24
|
+
const profileType = select(
|
|
25
|
+
'avatar',
|
|
26
|
+
[ProfileType.PERSONAL, ProfileType.BUSINESS],
|
|
27
|
+
ProfileType.PERSONAL,
|
|
28
|
+
);
|
|
36
29
|
const showCloseButton = boolean('show closeButton', true);
|
|
37
30
|
const showMobileBackButton = boolean('show mobile backButton', true);
|
|
38
31
|
const done = boolean('done', false);
|
|
39
|
-
const
|
|
40
|
-
'profileType',
|
|
41
|
-
Object.keys(ProfileType) as ProfileTypeKeys[],
|
|
42
|
-
undefined,
|
|
43
|
-
);
|
|
44
|
-
const avatarURL = text(
|
|
45
|
-
'avatarURL',
|
|
46
|
-
'https://wise.com/web-art/assets/illustrations/heart-small@2x.webp',
|
|
47
|
-
);
|
|
32
|
+
const avatarURL = text('avatarURL', '../tapestry-01.png');
|
|
48
33
|
|
|
49
34
|
return !closed ? (
|
|
50
35
|
<>
|
|
51
36
|
<FlowNavigation
|
|
52
|
-
avatar={
|
|
53
|
-
!showAvatar ? null : (
|
|
54
|
-
<Avatar type={AvatarType.ICON} size={Size.MEDIUM}>
|
|
55
|
-
{getAvatarProfile(showAvatar)}
|
|
56
|
-
</Avatar>
|
|
57
|
-
)
|
|
58
|
-
}
|
|
37
|
+
avatar={!profileType ? null : <AvatarView profileType={profileType} />}
|
|
59
38
|
logo={<Logo />}
|
|
60
39
|
activeStep={activeStep}
|
|
61
40
|
done={done}
|
|
@@ -132,7 +111,7 @@ export const Variants = () => {
|
|
|
132
111
|
|
|
133
112
|
<FlowNavigation
|
|
134
113
|
done={done}
|
|
135
|
-
avatar={<
|
|
114
|
+
avatar={<AvatarView imgSrc={avatarURL} profileType={profileType} />}
|
|
136
115
|
activeStep={activeStep}
|
|
137
116
|
steps={[
|
|
138
117
|
{
|
|
@@ -178,7 +157,7 @@ export const Variants = () => {
|
|
|
178
157
|
{/* Instance of always `done` FlowNav for visual testing */}
|
|
179
158
|
<FlowNavigation
|
|
180
159
|
done
|
|
181
|
-
avatar={<
|
|
160
|
+
avatar={<AvatarView imgSrc={avatarURL} profileType={profileType} />}
|
|
182
161
|
activeStep={activeStep}
|
|
183
162
|
steps={[
|
|
184
163
|
{ label: 'Recipient', hoverLabel: 'Daniele Tomboro', onClick: () => setActiveStep(0) },
|
|
@@ -240,11 +219,7 @@ export const SendFlow = () => {
|
|
|
240
219
|
return (
|
|
241
220
|
<>
|
|
242
221
|
<FlowNavigation
|
|
243
|
-
avatar={
|
|
244
|
-
<Avatar type={AvatarType.ICON} size={Size.MEDIUM}>
|
|
245
|
-
<ProfileIcon />
|
|
246
|
-
</Avatar>
|
|
247
|
-
}
|
|
222
|
+
avatar={<AvatarView />}
|
|
248
223
|
logo={<Logo />}
|
|
249
224
|
activeStep={activeStep}
|
|
250
225
|
steps={steps}
|
|
@@ -272,7 +247,11 @@ export const SendFlow = () => {
|
|
|
272
247
|
export const WithOverlayHeaderComparison = () => {
|
|
273
248
|
const [activeStep, setActiveStep] = useState(4);
|
|
274
249
|
const [closed, setClosed] = useState(false);
|
|
275
|
-
const
|
|
250
|
+
const profileType = select(
|
|
251
|
+
'avatar',
|
|
252
|
+
[ProfileType.PERSONAL, ProfileType.BUSINESS],
|
|
253
|
+
ProfileType.PERSONAL,
|
|
254
|
+
);
|
|
276
255
|
const showCloseButton = boolean('show closeButton', true);
|
|
277
256
|
const showMobileBackButton = boolean('show mobile backButton', true);
|
|
278
257
|
const done = boolean('done', false);
|
|
@@ -281,13 +260,7 @@ export const WithOverlayHeaderComparison = () => {
|
|
|
281
260
|
<>
|
|
282
261
|
<div style={{ border: '1px solid #e8e8e8' }}>
|
|
283
262
|
<FlowNavigation
|
|
284
|
-
avatar={
|
|
285
|
-
showAvatar ? (
|
|
286
|
-
<Avatar type={AvatarType.ICON} size={Size.MEDIUM}>
|
|
287
|
-
{getAvatarProfile(showAvatar)}
|
|
288
|
-
</Avatar>
|
|
289
|
-
) : null
|
|
290
|
-
}
|
|
263
|
+
avatar={profileType ? <AvatarView profileType={profileType} /> : null}
|
|
291
264
|
activeStep={activeStep}
|
|
292
265
|
done={done}
|
|
293
266
|
steps={[]}
|
|
@@ -301,13 +274,7 @@ export const WithOverlayHeaderComparison = () => {
|
|
|
301
274
|
</div>
|
|
302
275
|
<div style={{ border: '1px solid #e8e8e8' }}>
|
|
303
276
|
<OverlayHeader
|
|
304
|
-
avatar={
|
|
305
|
-
showAvatar ? (
|
|
306
|
-
<Avatar type={AvatarType.ICON} size={Size.MEDIUM}>
|
|
307
|
-
{getAvatarProfile(showAvatar)}
|
|
308
|
-
</Avatar>
|
|
309
|
-
) : null
|
|
310
|
-
}
|
|
277
|
+
avatar={profileType ? <AvatarView profileType={profileType} /> : null}
|
|
311
278
|
onClose={showCloseButton ? () => setClosed(true) : undefined}
|
|
312
279
|
/>
|
|
313
280
|
</div>
|
package/src/i18n/zh-HK.json
CHANGED
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
"neptune.SelectInput.noResultsFound": "找不到任何結果",
|
|
28
28
|
"neptune.SelectOption.action.label": "選擇",
|
|
29
29
|
"neptune.SelectOption.selected.action.label": "更改已選選項",
|
|
30
|
+
"neptune.StatusIcon.iconLabel.error": "錯誤:",
|
|
31
|
+
"neptune.StatusIcon.iconLabel.information": "資訊:",
|
|
32
|
+
"neptune.StatusIcon.iconLabel.pending": "處理中:",
|
|
33
|
+
"neptune.StatusIcon.iconLabel.success": "成功:",
|
|
34
|
+
"neptune.StatusIcon.iconLabel.warning": "警告:",
|
|
30
35
|
"neptune.Summary.statusDone": "已完成事項",
|
|
31
36
|
"neptune.Summary.statusNotDone": "未完成事項",
|
|
32
37
|
"neptune.Summary.statusPending": "待處理事項",
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type { ActionOptionProps } from './actionOption';
|
|
|
8
8
|
export type { SelectOptionProps, SelectOptionValue, SelectOptiopsSection } from './selectOption';
|
|
9
9
|
export type { AlertAction, AlertProps, AlertType } from './alert';
|
|
10
10
|
export type { AvatarProps } from './avatar';
|
|
11
|
+
export type { AvatarViewProps } from './avatarView';
|
|
11
12
|
export type { BadgeProps } from './badge';
|
|
12
13
|
export type { CardProps } from './card';
|
|
13
14
|
export type { CarouselProps } from './carousel';
|
|
@@ -103,6 +104,7 @@ export { default as ActionOption } from './actionOption';
|
|
|
103
104
|
export { default as SelectOption } from './selectOption';
|
|
104
105
|
export { default as Alert } from './alert';
|
|
105
106
|
export { default as Avatar } from './avatar';
|
|
107
|
+
export { default as AvatarView } from './avatarView';
|
|
106
108
|
export { default as AvatarWrapper } from './avatarWrapper';
|
|
107
109
|
export { default as Badge } from './badge';
|
|
108
110
|
export { default as Body } from './body';
|
|
@@ -244,6 +246,7 @@ export {
|
|
|
244
246
|
getLangFromLocale,
|
|
245
247
|
isBrowser,
|
|
246
248
|
isServerSide,
|
|
249
|
+
getBrandColorFromSeed,
|
|
247
250
|
} from './common';
|
|
248
251
|
|
|
249
252
|
/**
|