From db17cab0278578a2a32a8b8a56921f9308246823 Mon Sep 17 00:00:00 2001
From: Dexploarer <211557447+Dexploarer@users.noreply.github.com>
Date: Fri, 27 Feb 2026 02:08:23 +0000
Subject: [PATCH] feat(app): add feedback to wallet address copy buttons
- Created `CopyButton` component in `apps/app/src/components/shared/CopyButton.tsx` with visual feedback (checkmark and "copied" text).
- Updated `Header.tsx` to use `CopyButton` for EVM and Solana addresses.
- Updated `tsconfig.json` to include `bun-types` for better local testing compatibility.
---
apps/app/src/components/Header.test.tsx | 25 +++++-----
apps/app/src/components/Header.tsx | 36 ++++---------
apps/app/src/components/shared/CopyButton.tsx | 50 +++++++++++++++++++
apps/app/tsconfig.json | 4 +-
4 files changed, 74 insertions(+), 41 deletions(-)
create mode 100644 apps/app/src/components/shared/CopyButton.tsx
diff --git a/apps/app/src/components/Header.test.tsx b/apps/app/src/components/Header.test.tsx
index 0b3d664da..1bfa8bdb6 100644
--- a/apps/app/src/components/Header.test.tsx
+++ b/apps/app/src/components/Header.test.tsx
@@ -36,25 +36,16 @@ describe("Header", () => {
lifecycleAction: null,
handlePauseResume: vi.fn(),
handleRestart: vi.fn(),
- copyToClipboard: vi.fn(),
setTab: vi.fn(),
dropStatus: null,
loadDropStatus: vi.fn(),
registryStatus: null,
+ copyToClipboard: vi.fn(), // Needed for CopyButton
};
// @ts-expect-error - test uses a narrowed subset of the full app context type.
vi.spyOn(AppContext, "useApp").mockReturnValue(mockUseApp);
- // We need to render the component.
- // Note: Since we are in a non-browser environment (happy-dom/jsdom might not be set up fully for standard React testing library in this repo's specific config),
- // we will check if we can use react-test-renderer or if we should rely on a basic snapshot/class check.
- // However, the user's package.json includes "react-test-renderer".
- // Let's try react-test-renderer first as it avoids DOM emulation issues if not configured.
-
- // Actually, let's stick to the plan of using what's available.
- // The previous check showed "react-test-renderer": "^19.0.0".
-
let testRenderer: ReactTestRenderer | null = null;
await act(async () => {
testRenderer = create();
@@ -68,7 +59,6 @@ describe("Header", () => {
node.props.className.includes(className);
// Find the wallet wrapper
- // It has className "wallet-wrapper relative inline-flex shrink-0 group"
const walletWrapper = root.findAll((node: ReactTestInstance) =>
hasClass(node, "wallet-wrapper"),
);
@@ -77,12 +67,23 @@ describe("Header", () => {
expect(walletWrapper[0].props.className).toContain("group");
// Find the wallet tooltip
- // It should have className containing "group-hover:block"
const walletTooltip = root.findAll((node: ReactTestInstance) =>
hasClass(node, "wallet-tooltip"),
);
expect(walletTooltip.length).toBe(1);
expect(walletTooltip[0].props.className).toContain("group-hover:block");
+
+ // Verify CopyButtons are rendered
+ const copyButtons = root.findAll((node: ReactTestInstance) => {
+ return (
+ node.type === "button" &&
+ node.props["aria-label"] &&
+ node.props["aria-label"].startsWith("Copy")
+ );
+ });
+
+ // Should find 2 copy buttons (one for EVM, one for SOL)
+ expect(copyButtons.length).toBe(2);
});
});
diff --git a/apps/app/src/components/Header.tsx b/apps/app/src/components/Header.tsx
index 0b91b2880..e6cd3da5f 100644
--- a/apps/app/src/components/Header.tsx
+++ b/apps/app/src/components/Header.tsx
@@ -10,6 +10,7 @@ import {
import { useEffect } from "react";
import { useApp } from "../AppContext";
import { useBugReport } from "../hooks/useBugReport";
+import { CopyButton } from "./shared/CopyButton";
export function Header() {
const {
@@ -25,7 +26,6 @@ export function Header() {
lifecycleAction,
handlePauseResume,
handleRestart,
- copyToClipboard,
setTab,
dropStatus,
loadDropStatus,
@@ -204,19 +204,10 @@ export function Header() {
{evmShort}
-
+
)}
{solShort && (
@@ -227,19 +218,10 @@ export function Header() {
{solShort}
-
+
)}
diff --git a/apps/app/src/components/shared/CopyButton.tsx b/apps/app/src/components/shared/CopyButton.tsx
new file mode 100644
index 000000000..a2e5ddbd0
--- /dev/null
+++ b/apps/app/src/components/shared/CopyButton.tsx
@@ -0,0 +1,50 @@
+import { Check, Copy } from "lucide-react";
+import { useState } from "react";
+import { useApp } from "../../AppContext";
+
+interface CopyButtonProps {
+ value: string;
+ label?: string;
+ className?: string;
+}
+
+export function CopyButton({
+ value,
+ label = "copy",
+ className = "",
+}: CopyButtonProps) {
+ const { copyToClipboard } = useApp();
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = async (e: React.MouseEvent) => {
+ e.stopPropagation();
+ await copyToClipboard(value);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
+
+ return (
+
+ );
+}
diff --git a/apps/app/tsconfig.json b/apps/app/tsconfig.json
index f24fec7c4..fe6f9b48d 100644
--- a/apps/app/tsconfig.json
+++ b/apps/app/tsconfig.json
@@ -18,8 +18,8 @@
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"useUnknownInCatchVariables": true,
- "types": ["vite/client"]
+ "types": ["vite/client", "bun-types"]
},
- "include": ["src/**/*.ts", "src/**/*.tsx"],
+ "include": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts", "test/**/*.tsx"],
"exclude": ["node_modules", "dist", "ios", "android", "electron"]
}