Skip to content

Commit

Permalink
fix: update swap lifecyclestatus (#1249)
Browse files Browse the repository at this point in the history
  • Loading branch information
alessey authored Sep 14, 2024
1 parent d85f460 commit 477fc52
Show file tree
Hide file tree
Showing 14 changed files with 400 additions and 434 deletions.
20 changes: 19 additions & 1 deletion playground/nextjs-app-router/components/demo/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import {
SwapSettingsSlippageTitle,
SwapToggleButton,
} from '@coinbase/onchainkit/swap';
import type { SwapError } from '@coinbase/onchainkit/swap';
import type { Token } from '@coinbase/onchainkit/token';
import { useCallback, useContext } from 'react';
import type { TransactionReceipt } from 'viem';
import { base } from 'viem/chains';
import { AppContext } from '../AppProvider';

Expand Down Expand Up @@ -65,6 +67,17 @@ function SwapComponent() {
console.log('Status:', lifeCycleStatus);
}, []);

const handleOnSuccess = useCallback(
(transactionReceipt: TransactionReceipt) => {
console.log('Success:', transactionReceipt);
},
[],
);

const handleOnError = useCallback((swapError: SwapError) => {
console.log('Error:', swapError);
}, []);

return (
<div className="relative flex h-full w-full flex-col items-center">
{chainId !== base.id ? (
Expand All @@ -87,7 +100,12 @@ function SwapComponent() {
</div>
) : null}

<Swap className="border" onStatus={handleOnStatus}>
<Swap
className="border"
onStatus={handleOnStatus}
onSuccess={handleOnSuccess}
onError={handleOnError}
>
<SwapSettings>
<SwapSettingsSlippageTitle>Max. slippage</SwapSettingsSlippageTitle>
<SwapSettingsSlippageDescription>
Expand Down
38 changes: 32 additions & 6 deletions src/swap/components/SwapButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('SwapButton', () => {
address: '0x123',
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
lifeCycleStatus: { statusName: 'init' },
handleSubmit: mockHandleSubmit,
});
render(<SwapButton />);
Expand All @@ -44,7 +44,33 @@ describe('SwapButton', () => {
useSwapContextMock.mockReturnValue({
to: { loading: true, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
lifeCycleStatus: { statusName: 'init' },
handleSubmit: mockHandleSubmit,
});
render(<SwapButton />);
const button = screen.getByTestId('ockSwapButton_Button');
expect(screen.getByTestId('spinner')).toBeInTheDocument();
expect(button).toBeDisabled();
});

it('should render Spinner when transaction is pending', () => {
useSwapContextMock.mockReturnValue({
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
lifeCycleStatus: { statusName: 'transactionPending' },
handleSubmit: mockHandleSubmit,
});
render(<SwapButton />);
const button = screen.getByTestId('ockSwapButton_Button');
expect(screen.getByTestId('spinner')).toBeInTheDocument();
expect(button).toBeDisabled();
});

it('should render Spinner when transaction is approved', () => {
useSwapContextMock.mockReturnValue({
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
lifeCycleStatus: { statusName: 'transactionApproved' },
handleSubmit: mockHandleSubmit,
});
render(<SwapButton />);
Expand All @@ -57,7 +83,7 @@ describe('SwapButton', () => {
useSwapContextMock.mockReturnValue({
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: null, token: 'BTC' },
loading: false,
lifeCycleStatus: { statusName: 'init' },
handleSubmit: mockHandleSubmit,
});
render(<SwapButton />);
Expand All @@ -70,7 +96,7 @@ describe('SwapButton', () => {
address: '0x123',
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
lifeCycleStatus: { statusName: 'init' },
handleSubmit: mockHandleSubmit,
});
render(<SwapButton />);
Expand All @@ -84,7 +110,7 @@ describe('SwapButton', () => {
address: '0x123',
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
lifeCycleStatus: { statusName: 'init' },
handleSubmit: mockHandleSubmit,
});
const customClass = 'custom-class';
Expand All @@ -97,7 +123,7 @@ describe('SwapButton', () => {
useSwapContextMock.mockReturnValue({
to: { loading: false, amount: 1, token: 'ETH' },
from: { loading: false, amount: 1, token: 'BTC' },
loading: false,
lifeCycleStatus: { statusName: 'init' },
handleSubmit: mockHandleSubmit,
});
vi.mocked(useAccount).mockReturnValue({
Expand Down
14 changes: 11 additions & 3 deletions src/swap/components/SwapButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,19 @@ import type { SwapButtonReact } from '../types';
import { useSwapContext } from './SwapProvider';

export function SwapButton({ className, disabled = false }: SwapButtonReact) {
const { address, to, from, loading, isTransactionPending, handleSubmit } =
useSwapContext();
const {
address,
to,
from,
lifeCycleStatus: { statusName },
handleSubmit,
} = useSwapContext();

const isLoading =
to.loading || from.loading || loading || isTransactionPending;
to.loading ||
from.loading ||
statusName === 'transactionPending' ||
statusName === 'transactionApproved';

const isDisabled =
!from.amount ||
Expand Down
49 changes: 28 additions & 21 deletions src/swap/components/SwapMessage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ describe('SwapMessage', () => {
const mockContext = {
to: {},
from: {},
error: null,
loading: false,
lifeCycleStatus: { statusData: null },
lifeCycleStatus: { statusName: 'init', statusData: null },
};
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue(mockMessage);
Expand All @@ -44,9 +42,10 @@ describe('SwapMessage', () => {
const mockContext = {
to: {},
from: {},
error: 'Error occurred',
loading: false,
lifeCycleStatus: { statusData: null },
lifeCycleStatus: {
statusName: 'error',
statusData: { message: 'Error occurred' },
},
};
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue(mockMessage);
Expand All @@ -55,14 +54,26 @@ describe('SwapMessage', () => {
expect(messageDiv).toHaveTextContent(mockMessage);
});

it('should render with loading message', () => {
it('should render with loading message in transactionPending status', () => {
const mockMessage = 'Loading...';
const mockContext = {
to: {},
from: {},
error: null,
loading: true,
lifeCycleStatus: { statusData: null },
lifeCycleStatus: { statusName: 'transactionPending', statusData: null },
};
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue(mockMessage);
render(<SwapMessage />);
const messageDiv = screen.getByTestId('ockSwapMessage_Message');
expect(messageDiv).toHaveTextContent(mockMessage);
});

it('should render with loading message in transactionApproved status', () => {
const mockMessage = 'Loading...';
const mockContext = {
to: {},
from: {},
lifeCycleStatus: { statusName: 'transactionApproved', statusData: null },
};
useSwapContextMock.mockReturnValue(mockContext);
mockGetSwapMessage.mockReturnValue(mockMessage);
Expand All @@ -75,9 +86,7 @@ describe('SwapMessage', () => {
const mockContext = {
to: {},
from: {},
error: null,
loading: false,
lifeCycleStatus: { statusData: null },
lifeCycleStatus: { statusName: 'init', statusData: null },
};

useSwapContextMock.mockReturnValue(mockContext);
Expand All @@ -89,24 +98,22 @@ describe('SwapMessage', () => {
});

it('should set isMissingRequiredFields to true when reflected in statusData', () => {
const mockLifeCycleStatus = {
statusName: 'init',
statusData: { isMissingRequiredField: true },
};
const mockContext = {
to: { amount: 1, token: 'ETH' },
from: { amount: null, token: 'DAI' },
error: null,
loading: false,
isTransactionPending: false,
address: '0x123',
lifeCycleStatus: { statusData: { isMissingRequiredField: true } },
lifeCycleStatus: mockLifeCycleStatus,
};
useSwapContextMock.mockReturnValue(mockContext);
render(<SwapMessage />);
expect(mockGetSwapMessage).toHaveBeenCalledWith({
address: '0x123',
error: null,
from: { amount: null, token: 'DAI' },
loading: false,
isMissingRequiredFields: true,
isTransactionPending: false,
lifeCycleStatus: mockLifeCycleStatus,
to: { amount: 1, token: 'ETH' },
});
});
Expand Down
20 changes: 2 additions & 18 deletions src/swap/components/SwapMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,12 @@ import { getSwapMessage } from '../utils/getSwapMessage';
import { useSwapContext } from './SwapProvider';

export function SwapMessage({ className }: SwapMessageReact) {
const {
address,
to,
from,
error,
loading,
isTransactionPending,
lifeCycleStatus: { statusData },
} = useSwapContext();

const isMissingRequiredFields =
!!statusData &&
'isMissingRequiredField' in statusData &&
statusData?.isMissingRequiredField;
const { address, to, from, lifeCycleStatus } = useSwapContext();

const message = getSwapMessage({
address,
error,
from,
loading,
isMissingRequiredFields,
isTransactionPending,
lifeCycleStatus,
to,
});

Expand Down
Loading

0 comments on commit 477fc52

Please sign in to comment.