From e999051281bec1a75203f042535cb9387e047913 Mon Sep 17 00:00:00 2001 From: "Ibrahim Bamidele. S" <238790935+bamiebot-maker@users.noreply.github.com> Date: Sun, 18 Oct 2026 01:27:57 +0100 Subject: [PATCH 1/5] fix(wallet): prevent click bubbling and add responsive address truncation in WalletConnectButton (#887) --- src/components/NetworkSwitcher.tsx | 8 +- src/components/WalletConnectButton.test.tsx | 97 +++++++++++++++++++++ src/components/WalletConnectButton.tsx | 46 ++++++++-- src/lib/utils.test.ts | 28 ++++++ src/lib/utils.ts | 17 ++++ 5 files changed, 185 insertions(+), 11 deletions(-) diff --git a/src/components/NetworkSwitcher.tsx b/src/components/NetworkSwitcher.tsx index 5cc70d5d..e88bde00 100644 --- a/src/components/NetworkSwitcher.tsx +++ b/src/components/NetworkSwitcher.tsx @@ -152,15 +152,19 @@ export async function checkRpcHealth( } } catch (err) { if (timedOut) { - throw new Error( + const error = new Error( `RPC endpoint did not respond within ${Math.round(timeoutMs / 1000)}s.`, ); + (error as Error & { cause?: unknown }).cause = err; + throw error; } if (isAbortError(err)) throw err; if (err instanceof TypeError) { - throw new Error( + const error = new Error( "Could not reach the RPC endpoint. It may be offline or may not allow requests from this origin (CORS).", ); + (error as Error & { cause?: unknown }).cause = err; + throw error; } throw err instanceof Error ? err : new Error("RPC health check failed."); } finally { diff --git a/src/components/WalletConnectButton.test.tsx b/src/components/WalletConnectButton.test.tsx index f5265c6c..13be0bfe 100644 --- a/src/components/WalletConnectButton.test.tsx +++ b/src/components/WalletConnectButton.test.tsx @@ -395,5 +395,102 @@ describe("WalletConnectButton", () => { "Connection rejected by user", ); }); + + it("stops click event bubbling to parent containers when clicked in disconnected state (#887)", () => { + vi.mocked(useSorokit).mockReturnValue( + mockUseSorokit({ + isConnected: false, + }), + ); + + const parentClick = vi.fn(); + render( +
+ +
, + ); + + fireEvent.click(screen.getByRole("button", { name: "Connect Wallet" })); + expect(parentClick).not.toHaveBeenCalled(); + }); + + it("stops click event bubbling to parent containers when clicked in connected state (#887)", () => { + vi.mocked(useSorokit).mockReturnValue( + mockUseSorokit({ + isConnected: true, + address: "GABC1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ", + }), + ); + + const parentClick = vi.fn(); + render( +
+ +
, + ); + + fireEvent.click( + screen.getByRole("button", { name: /wallet connected/i }), + ); + expect(parentClick).not.toHaveBeenCalled(); + }); + + it("stops click event bubbling when onOpenModal is provided (#887)", () => { + const mockOnOpen = vi.fn(); + vi.mocked(useSorokit).mockReturnValue( + mockUseSorokit({ + isConnected: true, + address: "GABC1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ", + }), + ); + + const parentClick = vi.fn(); + render( +
+ +
, + ); + + fireEvent.click( + screen.getByRole("button", { name: /wallet connected/i }), + ); + expect(mockOnOpen).toHaveBeenCalledTimes(1); + expect(parentClick).not.toHaveBeenCalled(); + }); + + it("has accessible attributes aria-haspopup='dialog' and aria-expanded when disconnected (#887)", () => { + vi.mocked(useSorokit).mockReturnValue( + mockUseSorokit({ + isConnected: false, + }), + ); + + render(); + const btn = screen.getByRole("button", { name: "Connect Wallet" }); + expect(btn).toHaveAttribute("aria-haspopup", "dialog"); + expect(btn).toHaveAttribute("aria-expanded", "false"); + + fireEvent.click(btn); + expect(btn).toHaveAttribute("aria-expanded", "true"); + }); + + it("renders compact address on mobile (<360px) and standard address on larger viewports (#887)", () => { + const fullAddr = "GABC1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + vi.mocked(useSorokit).mockReturnValue( + mockUseSorokit({ + isConnected: true, + address: fullAddr, + }), + ); + + const { container } = render(); + const compactSpan = container.querySelector("[data-address-compact]"); + const standardSpan = container.querySelector("[data-address]"); + + expect(compactSpan).toBeInTheDocument(); + expect(compactSpan).toHaveTextContent("GAB...XYZ"); + expect(standardSpan).toBeInTheDocument(); + expect(standardSpan).toHaveTextContent("GABC12...WXYZ"); + }); }); }); diff --git a/src/components/WalletConnectButton.tsx b/src/components/WalletConnectButton.tsx index 4db6918e..b192beb2 100644 --- a/src/components/WalletConnectButton.tsx +++ b/src/components/WalletConnectButton.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from "react"; import { Button } from "@/components/ui/Button"; import { useSorokit } from "@/context/useSorokit"; import { useOutsideClick } from "@/hooks/useOutsideClick"; -import { truncateAddress } from "@/lib/utils"; +import { truncateAddress, truncateAddressResponsive } from "@/lib/utils"; import { WalletConnectModal } from "./WalletConnectModal"; @@ -88,18 +88,29 @@ export function WalletConnectButton({ onOpenModal }: WalletConnectButtonProps = return (