Skip to content

Commit 36e1821

Browse files
committed
chore: update based on feedback review
1 parent d395905 commit 36e1821

6 files changed

Lines changed: 20 additions & 228 deletions

File tree

app/components/Views/WhatsHappeningDetailView/components/AssetRow.test.tsx

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ jest.mock('../utils/getRelatedAssetImageSource', () => ({
99
getRelatedAssetImageSource: jest.fn(() => undefined),
1010
}));
1111

12-
jest.mock(
13-
'../../../UI/Tokens/components/TokenListSecurityBadge/TokenListSecurityBadge',
14-
() => 'TokenListSecurityBadge',
15-
);
16-
1712
const btcAsset: RelatedAsset = {
1813
sourceAssetId: 'bitcoin',
1914
symbol: 'BTC',
@@ -90,41 +85,6 @@ describe('AssetRow', () => {
9085
expect(onAction).toHaveBeenCalledTimes(1);
9186
});
9287

93-
it('renders the TokenListSecurityBadge when caipAssetId is provided', () => {
94-
renderWithProvider(
95-
<AssetRow
96-
asset={btcAsset}
97-
actionLabel="Buy"
98-
accessibilityLabel="Buy BTC"
99-
onAction={onAction}
100-
caipAssetId="eip155:1/slip44:0"
101-
/>,
102-
);
103-
expect(screen.getByTestId !== undefined).toBeTruthy();
104-
// The mocked component renders as 'TokenListSecurityBadge' native element
105-
const badge = screen.UNSAFE_getByType(
106-
'TokenListSecurityBadge' as unknown as React.ComponentType,
107-
);
108-
expect(badge).toBeTruthy();
109-
expect(badge.props.caipAssetId).toBe('eip155:1/slip44:0');
110-
});
111-
112-
it('does not render the security badge when caipAssetId is not provided', () => {
113-
renderWithProvider(
114-
<AssetRow
115-
asset={btcAsset}
116-
actionLabel="Buy"
117-
accessibilityLabel="Buy BTC"
118-
onAction={onAction}
119-
/>,
120-
);
121-
expect(
122-
screen.UNSAFE_queryByType(
123-
'TokenListSecurityBadge' as unknown as React.ComponentType,
124-
),
125-
).toBeNull();
126-
});
127-
12888
it('renders the price secondary line when secondaryLine is provided', () => {
12989
renderWithProvider(
13090
<AssetRow

app/components/Views/WhatsHappeningDetailView/components/AssetRow.tsx

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ import {
1515
TextVariant,
1616
} from '@metamask/design-system-react-native';
1717
import type { RelatedAsset } from '@metamask/ai-controllers';
18-
import type { CaipAssetType } from '@metamask/utils';
1918
import { getRelatedAssetImageSource } from '../utils/getRelatedAssetImageSource';
20-
import TokenListSecurityBadge from '../../../UI/Tokens/components/TokenListSecurityBadge/TokenListSecurityBadge';
2119

2220
export interface AssetRowSecondaryLine {
2321
priceText: string;
@@ -30,8 +28,6 @@ interface AssetRowProps {
3028
actionLabel?: string;
3129
accessibilityLabel?: string;
3230
onAction?: () => void;
33-
/** When provided, renders the security badge inline next to the asset name. */
34-
caipAssetId?: CaipAssetType;
3531
/** When provided, renders price + 24h change below the asset name. */
3632
secondaryLine?: AssetRowSecondaryLine;
3733
}
@@ -45,7 +41,6 @@ const AssetRow: React.FC<AssetRowProps> = ({
4541
actionLabel,
4642
accessibilityLabel,
4743
onAction,
48-
caipAssetId,
4944
secondaryLine,
5045
}) => {
5146
const rawImageSource = getRelatedAssetImageSource(asset);
@@ -74,23 +69,14 @@ const AssetRow: React.FC<AssetRowProps> = ({
7469
>
7570
{/* Left: name + optional badge + optional price/change */}
7671
<Box twClassName="flex-1 mr-2">
77-
<Box
78-
flexDirection={BoxFlexDirection.Row}
79-
alignItems={BoxAlignItems.Center}
80-
gap={1}
72+
<Text
73+
variant={TextVariant.BodyMd}
74+
fontWeight={FontWeight.Medium}
75+
color={TextColor.TextDefault}
76+
numberOfLines={1}
8177
>
82-
<Text
83-
variant={TextVariant.BodyMd}
84-
fontWeight={FontWeight.Medium}
85-
color={TextColor.TextDefault}
86-
numberOfLines={1}
87-
>
88-
{asset.name || asset.symbol}
89-
</Text>
90-
{caipAssetId && (
91-
<TokenListSecurityBadge caipAssetId={caipAssetId} />
92-
)}
93-
</Box>
78+
{asset.name || asset.symbol}
79+
</Text>
9480

9581
{secondaryLine && (
9682
<Box

app/components/Views/WhatsHappeningDetailView/components/PerpsRow.test.tsx

Lines changed: 7 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -36,18 +36,6 @@ jest.mock('../../../hooks/useAnalytics/useAnalytics', () => ({
3636
}),
3737
}));
3838

39-
jest.mock(
40-
'../../../UI/Tokens/components/TokenListSecurityBadge/TokenListSecurityBadge',
41-
() => 'TokenListSecurityBadge',
42-
);
43-
44-
jest.mock(
45-
'../../../../selectors/featureFlagController/tokenListSecurityBadges',
46-
() => ({
47-
selectTokenListSecurityBadgesEnabled: jest.fn(() => true),
48-
}),
49-
);
50-
5139
const perpsOnlyAsset: RelatedAsset = {
5240
sourceAssetId: 'tsla',
5341
symbol: 'TSLA',
@@ -56,15 +44,6 @@ const perpsOnlyAsset: RelatedAsset = {
5644
hlPerpsMarket: ['xyz:TSLA'],
5745
};
5846

59-
/** Asset that has both a perps market AND a caip19 id — eligible for badge */
60-
const dualAsset: RelatedAsset = {
61-
sourceAssetId: 'bitcoin',
62-
symbol: 'BTC',
63-
name: 'Bitcoin',
64-
caip19: ['eip155:1/slip44:0'],
65-
hlPerpsMarket: ['BTC'],
66-
};
67-
6847
const mockItem: WhatsHappeningItem = {
6948
id: 'trend-3',
7049
title: 'TSLA earnings',
@@ -125,10 +104,14 @@ describe('PerpsRow', () => {
125104
});
126105
});
127106

128-
it('uses first hlPerpsMarket entry as the market symbol', () => {
107+
it('uses first hlPerpsMarket entry as the market symbol when multiple are present', () => {
108+
const multiMarketAsset: RelatedAsset = {
109+
...perpsOnlyAsset,
110+
hlPerpsMarket: ['FIRST-MARKET', 'SECOND-MARKET'],
111+
};
129112
renderWithProvider(
130113
<PerpsRow
131-
asset={dualAsset}
114+
asset={multiMarketAsset}
132115
item={mockItem}
133116
cardIndex={0}
134117
perpsPriceBySymbol={emptyPriceMap}
@@ -138,7 +121,7 @@ describe('PerpsRow', () => {
138121
expect(mockNavigate).toHaveBeenCalledWith(Routes.PERPS.ROOT, {
139122
screen: Routes.PERPS.MARKET_DETAILS,
140123
params: expect.objectContaining({
141-
market: { symbol: 'BTC', name: 'Bitcoin' },
124+
market: { symbol: 'FIRST-MARKET', name: 'Tesla' },
142125
}),
143126
});
144127
});
@@ -217,69 +200,4 @@ describe('PerpsRow', () => {
217200
);
218201
expect(screen.queryByText('$')).toBeNull();
219202
});
220-
221-
describe('verified badge gating', () => {
222-
it('renders TokenListSecurityBadge when asset has caip19 and feature flags are enabled', () => {
223-
renderWithProvider(
224-
<PerpsRow
225-
asset={dualAsset}
226-
item={mockItem}
227-
cardIndex={0}
228-
perpsPriceBySymbol={emptyPriceMap}
229-
/>,
230-
{
231-
state: {
232-
settings: { basicFunctionalityEnabled: true },
233-
},
234-
},
235-
);
236-
const badge = screen.UNSAFE_getByType(
237-
'TokenListSecurityBadge' as unknown as React.ComponentType,
238-
);
239-
expect(badge).toBeTruthy();
240-
expect(badge.props.caipAssetId).toBe('eip155:1/slip44:0');
241-
});
242-
243-
it('does not render TokenListSecurityBadge when basicFunctionalityEnabled is false', () => {
244-
renderWithProvider(
245-
<PerpsRow
246-
asset={dualAsset}
247-
item={mockItem}
248-
cardIndex={0}
249-
perpsPriceBySymbol={emptyPriceMap}
250-
/>,
251-
{
252-
state: {
253-
settings: { basicFunctionalityEnabled: false },
254-
},
255-
},
256-
);
257-
expect(
258-
screen.UNSAFE_queryByType(
259-
'TokenListSecurityBadge' as unknown as React.ComponentType,
260-
),
261-
).toBeNull();
262-
});
263-
264-
it('does not render TokenListSecurityBadge for a perps-only asset (no caip19)', () => {
265-
renderWithProvider(
266-
<PerpsRow
267-
asset={perpsOnlyAsset}
268-
item={mockItem}
269-
cardIndex={0}
270-
perpsPriceBySymbol={emptyPriceMap}
271-
/>,
272-
{
273-
state: {
274-
settings: { basicFunctionalityEnabled: true },
275-
},
276-
},
277-
);
278-
expect(
279-
screen.UNSAFE_queryByType(
280-
'TokenListSecurityBadge' as unknown as React.ComponentType,
281-
),
282-
).toBeNull();
283-
});
284-
});
285203
});

app/components/Views/WhatsHappeningDetailView/components/PerpsRow.tsx

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import React, { useCallback, useMemo } from 'react';
2-
import { useSelector } from 'react-redux';
3-
import type { CaipAssetType } from '@metamask/utils';
42
import type { RelatedAsset } from '@metamask/ai-controllers';
53
import { strings } from '../../../../../locales/i18n';
64
import { MetaMetricsEvents } from '../../../../core/Analytics';
@@ -9,11 +7,9 @@ import { WhatsHappeningInteractionType } from '../../Homepage/Sections/WhatsHapp
97
import { getWhatsHappeningEventProps } from '../../Homepage/Sections/WhatsHappening/eventProperties';
108
import type { WhatsHappeningItem } from '../../Homepage/Sections/WhatsHappening/types';
119
import { formatAssetPrice } from '../utils/formatAssetPrice';
12-
import { selectTokenListSecurityBadgesEnabled } from '../../../../selectors/featureFlagController/tokenListSecurityBadges';
1310
import type { PerpsPriceEntry } from '../hooks/useWhatsHappeningAssetPrices';
1411
import AssetRow from './AssetRow';
1512
import useTradeNavigation from '../hooks/useTradeNavigation';
16-
import type { RootState } from '../../../../reducers';
1713

1814
interface PerpsRowProps {
1915
asset: RelatedAsset;
@@ -37,29 +33,6 @@ const PerpsRow: React.FC<PerpsRowProps> = ({
3733
}) => {
3834
const { handleTrade } = useTradeNavigation(asset);
3935
const { trackEvent, createEventBuilder } = useAnalytics();
40-
const isTokenListSecurityBadgesEnabled = useSelector(
41-
selectTokenListSecurityBadgesEnabled,
42-
);
43-
const basicFunctionalityEnabled = useSelector(
44-
(state: RootState) => state.settings.basicFunctionalityEnabled,
45-
);
46-
47-
// Show verified badge for assets that also have a caip19 identifier
48-
const caipAssetId = useMemo(() => {
49-
const firstCaip = asset.caip19?.[0];
50-
if (
51-
!firstCaip ||
52-
!basicFunctionalityEnabled ||
53-
!isTokenListSecurityBadgesEnabled
54-
) {
55-
return undefined;
56-
}
57-
return firstCaip as CaipAssetType;
58-
}, [
59-
asset.caip19,
60-
basicFunctionalityEnabled,
61-
isTokenListSecurityBadgesEnabled,
62-
]);
6336

6437
// Perps prices are always quoted in USD
6538
const secondaryLine = useMemo(() => {
@@ -108,7 +81,6 @@ const PerpsRow: React.FC<PerpsRowProps> = ({
10881
: undefined
10982
}
11083
onAction={perpsMarketSymbol ? handleTradeWithTracking : undefined}
111-
caipAssetId={caipAssetId}
11284
secondaryLine={secondaryLine}
11385
/>
11486
);

app/components/Views/WhatsHappeningDetailView/utils/formatAssetPrice.test.ts

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,5 @@
11
import { TextColor } from '@metamask/design-system-react-native';
2-
import {
3-
formatPrice,
4-
formatPercentageChange,
5-
formatAssetPrice,
6-
} from './formatAssetPrice';
7-
8-
describe('formatPrice', () => {
9-
it('formats a USD price with 2 decimal places', () => {
10-
expect(formatPrice(1.0, 'USD')).toBe('$1.00');
11-
});
12-
13-
it('formats a large USD price with comma separators', () => {
14-
const result = formatPrice(95000, 'USD');
15-
expect(result).toBe('$95,000.00');
16-
});
17-
18-
it('handles fractional cents', () => {
19-
const result = formatPrice(0.9998, 'USD');
20-
expect(result).toBe('$1.00');
21-
});
22-
23-
it('falls back to plain format for invalid currency codes', () => {
24-
const result = formatPrice(100, 'INVALID_CURRENCY_XYZ');
25-
// Should not throw; returns a fallback string
26-
expect(result).toBeTruthy();
27-
expect(result).toContain('100');
28-
});
29-
});
2+
import { formatPercentageChange, formatAssetPrice } from './formatAssetPrice';
303

314
describe('formatPercentageChange', () => {
325
it('returns dash text and alternative color when change is undefined', () => {

app/components/Views/WhatsHappeningDetailView/utils/formatAssetPrice.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,12 @@
11
import { TextColor } from '@metamask/design-system-react-native';
2+
import { formatPerpsFiat } from '@metamask/perps-controller';
23

34
export interface FormattedAssetPrice {
45
priceText: string;
56
changeText: string | undefined;
67
changeColor: TextColor;
78
}
89

9-
/**
10-
* Formats a fiat price for display. Mirrors the logic used in `PopularTokenRow`
11-
* and `TokenListItem` — fixed 2 decimal places with currency symbol.
12-
*/
13-
export function formatPrice(
14-
price: number,
15-
currency: string | undefined,
16-
): string {
17-
const safeCurrency = currency?.toUpperCase() || 'USD';
18-
try {
19-
return new Intl.NumberFormat(undefined, {
20-
style: 'currency',
21-
currency: safeCurrency,
22-
minimumFractionDigits: 2,
23-
maximumFractionDigits: 2,
24-
}).format(price);
25-
} catch {
26-
return `${safeCurrency} ${price.toFixed(2)}`;
27-
}
28-
}
29-
3010
/**
3111
* Returns the text and color for a 24h percentage change value.
3212
* Positive → success green, negative → error red, zero/undefined → muted.
@@ -64,7 +44,10 @@ export function formatAssetPrice(
6444
): FormattedAssetPrice {
6545
const priceText =
6646
price !== undefined && price !== null && Number.isFinite(price)
67-
? formatPrice(price, currency)
47+
? formatPerpsFiat(price, {
48+
currency: currency ?? 'USD',
49+
stripTrailingZeros: false,
50+
})
6851
: '—';
6952

7053
const { text: changeText, color: changeColor } =

0 commit comments

Comments
 (0)