Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/components/UI/Perps/Perps.testIds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ export const PerpsMarketDetailsViewSelectorsIDs = {
LOADING: 'perps-market-details-loading',
ERROR: 'perps-market-details-error',
HEADER: 'perps-market-header',
MARKET_SUMMARY: 'perps-market-details-market-summary',
FULLSCREEN_CHART_BUTTON: 'perps-market-details-fullscreen-chart-button',
STATISTICS_HIGH_24H: 'perps-statistics-high-24h',
STATISTICS_LOW_24H: 'perps-statistics-low-24h',
STATISTICS_VOLUME_24H: 'perps-statistics-volume-24h',
Expand Down Expand Up @@ -456,7 +458,8 @@ export const PerpsMarketHeaderSelectorsIDs = {
PRICE_CHANGE: 'perps-market-header-price-change',
MORE_BUTTON: 'perps-market-header-more-button',
FAVORITE_BUTTON: 'perps-market-header-favorite-button',
CATEGORY_SEARCH_BUTTON: 'perps-market-header-category-search-button',
MARKET_LIST_BUTTON: 'perps-market-header-market-list-button',
SUBTITLE: 'perps-market-header-subtitle',
};

// ========================================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export const createStyles = ({ theme }: { theme: Theme }) =>
paddingBottom: 16, // Consistent bottom padding
},

// Keeps the fullscreen button fixed while the price row shrinks
marketSummaryFullscreenButton: {
flexShrink: 0,
},

// Container for error state
container: {
flex: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2143,7 +2143,7 @@ describe('PerpsMarketDetailsView', () => {
await act(async () => {
fireEvent.press(
getByTestId(
`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`,
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
),
);
});
Expand Down Expand Up @@ -3493,7 +3493,7 @@ describe('PerpsMarketDetailsView', () => {

// Press fullscreen button
const fullscreenButton = getByTestId(
'perps-market-header-fullscreen-button',
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
);
fireEvent.press(fullscreenButton);

Expand All @@ -3517,7 +3517,7 @@ describe('PerpsMarketDetailsView', () => {

// Open the modal first
const fullscreenButton = getByTestId(
'perps-market-header-fullscreen-button',
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
);
fireEvent.press(fullscreenButton);

Expand Down Expand Up @@ -3550,7 +3550,7 @@ describe('PerpsMarketDetailsView', () => {

// Open the modal
const fullscreenButton = getByTestId(
'perps-market-header-fullscreen-button',
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
);
fireEvent.press(fullscreenButton);

Expand Down Expand Up @@ -3615,9 +3615,7 @@ describe('PerpsMarketDetailsView', () => {
);

fireEvent.press(
getByTestId(
`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`,
),
getByTestId(PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON),
);

await waitFor(() => {
Expand All @@ -3640,8 +3638,8 @@ describe('PerpsMarketDetailsView', () => {
});
});

describe('Category search shortcut', () => {
it('navigates to market list without filters when search button is pressed', () => {
describe('Market list shortcut', () => {
it('navigates to market list without filters when the market list button is pressed', () => {
const { getByTestId } = renderWithProvider(
<PerpsConnectionProvider>
<PerpsMarketDetailsView />
Expand All @@ -3651,15 +3649,50 @@ describe('PerpsMarketDetailsView', () => {
},
);

const searchButton = getByTestId(
'perps-market-header-category-search-button',
const marketListButton = getByTestId(
PerpsMarketHeaderSelectorsIDs.MARKET_LIST_BUTTON,
);
fireEvent.press(searchButton);
fireEvent.press(marketListButton);

expect(mockNavigateToMarketList).toHaveBeenCalledWith({
source: 'magnifying_glass',
source: 'perp_asset_screen',
});
});

it('tracks the market list button click with the correct analytics values', () => {
const mockTrack = jest.fn();
const { usePerpsEventTracking: mockUsePerpsEventTrackingFn } =
jest.requireMock('../../hooks/usePerpsEventTracking');
mockUsePerpsEventTrackingFn.mockImplementation(() => ({
track: mockTrack,
}));

const { getByTestId } = renderWithProvider(
<PerpsConnectionProvider>
<PerpsMarketDetailsView />
</PerpsConnectionProvider>,
{
state: initialState,
},
);

fireEvent.press(
getByTestId(PerpsMarketHeaderSelectorsIDs.MARKET_LIST_BUTTON),
);

expect(mockTrack).toHaveBeenCalledWith(
MetaMetricsEvents.PERPS_UI_INTERACTION,
expect.objectContaining({
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
PERPS_EVENT_VALUE.INTERACTION_TYPE.BUTTON_CLICKED,
[PERPS_EVENT_PROPERTY.BUTTON_CLICKED]:
PERPS_EVENT_VALUE.BUTTON_CLICKED.MARKET_LIST,
[PERPS_EVENT_PROPERTY.BUTTON_LOCATION]:
PERPS_EVENT_VALUE.BUTTON_LOCATION.PERP_MARKET_DETAILS,
[PERPS_EVENT_PROPERTY.ASSET]: 'BTC',
}),
);
});
});

describe('Position opened timestamp', () => {
Expand Down Expand Up @@ -4613,7 +4646,9 @@ describe('PerpsMarketDetailsView', () => {

// Should show the route market's leverage badge
expect(getByText('25x')).toBeOnTheScreen();
expect(getAllByText('ETH-USD').length).toBeGreaterThanOrEqual(1);
// Header shows the full asset name and the market pair subtitle
expect(getByText('Ethereum')).toBeOnTheScreen();
expect(getAllByText('ETH-USD perp').length).toBeGreaterThanOrEqual(1);
});

it('enriches market data when route maxLeverage is unformatted', async () => {
Expand Down Expand Up @@ -4820,7 +4855,8 @@ describe('PerpsMarketDetailsView', () => {

// Verify the header renders with correct market symbol
expect(getByTestId('perps-market-header')).toBeOnTheScreen();
expect(getAllByText('BTC-USD').length).toBeGreaterThanOrEqual(1);
expect(getByText('Bitcoin')).toBeOnTheScreen();
expect(getAllByText('BTC-USD perp').length).toBeGreaterThanOrEqual(1);

// Should show the enriched market's leverage badge from usePerpsMarkets
await waitFor(() => {
Expand All @@ -4844,7 +4880,7 @@ describe('PerpsMarketDetailsView', () => {
isRefreshing: false,
});

const { getAllByText, queryByText } = renderWithProvider(
const { getByText, getAllByText, queryByText } = renderWithProvider(
<PerpsConnectionProvider>
<PerpsMarketDetailsView />
</PerpsConnectionProvider>,
Expand All @@ -4854,7 +4890,8 @@ describe('PerpsMarketDetailsView', () => {
);

// Should show the asset name but no leverage badge (since no maxLeverage available)
expect(getAllByText('UNKNOWN-USD').length).toBeGreaterThanOrEqual(1);
expect(getByText('Unknown Asset')).toBeOnTheScreen();
expect(getAllByText('UNKNOWN-USD perp').length).toBeGreaterThanOrEqual(1);
// No leverage badge should be shown
expect(queryByText('40x')).toBeNull();
expect(queryByText('25x')).toBeNull();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import {
Box,
BoxAlignItems,
BoxFlexDirection,
BoxJustifyContent,
Button as DSButton,
ButtonIcon,
ButtonIconSize,
ButtonSemantic,
ButtonSemanticSeverity,
ButtonVariant,
ButtonSize as ButtonSizeRNDesignSystem,
IconName,
Text,
TextColor,
TextVariant,
useHeaderStandardAnimated,
} from '@metamask/design-system-react-native';
import {
useFocusEffect,
Expand Down Expand Up @@ -40,7 +47,6 @@ import { useDispatch, useSelector } from 'react-redux';
import { strings } from '../../../../../../locales/i18n';
import { setPerpsChartPreferredCandlePeriod } from '../../../../../actions/settings';
import { Skeleton } from '../../../../../component-library/components-temp/Skeleton';
import useHeaderStandardAnimated from '../../../../../component-library/components-temp/HeaderStandardAnimated/useHeaderStandardAnimated';
import { useStyles } from '../../../../../component-library/hooks';
import Routes from '../../../../../constants/navigation/Routes';
import Engine from '../../../../../core/Engine';
Expand All @@ -58,10 +64,12 @@ import PerpsCompactOrderRow from '../../components/PerpsCompactOrderRow';
import PerpsFlipPositionConfirmSheet from '../../components/PerpsFlipPositionConfirmSheet';
import {
PerpsMarketDetailsViewSelectorsIDs,
PerpsMarketHeaderSelectorsIDs,
PerpsOrderViewSelectorsIDs,
PerpsTutorialSelectorsIDs,
PerpsCompactOrderRowSelectorsIDs,
} from '../../Perps.testIds';
import LivePriceHeader from '../../components/LivePriceDisplay/LivePriceHeader';
import PerpsMarketInlineHeader from '../../components/PerpsMarketInlineHeader';
import PerpsMarketHoursBanner from '../../components/PerpsMarketHoursBanner';
import PerpsMarketStatisticsCard from '../../components/PerpsMarketStatisticsCard';
Expand Down Expand Up @@ -853,21 +861,21 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
});
}, [market, isWatchlist, track, showToast, PerpsToastOptions]);

const handleCategorySearchPress = useCallback(() => {
const handleMarketListPress = useCallback(() => {
if (!market) return;

track(MetaMetricsEvents.PERPS_UI_INTERACTION, {
[PERPS_EVENT_PROPERTY.INTERACTION_TYPE]:
PERPS_EVENT_VALUE.INTERACTION_TYPE.BUTTON_CLICKED,
[PERPS_EVENT_PROPERTY.BUTTON_CLICKED]:
PERPS_EVENT_VALUE.BUTTON_CLICKED.MAGNIFYING_GLASS,
PERPS_EVENT_VALUE.BUTTON_CLICKED.MARKET_LIST,
[PERPS_EVENT_PROPERTY.BUTTON_LOCATION]:
PERPS_EVENT_VALUE.BUTTON_LOCATION.ASSET_DETAILS,
PERPS_EVENT_VALUE.BUTTON_LOCATION.PERP_MARKET_DETAILS,
[PERPS_EVENT_PROPERTY.ASSET]: market.symbol,
});

navigateToMarketList({
source: PERPS_EVENT_VALUE.SOURCE.MAGNIFYING_GLASS,
source: PERPS_EVENT_VALUE.SOURCE.PERP_ASSET_SCREEN,
});
}, [market, track, navigateToMarketList]);

Expand Down Expand Up @@ -1540,14 +1548,42 @@ const PerpsMarketDetailsView: React.FC<PerpsMarketDetailsViewProps> = () => {
market={market}
currentPrice={syncedChartCurrentPrice}
onBackPress={handleBackPress}
onFullscreenPress={handleFullscreenChartOpen}
onFavoritePress={handleWatchlistPress}
onCategorySearchPress={handleCategorySearchPress}
onMarketListPress={handleMarketListPress}
isFavorite={isWatchlist}
useDetailLayout
testID={PerpsMarketDetailsViewSelectorsIDs.HEADER}
fullscreenButtonTestID={`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`}
/>

{/* Below header: live price + 24h change and fullscreen chart button */}
<Box
flexDirection={BoxFlexDirection.Row}
alignItems={BoxAlignItems.Center}
justifyContent={BoxJustifyContent.Between}
gap={2}
twClassName="px-4 pb-2"
testID={PerpsMarketDetailsViewSelectorsIDs.MARKET_SUMMARY}
>
{/* Flexible wrapper lets the price shrink; the button stays fixed. */}
<Box twClassName="flex-1">
<LivePriceHeader
symbol={market.symbol}
testIDPrice={PerpsMarketHeaderSelectorsIDs.PRICE}
testIDChange={PerpsMarketHeaderSelectorsIDs.PRICE_CHANGE}
currentPrice={syncedChartCurrentPrice}
size="large"
/>
</Box>
Comment thread
cursor[bot] marked this conversation as resolved.
<ButtonIcon
iconName={IconName.Expand}
size={ButtonIconSize.Md}
onPress={handleFullscreenChartOpen}
style={styles.marketSummaryFullscreenButton}
testID={PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON}
accessibilityLabel={strings('perps.market_details.fullscreen_chart')}
/>
</Box>

<View style={styles.scrollableContentContainer}>
<ScrollView
ref={scrollViewRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
*/
import '../../../../../../tests/component-view/mocks';
import type { ComponentType } from 'react';
import { act, fireEvent, screen, waitFor } from '@testing-library/react-native';
import {
act,
fireEvent,
screen,
waitFor,
within,
} from '@testing-library/react-native';
import type { Position } from '@metamask/perps-controller';
import {
MOCK_PERPS_MARKET_INSIGHTS_REPORT,
Expand Down Expand Up @@ -485,21 +491,44 @@ describe('PerpsMarketDetailsView', () => {
).toBeOnTheScreen();
expect(
screen.getByTestId(
`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`,
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
),
).toBeOnTheScreen();
});

it('renders header with market title', async () => {
it('renders header with full asset name and market pair subtitle', async () => {
renderEligibleNoPositionPerpsDetails();

expect(
await screen.findByTestId(PerpsMarketDetailsViewSelectorsIDs.HEADER),
).toBeOnTheScreen();
expect(screen.getByText('ETH-USD')).toBeOnTheScreen();
expect(screen.getByText('Ethereum')).toBeOnTheScreen();
expect(screen.getByText('ETH-USD perp')).toBeOnTheScreen();
});

it('renders header price when market has no maxLeverage', async () => {
it('renders live price, 24h change, and fullscreen button inside the market summary row', async () => {
renderEligibleNoPositionPerpsDetails();

const marketSummary = await screen.findByTestId(
PerpsMarketDetailsViewSelectorsIDs.MARKET_SUMMARY,
);

expect(
within(marketSummary).getByTestId(PerpsMarketHeaderSelectorsIDs.PRICE),
).toBeOnTheScreen();
expect(
within(marketSummary).getByTestId(
PerpsMarketHeaderSelectorsIDs.PRICE_CHANGE,
),
).toBeOnTheScreen();
expect(
within(marketSummary).getByTestId(
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
),
).toBeOnTheScreen();
});

it('renders the market summary price when the market has no maxLeverage', async () => {
renderPerpsMarketDetailsView({
initialParams: {
market: {
Expand Down Expand Up @@ -536,19 +565,24 @@ describe('PerpsMarketDetailsView', () => {
expect(
await screen.findByTestId(PerpsMarketDetailsViewSelectorsIDs.HEADER),
).toBeOnTheScreen();
const marketSummary = await screen.findByTestId(
PerpsMarketDetailsViewSelectorsIDs.MARKET_SUMMARY,
);
expect(
await screen.findByTestId(PerpsMarketHeaderSelectorsIDs.PRICE),
within(marketSummary).getByTestId(PerpsMarketHeaderSelectorsIDs.PRICE),
).toBeOnTheScreen();
expect(
await screen.findByTestId(PerpsMarketHeaderSelectorsIDs.PRICE_CHANGE),
within(marketSummary).getByTestId(
PerpsMarketHeaderSelectorsIDs.PRICE_CHANGE,
),
).toBeOnTheScreen();
});

it('opens fullscreen chart modal and close button is pressable', async () => {
renderEligibleNoPositionPerpsDetails();

const fullscreenButton = await screen.findByTestId(
`${PerpsMarketDetailsViewSelectorsIDs.HEADER}-fullscreen-button`,
PerpsMarketDetailsViewSelectorsIDs.FULLSCREEN_CHART_BUTTON,
);
fireEvent.press(fullscreenButton);

Expand Down
Loading
Loading