Add SharePanel component with social links and copy-to-clipboard
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
// @vitest-environment jsdom
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import { SharePanel } from "./SharePanel.js";
|
||||
|
||||
vi.mock("@/i18n/provider.js", () => ({
|
||||
useTranslation: () => ({ t: (k: string) => k }),
|
||||
}));
|
||||
|
||||
describe("SharePanel", () => {
|
||||
let writeTextMock: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
writeTextMock = vi.fn().mockResolvedValue(undefined);
|
||||
Object.defineProperty(navigator, "clipboard", {
|
||||
value: { writeText: writeTextMock },
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("renders 3 social links for en locale (no Weibo)", () => {
|
||||
render(<SharePanel url="https://example.com/flight" locale="en" onClose={() => {}} />);
|
||||
expect(screen.getByTestId("share-facebook")).toBeTruthy();
|
||||
expect(screen.getByTestId("share-vk")).toBeTruthy();
|
||||
expect(screen.getByTestId("share-twitter")).toBeTruthy();
|
||||
expect(screen.queryByTestId("share-weibo")).toBeNull();
|
||||
});
|
||||
|
||||
it("renders 4 social links for zh locale (includes Weibo)", () => {
|
||||
render(<SharePanel url="https://example.com/flight" locale="zh" onClose={() => {}} />);
|
||||
expect(screen.getByTestId("share-facebook")).toBeTruthy();
|
||||
expect(screen.getByTestId("share-vk")).toBeTruthy();
|
||||
expect(screen.getByTestId("share-twitter")).toBeTruthy();
|
||||
expect(screen.getByTestId("share-weibo")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("Facebook href contains encoded URL", () => {
|
||||
render(<SharePanel url="https://example.com/flight?a=1" locale="en" onClose={() => {}} />);
|
||||
const link = screen.getByTestId("share-facebook") as HTMLAnchorElement;
|
||||
expect(link.href).toContain(encodeURIComponent("https://example.com/flight?a=1"));
|
||||
});
|
||||
|
||||
it("copy button calls navigator.clipboard.writeText and onClose", async () => {
|
||||
const onClose = vi.fn();
|
||||
render(<SharePanel url="https://example.com/flight" locale="en" onClose={onClose} />);
|
||||
fireEvent.click(screen.getByTestId("share-copy"));
|
||||
await waitFor(() => {
|
||||
expect(writeTextMock).toHaveBeenCalledWith("https://example.com/flight");
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import type { FC } from "react";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import "./actions.scss";
|
||||
|
||||
export interface SharePanelProps {
|
||||
url: string;
|
||||
locale: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const SharePanel: FC<SharePanelProps> = ({ url, locale, onClose }) => {
|
||||
const { t } = useTranslation();
|
||||
const encoded = encodeURIComponent(url);
|
||||
|
||||
const handleCopy = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(url);
|
||||
onClose();
|
||||
} catch {
|
||||
// silent
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="share-panel" data-testid="share-panel">
|
||||
<a
|
||||
data-testid="share-facebook"
|
||||
href={`https://www.facebook.com/sharer/sharer.php?u=${encoded}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Facebook
|
||||
</a>
|
||||
<a
|
||||
data-testid="share-vk"
|
||||
href={`https://vk.com/share.php?url=${encoded}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
VK
|
||||
</a>
|
||||
<a
|
||||
data-testid="share-twitter"
|
||||
href={`https://twitter.com/share?text=${encodeURIComponent("My Flight")}&url=${encoded}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Twitter
|
||||
</a>
|
||||
{locale === "zh" && (
|
||||
<a
|
||||
data-testid="share-weibo"
|
||||
href={`https://service.weibo.com/share/share.php?url=${encoded}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Weibo
|
||||
</a>
|
||||
)}
|
||||
<button type="button" data-testid="share-copy" onClick={handleCopy}>
|
||||
{t("SHARED.COPY") || "Copy"}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user