@sproutsocial/seeds-react-profile 0.1.3 → 0.2.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +44 -0
- package/dist/esm/index.js +289 -28
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +81 -15
- package/dist/index.d.ts +81 -15
- package/dist/index.js +284 -27
- package/dist/index.js.map +1 -1
- package/package.json +11 -8
- package/src/InlineProfile.stories.tsx +74 -15
- package/src/InlineProfile.tsx +107 -30
- package/src/ProfileCard.stories.tsx +264 -0
- package/src/ProfileCard.tsx +186 -0
- package/src/ProfileToken.stories.tsx +90 -32
- package/src/ProfileToken.tsx +19 -7
- package/src/VerifiedProfileIcon.tsx +64 -0
- package/src/__tests__/InlineProfile.test.tsx +19 -20
- package/src/__tests__/ProfileCard.test.tsx +182 -0
- package/src/__tests__/ProfileToken.test.tsx +2 -1
- package/src/index.ts +1 -0
- package/src/types.ts +95 -9
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { PartnerLogo } from "@sproutsocial/seeds-react-partner-logo";
|
|
3
|
+
import { Avatar } from "@sproutsocial/seeds-react-avatar";
|
|
4
|
+
import {
|
|
5
|
+
Card,
|
|
6
|
+
CardHeader,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardFooter,
|
|
9
|
+
} from "@sproutsocial/seeds-react-card";
|
|
10
|
+
import { Icon } from "@sproutsocial/seeds-react-icon";
|
|
11
|
+
import { Link } from "@sproutsocial/seeds-react-link";
|
|
12
|
+
import { Text } from "@sproutsocial/seeds-react-text";
|
|
13
|
+
import type { TypeProfileCardProps } from "./types";
|
|
14
|
+
import { VerifiedProfileIcon } from "./VerifiedProfileIcon";
|
|
15
|
+
|
|
16
|
+
// Styled Components
|
|
17
|
+
const StyledProfileCard = styled(Card)`
|
|
18
|
+
max-width: 300px;
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const StyledCardHeader = styled(CardHeader)<{
|
|
22
|
+
$bannerColor?: string;
|
|
23
|
+
$partnerName?: string;
|
|
24
|
+
}>`
|
|
25
|
+
position: relative;
|
|
26
|
+
height: 80px;
|
|
27
|
+
padding: ${(props) => props.theme.space[400]}
|
|
28
|
+
${(props) => props.theme.space[400]} 0 ${(props) => props.theme.space[400]};
|
|
29
|
+
margin-bottom: 0;
|
|
30
|
+
color: ${(props) => props.theme.colors.text.inverse};
|
|
31
|
+
border-radius: ${(props) => props.theme.radii[500]}
|
|
32
|
+
${(props) => props.theme.radii[500]} 0 0;
|
|
33
|
+
${(props) => {
|
|
34
|
+
// Use explicit bannerColor if provided
|
|
35
|
+
if (props.$bannerColor) {
|
|
36
|
+
return `background: ${props.$bannerColor};`;
|
|
37
|
+
}
|
|
38
|
+
// Fall back to theme.colors.network[partnerName] if partnerName is available
|
|
39
|
+
if (props.$partnerName && props.theme.colors.network) {
|
|
40
|
+
const networkColor =
|
|
41
|
+
props.theme.colors.network[
|
|
42
|
+
props.$partnerName as keyof typeof props.theme.colors.network
|
|
43
|
+
];
|
|
44
|
+
return networkColor ? `background: ${networkColor};` : "";
|
|
45
|
+
}
|
|
46
|
+
return "";
|
|
47
|
+
}}
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
const BannerContent = styled.div`
|
|
51
|
+
display: flex;
|
|
52
|
+
justify-content: flex-end;
|
|
53
|
+
width: 100%;
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const ActionsSection = styled.div`
|
|
57
|
+
display: flex;
|
|
58
|
+
justify-content: flex-end;
|
|
59
|
+
gap: ${(props) => props.theme.space[100]};
|
|
60
|
+
padding: 0 ${(props) => props.theme.space[300]};
|
|
61
|
+
|
|
62
|
+
&:empty {
|
|
63
|
+
min-height: 44px;
|
|
64
|
+
}
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const StyledCardContent = styled(CardContent)`
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
gap: ${(props) => props.theme.space[300]};
|
|
71
|
+
padding: 0 ${(props) => props.theme.space[400]}
|
|
72
|
+
${(props) => props.theme.space[400]};
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
const ProfileNameSection = styled.div`
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: ${(props) => props.theme.space[200]};
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
const InfoRow = styled.div`
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
gap: ${(props) => props.theme.space[300]};
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A flexible profile component that can display user profiles in various formats
|
|
89
|
+
* across different social networks with consistent styling and behavior.
|
|
90
|
+
*/
|
|
91
|
+
export const ProfileCard = ({
|
|
92
|
+
partnerName,
|
|
93
|
+
name,
|
|
94
|
+
secondaryName,
|
|
95
|
+
avatarUrl,
|
|
96
|
+
subtext,
|
|
97
|
+
description,
|
|
98
|
+
verificationType,
|
|
99
|
+
profileUrl,
|
|
100
|
+
location,
|
|
101
|
+
bannerColor,
|
|
102
|
+
bannerContent,
|
|
103
|
+
metadata,
|
|
104
|
+
profileActions,
|
|
105
|
+
footer,
|
|
106
|
+
cardProps,
|
|
107
|
+
}: TypeProfileCardProps) => {
|
|
108
|
+
return (
|
|
109
|
+
<StyledProfileCard role="presentation" {...cardProps}>
|
|
110
|
+
<StyledCardHeader $bannerColor={bannerColor} $partnerName={partnerName}>
|
|
111
|
+
<Avatar src={avatarUrl} name={name} size="60px" mt={450} />
|
|
112
|
+
{bannerContent && <BannerContent>{bannerContent}</BannerContent>}
|
|
113
|
+
</StyledCardHeader>
|
|
114
|
+
|
|
115
|
+
<ActionsSection>{profileActions}</ActionsSection>
|
|
116
|
+
|
|
117
|
+
<StyledCardContent>
|
|
118
|
+
<ProfileNameSection>
|
|
119
|
+
{partnerName && (
|
|
120
|
+
<PartnerLogo
|
|
121
|
+
partnerName={partnerName}
|
|
122
|
+
aria-label={`${partnerName} profile`}
|
|
123
|
+
size="small"
|
|
124
|
+
/>
|
|
125
|
+
)}
|
|
126
|
+
<Text.SubHeadline as="h2">{name}</Text.SubHeadline>
|
|
127
|
+
</ProfileNameSection>
|
|
128
|
+
|
|
129
|
+
{subtext && (
|
|
130
|
+
<Text fontSize={200} color="text.subtext">
|
|
131
|
+
{subtext}
|
|
132
|
+
</Text>
|
|
133
|
+
)}
|
|
134
|
+
|
|
135
|
+
{secondaryName && (
|
|
136
|
+
<InfoRow>
|
|
137
|
+
{profileUrl ? (
|
|
138
|
+
<Link href={profileUrl} external>
|
|
139
|
+
<Text fontSize={200}>{secondaryName}</Text>
|
|
140
|
+
<Icon
|
|
141
|
+
name="arrow-right-up-outline"
|
|
142
|
+
aria-hidden
|
|
143
|
+
ml={100}
|
|
144
|
+
size="small"
|
|
145
|
+
/>
|
|
146
|
+
</Link>
|
|
147
|
+
) : (
|
|
148
|
+
<Text.SmallByline color="text.subtext">
|
|
149
|
+
{secondaryName}
|
|
150
|
+
</Text.SmallByline>
|
|
151
|
+
)}
|
|
152
|
+
{verificationType && (
|
|
153
|
+
<VerifiedProfileIcon
|
|
154
|
+
partnerName={partnerName}
|
|
155
|
+
verificationType={verificationType}
|
|
156
|
+
/>
|
|
157
|
+
)}
|
|
158
|
+
</InfoRow>
|
|
159
|
+
)}
|
|
160
|
+
|
|
161
|
+
{description && <Text fontSize={200}>{description}</Text>}
|
|
162
|
+
|
|
163
|
+
{location && (
|
|
164
|
+
<InfoRow>
|
|
165
|
+
<Icon name="location-pin-outline" aria-hidden size="small" />
|
|
166
|
+
<Text fontSize={200}>{location}</Text>
|
|
167
|
+
</InfoRow>
|
|
168
|
+
)}
|
|
169
|
+
|
|
170
|
+
{metadata && metadata.length > 0 && (
|
|
171
|
+
<InfoRow>
|
|
172
|
+
{metadata.map((info, index) => (
|
|
173
|
+
<Text fontSize={200} key={index}>
|
|
174
|
+
{info}
|
|
175
|
+
</Text>
|
|
176
|
+
))}
|
|
177
|
+
</InfoRow>
|
|
178
|
+
)}
|
|
179
|
+
</StyledCardContent>
|
|
180
|
+
|
|
181
|
+
{footer && <CardFooter>{footer}</CardFooter>}
|
|
182
|
+
</StyledProfileCard>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export default ProfileCard;
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
2
|
import { ProfileToken } from "./ProfileToken";
|
|
4
|
-
import { Icon } from "@sproutsocial/seeds-react-icon";
|
|
5
3
|
|
|
6
4
|
const avatarUrl =
|
|
7
5
|
"https://d672eyudr6aq1.cloudfront.net/avatar/cede1373e17c05542b1cc60f427067f2?s=30&d=404";
|
|
8
6
|
|
|
9
|
-
const VerificationBadge = () => (
|
|
10
|
-
<Icon name="check-solid" style={{ color: "#1da1f2" }} />
|
|
11
|
-
);
|
|
12
|
-
|
|
13
7
|
const meta: Meta<typeof ProfileToken> = {
|
|
14
8
|
title: "Components/Profile/ProfileToken",
|
|
15
9
|
component: ProfileToken,
|
|
@@ -17,7 +11,25 @@ const meta: Meta<typeof ProfileToken> = {
|
|
|
17
11
|
layout: "centered",
|
|
18
12
|
},
|
|
19
13
|
tags: ["autodocs"],
|
|
20
|
-
argTypes: {
|
|
14
|
+
argTypes: {
|
|
15
|
+
partnerName: {
|
|
16
|
+
control: { type: "select" },
|
|
17
|
+
options: [
|
|
18
|
+
"twitter",
|
|
19
|
+
"facebook",
|
|
20
|
+
"instagram",
|
|
21
|
+
"linkedin",
|
|
22
|
+
"youtube",
|
|
23
|
+
"tiktok",
|
|
24
|
+
"threads",
|
|
25
|
+
"bluesky",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
verificationType: {
|
|
29
|
+
control: { type: "select" },
|
|
30
|
+
options: ["verified", "blue_verified", "gray_verified", "not_verified"],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
21
33
|
};
|
|
22
34
|
|
|
23
35
|
export default meta;
|
|
@@ -27,7 +39,7 @@ export const Default: Story = {
|
|
|
27
39
|
args: {
|
|
28
40
|
name: "John Doe",
|
|
29
41
|
secondaryName: "@johndoe",
|
|
30
|
-
|
|
42
|
+
avatarUrl: avatarUrl,
|
|
31
43
|
partnerName: "twitter",
|
|
32
44
|
},
|
|
33
45
|
};
|
|
@@ -36,9 +48,9 @@ export const WithVerification: Story = {
|
|
|
36
48
|
args: {
|
|
37
49
|
name: "Elon Musk",
|
|
38
50
|
secondaryName: "@elonmusk",
|
|
39
|
-
|
|
51
|
+
avatarUrl: avatarUrl,
|
|
40
52
|
partnerName: "twitter",
|
|
41
|
-
|
|
53
|
+
verificationType: "verified",
|
|
42
54
|
},
|
|
43
55
|
};
|
|
44
56
|
|
|
@@ -46,7 +58,7 @@ export const FacebookUser: Story = {
|
|
|
46
58
|
args: {
|
|
47
59
|
name: "Facebook User",
|
|
48
60
|
secondaryName: "/facebookuser",
|
|
49
|
-
|
|
61
|
+
avatarUrl: avatarUrl,
|
|
50
62
|
partnerName: "facebook",
|
|
51
63
|
},
|
|
52
64
|
};
|
|
@@ -55,7 +67,7 @@ export const InstagramUser: Story = {
|
|
|
55
67
|
args: {
|
|
56
68
|
name: "Instagram User",
|
|
57
69
|
secondaryName: "@instagramuser",
|
|
58
|
-
|
|
70
|
+
avatarUrl: avatarUrl,
|
|
59
71
|
partnerName: "instagram",
|
|
60
72
|
},
|
|
61
73
|
};
|
|
@@ -64,7 +76,7 @@ export const LinkedInUser: Story = {
|
|
|
64
76
|
args: {
|
|
65
77
|
name: "LinkedIn User",
|
|
66
78
|
secondaryName: "linkedinuser",
|
|
67
|
-
|
|
79
|
+
avatarUrl: avatarUrl,
|
|
68
80
|
partnerName: "linkedin",
|
|
69
81
|
},
|
|
70
82
|
};
|
|
@@ -73,7 +85,7 @@ export const YouTubeUser: Story = {
|
|
|
73
85
|
args: {
|
|
74
86
|
name: "YouTube Creator",
|
|
75
87
|
secondaryName: "@youtubecreator",
|
|
76
|
-
|
|
88
|
+
avatarUrl: avatarUrl,
|
|
77
89
|
partnerName: "youtube",
|
|
78
90
|
},
|
|
79
91
|
};
|
|
@@ -82,7 +94,7 @@ export const TikTokUser: Story = {
|
|
|
82
94
|
args: {
|
|
83
95
|
name: "TikTok Creator",
|
|
84
96
|
secondaryName: "@tiktokcreator",
|
|
85
|
-
|
|
97
|
+
avatarUrl: avatarUrl,
|
|
86
98
|
partnerName: "tiktok",
|
|
87
99
|
},
|
|
88
100
|
};
|
|
@@ -91,7 +103,7 @@ export const ThreadsUser: Story = {
|
|
|
91
103
|
args: {
|
|
92
104
|
name: "Threads User",
|
|
93
105
|
secondaryName: "@threadsuser",
|
|
94
|
-
|
|
106
|
+
avatarUrl: avatarUrl,
|
|
95
107
|
partnerName: "threads",
|
|
96
108
|
},
|
|
97
109
|
};
|
|
@@ -100,7 +112,7 @@ export const BlueskyUser: Story = {
|
|
|
100
112
|
args: {
|
|
101
113
|
name: "Bluesky User",
|
|
102
114
|
secondaryName: "@blueskyuser",
|
|
103
|
-
|
|
115
|
+
avatarUrl: avatarUrl,
|
|
104
116
|
partnerName: "bluesky",
|
|
105
117
|
},
|
|
106
118
|
};
|
|
@@ -113,43 +125,53 @@ export const WithoutAvatar: Story = {
|
|
|
113
125
|
},
|
|
114
126
|
};
|
|
115
127
|
|
|
128
|
+
export const BlueVerifiedUser: Story = {
|
|
129
|
+
args: {
|
|
130
|
+
name: "Blue Verified User",
|
|
131
|
+
secondaryName: "@blueverified",
|
|
132
|
+
partnerName: "twitter",
|
|
133
|
+
avatarUrl: "https://via.placeholder.com/40",
|
|
134
|
+
verificationType: "blue_verified",
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
116
138
|
export const DifferentNetworks: Story = {
|
|
117
139
|
render: () => (
|
|
118
140
|
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
119
141
|
<ProfileToken
|
|
120
142
|
name="Twitter User"
|
|
121
143
|
secondaryName="@twitteruser"
|
|
122
|
-
|
|
144
|
+
avatarUrl={avatarUrl}
|
|
123
145
|
partnerName="twitter"
|
|
124
146
|
/>
|
|
125
147
|
<ProfileToken
|
|
126
148
|
name="Facebook User"
|
|
127
149
|
secondaryName="/facebookuser"
|
|
128
|
-
|
|
150
|
+
avatarUrl={avatarUrl}
|
|
129
151
|
partnerName="facebook"
|
|
130
152
|
/>
|
|
131
153
|
<ProfileToken
|
|
132
154
|
name="Instagram User"
|
|
133
155
|
secondaryName="@instagramuser"
|
|
134
|
-
|
|
156
|
+
avatarUrl={avatarUrl}
|
|
135
157
|
partnerName="instagram"
|
|
136
158
|
/>
|
|
137
159
|
<ProfileToken
|
|
138
160
|
name="LinkedIn User"
|
|
139
161
|
secondaryName="linkedinuser"
|
|
140
|
-
|
|
162
|
+
avatarUrl={avatarUrl}
|
|
141
163
|
partnerName="linkedin"
|
|
142
164
|
/>
|
|
143
165
|
<ProfileToken
|
|
144
166
|
name="YouTube Creator"
|
|
145
167
|
secondaryName="@youtubecreator"
|
|
146
|
-
|
|
168
|
+
avatarUrl={avatarUrl}
|
|
147
169
|
partnerName="youtube"
|
|
148
170
|
/>
|
|
149
171
|
<ProfileToken
|
|
150
172
|
name="TikTok Creator"
|
|
151
173
|
secondaryName="@tiktokcreator"
|
|
152
|
-
|
|
174
|
+
avatarUrl={avatarUrl}
|
|
153
175
|
partnerName="tiktok"
|
|
154
176
|
/>
|
|
155
177
|
</div>
|
|
@@ -162,41 +184,77 @@ export const TokenVariations: Story = {
|
|
|
162
184
|
<ProfileToken
|
|
163
185
|
name="Verified User"
|
|
164
186
|
secondaryName="@verified"
|
|
165
|
-
|
|
187
|
+
avatarUrl={avatarUrl}
|
|
166
188
|
partnerName="twitter"
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
</ProfileToken>
|
|
189
|
+
verificationType="verified"
|
|
190
|
+
/>
|
|
170
191
|
<ProfileToken
|
|
171
192
|
name="Regular User"
|
|
172
193
|
secondaryName="@regular"
|
|
173
|
-
|
|
194
|
+
avatarUrl={avatarUrl}
|
|
174
195
|
partnerName="twitter"
|
|
175
196
|
/>
|
|
176
197
|
<ProfileToken
|
|
177
198
|
name="Facebook User"
|
|
178
199
|
secondaryName="/facebookuser"
|
|
179
|
-
|
|
200
|
+
avatarUrl={avatarUrl}
|
|
180
201
|
partnerName="facebook"
|
|
181
202
|
/>
|
|
182
203
|
<ProfileToken
|
|
183
204
|
name="Instagram User"
|
|
184
205
|
secondaryName="@instagramuser"
|
|
185
|
-
|
|
206
|
+
avatarUrl={avatarUrl}
|
|
186
207
|
partnerName="instagram"
|
|
187
208
|
/>
|
|
188
209
|
<ProfileToken
|
|
189
210
|
name="LinkedIn User"
|
|
190
211
|
secondaryName="linkedinuser"
|
|
191
|
-
|
|
212
|
+
avatarUrl={avatarUrl}
|
|
192
213
|
partnerName="linkedin"
|
|
193
214
|
/>
|
|
194
215
|
<ProfileToken
|
|
195
216
|
name="YouTube Creator"
|
|
196
217
|
secondaryName="@youtubecreator"
|
|
197
|
-
|
|
218
|
+
avatarUrl={avatarUrl}
|
|
198
219
|
partnerName="youtube"
|
|
199
220
|
/>
|
|
200
221
|
</div>
|
|
201
222
|
),
|
|
202
223
|
};
|
|
224
|
+
|
|
225
|
+
export const LongNamesTruncation: Story = {
|
|
226
|
+
render: () => (
|
|
227
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "16px" }}>
|
|
228
|
+
<div
|
|
229
|
+
style={{ maxWidth: "250px", border: "1px solid #ccc", padding: "8px" }}
|
|
230
|
+
>
|
|
231
|
+
<ProfileToken
|
|
232
|
+
name="A Very Long User Name That Should Truncate"
|
|
233
|
+
secondaryName="@verylonghandlethatshouldalsotruncate"
|
|
234
|
+
avatarUrl={avatarUrl}
|
|
235
|
+
partnerName="twitter"
|
|
236
|
+
/>
|
|
237
|
+
</div>
|
|
238
|
+
<div
|
|
239
|
+
style={{ maxWidth: "180px", border: "1px solid #ccc", padding: "8px" }}
|
|
240
|
+
>
|
|
241
|
+
<ProfileToken
|
|
242
|
+
name="Another Long Name Token"
|
|
243
|
+
secondaryName="@anotherlonghandle"
|
|
244
|
+
avatarUrl={avatarUrl}
|
|
245
|
+
partnerName="facebook"
|
|
246
|
+
/>
|
|
247
|
+
</div>
|
|
248
|
+
<div
|
|
249
|
+
style={{ maxWidth: "120px", border: "1px solid #ccc", padding: "8px" }}
|
|
250
|
+
>
|
|
251
|
+
<ProfileToken
|
|
252
|
+
name="Very Narrow Token"
|
|
253
|
+
secondaryName="@narrow"
|
|
254
|
+
avatarUrl={avatarUrl}
|
|
255
|
+
partnerName="instagram"
|
|
256
|
+
/>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
),
|
|
260
|
+
};
|
package/src/ProfileToken.tsx
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
|
-
import
|
|
1
|
+
import styled from "styled-components";
|
|
2
2
|
import { Token } from "@sproutsocial/seeds-react-token";
|
|
3
3
|
import { InlineProfile } from "./InlineProfile";
|
|
4
4
|
import type { ProfileTokenProps } from "./types";
|
|
5
5
|
|
|
6
|
+
const StyledProfileToken = styled(Token)`
|
|
7
|
+
box-sizing: border-box;
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
max-width: 100%;
|
|
10
|
+
`;
|
|
11
|
+
|
|
6
12
|
/**
|
|
7
13
|
* A ProfileToken component that wraps CompactProfile in a Seeds Token component.
|
|
8
14
|
* This token is not closable and provides a compact way to display profile information inline.
|
|
9
15
|
*/
|
|
10
|
-
export const ProfileToken
|
|
11
|
-
tokenProps,
|
|
16
|
+
export const ProfileToken = ({
|
|
12
17
|
className,
|
|
18
|
+
onClick,
|
|
19
|
+
tokenProps,
|
|
13
20
|
...props
|
|
14
|
-
}) => (
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
}: ProfileTokenProps) => (
|
|
22
|
+
<StyledProfileToken
|
|
23
|
+
className={className}
|
|
24
|
+
closeable={false}
|
|
25
|
+
onClick={onClick}
|
|
26
|
+
{...tokenProps}
|
|
27
|
+
>
|
|
28
|
+
<InlineProfile size="small" {...props} />
|
|
29
|
+
</StyledProfileToken>
|
|
18
30
|
);
|
|
19
31
|
|
|
20
32
|
export default ProfileToken;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Icon } from "@sproutsocial/seeds-react-icon";
|
|
2
|
+
import type { TypeProfileNetwork, TypeProfileVerification } from "./types";
|
|
3
|
+
|
|
4
|
+
// TODO: Move these to a more appropriate location
|
|
5
|
+
const VERIFICATION_COLORS = {
|
|
6
|
+
twitter: "#000000",
|
|
7
|
+
facebook: "#1877F2",
|
|
8
|
+
x: "#000000",
|
|
9
|
+
linkedin: "#0A66C2",
|
|
10
|
+
instagram: "#e4405f",
|
|
11
|
+
gray: "#515e5f",
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Gets the verification color based on partnerName and verification type
|
|
16
|
+
*/
|
|
17
|
+
export const getVerificationColor = (
|
|
18
|
+
partnerName?: TypeProfileNetwork,
|
|
19
|
+
verificationType?: TypeProfileVerification
|
|
20
|
+
): string => {
|
|
21
|
+
if (!verificationType || verificationType === "not_verified") return "";
|
|
22
|
+
|
|
23
|
+
switch (verificationType) {
|
|
24
|
+
case "blue_verified":
|
|
25
|
+
// Use partner-specific blue color
|
|
26
|
+
return partnerName === "facebook"
|
|
27
|
+
? VERIFICATION_COLORS.facebook
|
|
28
|
+
: VERIFICATION_COLORS.twitter;
|
|
29
|
+
case "gray_verified":
|
|
30
|
+
return VERIFICATION_COLORS.gray;
|
|
31
|
+
case "verified":
|
|
32
|
+
default:
|
|
33
|
+
// Default to partner color or Twitter blue
|
|
34
|
+
if (partnerName && partnerName in VERIFICATION_COLORS) {
|
|
35
|
+
return VERIFICATION_COLORS[
|
|
36
|
+
partnerName as keyof typeof VERIFICATION_COLORS
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
return VERIFICATION_COLORS.twitter;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export interface TypeVerifiedProfileIconProps {
|
|
44
|
+
partnerName?: TypeProfileNetwork;
|
|
45
|
+
verificationType: TypeProfileVerification;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const VerifiedProfileIcon = ({
|
|
49
|
+
partnerName,
|
|
50
|
+
verificationType,
|
|
51
|
+
}: TypeVerifiedProfileIconProps) => {
|
|
52
|
+
if (!verificationType || verificationType === "not_verified") return;
|
|
53
|
+
|
|
54
|
+
const verificationColor = getVerificationColor(partnerName, verificationType);
|
|
55
|
+
return (
|
|
56
|
+
<Icon
|
|
57
|
+
title="Verified"
|
|
58
|
+
aria-label="Verified account"
|
|
59
|
+
color={verificationColor}
|
|
60
|
+
name="verified"
|
|
61
|
+
ml="150"
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -9,7 +9,7 @@ describe("InlineProfile", () => {
|
|
|
9
9
|
name="John Doe"
|
|
10
10
|
secondaryName="@johndoe"
|
|
11
11
|
partnerName="twitter"
|
|
12
|
-
|
|
12
|
+
avatarUrl="https://example.com/avatar.jpg"
|
|
13
13
|
/>
|
|
14
14
|
);
|
|
15
15
|
|
|
@@ -17,21 +17,6 @@ describe("InlineProfile", () => {
|
|
|
17
17
|
expect(screen.getByText("@johndoe")).toBeInTheDocument();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
it("renders with children", () => {
|
|
21
|
-
render(
|
|
22
|
-
<InlineProfile
|
|
23
|
-
name="John Doe"
|
|
24
|
-
secondaryName="@johndoe"
|
|
25
|
-
partnerName="twitter"
|
|
26
|
-
img="https://example.com/avatar.jpg"
|
|
27
|
-
>
|
|
28
|
-
Children
|
|
29
|
-
</InlineProfile>
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
expect(screen.getByText("Children")).toBeInTheDocument();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
20
|
it("renders network logo", () => {
|
|
36
21
|
render(
|
|
37
22
|
<InlineProfile
|
|
@@ -39,7 +24,7 @@ describe("InlineProfile", () => {
|
|
|
39
24
|
secondaryName="@johndoe"
|
|
40
25
|
partnerName="twitter"
|
|
41
26
|
partnerLogoLabel="Twitter Logo"
|
|
42
|
-
|
|
27
|
+
avatarUrl="https://example.com/avatar.jpg"
|
|
43
28
|
/>
|
|
44
29
|
);
|
|
45
30
|
|
|
@@ -52,7 +37,7 @@ describe("InlineProfile", () => {
|
|
|
52
37
|
<InlineProfile
|
|
53
38
|
name="John Doe"
|
|
54
39
|
secondaryName="@johndoe"
|
|
55
|
-
|
|
40
|
+
avatarUrl="https://example.com/avatar.jpg"
|
|
56
41
|
/>
|
|
57
42
|
);
|
|
58
43
|
|
|
@@ -66,7 +51,7 @@ describe("InlineProfile", () => {
|
|
|
66
51
|
<InlineProfile
|
|
67
52
|
name="John Doe"
|
|
68
53
|
partnerName="twitter"
|
|
69
|
-
|
|
54
|
+
avatarUrl="https://example.com/avatar.jpg"
|
|
70
55
|
/>
|
|
71
56
|
);
|
|
72
57
|
|
|
@@ -74,10 +59,24 @@ describe("InlineProfile", () => {
|
|
|
74
59
|
expect(screen.queryByText(/@/)).not.toBeInTheDocument();
|
|
75
60
|
});
|
|
76
61
|
|
|
77
|
-
it("renders avatar with name fallback when
|
|
62
|
+
it("renders avatar with name fallback when avatarUrl prop is not provided", () => {
|
|
78
63
|
render(<InlineProfile name="John Doe" />);
|
|
79
64
|
|
|
80
65
|
const avatar = screen.getByText("JD");
|
|
81
66
|
expect(avatar).toBeInTheDocument();
|
|
82
67
|
});
|
|
68
|
+
|
|
69
|
+
it("renders with verification badge when verified", () => {
|
|
70
|
+
render(
|
|
71
|
+
<InlineProfile
|
|
72
|
+
name="John Doe"
|
|
73
|
+
secondaryName="@johndoe"
|
|
74
|
+
partnerName="twitter"
|
|
75
|
+
avatarUrl="https://example.com/avatar.jpg"
|
|
76
|
+
verificationType="verified"
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(screen.getByLabelText("Verified account")).toBeInTheDocument();
|
|
81
|
+
});
|
|
83
82
|
});
|