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:
@@ -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();
|
||||||
});
|
});
|
||||||
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", () => {
|
it("does not show copy buttons when showCopyButtons is false", () => {
|
||||||
|
|||||||
@@ -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
|
|
||||||
open={copiedKind === "phone"}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) setCopiedKind(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
<button
|
||||||
type="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]"
|
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) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
void handleCopyPhone(e);
|
void handleCopyPhone(e);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{copiedKind === "phone" ? (
|
||||||
|
<Check className="size-5" aria-hidden />
|
||||||
|
) : (
|
||||||
<Copy className="size-5" aria-hidden />
|
<Copy className="size-5" aria-hidden />
|
||||||
|
)}
|
||||||
</button>
|
</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
|
|
||||||
open={copiedKind === "telegram"}
|
|
||||||
onOpenChange={(open) => {
|
|
||||||
if (!open) setCopiedKind(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
<button
|
||||||
type="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]"
|
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) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
void handleCopyTelegram(e);
|
void handleCopyTelegram(e);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{copiedKind === "telegram" ? (
|
||||||
|
<Check className="size-5" aria-hidden />
|
||||||
|
) : (
|
||||||
<Copy className="size-5" aria-hidden />
|
<Copy className="size-5" aria-hidden />
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent side="top" sideOffset={8}>
|
|
||||||
{t("contact.copied")}
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user