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(); 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); copyToClipboardMock.mockResolvedValue(true);
renderWithTooltip( render(
<ContactLinks <ContactLinks
phone="+79991234567" phone="+79991234567"
username={null} username={null}
@@ -201,10 +201,44 @@ describe("ContactLinks", () => {
fireEvent.click(copyBtn); fireEvent.click(copyBtn);
}); });
const tooltip = await screen.findByRole("tooltip", { expect(screen.queryByRole("tooltip")).not.toBeInTheDocument();
name: /Copied|Скопировано/i, expect(
screen.getByRole("button", { name: /Copied|Скопировано/i })
).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,
}); });
expect(tooltip).toBeInTheDocument(); 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", () => { 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 { openTelegramProfile } from "@/lib/telegram-link";
import { triggerHapticLight } from "@/lib/telegram-haptic"; import { triggerHapticLight } from "@/lib/telegram-haptic";
import { copyToClipboard } from "@/lib/copy-to-clipboard"; 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 { 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; const COPIED_RESET_MS = 1800;
@@ -128,30 +122,22 @@ export function ContactLinks({
<span className="truncate">{formatPhoneDisplay(phone!)}</span> <span className="truncate">{formatPhoneDisplay(phone!)}</span>
</a> </a>
{showCopy && ( {showCopy && (
<Tooltip <button
open={copiedKind === "phone"} type="button"
onOpenChange={(open) => { 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]"
if (!open) setCopiedKind(null); aria-label={copiedKind === "phone" ? t("contact.copied") : t("contact.copy_phone")}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
void handleCopyPhone(e);
}} }}
> >
<TooltipTrigger asChild> {copiedKind === "phone" ? (
<button <Check className="size-5" aria-hidden />
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]" <Copy className="size-5" aria-hidden />
aria-label={t("contact.copy_phone")} )}
onClick={(e) => { </button>
e.preventDefault();
e.stopPropagation();
void handleCopyPhone(e);
}}
>
<Copy className="size-5" aria-hidden />
</button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8}>
{t("contact.copied")}
</TooltipContent>
</Tooltip>
)} )}
</div> </div>
)} )}
@@ -173,30 +159,22 @@ export function ContactLinks({
<span className="truncate">@{cleanUsername}</span> <span className="truncate">@{cleanUsername}</span>
</a> </a>
{showCopy && ( {showCopy && (
<Tooltip <button
open={copiedKind === "telegram"} type="button"
onOpenChange={(open) => { 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]"
if (!open) setCopiedKind(null); aria-label={copiedKind === "telegram" ? t("contact.copied") : t("contact.copy_telegram")}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
void handleCopyTelegram(e);
}} }}
> >
<TooltipTrigger asChild> {copiedKind === "telegram" ? (
<button <Check className="size-5" aria-hidden />
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]" <Copy className="size-5" aria-hidden />
aria-label={t("contact.copy_telegram")} )}
onClick={(e) => { </button>
e.preventDefault();
e.stopPropagation();
void handleCopyTelegram(e);
}}
>
<Copy className="size-5" aria-hidden />
</button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={8}>
{t("contact.copied")}
</TooltipContent>
</Tooltip>
)} )}
</div> </div>
)} )}