feat: update copy functionality in ContactLinks component

- Modified the copy button behavior to display "Copied" via button aria-label instead of a tooltip after a successful copy action.
- Enhanced the component to revert the copy button icon when switching between phone number and Telegram username copy actions.
- Updated tests to reflect changes in copy button functionality and ensure proper rendering based on user interactions.
This commit is contained in:
2026-03-07 00:11:36 +03:00
parent 7cd00893ad
commit dc87b3ad97
2 changed files with 68 additions and 56 deletions

View File

@@ -184,9 +184,9 @@ describe("ContactLinks", () => {
expect(triggerHapticLightMock).toHaveBeenCalled();
});
it("shows Copied text in tooltip after successful copy", async () => {
it("shows Copied via button aria-label and no tooltip after successful copy", async () => {
copyToClipboardMock.mockResolvedValue(true);
renderWithTooltip(
render(
<ContactLinks
phone="+79991234567"
username={null}
@@ -201,10 +201,44 @@ describe("ContactLinks", () => {
fireEvent.click(copyBtn);
});
const tooltip = await screen.findByRole("tooltip", {
name: /Copied|Скопировано/i,
expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
expect(
screen.getByRole("button", { name: /Copied|Скопировано/i })
).toBeInTheDocument();
});
expect(tooltip).toBeInTheDocument();
it("reverts first copy button to Copy icon when copying the other field", async () => {
copyToClipboardMock.mockResolvedValue(true);
render(
<ContactLinks
phone="+79991234567"
username="alice_dev"
layout="block"
showCopyButtons
/>
);
const copyPhoneBtn = screen.getByRole("button", {
name: /Copy phone number|Скопировать номер/i,
});
const copyTelegramBtn = screen.getByRole("button", {
name: /Copy Telegram username|Скопировать логин Telegram/i,
});
await act(async () => {
fireEvent.click(copyPhoneBtn);
});
expect(
screen.getByRole("button", { name: /Copied|Скопировано/i })
).toBeInTheDocument();
await act(async () => {
fireEvent.click(copyTelegramBtn);
});
expect(
screen.getByRole("button", { name: /Copy phone number|Скопировать номер/i })
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: /Copied|Скопировано/i })
).toBeInTheDocument();
});
it("does not show copy buttons when showCopyButtons is false", () => {

View File

@@ -12,14 +12,8 @@ import { openPhoneLink } from "@/lib/open-phone-link";
import { openTelegramProfile } from "@/lib/telegram-link";
import { triggerHapticLight } from "@/lib/telegram-haptic";
import { copyToClipboard } from "@/lib/copy-to-clipboard";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { Phone as PhoneIcon, Send as TelegramIcon, Copy } from "lucide-react";
import { Phone as PhoneIcon, Send as TelegramIcon, Copy, Check } from "lucide-react";
const COPIED_RESET_MS = 1800;
@@ -128,30 +122,22 @@ export function ContactLinks({
<span className="truncate">{formatPhoneDisplay(phone!)}</span>
</a>
{showCopy && (
<Tooltip
open={copiedKind === "phone"}
onOpenChange={(open) => {
if (!open) setCopiedKind(null);
}}
>
<TooltipTrigger asChild>
<button
type="button"
className="flex h-12 w-12 shrink-0 items-center justify-center rounded-r-[calc(theme(borderRadius.md)-1px)] text-accent hover:bg-accent/15 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-[-2px]"
aria-label={t("contact.copy_phone")}
aria-label={copiedKind === "phone" ? t("contact.copied") : t("contact.copy_phone")}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
void handleCopyPhone(e);
}}
>
{copiedKind === "phone" ? (
<Check className="size-5" aria-hidden />
) : (
<Copy className="size-5" aria-hidden />
)}
</button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8}>
{t("contact.copied")}
</TooltipContent>
</Tooltip>
)}
</div>
)}
@@ -173,30 +159,22 @@ export function ContactLinks({
<span className="truncate">@{cleanUsername}</span>
</a>
{showCopy && (
<Tooltip
open={copiedKind === "telegram"}
onOpenChange={(open) => {
if (!open) setCopiedKind(null);
}}
>
<TooltipTrigger asChild>
<button
type="button"
className="flex h-12 w-12 shrink-0 items-center justify-center rounded-r-[calc(theme(borderRadius.md)-1px)] text-accent hover:bg-accent/15 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent focus-visible:outline-offset-[-2px]"
aria-label={t("contact.copy_telegram")}
aria-label={copiedKind === "telegram" ? t("contact.copied") : t("contact.copy_telegram")}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
void handleCopyTelegram(e);
}}
>
{copiedKind === "telegram" ? (
<Check className="size-5" aria-hidden />
) : (
<Copy className="size-5" aria-hidden />
)}
</button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8}>
{t("contact.copied")}
</TooltipContent>
</Tooltip>
)}
</div>
)}