Skip to content

Commit 1967f43

Browse files
committed
feat: PortalLink
1 parent b8e470c commit 1967f43

File tree

8 files changed

+280
-10
lines changed

8 files changed

+280
-10
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
],
9898
"private": false,
9999
"dependencies": {
100-
"@kinde/js-utils": "0.16.0"
100+
"@kinde/js-utils": "0.18.0-0"
101101
},
102102
"packageManager": "[email protected]+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
103103
}

pnpm-lock.yaml

Lines changed: 166 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/PortalLink.test.tsx

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import "@testing-library/jest-dom/vitest";
2+
import {
3+
act,
4+
cleanup,
5+
render,
6+
screen,
7+
fireEvent,
8+
} from "@testing-library/react";
9+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
10+
import { PortalLink } from ".";
11+
import { useKindeAuth } from "../hooks/useKindeAuth";
12+
13+
vi.mock("../hooks/useKindeAuth", () => ({
14+
useKindeAuth: vi.fn(),
15+
}));
16+
17+
describe("ProfileLink Component", () => {
18+
const mockGeneratePortalUrl = vi.fn().mockReturnValue({
19+
url: new URL("https://example.com/profile"),
20+
});
21+
22+
beforeEach(() => {
23+
vi.clearAllMocks();
24+
vi.mocked(useKindeAuth).mockReturnValue({
25+
generatePortalUrl: mockGeneratePortalUrl,
26+
store: {
27+
getSessionItem: vi.fn().mockResolvedValue("example.com"),
28+
},
29+
});
30+
});
31+
32+
afterEach(() => {
33+
cleanup();
34+
});
35+
36+
it("should render correctly when authed", async () => {
37+
await act(async () => {
38+
render(<PortalLink>Profile</PortalLink>);
39+
});
40+
const linkElement = screen.getByText("Profile");
41+
expect(linkElement).toBeInTheDocument();
42+
});
43+
44+
it("calls to generate profile link when clicked", async () => {
45+
render(<PortalLink>Profile</PortalLink>);
46+
47+
const button = screen.getByRole("button", { name: "Profile" });
48+
fireEvent.click(button);
49+
50+
expect(mockGeneratePortalUrl).toHaveBeenCalledTimes(1);
51+
});
52+
53+
it("passes HTML button props correctly", () => {
54+
render(
55+
<PortalLink className="test-class" disabled>
56+
Profile
57+
</PortalLink>,
58+
);
59+
60+
const button = screen.getByRole("button", { name: "Profile" });
61+
expect(button).toHaveClass("test-class");
62+
expect(button).toBeDisabled();
63+
});
64+
});

src/components/PortalLink.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React, { useCallback } from "react";
2+
import { useKindeAuth } from "../hooks/useKindeAuth";
3+
import { PortalLinkProps } from "../state/types";
4+
5+
export function PortalLink({ children, ...props }: PortalLinkProps) {
6+
const auth = useKindeAuth();
7+
8+
const viewProfile = useCallback(async () => {
9+
const generatedUrl = await auth.generatePortalUrl({
10+
subNav: props.subNav,
11+
returnUrl: props.returnUrl || window.location.href,
12+
});
13+
window.location.href = generatedUrl.url.toString();
14+
}, [auth, props.returnUrl, props.subNav]);
15+
16+
return (
17+
<button
18+
type="button"
19+
{...props}
20+
onClick={() => {
21+
viewProfile();
22+
}}
23+
>
24+
{children}
25+
</button>
26+
);
27+
}

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { LoginLink } from "./LoginLink";
22
export { RegisterLink } from "./RegisterLink";
33
export { LogoutLink } from "./LogoutLink";
4+
export { PortalLink } from "./PortalLink";

0 commit comments

Comments
 (0)