diff --git a/src/transaction/components/TransactionToastAction.test.tsx b/src/transaction/components/TransactionToastAction.test.tsx
index 61924ab518..17c125327e 100644
--- a/src/transaction/components/TransactionToastAction.test.tsx
+++ b/src/transaction/components/TransactionToastAction.test.tsx
@@ -1,15 +1,15 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
-import { useGetTransactionToast } from '../hooks/useGetTransactionToast';
+import { useGetTransactionToastAction } from '../hooks/useGetTransactionToastAction';
import { TransactionToastAction } from './TransactionToastAction';
-vi.mock('../hooks/useGetTransactionToast', () => ({
- useGetTransactionToast: vi.fn(),
+vi.mock('../hooks/useGetTransactionToastAction', () => ({
+ useGetTransactionToastAction: vi.fn(),
}));
describe('TransactionToastAction', () => {
it('renders transaction status action', () => {
- (useGetTransactionToast as vi.Mock).mockReturnValue({
+ (useGetTransactionToastAction as vi.Mock).mockReturnValue({
actionElement:
,
});
diff --git a/src/transaction/components/TransactionToastAction.tsx b/src/transaction/components/TransactionToastAction.tsx
index d51ae55e13..0a4b64c1b4 100644
--- a/src/transaction/components/TransactionToastAction.tsx
+++ b/src/transaction/components/TransactionToastAction.tsx
@@ -1,11 +1,11 @@
import { cn, text } from '../../styles/theme';
-import { useGetTransactionToast } from '../hooks/useGetTransactionToast';
+import { useGetTransactionToastAction } from '../hooks/useGetTransactionToastAction';
import type { TransactionToastActionReact } from '../types';
export function TransactionToastAction({
className,
}: TransactionToastActionReact) {
- const { actionElement } = useGetTransactionToast();
+ const { actionElement } = useGetTransactionToastAction();
return (
diff --git a/src/transaction/components/TransactionToastIcon.test.tsx b/src/transaction/components/TransactionToastIcon.test.tsx
index 257776fd41..d8de66c253 100644
--- a/src/transaction/components/TransactionToastIcon.test.tsx
+++ b/src/transaction/components/TransactionToastIcon.test.tsx
@@ -1,21 +1,53 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
-import { useGetTransactionToast } from '../hooks/useGetTransactionToast';
+import { useTransactionContext } from '../components/TransactionProvider';
import { TransactionToastIcon } from './TransactionToastIcon';
-vi.mock('../hooks/useGetTransactionToast', () => ({
- useGetTransactionToast: vi.fn(),
+vi.mock('../components/TransactionProvider', () => ({
+ useTransactionContext: vi.fn(),
}));
describe('TransactionToastIcon', () => {
- it('renders transaction toast icon', () => {
- (useGetTransactionToast as vi.Mock).mockReturnValue({
- icon:
icon
,
+ it('renders success icon when receipt exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ receipt: '123',
});
render(
);
- const iconElement = screen.getByText('icon');
+ const iconElement = screen.getByTestId('ockSuccessSvg');
expect(iconElement).toBeInTheDocument();
});
+ it('renders error icon when error exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: 'error',
+ });
+
+ render(
);
+
+ const iconElement = screen.getByTestId('ockErrorSvg');
+ expect(iconElement).toBeInTheDocument();
+ });
+ it('renders loading icon when txn is in progress', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ isLoading: true,
+ });
+
+ render(
);
+
+ const iconElement = screen.getByTestId('ockSpinner');
+ expect(iconElement).toBeInTheDocument();
+ });
+ it('renders null when if no status exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ isLoading: false,
+ });
+
+ const { container } = render(
+
,
+ );
+
+ // Assert that nothing is rendered (container should be empty)
+ expect(container.firstChild).toBeNull();
+ });
});
diff --git a/src/transaction/components/TransactionToastIcon.tsx b/src/transaction/components/TransactionToastIcon.tsx
index 5401adc107..19915e01e8 100644
--- a/src/transaction/components/TransactionToastIcon.tsx
+++ b/src/transaction/components/TransactionToastIcon.tsx
@@ -1,8 +1,33 @@
+import { useMemo } from 'react';
+import { Spinner } from '../../internal/components/Spinner';
+import { errorSvg } from '../../internal/svg/errorSvg';
+import { successSvg } from '../../internal/svg/successSvg';
import { cn, text } from '../../styles/theme';
-import { useGetTransactionToast } from '../hooks/useGetTransactionToast';
import type { TransactionToastIconReact } from '../types';
+import { useTransactionContext } from './TransactionProvider';
export function TransactionToastIcon({ className }: TransactionToastIconReact) {
- const { icon } = useGetTransactionToast();
+ const { errorMessage, isLoading, receipt, transactionHash, transactionId } =
+ useTransactionContext();
+ const isInProgress = isLoading || !!transactionId || !!transactionHash;
+
+ const icon = useMemo(() => {
+ // txn successful
+ if (receipt) {
+ return successSvg;
+ }
+ if (errorMessage) {
+ return errorSvg;
+ }
+ if (isInProgress) {
+ return
;
+ }
+ return null;
+ }, [isInProgress, errorMessage, receipt]);
+
+ if (!icon) {
+ return null;
+ }
+
return
{icon}
;
}
diff --git a/src/transaction/components/TransactionToastLabel.test.tsx b/src/transaction/components/TransactionToastLabel.test.tsx
index 068327cc84..1ecea3873a 100644
--- a/src/transaction/components/TransactionToastLabel.test.tsx
+++ b/src/transaction/components/TransactionToastLabel.test.tsx
@@ -1,15 +1,15 @@
import { render, screen } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
-import { useGetTransactionToast } from '../hooks/useGetTransactionToast';
+import { useGetTransactionToastLabel } from '../hooks/useGetTransactionToastLabel';
import { TransactionToastLabel } from './TransactionToastLabel';
-vi.mock('../hooks/useGetTransactionToast', () => ({
- useGetTransactionToast: vi.fn(),
+vi.mock('../hooks/useGetTransactionToastLabel', () => ({
+ useGetTransactionToastLabel: vi.fn(),
}));
describe('TransactionToastLabel', () => {
it('renders transaction status action', () => {
- (useGetTransactionToast as vi.Mock).mockReturnValue({
+ (useGetTransactionToastLabel as vi.Mock).mockReturnValue({
label: 'Successful',
});
diff --git a/src/transaction/components/TransactionToastLabel.tsx b/src/transaction/components/TransactionToastLabel.tsx
index d1b2c69699..dfba18e5e9 100644
--- a/src/transaction/components/TransactionToastLabel.tsx
+++ b/src/transaction/components/TransactionToastLabel.tsx
@@ -1,11 +1,11 @@
import { cn, color, text } from '../../styles/theme';
-import { useGetTransactionToast } from '../hooks/useGetTransactionToast';
+import { useGetTransactionToastLabel } from '../hooks/useGetTransactionToastLabel';
import type { TransactionToastLabelReact } from '../types';
export function TransactionToastLabel({
className,
}: TransactionToastLabelReact) {
- const { label } = useGetTransactionToast();
+ const { label } = useGetTransactionToastLabel();
return (
{label}
diff --git a/src/transaction/hooks/useGetTransactionStatus.test.tsx b/src/transaction/hooks/useGetTransactionStatus.test.tsx
deleted file mode 100644
index ea5abf9b82..0000000000
--- a/src/transaction/hooks/useGetTransactionStatus.test.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { renderHook } from '@testing-library/react';
-import { beforeEach, describe, expect, it, vi } from 'vitest';
-import { useChainId } from 'wagmi';
-import { useTransactionContext } from '../components/TransactionProvider';
-import { useGetTransactionStatus } from './useGetTransactionStatus';
-
-vi.mock('../components/TransactionProvider', () => ({
- useTransactionContext: vi.fn(),
-}));
-
-vi.mock('wagmi', () => ({
- useChainId: vi.fn(),
-}));
-
-describe('useGetTransactionStatus', () => {
- beforeEach(() => {
- (useChainId as vi.Mock).mockReturnValue(123);
- });
- it('should return correct status and actionElement when transaction is pending', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- statusWriteContract: 'pending',
- });
- const { result } = renderHook(() => useGetTransactionStatus());
- expect(result.current.label).toBe('Confirm in wallet.');
- });
-
- it('should return correct status and actionElement when transaction hash exists', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- transactionHash: 'ab123',
- });
- const { result } = renderHook(() => useGetTransactionStatus());
- expect(result.current.label).toBe('Transaction in progress...');
- expect(result.current.actionElement).not.toBeNull();
- });
-
- it('should return correct status and actionElement when receipt exists', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- receipt: 'receipt',
- transactionHash: '123',
- });
- const { result } = renderHook(() => useGetTransactionStatus());
- expect(result.current.label).toBe('Successful!');
- expect(result.current.actionElement).not.toBeNull();
- });
-
- it('should return correct status and actionElement when error occurs', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- errorMessage: 'error',
- });
- const { result } = renderHook(() => useGetTransactionStatus());
- expect(result.current.label).toBe('error');
- expect(result.current.labelClassName).toBe('text-ock-error');
- });
-
- it('should return correct status and actionElement when no status available', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- errorMessage: '',
- });
- const { result } = renderHook(() => useGetTransactionStatus());
- expect(result.current.label).toBe('');
- expect(result.current.actionElement).toBeNull();
- });
-});
diff --git a/src/transaction/hooks/useGetTransactionStatusAction.test.tsx b/src/transaction/hooks/useGetTransactionStatusAction.test.tsx
new file mode 100644
index 0000000000..17ce2a069b
--- /dev/null
+++ b/src/transaction/hooks/useGetTransactionStatusAction.test.tsx
@@ -0,0 +1,119 @@
+import { renderHook } from '@testing-library/react';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { useChainId } from 'wagmi';
+import { useShowCallsStatus } from 'wagmi/experimental';
+import { getChainExplorer } from '../../network/getChainExplorer';
+import { useTransactionContext } from '../components/TransactionProvider';
+import { useGetTransactionStatusAction } from './useGetTransactionStatusAction';
+
+vi.mock('../components/TransactionProvider', () => ({
+ useTransactionContext: vi.fn(),
+}));
+
+vi.mock('wagmi', () => ({
+ useChainId: vi.fn(),
+}));
+
+vi.mock('wagmi/experimental', () => ({
+ useShowCallsStatus: vi.fn(),
+}));
+
+vi.mock('../../network/getChainExplorer', () => ({
+ getChainExplorer: vi.fn(),
+}));
+
+const mockGetChainExplorer = 'https://etherscan.io';
+
+describe('useGetTransactionStatusAction', () => {
+ beforeEach(() => {
+ (useChainId as vi.Mock).mockReturnValue(123);
+ (useShowCallsStatus as vi.Mock).mockReturnValue({
+ showCallsStatus: vi.fn(),
+ });
+ (getChainExplorer as vi.Mock).mockReturnValue(mockGetChainExplorer);
+ });
+
+ it('should return actionElement when transaction hash exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionHash: '0x123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusAction());
+
+ expect(result.current.actionElement).toMatchInlineSnapshot(`
+
+
+ View transaction
+
+
+ `);
+ });
+
+ it('should return actionElement when transaction id exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionId: 'ab123',
+ onSubmit: vi.fn(),
+ });
+
+ const showCallsStatus = vi.fn();
+ (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus });
+
+ const { result } = renderHook(() => useGetTransactionStatusAction());
+
+ const button = result.current.actionElement as JSX.Element;
+ expect(button.props.onClick).toBeDefined();
+ expect(button).not.toBeNull();
+ });
+
+ it('should return null when receipt exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ receipt: 'receipt',
+ transactionHash: '123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusAction());
+
+ expect(result.current.actionElement).toBeNull();
+ });
+
+ it('should return actionElement when error occurs', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: 'error',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusAction());
+
+ expect(result.current.actionElement).toBeNull();
+ });
+
+ it('should return actionElement when no status available', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: '',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusAction());
+
+ expect(result.current.actionElement).toBeNull();
+ });
+
+ it('should call showCallsStatus when button is clicked', () => {
+ const showCallsStatus = vi.fn();
+ (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus });
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionId: 'ab123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusAction());
+
+ const button = result.current.actionElement as JSX.Element;
+ button.props.onClick();
+
+ expect(showCallsStatus).toHaveBeenCalledWith({ id: 'ab123' });
+ });
+});
diff --git a/src/transaction/hooks/useGetTransactionStatus.tsx b/src/transaction/hooks/useGetTransactionStatusAction.tsx
similarity index 53%
rename from src/transaction/hooks/useGetTransactionStatus.tsx
rename to src/transaction/hooks/useGetTransactionStatusAction.tsx
index b101dcca17..0bb6d72dae 100644
--- a/src/transaction/hooks/useGetTransactionStatus.tsx
+++ b/src/transaction/hooks/useGetTransactionStatusAction.tsx
@@ -1,41 +1,24 @@
import { useMemo } from 'react';
import type { ReactNode } from 'react';
import { useChainId } from 'wagmi';
+import { useShowCallsStatus } from 'wagmi/experimental';
import { getChainExplorer } from '../../network/getChainExplorer';
import { cn, color, text } from '../../styles/theme';
import { useTransactionContext } from '../components/TransactionProvider';
-export function useGetTransactionStatus() {
- const {
- chainId,
- errorMessage,
- isLoading,
- receipt,
- statusWriteContract,
- statusWriteContracts,
- transactionHash,
- transactionId,
- } = useTransactionContext();
+export function useGetTransactionStatusAction() {
+ const { chainId, receipt, transactionHash, transactionId } =
+ useTransactionContext();
const accountChainId = chainId ?? useChainId();
- const isPending =
- statusWriteContract === 'pending' || statusWriteContracts === 'pending';
- const isInProgress = isLoading || !!transactionId || !!transactionHash;
+
+ const { showCallsStatus } = useShowCallsStatus();
return useMemo(() => {
const chainExplorer = getChainExplorer(accountChainId);
let actionElement: ReactNode = null;
- let label = '';
- let labelClassName: string = color.foregroundMuted;
-
- if (isPending) {
- label = 'Confirm in wallet.';
- }
-
- if (isInProgress) {
- label = 'Transaction in progress...';
- }
+ // EOA will have txn hash
if (transactionHash) {
actionElement = (
showCallsStatus({ id: transactionId })}
+ type="button"
+ >
+
+ View transaction
+
+
+ );
}
- if (errorMessage) {
- label = errorMessage;
- labelClassName = color.error;
+ if (receipt) {
+ actionElement = null;
}
- return { actionElement, label, labelClassName };
+ return { actionElement };
}, [
accountChainId,
- errorMessage,
- isInProgress,
- isPending,
receipt,
+ showCallsStatus,
transactionHash,
+ transactionId,
]);
}
diff --git a/src/transaction/hooks/useGetTransactionStatusLabel.test.tsx b/src/transaction/hooks/useGetTransactionStatusLabel.test.tsx
new file mode 100644
index 0000000000..b4ee914e4a
--- /dev/null
+++ b/src/transaction/hooks/useGetTransactionStatusLabel.test.tsx
@@ -0,0 +1,98 @@
+import { renderHook } from '@testing-library/react';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { useChainId } from 'wagmi';
+import { useShowCallsStatus } from 'wagmi/experimental';
+import { getChainExplorer } from '../../network/getChainExplorer';
+import { useTransactionContext } from '../components/TransactionProvider';
+import { useGetTransactionStatusLabel } from './useGetTransactionStatusLabel';
+
+vi.mock('../components/TransactionProvider', () => ({
+ useTransactionContext: vi.fn(),
+}));
+
+vi.mock('wagmi', () => ({
+ useChainId: vi.fn(),
+}));
+
+vi.mock('wagmi/experimental', () => ({
+ useShowCallsStatus: vi.fn(),
+}));
+
+vi.mock('../../network/getChainExplorer', () => ({
+ getChainExplorer: vi.fn(),
+}));
+
+const mockGetChainExplorer = 'https://etherscan.io';
+describe('useGetTransactionStatusLabel', () => {
+ beforeEach(() => {
+ (useChainId as vi.Mock).mockReturnValue(123);
+ (useShowCallsStatus as vi.Mock).mockReturnValue({
+ showCallsStatus: vi.fn(),
+ });
+ (getChainExplorer as vi.Mock).mockReturnValue(mockGetChainExplorer);
+ });
+
+ it('should return correct status when transaction is pending', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ statusWriteContract: 'pending',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusLabel());
+
+ expect(result.current.label).toBe('Confirm in wallet.');
+ });
+ it('should return status when transaction hash exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionHash: '0x123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusLabel());
+
+ expect(result.current.label).toBe('Transaction in progress...');
+ });
+
+ it('should return status when transaction id exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionId: 'ab123',
+ onSubmit: vi.fn(),
+ });
+
+ const showCallsStatus = vi.fn();
+ (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus });
+
+ const { result } = renderHook(() => useGetTransactionStatusLabel());
+
+ expect(result.current.label).toBe('Transaction in progress...');
+ });
+
+ it('should return status when receipt exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ receipt: 'receipt',
+ transactionHash: '123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusLabel());
+
+ expect(result.current.label).toBe('Successful');
+ });
+
+ it('should return status when error occurs', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: 'error',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusLabel());
+
+ expect(result.current.label).toBe('error');
+ });
+
+ it('should return status when no status available', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: '',
+ });
+
+ const { result } = renderHook(() => useGetTransactionStatusLabel());
+
+ expect(result.current.label).toBe('');
+ });
+});
diff --git a/src/transaction/hooks/useGetTransactionStatusLabel.tsx b/src/transaction/hooks/useGetTransactionStatusLabel.tsx
new file mode 100644
index 0000000000..f3b31536ea
--- /dev/null
+++ b/src/transaction/hooks/useGetTransactionStatusLabel.tsx
@@ -0,0 +1,45 @@
+import { useMemo } from 'react';
+import { color } from '../../styles/theme';
+import { useTransactionContext } from '../components/TransactionProvider';
+
+export function useGetTransactionStatusLabel() {
+ const {
+ errorMessage,
+ isLoading,
+ receipt,
+ statusWriteContract,
+ statusWriteContracts,
+ transactionHash,
+ transactionId,
+ } = useTransactionContext();
+ // user confirmed in wallet, txn in progress
+ const isInProgress = isLoading || !!transactionId || !!transactionHash;
+
+ // user started txn and needs to confirm in wallet
+ const isPending =
+ statusWriteContract === 'pending' || statusWriteContracts === 'pending';
+
+ return useMemo(() => {
+ let label = '';
+ let labelClassName: string = color.foregroundMuted;
+
+ if (isPending) {
+ label = 'Confirm in wallet.';
+ }
+
+ if (isInProgress) {
+ label = 'Transaction in progress...';
+ }
+
+ if (receipt) {
+ label = 'Successful';
+ }
+
+ if (errorMessage) {
+ label = errorMessage;
+ labelClassName = color.error;
+ }
+
+ return { label, labelClassName };
+ }, [errorMessage, isInProgress, isPending, receipt]);
+}
diff --git a/src/transaction/hooks/useGetTransactionToast.test.tsx b/src/transaction/hooks/useGetTransactionToast.test.tsx
deleted file mode 100644
index c0c683566e..0000000000
--- a/src/transaction/hooks/useGetTransactionToast.test.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { renderHook } from '@testing-library/react';
-import { beforeEach, describe, expect, it, vi } from 'vitest';
-import { useChainId } from 'wagmi';
-import { useTransactionContext } from '../components/TransactionProvider';
-import { useGetTransactionToast } from './useGetTransactionToast';
-
-vi.mock('../components/TransactionProvider', () => ({
- useTransactionContext: vi.fn(),
-}));
-
-vi.mock('wagmi', () => ({
- useChainId: vi.fn(),
-}));
-
-describe('useGetTransactionToast', () => {
- beforeEach(() => {
- (useChainId as vi.Mock).mockReturnValue(123);
- });
- it('should return correct toast and actionElement when transaction is loading', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- isLoading: true,
- });
-
- const { result } = renderHook(() => useGetTransactionToast());
-
- expect(result.current.label).toBe('Transaction in progress');
- });
-
- it('should return correct toast and actionElement when receipt exists', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- receipt: 'receipt',
- transactionHash: '123',
- });
-
- const { result } = renderHook(() => useGetTransactionToast());
-
- expect(result.current.label).toBe('Successful');
- expect(result.current.actionElement).not.toBeNull();
- });
-
- it('should return correct toast and actionElement when error occurs', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- errorMessage: 'error',
- });
-
- const { result } = renderHook(() => useGetTransactionToast());
-
- expect(result.current.label).toBe('Something went wrong');
- expect(result.current.actionElement).not.toBeNull();
- });
-
- it('should return correct toast and actionElement when no status available', () => {
- (useTransactionContext as vi.Mock).mockReturnValue({
- errorMessage: '',
- });
-
- const { result } = renderHook(() => useGetTransactionToast());
-
- expect(result.current.label).toBe('');
- expect(result.current.actionElement).toBeNull();
- });
-});
diff --git a/src/transaction/hooks/useGetTransactionToastAction.test.tsx b/src/transaction/hooks/useGetTransactionToastAction.test.tsx
new file mode 100644
index 0000000000..2923a43e31
--- /dev/null
+++ b/src/transaction/hooks/useGetTransactionToastAction.test.tsx
@@ -0,0 +1,172 @@
+import { renderHook } from '@testing-library/react';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { useChainId } from 'wagmi';
+import { useShowCallsStatus } from 'wagmi/experimental';
+import { getChainExplorer } from '../../network/getChainExplorer';
+import { useTransactionContext } from '../components/TransactionProvider';
+import { useGetTransactionToastAction } from './useGetTransactionToastAction';
+
+vi.mock('../components/TransactionProvider', () => ({
+ useTransactionContext: vi.fn(),
+}));
+
+vi.mock('wagmi', () => ({
+ useChainId: vi.fn(),
+}));
+
+vi.mock('wagmi/experimental', () => ({
+ useShowCallsStatus: vi.fn(),
+}));
+
+vi.mock('../../network/getChainExplorer', () => ({
+ getChainExplorer: vi.fn(),
+}));
+
+const mockGetChainExplorer = 'https://etherscan.io';
+
+describe('useGetTransactionToastAction', () => {
+ beforeEach(() => {
+ (useChainId as vi.Mock).mockReturnValue(123);
+ (useShowCallsStatus as vi.Mock).mockReturnValue({
+ showCallsStatus: vi.fn(),
+ });
+ (getChainExplorer as vi.Mock).mockReturnValue(mockGetChainExplorer);
+ });
+
+ it('should return actionElement when transaction hash exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionHash: '0x123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ expect(result.current.actionElement).toMatchInlineSnapshot(`
+
+
+ View transaction
+
+
+ `);
+ });
+
+ it('should return actionElement when transaction id exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionId: 'ab123',
+ onSubmit: vi.fn(),
+ });
+
+ const showCallsStatus = vi.fn();
+ (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ const button = result.current.actionElement as JSX.Element;
+ expect(button.props.onClick).toBeDefined();
+ expect(button).not.toBeNull();
+ });
+
+ it('should return actionElement when receipt exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ receipt: 'receipt',
+ transactionHash: '0x123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ expect(result.current.actionElement).toMatchInlineSnapshot(`
+
+
+ View transaction
+
+
+ `);
+ });
+
+ it('should return actionElement when error occurs', () => {
+ const onSubmitMock = vi.fn();
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: 'error',
+ onSubmit: onSubmitMock,
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ const button = result.current.actionElement as JSX.Element;
+ expect(button.props.onClick).toBe(onSubmitMock);
+ });
+
+ it('should return actionElement when no status available', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: '',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ expect(result.current.actionElement).toBeNull();
+ });
+
+ it('should prioritize transactionId over transactionHash when both are provided', () => {
+ const showCallsStatus = vi.fn();
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionHash: '0x123',
+ transactionId: 'ab123',
+ });
+ (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ const button = result.current.actionElement as JSX.Element;
+ expect(button.props.onClick).toBeDefined();
+ expect(button).not.toBeNull();
+ });
+
+ it('should use accountChainId from useChainId when chainId is not available in context', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ chainId: undefined,
+ transactionHash: '0x123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ expect(result.current.actionElement).toMatchInlineSnapshot(`
+
+
+ View transaction
+
+
+ `);
+ });
+
+ it('should call showCallsStatus when button is clicked', () => {
+ const showCallsStatus = vi.fn();
+ (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus });
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionId: 'ab123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastAction());
+
+ const button = result.current.actionElement as JSX.Element;
+ button.props.onClick();
+
+ expect(showCallsStatus).toHaveBeenCalledWith({ id: 'ab123' });
+ });
+});
diff --git a/src/transaction/hooks/useGetTransactionToast.tsx b/src/transaction/hooks/useGetTransactionToastAction.tsx
similarity index 58%
rename from src/transaction/hooks/useGetTransactionToast.tsx
rename to src/transaction/hooks/useGetTransactionToastAction.tsx
index 6006e0173c..846209fab5 100644
--- a/src/transaction/hooks/useGetTransactionToast.tsx
+++ b/src/transaction/hooks/useGetTransactionToastAction.tsx
@@ -1,38 +1,24 @@
import { useMemo } from 'react';
import type { ReactNode } from 'react';
import { useChainId } from 'wagmi';
-import { Spinner } from '../../internal/components/Spinner';
-import { errorSvg } from '../../internal/svg/errorSvg';
-import { successSvg } from '../../internal/svg/successSvg';
+import { useShowCallsStatus } from 'wagmi/experimental';
import { getChainExplorer } from '../../network/getChainExplorer';
import { cn, color, text } from '../../styles/theme';
import { useTransactionContext } from '../components/TransactionProvider';
-export function useGetTransactionToast() {
- const {
- chainId,
- errorMessage,
- isLoading,
- onSubmit,
- receipt,
- transactionHash,
- transactionId,
- } = useTransactionContext();
+export function useGetTransactionToastAction() {
+ const { chainId, errorMessage, onSubmit, transactionHash, transactionId } =
+ useTransactionContext();
const accountChainId = chainId ?? useChainId();
- const isInProgress = isLoading || !!transactionId || !!transactionHash;
+ const { showCallsStatus } = useShowCallsStatus();
return useMemo(() => {
const chainExplorer = getChainExplorer(accountChainId);
let actionElement: ReactNode = null;
- let label = '';
- let icon: ReactNode = null;
- if (isInProgress) {
- icon =
;
- label = 'Transaction in progress';
- }
+ // EOA will have txn hash
if (transactionHash) {
actionElement = (
);
}
- if (receipt) {
- icon = successSvg;
- label = 'Successful';
+
+ // SW will have txn id
+ if (transactionId) {
+ actionElement = (
+
+ );
}
+
if (errorMessage) {
actionElement = (
);
- icon = errorSvg;
- label = 'Something went wrong';
}
- return { actionElement, icon, label };
+ return { actionElement };
}, [
accountChainId,
errorMessage,
- isInProgress,
onSubmit,
- receipt,
+ showCallsStatus,
transactionHash,
+ transactionId,
]);
}
diff --git a/src/transaction/hooks/useGetTransactionToastLabel.test.tsx b/src/transaction/hooks/useGetTransactionToastLabel.test.tsx
new file mode 100644
index 0000000000..49b761b7fd
--- /dev/null
+++ b/src/transaction/hooks/useGetTransactionToastLabel.test.tsx
@@ -0,0 +1,101 @@
+import { renderHook } from '@testing-library/react';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+import { useChainId } from 'wagmi';
+import { useShowCallsStatus } from 'wagmi/experimental';
+import { getChainExplorer } from '../../network/getChainExplorer';
+import { useTransactionContext } from '../components/TransactionProvider';
+import { useGetTransactionToastLabel } from './useGetTransactionToastLabel';
+
+vi.mock('../components/TransactionProvider', () => ({
+ useTransactionContext: vi.fn(),
+}));
+
+vi.mock('wagmi', () => ({
+ useChainId: vi.fn(),
+}));
+
+vi.mock('wagmi/experimental', () => ({
+ useShowCallsStatus: vi.fn(),
+}));
+
+vi.mock('../../network/getChainExplorer', () => ({
+ getChainExplorer: vi.fn(),
+}));
+
+const mockGetChainExplorer = 'https://etherscan.io';
+
+describe('useGetTransactionToastLabel', () => {
+ beforeEach(() => {
+ (useChainId as vi.Mock).mockReturnValue(123);
+ (useShowCallsStatus as vi.Mock).mockReturnValue({
+ showCallsStatus: vi.fn(),
+ });
+ (getChainExplorer as vi.Mock).mockReturnValue(mockGetChainExplorer);
+ });
+ it('should return correct toast and actionElement when transaction is loading', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ isLoading: true,
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastLabel());
+
+ expect(result.current.label).toBe('Transaction in progress');
+ });
+
+ it('should return status when transaction hash exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionHash: '0x123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastLabel());
+
+ expect(result.current.label).toBe('Transaction in progress');
+ });
+
+ it('should return status when transaction id exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ transactionId: 'ab123',
+ onSubmit: vi.fn(),
+ });
+
+ const showCallsStatus = vi.fn();
+ (useShowCallsStatus as vi.Mock).mockReturnValue({ showCallsStatus });
+
+ const { result } = renderHook(() => useGetTransactionToastLabel());
+
+ expect(result.current.label).toBe('Transaction in progress');
+ });
+
+ it('should return status when receipt exists', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ receipt: 'receipt',
+ transactionHash: '0x123',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastLabel());
+
+ expect(result.current.label).toBe('Successful');
+ });
+
+ it('should return status when error occurs', () => {
+ const onSubmitMock = vi.fn();
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: 'error',
+ onSubmit: onSubmitMock,
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastLabel());
+
+ expect(result.current.label).toBe('Something went wrong');
+ });
+
+ it('should return status when no status available', () => {
+ (useTransactionContext as vi.Mock).mockReturnValue({
+ errorMessage: '',
+ });
+
+ const { result } = renderHook(() => useGetTransactionToastLabel());
+
+ expect(result.current.label).toBe('');
+ });
+});
diff --git a/src/transaction/hooks/useGetTransactionToastLabel.tsx b/src/transaction/hooks/useGetTransactionToastLabel.tsx
new file mode 100644
index 0000000000..1d16c00326
--- /dev/null
+++ b/src/transaction/hooks/useGetTransactionToastLabel.tsx
@@ -0,0 +1,31 @@
+import { useMemo } from 'react';
+import { color } from '../../styles/theme';
+import { useTransactionContext } from '../components/TransactionProvider';
+
+export function useGetTransactionToastLabel() {
+ const { errorMessage, isLoading, receipt, transactionHash, transactionId } =
+ useTransactionContext();
+
+ // user confirmed in wallet, txn in progress
+ const isInProgress = isLoading || !!transactionId || !!transactionHash;
+
+ return useMemo(() => {
+ let label = '';
+ let labelClassName: string = color.foregroundMuted;
+
+ if (isInProgress) {
+ label = 'Transaction in progress';
+ }
+
+ if (receipt) {
+ label = 'Successful';
+ }
+
+ if (errorMessage) {
+ label = 'Something went wrong';
+ labelClassName = color.error;
+ }
+
+ return { label, labelClassName };
+ }, [errorMessage, isInProgress, receipt]);
+}