diff --git a/apps/trading/lib/hooks/use-vega-transaction-toasts.spec.tsx b/apps/trading/lib/hooks/use-vega-transaction-toasts.spec.tsx index 3fb7d229f..10cd72f40 100644 --- a/apps/trading/lib/hooks/use-vega-transaction-toasts.spec.tsx +++ b/apps/trading/lib/hooks/use-vega-transaction-toasts.spec.tsx @@ -7,7 +7,11 @@ import { } from '@vegaprotocol/types'; import type { VegaStoredTxState } from '@vegaprotocol/wallet'; import { VegaTxStatus } from '@vegaprotocol/wallet'; -import { VegaTransactionDetails } from './use-vega-transaction-toasts'; +import { + VegaTransactionDetails, + getVegaTransactionContentIntent, +} from './use-vega-transaction-toasts'; +import { Intent } from '@vegaprotocol/ui-toolkit'; jest.mock('@vegaprotocol/assets', () => { const A1 = { @@ -278,3 +282,27 @@ describe('VegaTransactionDetails', () => { expect(queryByTestId('toast-panel')?.textContent).toEqual(details); }); }); + +describe('getVegaTransactionContentIntent', () => { + it('returns the correct intent for a transaction', () => { + expect(getVegaTransactionContentIntent(withdraw).intent).toBe( + Intent.Primary + ); + expect(getVegaTransactionContentIntent(submitOrder).intent).toBe( + Intent.Success + ); + expect(getVegaTransactionContentIntent(editOrder).intent).toBe( + Intent.Success + ); + expect(getVegaTransactionContentIntent(cancelOrder).intent).toBe( + Intent.Primary + ); + expect(getVegaTransactionContentIntent(cancelAll).intent).toBe( + Intent.Primary + ); + expect(getVegaTransactionContentIntent(closePosition).intent).toBe( + Intent.Primary + ); + expect(getVegaTransactionContentIntent(batch).intent).toBe(Intent.Primary); + }); +}); diff --git a/apps/trading/lib/hooks/use-vega-transaction-toasts.tsx b/apps/trading/lib/hooks/use-vega-transaction-toasts.tsx index c0591a94e..86a70d6c6 100644 --- a/apps/trading/lib/hooks/use-vega-transaction-toasts.tsx +++ b/apps/trading/lib/hooks/use-vega-transaction-toasts.tsx @@ -547,7 +547,11 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => { return ( <> - {t('Confirmed')} + + {tx.order?.status + ? getOrderToastTitle(tx.order.status) + : t('Confirmed')} +

{t('Your transaction has been confirmed ')}

{tx.txHash && (

@@ -634,25 +638,8 @@ export const useVegaTransactionToasts = () => { ); const fromVegaTransaction = (tx: VegaStoredTxState): Toast => { - let content: ToastContent; const closeAfter = isFinal(tx) ? CLOSE_AFTER : undefined; - if (tx.status === VegaTxStatus.Requested) { - content = ; - } - if (tx.status === VegaTxStatus.Pending) { - content = ; - } - if (tx.status === VegaTxStatus.Complete) { - content = ; - } - if (tx.status === VegaTxStatus.Error) { - content = ; - } - - // Transaction can be successful but the order can be rejected by the network - const intent = - (tx.order && getOrderToastIntent(tx.order.status)) || - intentMap[tx.status]; + const { intent, content } = getVegaTransactionContentIntent(tx); return { id: `vega-${tx.id}`, @@ -676,3 +663,24 @@ export const useVegaTransactionToasts = () => { } ); }; + +export const getVegaTransactionContentIntent = (tx: VegaStoredTxState) => { + let content: ToastContent; + if (tx.status === VegaTxStatus.Requested) { + content = ; + } + if (tx.status === VegaTxStatus.Pending) { + content = ; + } + if (tx.status === VegaTxStatus.Complete) { + content = ; + } + if (tx.status === VegaTxStatus.Error) { + content = ; + } + + // Transaction can be successful but the order can be rejected by the network + const intent = + (tx.order && getOrderToastIntent(tx.order.status)) || intentMap[tx.status]; + return { intent, content }; +}; diff --git a/libs/orders/src/lib/utils.spec.ts b/libs/orders/src/lib/utils.spec.ts new file mode 100644 index 000000000..ac7c8937d --- /dev/null +++ b/libs/orders/src/lib/utils.spec.ts @@ -0,0 +1,129 @@ +import { Intent } from '@vegaprotocol/ui-toolkit'; +import { + getOrderToastIntent, + getOrderToastTitle, + getRejectionReason, + timeInForceLabel, +} from './utils'; +import * as Types from '@vegaprotocol/types'; + +describe('getOrderToastTitle', () => { + it('should return the correct title', () => { + expect(getOrderToastTitle(Types.OrderStatus.STATUS_ACTIVE)).toBe( + 'Order submitted' + ); + expect(getOrderToastTitle(Types.OrderStatus.STATUS_FILLED)).toBe( + 'Order filled' + ); + expect(getOrderToastTitle(Types.OrderStatus.STATUS_PARTIALLY_FILLED)).toBe( + 'Order partially filled' + ); + expect(getOrderToastTitle(Types.OrderStatus.STATUS_PARKED)).toBe( + 'Order parked' + ); + expect(getOrderToastTitle(Types.OrderStatus.STATUS_STOPPED)).toBe( + 'Order stopped' + ); + expect(getOrderToastTitle(Types.OrderStatus.STATUS_CANCELLED)).toBe( + 'Order cancelled' + ); + expect(getOrderToastTitle(Types.OrderStatus.STATUS_EXPIRED)).toBe( + 'Order expired' + ); + expect(getOrderToastTitle(Types.OrderStatus.STATUS_REJECTED)).toBe( + 'Order rejected' + ); + expect(getOrderToastTitle(undefined)).toBe(undefined); + }); +}); + +describe('getOrderToastIntent', () => { + it('should return the correct intent', () => { + expect(getOrderToastIntent(Types.OrderStatus.STATUS_PARKED)).toBe( + Intent.Warning + ); + expect(getOrderToastIntent(Types.OrderStatus.STATUS_EXPIRED)).toBe( + Intent.Warning + ); + expect(getOrderToastIntent(Types.OrderStatus.STATUS_PARTIALLY_FILLED)).toBe( + Intent.Warning + ); + expect(getOrderToastIntent(Types.OrderStatus.STATUS_REJECTED)).toBe( + Intent.Danger + ); + expect(getOrderToastIntent(Types.OrderStatus.STATUS_STOPPED)).toBe( + Intent.Danger + ); + expect(getOrderToastIntent(Types.OrderStatus.STATUS_FILLED)).toBe( + Intent.Success + ); + expect(getOrderToastIntent(Types.OrderStatus.STATUS_ACTIVE)).toBe( + Intent.Success + ); + expect(getOrderToastIntent(Types.OrderStatus.STATUS_CANCELLED)).toBe( + Intent.Success + ); + expect(getOrderToastIntent(undefined)).toBe(undefined); + }); +}); + +describe('getRejectionReason', () => { + it('should return the correct rejection reason for insufficient asset balance', () => { + expect( + getRejectionReason({ + rejectionReason: + Types.OrderRejectionReason.ORDER_ERROR_INSUFFICIENT_ASSET_BALANCE, + status: Types.OrderStatus.STATUS_REJECTED, + id: '', + createdAt: undefined, + size: '', + price: '', + timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK, + side: Types.Side.SIDE_BUY, + marketId: '', + }) + ).toBe('Insufficient asset balance'); + }); + + it('should return the correct rejection reason when order is stopped', () => { + expect( + getRejectionReason({ + rejectionReason: null, + status: Types.OrderStatus.STATUS_STOPPED, + id: '', + createdAt: undefined, + size: '', + price: '', + timeInForce: Types.OrderTimeInForce.TIME_IN_FORCE_FOK, + side: Types.Side.SIDE_BUY, + marketId: '', + }) + ).toBe( + 'Your Fill or Kill (FOK) order was not filled and it has been stopped' + ); + }); +}); + +describe('timeInForceLabel', () => { + it('should return the correct label for time in force', () => { + expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_FOK)).toBe( + `Fill or Kill (FOK)` + ); + expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GTC)).toBe( + `Good 'til Cancelled (GTC)` + ); + expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_IOC)).toBe( + `Immediate or Cancel (IOC)` + ); + expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GTT)).toBe( + `Good 'til Time (GTT)` + ); + expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GFA)).toBe( + `Good for Auction (GFA)` + ); + expect(timeInForceLabel(Types.OrderTimeInForce.TIME_IN_FORCE_GFN)).toBe( + `Good for Normal (GFN)` + ); + expect(timeInForceLabel('')).toBe(''); + }); +}); diff --git a/libs/orders/src/lib/utils.ts b/libs/orders/src/lib/utils.ts index 2acac6fcb..e71ebaf59 100644 --- a/libs/orders/src/lib/utils.ts +++ b/libs/orders/src/lib/utils.ts @@ -82,10 +82,10 @@ export const getOrderToastIntent = ( return Intent.Warning; case Schema.OrderStatus.STATUS_REJECTED: case Schema.OrderStatus.STATUS_STOPPED: - case Schema.OrderStatus.STATUS_CANCELLED: return Intent.Danger; case Schema.OrderStatus.STATUS_FILLED: case Schema.OrderStatus.STATUS_ACTIVE: + case Schema.OrderStatus.STATUS_CANCELLED: return Intent.Success; default: return;