From 584fdb52cfab87ab2487609462787b71ee5736f3 Mon Sep 17 00:00:00 2001 From: jaydenkalu Date: Mon, 29 Jun 2026 12:21:09 +0000 Subject: [PATCH] feat: copy feedback and mobile clipboard support on verify page (#476) - copy-button.tsx: add writeToClipboard() with execCommand fallback for mobile browsers that lack navigator.clipboard; add aria-pressed state; add optional onCopied callback; export writeToClipboard for reuse - verify/page.tsx: replace bare navigator.clipboard.writeText with writeToClipboard; push success/error toast on copy; add aria-live region to Share button label for screen-reader feedback - Update snapshots for CopyButton/CopyableText (aria-pressed added) Closes #476 --- .../__snapshots__/snapshot.test.tsx.snap | 44 ++++++++++++++++--- apps/web/src/app/verify/page.tsx | 21 ++++++--- apps/web/src/components/copy-button.tsx | 32 ++++++++++++-- 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap b/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap index a21eb9a..198b065 100644 --- a/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap +++ b/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap @@ -3,6 +3,7 @@ exports[`Copy components snapshots > CopyButton renders correctly 1`] = ` diff --git a/apps/web/src/components/copy-button.tsx b/apps/web/src/components/copy-button.tsx index ff4af53..52c5657 100644 --- a/apps/web/src/components/copy-button.tsx +++ b/apps/web/src/components/copy-button.tsx @@ -8,18 +8,38 @@ interface CopyButtonProps { label?: string className?: string iconSize?: number + onCopied?: () => void } -export function CopyButton({ value, label, className = '', iconSize = 14 }: CopyButtonProps) { +/** Write text to clipboard with a mobile-safe fallback via a temporary textarea. */ +async function writeToClipboard(text: string): Promise { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(text) + return + } + // Fallback for mobile browsers that don't support navigator.clipboard + const el = document.createElement('textarea') + el.value = text + el.style.cssText = 'position:fixed;top:0;left:0;opacity:0' + document.body.appendChild(el) + el.focus() + el.select() + const ok = document.execCommand('copy') + document.body.removeChild(el) + if (!ok) throw new Error('copy failed') +} + +export function CopyButton({ value, label, className = '', iconSize = 14, onCopied }: CopyButtonProps) { const [copied, setCopied] = useState(false) async function handleCopy() { try { - await navigator.clipboard.writeText(value) + await writeToClipboard(value) setCopied(true) + onCopied?.() setTimeout(() => setCopied(false), 2000) - } catch (err) { - console.error('Failed to copy:', err) + } catch { + // Surface nothing to the user visually — caller can supply onCopied for toast } } @@ -27,6 +47,7 @@ export function CopyButton({ value, label, className = '', iconSize = 14 }: Copy