@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.
@@ -0,0 +1,182 @@
1
+ import React from "react";
2
+ import { render, screen } from "@sproutsocial/seeds-react-testing-library";
3
+ import { ProfileCard } from "../ProfileCard";
4
+
5
+ describe("ProfileCard", () => {
6
+ const defaultProps = {
7
+ name: "John Doe",
8
+ secondaryName: "@johndoe",
9
+ partnerName: "twitter" as const,
10
+ avatarUrl: "https://example.com/avatar.jpg",
11
+ };
12
+
13
+ it("renders with name and secondaryName", () => {
14
+ render(<ProfileCard {...defaultProps} />);
15
+
16
+ expect(screen.getByText("John Doe")).toBeInTheDocument();
17
+ expect(screen.getByText("@johndoe")).toBeInTheDocument();
18
+ });
19
+
20
+ it("renders with subtext", () => {
21
+ render(<ProfileCard {...defaultProps} subtext="Software Engineer" />);
22
+
23
+ expect(screen.getByText("Software Engineer")).toBeInTheDocument();
24
+ });
25
+
26
+ it("does not render subtext when not provided", () => {
27
+ render(<ProfileCard {...defaultProps} />);
28
+
29
+ expect(screen.queryByText("Software Engineer")).not.toBeInTheDocument();
30
+ });
31
+
32
+ it("renders network logo", () => {
33
+ render(<ProfileCard {...defaultProps} />);
34
+
35
+ expect(screen.getByDataQaLabel({ logo: "twitter" })).toBeInTheDocument();
36
+ expect(screen.getByLabelText("twitter profile")).toBeInTheDocument();
37
+ });
38
+
39
+ it("renders description", () => {
40
+ render(
41
+ <ProfileCard
42
+ {...defaultProps}
43
+ description="Building amazing things with code"
44
+ />
45
+ );
46
+
47
+ expect(
48
+ screen.getByText("Building amazing things with code")
49
+ ).toBeInTheDocument();
50
+ });
51
+
52
+ it("renders profile URL link with secondaryName", () => {
53
+ render(
54
+ <ProfileCard {...defaultProps} profileUrl="https://twitter.com/johndoe" />
55
+ );
56
+
57
+ const link = screen.getByRole("link");
58
+ expect(link).toHaveAttribute("href", "https://twitter.com/johndoe");
59
+ expect(screen.getByText("@johndoe")).toBeInTheDocument();
60
+ });
61
+
62
+ it("does not render profile URL when secondaryName is not provided", () => {
63
+ const { secondaryName, ...propsWithoutSecondary } = defaultProps;
64
+ render(
65
+ <ProfileCard
66
+ {...propsWithoutSecondary}
67
+ profileUrl="https://twitter.com/johndoe"
68
+ />
69
+ );
70
+
71
+ expect(screen.queryByRole("link")).not.toBeInTheDocument();
72
+ });
73
+
74
+ it("renders secondaryName without link when profileUrl is not provided", () => {
75
+ render(<ProfileCard {...defaultProps} />);
76
+
77
+ expect(screen.getByText("@johndoe")).toBeInTheDocument();
78
+ expect(screen.queryByRole("link")).not.toBeInTheDocument();
79
+ });
80
+
81
+ it("renders location with icon", () => {
82
+ render(<ProfileCard {...defaultProps} location="San Francisco, CA" />);
83
+
84
+ expect(screen.getByText("San Francisco, CA")).toBeInTheDocument();
85
+ });
86
+
87
+ it("renders metadata array", () => {
88
+ render(
89
+ <ProfileCard
90
+ {...defaultProps}
91
+ metadata={["1.2K followers", "500 following"]}
92
+ />
93
+ );
94
+
95
+ expect(screen.getByText("1.2K followers")).toBeInTheDocument();
96
+ expect(screen.getByText("500 following")).toBeInTheDocument();
97
+ });
98
+
99
+ it("renders with verification badge when verified", () => {
100
+ render(<ProfileCard {...defaultProps} verificationType="verified" />);
101
+
102
+ expect(screen.getByLabelText("Verified account")).toBeInTheDocument();
103
+ });
104
+
105
+ it("renders banner content when provided", () => {
106
+ render(
107
+ <ProfileCard
108
+ {...defaultProps}
109
+ bannerContent={<div>Custom banner content</div>}
110
+ />
111
+ );
112
+
113
+ expect(screen.getByText("Custom banner content")).toBeInTheDocument();
114
+ });
115
+
116
+ it("renders profile actions when provided", () => {
117
+ render(
118
+ <ProfileCard
119
+ {...defaultProps}
120
+ profileActions={<button>Edit Profile</button>}
121
+ />
122
+ );
123
+
124
+ expect(
125
+ screen.getByRole("button", { name: "Edit Profile" })
126
+ ).toBeInTheDocument();
127
+ });
128
+
129
+ it("renders footer when provided", () => {
130
+ render(
131
+ <ProfileCard
132
+ {...defaultProps}
133
+ footer={<div>Custom footer content</div>}
134
+ />
135
+ );
136
+
137
+ expect(screen.getByText("Custom footer content")).toBeInTheDocument();
138
+ });
139
+
140
+ it("renders without network logo when partnerName is not provided", () => {
141
+ const { partnerName, ...propsWithoutPartner } = defaultProps;
142
+ render(<ProfileCard {...propsWithoutPartner} />);
143
+
144
+ expect(
145
+ screen.queryByDataQaLabel({ logo: "twitter" })
146
+ ).not.toBeInTheDocument();
147
+ });
148
+
149
+ it("renders with all features combined", () => {
150
+ render(
151
+ <ProfileCard
152
+ {...defaultProps}
153
+ subtext="Lead Engineer"
154
+ description="Passionate about building great products"
155
+ profileUrl="https://twitter.com/johndoe"
156
+ location="Seattle, WA"
157
+ metadata={["5K followers", "1K following"]}
158
+ verificationType="blue_verified"
159
+ bannerContent={<div>Banner</div>}
160
+ profileActions={<button>Actions</button>}
161
+ footer={<div>Footer</div>}
162
+ />
163
+ );
164
+
165
+ expect(screen.getByText("John Doe")).toBeInTheDocument();
166
+ expect(screen.getByText("Lead Engineer")).toBeInTheDocument();
167
+ expect(
168
+ screen.getByText("Passionate about building great products")
169
+ ).toBeInTheDocument();
170
+ expect(screen.getByText("@johndoe")).toBeInTheDocument();
171
+ expect(screen.getByRole("link")).toHaveAttribute(
172
+ "href",
173
+ "https://twitter.com/johndoe"
174
+ );
175
+ expect(screen.getByText("Seattle, WA")).toBeInTheDocument();
176
+ expect(screen.getByText("5K followers")).toBeInTheDocument();
177
+ expect(screen.getByLabelText("Verified account")).toBeInTheDocument();
178
+ expect(screen.getByText("Banner")).toBeInTheDocument();
179
+ expect(screen.getByRole("button", { name: "Actions" })).toBeInTheDocument();
180
+ expect(screen.getByText("Footer")).toBeInTheDocument();
181
+ });
182
+ });
@@ -7,7 +7,8 @@ describe("ProfileToken", () => {
7
7
  name: "John Doe",
8
8
  secondaryName: "@johndoe",
9
9
  partnerName: "twitter" as const,
10
- img: "https://example.com/avatar.jpg",
10
+ avatarUrl: "https://example.com/avatar.jpg",
11
+ verificationType: "verified" as const,
11
12
  };
12
13
 
13
14
  it("renders profile information in token format", () => {
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./ProfileCard";
1
2
  export * from "./InlineProfile";
2
3
  export * from "./ProfileToken";
3
4
  export * from "./types";
package/src/types.ts CHANGED
@@ -6,21 +6,107 @@
6
6
  */
7
7
 
8
8
  import type { EnumLogoNamesWithoutVariants } from "@sproutsocial/seeds-partner-logos";
9
- import type Token from "@sproutsocial/seeds-react-token";
9
+ import type { TypeTokenProps } from "@sproutsocial/seeds-react-token";
10
+ import type { TypeCardProps } from "@sproutsocial/seeds-react-card";
10
11
 
11
- export interface TypeInlineProfileProps {
12
+ /**
13
+ * Social network types supported by the Profile component
14
+ * This is a subset of EnumLogoNamesWithoutVariants that only includes actual social networks
15
+ */
16
+ export type TypeProfileNetwork = Extract<
17
+ EnumLogoNamesWithoutVariants,
18
+ | "twitter"
19
+ | "facebook"
20
+ | "instagram"
21
+ | "linkedin"
22
+ | "youtube"
23
+ | "tiktok"
24
+ | "threads"
25
+ | "bluesky"
26
+ | "reddit"
27
+ | "pinterest"
28
+ | "whatsapp"
29
+ | "glassdoor"
30
+ | "trustpilot"
31
+ | "tumblr"
32
+ | "yelp"
33
+ | "x-twitter"
34
+ >;
35
+
36
+ /**
37
+ * Profile verification types
38
+ */
39
+ export type TypeProfileVerification =
40
+ | "blue_verified" // Blue checkmark (Facebook, Twitter, etc.)
41
+ | "gray_verified" // Gray checkmark (Facebook, etc.)
42
+ | "verified" // Standard verification
43
+ | "not_verified"; // No verification
44
+
45
+ /**
46
+ * Base props interface shared between Profile and InlineProfile components
47
+ */
48
+ export interface TypeBaseProfileProps {
49
+ /** The network of the profile */
50
+ partnerName?: TypeProfileNetwork;
51
+ /** A custom label to apply to the partner logo */
52
+ partnerLogoLabel?: string;
53
+ /** The display name of the profile */
12
54
  name: string;
55
+ /** The handle or username of the profile */
13
56
  secondaryName?: string;
57
+ /** The profile picture URL */
58
+ avatarUrl?: string;
59
+ /** The type of verification for the profile, if applicable */
60
+ verificationType?: TypeProfileVerification;
61
+ /** Additional CSS class name */
62
+ className?: string;
63
+ }
64
+
65
+ /**
66
+ * Props interface for the InlineProfile component
67
+ */
68
+ export interface TypeInlineProfileProps
69
+ extends Omit<TypeBaseProfileProps, "name"> {
70
+ /** The display name of the profile (optional for inline) */
71
+ name?: string;
72
+ /** Size variant for the inline profile */
73
+ size?: "small" | "medium" | "large";
74
+ /** The px size of the avatar, which has a default based on the size prop */
14
75
  avatarSize?: string;
15
- partnerName?: EnumLogoNamesWithoutVariants;
16
- partnerLogoLabel?: string;
17
- img?: string;
18
- children?: React.ReactNode | React.ReactNode[];
19
76
  }
20
77
 
21
78
  export interface ProfileTokenProps extends TypeInlineProfileProps {
22
- /** Additional CSS class name */
23
- className?: string;
79
+ /** Click handler for the entire inline profile */
80
+ onClick?: TypeTokenProps["onClick"];
24
81
  /** Additional props to pass to the Token component */
25
- tokenProps?: Omit<React.ComponentProps<typeof Token>, "children">;
82
+ tokenProps?: Omit<TypeTokenProps, "children">;
83
+ }
84
+
85
+ /**
86
+ * Main props interface for the Profile component
87
+ */
88
+ export interface TypeProfileCardProps extends TypeBaseProfileProps {
89
+ /** Subtext for below the profile name */
90
+ subtext?: string;
91
+ /** Profile description or bio */
92
+ description?: string;
93
+ /** External profile URL (e.g., "https://twitter.com/username"), only displayed if secondaryName is provided */
94
+ profileUrl?: string;
95
+ /** Location text (e.g., "San Francisco, CA") */
96
+ location?: string;
97
+ /**
98
+ * Background color for the banner header.
99
+ * If not provided, will use theme.colors.network[partnerName] when partnerName is available.
100
+ */
101
+ bannerColor?: string;
102
+ /** Generic content slot for banners, alerts, or status messages */
103
+ bannerContent?: React.ReactNode;
104
+ /** Array of metadata strings (e.g., ["1.2K followers", "500 following"]) */
105
+ metadata?: string[];
106
+ /** Action buttons or overflow menus for profile operations. Rendered in order provided, right-aligned. */
107
+ profileActions?: React.ReactNode;
108
+ /** Custom footer content */
109
+ footer?: React.ReactNode;
110
+ /** Props to pass down to the Card */
111
+ cardProps?: TypeCardProps;
26
112
  }