The WalletConnectButton component provides a unified control for users to connect and manage their crypto wallet session within the TalentTrust application.
src/components/WalletConnectButton.tsx
import { WalletConnectButton } from '@/components/WalletConnectButton';
export function Header() {
return (
<header>
<WalletConnectButton />
</header>
);
}- Global State Integration: Uses
useWalletcontext to ensure the connection state is shared across the app, such as gating actions inActionPanel. - Three rendered branches:
- Disconnected / connecting: Renders the primary "Connect Wallet" button and swaps to a disabled spinner state with "Connecting..." while
isConnectingis true. - Error: Renders a "Connection Error" banner with a retry control labeled
Retry wallet connection. - Connected: Renders the truncated wallet address plus icon-only controls for copy and disconnect, labeled
Copy address to clipboardandDisconnect wallet.
- Disconnected / connecting: Renders the primary "Connect Wallet" button and swaps to a disabled spinner state with "Connecting..." while
- Clipboard Copy (hardened): See details in the section below.
- Accessibility: Fully accessible with ARIA labels, semantic HTML, and proper focus states. All interactive elements are keyboard operable.
- Responsiveness: Works across mobile and desktop viewpoints.
The copy button uses the Clipboard API (navigator.clipboard.writeText). The implementation is hardened against the following failure modes:
| Scenario | Behaviour |
|---|---|
| Successful write | Checkmark icon is shown; reverts to copy icon after 2 s. |
navigator.clipboard is undefined (insecure context, old browser) |
Error toast: "Copy not supported" — no checkmark shown. |
navigator.clipboard.writeText is absent |
Error toast: "Copy not supported" — no checkmark shown. |
writeText promise rejects (e.g. permission denied) |
Error toast: "Copy failed" — no checkmark shown. |
Security note: The wallet address is never written to console.error, console.warn, or any other log on failure. It is treated as sensitive user data.
Error notifications use the application's showError method from useToast. They appear as dismissible toasts in the top-right corner and are announced by a role="alert" live region for screen reader users.
@/contexts/WalletContext— providesaddress,connect,disconnect,isConnecting,error.@/components/toast/toast-provider— providesuseToast/showErrorfor clipboard failure notifications.@/lib/truncateAddress— derives the connected-state address pill text shown to the user.- Inline SVGs for icons (no external icon library dependency).
Tested with Jest and React Testing Library in src/components/__tests__/WalletConnectButton.test.tsx and src/hooks/__tests__/useCopyToClipboard.test.ts.
Coverage targets ≥ 95% for this module and includes the following scenarios:
- Disconnected branch: renders the connect button with the expected accessible name and calls
connect. - Connecting branch: disables the connect button and renders the loading spinner without leaking connected or error controls.
- Error branch: renders the retry control with the expected accessible name and calls
connectagain on retry. - Connected branch: verifies the address display comes from
truncateAddress, exposes the copy and disconnect controls, and callsdisconnect. - Copy success flow: mocks
navigator.clipboard.writeText, verifies the full address is copied, swaps to the success icon, and resets back after 2 seconds with fake timers. - Clipboard write rejection: when
writeTextrejects (e.g., permission denied), an error toast is shown instead of a success checkmark. - Missing clipboard API: when
navigator.clipboardisundefined(insecure context, old browser), the appropriate error toast is displayed without attempting to write. - Rapid consecutive clicks: handles rapid copy button clicks by cleaning up the previous timer and starting a new 2-second reset, ensuring only the final action's result persists.
- Unmount safety: cleans up timers on component unmount to prevent state updates on unmounted components and avoid memory leaks.
- No-op when address is missing: does not attempt clipboard writes if the address is not available.
- Accessibility: runs
jest-axeagainst the connected state and failure states viasrc/test-utils/a11y.tsxand expects zero violations.
- Successful copy: verifies
copiedstate is set totrue, callbacks are invoked, and the state resets after the configured delay. - Custom delay: tests that non-default delay values are respected.
- Missing clipboard API: gracefully returns
falsewithout throwing when clipboard is unavailable. - Missing writeText method: gracefully handles
clipboardobject existing but lackingwriteText. - Promise rejection: catches
writeTextpromise rejections and invokes the error callback. - Timer cleanup on new copy: ensures that triggering a new copy operation cleans up the previous reset timer, preventing race conditions.
- Unmount cleanup: cleans up timers when the hook unmounts to prevent memory leaks and state updates on unmounted components.
- SSR safety: guards against SSR environments where
windowornavigatorisundefined.