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 (