Skip to content

Commit

Permalink
clean
Browse files Browse the repository at this point in the history
  • Loading branch information
cpcramer committed Aug 2, 2024
1 parent c1ca1ea commit ecb628d
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 34 deletions.
121 changes: 100 additions & 21 deletions src/wallet/components/Wallet.test.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,119 @@
import '@testing-library/jest-dom';
import { render, screen, waitFor } from '@testing-library/react';
import { useAccount, useConnect, useDisconnect } from 'wagmi';
import { fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { ConnectWallet } from './ConnectWallet';
import { Wallet } from './Wallet';
import { WalletDropdown } from './WalletDropdown';
import { useWalletContext } from './WalletProvider';

vi.mock('wagmi', () => ({
useAccount: vi.fn(),
useConnect: vi.fn(),
useDisconnect: vi.fn(),
vi.mock('./WalletProvider', () => ({
useWalletContext: vi.fn(),
WalletProvider: ({ children }) => <>{children}</>,
}));

vi.mock('./ConnectWallet', () => ({
ConnectWallet: () => <div data-testid="connect-wallet">Connect Wallet</div>,
}));

vi.mock('./WalletDropdown', () => ({
WalletDropdown: () => (
<div data-testid="wallet-dropdown">Wallet Dropdown</div>
),
}));

describe('Wallet Component', () => {
let mockSetIsOpen: ReturnType<typeof vi.fn>;

beforeEach(() => {
vi.clearAllMocks();
(useAccount as vi.Mock).mockReturnValue({ status: 'disconnected' });
(useConnect as vi.Mock).mockReturnValue({
connectors: [{ name: 'injected' }],
connect: vi.fn(),
mockSetIsOpen = vi.fn();
(useWalletContext as ReturnType<typeof vi.fn>).mockReturnValue({
isOpen: false,
setIsOpen: mockSetIsOpen,
});
(useDisconnect as vi.Mock).mockReturnValue({ disconnect: vi.fn() });
});

it('should render the Wallet component with ConnectWallet', async () => {
it('should render the Wallet component with ConnectWallet', () => {
render(
<Wallet>
<ConnectWallet />
<WalletDropdown>
<div />
</WalletDropdown>
<WalletDropdown />
</Wallet>,
);
await waitFor(() => {
expect(
screen.getByTestId('ockConnectWallet_Container'),
).toBeInTheDocument();

expect(screen.getByTestId('connect-wallet')).toBeDefined();
expect(screen.queryByTestId('wallet-dropdown')).toBeNull();
});

it('should close the wallet when clicking outside', () => {
(useWalletContext as ReturnType<typeof vi.fn>).mockReturnValue({
isOpen: true,
setIsOpen: mockSetIsOpen,
});

render(
<Wallet>
<ConnectWallet />
<WalletDropdown />
</Wallet>,
);

expect(screen.getByTestId('wallet-dropdown')).toBeDefined();

fireEvent.click(document.body);

expect(mockSetIsOpen).toHaveBeenCalledWith(false);
});

it('should not close the wallet when clicking inside', () => {
(useWalletContext as ReturnType<typeof vi.fn>).mockReturnValue({
isOpen: true,
setIsOpen: mockSetIsOpen,
});

render(
<Wallet>
<ConnectWallet />
<WalletDropdown />
</Wallet>,
);

const walletDropdown = screen.getByTestId('wallet-dropdown');
expect(walletDropdown).toBeDefined();

fireEvent.click(walletDropdown);

expect(mockSetIsOpen).not.toHaveBeenCalled();
});

it('should not trigger click handler when wallet is closed', () => {
render(
<Wallet>
<ConnectWallet />
<WalletDropdown />
</Wallet>,
);

expect(screen.queryByTestId('wallet-dropdown')).toBeNull();

fireEvent.click(document.body);

expect(mockSetIsOpen).not.toHaveBeenCalled();
});

it('should remove event listener on unmount', () => {
const removeEventListenerSpy = vi.spyOn(document, 'removeEventListener');

const { unmount } = render(
<Wallet>
<ConnectWallet />
<WalletDropdown />
</Wallet>,
);

unmount();

expect(removeEventListenerSpy).toHaveBeenCalledWith(
'click',
expect.any(Function),
);
});
});
32 changes: 19 additions & 13 deletions src/wallet/components/Wallet.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,52 @@
import { Children, useMemo, useRef, useEffect } from 'react';
import { Children, useEffect, useMemo, useRef } from 'react';
import type { WalletReact } from '../types';
import { ConnectWallet } from './ConnectWallet';
import { WalletDropdown } from './WalletDropdown';
import { WalletProvider, useWalletContext } from './WalletProvider';

function WalletContent({ children }: WalletReact) {
const WalletContent = ({ children }: WalletReact) => {
const { isOpen, setIsOpen } = useWalletContext();
const containerRef = useRef<HTMLDivElement>(null);
const walletContainerRef = useRef<HTMLDivElement>(null);

const { connect, dropdown } = useMemo(() => {
const childrenArray = Children.toArray(children);
return {
// @ts-ignore
connect: childrenArray.filter(({ type }) => type === ConnectWallet),
// @ts-ignore
dropdown: childrenArray.filter(({ type }) => type === WalletDropdown),
};
}, [children]);

// Handle clicking outside the wallet component to close the dropdown.
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node) && isOpen) {
const handleClickOutsideComponent = (event: MouseEvent) => {
if (
walletContainerRef.current &&
!walletContainerRef.current.contains(event.target as Node) &&
isOpen
) {
setIsOpen(false);
}
};

document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
document.addEventListener('click', handleClickOutsideComponent);
return () =>
document.removeEventListener('click', handleClickOutsideComponent);
}, [isOpen, setIsOpen]);

return (
<div ref={containerRef} className="relative w-fit shrink-0">
<div ref={walletContainerRef} className="relative w-fit shrink-0">
{connect}
{isOpen && dropdown}
</div>
);
}
};

export function Wallet({ children }: WalletReact) {
export const Wallet = ({ children }: WalletReact) => {
return (
<WalletProvider>
<WalletContent>{children}</WalletContent>
</WalletProvider>
);
}
};

0 comments on commit ecb628d

Please sign in to comment.