diff --git a/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.stories.tsx b/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.stories.tsx index cc70141cc131..a517361209c9 100644 --- a/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.stories.tsx +++ b/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.stories.tsx @@ -7,10 +7,9 @@ const PerpsEmptyStateMeta = { export default PerpsEmptyStateMeta; -// Default story export const Default = { args: { - onStartTrading: () => { + onAction: () => { // eslint-disable-next-line no-console console.log('Start Trading pressed'); }, diff --git a/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.test.tsx b/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.test.tsx index 4d9d457146a3..1933ca2030d3 100644 --- a/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.test.tsx +++ b/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.test.tsx @@ -4,7 +4,7 @@ import renderWithProvider from '../../../../../util/test/renderWithProvider'; import { PerpsEmptyState } from './PerpsEmptyState'; describe('PerpsEmptyState', () => { - const mockOnStartTrading = jest.fn(); + const mockOnAction = jest.fn(); beforeEach(() => { jest.clearAllMocks(); @@ -12,7 +12,7 @@ describe('PerpsEmptyState', () => { it('should render correctly', () => { const { getByText } = renderWithProvider( - , + , ); expect( @@ -21,14 +21,14 @@ describe('PerpsEmptyState', () => { expect(getByText('Start trading')).toBeTruthy(); }); - it('should call onStartTrading when button is pressed', () => { + it('should call onAction when button is pressed', () => { const { getByText } = renderWithProvider( - , + , ); const startTradingButton = getByText('Start trading'); fireEvent.press(startTradingButton); - expect(mockOnStartTrading).toHaveBeenCalledTimes(1); + expect(mockOnAction).toHaveBeenCalledTimes(1); }); }); diff --git a/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.tsx b/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.tsx index 8a2d54cd2b90..0cd1d05bcf7d 100644 --- a/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.tsx +++ b/app/components/UI/Perps/Views/PerpsEmptyState/PerpsEmptyState.tsx @@ -11,12 +11,10 @@ import emptyStatePerpsLight from '../../../../../images/empty-state-perps-light. import emptyStatePerpsDark from '../../../../../images/empty-state-perps-dark.png'; export interface PerpsEmptyStateProps extends TabEmptyStateProps { - onStartTrading: () => void; testID?: string; } export const PerpsEmptyState: React.FC = ({ - onStartTrading, testID, ...props }) => { @@ -36,7 +34,6 @@ export const PerpsEmptyState: React.FC = ({ } description={strings('perps.position.list.first_time_description')} actionButtonText={strings('perps.position.list.start_trading')} - onAction={onStartTrading} testID={testID} {...props} /> diff --git a/app/components/UI/Perps/Views/PerpsPositionsView/PerpsPositionsView.tsx b/app/components/UI/Perps/Views/PerpsPositionsView/PerpsPositionsView.tsx index 92cf80c98d1d..e3f659cc0ce4 100644 --- a/app/components/UI/Perps/Views/PerpsPositionsView/PerpsPositionsView.tsx +++ b/app/components/UI/Perps/Views/PerpsPositionsView/PerpsPositionsView.tsx @@ -24,6 +24,7 @@ import type { Position } from '../../controllers/types'; import { usePerpsLivePositions, usePerpsTPSLUpdate } from '../../hooks'; import { usePerpsLiveAccount } from '../../hooks/stream'; import { formatPnl, formatPrice } from '../../utils/formatUtils'; +import { getPositionDirection } from '../../utils/positionCalculations'; import { calculateTotalPnL } from '../../utils/pnlCalculations'; import { createStyles } from './PerpsPositionsView.styles'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -125,14 +126,7 @@ const PerpsPositionsView: React.FC = () => { {positions.map((position, index) => { - const sizeValue = parseFloat(position.size); - const directionSegment = Number.isFinite(sizeValue) - ? sizeValue > 0 - ? 'long' - : sizeValue < 0 - ? 'short' - : 'unknown' - : 'unknown'; + const directionSegment = getPositionDirection(position.size); return ( ({ // Mock PerpsEmptyState component to avoid Redux context issues while preserving testID jest.mock('../PerpsEmptyState', () => ({ PerpsEmptyState: ({ - onStartTrading, + onAction, testID, }: { - onStartTrading: () => void; + onAction?: () => void; testID?: string; }) => { const { TouchableOpacity, Text, View } = jest.requireActual('react-native'); return ( Bet on price movements with up to 40x leverage. - + Start trading diff --git a/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx b/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx index 09e1cb698389..e0478585cbca 100644 --- a/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx +++ b/app/components/UI/Perps/Views/PerpsTabView/PerpsTabView.tsx @@ -38,6 +38,7 @@ import { usePerpsLivePositions, usePerpsPerformance, } from '../../hooks'; +import { getPositionDirection } from '../../utils/positionCalculations'; import { usePerpsLiveAccount, usePerpsLiveOrders } from '../../hooks/stream'; import { selectPerpsEligibility } from '../../selectors/perpsController'; import styleSheet from './PerpsTabView.styles'; @@ -208,14 +209,7 @@ const PerpsTabView: React.FC = () => { {positions.map((position, index) => { - const sizeValue = parseFloat(position.size); - const directionSegment = Number.isFinite(sizeValue) - ? sizeValue > 0 - ? 'long' - : sizeValue < 0 - ? 'short' - : 'unknown' - : 'unknown'; + const directionSegment = getPositionDirection(position.size); return ( = () => { {!isInitialLoading && hasNoPositionsOrOrders ? ( diff --git a/app/components/UI/Perps/utils/positionCalculations.test.ts b/app/components/UI/Perps/utils/positionCalculations.test.ts index b7d5ce2031a4..184ac727f7f4 100644 --- a/app/components/UI/Perps/utils/positionCalculations.test.ts +++ b/app/components/UI/Perps/utils/positionCalculations.test.ts @@ -5,6 +5,7 @@ import { calculateCloseValue, calculatePercentageFromTokenAmount, calculatePercentageFromUSDAmount, + getPositionDirection, } from './positionCalculations'; describe('Position Calculations Utils', () => { @@ -193,4 +194,50 @@ describe('Position Calculations Utils', () => { expect(result).toBe(0); }); }); + + describe('getPositionDirection', () => { + it('returns "long" for positive position sizes', () => { + expect(getPositionDirection('10.5')).toBe('long'); + expect(getPositionDirection('0.01')).toBe('long'); + expect(getPositionDirection('1000')).toBe('long'); + }); + + it('returns "short" for negative position sizes', () => { + expect(getPositionDirection('-10.5')).toBe('short'); + expect(getPositionDirection('-0.01')).toBe('short'); + expect(getPositionDirection('-1000')).toBe('short'); + }); + + it('returns "unknown" for zero position size', () => { + expect(getPositionDirection('0')).toBe('unknown'); + expect(getPositionDirection('0.0')).toBe('unknown'); + expect(getPositionDirection('-0')).toBe('unknown'); + }); + + it('returns "unknown" for invalid strings', () => { + expect(getPositionDirection('abc')).toBe('unknown'); + expect(getPositionDirection('not a number')).toBe('unknown'); + expect(getPositionDirection('')).toBe('unknown'); + expect(getPositionDirection(' ')).toBe('unknown'); + }); + + it('returns "unknown" for non-finite values', () => { + expect(getPositionDirection('Infinity')).toBe('unknown'); + expect(getPositionDirection('-Infinity')).toBe('unknown'); + expect(getPositionDirection('NaN')).toBe('unknown'); + }); + + it('handles edge cases correctly', () => { + expect(getPositionDirection('1e-10')).toBe('long'); // Very small positive + expect(getPositionDirection('-1e-10')).toBe('short'); // Very small negative + expect(getPositionDirection('1.23e15')).toBe('long'); // Large positive + expect(getPositionDirection('-1.23e15')).toBe('short'); // Large negative + }); + + it('handles strings with whitespace', () => { + expect(getPositionDirection(' 10.5 ')).toBe('long'); + expect(getPositionDirection(' -10.5 ')).toBe('short'); + expect(getPositionDirection(' 0 ')).toBe('unknown'); + }); + }); }); diff --git a/app/components/UI/Perps/utils/positionCalculations.ts b/app/components/UI/Perps/utils/positionCalculations.ts index fb2070ddb4a2..a33256da1037 100644 --- a/app/components/UI/Perps/utils/positionCalculations.ts +++ b/app/components/UI/Perps/utils/positionCalculations.ts @@ -175,3 +175,28 @@ export function calculatePercentageFromUSDAmount( const percentage = (usdAmount / totalPositionValue) * 100; return Math.max(0, Math.min(100, percentage)); } + +/** + * Helper function to determine position direction based on size value + * @param sizeString - The position size as a string + * @returns 'long', 'short', or 'unknown' + */ +export function getPositionDirection( + sizeString: string, +): 'long' | 'short' | 'unknown' { + const sizeValue = parseFloat(sizeString); + + if (!Number.isFinite(sizeValue)) { + return 'unknown'; + } + + if (sizeValue > 0) { + return 'long'; + } + + if (sizeValue < 0) { + return 'short'; + } + + return 'unknown'; +}