Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/__tests__/useEscapeKey.test.tsx
Original file line number Diff line number Diff line change
@@ -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 <div data-testid="test-modal">Modal Content</div>;
};

describe('useEscapeKey Hook', () => {
it('triggers onEscape callback when Escape key is pressed', () => {
const handleEscape = vi.fn();
render(<TestComponent onEscape={handleEscape} enabled={true} />);

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(<TestComponent onEscape={handleEscape} enabled={true} />);

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(<TestComponent onEscape={handleEscape} enabled={false} />);

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(<TestComponent onEscape={handleEscape} enabled={true} />);

unmount();
fireEvent.keyDown(window, { key: 'Escape', code: 'Escape' });
expect(handleEscape).not.toHaveBeenCalled();
});
});
2 changes: 2 additions & 0 deletions src/components/wallet/WalletModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -21,6 +22,7 @@ export const WalletModal: React.FC<WalletModalProps> = ({
activeWalletId,
isConnecting,
}) => {
useEscapeKey(onClose, isOpen);
if (!isOpen) return null;

return (
Expand Down
25 changes: 25 additions & 0 deletions src/hooks/useEscapeKey.ts
Original file line number Diff line number Diff line change
@@ -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]);
}