diff --git a/src/__tests__/useEscapeKey.test.tsx b/src/__tests__/useEscapeKey.test.tsx
new file mode 100644
index 0000000..05da9ec
--- /dev/null
+++ b/src/__tests__/useEscapeKey.test.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+import { describe, it, expect, vi } from 'vitest';
+import { render, fireEvent } from '@testing-library/react';
+import { useEscapeKey } from '../hooks/useEscapeKey';
+
+const TestComponent = ({ onEscape, enabled = true }: { onEscape: () => void; enabled?: boolean }) => {
+ useEscapeKey(onEscape, enabled);
+ return
Modal Content
;
+};
+
+describe('useEscapeKey Hook', () => {
+ it('triggers onEscape callback when Escape key is pressed', () => {
+ const handleEscape = vi.fn();
+ render();
+
+ fireEvent.keyDown(window, { key: 'Escape', code: 'Escape' });
+ expect(handleEscape).toHaveBeenCalledTimes(1);
+ });
+
+ it('does not trigger onEscape when another key is pressed', () => {
+ const handleEscape = vi.fn();
+ render();
+
+ fireEvent.keyDown(window, { key: 'Enter', code: 'Enter' });
+ fireEvent.keyDown(window, { key: 'Tab', code: 'Tab' });
+ expect(handleEscape).not.toHaveBeenCalled();
+ });
+
+ it('does not trigger onEscape when disabled', () => {
+ const handleEscape = vi.fn();
+ render();
+
+ fireEvent.keyDown(window, { key: 'Escape', code: 'Escape' });
+ expect(handleEscape).not.toHaveBeenCalled();
+ });
+
+ it('cleans up event listener on unmount', () => {
+ const handleEscape = vi.fn();
+ const { unmount } = render();
+
+ unmount();
+ fireEvent.keyDown(window, { key: 'Escape', code: 'Escape' });
+ expect(handleEscape).not.toHaveBeenCalled();
+ });
+});
\ No newline at end of file
diff --git a/src/components/wallet/WalletModal.tsx b/src/components/wallet/WalletModal.tsx
index 820a6ae..165a51d 100644
--- a/src/components/wallet/WalletModal.tsx
+++ b/src/components/wallet/WalletModal.tsx
@@ -3,6 +3,7 @@
import React from 'react';
import { X, ExternalLink, CheckCircle, ShieldCheck } from 'lucide-react';
import { WalletOption, WalletProviderId } from '../../types/stellar';
+import { useEscapeKey } from '../../hooks/useEscapeKey';
interface WalletModalProps {
isOpen: boolean;
@@ -21,6 +22,7 @@ export const WalletModal: React.FC = ({
activeWalletId,
isConnecting,
}) => {
+ useEscapeKey(onClose, isOpen);
if (!isOpen) return null;
return (
diff --git a/src/hooks/useEscapeKey.ts b/src/hooks/useEscapeKey.ts
new file mode 100644
index 0000000..d50c3f2
--- /dev/null
+++ b/src/hooks/useEscapeKey.ts
@@ -0,0 +1,25 @@
+'use client';
+
+import { useEffect } from 'react';
+
+/**
+ * Custom hook to execute a callback when the Escape key is pressed.
+ * @param onEscape Callback to invoke upon pressing Escape
+ * @param enabled Whether the listener is active (default true)
+ */
+export function useEscapeKey(onEscape: () => void, enabled: boolean = true): void {
+ useEffect(() => {
+ if (!enabled) return;
+
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === 'Escape' || event.key === 'Esc') {
+ onEscape();
+ }
+ };
+
+ window.addEventListener('keydown', handleKeyDown);
+ return () => {
+ window.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [onEscape, enabled]);
+}
\ No newline at end of file